diff --git a/proxyclient/experiments/pmp_init.py b/proxyclient/experiments/pmp_init.py new file mode 100644 index 000000000..d766873de --- /dev/null +++ b/proxyclient/experiments/pmp_init.py @@ -0,0 +1,276 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: MIT +import sys, pathlib +sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) + +import struct +from construct import * +from copy import deepcopy +from m1n1.setup import * +from m1n1.shell import run_shell +from m1n1.fw.asc import StandardASC +from m1n1.fw.asc.base import ASCBaseEndpoint, msg_handler +from m1n1.utils import * +from m1n1.hw.dart import DART + +def round_up(x, y): return ((x + (y - 1)) & (-y)) +def round_down(x, y): return (x - (x % y)) + +AOPBootargsItem = Struct( + "key" / PaddedString(4, "utf8"), + "size" / Int32ul, +) + +class AOPBootargs: + def __init__(self, bytes_): + self.blob = bytearray(bytes_) + self.index = self.build_index(self.blob) + + def build_index(self, blob): + off = 0 + fields = [] + while off < len(blob): + item = AOPBootargsItem.parse(blob[off:off+AOPBootargsItem.sizeof()]) + off += AOPBootargsItem.sizeof() + fields.append((item.key, (off, item.size))) + off += item.size + if off > len(blob): + raise ValueError('blob overran during parsing') + return dict(fields) + + def items(self): + for key, span in self.index.items(): + off, length = span + yield key, self.blob[off:off + length] + + def __getitem__(self, key): + off, length = self.index[key] + return bytes(self.blob[off:off + length]) + + def __setitem__(self, key, value): + off, length = self.index[key] + if type(value) is int: + value = int.to_bytes(value, length, byteorder='little') + elif type(value) is str: + value = value.encode('ascii') + if len(value) > length: + raise ValueError(f'field {key:s} overflown') + self.blob[off:off + length] = value + + def update(self, keyvals): + for key, val in keyvals.items(): + self[key] = val + + def keys(self): + return self.index.keys() + + def dump(self, logger): + for key, val in self.items(): + logger(f"{key:4s} = {val}") + + def dump_diff(self, other, logger): + assert self.index == other.index + for key in self.keys(): + if self[key] != other[key]: + logger(f"\t{key:4s} = {self[key]} -> {other[key]}") + + def to_bytes(self): + return bytes(self.blob) + +class AOPBase: + def __init__(self, u): + self.u = u + self.nub_base = u.adt["/arm-io/pmp/iop-pmp-nub"].region_base + + @property + def _bootargs_span(self): + """ + [cpu1] MMIO: R.4 0x24ac0022c (aop[2], offset 0x22c) = 0xaffd8 // offset + [cpu1] MMIO: R.4 0x24ac00230 (aop[2], offset 0x230) = 0x2ae // size + [cpu1] MMIO: R.4 0x24ac00234 (aop[2], offset 0x234) = 0x82000 // va? low + [cpu1] MMIO: R.4 0x24ac00238 (aop[2], offset 0x238) = 0x0 // va? high + [cpu1] MMIO: R.4 0x24ac0023c (aop[2], offset 0x23c) = 0x4ac82000 // phys low + [cpu1] MMIO: R.4 0x24ac00240 (aop[2], offset 0x240) = 0x2 // phys high + [cpu1] MMIO: W.4 0x24acaffd8 (aop[2], offset 0xaffd8) = 0x53544b47 // start of bootargs + [cpu1] MMIO: W.4 0x24acaffdc (aop[2], offset 0xaffdc) = 0x8 + [cpu1] MMIO: W.4 0x24acaffe0 (aop[2], offset 0xaffe0) = 0x73eed2a3 + ... + [cpu1] MMIO: W.4 0x24acb0280 (aop[2], offset 0xb0280) = 0x10000 + [cpu1] MMIO: W.4 0x24acb0284 (aop[2], offset 0xb0284) = 0x0 // end of bootargs + """ + offset = self.u.proxy.read32(self.nub_base + 0x22c) # 0x224 in 12.3 + size = self.u.proxy.read32(self.nub_base + 0x230) # 0x228 in 12.3 + return (self.nub_base + offset, size) + + def read_bootargs(self): + addr, size = self._bootargs_span + blob = self.u.proxy.iface.readmem(addr, size) + return AOPBootargs(blob) + + def write_bootargs(self, args): + base, _ = self._bootargs_span + self.u.proxy.iface.writemem(base, args.to_bytes()) + + def update_bootargs(self, keyval, logger=print): + args = self.read_bootargs() + old = deepcopy(args) + args.update(keyval) + self.write_bootargs(args) + old.dump_diff(args, logger) + +class PMPMessage(Register64): + TYPE = 56, 48 + +class PMPMessage_IOVATableAck(PMPMessage): + TYPE = 56, 48, Constant(0x11) + IOVA = 47, 0 + +class PMPMessage_Malloc(PMPMessage): + TYPE = 56, 48, Constant(0x12) + SIZE = 23, 0 + +class PMPMessage_MallocAck(PMPMessage): + TYPE = 56, 48, Constant(0x13) + IOVA = 47, 0 + +class PMPMessage_Free(PMPMessage): + TYPE = 56, 48, Constant(0x14) + IOVA = 47, 0 + +class PMPMessage_SetBuf(PMPMessage): + TYPE = 56, 48, Constant(0x30) + IOVA = 47, 0 + +class PMPMessage_Advertise(PMPMessage): + TYPE = 56, 48, Constant(0x32) + IOVA = 47, 0 + +class PMPMessage_AdvertiseAck(PMPMessage): + TYPE = 56, 48, Constant(0x33) + INDEX = 47, 32 + SIZE = 31, 0 + +class PMPMessage_SetVal(PMPMessage): + TYPE = 56, 48, Constant(0x34) + INDEX = 15, 0 + +class PMPMessage_SetValAck(PMPMessage): + TYPE = 56, 48, Constant(0x35) + SIZE = 31, 0 + +class PMPEp(ASCBaseEndpoint): + BASE_MESSAGE = PMPMessage + SHORT = "pmp" + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.allocs = {} + self.ioregs = [] + + @msg_handler(0x10, PMPMessage) + def GetIOVATable(self, msg): + self.log(f'IovaTable: {msg}') + table, table_dva = self.asc.ioalloc(512) + self.asc.iowrite(table_dva, b'\0' * 512) + pio_base = u.adt["/arm-io/dart-pmp"].pio_vm_base + granularity = u.adt["/arm-io/dart-pmp"].pio_granularity + i = 0 + for j in range(4, len(u.adt["/arm-io/pmp"].reg)): + host_addr, size = u.adt["/arm-io/pmp"].get_reg(j) + self.asc.dart.iomap_at(0, pio_base, host_addr, size) + self.asc.dart.invalidate_streams(1) + self.asc.iowrite(table_dva + 24 * i, struct.pack(" 0xffffffff: + data = struct.pack(" c_int { + let ptr: usize = unsafe { adt.add(offset as usize) as usize }; + let p = ADTProperty::from_ptr(ptr).unwrap(); + unsafe { + p.as_ptr() + .add(size_of::<[c_char; 32]>()) + .add(size_of::()) + .add((p.size as usize + (ADT_ALIGN - 1)) & !(ADT_ALIGN - 1)) + .sub(adt as usize) as c_int + } +} + #[no_mangle] pub unsafe extern "C" fn adt_first_child_offset(_dt: *const c_void, offset: c_int) -> c_int { let ptr: *const ADTNode = unsafe { adt.add(offset as usize) as *const ADTNode }; diff --git a/src/adt.h b/src/adt.h index d963703ed..6bea107d0 100644 --- a/src/adt.h +++ b/src/adt.h @@ -18,6 +18,8 @@ struct adt_node_hdr { u32 child_count; }; +#define ADT_PROP(adt, offset) ((const struct adt_property *)(((u8 *)(adt)) + (offset))) + /* This API is designed to match libfdt's read-only API */ /* Required for Rust until we move xnuboot across */ @@ -30,6 +32,8 @@ int adt_get_property_count(const void *adt, int offset); int adt_first_property_offset(const void *adt, int offset); +int adt_next_property_offset(const void *adt, int offset); + const struct adt_property *adt_get_property_by_offset(const void *adt, int offset); int adt_get_child_count(const void *adt, int offset); @@ -63,6 +67,13 @@ bool adt_is_compatible_at(const void *adt, int nodeoffset, const char *compat, s for (node = adt_first_child_offset(adt, node); _child_count--; \ node = adt_next_sibling_offset(adt, node)) +#define ADT_FOREACH_PROPERTY(adt, node, prop) \ + for (int _prop_count = adt_get_property_count(adt, node), \ + _poff = adt_first_property_offset(adt, node); \ + _prop_count; _prop_count = 0) \ + for (const struct adt_property *prop = ADT_PROP(adt, _poff); _prop_count--; \ + prop = ADT_PROP(adt, _poff = adt_next_property_offset(adt, _poff))) + /* Common ADT properties */ struct adt_segment_ranges { u64 phys; diff --git a/src/kboot.c b/src/kboot.c index 19fef6f73..15232a2c6 100644 --- a/src/kboot.c +++ b/src/kboot.c @@ -1984,6 +1984,55 @@ static int dt_set_display(void) return dt_vram_reserved_region("dcp", "disp0"); } +static const char *excluded_pmp_props[] = { + "compatible", "AAPL,phandle", "region-base", "region-size", + "segment-names", "segment-ranges", "pre-loaded", "firmware-name", + "dram-capacity", "coredump-enable", NULL, +}; + +static int dt_set_pmp(void) +{ + int chosen_anode = adt_path_offset(adt, "/chosen"); + if (chosen_anode < 0) + bail("ADT: /chosen not found \n"); + int pmp_iop_anode = adt_path_offset(adt, "/arm-io/pmp/iop-pmp-nub"); + if (pmp_iop_anode < 0) + bail("ADT: /arm-io/pmp/iop-pmp-nub not found \n"); + + u32 board_id, dram_vendor_id, dram_capacity; + if (ADT_GETPROP(adt, chosen_anode, "board-id", &board_id) < 0) + bail("ADT: failed to get board id\n"); + if (ADT_GETPROP(adt, chosen_anode, "dram-vendor-id", &dram_vendor_id) < 0) + bail("ADT: failed to get dram vendor id\n"); + if (ADT_GETPROP(adt, pmp_iop_anode, "dram-capacity", &dram_capacity) < 0) + bail("ADT: failed to get dram capacity\n"); + + int pmp_node = fdt_path_offset(dt, "pmp"); + if (pmp_node < 0) + bail("FDT: pmp not not found in devtree\n"); + if (fdt_setprop_u32(dt, pmp_node, "apple,board-id", board_id)) + bail("FDT: failed to set board id\n"); + if (fdt_setprop_u32(dt, pmp_node, "apple,dram-vendor-id", dram_vendor_id)) + bail("FDT: failed to set dram vendor id\n"); + if (fdt_setprop_u32(dt, pmp_node, "apple,dram-capacity", dram_capacity)) + bail("FDT: failed to set dram capacity\n"); + + int tunables_node = fdt_subnode_offset(dt, pmp_node, "tunables"); + if (tunables_node < 0) + bail("FDT: pmp tunables not not found in devtree\n"); + + ADT_FOREACH_PROPERTY(adt, pmp_iop_anode, prop) + { + for (int i = 0; excluded_pmp_props[i]; i++) + if (!strcmp(prop->name, excluded_pmp_props[i])) + goto next_adt_prop; + if (fdt_setprop(dt, tunables_node, prop->name, prop->value, prop->size)) + bail("FDT: failed to transfer pmp tunable"); + next_adt_prop:; + } + return 0; +} + static int dt_set_sep(void) { const char *path = fdt_get_alias(dt, "sep"); @@ -2685,6 +2734,8 @@ int kboot_prepare_dt(void *fdt) return -1; if (dt_set_sep()) return -1; + if (dt_set_pmp()) + return -1; if (dt_set_nvram()) return -1; if (dt_set_ipd())