2021-12-07 21:51:25 +00:00
|
|
|
type mft_type =
|
|
|
|
| Dev_block_basic
|
|
|
|
| Dev_net_basic
|
|
|
|
| Reserved_first
|
|
|
|
|
2021-12-09 15:17:11 +00:00
|
|
|
type mft_entry =
|
2021-12-15 15:12:31 +00:00
|
|
|
| Dev_block_basic of string
|
|
|
|
| Dev_net_basic of string
|
2021-12-09 15:17:11 +00:00
|
|
|
|
2021-12-10 10:19:06 +00:00
|
|
|
type mft = {
|
|
|
|
version : int;
|
2021-12-13 13:58:44 +00:00
|
|
|
entries : mft_entry list;
|
2021-12-10 10:19:06 +00:00
|
|
|
}
|
|
|
|
|
2021-12-16 11:12:43 +00:00
|
|
|
type abi_target =
|
|
|
|
| Hvt
|
|
|
|
| Spt
|
|
|
|
| Virtio
|
|
|
|
| Muen
|
|
|
|
| Genode
|
|
|
|
| Xen
|
|
|
|
|
|
|
|
type abi = {
|
|
|
|
target : abi_target;
|
|
|
|
version : int32;
|
|
|
|
}
|
|
|
|
|
|
|
|
let mft_type_of_int : int32 -> (mft_type, _) result = function
|
|
|
|
| 1l -> Ok Dev_block_basic
|
|
|
|
| 2l -> Ok Dev_net_basic
|
|
|
|
| 1073741824l -> Ok Reserved_first
|
|
|
|
| v -> Error (`Msg ("unknown manifest entry type: " ^ Int32.to_string v))
|
|
|
|
|
|
|
|
let abi_target_of_int : int32 -> (abi_target, _) result = function
|
|
|
|
| 1l -> Ok Hvt
|
|
|
|
| 2l -> Ok Spt
|
|
|
|
| 3l -> Ok Virtio
|
|
|
|
| 4l -> Ok Muen
|
|
|
|
| 5l -> Ok Genode
|
|
|
|
| 6l -> Ok Xen
|
|
|
|
| v -> Error (`Msg ("unknown abi target: " ^ Int32.to_string v))
|
2021-12-13 13:58:44 +00:00
|
|
|
|
2021-12-09 15:17:11 +00:00
|
|
|
let pp_mft_entry ppf = function
|
2021-12-15 15:12:31 +00:00
|
|
|
| Dev_block_basic name ->
|
2021-12-10 10:19:06 +00:00
|
|
|
Fmt.pf ppf {|{@[<1>@ "name": %S,@ "type": "BLOCK_BASIC"@]@ }|} name
|
2021-12-15 15:12:31 +00:00
|
|
|
| Dev_net_basic name ->
|
2021-12-10 10:19:06 +00:00
|
|
|
Fmt.pf ppf {|{@[<1>@ "name": %S,@ "type": "NET_BASIC"@]@ }|} name
|
|
|
|
|
|
|
|
let pp_mft ppf { version; entries } =
|
|
|
|
Fmt.pf ppf
|
|
|
|
{|{@[<1>@ "type": "solo5.manifest",@ "version": %d,@ "devices": [@[<1>@ %a@]@ ]@]@ }|}
|
2021-12-13 13:58:44 +00:00
|
|
|
version Fmt.(list ~sep:(append (any ",") sp) pp_mft_entry) entries
|
|
|
|
|
2021-12-16 11:12:43 +00:00
|
|
|
let pp_abi_target ppf = function
|
|
|
|
| Hvt -> Format.fprintf ppf "hvt"
|
|
|
|
| Spt -> Format.fprintf ppf "spt"
|
|
|
|
| Virtio -> Format.fprintf ppf "virtio"
|
|
|
|
| Muen -> Format.fprintf ppf "muen"
|
|
|
|
| Genode -> Format.fprintf ppf "genode"
|
|
|
|
| Xen -> Format.fprintf ppf "xen"
|
|
|
|
|
|
|
|
let pp_abi ppf { version; target } =
|
|
|
|
Fmt.pf ppf
|
|
|
|
{|{@[<1>@ "type": "solo5.abi",@ "target": "%a",@ "version": %lu@ @]@ }|}
|
|
|
|
pp_abi_target target version
|
|
|
|
|
2021-12-13 13:58:44 +00:00
|
|
|
let ( let* ) = Result.bind
|
|
|
|
let guard m b = if not b then Error (`Msg m) else Ok ()
|
2021-12-09 15:17:11 +00:00
|
|
|
|
|
|
|
let sizeof_mft_entry = 104
|
2021-12-13 13:58:44 +00:00
|
|
|
let mft_max_entries = 64l
|
2021-12-09 15:17:11 +00:00
|
|
|
|
2025-01-30 12:22:56 +00:00
|
|
|
let parse_mft_entry s =
|
2021-12-13 13:58:44 +00:00
|
|
|
(* invariant: Cstruct.length buf = sizeof_mft_entry *)
|
2025-01-30 12:22:56 +00:00
|
|
|
let name_raw = String.sub s 0 68 in
|
|
|
|
let typ = String.get_int32_le s 68 in
|
|
|
|
let u = String.sub s 72 16 in
|
|
|
|
let b = String.sub s 88 8 in
|
|
|
|
let attached = String.get_uint8 s 96 <> 0 in
|
2021-12-13 13:58:44 +00:00
|
|
|
let* name =
|
2025-01-30 12:22:56 +00:00
|
|
|
String.index_opt name_raw '\000'
|
|
|
|
|> Option.map (fun idx -> String.sub name_raw 0 idx)
|
2021-12-13 13:58:44 +00:00
|
|
|
|> Option.to_result ~none:(`Msg "unterminated device name")
|
2021-12-09 15:17:11 +00:00
|
|
|
in
|
2025-01-30 12:22:56 +00:00
|
|
|
let* () = guard "non-zero mft_entry.u" (String.for_all ((=) '\000') u) in
|
|
|
|
let* () = guard "non-zero mft_entry.b" (String.for_all ((=) '\000') b) in
|
2021-12-13 13:58:44 +00:00
|
|
|
let* () = guard "non-zero mft_entry.attached" (not attached) in
|
2021-12-16 11:12:43 +00:00
|
|
|
let* typ = mft_type_of_int typ in
|
|
|
|
match typ with
|
2021-12-09 15:17:11 +00:00
|
|
|
| Reserved_first ->
|
2025-01-30 12:22:56 +00:00
|
|
|
let* () = guard "non-zero RESERVED_FIRST" (String.for_all ((=) '\000') name_raw) in
|
2021-12-13 13:58:44 +00:00
|
|
|
Ok `Reserved_first
|
2021-12-09 15:17:11 +00:00
|
|
|
| Dev_block_basic ->
|
2021-12-13 13:58:44 +00:00
|
|
|
Ok (`Dev_block_basic name)
|
2021-12-09 15:17:11 +00:00
|
|
|
| Dev_net_basic ->
|
2021-12-13 13:58:44 +00:00
|
|
|
Ok (`Dev_net_basic name)
|
2021-12-09 15:17:11 +00:00
|
|
|
|
2025-01-30 12:22:56 +00:00
|
|
|
let parse_mft s =
|
2021-12-13 13:58:44 +00:00
|
|
|
let* () = guard "manifest too small"
|
2025-01-30 12:22:56 +00:00
|
|
|
(String.length s >= 4 + 8 + sizeof_mft_entry)
|
2021-12-13 13:58:44 +00:00
|
|
|
in
|
2021-12-15 15:12:31 +00:00
|
|
|
(* Solo5 defines a struct mft1_note consisting of the ELF note header
|
|
|
|
* followed by a struct mft for reading and writing the ELF note. The note
|
|
|
|
* header is 20 bytes long, so to get 8-byte alignment the note header is
|
|
|
|
* padded with 4 bytes. See {[solo5/mft_abi.h]}. *)
|
2025-01-30 12:22:56 +00:00
|
|
|
let version = String.get_int32_le s 4
|
|
|
|
and entries = String.get_int32_le s 8
|
2021-12-09 15:17:11 +00:00
|
|
|
in
|
2021-12-13 13:58:44 +00:00
|
|
|
let* () = guard "unsupported manifest version" (version = 1l) in
|
|
|
|
let* () = guard "zero manifest entries" (Int32.unsigned_compare entries 0l > 0) in
|
|
|
|
(* this implicitly checks [Int32.to_int entries > 0] *)
|
|
|
|
let* () = guard "too many manifest entries"
|
|
|
|
(Int32.unsigned_compare entries mft_max_entries <= 0)
|
|
|
|
in
|
2021-12-15 15:12:31 +00:00
|
|
|
(* We have checked that entries interpreted unsigned is between 0 and
|
|
|
|
* mft_max_entries, so this is safely equivalent to:
|
|
|
|
* (Option.get (Int32.unsigned_to_int entries) *)
|
2021-12-13 13:58:44 +00:00
|
|
|
let entries = Int32.to_int entries in
|
2025-01-30 12:22:56 +00:00
|
|
|
let off = 12 in
|
2021-12-13 13:58:44 +00:00
|
|
|
let* () = guard "unexpected note size"
|
2025-01-30 12:22:56 +00:00
|
|
|
(String.length s = entries * sizeof_mft_entry + 12)
|
2021-12-13 13:58:44 +00:00
|
|
|
in
|
|
|
|
let* () =
|
2025-01-30 12:22:56 +00:00
|
|
|
match parse_mft_entry (String.sub s off sizeof_mft_entry) with
|
2021-12-13 13:58:44 +00:00
|
|
|
| Ok `Reserved_first -> Ok ()
|
|
|
|
| _ -> Error (`Msg "expected RESERVED_FIRST")
|
|
|
|
in
|
2025-01-30 12:22:56 +00:00
|
|
|
let off = off + sizeof_mft_entry in
|
2021-12-09 15:17:11 +00:00
|
|
|
let entries =
|
2021-12-13 13:58:44 +00:00
|
|
|
Array.init (entries - 1)
|
2025-01-30 12:22:56 +00:00
|
|
|
(fun i -> String.sub s (off + i * sizeof_mft_entry) sizeof_mft_entry)
|
2021-12-13 13:58:44 +00:00
|
|
|
in
|
|
|
|
let* entries =
|
|
|
|
Array.fold_left
|
2025-01-30 12:22:56 +00:00
|
|
|
(fun r s ->
|
2021-12-13 13:58:44 +00:00
|
|
|
let* acc = r in
|
2025-01-30 12:22:56 +00:00
|
|
|
let* mft_entry = parse_mft_entry s in
|
2021-12-13 13:58:44 +00:00
|
|
|
match mft_entry with
|
2021-12-15 15:12:31 +00:00
|
|
|
| `Dev_block_basic name -> Ok (Dev_block_basic name :: acc)
|
|
|
|
| `Dev_net_basic name -> Ok (Dev_net_basic name :: acc)
|
2021-12-13 13:58:44 +00:00
|
|
|
| `Reserved_first -> Error (`Msg "found RESERVED_FIRST not as first entry"))
|
|
|
|
(Ok [])
|
|
|
|
entries
|
2021-12-14 12:39:56 +00:00
|
|
|
|> Result.map List.rev
|
2021-12-09 15:17:11 +00:00
|
|
|
in
|
2021-12-13 13:58:44 +00:00
|
|
|
Ok { version = Int32.to_int version; entries }
|
2021-12-09 15:17:11 +00:00
|
|
|
|
2025-01-30 12:22:56 +00:00
|
|
|
let parse_abi s =
|
|
|
|
let* () = guard "abi manifest size mismatch" (String.length s = 4 * 4) in
|
|
|
|
let target = String.get_int32_le s 0 in
|
|
|
|
let version = String.get_int32_le s 4 in
|
|
|
|
let reserved0 = String.get_int32_le s 8 in
|
|
|
|
let reserved1 = String.get_int32_le s 12 in
|
2021-12-16 11:12:43 +00:00
|
|
|
let* target = abi_target_of_int target in
|
|
|
|
let* () = guard "non-zero reserved0" (reserved0 = 0l) in
|
|
|
|
let* () = guard "non-zero reserved1" (reserved1 = 0l) in
|
|
|
|
(* XXX: should we check version = 1l ? *)
|
|
|
|
Ok { target; version }
|
|
|
|
|
2025-01-30 11:39:39 +00:00
|
|
|
let query_manifest c =
|
|
|
|
match Elf.find c Elf.section_manifest Elf.typ_mft1 with
|
2025-01-24 19:34:20 +00:00
|
|
|
| None -> Error (`Msg "manifest not found")
|
|
|
|
| Some desc -> parse_mft desc
|
2025-01-30 13:44:07 +00:00
|
|
|
| exception Elf.Elf_error | exception Cachet.Out_of_bounds _ ->
|
|
|
|
Error (`Msg "error during ELF parsing")
|
2022-01-28 12:47:30 +00:00
|
|
|
|
2025-01-30 11:39:39 +00:00
|
|
|
let query_abi c =
|
|
|
|
match Elf.find c Elf.section_abi Elf.typ_abi1 with
|
2025-01-24 19:34:20 +00:00
|
|
|
| None -> Error (`Msg "manifest not found")
|
|
|
|
| Some desc -> parse_abi desc
|
2025-01-30 13:44:07 +00:00
|
|
|
| exception Elf.Elf_error | exception Cachet.Out_of_bounds _ ->
|
|
|
|
Error (`Msg "error during ELF parsing")
|