blob: f30331becc112d1102ab36dee7e62b92282285f4 [file] [log] [blame] [edit]
--
-- 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)