| -- | |
| -- Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) | |
| -- | |
| -- SPDX-License-Identifier: BSD-2-Clause | |
| -- | |
| -- Printer for C source format to be consumed by the CapDL initialiser. | |
| -- Note: corresponds to the -c/--code argument. | |
| module CapDL.PrintC where | |
| import CapDL.Model | |
| import CapDL.PrintUtils (sortObjects) | |
| import Prelude () | |
| import Prelude.Compat | |
| import Control.Exception (assert) | |
| import Data.Ord (comparing) | |
| import Data.List.Compat | |
| import Data.List.Utils | |
| import Data.Maybe (fromJust, fromMaybe) | |
| import qualified Data.Map as Map | |
| import Data.Map (Map) | |
| import qualified Data.Set as Set | |
| import Data.Bits | |
| import Numeric (showHex) | |
| import Text.PrettyPrint | |
| -- What we print depends on whether objects are pre-allocated. | |
| data AllocationType = | |
| StaticAlloc | |
| | DynamicAlloc ObjectSizeMap | |
| (+++) :: String -> String -> String | |
| s1 +++ s2 = s1 ++ "\n" ++ s2 | |
| joinBy :: String -> [String] -> String | |
| joinBy = intercalate | |
| hex :: Word -> String | |
| hex x = "0x" ++ showHex x "" | |
| -- This prints paddrs in hex, except that Nothing (no fixed address) | |
| -- is simply "0" for clarity. | |
| pointerOfPAddr :: Maybe Word -> String | |
| pointerOfPAddr p = fromMaybe "0" (hex <$> p) | |
| maxObjects :: Int -> String | |
| maxObjects count = "#define MAX_OBJECTS " ++ show count | |
| memberArch :: Arch -> String | |
| memberArch arch = | |
| "#if !defined(CONFIG_ARCH_" ++ a ++ ")" +++ | |
| "# error \"invalid target architecture; expecting " ++ a ++ "\"" +++ | |
| "#endif" | |
| where | |
| a = case arch of | |
| IA32 -> "IA32" | |
| ARM11 -> "ARM" | |
| X86_64 -> "X86_64" | |
| AARCH64 -> "AARCH64" | |
| RISCV -> "RISCV" | |
| memberNum :: Int -> String | |
| memberNum n = ".num = " ++ show n ++ "," | |
| showObjID :: Map ObjID Int -> ObjID -> String | |
| showObjID xs id = (case Map.lookup id xs of | |
| Just w -> show w | |
| _ -> "INVALID_SLOT") ++ " /* " ++ fst id ++ " */" | |
| rightConst :: Rights -> String | |
| rightConst = ("CDL_Can"++) . show | |
| showRights :: CapRights -> String | |
| showRights rights | |
| | Set.null rights = "0" | |
| | otherwise = | |
| "(" ++ joinBy "|" (rightConst <$> Set.toList rights) ++ ")" | |
| showPCI :: Word -> (Word, Word, Word) -> String | |
| showPCI domainID (pciBus, pciDev, pciFun) = | |
| hex $ shift domainID 16 .|. shift pciBus 8 .|. shift pciDev 3 .|. pciFun | |
| -- Lookup-by-value on a dictionary. I feel like I need a shower. | |
| lookupByValue :: (a -> Bool) -> Map k a -> k | |
| lookupByValue f m = head $ Map.keys $ Map.filter f m | |
| showCap :: Map ObjID Int -> Cap -> IRQMap -> String -> ObjMap Word -> String | |
| showCap _ NullCap _ _ _ = "{.type = CDL_NullCap}" | |
| showCap objs (UntypedCap id) _ _ _ = | |
| "{.type = CDL_UntypedCap, .obj_id = " ++ showObjID objs id ++ "}" | |
| showCap objs (EndpointCap id badge rights) _ is_orig _ = | |
| "{.type = CDL_EPCap, .obj_id = " ++ showObjID objs id ++ | |
| ", .is_orig = " ++ is_orig ++ | |
| ", .rights = " ++ showRights rights ++ | |
| ", .data = { .tag = CDL_CapData_Badge, .badge = " ++ show badge ++ "}}" | |
| showCap objs (NotificationCap id badge rights) _ is_orig _ = | |
| "{.type = CDL_NotificationCap, .obj_id = " ++ showObjID objs id ++ | |
| ", .is_orig = " ++ is_orig ++ | |
| ", .rights = " ++ showRights rights ++ | |
| ", .data = { .tag = CDL_CapData_Badge, .badge = " ++ show badge ++ | |
| "}}" | |
| showCap objs (ReplyCap id) _ _ _ = | |
| "{.type = CDL_ReplyCap, .obj_id = " ++ showObjID objs id ++ "}" | |
| -- XXX: Does it even make sense to give out a reply cap? How does init fake this? | |
| showCap objs (MasterReplyCap id) _ _ _ = | |
| "{.type = CDL_MasterReplyCap, .obj_id = " ++ showObjID objs id ++ "}" | |
| -- XXX: As above. | |
| showCap objs (CNodeCap id guard guard_size) _ is_orig _ = | |
| "{.type = CDL_CNodeCap, .obj_id = " ++ showObjID objs id ++ | |
| ", .is_orig = " ++ is_orig ++ | |
| ", .rights = CDL_AllRights, .data = CDL_CapData_MakeGuard(" ++ | |
| show guard_size ++ ", " ++ show guard ++ ")}" | |
| showCap objs (TCBCap id) _ is_orig _ = | |
| "{.type = CDL_TCBCap, .obj_id = " ++ showObjID objs id ++ | |
| ", .is_orig = " ++ is_orig ++ | |
| ", .rights = CDL_AllRights}" | |
| showCap _ IRQControlCap _ _ _ = "{.type = CDL_IRQControlCap}" | |
| showCap _ DomainCap _ _ _ = "{.type = CDL_DomainCap}" | |
| showCap _ (IRQHandlerCap id) irqNode is_orig _ = | |
| "{.type = CDL_IRQHandlerCap, .obj_id = INVALID_OBJ_ID" ++ | |
| ", .is_orig = " ++ is_orig ++ | |
| ", .irq = " ++ show (lookupByValue (== id) irqNode) ++ "}" | |
| -- Caps have obj_ids, or IRQs, but not both. | |
| showCap _ (IRQIOAPICHandlerCap id) irqNode is_orig _ = | |
| "{.type = CDL_IRQHandlerCap, .obj_id = INVALID_OBJ_ID" ++ | |
| ", .is_orig = " ++ is_orig ++ | |
| ", .irq = " ++ show (lookupByValue (== id) irqNode) ++ "}" | |
| -- Caps have obj_ids, or IRQs, but not both. | |
| showCap _ (IRQMSIHandlerCap id) irqNode is_orig _ = | |
| "{.type = CDL_IRQHandlerCap, .obj_id = INVALID_OBJ_ID" ++ | |
| ", .is_orig = " ++ is_orig ++ | |
| ", .irq = " ++ show (lookupByValue (== id) irqNode) ++ "}" | |
| showCap _ (ARMIRQHandlerCap id) irqNode is_orig _ = | |
| "{.type = CDL_IRQHandlerCap, .obj_id = INVALID_OBJ_ID" ++ | |
| ", .is_orig = " ++ is_orig ++ | |
| ", .irq = " ++ show (lookupByValue (== id) irqNode) ++ "}" | |
| -- Caps have obj_ids, or IRQs, but not both. | |
| showCap objs (FrameCap id rights _ cached maybe_mapping) _ is_orig _ = | |
| "{.type = CDL_FrameCap, .obj_id = " ++ showObjID objs id ++ | |
| ", .is_orig = " ++ is_orig ++ | |
| ", .rights = " ++ showRights rights ++ | |
| ", .vm_attribs = " ++ | |
| (if cached then "seL4_ARCH_Default_VMAttributes" else "CDL_VM_CacheDisabled") ++ | |
| ", .mapping_container_id = " ++ | |
| (case maybe_mapping of | |
| Just (mapping_container, _) -> showObjID objs mapping_container; | |
| _ -> "INVALID_OBJ_ID") ++ | |
| ", .mapping_slot = " ++ | |
| (case maybe_mapping of | |
| Just (_, mapping_slot) -> show mapping_slot; | |
| _ -> "0") ++ | |
| "}" | |
| -- FIXME: I feel like I should be doing something with the ASID data here... | |
| showCap objs (ARMSIDCap id) _ is_orig _ = | |
| "{.type = CDL_SIDCap, .obj_id = " ++ showObjID objs id ++ ", .is_orig = " ++ is_orig ++ "}" | |
| showCap objs (ARMCBCap id) _ is_orig _ = | |
| "{.type = CDL_CBCap, .obj_id = " ++ showObjID objs id ++ ", .is_orig = " ++ is_orig ++ "}" | |
| showCap objs (PTCap id _) _ is_orig _ = | |
| "{.type = CDL_PTCap, .obj_id = " ++ showObjID objs id ++ | |
| ", .is_orig = " ++ is_orig ++ "}" | |
| showCap objs (PDCap id _) _ is_orig _ = | |
| "{.type = CDL_PDCap, .obj_id = " ++ showObjID objs id ++ | |
| ", .is_orig = " ++ is_orig ++ "}" | |
| showCap objs (PDPTCap id _) _ is_orig _ = | |
| "{.type = CDL_PDPTCap, .obj_id = " ++ showObjID objs id ++ | |
| ", .is_orig = " ++ is_orig ++ "}" | |
| showCap objs (PML4Cap id _) _ is_orig _ = | |
| "{.type = CDL_PML4Cap, .obj_id = " ++ showObjID objs id ++ | |
| ", .is_orig = " ++ is_orig ++ "}" | |
| showCap objs (PUDCap id _) _ is_orig _ = | |
| "{.type = CDL_PUDCap, .obj_id = " ++ showObjID objs id ++ | |
| ", .is_orig = " ++ is_orig ++ "}" | |
| showCap objs (PGDCap id _) _ is_orig _ = | |
| "{.type = CDL_PGDCap, .obj_id = " ++ showObjID objs id ++ | |
| ", .is_orig = " ++ is_orig ++ "}" | |
| showCap _ ASIDControlCap _ _ _ = | |
| "{.type = CDL_ASIDControlCap}" | |
| showCap objs (ASIDPoolCap id) _ _ _ = | |
| "{.type = CDL_ASIDPoolCap, .obj_id = " ++ showObjID objs id ++ "}" | |
| showCap objs (IOPortsCap id) _ is_orig _ = | |
| "{.type = CDL_IOPortsCap, .obj_id = " ++ showObjID objs id ++ | |
| ", .is_orig = " ++ is_orig ++ "}" | |
| showCap objs (IOSpaceCap id) _ is_orig ms = | |
| "{.type = CDL_IOSpaceCap, .obj_id = " ++ showObjID objs id ++ | |
| ", .is_orig = " ++ is_orig ++ | |
| ", .data = { .tag = CDL_CapData_Raw, .data = " ++ showPCI dom pci ++ "}}" | |
| where pci = pciDevice $ fromJust $ Map.lookup id ms | |
| dom = domainID $ fromJust $ Map.lookup id ms | |
| showCap objs (ARMIOSpaceCap id) _ is_orig ms = | |
| "{.type = CDL_ARMIOSpaceCap, .obj_id = " ++ showObjID objs id ++ | |
| ", .is_orig = " ++ is_orig ++ | |
| ", .data = { .tag = CDL_CapData_Raw, .data = " ++ show iospace ++ "}}" | |
| where iospace = armiospace $ fromJust $ Map.lookup id ms | |
| showCap objs (VCPUCap id) _ _ _ = "{.type = CDL_VCPUCap, .obj_id = " ++ showObjID objs id ++ "}" | |
| showCap _ (SchedControlCap affinity) _ _ _ = | |
| "{.type = CDL_SchedControlCap, .obj_id = " ++ (hex affinity) ++ " }" | |
| showCap objs (RTReplyCap id rights) _ _ _ = | |
| "{.type = CDL_RTReplyCap, .obj_id = " ++ showObjID objs id ++ | |
| ", .rights = " ++ showRights rights ++ "}" | |
| showCap objs (SCCap id) _ is_orig _ = | |
| "{.type = CDL_SCCap, .obj_id = " ++ showObjID objs id ++ | |
| ", .is_orig = " ++ is_orig ++ "}" | |
| showCap _ x _ _ _ = assert False $ | |
| "UNSUPPORTED CAP TYPE: " ++ show x | |
| -- These are not supported by the initialiser itself. | |
| showSlots :: Map ObjID Int -> ObjID -> [(Word, Cap)] -> IRQMap -> CDT -> ObjMap Word -> String | |
| showSlots _ _ [] _ _ _ = "" | |
| showSlots objs obj_id (x:xs) irqNode cdt ms = | |
| "{" ++ show index ++ ", " ++ slot ++ "}," +++ | |
| showSlots objs obj_id xs irqNode cdt ms | |
| where | |
| index = fst x | |
| slot = showCap objs (snd x) irqNode is_orig ms | |
| is_orig = if Map.notMember (obj_id, index) cdt then "true" else "false" | |
| memberSlots :: Map ObjID Int -> ObjID -> CapMap Word -> IRQMap -> CDT -> ObjMap Word -> String | |
| memberSlots objs obj_id slots irqNode cdt ms = | |
| ".slots.num = " ++ show slot_count ++ "," +++ | |
| ".slots.slot = (CDL_CapSlot[]) {" +++ | |
| showSlots objs obj_id (Map.toList slots) irqNode cdt ms +++ | |
| "}," | |
| where | |
| slot_count = Map.size slots | |
| printInit :: [Word] -> String | |
| printInit argv = | |
| "{" ++ joinBy ", " (map show argv) ++ "}" | |
| showFrameFill :: [String] -> String | |
| showFrameFill (dest_offset:dest_len:info_type:extra) = | |
| "{.type = " ++ info_type ++ "," +++ | |
| ".dest_offset = " ++ dest_offset ++ "," +++ | |
| ".dest_len = " ++ dest_len ++ "," +++ | |
| case (info_type,extra) of | |
| ("CDL_FrameFill_BootInfo",(bi_type:src_offset:[])) -> | |
| ".bi_type = {.type = " ++ bi_type ++ "," +++ | |
| ".src_offset = " ++ src_offset +++ | |
| "}}," | |
| ("CDL_FrameFill_FileData",(filename:file_offset:[])) -> | |
| ".file_data_type = {.filename = " ++ filename ++ "," +++ | |
| ".file_offset = " ++ file_offset +++ | |
| "}}," | |
| _ -> "#error Bad CDL_FrameFill_BootInfo args" | |
| showFrameFill _ = "" | |
| showFrameFills :: Maybe [[String]] -> String | |
| showFrameFills (Just fills) = | |
| ".fill = { " ++ unwords (map showFrameFill fills) ++ "}" | |
| showFrameFills _ = "" | |
| showFramePaddr :: Maybe Word -> String | |
| showFramePaddr paddr = | |
| ".paddr = " ++ pointerOfPAddr paddr ++ "," | |
| showObjectFields :: Map ObjID Int -> ObjID -> KernelObject Word -> IRQMap -> CDT -> ObjMap Word -> String | |
| showObjectFields _ _ Endpoint _ _ _ = ".type = CDL_Endpoint," | |
| showObjectFields _ _ Notification _ _ _ = ".type = CDL_Notification," | |
| showObjectFields objs obj_id (TCB slots faultEndpoint info domain argv) _ _ _ = | |
| ".type = CDL_TCB," +++ | |
| ".tcb_extra = {" +++ | |
| "#if (" ++ hex ipcbuffer_addr ++ " & ((1 << seL4_IPCBufferSizeBits) - 1)) != 0" +++ | |
| "# error \"IPC buffer not correctly aligned\"" +++ | |
| "#endif" +++ | |
| ".ipcbuffer_addr = " ++ hex ipcbuffer_addr ++ "," +++ | |
| ".priority = " ++ show priority ++ "," +++ | |
| ".max_priority = " ++ show max_priority ++ "," +++ | |
| ".affinity = " ++ show affinity ++ "," +++ | |
| ".pc = " ++ hex pc ++ "," +++ | |
| ".sp = " ++ hex stack ++ "," +++ | |
| ".init = (const seL4_Word[])" ++ printInit argv ++ "," +++ | |
| ".init_sz = " ++ show (length argv) ++ "," +++ | |
| ".domain = " ++ show domain ++ "," +++ | |
| ".resume = " ++ show resume' ++ "," +++ | |
| ".fault_ep = " ++ show fault_ep ++ "," +++ | |
| "}," +++ | |
| memberSlots objs obj_id slots Map.empty Map.empty Map.empty -- IRQ, cdt and obj map not required | |
| where | |
| ipcbuffer_addr = case info of {Just i -> ipcBufferAddr i; _ -> 0} | |
| priority = case info of {Just i -> case prio i of {Just p -> p; _ -> 125}; _ -> 125} | |
| max_priority = case info of {Just i -> case max_prio i of {Just p -> p; _ -> 125}; _ -> 125} | |
| affinity = case info of {Just i -> case affin i of {Just p -> p; _ -> 0}; _ -> 0} | |
| resume' = case info of {Just i -> case resume i of {Just True -> 1; Just False -> 0; _ -> 1}; _ -> 1} | |
| pc = case info of {Just i -> case ip i of {Just v -> v; _ -> 0}; _ -> 0} | |
| stack = case info of {Just i -> case sp i of {Just v -> v; _ -> 0}; _ -> 0} | |
| fault_ep = case faultEndpoint of {Just w -> w; _ -> 0} | |
| showObjectFields objs obj_id (CNode slots sizeBits info) irqNode cdt ms = | |
| ".type = " ++ t ++ "," +++ | |
| ".size_bits = " ++ show sizeBits ++ "," +++ | |
| cnode_extra ++ | |
| memberSlots objs obj_id slots irqNode cdt ms | |
| where | |
| -- IRQs are represented in CapDL as 0-sized CNodes. This is fine for | |
| -- the model, but the initialiser needs to know what objects represent | |
| -- interrupts to avoid trying to create them at runtime. It's a bit of | |
| -- a hack to assume that any 0-sized CNode is an interrupt, but this is | |
| -- an illegal size for a valid CNode so everything should work out. | |
| t = if sizeBits == 0 then "CDL_Interrupt" else "CDL_CNode" | |
| -- CNode-only attributes: | |
| -- hasUntyped instructs the rootserver to hand-off UntypedMemory objects | |
| untyped = case hasUntyped info of {Just True -> 1; _ -> 0} | |
| cnode_extra = if sizeBits == 0 then "" else | |
| ".cnode_extra = {" +++ | |
| ".has_untyped_memory = " ++ show untyped ++ "," +++ | |
| "},\n" | |
| showObjectFields objs obj_id (IOAPICIrq slots ioapic pin level polarity) irqNode cdt ms = | |
| ".type = CDL_IOAPICInterrupt, " +++ | |
| memberSlots objs obj_id slots irqNode cdt ms +++ | |
| ".ioapicirq_extra = {" +++ | |
| ".ioapic = " ++ show ioapic ++ "," +++ | |
| ".ioapic_pin = " ++ show pin ++ "," +++ | |
| ".level = " ++ show level ++ "," +++ | |
| ".polarity = " ++ show polarity ++ "," +++ | |
| "}," | |
| showObjectFields objs obj_id (MSIIrq slots handle bus dev fun) irqNode cdt ms = | |
| ".type = CDL_MSIInterrupt, " +++ | |
| memberSlots objs obj_id slots irqNode cdt ms +++ | |
| ".msiirq_extra = {" +++ | |
| ".handle = " ++ show handle ++ "," +++ | |
| ".pci_bus = " ++ show bus ++ "," +++ | |
| ".pci_dev = " ++ show dev ++ "," +++ | |
| ".pci_fun = " ++ show fun ++ "," +++ | |
| "}," | |
| showObjectFields objs obj_id (ARMIrq slots trigger target) irqNode cdt ms = | |
| ".type = CDL_ARMInterrupt, " +++ | |
| memberSlots objs obj_id slots irqNode cdt ms +++ | |
| ".armirq_extra = {" +++ | |
| ".trigger = " ++ show trigger ++ "," +++ | |
| ".target = " ++ show target ++ "," +++ | |
| "}," | |
| showObjectFields _ _ (Untyped size_bits paddr) _ _ _ = | |
| ".type = CDL_Untyped," +++ | |
| ".size_bits = " ++ maybe "-1" show size_bits ++ "," +++ | |
| ".paddr = " ++ pointerOfPAddr paddr ++ "," | |
| showObjectFields objs obj_id (PT slots) _ _ _ = | |
| ".type = CDL_PT," +++ | |
| memberSlots objs obj_id slots Map.empty Map.empty Map.empty -- IRQ, cdt and obj map not required | |
| showObjectFields objs obj_id (PD slots) _ _ _ = | |
| ".type = CDL_PD," +++ | |
| memberSlots objs obj_id slots Map.empty Map.empty Map.empty -- IRQ, cdt and obj map not required | |
| showObjectFields objs obj_id (PDPT slots) _ _ _ = | |
| ".type = CDL_PDPT," +++ | |
| memberSlots objs obj_id slots Map.empty Map.empty Map.empty -- IRQ, cdt and obj map not required | |
| showObjectFields objs obj_id (PML4 slots) _ _ _ = | |
| ".type = CDL_PML4," +++ | |
| memberSlots objs obj_id slots Map.empty Map.empty Map.empty -- IRQ, cdt and obj map not required | |
| showObjectFields objs obj_id (PUD slots) _ _ _ = | |
| ".type = CDL_PUD," +++ | |
| memberSlots objs obj_id slots Map.empty Map.empty Map.empty -- IRQ, cdt and obj map not required | |
| showObjectFields objs obj_id (PGD slots) _ _ _ = | |
| ".type = CDL_PGD," +++ | |
| memberSlots objs obj_id slots Map.empty Map.empty Map.empty -- IRQ, cdt and obj map not required | |
| showObjectFields _ _ (Frame size paddr extra) _ _ _ = | |
| ".type = CDL_Frame," +++ | |
| ".size_bits = " ++ show size ++ "," +++ | |
| ".frame_extra = { " ++ showFramePaddr paddr ++ showFrameFills extra +++ " }," | |
| showObjectFields _ _ (IOPorts (start, end)) _ _ _ = | |
| ".type = CDL_IOPorts," +++ | |
| ".start = " ++ show start ++ "," ++ | |
| ".end = " ++ show end ++ "," | |
| showObjectFields objs obj_id (ASIDPool slots asidHigh) _ _ _ = | |
| ".type = CDL_ASIDPool," +++ | |
| ".asid_high = " ++ maybe "-1" hex asidHigh ++ "," +++ | |
| memberSlots objs obj_id slots Map.empty Map.empty Map.empty -- IRQ, cdt and obj map not required | |
| showObjectFields _ _ IODevice{} _ _ _ = | |
| ".type = CDL_IODevice," | |
| showObjectFields _ _ ARMIODevice{} _ _ _ = | |
| ".type = CDL_ARMIODevice," | |
| showObjectFields _ _ VCPU _ _ _ = ".type = CDL_VCPU," | |
| showObjectFields _ _ (SC info size_bits) _ _ _ = | |
| ".type = CDL_SchedContext," +++ | |
| ".sc_extra = {" +++ | |
| ".period = " ++ show sc_period ++ "," +++ | |
| ".budget = " ++ show sc_budget ++ "," +++ | |
| ".data = " ++ show sc_data ++ "," +++ | |
| "}," +++ | |
| ".size_bits = " ++ show sizeBits ++ "," | |
| where | |
| sc_period = fromMaybe 0 (maybe Nothing period info) | |
| sc_budget = fromMaybe 0 (maybe Nothing budget info) | |
| sc_data = fromMaybe 0 (maybe Nothing scData info) | |
| sizeBits = fromMaybe 0 size_bits | |
| showObjectFields _ _ (RTReply {}) _ _ _ = ".type = CDL_RTReply," | |
| showObjectFields _ _ (ARMSID {}) _ _ _ = ".type = CDL_SID," | |
| showObjectFields _ _ (ARMCB {}) _ _ _ = ".type = CDL_CB," | |
| showObjectFields _ _ x _ _ _ = assert False $ | |
| "UNSUPPORTED OBJECT TYPE: " ++ show x | |
| showObject :: Map ObjID Int -> (ObjID, KernelObject Word) -> IRQMap -> CDT -> ObjMap Word -> String | |
| showObject objs obj irqNode cdt ms = | |
| "{" +++ | |
| "#ifdef CONFIG_DEBUG_BUILD" +++ ".name = \"" ++ name ++ "\"," +++ "#endif" +++ | |
| showObjectFields objs id (snd obj) irqNode cdt ms +++ | |
| "}" | |
| where | |
| id = fst obj | |
| name = fst id ++ (case snd id of | |
| Just index -> "[" ++ show index ++ "]" | |
| _ -> "") | |
| showObjects :: Map ObjID Int -> Int -> [(ObjID, KernelObject Word)] -> IRQMap -> CDT -> ObjMap Word -> String | |
| showObjects _ _ [] _ _ _ = "" | |
| showObjects objs counter (x:xs) irqNode cdt ms = | |
| "[" ++ show counter ++ "] = " ++ showObject objs x irqNode cdt ms ++ "," +++ | |
| showObjects objs (counter + 1) xs irqNode cdt ms | |
| memberObjects :: Map ObjID Int -> [(ObjID, KernelObject Word)] -> IRQMap -> CDT -> | |
| ObjMap Word -> String | |
| memberObjects obj_ids obj_list irqNode cdt objs = | |
| ".objects = (CDL_Object[]) {" +++ | |
| showObjects obj_ids 0 obj_list irqNode cdt objs +++ | |
| "}," | |
| -- Emit an array where each entry represents a given interrupt. Each is -1 if | |
| -- that interrupt has no handler or else the object ID of the interrupt | |
| -- (0-sized CNode). | |
| memberIRQs :: Map ObjID Int -> IRQMap -> Arch -> String | |
| memberIRQs objs irqNode _ = | |
| let maxIrq = case Map.null irqNode of | |
| False -> fst $ Map.findMax irqNode | |
| True -> 0 | |
| in | |
| ".num_irqs = " ++ show (maxIrq + 1) ++ "," +++ | |
| ".irqs = (CDL_ObjID[]){" +++ | |
| joinBy ", " | |
| [ case Map.lookup k irqNode of | |
| Just i -> show $ fromJust $ Map.lookup i objs | |
| _ -> "-1" | |
| | k <- [0 .. maxIrq] | |
| ] +++ | |
| "}," | |
| showUntypedDerivation :: Map ObjID Int -> ObjID -> [ObjID] -> String | |
| showUntypedDerivation objs utID utChildren = | |
| "{" +++ | |
| " .untyped = " ++ showObjID objs utID ++ "," +++ | |
| " .num = " ++ show (length utChildren) ++ "," +++ | |
| " .children = (CDL_ObjID[]){" +++ | |
| Data.List.Utils.join ",\n" | |
| [" " ++ showObjID objs childID | childID <- utChildren] +++ | |
| " }" +++ | |
| "}" | |
| showUntypedDerivations :: AllocationType -> Map ObjID Int -> CoverMap -> String | |
| showUntypedDerivations DynamicAlloc{} _ untypedCovers | |
| | all null (Map.elems untypedCovers) = | |
| ".num_untyped = 0," +++ | |
| ".untyped = NULL," | |
| | otherwise = error $ | |
| "refusing to generate spec for dynamic allocation because the " ++ | |
| "following untypeds already have children:\n" ++ | |
| Data.List.Utils.join "\n" | |
| [ " " ++ show utID | |
| | (utID, utChildren) <- Map.toList untypedCovers | |
| , not $ null utChildren ] | |
| showUntypedDerivations StaticAlloc objs untypedCovers = | |
| ".num_untyped = " ++ show (Map.size untypedCovers) ++ "," +++ | |
| ".untyped = (CDL_UntypedDerivation[]){" +++ | |
| joinBy ",\n" (map (uncurry (showUntypedDerivation objs)) $ | |
| Map.toList untypedCovers) +++ | |
| "}," | |
| -- find all ASIDPools and prepare them for allocation wrt. asid_high. | |
| getASIDPoolDerivations :: ObjMap Word -> [(Word, ObjID)] | |
| getASIDPoolDerivations ms = | |
| let table = | |
| sortBy (comparing fst) | |
| [ (asidHigh, objID) | |
| | (objID, ASIDPool lowSlots maybeAsidHigh) <- Map.toList ms, | |
| -- sanity checks | |
| Map.null lowSlots | |
| || can'tAllocate objID "it has nonempty low slots", | |
| maybeAsidHigh /= Nothing | |
| || can'tAllocate objID "it has no assigned asid_high", | |
| let Just asidHigh = maybeAsidHigh | |
| ] | |
| asidHighs = map fst table | |
| in -- more sanity checks | |
| if asidHighs /= [1..fromIntegral (length asidHighs)] | |
| then error $ "ASID pools don't have slot numbers of the form [1..n]: " ++ show table | |
| else table | |
| where can'tAllocate objID reason = | |
| error $ "can't allocate ASID pool " ++ show objID ++ " because " ++ reason | |
| showASIDPoolDerivations :: Map ObjID Int -> ObjMap Word -> String | |
| showASIDPoolDerivations objs ms = | |
| let table = getASIDPoolDerivations ms | |
| -- include ignored slot 0 | |
| array = "(CDL_ObjID)-1 /* slot reserved for root thread, ignored */" | |
| : map (showObjID objs . snd) table | |
| in ".num_asid_slots = " ++ show (length array) ++ "," +++ | |
| ".asid_slots = (CDL_ObjID[]){" +++ | |
| joinBy ",\n" [" " ++ idStr | idStr <- array] +++ | |
| "}," | |
| printC :: AllocationType -> Model Word -> Idents CapName -> CopyMap -> Doc | |
| printC allocType (Model arch objs irqNode cdt untypedCovers) _ _ = | |
| text $ | |
| "/* Generated file. Your changes will be overwritten. */" +++ | |
| "" +++ | |
| "#include <capdl.h>" +++ | |
| "#include <sel4/sel4.h>" +++ | |
| "" +++ | |
| "#ifndef INVALID_SLOT" +++ | |
| "#define INVALID_SLOT (-1)" +++ | |
| "#endif" +++ | |
| "" +++ | |
| maxObjects objs_sz +++ -- FIXME: I suspect this is not the right list to measure. | |
| "" +++ | |
| "CDL_Model capdl_spec = {" +++ | |
| memberArch arch +++ | |
| memberNum objs_sz +++ | |
| memberIRQs obj_ids irqNode arch +++ | |
| memberObjects obj_ids obj_list irqNode cdt objs +++ | |
| showUntypedDerivations allocType obj_ids untypedCovers +++ | |
| showASIDPoolDerivations obj_ids objs +++ | |
| "};" | |
| where objs_sz = length $ Map.toList objs | |
| obj_list = case allocType of | |
| StaticAlloc -> Map.toList objs | |
| DynamicAlloc objSizeMap -> sortObjects objSizeMap (Map.toList objs) | |
| obj_ids = Map.fromList $ flip zip [0..] $ map fst obj_list |