Message ID | 87egibw7yh.fsf@belgarion.home (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Sun, Sep 06, 2015 at 11:31:34PM +0200, Robert Jarzmik wrote: > Russell King - ARM Linux <linux@arm.linux.org.uk> writes: > > >> [1] Approach 1 : translation table sync > >> ======================================= > ... > > The important place is in arch/arm/include/asm/domain.h, which is where > > we manipulate the DACR within probe_kernel_address(). > > Gah, silly me. But even with [1], I still get an error [2]. I have a > confirmation that I have a "Page Permission" fault on the > probe_kernel_address(). Hmm, that's not right. If it's the DACR, then it should be a page domain fault, not a page permission fault. > [2] Oops > ======== > # insmod /tmp/unalign.ko > RJK1: fsr=23 far=e1c23643 dacr=51 > RJK2: fsr=23 far=e1c23643 dacr=51 > RJK3: fsr=2f far=bf00202c dacr=51 > RJK: fault=4 instr=0x00000000 instrptr=bf00202c Can you add a show_pte(current->mm, instrptr) to dump those page table entries please?
Russell King - ARM Linux <linux@arm.linux.org.uk> writes: >> Gah, silly me. But even with [1], I still get an error [2]. I have a >> confirmation that I have a "Page Permission" fault on the >> probe_kernel_address(). > > Hmm, that's not right. If it's the DACR, then it should be a page domain > fault, not a page permission fault. > >> [2] Oops >> ======== >> # insmod /tmp/unalign.ko >> RJK1: fsr=23 far=e1c23643 dacr=51 >> RJK2: fsr=23 far=e1c23643 dacr=51 >> RJK3: fsr=2f far=bf00202c dacr=51 >> RJK: fault=4 instr=0x00000000 instrptr=bf00202c > > Can you add a show_pte(current->mm, instrptr) to dump those page > table entries please? Most certainly, here we go : # insmod /tmp/unalign.ko RJK1: fsr=23 far=e1c1f743 dacr=51 RJK2: fsr=23 far=e1c1f743 dacr=51 pgd = e1cc4000 [bf00202c] *pgd=c1cab851, *pte=c1cb504f, *ppte=c1cb501f RJK3: fsr=2f far=bf00202c dacr=51 RJK4: fault=4 instr=0x00000000 instrptr=bf00202c pgd = e1cc4000 [bf00202c] *pgd=c1cab851, *pte=c1cb504f, *ppte=c1cb501f Unable to handle kernel paging request at virtual address e1c1f743 pgd = e1cc4000 [e1c1f743] *pgd=c1c0044e(bad) Internal error: Oops: 823 [#1] ARM Modules linked in: unalign(+) CPU: 0 PID: 608 Comm: insmod Not tainted 4.2.0-rc8-next-20150828-cm-x300+ #926 Hardware name: CM-X300 module task: e1c68380 ti: e1c84000 task.ti: e1c84000 PC is at u_init+0x2c/0x40 [unalign] LR is at u_init+0x14/0x40 [unalign] pc : [<bf00202c>] lr : [<bf002014>] psr: a0000013 sp : e1c85df8 ip : e1c1f700 fp : 1e3e041c r10: e1c1fc00 r9 : 00000001 r8 : 00000000 r7 : bf002000 r6 : e1cad660 r5 : c0b85b80 r4 : c0b85b80 r3 : e1c1f740 r2 : 00000004 r1 : a0000013 r0 : 00000000 Flags: NzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment none Control: 0000397f Table: c1cc4018 DAC: 00000051 Process insmod (pid: 608, stack limit = 0xe1c84198) It happens on both mioa701(pxa270) and cm-x300(pxa310), with the same cross-compiler+host and kernel source. Yet doesn't happen on zylonite(pxa310), but different cross-compiler+host. I'll try to have a single kernel (binary) tried over the cm-x300 and zylonite to cross-check. Cheers.
On Tue, Sep 08, 2015 at 07:01:00PM +0200, Robert Jarzmik wrote: > Russell King - ARM Linux <linux@arm.linux.org.uk> writes: > > >> Gah, silly me. But even with [1], I still get an error [2]. I have a > >> confirmation that I have a "Page Permission" fault on the > >> probe_kernel_address(). > > > > Hmm, that's not right. If it's the DACR, then it should be a page domain > > fault, not a page permission fault. > > > >> [2] Oops > >> ======== > >> # insmod /tmp/unalign.ko > >> RJK1: fsr=23 far=e1c23643 dacr=51 > >> RJK2: fsr=23 far=e1c23643 dacr=51 > >> RJK3: fsr=2f far=bf00202c dacr=51 > >> RJK: fault=4 instr=0x00000000 instrptr=bf00202c > > > > Can you add a show_pte(current->mm, instrptr) to dump those page > > table entries please? > Most certainly, here we go : > > # insmod /tmp/unalign.ko > RJK1: fsr=23 far=e1c1f743 dacr=51 > RJK2: fsr=23 far=e1c1f743 dacr=51 > pgd = e1cc4000 > [bf00202c] *pgd=c1cab851, *pte=c1cb504f, *ppte=c1cb501f > RJK3: fsr=2f far=bf00202c dacr=51 > RJK4: fault=4 instr=0x00000000 instrptr=bf00202c > pgd = e1cc4000 > [bf00202c] *pgd=c1cab851, *pte=c1cb504f, *ppte=c1cb501f Okay, so domain = 2 (which for an Xscale 3 kernel is DOMAIN_KERNEL). The page table entry has AP=01, which is user no-access, svc read/write. What should happen is: #define probe_kernel_address(addr, retval) \ ({ \ long ret; \ mm_segment_t old_fs = get_fs(); \ \ set_fs(KERNEL_DS); \ This should update the DACR from 0x51 to 0x71 - switching domain 2 to manager mode. pagefault_disable(); \ ret = __copy_from_user_inatomic(&(retval), \ (__force typeof(retval) __user *)(addr), \ sizeof(retval)); \ ... __copy_from_user_inatomic is an alias for __copy_from_user: static inline unsigned long __must_check __copy_from_user(void *to, const void __user *from, unsigned long n) { unsigned int __ua_flags = uaccess_save_and_enable(); and uaccess_save_and_enable() does: unsigned int old_domain = get_domain(); /* Set the current domain access to permit user accesses */ set_domain((old_domain & ~domain_mask(DOMAIN_USER)) | domain_val(DOMAIN_USER, DOMAIN_CLIENT)); So this should then end up changing the DACR from 0x71 to 0x75, enabling user access (because this function may access userspace.) What this means is that (going back to __copy_from_user): n = arm_copy_from_user(to, from, n); At the point we call into this code, the DACR should be 0x75, which should allow us to read the instruction at 0xbf00202c. But this is failing with a permission error - which it would do if it thought the kernel domain was in manager mode (iow, 0x55). I think some debugging in the above areas is needed - but I can't see anything wrong. If something were wrong, I'm pretty sure we'd have every pre-ARMv6 machine exploding right now because of it - and oopses wouldn't work. You're clearly getting oopses reported correctly, which rather proves out this code path. Now, when you get the fault inside arm_copy_from_user(), you can print the DACR value saved at the time the fault was generated by printing the word above struct pt_regs on the stack - add to arch/arm/mm/fault.c:do_DataAbort(): if (addr == 0xbf00202c) printk("DACR=0x%08x\n", *(u32 *)(regs + 1)); before the "if (!inf->fn(addr, fsr & ~FSR_LNX_PF, regs))" line. That'll tell us what the DACR register was when we saved it. If it isn't 0x75, then the next part of the puzzle is going to be working out why it isn't.
Russell King - ARM Linux <linux@arm.linux.org.uk> writes: > What should happen is: Thanks very much for the explanation, hopefully I have enough material to fly on my own now. > Now, when you get the fault inside arm_copy_from_user(), you can > print the DACR value saved at the time the fault was generated by > printing the word above struct pt_regs on the stack - add to > arch/arm/mm/fault.c:do_DataAbort(): > > if (addr == 0xbf00202c) printk("DACR=0x%08x\n", *(u32 *)(regs + 1)); > > before the "if (!inf->fn(addr, fsr & ~FSR_LNX_PF, regs))" line. > That'll tell us what the DACR register was when we saved it. > > If it isn't 0x75, then the next part of the puzzle is going to be > working out why it isn't. It's 0x55. I'll track down how this happens, there are not that many places where DACR is touched, and I'm in a very controlled environement, so I can cunningly place JTAG breakpoints and watch DACR. I'll report once I have a better idea what is happening, that might take me a couple of days given that most of my workforce is available on weekends only. Thanks again for the free lesson.
======================================================= diff --git a/arch/arm/include/asm/assembler.h b/arch/arm/include/asm/assembler.h index 7bbf325a4f31..73d5ad456e32 100644 --- a/arch/arm/include/asm/assembler.h +++ b/arch/arm/include/asm/assembler.h @@ -449,6 +449,13 @@ THUMB( orr \reg , \reg , #PSR_T_BIT ) #endif .endm + .macro dacr_sync, rd + mrc p15, 0, \rd, c2, c0, 0 + mov \rd, \rd + sub pc, pc, #4 + mcr p15, 0, \rd, c7, c5, 4 + .endm + .macro uaccess_disable, tmp, isb=1 #ifdef CONFIG_CPU_SW_DOMAIN_PAN /* @@ -457,6 +464,7 @@ THUMB( orr \reg , \reg , #PSR_T_BIT ) */ mov \tmp, #DACR_UACCESS_DISABLE mcr p15, 0, \tmp, c3, c0, 0 @ Set domain register + dacr_sync \tmp .if \isb instr_sync .endif @@ -471,6 +479,7 @@ THUMB( orr \reg , \reg , #PSR_T_BIT ) */ mov \tmp, #DACR_UACCESS_ENABLE mcr p15, 0, \tmp, c3, c0, 0 + dacr_sync \tmp .if \isb instr_sync .endif @@ -488,6 +497,7 @@ THUMB( orr \reg , \reg , #PSR_T_BIT ) #ifdef CONFIG_CPU_SW_DOMAIN_PAN ldr r0, [sp, #S_FRAME_SIZE] mcr p15, 0, r0, c3, c0, 0 + dacr_sync r0 #endif .endm diff --git a/arch/arm/include/asm/domain.h b/arch/arm/include/asm/domain.h index e878129f2fee..10c9a38636ac 100644 --- a/arch/arm/include/asm/domain.h +++ b/arch/arm/include/asm/domain.h @@ -97,7 +97,11 @@ static inline unsigned int get_domain(void) static inline void set_domain(unsigned val) { asm volatile( - "mcr p15, 0, %0, c3, c0 @ set domain" + "mcr p15, 0, %0, c3, c0; @ set domain \ + mrc p15, 0, %0, c2, c0, 0; \ + mov %0, %0; \ + sub pc, pc, #4; \ + mcr p15, 0, %0, c7, c5, 4" : : "r" (val)); isb(); } diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c index 9769f1eefe3b..c9c454129344 100644 --- a/arch/arm/mm/alignment.c +++ b/arch/arm/mm/alignment.c @@ -747,6 +747,27 @@ do_alignment_t32_to_handler(unsigned long *pinstr, struct pt_regs *regs, return NULL; } +static u32 far_read(void) +{ + u32 far; + asm("mrc p15, 0, %0, c6, c0, 0" : "=r" (far)); + return far; +} + +static u32 fsr_read(void) +{ + u32 fsr; + asm("mrc p15, 0, %0, c5, c0, 0" : "=r" (fsr)); + return fsr; +} + +static u32 dacr_read(void) +{ + u32 dacr; + asm("mrc p15, 0, %0, c3, c0, 0" : "=r" (dacr)); + return dacr; +} + static int do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { @@ -763,6 +784,8 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs) local_irq_enable(); instrptr = instruction_pointer(regs); + pr_info("RJK1: fsr=%x far=%x dacr=%x\n", fsr_read(), far_read(), dacr_read()); + pr_info("RJK2: fsr=%x far=%x dacr=%x\n", fsr_read(), far_read(), dacr_read()); if (thumb_mode(regs)) { u16 *ptr = (u16 *)(instrptr & ~1); @@ -787,6 +810,8 @@ do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs) instr = __mem_to_opcode_arm(instr); } + pr_info("RJK3: fsr=%x far=%x dacr=%x\n", fsr_read(), far_read(), dacr_read()); + pr_info("RJK: fault=%d instr=0x%08x instrptr=%p\n", fault, instr, instrptr); if (fault) { type = TYPE_FAULT; goto bad_or_fault;