| -- |
| -- 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 |