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