| -- | |
| -- Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) | |
| -- | |
| -- SPDX-License-Identifier: BSD-2-Clause | |
| -- | |
| module CapDL.DumpParser where | |
| import CapDL.AST | |
| import CapDL.Model | |
| import CapDL.ParserUtils | |
| import Text.ParserCombinators.Parsec | |
| import Prelude () | |
| import Prelude.Compat | |
| import Control.Monad (when) | |
| import qualified Data.Set as Set | |
| import qualified Data.Map as Map | |
| import Data.List.Compat | |
| import Data.List.Split | |
| import Data.Ord | |
| import Numeric | |
| import Data.Maybe | |
| --Assumes that untypeds come before anything with an address | |
| insertCov :: ObjID -> ObjID -> MapParser () | |
| insertCov cov untyp = do | |
| maps <- getState | |
| let covM = covMap maps | |
| covSet = Map.findWithDefault Set.empty untyp covM | |
| covSet' = Set.insert cov covSet | |
| setState $ maps { covMap = Map.insert untyp covSet' covM } | |
| getCovs :: MapParser [(ObjID, Set.Set ObjID)] | |
| getCovs = do | |
| maps <- getState | |
| return $ Map.toList $ covMap maps | |
| insertAddrs :: (Word, Word) -> ObjID -> MapParser () | |
| insertAddrs addrs objID = do | |
| maps <- getState | |
| let addrM = addrMap maps | |
| setState $ maps { addrMap = Map.insert objID addrs addrM } | |
| included :: Word -> (Word, Word) -> Bool | |
| included n (st, end) = n >= st && n < end | |
| getUntypSize :: String -> Int | |
| getUntypSize string = read $ splitOn "@" string !! 2 | |
| lookupAddr :: Word -> MapParser (Maybe ObjID) | |
| lookupAddr addr = do | |
| maps <- getState | |
| let ids = map fst $ filter (included addr . snd) $ Map.toList $ addrMap maps | |
| id = minimumBy (comparing (getUntypSize . fst)) ids | |
| return $ case ids of | |
| [] -> Nothing | |
| _ -> Just id | |
| insertRef :: Name -> ObjID -> MapParser () | |
| insertRef dumpID objID = do | |
| maps <- getState | |
| let refM = refMap maps | |
| setState $ maps { refMap = Map.insert dumpID objID refM } | |
| lookupRef :: Name -> MapParser ObjID | |
| lookupRef dumpID = do | |
| maps <- getState | |
| let id = Map.lookup dumpID $ refMap maps | |
| case id of | |
| Just id -> return id | |
| Nothing -> return (dumpID, Nothing) | |
| getAddr :: String -> Maybe Word | |
| getAddr string = | |
| if '@' `elem` string | |
| then Just $ fst.head.readHex $ splitOn "@0x" string !! 1 | |
| else Nothing | |
| getObjTyp :: String -> Maybe String | |
| getObjTyp string = | |
| if '@' `elem` string | |
| then Just $ splitOn "@" string !! 0 | |
| else Nothing | |
| maybeInsertIRQ :: Name -> MapParser () | |
| maybeInsertIRQ obj = | |
| when (getObjTyp obj == Just "irqhandler") $ do | |
| maps <- getState | |
| let irqM = irqMap maps | |
| slot = fromJust $ getAddr obj | |
| setState $ maps { irqMap = Map.insert slot obj irqM } | |
| object :: MapParser KO | |
| object = do | |
| typ <- object_type | |
| params <- object_params | |
| return (Obj typ params []) | |
| maybe_object :: MapParser (Maybe (Name, KO)) | |
| maybe_object = | |
| do name <- name | |
| _ <- symbol "=" | |
| obj <- CapDL.DumpParser.object | |
| return $ Just (name, obj) | |
| <|> return Nothing | |
| sizeOf :: Arch -> KO -> Word | |
| sizeOf _ (Obj Frame_T [VMSize vmSz] _) = vmSz | |
| sizeOf _ (Obj Untyped_T [BitSize bSz] _) = 2 ^ bSz | |
| sizeOf _ (Obj CNode_T [BitSize bSz] _) = 16 * 2 ^ bSz | |
| sizeOf _ (Obj IrqSlot_T _ _) = 1 | |
| sizeOf _ (Obj Endpoint_T _ _) = 16 | |
| sizeOf _ (Obj Notification_T _ _) = 16 | |
| sizeOf _ (Obj ASIDPool_T _ _) = 4 * 2^10 | |
| sizeOf _ (Obj IOPT_T _ _) = 4 * 2^10 | |
| sizeOf _ (Obj IODevice_T _ _) = 1 | |
| sizeOf _ (Obj ARMIODevice_T _ _) = 1 | |
| sizeOf IA32 (Obj TCB_T _ _) = 2^10 | |
| sizeOf IA32 (Obj PD_T _ _) = 4 * 2^10 | |
| sizeOf IA32 (Obj PT_T _ _) = 4 * 2^10 | |
| sizeOf ARM11 (Obj TCB_T _ _) = 512 | |
| sizeOf ARM11 (Obj PD_T _ _) = 16 * 2^10 | |
| sizeOf ARM11 (Obj PT_T _ _) = 2^10 | |
| sizeOf _ _ = 0 | |
| consecutive :: Arch -> (Name, KO) -> Maybe (Name, KO) -> Word -> Bool | |
| consecutive _ _ Nothing _ = False | |
| consecutive arch (name1, obj1) (Just (name2, obj2)) num = | |
| let addr1 = getAddr name1 | |
| addr2 = getAddr name2 | |
| in case (addr1, addr2) of | |
| (Just addr1, Just addr2) -> | |
| obj1 == obj2 && addr1 + num * sizeOf arch obj1 == addr2 | |
| _ -> False | |
| considerUntypeds :: Arch -> ObjID -> Maybe Word -> KO -> MapParser () | |
| considerUntypeds arch refr addr obj = | |
| case (addr, obj) of | |
| (Just addr, Obj Untyped_T _ _ ) -> do | |
| covUn <- lookupAddr addr | |
| insertAddrs (addr, addr + sizeOf arch obj - 1) refr | |
| case covUn of | |
| Just covUn -> insertCov refr covUn | |
| Nothing -> return () | |
| (Just addr, _) -> do covUn <- lookupAddr addr | |
| case covUn of | |
| Just covUn -> insertCov refr covUn | |
| Nothing -> return () | |
| (Nothing, _) -> return () | |
| maybe_obj_decl :: Arch -> (Name, KO) -> Word -> MapParser Word | |
| maybe_obj_decl arch pre num = do | |
| next <- lookAhead maybe_object | |
| if consecutive arch pre next num | |
| then do let (name, obj) = fromJust next | |
| refr = (fst pre, Just num) | |
| addr = getAddr name | |
| _ <- maybe_object | |
| maybeInsertIRQ name | |
| insertRef name refr | |
| considerUntypeds arch refr addr obj | |
| total <- maybe_obj_decl arch pre (num + 1) | |
| return total | |
| else return num | |
| obj_decl :: Arch -> MapParser KODecl | |
| obj_decl arch = do | |
| name <- name | |
| _ <- symbol "=" | |
| obj <- CapDL.DumpParser.object | |
| total <- maybe_obj_decl arch (name, obj) 1 | |
| let (decl, refr) = if total == 1 | |
| then ((name, []), (name, Nothing)) | |
| else ((name, [Only total]), (name, Just 0)) | |
| addr = getAddr name | |
| maybeInsertIRQ name | |
| insertRef name refr | |
| considerUntypeds arch refr addr obj | |
| return (KODecl [decl] obj) | |
| obj_decls :: Arch -> MapParser [Decl] | |
| obj_decls arch = do | |
| reserved "objects" | |
| decls <- braces $ many (obj_decl arch) | |
| return $ map ObjDecl decls | |
| id_to_ref :: ObjID -> NameRef | |
| id_to_ref (name, Nothing) = (name, []) | |
| id_to_ref (name, Just n) = (name, [Only n]) | |
| make_obj :: Set.Set ObjID -> KO | |
| make_obj covs = Obj Untyped_T [] $ map (Right . id_to_ref) $ Set.toList covs | |
| cov_decl :: (ObjID, Set.Set ObjID) -> KODecl | |
| cov_decl (refr, covs) = | |
| let obj = make_obj covs | |
| in KODecl [id_to_ref refr] obj | |
| cov_decls :: MapParser [Decl] | |
| cov_decls = do | |
| covs <- getCovs | |
| return $ map (ObjDecl . cov_decl) covs | |
| cap_mapping :: MapParser CapMapping | |
| cap_mapping = do | |
| sl <- maybe_slot | |
| obj <- name | |
| (n,num) <- lookupRef obj | |
| let obj' = case num of | |
| Just num -> (n, [Only num]) | |
| Nothing -> (n, []) | |
| params <- cap_params | |
| parent <- maybe_parent | |
| return $ CapMapping sl Nothing obj' params parent | |
| refToNameRef :: ObjID -> NameRef | |
| refToNameRef (id, num) = | |
| case num of | |
| Just num -> (id, [Only num]) | |
| Nothing -> (id, []) | |
| cap_decl :: MapParser Decl | |
| cap_decl = do | |
| n <- name | |
| ref <- lookupRef n | |
| let n' = refToNameRef ref | |
| ms <- braces (sepEndBy cap_mapping opt_semi) | |
| return $ CapDecl n' ms | |
| cap_decls :: MapParser [Decl] | |
| cap_decls = do | |
| reserved "caps" | |
| braces $ many (try cap_decl) | |
| make_irq_mapping :: (Word, Name) -> MapParser CapMapping | |
| make_irq_mapping (slot, irq) = do | |
| ref <- lookupRef irq | |
| let irq' = refToNameRef ref | |
| return $ IRQMapping (Just slot) irq' | |
| make_irq_decl :: MapParser [Decl] | |
| make_irq_decl = do | |
| maps <- getState | |
| let irqs = Map.toList $ irqMap maps | |
| irq_decls <- mapM make_irq_mapping irqs | |
| return [IRQDecl irq_decls] | |
| all_decls :: Arch -> MapParser [Decl] | |
| all_decls arch = do | |
| objs <- obj_decls arch | |
| cov <- cov_decls | |
| caps <- cap_decls | |
| cdt <- cdt_decls | |
| irq <- make_irq_decl | |
| return $ objs ++ cov ++ caps ++ cdt ++ irq | |
| capDLDumpModule :: MapParser Module | |
| capDLDumpModule = do | |
| whiteSpace | |
| arch <- parse_arch | |
| decls <- all_decls arch | |
| eof | |
| return (Module arch decls) |