blob: ee65990c1f4f4d61fed2536393515a1da10e2b9e [file] [log] [blame] [edit]
--
-- Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
--
-- SPDX-License-Identifier: BSD-2-Clause
--
module CapDL.State where
import CapDL.Model
import Prelude ()
import Prelude.Compat
import Control.Monad.State
import Control.Monad.Writer
import Data.Maybe
import Data.List.Compat
import qualified Data.Map as Map
import qualified Data.Set as Set
import Text.PrettyPrint (Doc, text)
import Control.Lens.Extras (is)
type Kernel a = State (Model Word) a
maybeObject :: ObjID -> Model Word -> Maybe (KernelObject Word)
maybeObject ref = Map.lookup ref . objects
isObject :: ObjID -> Model Word -> Bool
isObject ref = isJust . maybeObject ref
object :: ObjID -> Model Word -> KernelObject Word
object ref m =
case maybeObject ref m of
Just obj -> obj
Nothing -> error $ "Object does not exist: " ++ show ref
modifyObject :: ObjID -> (KernelObject Word -> KernelObject Word) -> Kernel ()
modifyObject ref f =
modify (\s -> s { objects = Map.update (Just . f) ref (objects s) })
setObject :: ObjID -> KernelObject Word -> Kernel ()
setObject ref obj = modifyObject ref (const obj)
objSlots :: KernelObject Word -> CapMap Word
objSlots obj =
if hasSlots obj then slots obj
else Map.empty
slotsOfMaybe :: Maybe (KernelObject Word) -> CapMap Word
slotsOfMaybe = maybe Map.empty objSlots
slotsOf :: ObjID -> Model Word -> CapMap Word
slotsOf ref = slotsOfMaybe . maybeObject ref
getCovered :: ObjID -> Model Word -> [ObjID]
getCovered ut_ref = getUTCover ut_ref . untypedCovers
setCovered :: ObjID -> [ObjID] -> Kernel ()
setCovered ut_ref covers =
modify (\s -> s { untypedCovers = Map.insert ut_ref covers (untypedCovers s) })
type SlotsLookup = ObjID -> Model Word -> CapMap Word
cNodeSlots :: SlotsLookup
cNodeSlots ref m = case maybeObject ref m of
Just (CNode slots _ _) -> slots
_ -> Map.empty
pdSlots :: SlotsLookup
pdSlots ref m = case maybeObject ref m of
Just (PD slots) -> slots
Just (PT slots) -> slots
_ -> Map.empty
ioptSlots :: SlotsLookup
ioptSlots ref m = case maybeObject ref m of
Just (IOPT slots _) -> slots
_ -> Map.empty
maybeObjID :: Cap -> Maybe ObjID
maybeObjID cap = if hasObjID cap then Just (objID cap) else Nothing
lookupOf :: Cap -> SlotsLookup -> Model Word -> CapMap Word
lookupOf cap lookup = maybe (const Map.empty) lookup (maybeObjID cap)
type CapLookup = [([Word], Cap)]
singleton :: a -> [a]
singleton x = [x]
mapKeys :: (a -> b) -> [(a,c)] -> [(b,c)]
mapKeys f = map (\(a,b) -> (f a,b))
mapVals :: (b -> c) -> [(a,b)] -> [(a,c)]
mapVals f = map (\(a,b) -> (a,f b))
mapWithKey :: (a -> b -> c) -> [(a,b)] -> [c]
mapWithKey f = map (\(a,b) -> f a b)
snoc :: [a] -> a -> [a]
snoc xs x = xs ++ [x]
-- FIXME: ignoring guards
nextLookup :: Model Word -> SlotsLookup -> [Word] -> Cap -> CapLookup
nextLookup m lookup ws cap =
Map.toList $ Map.mapKeys (snoc ws) (lookupOf cap lookup m)
-- FIXME: ignoring guards
nextLevel :: Model Word -> CapLookup -> SlotsLookup -> CapLookup
nextLevel m cl lookup = concat (mapWithKey (nextLookup m lookup) cl)
allLevels :: CapLookup -> Int -> SlotsLookup -> Model Word -> CapLookup
allLevels [] _ _ _ = []
allLevels _ 0 _ _ = []
allLevels cl d lookup m =
cl ++ allLevels (nextLevel m cl lookup) (d - 1) lookup m
maxDepth = 4
capSpaceLookup :: CapMap Word -> SlotsLookup -> Model Word -> CapLookup
capSpaceLookup caps lookup =
let firstLevel = Map.toList . Map.mapKeys singleton $ caps
in allLevels firstLevel maxDepth lookup
capSpace :: CapMap Word -> SlotsLookup -> Model Word -> CapMap [Word]
capSpace caps lookup = Map.fromList . capSpaceLookup caps lookup
cspaceCap :: ObjID -> Model Word -> Cap
cspaceCap tcb_ref =
fromJust . Map.lookup tcbCTableSlot . slots . object tcb_ref
cspace :: ObjID -> Model Word -> CapMap [Word]
cspace tcb_ref m =
let cap = cspaceCap tcb_ref m
in capSpace (lookupOf cap cNodeSlots m) cNodeSlots m
capLookup :: [Word] -> ObjID -> Model Word -> Maybe Cap
capLookup ref tcb_ref = Map.lookup ref . cspace tcb_ref
flattenCnode :: KernelObject Word -> Model Word -> (ObjID, KernelObject [Word])
flattenCnode tcb m =
let cnode_id = objID $ fromJust . Map.lookup tcbCTableSlot . slots $ tcb
cnode = object cnode_id m
in (cnode_id, cnode { slots = capSpace (slots cnode) cNodeSlots m })
flattenPD :: KernelObject Word -> Model Word -> KernelObject [Word]
flattenPD obj m =
let newSlots = capSpace (slots obj) pdSlots m
origSlots = Map.mapKeys singleton (slots obj)
in obj { slots = Map.difference newSlots origSlots }
isFrame :: Cap -> Maybe Cap
isFrame cap =
case cap of
FrameCap {} -> Just cap
_ -> Nothing
flattenIOPT :: KernelObject Word -> Model Word -> KernelObject [Word]
flattenIOPT obj m =
let newSlots = capSpace (slots obj) ioptSlots m
newSlots' = foldl (flip (Map.update isFrame)) newSlots $
map fst (Map.toList newSlots)
in obj { slots = newSlots' }
flatten' :: Model Word -> ObjMap [Word] -> (ObjID, KernelObject Word) ->
ObjMap [Word]
flatten' m objs (n, obj@(PD {})) = Map.insert n (flattenPD obj m) objs
flatten' m objs (n, obj@(IOPT _ 1)) = Map.insert n (flattenIOPT obj m) objs
flatten' m objs (n, obj@(TCB {})) =
let tcb = obj { slots = Map.mapKeys singleton (slots obj) }
objs' = Map.insert n tcb objs
(cnode_id, cnode) = flattenCnode obj m
in Map.insert cnode_id cnode objs'
flatten' _ objs (n, obj)
| hasSlots obj =
let obj' = obj { slots = Map.mapKeys singleton (slots obj) }
in Map.insertWith const n obj' objs
| otherwise =
let obj' = case obj of
Endpoint -> Endpoint
Notification -> Notification
Untyped msb paddr-> Untyped msb paddr
Frame vms p f -> Frame vms p f
IOPorts sz -> IOPorts sz
_ -> error "unmatched"
in Map.insert n obj' objs
flatten :: Model Word -> Model [Word]
flatten m@(Model arch objs irqNode cdt untypedCovers) =
Model arch (foldl (flatten' m) Map.empty (Map.toList objs)) irqNode cdt untypedCovers
-- slots of one object
getSlots :: ObjID -> Kernel (CapMap Word)
getSlots ref = gets $ slotsOf ref
-- Caps
maybeSlotCap :: CapRef -> Model Word -> Maybe Cap
maybeSlotCap (ref,slot) = Map.lookup slot . slotsOf ref
slotCap :: CapRef -> Model Word -> Cap
slotCap cref = fromJust . maybeSlotCap cref
setSlots :: ObjID -> CapMap Word -> Kernel ()
setSlots ref sl = do
obj <- gets $ object ref
when (hasSlots obj) $ setObject ref $ obj {slots = sl}
setCap :: CapRef -> Cap -> Kernel ()
setCap (ref,slot) cap = do
slots <- gets $ slotsOf ref
case Map.lookup slot slots of
Just NullCap -> setSlots ref $ Map.insert slot cap slots
Nothing -> setSlots ref $ Map.insert slot cap slots
_ -> error ("Slot already filled: slot " ++ show slot ++
" in " ++ show ref)
removeCap :: CapRef -> Kernel ()
removeCap (ref,slot) = do
slots <- gets $ slotsOf ref
setSlots ref $ Map.delete slot slots
allObjs :: Model Word -> [(ObjID,KernelObject Word)]
allObjs = Map.toList . objects
refSlots :: (ObjID,KernelObject Word) -> [(CapRef, Cap)]
refSlots (ref,obj) =
map (\(slot,cap) -> ((ref,slot),cap)) $ Map.toList $ objSlots obj
allSlots :: Model Word -> [(CapRef,Cap)]
allSlots = concatMap refSlots . allObjs
findSlotCapsWithRef :: (CapRef -> Cap -> Bool) -> Model Word -> [(CapRef, Cap)]
findSlotCapsWithRef p = filter (uncurry p) . allSlots
findSlotCaps :: (Cap -> Bool) -> Model Word -> [(CapRef, Cap)]
findSlotCaps p = findSlotCapsWithRef (\_ -> p)
findSlots :: (Cap -> Bool) -> Model Word -> [CapRef]
findSlots p = map fst . findSlotCaps p
hasTarget :: ObjID -> Cap -> Bool
hasTarget ref cap = hasObjID cap && objID cap == ref
-- validity
type Logger a = Writer Doc a
sameID :: ObjID -> Cap -> Bool
sameID id cap = hasObjID cap && id == objID cap
isMapped :: ObjID -> KernelObject Word -> Bool
isMapped id obj
| hasSlots obj = any (sameID id) $ Map.elems (slots obj)
| otherwise = False
allMappings :: ObjID -> Model Word -> [(ObjID, KernelObject Word)]
allMappings id m = filter (isMapped id . snd) (allObjs m)
isASID :: KernelObject Word -> Bool
isASID (ASIDPool {}) = True
isASID _ = False
uniqueASID :: ObjID -> Model Word -> Bool
uniqueASID id m =
let mappings = filter (isASID . snd) (allMappings id m)
in length mappings <= 1
checkASID :: ObjID -> Model Word -> Logger Bool
checkASID id m = do
let valid = uniqueASID id m
unless valid (tell $ text $ show id ++ " is mapped into multiple ASID's\n")
return valid
isPD :: KernelObject Word -> Bool
isPD (PD {}) = True
isPD _ = False
uniquePD :: ObjID -> Model Word -> Bool
uniquePD id m =
let mappings = filter (isPD . snd) (allMappings id m)
in length mappings <= 1
checkPD :: ObjID -> Model Word -> Logger Bool
checkPD id m = do
let valid = uniquePD id m
unless valid (tell $ text $ show id ++ " is mapped into multiple PD's\n")
return valid
checkMapping :: Model Word -> (ObjID, KernelObject Word) -> Logger Bool
checkMapping m (id, obj) =
case obj of
PD {} -> checkASID id m
PT {} -> checkPD id m
_ -> return True
checkMappings :: Model Word -> Logger Bool
checkMappings m = do
let objs = allObjs m
allM (checkMapping m) objs
koType :: KernelObject Word -> KOType
koType Endpoint = Endpoint_T
koType Notification = Notification_T
koType (TCB {}) = TCB_T
koType (CNode {}) = CNode_T
koType (Untyped {}) = Untyped_T
koType (ASIDPool {}) = ASIDPool_T
koType (PML4 {}) = PML4_T
koType (PDPT {}) = PDPT_T
koType (PD {}) = PD_T
koType (PT {}) = PT_T
koType (PGD {}) = PGD_T
koType (PUD {}) = PUD_T
koType (Frame {}) = Frame_T
koType (IOPorts {}) = IOPorts_T
koType (IODevice {}) = IODevice_T
koType (ARMIODevice {}) = ARMIODevice_T
koType (IOPT {}) = IOPT_T
koType (VCPU {}) = VCPU_T
koType (SC {}) = SC_T
koType (RTReply {}) = RTReply_T
koType (IOAPICIrq {}) = IOAPICIrqSlot_T
koType (MSIIrq {}) = MSIIrqSlot_T
koType (ARMIrq {}) = ARMIrqSlot_T
koType (ARMSID {}) = ARMSID_T
koType (ARMCB {}) = ARMCB_T
objAt :: (KernelObject Word -> Bool) -> ObjID -> Model Word -> Bool
objAt p ref = maybe False p . maybeObject ref
typAt :: KOType -> ObjID -> Model Word -> Bool
typAt t = objAt $ (==) t . koType
capTyp :: Cap -> KOType
capTyp (UntypedCap {}) = Untyped_T
capTyp (EndpointCap {}) = Endpoint_T
capTyp (NotificationCap {}) = Notification_T
capTyp (ReplyCap {}) = TCB_T
capTyp (MasterReplyCap {}) = TCB_T
capTyp (CNodeCap {}) = CNode_T
capTyp (TCBCap {}) = TCB_T
capTyp (IRQHandlerCap {}) = CNode_T
capTyp (FrameCap {}) = Frame_T
capTyp (PTCap {}) = PT_T
capTyp (PDCap {}) = PD_T
capTyp (PML4Cap {} ) = PML4_T
capTyp (PDPTCap {} ) = PDPT_T
capTyp (PUDCap {} ) = PUD_T
capTyp (PGDCap {} ) = PGD_T
capTyp (ASIDPoolCap {}) = ASIDPool_T
capTyp (IOPortsCap {}) = IOPorts_T
capTyp (IOSpaceCap {}) = IODevice_T
capTyp (ARMIOSpaceCap {}) = ARMIODevice_T
capTyp (IOPTCap {}) = IOPT_T
capTyp (VCPUCap {}) = VCPU_T
capTyp (SCCap {}) = SC_T
capTyp (RTReplyCap {}) = RTReply_T
capTyp (IRQIOAPICHandlerCap {}) = IOAPICIrqSlot_T
capTyp (IRQMSIHandlerCap {}) = MSIIrqSlot_T
capTyp (ARMIRQHandlerCap {}) = ARMIrqSlot_T
capTyp (ARMSIDCap {}) = ARMSID_T
capTyp (ARMCBCap {}) = ARMCB_T
capTyp _ = error "cap has no object"
checkTypAt :: Cap -> Model Word -> ObjID -> Word -> Logger Bool
checkTypAt cap m contID slot = do
let valid = not (hasObjID cap) || typAt (capTyp cap) (objID cap) m
unless valid (tell $ text $ "The cap at slot " ++ show slot ++ " in " ++
show contID ++ " refers to an object of the wrong type\n")
--FIXME:Needs better error message
return valid
validCapArch :: Arch -> Cap -> Bool
validCapArch _ NullCap = True
validCapArch _ (UntypedCap {}) = True
validCapArch _ (EndpointCap {}) = True
validCapArch _ (NotificationCap {}) = True
validCapArch _ (ReplyCap {}) = True
validCapArch _ (MasterReplyCap {}) = True
validCapArch _ (CNodeCap {}) = True
validCapArch _ (TCBCap {}) = True
validCapArch _ IRQControlCap = True
validCapArch _ (IRQHandlerCap {}) = True
validCapArch _ (DomainCap {}) = True
validCapArch _ (FrameCap {}) = True
validCapArch _ (PTCap {}) = True
validCapArch _ (PDCap {}) = True
validCapArch _ (ASIDPoolCap {}) = True
validCapArch _ ASIDControlCap = True
validCapArch _ (SCCap {}) = True
validCapArch _ (RTReplyCap {}) = True
validCapArch _ (SchedControlCap {}) = True
validCapArch RISCV (VCPUCap {}) = False
validCapArch _ (VCPUCap {}) = True
validCapArch IA32 (IRQIOAPICHandlerCap {}) = True
validCapArch IA32 (IRQMSIHandlerCap {}) = True
validCapArch IA32 (IOPortsCap {}) = True
validCapArch IA32 IOSpaceMasterCap = True
validCapArch IA32 (IOSpaceCap {}) = True
validCapArch IA32 (IOPTCap {}) = True
validCapArch X86_64 (PML4Cap {}) = True
validCapArch X86_64 (PDPTCap {}) = True
validCapArch X86_64 (IRQIOAPICHandlerCap {}) = True
validCapArch X86_64 (IRQMSIHandlerCap {}) = True
validCapArch X86_64 (IOPortsCap {}) = True
validCapArch X86_64 IOSpaceMasterCap = True
validCapArch X86_64 (IOSpaceCap {}) = True
validCapArch X86_64 (IOPTCap {}) = True
validCapArch ARM11 (ARMIOSpaceCap {}) = True
validCapArch ARM11 (ARMIRQHandlerCap {}) = True
validCapArch AARCH64 (ARMIRQHandlerCap {}) = True
validCapArch AARCH64 (PUDCap {}) = True
validCapArch AARCH64 (PGDCap {}) = True
validCapArch AARCH64 (ARMSIDCap {}) = True
validCapArch AARCH64 (ARMCBCap {}) = True
validCapArch _ _ = False
checkCapArch :: Arch -> Cap -> ObjID -> Word -> Logger Bool
checkCapArch arch cap contID slot = do
let valid = validCapArch arch cap
unless valid (tell $ text $ "The cap at slot " ++ show slot ++ " in " ++
show contID ++ " is not valid for this architecture\n")
return valid
checkValidCap :: Arch -> ObjID -> Model Word -> (Word, Cap) -> Logger Bool
checkValidCap arch contID m (slot, cap) = do
capArch <- checkCapArch arch cap contID slot
typ <- checkTypAt cap m contID slot
return $ capArch && typ
checkValidCaps :: Arch -> KernelObject Word -> ObjID -> Model Word -> Logger Bool
checkValidCaps arch obj id m =
allM (checkValidCap arch id m) (Map.toList $ objSlots obj)
allM f xs = do
bs <- mapM f xs
return $ and bs
validObjArch :: Arch -> KernelObject Word -> Bool
validObjArch _ Endpoint = True
validObjArch _ Notification = True
validObjArch _ (TCB {}) = True
validObjArch _ (CNode {}) = True
validObjArch _ (Untyped {}) = True
validObjArch _ (ASIDPool {}) = True
validObjArch _ (PD {}) = True
validObjArch _ (PT {}) = True
validObjArch _ (Frame {}) = True
validObjArch _ (SC {}) = True
validObjArch _ (RTReply {}) = True
validObjArch RISCV (VCPU {}) = False
validObjArch _ (VCPU {}) = True
validObjArch ARM11 (ARMIODevice {}) = True
validObjArch ARM11 (ARMIrq {}) = True
validObjArch IA32 (IOPorts {}) = True
validObjArch IA32 (IODevice {}) = True
validObjArch IA32 (IOPT {}) = True
validObjArch IA32 (IOAPICIrq {}) = True
validObjArch IA32 (MSIIrq {}) = True
validObjArch X86_64 (PML4 {}) = True
validObjArch X86_64 (PDPT {}) = True
validObjArch X86_64 (IOPorts {}) = True
validObjArch X86_64 (IODevice {}) = True
validObjArch X86_64 (IOPT {}) = True
validObjArch X86_64 (IOAPICIrq {}) = True
validObjArch X86_64 (MSIIrq {}) = True
validObjArch AARCH64 (ARMIrq {}) = True
validObjArch AARCH64 (PUD {}) = True
validObjArch AARCH64 (PGD {}) = True
validObjArch AARCH64 (ARMSID {}) = True
validObjArch AARCH64 (ARMCB {}) = True
validObjArch _ _ = False
checkObjArch :: Arch -> KernelObject Word -> ObjID -> Logger Bool
checkObjArch arch obj id = do
let valid = validObjArch arch obj
unless valid
(tell $ text $ show id ++ " is not a valid object for this architecture\n")
return valid
validTCBSlotCap :: Arch -> Word -> Cap -> Bool
validTCBSlotCap arch slot cap
| slot == tcbCTableSlot = is _CNodeCap cap
| slot == tcbVTableSlot
= case arch of
IA32 -> is _PDCap cap
ARM11 -> is _PDCap cap
X86_64 -> is _PML4Cap cap
AARCH64 -> is _PGDCap cap || is _PUDCap cap
RISCV -> is _PTCap cap
| slot == tcbReplySlot = is _MasterReplyCap cap
| slot == tcbCallerSlot = is _ReplyCap cap
| slot == tcbIPCBufferSlot = is _FrameCap cap
| slot == tcbFaultEPSlot = is _EndpointCap cap
| slot == tcbSCSlot = is _SCCap cap
| slot == tcbTempFaultEPSlot = is _EndpointCap cap
| slot == tcbBoundVCPUSlot = arch /= RISCV && is _VCPUCap cap
| otherwise = cap == NullCap
validObjCap :: Arch -> KernelObject Word -> Word -> Cap -> Bool
validObjCap arch (TCB {}) slot cap = validTCBSlotCap arch slot cap
validObjCap _ (CNode _ 0 _) slot cap = slot == 0 && is _NotificationCap cap -- FIXME: we should add a separate IRQObject
validObjCap _ (CNode {}) _ _ = True
validObjCap _ (ASIDPool {}) _ cap = is _PDCap cap
validObjCap RISCV (PT {}) _ cap = is _FrameCap cap || is _PTCap cap
validObjCap _ (PT {}) _ cap = is _FrameCap cap
validObjCap _ (PD {}) _ cap = is _FrameCap cap || is _PTCap cap
validObjCap _ (PDPT {}) _ cap = is _FrameCap cap || is _PDCap cap
validObjCap _ (PML4 {}) _ cap = is _PDPTCap cap
validObjCap _ (PUD {}) _ cap = is _FrameCap cap || is _PDCap cap
validObjCap _ (PGD {}) _ cap = is _PUDCap cap
validObjCap _ (ARMIODevice {}) _ _ = True -- FIXME: we should check IODevices properly
validObjCap _ (ARMIrq {}) slot cap = slot == 0 && is _NotificationCap cap
validObjCap _ (IODevice {}) _ _ = True -- FIXME: we should check IODevices properly
validObjCap _ (IOPT {}) _ cap = is _FrameCap cap || is _IOPTCap cap
validObjCap _ (IOAPICIrq {}) slot cap = slot == 0 && is _NotificationCap cap
validObjCap _ (MSIIrq {}) slot cap = slot == 0 && is _NotificationCap cap
validObjCap _ _ _ _ = False
checkValidSlot :: Arch -> KernelObject Word -> ObjID -> (Word, Cap) -> Logger Bool
checkValidSlot arch obj contID (slot, cap) = do
let valid = validObjCap arch obj slot cap
unless valid (tell $ text $ "The cap at slot " ++ show slot ++ " in " ++
show contID ++ " is not valid for this object\n")
return valid
checkValidSlots :: Arch -> KernelObject Word -> ObjID -> Logger Bool
checkValidSlots arch obj id =
allM (checkValidSlot arch obj id) (Map.toList $ objSlots obj)
-- FIXME: we should check that all irq objects are in the irqNode
checkObj :: Arch -> Model Word -> (ObjID, KernelObject Word) -> Logger Bool
checkObj arch m (id, obj) = do
objArch <- checkObjArch arch obj id
caps <- checkValidCaps arch obj id m
slots <- checkValidSlots arch obj id
return $ objArch && caps && slots
checkObjs :: Arch -> Model Word -> Logger Bool
checkObjs arch m = do
let objs = allObjs m
allM (checkObj arch m) objs
allCovers :: Model Word -> [[ObjID]]
allCovers (Model _ _ _ _ covMap) =
let covers = map snd (Map.toList covMap)
in filter (not . null) covers
nullIntersections :: Ord a => [Set.Set a] -> Bool
nullIntersections [] = True
nullIntersections (x:xs) = check x xs
where check _ [] = True
check seen (x:xs) =
Set.null (x `Set.intersection` seen) && check (Set.union x seen) xs
checkUntyped :: Model Word -> ObjID -> Logger Bool
checkUntyped m ref = do
let valid =
case object ref m of
Untyped _ _ -> True
_ -> False
unless valid
(tell $ text $ show ref ++ " covers objects but is not an untyped\n")
return valid
checkUntypeds :: Model Word -> Logger Bool
checkUntypeds m@(Model _ _ _ _ covMap) = do
let objs = map fst (Map.toList covMap)
allM (checkUntyped m) objs
checkUntypedCover :: Model Word -> (ObjID, [ObjID]) -> Logger Bool
checkUntypedCover m (id, objs) = do
let valid = allM (objAt (const True)) objs m
unless valid
(tell $ text $ show id ++ " covers a non-existant object\n")
return valid
checkUntypedCovers :: Model Word -> Logger Bool
checkUntypedCovers m =
allM (checkUntypedCover m) $ Map.toList $ untypedCovers m
checkCovers :: Model Word -> Logger Bool
checkCovers m = do
let valid = nullIntersections $ map Set.fromList $ allCovers m
unless valid (tell $ text $ "At least two untypeds have intersecting covers\n")
untypeds <- checkUntypeds m
covers <- checkUntypedCovers m
return $ valid && covers && untypeds
isIRQ :: KernelObject Word -> Bool
isIRQ (CNode _ 0 _) = True --check irqNode
isIRQ IOAPICIrq{} = True
isIRQ MSIIrq{} = True
isIRQ ARMIrq{} = True
isIRQ _ = False
validIRQ :: Model Word -> ObjID -> Bool
validIRQ m irq = isIRQ $ object irq m
checkIRQ :: Model Word -> (Word, ObjID) -> Logger Bool
checkIRQ m (slot, irq) = do
let valid = validIRQ m irq
unless valid (tell $ text $ "The object mapped by irq " ++ show slot ++ --FIXME:rewrite
" in the irqNode is not a valid irq_slot\n")
return valid
checkIRQNode :: Model Word -> Logger Bool
checkIRQNode m = allM (checkIRQ m) (Map.toList $ irqNode m)
flattenCNodeSlots :: [(ObjID, KernelObject Word)] -> [Cap]
flattenCNodeSlots [] = []
flattenCNodeSlots ((_, CNode slots _ _) : xs) = map snd (Map.toList slots) ++ flattenCNodeSlots xs
flattenCNodeSlots (_ : xs) = flattenCNodeSlots xs
isMappedFrameCap :: Cap -> Bool
isMappedFrameCap (FrameCap _ _ _ _ (Just _)) = True
isMappedFrameCap _ = False
-- Returns list containing each element in argument list occuring more than once.
-- There are no duplicates in the returned list.
findDuplicates :: Ord a => [a] -> [a]
findDuplicates = map head . filter ((>1) . length) . groupBy (==) . sort
-- Checks that each mapping is specified by at most 1 frame cap
checkDuplicateMappedFrameCaps :: [(ObjID, Word)] -> Logger Bool
checkDuplicateMappedFrameCaps mappings = do
let duplicates = findDuplicates mappings
let valid = null duplicates
showCapSlot ((container, _), slot) = container ++ ", slot " ++ show slot
unless valid (tell $ text $ "Mappings referenced by multiple frame caps:\n" ++
(intercalate "\n" $ map showCapSlot duplicates)
++ "\n")
return valid
checkMappingSlotSanity :: CapMap Word -> Word -> Logger Bool
checkMappingSlotSanity slots slot =
let cap = Map.lookup slot slots
in case cap of
Just (FrameCap {}) -> return True
Just _ -> do
tell $ text "Frame references mapping to object other than a frame\n"
return False
Nothing -> do
tell $ text "Frame references mapping in empty or non-existent slot\n"
return False
checkMappingSanity :: (ObjID, KernelObject Word, Word) -> Logger Bool
checkMappingSanity (id, obj, slot) =
case obj of
PT slots -> checkMappingSlotSanity slots slot
PD slots -> checkMappingSlotSanity slots slot
_ -> do
tell $ text $ "Object specified in mapping(" ++ fst id ++ ", " ++ show slot ++ ") is neither page table nor page directory\n"
return False
checkMappedFrameCapsSanity :: Model Word -> [(ObjID, Word)] -> Logger Bool
checkMappedFrameCapsSanity m mappings = do
let object_mappings = map (\(objID, slot) -> (objID, object objID m, slot)) mappings
allM checkMappingSanity object_mappings
isCNode :: KernelObject Word -> Bool
isCNode (CNode _ _ _) = True
isCNode _ = False
getSlotsFromKernelObject :: KernelObject Word -> Maybe (CapMap Word)
getSlotsFromKernelObject (TCB slots _ _ _ _) = Just slots
getSlotsFromKernelObject (CNode slots _ _) = Just slots
getSlotsFromKernelObject (ASIDPool slots _) = Just slots
getSlotsFromKernelObject (PT slots) = Just slots
getSlotsFromKernelObject (PD slots) = Just slots
getSlotsFromKernelObject (PML4 slots) = Just slots
getSlotsFromKernelObject (PDPT slots) = Just slots
getSlotsFromKernelObject (PUD slots) = Just slots
getSlotsFromKernelObject (PGD slots) = Just slots
getSlotsFromKernelObject (IODevice slots _ _) = Just slots
getSlotsFromKernelObject (ARMIODevice slots _) = Just slots
getSlotsFromKernelObject (IOPT slots _) = Just slots
getSlotsFromKernelObject _ = Nothing
-- Checks that an object contains no frame caps with a specified mapping
checkObjectContainsNoMappedFrameCap :: (ObjID, KernelObject Word) -> Logger Bool
checkObjectContainsNoMappedFrameCap (id, obj) =
let maybe_slots = getSlotsFromKernelObject obj
in case maybe_slots of
Nothing -> return True
Just slots -> do
let valid = all (not . isMappedFrameCap) $ map snd $ Map.toList slots
unless valid $ tell $ text $ "Non-CNode object '" ++ fst id ++ "' contains frame cap with mapping\n"
return valid
checkMappedFrameCapsOnlyInCNodes :: Model Word -> Logger Bool
checkMappedFrameCapsOnlyInCNodes m =
allM checkObjectContainsNoMappedFrameCap $ filter (not . isCNode . snd) $ allObjs m
-- Checks that mappings specified by frame caps refer to valid slots in
-- container objects (page directories or page tables) which contain a
-- mapping to a frame.
checkMappedFrameCaps :: Model Word -> Logger Bool
checkMappedFrameCaps m = do
let mappings = map (\(FrameCap _ _ _ _ (Just x)) -> x) $
filter isMappedFrameCap $ flattenCNodeSlots $ allObjs m
no_duplicates <- checkDuplicateMappedFrameCaps mappings
sane_mappings <- checkMappedFrameCapsSanity m mappings
only_in_cnodes <- checkMappedFrameCapsOnlyInCNodes m
return $ no_duplicates && sane_mappings && only_in_cnodes
checkModel :: Model Word -> Logger Bool
checkModel m = do
objs <- checkObjs (arch m) m
covers <- checkCovers m
mappings <- checkMappings m
irq <- checkIRQNode m
mapped_frame_caps <- checkMappedFrameCaps m
tell $ text ""
return $ objs && covers && mappings && irq && mapped_frame_caps
-- Utils for getting object sizes
lookupSizeMap :: KOType -> ObjectSizeMap -> Word
lookupSizeMap k objSizeMap = case Map.lookup k objSizeMap of
Just v -> v
Nothing -> error $ "missing size info for type: " ++ show k
objSizeBits :: ObjectSizeMap -> KernelObject Word -> Word
-- 0-size CNodes are actually IRQ slots, and have no allocation size
objSizeBits _ (CNode { sizeBits = 0}) = 0
-- variable sized objects
objSizeBits _ (Untyped { maybeSizeBits = Just b }) = b
objSizeBits _ (Frame { vmSizeBits = b }) = b
objSizeBits objSizeMap (CNode { sizeBits = b }) =
b + lookupSizeMap CNode_T objSizeMap
objSizeBits _ (SC { maybeSizeBits = Just b }) = b
-- everything else
objSizeBits objSizeMap ko =
lookupSizeMap (koType ko) objSizeMap
{-
- Check that all objects belong to an untyped cover and all untyped
- covers have paddrs. This is required for generating Isabelle output.
-}
isFullyAllocated :: ObjectSizeMap -> ObjMap Word -> CoverMap -> Either (String, [ObjID]) ()
isFullyAllocated sizeMap objs untypedCovers =
do
-- check untypeds
let missingPaddrs = [ utID | utID <- Map.keys untypedCovers
, isNothing $ maybePaddr $ objs Map.! utID ]
unless (null missingPaddrs) $
Left ("Untyped(s) have children but don't have paddrs", missingPaddrs)
-- check objects
let coveredObjs = Set.fromList $ concat $ Map.elems untypedCovers
missingObjs = [ objID | (objID, obj) <- Map.toList objs
, objID `Map.notMember` untypedCovers
, objSizeBits sizeMap obj /= 0
, objID `Set.notMember` coveredObjs ]
unless (null missingObjs) $
Left ("Object(s) are not allocated from any untyped", missingObjs)