blob: 8c2451c2ef9304eba3a8f9894e591f912a979d14 [file]
--
-- Copyright 2018, Data61
-- Commonwealth Scientific and Industrial Research Organisation (CSIRO)
-- ABN 41 687 119 230.
--
-- This software may be distributed and modified according to the terms of
-- the GNU General Public License version 2. Note that NO WARRANTY is provided.
-- See "LICENSE_GPLv2.txt" for details.
--
-- @TAG(DATA61_GPL)
--
{-# LANGUAGE CPP #-}
{-# LANGUAGE ImplicitParams #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE TemplateHaskell #-}
module Readpage where
import Control.Arrow
import Data.Array
import Data.Bits
import Data.List
import qualified Data.Map as M
import Data.Maybe (fromJust)
import Data.Tuple.Select (sel1, sel2, sel3)
import Data.Word
-- import Data.Void
import Numeric (showHex)
import Test.QuickCheck hiding (Success)
import qualified Test.QuickCheck as Qc (Result(..))
import CogentMonad
import Corres
import qualified Readpage_Shallow_Desugar_Tuples as C
import Fsop as Ax
import Util
-- import Debug.Trace
-- /////////////////////////////////////////////////////////////////////////////
--
-- * Haskell spec.
data VfsInode = VfsInode { ino :: U32, isize :: VfsSize } deriving (Eq, Ord, Show)
vfs_inode_get_ino :: VfsInode -> VfsIno
vfs_inode_get_ino = ino
vfs_inode_get_size :: VfsInode -> VfsSize
vfs_inode_get_size = isize
type OSPageOffset = U64
hs_fsop_readpage :: AfsState
-> VfsInode
-> OSPageOffset
-> WordArray U8
-> CogentMonad (Either ErrCode (WordArray U8))
hs_fsop_readpage afs vnode block buf =
let size = vfs_inode_get_size vnode :: U64 -- the number of bytes we need to read
limit = size `shiftR` fromIntegral bilbyFsBlockShift -- the number of blocks we need to read
in if | block > limit -> return $ Left eNoEnt
-- ^ if we are reading beyond the last block we need to read, return an zeroed buffer
| block == limit && (size `mod` fromIntegral bilbyFsBlockSize == 0) ->
return $ Right buf
-- ^ if we are reading the "last" one which extra bytes in this block is 0, then return old buffer
| otherwise -> return (Right $ read_block afs vnode block) <|>
(Left <$> [eIO, eNoMem, eInval, eBadF, eNoEnt])
-- ^ if we are reading a block which contains data, then we read the block
read_block :: AfsState -> VfsInode -> OSPageOffset -> WordArray U8
read_block afs inode block =
let pages = fromJust $ M.lookup (vfs_inode_get_ino inode) afs
in pages !! fromIntegral block
-- /////////////////////////////////////////////////////////////////////////////
--
-- * Testing @fsop_readpage@
prop_corres_fsop_readpage :: Property
prop_corres_fsop_readpage =
forAll gen_fsop_readpage_arg $ \ic ->
forAll gen_oracle $ \o ->
let ?o = o in
let ia = abs_fsop_readpage_arg ic
oa = uncurry4 hs_fsop_readpage ia
oc = C.fsop_readpage ic
in corres rel_fsop_readpage_ret oa oc
gen_oracle :: Gen O
gen_oracle = frequency [ (9, pure 0)
, (1, elements [eIO, eNoMem, eInval, eBadF])
]
gen_fsop_readpage_arg :: Gen C.Fsop_readpage_ArgT
gen_fsop_readpage_arg = do
ino <- arbitrary
m <- getSmall <$> arbitrary
isize <- frequency [ (1, pure (m * fromIntegral bilbyFsBlockSize))
, (1, fromIntegral <$> (arbitrary :: Gen VfsSize)) ]
-- \ ^ NOTE: when this size is too large, it will be extremely slow
C.R7 <$> pure ()
<*> gen_FsState ino isize
<*> gen_VfsInode ino isize
<*> gen_OSPageOffset isize
<*> gen_Buffer
gen_FsState :: C.VfsIno -> C.VfsSize -> Gen C.FsState
gen_FsState ino isize = C.R22 <$> gen_FsopState <*> gen_MountState <*> gen_OstoreState ino isize
gen_FsopState :: Gen C.FsopState
gen_FsopState = arbitrary
gen_MountState :: Gen C.MountState
gen_MountState = arbitrary
gen_OstoreState :: C.VfsIno -> C.VfsSize -> Gen C.OstoreState
gen_OstoreState ino isize = do
let numOfBlk = ceiling (fromInteger (fromIntegral isize) / fromInteger (fromIntegral bilbyFsBlockSize))
blk <- choose (0, numOfBlk - 1)
oids <- sublistOf (map (obj_id_data_mk ino . fromIntegral) $ blk `delete` [0 .. numOfBlk - 1])
hit_entry <- (,) <$> pure (obj_id_data_mk ino blk) <*> gen_Obj
entries <- zip oids <$> listOf gen_Obj
return $ M.fromList (hit_entry : entries)
gen_Obj :: Gen C.Obj
gen_Obj = C.R28 <$> arbitrary -- magic
<*> arbitrary -- crc
<*> arbitrary -- sqnum
<*> arbitrary -- offs
<*> arbitrary -- len
<*> arbitrary -- trans
<*> arbitrary -- otype
<*> gen_ObjUnion_Data -- ounion
gen_ObjUnion_Data :: Gen C.ObjUnion
gen_ObjUnion_Data = C.V29_TObjData <$> gen_ObjData
-- In fact, the buffers in ObjData can be smaller than the max size, then the abs
-- function @abs_OstoreState@ will do more work to pad the smaller ones.
gen_ObjData :: Gen C.ObjData
gen_ObjData = C.R30 <$> arbitrary <*> (gen_WordArray_Word8 =<< choose (1, bilbyFsBlockSize))
gen_VfsInode :: C.VfsIno -> C.VfsSize -> Gen C.VfsInode
gen_VfsInode ino isize = C.R20 <$> gen_VfsInodeAbstract ino isize <*> (C.R21 <$> arbitrary)
gen_VfsInodeAbstract :: C.VfsIno -> C.VfsSize -> Gen C.VfsInodeAbstract
gen_VfsInodeAbstract = (return .) . C.VfsInodeAbstract
gen_OSPageOffset :: C.VfsSize -> Gen C.OSPageOffset
gen_OSPageOffset isize = do
let limit = isize `shiftR` fromIntegral bilbyFsBlockShift
pos <- getPositive <$> arbitrary
frequency [ (1, pure $ limit + pos)
, (1, pure $ limit)
, (3, choose (1, limit - 1))
]
gen_Buffer :: Gen C.Buffer
gen_Buffer = do
arr <- gen_WordArray_Word8 bilbyFsBlockSize
return $ C.R8 arr bilbyFsBlockSize
gen_WordArray_Word8 :: Word32 -> Gen (WordArray Word8)
gen_WordArray_Word8 sz = do
elems <- vector (fromIntegral sz)
return $ listArray (0, fromIntegral sz - 1) elems
abs_fsop_readpage_arg :: C.Fsop_readpage_ArgT -> (AfsState, VfsInode, OSPageOffset, WordArray U8)
abs_fsop_readpage_arg (C.R7 _ fs_st_c vnode_c block_c buf_c) =
let ostore_c = C.ostore_st fs_st_c
C.R20 (C.VfsInodeAbstract ino isize) _ = vnode_c
vnode_a = VfsInode ino isize
block_a = block_c
buf_a = abs_Buffer buf_c
afs_a = abs_OstoreState ostore_c isize
in (afs_a, vnode_a, block_a, buf_a)
abs_OstoreState :: C.OstoreState -> C.VfsSize -> AfsState
abs_OstoreState ostore isize =
let ostore' = M.toList ostore
tuples = for ostore' $ \(oid, obj) ->
let C.R28 _ _ _ _ _ _ _ ounion = obj
C.V29_TObjData (C.R30 _ odata) = ounion
ino = inum_from_obj_id oid
blk = oid Data.Bits..&. 0x1fffffff -- lower 29 bits
in (ino, blk, odata)
ino = sel1 $ head tuples
numOfBlk = ceiling (fromInteger (fromIntegral isize) / fromInteger (fromIntegral bilbyFsBlockSize))
all0 = listArray (0, bilbyFsBlockSize - 1) (replicate (fromIntegral bilbyFsBlockSize) 0)
base = map (\idx -> (idx, all0)) [0 .. numOfBlk - 1]
pages = (M.fromList $ map (\(_,b,c) -> (b,c)) tuples) `M.union` M.fromList base
pages' = M.map (\page -> if fromIntegral (length page) < bilbyFsBlockSize then pad page else page) pages
in M.fromList [(ino, M.elems pages')]
where
pad :: WordArray U8 -> WordArray U8
pad arr = let elems' = elems arr ++ repeat 0
in listArray (0, bilbyFsBlockSize - 1) elems'
abs_Buffer :: C.Buffer -> WordArray U8
abs_Buffer (C.R8 buf bd) =
let len = min (fromIntegral $ length buf) bd
arrElems = genericTake len $ elems buf
in listArray (0, len - 1) arrElems
rel_fsop_readpage_ret :: Either ErrCode (WordArray U8)
-> C.Fsop_readpage_RetT
-> Bool
rel_fsop_readpage_ret (Left e_a) (_, C.V34_Error e_c) = e_a == e_c
rel_fsop_readpage_ret (Right arr_a) (C.R6 _ _ _ (C.R8 data_c bound_c), C.V34_Success ()) =
let len_a = length arr_a
len_c = length data_c
in len_a == min len_c (fromIntegral bound_c) &&
elems arr_a == elems data_c
rel_fsop_readpage_ret _ _ = False
-- /////////////////////////////////////////////////////////////////////////////
--
-- low-levle properties
-- /////////////////////////////////////////////////////////////////////////////
--
-- misc.
for = flip map
-- /////////////////////////////////////////////////////////////////////////////
--
-- top level
main = do
r <- quickCheckWithResult (stdArgs { chatty = True, maxSuccess = 500, maxSize = 40 }) prop_corres_fsop_readpage
case r of Qc.Success {} -> putStrLn "Passed!"
_ -> putStrLn $ "Failed!"