| -- | |
| -- Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) | |
| -- | |
| -- SPDX-License-Identifier: BSD-2-Clause | |
| -- | |
| {-# LANGUAGE FlexibleInstances #-} | |
| {-# LANGUAGE TypeSynonymInstances #-} | |
| module CapDL.PrintUtils where | |
| import CapDL.Model | |
| import CapDL.State (objSizeBits) | |
| import Prelude () | |
| import Prelude.Compat | |
| import Text.PrettyPrint | |
| import qualified Data.Set as Set | |
| import Data.Word | |
| import Numeric | |
| import Data.List | |
| import Data.Ord (comparing, Down(..)) | |
| listSucc :: Enum a => [a] -> [a] | |
| listSucc list = init list ++ [succ (last list)] | |
| class (Show a, Eq a) => Printing a where | |
| isSucc :: a -> a -> Bool | |
| num :: a -> Doc | |
| instance Printing Word where | |
| isSucc first second = succ first == second | |
| num n = int (fromIntegral n) | |
| instance Printing [Word] where | |
| isSucc first second = listSucc first == second | |
| num ns = hsep $ punctuate comma (map num ns) | |
| hex :: Word -> String | |
| hex x = "0x" ++ showHex x "" | |
| showID :: ObjID -> String | |
| showID (name, Nothing) = name | |
| showID (name, Just num) = name ++ "[" ++ show num ++ "]" | |
| maybeParens text | |
| | isEmpty text = empty | |
| | otherwise = parens text | |
| maybeParensList text = | |
| maybeParens $ hsep $ punctuate comma $ filter (not . isEmpty) text | |
| prettyBits bits = num bits <+> text "bits" | |
| prettyMBits mbits = | |
| case mbits of | |
| Nothing -> empty | |
| Just bits -> prettyBits bits | |
| prettyLevel l = text "level" <> colon <+> num l | |
| prettyVMSize vmSzBits = | |
| -- NB: capDL syntax only uses these two units | |
| if vmSzBits >= 20 | |
| then num (2^(vmSzBits - 20) :: Word) <> text "M" | |
| else num (2^(vmSzBits - 10) :: Word) <> text "k" | |
| prettyPaddr :: Maybe Word -> Doc | |
| prettyPaddr Nothing = empty | |
| prettyPaddr (Just p) = text "paddr:" <+> (text $ hex p) | |
| prettyAddr :: Word -> Doc | |
| prettyAddr addr = text "addr:" <+> num addr | |
| prettyIP :: Maybe Word -> Doc | |
| prettyIP Nothing = empty | |
| prettyIP (Just ip) = text "ip:" <+> num ip | |
| prettySP :: Maybe Word -> Doc | |
| prettySP Nothing = empty | |
| prettySP (Just sp) = text "sp:" <+> num sp | |
| prettyPrio :: Maybe Integer -> Doc | |
| prettyPrio Nothing = empty | |
| prettyPrio (Just prio) = text "prio:" <+> (text $ show prio) | |
| prettyMaxPrio :: Maybe Integer -> Doc | |
| prettyMaxPrio Nothing = empty | |
| prettyMaxPrio (Just max_prio) = text "max_prio:" <+> (text $ show max_prio) | |
| prettyAffinity :: Maybe Integer -> Doc | |
| prettyAffinity Nothing = empty | |
| prettyAffinity (Just affinity) = text "affinity:" <+> (text $ show affinity) | |
| prettyResume :: Maybe Bool -> Doc | |
| prettyResume Nothing = empty | |
| prettyResume (Just resume) = text "resume:" <+> (text $ show resume) | |
| prettyDom :: Integer -> Doc | |
| prettyDom dom = text "dom:" <+> (text $ show dom) | |
| prettyFaultEP :: Maybe Word -> Doc | |
| prettyFaultEP Nothing = empty | |
| prettyFaultEP (Just fault_ep) = text "fault_ep:" <+> (text $ show fault_ep) | |
| prettyExtraInfo :: Maybe TCBExtraInfo -> Doc | |
| prettyExtraInfo Nothing = empty | |
| prettyExtraInfo (Just (TCBExtraInfo addr ip sp prio max_prio affinity resume)) = | |
| hsep $ punctuate comma $ filter (not . isEmpty) | |
| [prettyAddr addr, prettyIP ip, prettySP sp, prettyPrio prio, prettyMaxPrio max_prio, prettyAffinity affinity, prettyResume resume] | |
| prettyInitArguments :: [Word] -> Doc | |
| prettyInitArguments [] = empty | |
| prettyInitArguments init = | |
| text "init:" <+> brackets (hsep $ punctuate comma $ map num init) | |
| prettyDomainID :: Word -> Doc | |
| prettyDomainID dom = text "domainID:" <+> num dom | |
| prettyPeriod :: Maybe Word64 -> Doc | |
| prettyPeriod Nothing = empty | |
| prettyPeriod (Just period) = text "period:" <+> (text $ show period) | |
| prettyBudget :: Maybe Word64 -> Doc | |
| prettyBudget Nothing = empty | |
| prettyBudget (Just budget) = text "budget:" <+> (text $ show budget) | |
| prettySCData :: Maybe Word -> Doc | |
| prettySCData Nothing = empty | |
| prettySCData (Just scData) = text "data:" <+> (text $ show scData) | |
| prettySCExtraInfo :: Maybe SCExtraInfo -> Doc | |
| prettySCExtraInfo Nothing = empty | |
| prettySCExtraInfo (Just (SCExtraInfo period budget scData)) = | |
| hsep $ punctuate comma $ filter (not . isEmpty) | |
| [prettyPeriod period, prettyBudget budget, prettySCData scData] | |
| prettyPCIDevice :: (Word, Word, Word) -> Doc | |
| prettyPCIDevice (pci_bus, pci_dev, pci_fun) = | |
| num pci_bus <> colon <> num pci_dev <> text "." <> num pci_fun | |
| prettyIOAPICNum :: Word -> Doc | |
| prettyIOAPICNum ioapic = text "ioapic_num:" <+> (text $ show ioapic) | |
| prettyIOAPICPin :: Word -> Doc | |
| prettyIOAPICPin pin = text "ioapic_pin:" <+> (text $ show pin) | |
| prettyIOAPICLevel :: Word -> Doc | |
| prettyIOAPICLevel level = text "ioapic_level:" <+> (text $ show level) | |
| prettyIOAPICPolarity :: Word -> Doc | |
| prettyIOAPICPolarity polarity = text "ioapic_polarity:" <+> (text $ show polarity) | |
| prettyMSIHandle :: Word -> Doc | |
| prettyMSIHandle handle = text "msi_handle:" <+> (text $ show handle) | |
| prettyMSIPCIBus :: Word -> Doc | |
| prettyMSIPCIBus bus = text "msi_pci_bus:" <+> (text $ show bus) | |
| prettyMSIPCIDev :: Word -> Doc | |
| prettyMSIPCIDev dev = text "msi_pci_dev:" <+> (text $ show dev) | |
| prettyMSIPCIFun :: Word -> Doc | |
| prettyMSIPCIFun fun = text "msi_pci_fun:" <+> (text $ show fun) | |
| prettyARMIRQTrigger :: Word -> Doc | |
| prettyARMIRQTrigger trigger = text "irq_trigger:" <+> (text $ show trigger) | |
| prettyARMIRQTarget :: Word -> Doc | |
| prettyARMIRQTarget target = text "irq_target:" <+> (text $ show target) | |
| prettyARMIODevice :: Word -> Doc | |
| prettyARMIODevice iospace = text "iospace:" <+> (text $ show iospace) | |
| prettyFills :: Maybe [[String]] -> Doc | |
| prettyFills (Just fills) = text "fill:" <+> brackets (hsep (punctuate comma (map (braces . text . unwords) fills))) | |
| prettyFills Nothing = empty | |
| prettyPorts :: (Word, Word) -> Doc | |
| prettyPorts (start, end) = | |
| text "ports:" <+> brackets (num start <> text ".." <> num end) | |
| prettyAsidHigh :: Maybe Word -> Doc | |
| prettyAsidHigh (Just asidHigh) = text "asid_high:" <+> (text $ hex asidHigh) | |
| prettyAsidHigh Nothing = empty | |
| prettyObjParams obj = case obj of | |
| Endpoint -> text "ep" | |
| Notification -> text "notification" | |
| TCB _ fault_ep extra dom init -> | |
| text "tcb" <+> maybeParensList [prettyExtraInfo extra, prettyFaultEP fault_ep, prettyDom dom, prettyInitArguments init] | |
| CNode _ 0 _ -> text "irq" --FIXME: This should check if the obj is in the irqNode | |
| CNode _ bits _ -> text "cnode" <+> maybeParensList [prettyBits bits] | |
| Untyped mbits paddr -> text "ut" <+> maybeParensList [prettyMBits mbits, prettyPaddr paddr] | |
| ASIDPool _ asidHigh -> text "asid_pool" <+> maybeParensList [prettyAsidHigh asidHigh] | |
| PT {} -> text "pt" | |
| PD {} -> text "pd" | |
| PML4 {} -> text "pml4" | |
| PDPT {} -> text "pdpt" | |
| PUD {} -> text "pud" | |
| PGD {} -> text "pgd" | |
| Frame vmSzBits paddr fill -> text "frame" <+> maybeParensList [prettyVMSize vmSzBits, prettyPaddr paddr, prettyFills fill] | |
| IOPT _ level -> text "io_pt" <+> maybeParensList [prettyLevel level] | |
| IOPorts ports -> text "io_ports" <+> maybeParensList [prettyPorts ports] | |
| IODevice _ dom pci -> text "io_device" <+> maybeParensList [prettyDomainID dom, | |
| prettyPCIDevice pci] | |
| ARMIODevice _ iospace -> text "io_device" <+> maybeParensList [prettyARMIODevice iospace] | |
| VCPU {} -> text "vcpu" | |
| SC extra mbits -> text "sc" <+> maybeParensList [prettySCExtraInfo extra, prettyMBits mbits] | |
| RTReply -> text "rtreply" | |
| IOAPICIrq _ ioapic pin level polarity -> text "ioapic_irq" <+> maybeParensList[prettyIOAPICNum ioapic, prettyIOAPICPin pin, prettyIOAPICLevel level, prettyIOAPICPolarity polarity] | |
| MSIIrq _ handle bus dev fun -> text "msi_irq" <+> maybeParensList[prettyMSIHandle handle, prettyMSIPCIBus bus, prettyMSIPCIDev dev, prettyMSIPCIFun fun] | |
| ARMIrq _ trigger target -> text "arm_irq" <+> maybeParensList[prettyARMIRQTrigger trigger, prettyARMIRQTarget target] | |
| ARMSID {} -> text "streamid" | |
| ARMCB {} -> text "contextbank" | |
| capParams [] = empty | |
| capParams xs = parens (hsep $ punctuate comma xs) | |
| successiveWordsUp :: [Maybe Word] -> [Word] | |
| successiveWordsUp [] = [] | |
| successiveWordsUp [Just x] = [x] | |
| successiveWordsUp ls@((Just first):(Just second):_) | |
| | succ first == second = first:(successiveWordsUp (tail ls)) | |
| | otherwise = [first] | |
| successiveWordsUp _ = error "successiveWordsUp" | |
| successiveWordsDown :: [Maybe Word] -> [Word] | |
| successiveWordsDown [] = [] | |
| successiveWordsDown [Just x] = [x] | |
| successiveWordsDown ls@((Just first):(Just second):_) | |
| | first == succ second = first:(successiveWordsDown (tail ls)) | |
| | otherwise = [first] | |
| successiveWordsDown _ = error "successiveWordsDown" | |
| successiveWords :: [Maybe Word] -> [Word] | |
| successiveWords [] = [] | |
| successiveWords list = if length up == 1 then down else up | |
| where up = successiveWordsUp list | |
| down = successiveWordsDown list | |
| breakSuccessive :: [Maybe Word] -> [[Word]] | |
| breakSuccessive [] = [] | |
| breakSuccessive list = range:(breakSuccessive (drop (length range) list)) | |
| where range = successiveWords list | |
| prettyRange :: [Word] -> Doc | |
| prettyRange [x] = num x | |
| prettyRange range = | |
| num (head range) <> text ".." <> num (last range) | |
| prettyRanges :: [Maybe Word] -> Doc | |
| prettyRanges range = | |
| hsep $ punctuate comma $ map prettyRange ranges | |
| where ranges = breakSuccessive range | |
| prettyBrackets :: [Maybe Word] -> Doc | |
| prettyBrackets [Nothing] = empty | |
| prettyBrackets list = brackets (prettyRanges list) | |
| prettyParemNum t n = [text t <> colon <+> num n] | |
| maybeNum _ 0 = [] | |
| maybeNum t n = prettyParemNum t n | |
| maybeBadge = maybeNum "badge" | |
| prettyRight _ Read = text "R" | |
| prettyRight _ Write = text "W" | |
| prettyRight True Grant = text "X" | |
| prettyRight False Grant = text "G" | |
| prettyRight _ GrantReply = text "P" | |
| maybeRightsList _ [] = [] | |
| maybeRightsList isFrame xs = [hcat (map (prettyRight isFrame) xs)] | |
| maybeRights isFrame r = maybeRightsList isFrame (Set.toList r) | |
| maybeGuard = maybeNum "guard" | |
| maybeGSize = maybeNum "guard_size" | |
| zombieNum n = [text "zombie" <> colon <+> num n] | |
| printAsid (high, low) = | |
| text "(" <> text (hex high) <> text ", " <> text (hex low) <> text ")" | |
| prettyAsid asid = [text "asid:" <+> printAsid asid] | |
| prettyCore core = [text "core:" <+> num core] | |
| maybeAsid Nothing = [] | |
| maybeAsid (Just asid) = prettyAsid asid | |
| prettyFrameMapping (container, slot) = | |
| [text "mapping: (" <> text (showID container) <> text ", " <> num slot <> text ")"] | |
| maybeFrameMapping Nothing = [] | |
| maybeFrameMapping (Just mapping) = prettyFrameMapping mapping | |
| maybeCapParams :: Cap -> Doc | |
| maybeCapParams cap = case cap of | |
| EndpointCap _ badge rights -> | |
| capParams (maybeBadge badge ++ maybeRights False rights) | |
| NotificationCap _ badge rights -> | |
| capParams (maybeBadge badge ++ maybeRights False rights) | |
| RTReplyCap _ rights -> capParams (maybeRights False rights) | |
| ReplyCap _ -> capParams [text "reply"] | |
| MasterReplyCap _ -> capParams [text "master_reply"] | |
| CNodeCap _ guard gsize -> | |
| capParams (maybeGuard guard ++ maybeGSize gsize) | |
| FrameCap _ rights asid cached mapping -> capParams (maybeRights True rights ++ maybeAsid asid ++ | |
| (if cached then [] else [text "uncached"]) ++ maybeFrameMapping mapping) | |
| PTCap _ asid -> capParams (maybeAsid asid) | |
| PDCap _ asid -> capParams (maybeAsid asid) | |
| SchedControlCap core -> capParams (prettyCore core) | |
| _ -> empty | |
| printCap :: Cap -> Doc | |
| printCap cap = case cap of | |
| NullCap -> text "null" | |
| IOSpaceMasterCap -> text ioSpaceMaster | |
| ASIDControlCap -> text asidControl | |
| IRQControlCap -> text irqControl | |
| DomainCap -> text domain | |
| (SchedControlCap {}) -> text schedControl | |
| _ -> text $ fst $ objID cap | |
| sameName :: ObjID -> ObjID -> Bool | |
| sameName (first, _) (second, _) = first == second | |
| sameParams :: Cap -> Cap -> Bool | |
| sameParams cap1 cap2 = | |
| case (cap1, cap2) of | |
| ((EndpointCap _ b1 r1), (EndpointCap _ b2 r2)) -> b1 == b2 && r1 == r2 | |
| ((NotificationCap _ b1 r1), (NotificationCap _ b2 r2)) -> | |
| b1 == b2 && r1 == r2 | |
| ((CNodeCap _ g1 gs1), (CNodeCap _ g2 gs2)) -> | |
| g1 == g2 && gs1 == gs2 | |
| ((FrameCap _ r1 a1 c1 m1), (FrameCap _ r2 a2 c2 m2)) -> r1 == r2 && a1 == a2 && c1 == c2 && m1 == m2 | |
| ((PTCap _ a1), (PTCap _ a2)) -> a1 == a2 | |
| ((PDCap _ a1), (PDCap _ a2)) -> a1 == a2 | |
| ((RTReplyCap _ r1), (RTReplyCap _ r2)) -> r1 == r2 | |
| _ -> True | |
| sameCapName :: Cap -> Cap -> Bool | |
| sameCapName first second | |
| | not (hasObjID first) || not (hasObjID second) = False | |
| | snd (objID first) == Nothing || snd (objID second) == Nothing = False | |
| | otherwise = sameName (objID first) (objID second) | |
| sameCap :: Cap -> Cap -> Bool | |
| sameCap first second = | |
| sameCapName first second && sameParams first second | |
| class Arrayable a where | |
| isSameArray :: a -> a -> Bool | |
| instance Arrayable Cap where | |
| isSameArray = sameCap | |
| instance Arrayable ObjID where | |
| isSameArray = sameName | |
| sameArray :: (Printing a, Arrayable b) => [(a, b)] -> [(a, b)] | |
| sameArray [] = [] | |
| sameArray [x] = [x] | |
| sameArray ls@(x@(slot1, first):(slot2, second):_) | |
| | isSameArray first second && isSucc slot1 slot2 = x:(sameArray (tail ls)) | |
| | otherwise = [x] | |
| same :: Printing a => (ObjID, KernelObject a) -> (ObjID, KernelObject a) -> Bool | |
| same (name1, obj1) (name2, obj2) = | |
| if (hasSlots obj1 && hasSlots obj2) | |
| then sameName name1 name2 && slots obj1 == slots obj2 | |
| else sameName name1 name2 | |
| prettyArch ARM11 = text "arm11" | |
| prettyArch IA32 = text "ia32" | |
| prettyArch X86_64 = text "x86_64" | |
| prettyArch AARCH64 = text "aarch64" | |
| prettyArch RISCV = text "riscv" | |
| -- | Helper for sorting function. | |
| -- Return results in 'Left', so they sort before non-results. | |
| justLeft :: b -> Maybe a -> Either a b | |
| justLeft r Nothing = Right r | |
| justLeft _ (Just l) = Left l | |
| {- | |
| - A custom sorting function for CapDL objects. Essentially, we sort by | |
| - (physical address, descending size, name). | |
| - | |
| - We place objects that have physical addresses first. These are almost | |
| - certainly being allocated from device untypeds and need to be allocated | |
| - by the capDL loader in physical address order. | |
| - | |
| - Other objects will be allocated from normal untypeds and should be in | |
| - descending order of size to reduce fragmentation. But to provide some | |
| - finer control, we also sort equal-size objects by name. This means the | |
| - spec creator can induce a specific ordering for identically sized objects. | |
| - This is primarily useful for getting physically contiguous frames, | |
| - for example, in the CAmkES DMA allocator. | |
| - | |
| - (This is used for the dynamic allocator in the capDL loader. If static | |
| - allocation is used, objects will already be sorted in allocation order | |
| - and we do not sort them again.) | |
| -} | |
| sortObjects :: ObjectSizeMap -> [(ObjID, KernelObject Word)] -> [(ObjID, KernelObject Word)] | |
| sortObjects objSizeMap = sortBy (comparing cmp) | |
| where cmp (objID, obj) = (justLeft () $ objPaddr obj, | |
| Down $ objSizeBits objSizeMap obj, | |
| objID) |