aarch64 haskell/design: PTE encoding
Now that the C code is available, we can settle the PTE encoding for the spec. Notable differences to RISCV64 are: - the base address uses field-high and doesn't need shifting - leads to simpler/more direct address access - PTEs use different attributes - uses a flag for 4k pages which have a different hardware encoding - page table PTEs have no rights/attributes Signed-off-by: Gerwin Klein <gerwin.klein@proofcraft.systems>
This commit is contained in:
parent
393ee8b687
commit
02dbb29b9f
|
@ -32,8 +32,8 @@ where
|
||||||
pte <- pteAtIndex level ptPtr vPtr;
|
pte <- pteAtIndex level ptPtr vPtr;
|
||||||
if isPageTablePTE pte
|
if isPageTablePTE pte
|
||||||
then do
|
then do
|
||||||
checkPTAt (getPPtrFromHWPTE pte);
|
checkPTAt (getPPtrFromPTE pte);
|
||||||
lookupPTSlotFromLevel (level - 1) (getPPtrFromHWPTE pte) vPtr
|
lookupPTSlotFromLevel (level - 1) (getPPtrFromPTE pte) vPtr
|
||||||
od
|
od
|
||||||
else return (ptBitsLeft level, ptSlotIndex level ptPtr vPtr)
|
else return (ptBitsLeft level, ptSlotIndex level ptPtr vPtr)
|
||||||
od"
|
od"
|
||||||
|
@ -48,7 +48,7 @@ where
|
||||||
slot <- returnOk $ ptSlotIndex level ptPtr vPtr;
|
slot <- returnOk $ ptSlotIndex level ptPtr vPtr;
|
||||||
pte <- withoutFailure $ getObject slot;
|
pte <- withoutFailure $ getObject slot;
|
||||||
unlessE (isPageTablePTE pte) $ throw InvalidRoot;
|
unlessE (isPageTablePTE pte) $ throw InvalidRoot;
|
||||||
ptr <- returnOk (getPPtrFromHWPTE pte);
|
ptr <- returnOk (getPPtrFromPTE pte);
|
||||||
if ptr = targetPtPtr
|
if ptr = targetPtPtr
|
||||||
then returnOk slot
|
then returnOk slot
|
||||||
else doE
|
else doE
|
||||||
|
|
|
@ -133,13 +133,8 @@ isPagePTE :: PTE -> Bool
|
||||||
isPagePTE (PagePTE {}) = True
|
isPagePTE (PagePTE {}) = True
|
||||||
isPagePTE _ = False
|
isPagePTE _ = False
|
||||||
|
|
||||||
-- FIXME AARCH64: replace name with whatever ppns will be called
|
getPPtrFromPTE :: PTE -> PPtr PTE
|
||||||
-- only used on non-toplevel tables
|
getPPtrFromPTE pte = ptrFromPAddr $ pteBaseAddress pte
|
||||||
pteAddr :: PTE -> PAddr
|
|
||||||
pteAddr pte = ptePPN pte `shiftL` ptBits False
|
|
||||||
|
|
||||||
getPPtrFromHWPTE :: PTE -> PPtr PTE
|
|
||||||
getPPtrFromHWPTE pte = ptrFromPAddr $ pteAddr pte
|
|
||||||
|
|
||||||
-- how many bits there are left to be translated at a given level (0 = bottom
|
-- how many bits there are left to be translated at a given level (0 = bottom
|
||||||
-- level). This counts the bits the levels below the current one translate, so
|
-- level). This counts the bits the levels below the current one translate, so
|
||||||
|
@ -176,8 +171,8 @@ lookupPTSlotFromLevel level ptPtr vPtr = do
|
||||||
pte <- pteAtIndex level ptPtr vPtr
|
pte <- pteAtIndex level ptPtr vPtr
|
||||||
if isPageTablePTE pte
|
if isPageTablePTE pte
|
||||||
then do
|
then do
|
||||||
checkPTAt (getPPtrFromHWPTE pte)
|
checkPTAt (getPPtrFromPTE pte)
|
||||||
lookupPTSlotFromLevel (level-1) (getPPtrFromHWPTE pte) vPtr
|
lookupPTSlotFromLevel (level-1) (getPPtrFromPTE pte) vPtr
|
||||||
else return (ptBitsLeft level, ptSlotIndex level ptPtr vPtr)
|
else return (ptBitsLeft level, ptSlotIndex level ptPtr vPtr)
|
||||||
|
|
||||||
-- lookupPTSlot walks the page table and returns a pointer to the slot that maps
|
-- lookupPTSlot walks the page table and returns a pointer to the slot that maps
|
||||||
|
@ -191,7 +186,7 @@ lookupFrame vspaceRoot vPtr = do
|
||||||
(bitsLeft, ptePtr) <- lookupPTSlot vspaceRoot vPtr
|
(bitsLeft, ptePtr) <- lookupPTSlot vspaceRoot vPtr
|
||||||
pte <- getObject ptePtr
|
pte <- getObject ptePtr
|
||||||
if isPagePTE pte
|
if isPagePTE pte
|
||||||
then return $ Just (bitsLeft, pteAddr pte)
|
then return $ Just (bitsLeft, pteBaseAddress pte)
|
||||||
else return Nothing
|
else return Nothing
|
||||||
|
|
||||||
{- Page Table Modification -}
|
{- Page Table Modification -}
|
||||||
|
@ -322,7 +317,7 @@ lookupPTFromLevel level ptPtr vPtr targetPtPtr = do
|
||||||
let slot = ptSlotIndex level ptPtr vPtr
|
let slot = ptSlotIndex level ptPtr vPtr
|
||||||
pte <- withoutFailure $ getObject slot
|
pte <- withoutFailure $ getObject slot
|
||||||
unless (isPageTablePTE pte) $ throw InvalidRoot
|
unless (isPageTablePTE pte) $ throw InvalidRoot
|
||||||
let ptr = getPPtrFromHWPTE pte
|
let ptr = getPPtrFromPTE pte
|
||||||
if ptr == targetPtPtr
|
if ptr == targetPtPtr
|
||||||
then return slot
|
then return slot
|
||||||
else do
|
else do
|
||||||
|
@ -344,9 +339,8 @@ unmapPageTable asid vaddr pt = ignoreFailure $ do
|
||||||
checkMappingPPtr :: PPtr Word -> PTE -> KernelF LookupFailure ()
|
checkMappingPPtr :: PPtr Word -> PTE -> KernelF LookupFailure ()
|
||||||
checkMappingPPtr pptr pte =
|
checkMappingPPtr pptr pte =
|
||||||
case pte of
|
case pte of
|
||||||
PagePTE { ptePPN = ppn } ->
|
PagePTE { pteBaseAddress = addr } ->
|
||||||
-- PagePTEs can only occur on non-toplevel tables
|
unless (addr == addrFromPPtr pptr) $ throw InvalidRoot
|
||||||
unless (ptrFromPAddr (ppn `shiftL` ptBits False) == pptr) $ throw InvalidRoot
|
|
||||||
_ -> throw InvalidRoot
|
_ -> throw InvalidRoot
|
||||||
|
|
||||||
unmapPage :: VMPageSize -> ASID -> VPtr -> PPtr Word -> Kernel ()
|
unmapPage :: VMPageSize -> ASID -> VPtr -> PPtr Word -> Kernel ()
|
||||||
|
@ -499,21 +493,23 @@ maskVMRights r m = case (r, capAllowRead m, capAllowWrite m) of
|
||||||
_ -> VMKernelOnly
|
_ -> VMKernelOnly
|
||||||
|
|
||||||
|
|
||||||
{- Decoding RISC-V Invocations -}
|
{- Decoding AARCH64 Invocations -}
|
||||||
|
|
||||||
attribsFromWord :: Word -> VMAttributes
|
attribsFromWord :: Word -> VMAttributes
|
||||||
attribsFromWord w = VMAttributes { riscvExecuteNever = w `testBit` 0 }
|
attribsFromWord w = VMAttributes {
|
||||||
|
armExecuteNever = w `testBit` 2,
|
||||||
|
armParityEnabled = w `testBit` 1, -- FIXME AARCH64: this seems to be ignored, but is present in the API
|
||||||
|
armPageCacheable = w `testBit` 0 }
|
||||||
|
|
||||||
makeUserPTE :: PAddr -> Bool -> VMRights -> PTE
|
makeUserPTE :: PAddr -> VMRights -> VMAttributes -> VMPageSize -> PTE
|
||||||
makeUserPTE baseAddr executable rights =
|
makeUserPTE baseAddr rights attrs vmSize =
|
||||||
if rights == VMKernelOnly && not executable
|
PagePTE {
|
||||||
then InvalidPTE
|
pteBaseAddress = baseAddr,
|
||||||
else PagePTE {
|
pteSmallPage = vmSize == ARMSmallPage,
|
||||||
ptePPN = baseAddr `shiftR` pageBits,
|
pteGlobal = False,
|
||||||
pteGlobal = False,
|
pteExecuteNever = armExecuteNever attrs,
|
||||||
pteUser = True,
|
pteDevice = armPageCacheable attrs,
|
||||||
pteExecute = executable,
|
pteRights = rights }
|
||||||
pteRights = rights }
|
|
||||||
|
|
||||||
checkVPAlignment :: VMPageSize -> VPtr -> KernelF SyscallError ()
|
checkVPAlignment :: VMPageSize -> VPtr -> KernelF SyscallError ()
|
||||||
checkVPAlignment sz w =
|
checkVPAlignment sz w =
|
||||||
|
@ -554,10 +550,11 @@ decodeRISCVFrameInvocationMap cte cap vptr rightsMask attr vspaceCap = do
|
||||||
_ -> throw $ InvalidCapability 1
|
_ -> throw $ InvalidCapability 1
|
||||||
vspaceCheck <- lookupErrorOnFailure False $ findVSpaceForASID asid
|
vspaceCheck <- lookupErrorOnFailure False $ findVSpaceForASID asid
|
||||||
when (vspaceCheck /= vspace) $ throw $ InvalidCapability 1
|
when (vspaceCheck /= vspace) $ throw $ InvalidCapability 1
|
||||||
let pgBits = pageBitsForSize $ capFSize cap
|
let frameSize = capFSize cap
|
||||||
|
let pgBits = pageBitsForSize frameSize
|
||||||
let vtop = vptr + (bit pgBits - 1)
|
let vtop = vptr + (bit pgBits - 1)
|
||||||
when (vtop >= pptrUserTop) $ throw $ InvalidArgument 0
|
when (vtop >= pptrUserTop) $ throw $ InvalidArgument 0
|
||||||
checkVPAlignment (capFSize cap) vptr
|
checkVPAlignment frameSize vptr
|
||||||
(bitsLeft, slot) <- withoutFailure $ lookupPTSlot vspace vptr
|
(bitsLeft, slot) <- withoutFailure $ lookupPTSlot vspace vptr
|
||||||
unless (bitsLeft == pgBits) $ throw $
|
unless (bitsLeft == pgBits) $ throw $
|
||||||
FailedLookup False $ MissingCapability bitsLeft
|
FailedLookup False $ MissingCapability bitsLeft
|
||||||
|
@ -569,11 +566,10 @@ decodeRISCVFrameInvocationMap cte cap vptr rightsMask attr vspaceCap = do
|
||||||
Nothing -> checkSlot slot (\pte -> pte == InvalidPTE)
|
Nothing -> checkSlot slot (\pte -> pte == InvalidPTE)
|
||||||
let vmRights = maskVMRights (capFVMRights cap) $ rightsFromWord rightsMask
|
let vmRights = maskVMRights (capFVMRights cap) $ rightsFromWord rightsMask
|
||||||
let framePAddr = addrFromPPtr (capFBasePtr cap)
|
let framePAddr = addrFromPPtr (capFBasePtr cap)
|
||||||
let exec = not $ riscvExecuteNever (attribsFromWord attr)
|
|
||||||
return $ InvokePage $ PageMap {
|
return $ InvokePage $ PageMap {
|
||||||
pageMapCap = ArchObjectCap $ cap { capFMappedAddress = Just (asid,vptr) },
|
pageMapCap = ArchObjectCap $ cap { capFMappedAddress = Just (asid,vptr) },
|
||||||
pageMapCTSlot = cte,
|
pageMapCTSlot = cte,
|
||||||
pageMapEntries = (makeUserPTE framePAddr exec vmRights, slot) }
|
pageMapEntries = (makeUserPTE framePAddr vmRights (attribsFromWord attr) frameSize, slot) }
|
||||||
|
|
||||||
decodeARMFrameInvocationFlush :: Word -> [Word] -> ArchCapability ->
|
decodeARMFrameInvocationFlush :: Word -> [Word] -> ArchCapability ->
|
||||||
KernelF SyscallError ArchInv.Invocation
|
KernelF SyscallError ArchInv.Invocation
|
||||||
|
@ -640,9 +636,7 @@ decodeRISCVPageTableInvocationMap cte cap vptr attr vspaceCap = do
|
||||||
oldPTE <- withoutFailure $ getObject slot
|
oldPTE <- withoutFailure $ getObject slot
|
||||||
when (bitsLeft == pageBits || oldPTE /= InvalidPTE) $ throw DeleteFirst
|
when (bitsLeft == pageBits || oldPTE /= InvalidPTE) $ throw DeleteFirst
|
||||||
let pte = PageTablePTE {
|
let pte = PageTablePTE {
|
||||||
ptePPN = addrFromPPtr (capPTBasePtr cap) `shiftR` pageBits,
|
pteBaseAddress = addrFromPPtr (capPTBasePtr cap) }
|
||||||
pteGlobal = False,
|
|
||||||
pteUser = False }
|
|
||||||
let vptr = vptr .&. complement (mask bitsLeft)
|
let vptr = vptr .&. complement (mask bitsLeft)
|
||||||
return $ InvokePageTable $ PageTableMap {
|
return $ InvokePageTable $ PageTableMap {
|
||||||
ptMapCap = ArchObjectCap $ cap { capPTMappedAddress = Just (asid, vptr) },
|
ptMapCap = ArchObjectCap $ cap { capPTMappedAddress = Just (asid, vptr) },
|
||||||
|
|
|
@ -293,7 +293,10 @@ writeVCPUHardwareReg reg val = error "Unimplemented - machine op"
|
||||||
{- Page Table Structure -}
|
{- Page Table Structure -}
|
||||||
|
|
||||||
data VMAttributes
|
data VMAttributes
|
||||||
= VMAttributes { riscvExecuteNever :: Bool }
|
= VMAttributes {
|
||||||
|
armExecuteNever :: Bool,
|
||||||
|
armParityEnabled :: Bool,
|
||||||
|
armPageCacheable :: Bool }
|
||||||
|
|
||||||
-- The C code also defines VMWriteOnly.
|
-- The C code also defines VMWriteOnly.
|
||||||
-- Leaving it out here will show that it is unused.
|
-- Leaving it out here will show that it is unused.
|
||||||
|
@ -329,24 +332,21 @@ vmRightsFromBits rw = getVMRights (testBit rw 1) (testBit rw 0)
|
||||||
-- Page Table entries
|
-- Page Table entries
|
||||||
|
|
||||||
-- Encoding notes:
|
-- Encoding notes:
|
||||||
-- - dirty and accessed bits are always 1 for valid PTEs
|
-- - pteSmallPage distinguishes pte_4k_page from pte_page
|
||||||
-- - SW bits always 0
|
-- - AF is always 1
|
||||||
-- - valid = 1 and read/write/execute = 0 => table PTE
|
-- - mair/mair_s2 (Memory Attribute Indirection Register) encoded as pteDevice
|
||||||
-- - valid = 0 => invalid PTE
|
|
||||||
-- - otherwise => page PTE
|
|
||||||
|
|
||||||
data PTE
|
data PTE
|
||||||
= InvalidPTE
|
= InvalidPTE
|
||||||
| PagePTE {
|
| PagePTE {
|
||||||
ptePPN :: PAddr,
|
pteBaseAddress :: PAddr,
|
||||||
|
pteSmallPage :: Bool,
|
||||||
pteGlobal :: Bool,
|
pteGlobal :: Bool,
|
||||||
pteUser :: Bool,
|
pteExecuteNever :: Bool,
|
||||||
pteExecute :: Bool,
|
pteDevice :: Bool,
|
||||||
pteRights :: VMRights }
|
pteRights :: VMRights }
|
||||||
| PageTablePTE {
|
| PageTablePTE {
|
||||||
ptePPN :: PAddr,
|
pteBaseAddress :: PAddr }
|
||||||
pteGlobal :: Bool,
|
|
||||||
pteUser :: Bool }
|
|
||||||
deriving (Show, Eq)
|
deriving (Show, Eq)
|
||||||
|
|
||||||
{- Simulator callbacks -}
|
{- Simulator callbacks -}
|
||||||
|
|
Loading…
Reference in New Issue