|
| 1 | +#!/usr/bin/env python3 |
| 2 | +# SPDX-License-Identifier: MIT |
| 3 | +import sys, pathlib |
| 4 | +sys.path.append(str(pathlib.Path(__file__).resolve().parents[1])) |
| 5 | + |
| 6 | +import struct |
| 7 | +from construct import * |
| 8 | +from copy import deepcopy |
| 9 | +from m1n1.setup import * |
| 10 | +from m1n1.shell import run_shell |
| 11 | +from m1n1.fw.asc import StandardASC |
| 12 | +from m1n1.fw.asc.base import ASCBaseEndpoint, msg_handler |
| 13 | +from m1n1.utils import * |
| 14 | +from m1n1.hw.dart import DART |
| 15 | + |
| 16 | +def round_up(x, y): return ((x + (y - 1)) & (-y)) |
| 17 | +def round_down(x, y): return (x - (x % y)) |
| 18 | + |
| 19 | +AOPBootargsItem = Struct( |
| 20 | + "key" / PaddedString(4, "utf8"), |
| 21 | + "size" / Int32ul, |
| 22 | +) |
| 23 | + |
| 24 | +class AOPBootargs: |
| 25 | + def __init__(self, bytes_): |
| 26 | + self.blob = bytearray(bytes_) |
| 27 | + self.index = self.build_index(self.blob) |
| 28 | + |
| 29 | + def build_index(self, blob): |
| 30 | + off = 0 |
| 31 | + fields = [] |
| 32 | + while off < len(blob): |
| 33 | + item = AOPBootargsItem.parse(blob[off:off+AOPBootargsItem.sizeof()]) |
| 34 | + off += AOPBootargsItem.sizeof() |
| 35 | + fields.append((item.key, (off, item.size))) |
| 36 | + off += item.size |
| 37 | + if off > len(blob): |
| 38 | + raise ValueError('blob overran during parsing') |
| 39 | + return dict(fields) |
| 40 | + |
| 41 | + def items(self): |
| 42 | + for key, span in self.index.items(): |
| 43 | + off, length = span |
| 44 | + yield key, self.blob[off:off + length] |
| 45 | + |
| 46 | + def __getitem__(self, key): |
| 47 | + off, length = self.index[key] |
| 48 | + return bytes(self.blob[off:off + length]) |
| 49 | + |
| 50 | + def __setitem__(self, key, value): |
| 51 | + off, length = self.index[key] |
| 52 | + if type(value) is int: |
| 53 | + value = int.to_bytes(value, length, byteorder='little') |
| 54 | + elif type(value) is str: |
| 55 | + value = value.encode('ascii') |
| 56 | + if len(value) > length: |
| 57 | + raise ValueError(f'field {key:s} overflown') |
| 58 | + self.blob[off:off + length] = value |
| 59 | + |
| 60 | + def update(self, keyvals): |
| 61 | + for key, val in keyvals.items(): |
| 62 | + self[key] = val |
| 63 | + |
| 64 | + def keys(self): |
| 65 | + return self.index.keys() |
| 66 | + |
| 67 | + def dump(self, logger): |
| 68 | + for key, val in self.items(): |
| 69 | + logger(f"{key:4s} = {val}") |
| 70 | + |
| 71 | + def dump_diff(self, other, logger): |
| 72 | + assert self.index == other.index |
| 73 | + for key in self.keys(): |
| 74 | + if self[key] != other[key]: |
| 75 | + logger(f"\t{key:4s} = {self[key]} -> {other[key]}") |
| 76 | + |
| 77 | + def to_bytes(self): |
| 78 | + return bytes(self.blob) |
| 79 | + |
| 80 | +class AOPBase: |
| 81 | + def __init__(self, u): |
| 82 | + self.u = u |
| 83 | + self.nub_base = u.adt["/arm-io/pmp/iop-pmp-nub"].region_base |
| 84 | + |
| 85 | + @property |
| 86 | + def _bootargs_span(self): |
| 87 | + """ |
| 88 | + [cpu1] MMIO: R.4 0x24ac0022c (aop[2], offset 0x22c) = 0xaffd8 // offset |
| 89 | + [cpu1] MMIO: R.4 0x24ac00230 (aop[2], offset 0x230) = 0x2ae // size |
| 90 | + [cpu1] MMIO: R.4 0x24ac00234 (aop[2], offset 0x234) = 0x82000 // va? low |
| 91 | + [cpu1] MMIO: R.4 0x24ac00238 (aop[2], offset 0x238) = 0x0 // va? high |
| 92 | + [cpu1] MMIO: R.4 0x24ac0023c (aop[2], offset 0x23c) = 0x4ac82000 // phys low |
| 93 | + [cpu1] MMIO: R.4 0x24ac00240 (aop[2], offset 0x240) = 0x2 // phys high |
| 94 | + [cpu1] MMIO: W.4 0x24acaffd8 (aop[2], offset 0xaffd8) = 0x53544b47 // start of bootargs |
| 95 | + [cpu1] MMIO: W.4 0x24acaffdc (aop[2], offset 0xaffdc) = 0x8 |
| 96 | + [cpu1] MMIO: W.4 0x24acaffe0 (aop[2], offset 0xaffe0) = 0x73eed2a3 |
| 97 | + ... |
| 98 | + [cpu1] MMIO: W.4 0x24acb0280 (aop[2], offset 0xb0280) = 0x10000 |
| 99 | + [cpu1] MMIO: W.4 0x24acb0284 (aop[2], offset 0xb0284) = 0x0 // end of bootargs |
| 100 | + """ |
| 101 | + offset = self.u.proxy.read32(self.nub_base + 0x22c) # 0x224 in 12.3 |
| 102 | + size = self.u.proxy.read32(self.nub_base + 0x230) # 0x228 in 12.3 |
| 103 | + return (self.nub_base + offset, size) |
| 104 | + |
| 105 | + def read_bootargs(self): |
| 106 | + addr, size = self._bootargs_span |
| 107 | + blob = self.u.proxy.iface.readmem(addr, size) |
| 108 | + return AOPBootargs(blob) |
| 109 | + |
| 110 | + def write_bootargs(self, args): |
| 111 | + base, _ = self._bootargs_span |
| 112 | + self.u.proxy.iface.writemem(base, args.to_bytes()) |
| 113 | + |
| 114 | + def update_bootargs(self, keyval, logger=print): |
| 115 | + args = self.read_bootargs() |
| 116 | + old = deepcopy(args) |
| 117 | + args.update(keyval) |
| 118 | + self.write_bootargs(args) |
| 119 | + old.dump_diff(args, logger) |
| 120 | + |
| 121 | +class PMPMessage(Register64): |
| 122 | + TYPE = 56, 48 |
| 123 | + |
| 124 | +class PMPMessage_IOVATableAck(PMPMessage): |
| 125 | + TYPE = 56, 48, Constant(0x11) |
| 126 | + IOVA = 47, 0 |
| 127 | + |
| 128 | +class PMPMessage_Malloc(PMPMessage): |
| 129 | + TYPE = 56, 48, Constant(0x12) |
| 130 | + SIZE = 23, 0 |
| 131 | + |
| 132 | +class PMPMessage_MallocAck(PMPMessage): |
| 133 | + TYPE = 56, 48, Constant(0x13) |
| 134 | + IOVA = 47, 0 |
| 135 | + |
| 136 | +class PMPMessage_Free(PMPMessage): |
| 137 | + TYPE = 56, 48, Constant(0x14) |
| 138 | + IOVA = 47, 0 |
| 139 | + |
| 140 | +class PMPMessage_SetBuf(PMPMessage): |
| 141 | + TYPE = 56, 48, Constant(0x30) |
| 142 | + IOVA = 47, 0 |
| 143 | + |
| 144 | +class PMPMessage_Advertise(PMPMessage): |
| 145 | + TYPE = 56, 48, Constant(0x32) |
| 146 | + IOVA = 47, 0 |
| 147 | + |
| 148 | +class PMPMessage_AdvertiseAck(PMPMessage): |
| 149 | + TYPE = 56, 48, Constant(0x33) |
| 150 | + INDEX = 47, 32 |
| 151 | + SIZE = 31, 0 |
| 152 | + |
| 153 | +class PMPMessage_SetVal(PMPMessage): |
| 154 | + TYPE = 56, 48, Constant(0x34) |
| 155 | + INDEX = 15, 0 |
| 156 | + |
| 157 | +class PMPMessage_SetValAck(PMPMessage): |
| 158 | + TYPE = 56, 48, Constant(0x35) |
| 159 | + SIZE = 31, 0 |
| 160 | + |
| 161 | +class PMPEp(ASCBaseEndpoint): |
| 162 | + BASE_MESSAGE = PMPMessage |
| 163 | + SHORT = "pmp" |
| 164 | + def __init__(self, *args, **kwargs): |
| 165 | + super().__init__(*args, **kwargs) |
| 166 | + self.allocs = {} |
| 167 | + self.ioregs = [] |
| 168 | + |
| 169 | + @msg_handler(0x10, PMPMessage) |
| 170 | + def GetIOVATable(self, msg): |
| 171 | + self.log(f'IovaTable: {msg}') |
| 172 | + table, table_dva = self.asc.ioalloc(512) |
| 173 | + self.asc.iowrite(table_dva, b'\0' * 512) |
| 174 | + pio_base = u.adt["/arm-io/dart-pmp"].pio_vm_base |
| 175 | + granularity = u.adt["/arm-io/dart-pmp"].pio_granularity |
| 176 | + i = 0 |
| 177 | + for j in range(4, len(u.adt["/arm-io/pmp"].reg)): |
| 178 | + host_addr, size = u.adt["/arm-io/pmp"].get_reg(j) |
| 179 | + self.asc.dart.iomap_at(0, pio_base, host_addr, size) |
| 180 | + self.asc.dart.invalidate_streams(1) |
| 181 | + self.asc.iowrite(table_dva + 24 * i, struct.pack("<QQQ", host_addr, pio_base, size)) |
| 182 | + pio_base += granularity |
| 183 | + i += 1 |
| 184 | + self.send(PMPMessage_IOVATableAck(IOVA=table_dva)) |
| 185 | + @msg_handler(0x12, PMPMessage_Malloc) |
| 186 | + def Malloc(self, msg): |
| 187 | + self.log(f'Malloc: {msg}') |
| 188 | + addr, dva = self.asc.ioalloc(msg.SIZE) |
| 189 | + self.allocs[dva] = addr |
| 190 | + self.send(PMPMessage_MallocAck(IOVA=dva)) |
| 191 | + @msg_handler(0x14, PMPMessage_Free) |
| 192 | + def Free(self, msg): |
| 193 | + self.log(f'Free: {msg}') |
| 194 | + #i dont think we can |
| 195 | + self.send(PMPMessage(TYPE=0x15)) |
| 196 | + |
| 197 | + @msg_handler(0x30, PMPMessage_SetBuf) |
| 198 | + def SetBuf(self, msg): |
| 199 | + self.log(f'SetBuf: {msg}') |
| 200 | + self.buf_ptr, _ = struct.unpack("<QQ", self.asc.ioread(msg.IOVA, 16)) |
| 201 | + self.send(PMPMessage(TYPE=0x31)) |
| 202 | + @msg_handler(0x32, PMPMessage_Advertise) |
| 203 | + def Advertise(self, msg): |
| 204 | + self.log(f'Advertise: {msg}') |
| 205 | + data = self.asc.ioread(msg.IOVA, 0x50) |
| 206 | + name = data[:0x30] + b'\0' |
| 207 | + size = struct.unpack("<I", data[0x40:0x44])[0] |
| 208 | + if size == 0: |
| 209 | + i = 0 |
| 210 | + print(name) |
| 211 | + while name[i] != 0: |
| 212 | + i += 1 |
| 213 | + name = name[:i].decode() |
| 214 | + self.log(f"Reading prop {name}") |
| 215 | + data = getattr(u.adt["/arm-io/pmp/iop-pmp-nub"], name, None) |
| 216 | + if data is None: |
| 217 | + self.log("unknown property") |
| 218 | + size = 0 |
| 219 | + else: |
| 220 | + if isinstance(data, int): |
| 221 | + if data > 0xffffffff: |
| 222 | + data = struct.pack("<Q", data) |
| 223 | + else: |
| 224 | + data = struct.pack("<I", data) |
| 225 | + self.asc.iowrite(self.buf_ptr, data) |
| 226 | + size = len(data) |
| 227 | + self.ioregs.append(size) |
| 228 | + index = len(self.ioregs) |
| 229 | + self.send(PMPMessage_AdvertiseAck(INDEX=index, SIZE=size)) |
| 230 | + |
| 231 | + @msg_handler(0x34, PMPMessage_SetVal) |
| 232 | + def SetVal(self, msg): |
| 233 | + self.log(f'SetVal: {msg}') |
| 234 | + self.send(PMPMessage_SetValAck(SIZE=self.ioregs[msg.INDEX])) |
| 235 | + |
| 236 | + |
| 237 | +class PMPClient(StandardASC, AOPBase): |
| 238 | + ENDPOINTS = {0x20: PMPEp} |
| 239 | + def __init__(self, u, dev_path, dart=None): |
| 240 | + node = u.adt[dev_path] |
| 241 | + asc_base = node.get_reg(0)[0] |
| 242 | + AOPBase.__init__(self, u) |
| 243 | + super().__init__(u, asc_base, dart) |
| 244 | + self.dart = dart |
| 245 | + |
| 246 | +if u.adt['/arm-io'].compatible[0].startswith('arm-io,t8103'): |
| 247 | + print("you have a pmp v1, this script is for v2 only") |
| 248 | + exit(1) |
| 249 | +elif u.adt['/arm-io'].compatible[0].startswith('arm-io,t600'): |
| 250 | + p.write64(0x28e3d07c0, 0x1000) |
| 251 | +elif u.adt['/arm-io'].compatible[0].startswith('arm-io,t602'): |
| 252 | + p.write64(0x28e3d1000, 0x2000) |
| 253 | +elif u.adt['/arm-io'].compatible[0].startswith('arm-io,t8112'): |
| 254 | + p.write64(0x23b3d0500, 0x80) |
| 255 | +else: |
| 256 | + print("FIXME: put the correct SOC-DEV-PS-REQ offset for your machine here") |
| 257 | + exit(1) |
| 258 | + |
| 259 | +dart = DART.from_adt(u, "/arm-io/dart-pmp") |
| 260 | +dart.verbose = 1 |
| 261 | +dart.initialize() |
| 262 | + |
| 263 | +pmp = PMPClient(u, "/arm-io/pmp", dart) |
| 264 | +pmp.verbose = 4 |
| 265 | +pmp.update_bootargs({ |
| 266 | + 'BDID'[::-1]: u.adt['/chosen'].board_id, |
| 267 | + 'DCAP'[::-1]: u.adt["/arm-io/pmp/iop-pmp-nub"].dram_capacity, |
| 268 | + 'DVID'[::-1]: u.adt['/chosen'].dram_vendor_id, |
| 269 | +}) |
| 270 | +p.dapf_init_all() |
| 271 | + |
| 272 | +pmp.start() |
| 273 | +pmp.start_ep(0x20) |
| 274 | +pmp.work_for(10) |
| 275 | + |
| 276 | +run_shell(locals(), poll_func=pmp.work) |
0 commit comments