1% 2% Copyright 2014, General Dynamics C4 Systems 3% 4% This software may be distributed and modified according to the terms of 5% the GNU General Public License version 2. Note that NO WARRANTY is provided. 6% See "LICENSE_GPLv2.txt" for details. 7% 8% @TAG(GD_GPL) 9% 10 11This module contains the data structure and operations for the physical memory model. 12 13> module SEL4.Model.PSpace ( 14> PSpace, newPSpace, initPSpace, 15> PSpaceStorable, 16> objBits, injectKO, projectKO, makeObject, loadObject, updateObject, 17> getObject, setObject, deleteObjects, reserveFrame, 18> typeError, alignError, alignCheck, sizeCheck, 19> loadWordUser, storeWordUser, placeNewObject, 20> ) where 21 22\begin{impdetails} 23 24% {-# BOOT-IMPORTS: Data.Map SEL4.Object.Structures SEL4.Machine.RegisterSet #-} 25% {-# BOOT-EXPORTS: PSpace #PRegion newPSpace #-} 26 27> import Prelude hiding (Word) 28> import SEL4.Model.StateData 29> import SEL4.Object.Structures 30 31> import qualified Data.Map 32> import Data.Bits 33> import Data.List 34> import SEL4.Machine.RegisterSet 35> import SEL4.Machine.Hardware 36 37 38\end{impdetails} 39 40\subsection{Physical Address Space} 41 42The physical address space is represented by a map from physical addresses to objects. The objects themselves are wrapped in the "KernelObject" type. 43 44> newtype PSpace = PSpace { psMap :: Data.Map.Map Word KernelObject } 45 46\subsection{Storable Objects} 47 48The type class "PSpaceStorable" defines a set of operations that may be performed on any object that is storable in physical memory. 49 50> class PSpaceStorable a where 51 52\begin{impdetails} 53For a \emph{pure} kernel object --- one which is only accessed by the kernel itself, and may therefore be stored in the Haskell "PSpace" structure --- it is usually sufficient to define the "objBits", "injectKO" and "projectKO" functions for an instance of this class. 54 55Some kernel objects, such as capability table entries ("CTE"s), may be stored either alone in the "PSpace" or encapsulated inside another kernel object. Instances for such objects must override the default "loadObject" and "updateObject" definitions, as "injectKO" and "projectKO" are not sufficient. 56 57Objects such as virtual memory pages or hardware-defined page tables must be accessed in the hardware monad. Instances for these objects must override "getObject" and "setObject". 58 59All instances must either define "injectKO" and "makeObject" or override "placeNewObject" to allow new objects to be created. 60\end{impdetails} 61 62\subsubsection{Object Properties} 63 64The size and alignment of the physical region occupied by objects of type "a". This is the logarithm base 2 of the object's size (i.e., the number of low-order bits of the physical address that are not necessary to locate the object). 65 66The default contents of a kernel object of this type. 67 68> makeObject :: a 69 70\subsubsection{Storing Objects in PSpace} 71 72The "loadObject" and "updateObject" functions are used to insert or extract an object from a "KernelObject" wrapper, given any remaining unresolved physical address bits. Normally these bits must all be zero, and the number of bits must equal the result of "objBits"; this ensures that the alignment is correct. 73 74\begin{impdetails} 75The default definitions are sufficient for most kernel objects. There is one exception in the platform-independent code, for "CTE" objects; it can be found in \autoref{sec:object.instances}. 76\end{impdetails} 77 78> loadObject :: (Monad m) => Word -> Word -> Maybe Word -> 79> KernelObject -> m a 80> loadObject ptr ptr' next obj = do 81> unless (ptr == ptr') $ fail $ "no object at address given in pspace,target=" ++ (show ptr) ++",lookup=" ++ (show ptr') 82> val <- projectKO obj 83> alignCheck ptr (objBits val) 84> sizeCheck ptr next (objBits val) 85> return val 86 87> updateObject :: (Monad m) => a -> KernelObject -> Word -> 88> Word -> Maybe Word -> m KernelObject 89> updateObject val oldObj ptr ptr' next = do 90> unless (ptr == ptr') $ fail $ "no object at address given in pspace,target=" ++ (show ptr) ++",lookup=" ++ (show ptr') 91> liftM (asTypeOf val) $ projectKO oldObj -- for the type error 92> alignCheck ptr (objBits val) 93> sizeCheck ptr next (objBits val) 94> return (injectKO val) 95 96The "injectKO" and "projectKO" functions convert to and from a "KernelObject", which is the type used to encapsulate all objects stored in the "PSpace" structure. 97 98> injectKO :: a -> KernelObject 99> projectKO :: (Monad m) => KernelObject -> m a 100 101> objBits :: PSpaceStorable a => a -> Int 102> objBits a = objBitsKO (injectKO a) 103 104\subsection{Functions} 105 106\subsubsection{Initialisation} 107 108A new physical address space has an empty object map. 109 110> newPSpace :: PSpace 111> newPSpace = PSpace { psMap = Data.Map.empty } 112 113The "initPSpace" function currently does nothing. In earlier versions of the Haskell model, it was used to configure the "PSpace" model to signal a bus error if an invalid physical address was accessed. This is useful only for debugging of the Haskell model, and is not strictly necessary; it has no equivalent in a real implementation. 114% FIXME maybe check that the arguments are OK 115 116> initPSpace :: [(PPtr (), PPtr ())] -> Kernel () 117> initPSpace _ = return () 118 119\subsubsection{Accessing Objects} 120 121Given a pointer into physical memory, an attempt may be made to fetch 122or update an object of any storable type from the address space. The caller is 123assumed to have checked that the address is correctly aligned for the 124requested object type and that it actually contains an object of the 125requested type. 126 127> getObject :: PSpaceStorable a => PPtr a -> Kernel a 128> getObject ptr = do 129> map <- gets $ psMap . ksPSpace 130> let (before, after) = lookupAround2 (fromPPtr ptr) map 131> (ptr', val) <- maybeToMonad before 132> loadObject (fromPPtr ptr) ptr' after val 133 134> setObject :: PSpaceStorable a => PPtr a -> a -> Kernel () 135> setObject ptr val = do 136> ps <- gets ksPSpace 137> let map = psMap ps 138> let (before, after) = lookupAround2 (fromPPtr ptr) map 139> (ptr', obj) <- maybeToMonad before 140> obj' <- updateObject val obj (fromPPtr ptr) ptr' after 141> let map' = Data.Map.insert ptr' obj' map 142> let ps' = ps { psMap = map' } 143> modify (\ks -> ks { ksPSpace = ps'}) 144 145> lookupAround :: Ord k => k -> Data.Map.Map k a -> 146> (Maybe (k, a), Maybe a, Maybe (k, a)) 147> lookupAround ptr map = (nullProtect Data.Map.findMax before, 148> at, nullProtect Data.Map.findMin after) 149> where 150> (before, at, after) = Data.Map.splitLookup ptr map 151> nullProtect f m 152> | Data.Map.null m = Nothing 153> | otherwise = Just (f m) 154 155> lookupAround2 :: Ord k => k -> Data.Map.Map k a -> (Maybe (k, a), Maybe k) 156> lookupAround2 ptr mp = case middle of 157> Just v -> (Just (ptr, v), after') 158> Nothing -> (before, after') 159> where 160> (before, middle, after) = lookupAround ptr mp 161> after' = maybe Nothing (Just . fst) after 162 163> maybeToMonad :: Monad m => Maybe a -> m a 164> maybeToMonad (Just x) = return x 165> maybeToMonad Nothing = fail "maybeToMonad: got Nothing" 166 167\subsubsection{Creating Objects} 168 169Create a new object, and place it in memory. Some objects (such as page 170directories) are actually arrays of smaller objects. To handle these cases, the 171function `placeNewObject' accepts an argument allowing multiple objects of the 172same type to be created as a group. For standard object types, this argument 173will always be set to create only a single object. 174 175The arguments to "placeNewObject" are a pointer to the start of the region 176(which must be aligned to the object's size); the value used to initialise the 177created objects; and the number of copies that will be created (the input 178represent as a power-of-two). 179 180> placeNewObject :: PSpaceStorable a => PPtr () -> a -> Int -> Kernel () 181> placeNewObject ptr val groupSizeBits = 182> placeNewObject' ptr (injectKO val) groupSizeBits 183> 184> placeNewObject' :: PPtr () -> KernelObject -> Int -> Kernel () 185> placeNewObject' ptr val groupSizeBits = do 186 187Calculate the size (as a power of two) of the region the new object will be placed in. 188 189> let objSizeBits = objBitsKO val 190> let totalBits = objSizeBits + groupSizeBits 191 192Check the alignment of the specified region. 193 194> unless (fromPPtr ptr .&. mask totalBits == 0) $ 195> alignError totalBits 196 197Fetch the "PSpace" structure from the current state, and search the region for existing objects; fail if any are found. 198 199> ps <- gets ksPSpace 200> let end = fromPPtr ptr + ((1 `shiftL` totalBits) - 1) 201> let (before, _) = lookupAround2 end (psMap ps) 202> case before of 203> Nothing -> return () 204> Just (x, _) -> assert (x < fromPPtr ptr) 205> "Object creation would destroy an existing object" 206 207Make a list of addresses at which to create objects. 208 209> let addresses = map 210> (\n -> fromPPtr ptr + n `shiftL` objSizeBits) 211> [0 .. (1 `shiftL` groupSizeBits) - 1] 212 213Insert the objects into the "PSpace" map. 214 215> let map' = foldr 216> (\addr map -> Data.Map.insert addr val map) 217> (psMap ps) addresses 218 219Update the state with the new "PSpace" map. 220 221> let ps' = ps { psMap = map' } 222> modify (\ks -> ks { ksPSpace = ps'}) 223 224\subsubsection{Deleting Objects} 225 226> deleteRange :: Data.Map.Map Word a -> Word -> Int -> Data.Map.Map Word a 227> deleteRange m pstart bits = 228> let (_,lr) = Data.Map.split (pstart-1) m 229> pend = pstart + 2^bits 230> (mid,_) = Data.Map.split pend lr in 231> foldl' (flip Data.Map.delete) m (Data.Map.keys mid) 232 233No type checks are performed when deleting objects; "deleteObjects" simply deletes every object in the given region. If an object partially overlaps with the given region but is not completely inside it, this function's behaviour is undefined. 234 235> deleteObjects :: PPtr a -> Int -> Kernel () 236> deleteObjects ptr bits = do 237> unless (fromPPtr ptr .&. mask bits == 0) $ 238> alignError bits 239> stateAssert (deletionIsSafe ptr bits) 240> "Object deletion would leave dangling pointers" 241> doMachineOp $ freeMemory (PPtr (fromPPtr ptr)) bits 242> ps <- gets ksPSpace 243> let inRange = (\x -> x .&. ((- mask bits) - 1) == fromPPtr ptr) 244> let map' = deleteRange (psMap ps) (fromPPtr ptr) bits 245> let ps' = ps { psMap = map' } 246> modify (\ks -> ks { ksPSpace = ps'}) 247 248Clear the ghost state for user pages and cnodes within the deleted range. 249 250> modify (\ks -> ks { gsUserPages = (\x -> if inRange x 251> then Nothing else gsUserPages ks x) }) 252> stateAssert (\x -> not (cNodePartialOverlap (gsCNodes x) inRange)) 253> "Object deletion would split CNodes." 254> modify (\ks -> ks { gsCNodes = (\x -> if inRange x 255> then Nothing else gsCNodes ks x) }) 256> stateAssert ksASIDMapSafe "Object deletion would leave dangling PD pointers" 257 258In "deleteObjects" above, we assert "deletionIsSafe"; that is, that there are no pointers to these objects remaining elsewhere in the kernel state. Since we cannot easily check this in the Haskell model, we assume that it is always true; the assertion is strengthened during translation into Isabelle. 259 260> deletionIsSafe :: PPtr a -> Int -> KernelState -> Bool 261> deletionIsSafe _ _ _ = True 262 263We also assert that the ghost CNodes are all either completely deleted or unchanged; no CNode should be partially in the range and partially deleted. Again, this assertion requires logical quantifiers, and is inserted in translation. 264 265> cNodePartialOverlap :: (Word -> Maybe Int) -> (Word -> Bool) -> Bool 266> cNodePartialOverlap _ _ = False 267 268After deletion, we assert ksASIDMapSafe which states that there are page directories at the addresses in the asid map. Again, the real assertion is only inserted in the translation to the theorem prover. 269 270> ksASIDMapSafe :: KernelState -> Bool 271> ksASIDMapSafe _ = True 272 273 274\subsubsection{Reserving Memory Regions} 275 276The "reserveFrame" function marks a page-sized physical region as being in use for kernel or user data. This prevents any new kernel objects being created in these regions, but does not store any real data in the "PSpace". 277 278\begin{impdetails} 279This is intended for use by alternate implementations of "placeNewObject", for objects that are stored outside the "PSpace" structure. 280\end{impdetails} 281 282> reserveFrame :: PPtr a -> Bool -> Kernel () 283> reserveFrame ptr isKernel = do 284> let val = if isKernel then KOKernelData else KOUserData 285> placeNewObject' (PPtr (fromPPtr ptr)) val 0 286> return () 287 288\subsubsection{Access Failures} 289 290These two functions halt the kernel with an error message when a memory access is performed with incorrect type or alignment. 291 292> typeError :: Monad m => String -> KernelObject -> m a 293> typeError t1 t2 = fail ("Wrong object type - expected " ++ t1 ++ 294> ", found " ++ (kernelObjectTypeName t2)) 295 296> alignError :: Monad m => Int -> m a 297> alignError n = fail ("Unaligned access - lowest " ++ 298> (show n) ++ " bits must be 0") 299 300> alignCheck :: Monad m => Word -> Int -> m () 301> alignCheck x n = unless (x .&. mask n == 0) $ alignError n 302 303> sizeCheck :: Monad m => Word -> Maybe Word -> Int -> m () 304> sizeCheck _ Nothing _ = return () 305> sizeCheck start (Just end) n = 306> when (end - start < 1 `shiftL` n) 307> (fail ("Object must be at least 2^" ++ (show n) ++ " bytes long.")) 308 309\subsubsection{Accessing user data} 310 311The following functions are used to access words in user-accessible data pages. They are equivalent to "loadWord" and "storeWord", except that they also assert that the pointer is in a user data page. 312 313> loadWordUser :: PPtr Word -> Kernel Word 314> loadWordUser p = do 315> stateAssert (pointerInUserData p) 316> "loadWordUser needs a user data page" 317> doMachineOp $ loadWord p 318 319> storeWordUser :: PPtr Word -> Word -> Kernel () 320> storeWordUser p w = do 321> stateAssert (pointerInUserData p) 322> "storeWordUser needs a user data page" 323> doMachineOp $ storeWord p w 324 325The following predicate is used above to assert that the pointer is a valid pointer to user data. It is always "True" here, but is replaced with a stronger assertion in the Isabelle translation. % FIXME: this can probably actually be stronger here too 326 327> pointerInUserData :: PPtr Word -> KernelState -> Bool 328> pointerInUserData _ _ = True 329 330 331