Use seL4_UserVSpaceTop for KERNEL_RESERVED_START - #112
midnightveil wants to merge 2 commits into
Conversation
This value is consistent across platforms. Note that the previous definition incorrectly rounded down the inclusive addresses used by architectures e.g. AArch64. Signed-off-by: Julia Vassiliki <julia.vassiliki@unsw.edu.au>
39daf24 to
0ea9e2f
Compare
|
This PR is, strictly speaking, unnecessary: the old behaviour with |
Indanz
left a comment
There was a problem hiding this comment.
Just one nitpick, otherwise it seems fine.
Don't merge before the seL4 change as been merged.
| /* we only have regions to reserve if the seL4_UserVSpaceTop is less than | ||
| * the top of the VA range. Why this is ever the case is beyond me... | ||
| * But it happens for AArch64 hyp-mode with a PA Range of 2^40. | ||
| */ |
There was a problem hiding this comment.
| /* we only have regions to reserve if the seL4_UserVSpaceTop is less than | |
| * the top of the VA range. Why this is ever the case is beyond me... | |
| * But it happens for AArch64 hyp-mode with a PA Range of 2^40. | |
| */ | |
| /* we only have regions to reserve if the seL4_UserVSpaceTop is less than | |
| * the top of the VA range. | |
| * This happens for e.g. AArch64 hyp-mode with a PA Range of 2^40. | |
| */ |
There was a problem hiding this comment.
It's not e.g., it's the only one I know about (all the other arches worked)
I've adjusted it now aside from that.
In the case of AArch64 hyp-mode with PA_SIZE=40 bits, the kernel only allows us to use 3-level page tables, so the VA range is 0..<2^39, and so the seL4_UserTop/ seL4_UserVSpaceTop of 2^40-1 is bigger than the VA range, which causes TOP_LEVEL_INDEX(2^40-1) to report an index of 0, and hence this code marks all of the memory as kernel-reserved. Which breaks things. Previously we always rounded down the inclusive addresses that AArch64 uses and so it (by-coincidence) landed in the 511th slot of the top-level page table. Signed-off-by: Julia Vassiliki <julia.vassiliki@unsw.edu.au>
0ea9e2f to
56304ce
Compare
|
|
||
| #define BYTES_FOR_LEVEL(l) BIT(VSPACE_LEVEL_BITS * (l) + PAGE_BITS_4K) | ||
| #define ALIGN_FOR_LEVEL(l) (~(MASK(VSPACE_LEVEL_BITS * (l) + PAGE_BITS_4K))) | ||
| #define BITS_FOR_LEVEL(l) (VSPACE_LEVEL_BITS * (l) + PAGE_BITS_4K) |
There was a problem hiding this comment.
You didn't introduce this, but this seems just wrong to me. It assumes that VSPACE_LEVEL_BITS is the same for each level on all platforms, which is not the case precisely for AArch64 hyp PA40. In that config, the top level is twice the size of the other levels and this macro therefore returns the wrong size.
I also thought page directories and page tables on AArch32 could translate different numbers of bits in some configs, but I might be misremembering that.
There was a problem hiding this comment.
It assumes that VSPACE_LEVEL_BITS is the same for each level on all platforms, which is not the case precisely for AArch64 hyp PA40.
Oh! I missed that. I guessed the S2_START_L1 changes the exact size of PTs? (i.e. it's 1024 not 512 there? Or something?)
Then my comment on the bootstrap check is actually wrong: the UserVSpaceTop is not, in fact, higher than the VA size?
There was a problem hiding this comment.
Yes, the top-level table under S2_START_L1 translates 10 bits, the lower ones 9. I.e. 10 + 9 + 9 + 12 = 40. Without S2_START_L1, they are all the same, i.e. 4*9 + 12 =48 (which I think means the top half of the top level is not actually usable when translating IPA to PA, both at 44 bits).
There was a problem hiding this comment.
Right. Then that check should instead become an assert; if it's not true then something went wrong with our map calculations.
There was a problem hiding this comment.
I suppose we should duplicate what seL4 does here:
/* Extract the n-level PT index from a virtual address:
* - n is page table level counting from the root page table,
* - NUM_PT_LEVELS are either 3 or 4 page table levels depending
* on whether the address range being translated is 48bits, 44 bits or 40 bits.
* - If translating an address in the kernel addrspace NUM_PT_LEVELS = 4 always.
* - PageTables always have 512 slots (PT_INDEX_BITS = 9) but if there are only
* 3 total levels then the root level is implemented with 4 concatenated tables
* meaning 2048 slots (UPT_LEVELS = 3 => seL4_VSpaceIndexBits = 12)
*
* PT_LEVEL_SHIFT(n) == PT_INDEX_BITS * (NUM_PT_LEVELS - n) + seL4_PageBits
* GET_PT_INDEX(addr, n) == (addr >> PT_LEVEL_SHIFT(n)) & MASK(PT_INDEX_BITS)
*/
#ifdef AARCH64_VSPACE_S2_START_L1
#define UPT_LEVELS 3
#define ULVL_FRM_ARM_PT_LVL(n) ((n)-1)
#else
#define UPT_LEVELS 4
#define ULVL_FRM_ARM_PT_LVL(n) (n)
#endif
#define KPT_LEVELS 4
#define KLVL_FRM_ARM_PT_LVL(n) (n)
#define KPT_LEVEL_SHIFT(n) (((PT_INDEX_BITS) * (((KPT_LEVELS) - 1) - (n))) + seL4_PageBits)
#define GET_KPT_INDEX(addr, n) (((addr) >> KPT_LEVEL_SHIFT(n)) & MASK(PT_INDEX_BITS))
#define GET_KLVL_PGSIZE(n) BIT(KPT_LEVEL_SHIFT((n)))
#define UPT_LEVEL_SHIFT(n) (((PT_INDEX_BITS) * (((UPT_LEVELS) - 1) - (n))) + seL4_PageBits)
#define UPT_INDEX_MASK(n) (n == 0 ? seL4_VSpaceIndexBits : PT_INDEX_BITS)
#define GET_UPT_INDEX(addr, n) (((addr) >> UPT_LEVEL_SHIFT(n)) & MASK(UPT_INDEX_MASK(n)))
#define GET_ULVL_PGSIZE_BITS(n) UPT_LEVEL_SHIFT((n))
#define GET_ULVL_PGSIZE(n) BIT(UPT_LEVEL_SHIFT((n)))
There was a problem hiding this comment.
It's possible that the code before never tried to compute the total and never needed to know the number of bits translated at the top level, yes. That could have worked.
It wouldn't because user space doesn't manage the top level, the kernel does. All the user page tables are the same size I think.
There was a problem hiding this comment.
It's possible that the code before never tried to compute the total and never needed to know the number of bits translated at the top level, yes. That could have worked.
It wouldn't because user space doesn't manage the top level, the kernel does.
The user does manage all levels: it has to retype top-level page tables and know their size (apart from the initial thread), and it can map pages into the top level, depending on architecture.
All the user page tables are the same size I think.
It's a bit of a wild assumption to make given how diverse page table structures are, but it's possible that it works out for what we currently support, because for all the 32 bit architectures we have (where there is just chaos, really), there are only 2 levels in total, and the 64 bit architectures all seem more sane.
There was a problem hiding this comment.
What I mean is that the number of index bits for each non-top level page table determines how many bits it resolves and hence is relevant for page table management in user space. For intermediate levels you need to know the boundary between sibling page tables, because that determines whether you need to allocate a new page table or can keep using the exiting one. There is always only one top level, so user space doesn't need to care about how many index bits it has.
Well, it's no coincidence that everyone ends up being compatible in choosing page sizes. Either explicitly to be compatible and reduce the porting pain, or by making the same trade-offs and ending at the same result. And once you have that, using a small page sized area for one page table level is only natural. And if you try to be as space efficient as possible, with 4k pages you end up with 10 bits index for 32-bit and 9 bits index for 64-bit platforms. The top level being a different size is much easier to deal with.
There was a problem hiding this comment.
And if you try to be as space efficient as possible, with 4k pages you end up with 10 bits index for 32-bit and 9 bits index for 64-bit platforms. The top level being a different size is much easier to deal with.
But of course those numbers are not true AArch32 EL2 for instance. The assumption that all page table levels underneath the top-level at least amongst themselves translate the same number of of bits still works, because there is only one such level on AArch32 (but that number is different between EL1 and EL2 tables).
There was a problem hiding this comment.
AArch32 HYP uses Large Physical Address Extension (LPAE) for 40-bit physical address space, so I would count that as a 64-bit platform as far as address translation goes.
| * a PA Range of 2^40. Otherwise, we would be reserving random indices | ||
| * at the bottom of the virtual address range due to wrapping. | ||
| */ | ||
| if (seL4_UserVSpaceTop <= VSPACE_VA_TOP) { |
There was a problem hiding this comment.
Just to double-check, the reason this works now is because seL4_UserVSpaceTop is not <= VSPACE_VA_TOP on AArch64 hyp PA40? I.e. it's not trying to access the TOP_LEVEL_INDEX, which in this case would be 0. Is that correct?
If we fix the macro above, VSPACE_VA_TOP would become mask 40, which should be the same as seL4_UserVSpaceTop for that config, which means the condition is now true.
The code overall seems a bit strange, because there are no kernel reserved regions in EL2 kernels, neither on AArch32 nor AArch64. Maybe it happened to work out before because it unnecessarily reserved one entry for the kernel even though it didn't need to, not sure.
So maybe the condition should instead be on CONFIG_ARM_HYPERVISOR_SUPPORT? I think currently all other kernel configs do have reserved regions in the top-level table.
There was a problem hiding this comment.
It might have been working out for PA44, because the top half of the top table is reserved because IPAs are 44 bits, not 48 bits (and the table could otherwise translate 48 bits).
Which means my condition on just CONFIG_ARM_HYPERVISOR_SUPPORT would be wrong, because I guess the library should know that it can't allocate there (not because it is kernel-reserved, just because these addresses don't exist on that hardware).
There was a problem hiding this comment.
Maybe it happened to work out before because it unnecessarily reserved one entry for the kernel even though it didn't need to, not sure
Yes, it was, because of the round down of the AArch64 inclusive value.
It might have been working out for PA44, because the top half of the top table is reserved because IPAs are 44 bits, not 48 bits (and the table could otherwise translate 48 bit
Yeah, it was like 0x1f vs 0x20.
I printed these out: seL4/seL4#1695 (comment)
|
All hw tests fail with "No 'HW_SSH' key provided", not sure what's going on. I would like to see a successful test before merging this, just in case. |
Apparently we have never done a PR hw-test from this repo, the access to the machine queue keys wasn't configured. That's now done and I have restarted the test. |
|
We should not merge it like this, even if it happens to work. The macro |
This value is consistent across platforms. Note that the previous definition incorrectly rounded down the inclusive addresses used by architectures e.g. AArch64.
Test with: seL4/seL4#1695