Re: [PATCH v2 00/15] powerpc/32s: Use BATs/LTLBs for STRICT_KERNEL_RWX

From: Christophe Leroy
Date: Wed Jan 16 2019 - 01:55:37 EST




Le 16/01/2019 Ã 01:35, Jonathan NeuschÃfer a ÃcritÂ:
On Tue, Jan 15, 2019 at 07:51:01AM +0100, Christophe Leroy wrote:
Le 15/01/2019 Ã 01:33, Jonathan NeuschÃfer a ÃcritÂ:
[...]
I've checked it patch-by-patch now (with STRICT_KERNEL_RWX):

- patches 1 and 2 build and boot fine
- patches 3 to 6 build, but fail to boot with this error:

The bug is in patch 2, mmu_mapin_ram() should return base instead of
returning 0 when __map_without_bats is set.

Indeed, with this change, I can boot up to patch 11.

- patches 12 to 15 build but fail to boot with this error:

Thats the one we need to really understand.

Do you have modules ? If so, can you try without ?

I don't use any modules in my test setup, but I have module support
enabled. Disabling CONFIG_MODULES makes no difference, as far as I can
see (I get the same backtrace with memblock_alloc_base+0x34/0x44).

[ 0.000000] [c0f1ff30] [c00280f0] panic+0x144/0x324 (unreliable)
[ 0.000000] [c0f1ff90] [c0c18a34] memblock_alloc_base+0x34/0x44
[ 0.000000] [c0f1ffa0] [c0c071e0] MMU_init_hw+0xcc/0x300
[ 0.000000] [c0f1ffd0] [c0c06554] MMU_init+0x12c/0x198
[ 0.000000] [c0f1fff0] [c0003418] start_here+0x40/0x78

With a few printks[1], I traced this error, and got the following
result:

[ 0.000000] __memblock_find_range_top_down(1000:1800000, 100000:100000, ffffffff, 0)
[ 0.000000] __memblock_find_range_top_down: in loop, 10000000:13f00000
[ 0.000000] __memblock_find_range_top_down: in loop, 179962d:1800000
[ 0.000000] __memblock_find_range_top_down: in loop, 1676000:17987a0
[ 0.000000] __memblock_find_range_top_down: nothing found :(

The limit of 0x1800000 comes from setup_initial_memory_limit, which only
considers the first memblock, but the second memblock starts at 256MiB,
so it wouldn't be usable anyway, according to the comment in
setup_initial_memory_limit.

Yes, initial_bats() in head_32.S sets one 256Mb BAT for initial booting:

/*
* On 601, we use 3 BATs to map up to 24M of RAM at _PAGE_OFFSET
* (we keep one for debugging) and on others, we use one 256M BAT.
*/
initial_bats:
lis r11,PAGE_OFFSET@h
mfspr r9,SPRN_PVR
rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */
cmpwi 0,r9,1
bne 4f
...
4: tophys(r8,r11)
#ifdef CONFIG_SMP
ori r8,r8,0x12 /* R/W access, M=1 */
#else
ori r8,r8,2 /* R/W access */
#endif /* CONFIG_SMP */
ori r11,r11,BL_256M<<2|0x2 /* set up BAT registers for 604 */

mtspr SPRN_DBAT0L,r8 /* N.B. 6xx (not 601) have valid */
mtspr SPRN_DBAT0U,r11 /* bit in upper BAT register */
mtspr SPRN_IBAT0L,r8
mtspr SPRN_IBAT0U,r11
isync
blr



Thinning the kernel down a bit actually makes it boot again. Ooops...!
Maybe enabling CONFIG_STRICT_KERNEL_RWX has made it just large enough to
fail the hash table allocation, but there may have been other factors
involved (I'm not sure exactly). Sorry for the confusion!

Ok, that must be the reason. Thanks for testing.

What about the following modification which maps a second 256Mb BAT, does it helps ?



diff --git a/arch/powerpc/kernel/head_32.S b/arch/powerpc/kernel/head_32.S
index c2f564690778..ea574596de37 100644
--- a/arch/powerpc/kernel/head_32.S
+++ b/arch/powerpc/kernel/head_32.S
@@ -1160,6 +1160,14 @@ initial_bats:
mtspr SPRN_DBAT0U,r11 /* bit in upper BAT register */
mtspr SPRN_IBAT0L,r8
mtspr SPRN_IBAT0U,r11
+#ifdef CONFIG_WII
+ addis r11,r11,0x10000000@h
+ addis r8,r8,0x10000000@h
+ mtspr SPRN_DBAT2L,r8
+ mtspr SPRN_DBAT2U,r11
+ mtspr SPRN_IBAT2L,r8
+ mtspr SPRN_IBAT2U,r11
+#endif
isync
blr

diff --git a/arch/powerpc/mm/ppc_mmu_32.c b/arch/powerpc/mm/ppc_mmu_32.c
index 3f4193201ee7..a334fd5210a8 100644
--- a/arch/powerpc/mm/ppc_mmu_32.c
+++ b/arch/powerpc/mm/ppc_mmu_32.c
@@ -259,6 +259,8 @@ void setup_initial_memory_limit(phys_addr_t first_memblock_base,
/* 601 can only access 16MB at the moment */
if (PVR_VER(mfspr(SPRN_PVR)) == 1)
memblock_set_current_limit(min_t(u64, first_memblock_size, 0x01000000));
+ else if (IS_ENABLED(CONFIG_WII))
+ memblock_set_current_limit(min_t(u64, first_memblock_size, 0x20000000));
else /* Anything else has 256M mapped */
memblock_set_current_limit(min_t(u64, first_memblock_size, 0x10000000));
}


Christophe



Jonathan

[1]:
diff --git a/mm/memblock.c b/mm/memblock.c
index 022d4cbb3618..66d588e08487 100644
--- a/mm/memblock.c
+++ b/mm/memblock.c
@@ -215,8 +215,11 @@ __memblock_find_range_top_down(phys_addr_t start, phys_addr_t end,
phys_addr_t this_start, this_end, cand;
u64 i;
+ printk("%s(%x:%x, %x:%x, %x, %x)\n", __func__, start, end, size, align, nid, flags);
+
for_each_free_mem_range_reverse(i, nid, flags, &this_start, &this_end,
NULL) {
+ printk("%s: in loop, %x:%x\n", __func__, this_start, this_end);
this_start = clamp(this_start, start, end);
this_end = clamp(this_end, start, end);
@@ -228,6 +231,7 @@ __memblock_find_range_top_down(phys_addr_t start, phys_addr_t end,
return cand;
}
+ printk("%s: nothing found :(\n", __func__);
return 0;
}