summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/.clang-format32
-rw-r--r--tools/.editorconfig16
-rw-r--r--tools/.gitignore8
-rw-r--r--tools/.gitmodules27
-rw-r--r--tools/3rdparty_licenses/LICENSE.BSD-2.libfdt23
-rw-r--r--tools/3rdparty_licenses/LICENSE.BSD-3.arm26
-rw-r--r--tools/3rdparty_licenses/LICENSE.BSD-3.dwc333
-rw-r--r--tools/3rdparty_licenses/LICENSE.CC0121
-rw-r--r--tools/3rdparty_licenses/LICENSE.GPL-2339
-rw-r--r--tools/3rdparty_licenses/LICENSE.OFL-1.193
-rw-r--r--tools/3rdparty_licenses/LICENSE.minlzma21
-rw-r--r--tools/3rdparty_licenses/LICENSE.tinf22
-rw-r--r--tools/Dockerfile12
-rw-r--r--tools/LICENSE22
-rw-r--r--tools/Makefile239
-rw-r--r--tools/README.md138
-rw-r--r--tools/config.h29
-rw-r--r--tools/data/bootlogo_128.binbin0 -> 65536 bytes
l---------tools/data/bootlogo_128.png1
-rw-r--r--tools/data/bootlogo_256.binbin0 -> 262144 bytes
l---------tools/data/bootlogo_256.png1
-rwxr-xr-xtools/data/makelogo.sh2
-rw-r--r--tools/docker-compose.yml5
-rw-r--r--tools/font/README.md3
-rw-r--r--tools/font/SourceCodePro-Bold.ttfbin0 -> 138268 bytes
-rw-r--r--tools/font/font.binbin0 -> 12160 bytes
-rw-r--r--tools/font/font_retina.binbin0 -> 48640 bytes
-rwxr-xr-xtools/font/makefont.sh27
-rw-r--r--tools/m1n1-raw.ld150
-rw-r--r--tools/m1n1.ld256
-rwxr-xr-xtools/proxyclient/experiments/addrdump.py28
-rw-r--r--tools/proxyclient/experiments/aes.py144
-rw-r--r--tools/proxyclient/experiments/agx_1tri.py840
-rwxr-xr-xtools/proxyclient/experiments/agx_boot.py27
-rw-r--r--tools/proxyclient/experiments/agx_cancel.py246
-rw-r--r--tools/proxyclient/experiments/agx_deps.py242
-rw-r--r--tools/proxyclient/experiments/agx_dumpstructs.py19
-rw-r--r--tools/proxyclient/experiments/agx_parallel.py341
-rw-r--r--tools/proxyclient/experiments/agx_renderframe.py73
-rw-r--r--tools/proxyclient/experiments/agx_tlb.py295
-rw-r--r--tools/proxyclient/experiments/agx_tracetimings.py314
-rwxr-xr-xtools/proxyclient/experiments/aic_test.py411
-rw-r--r--tools/proxyclient/experiments/amcc_err_handler.py43
-rwxr-xr-xtools/proxyclient/experiments/aop.py310
-rwxr-xr-xtools/proxyclient/experiments/audio_capture.py132
-rwxr-xr-xtools/proxyclient/experiments/chickens.py108
-rwxr-xr-xtools/proxyclient/experiments/cpu_pstate_latencies.py239
-rwxr-xr-xtools/proxyclient/experiments/cpu_pstates.py150
-rwxr-xr-xtools/proxyclient/experiments/dart_dump.py19
-rwxr-xr-xtools/proxyclient/experiments/dcp.py351
-rwxr-xr-xtools/proxyclient/experiments/dcp_iboot.py108
-rw-r--r--tools/proxyclient/experiments/dcpext_iboot.py126
-rwxr-xr-xtools/proxyclient/experiments/find_sprr_regs.py82
-rwxr-xr-xtools/proxyclient/experiments/fptest.py114
-rwxr-xr-xtools/proxyclient/experiments/hacr_trap_bits.py128
-rwxr-xr-xtools/proxyclient/experiments/i2c.py124
-rw-r--r--tools/proxyclient/experiments/jpeg.py1222
-rw-r--r--tools/proxyclient/experiments/jpeg_doc.md1019
-rwxr-xr-xtools/proxyclient/experiments/memdump.py22
-rwxr-xr-xtools/proxyclient/experiments/mmio_sweep.py212
-rw-r--r--tools/proxyclient/experiments/mtp.py129
-rwxr-xr-xtools/proxyclient/experiments/ohmmeter.py88
-rwxr-xr-xtools/proxyclient/experiments/pcie_enable_devices.py18
-rw-r--r--tools/proxyclient/experiments/prores.py695
-rw-r--r--tools/proxyclient/experiments/scaler.py1527
-rwxr-xr-xtools/proxyclient/experiments/smc.py47
-rwxr-xr-xtools/proxyclient/experiments/smc_watcher.py115
-rwxr-xr-xtools/proxyclient/experiments/speaker_amp.py148
-rw-r--r--tools/proxyclient/experiments/spi.py182
-rwxr-xr-xtools/proxyclient/experiments/sprr_test_permissions.py366
-rwxr-xr-xtools/proxyclient/experiments/timer_test.py109
-rw-r--r--tools/proxyclient/hv/README.md4
-rw-r--r--tools/proxyclient/hv/trace_agx.py107
-rw-r--r--tools/proxyclient/hv/trace_agx_defer.py35
-rw-r--r--tools/proxyclient/hv/trace_agx_pwr.py158
-rw-r--r--tools/proxyclient/hv/trace_all.py25
-rw-r--r--tools/proxyclient/hv/trace_all_more.py23
-rw-r--r--tools/proxyclient/hv/trace_aop.py349
-rw-r--r--tools/proxyclient/hv/trace_atc.py61
-rw-r--r--tools/proxyclient/hv/trace_cd3217.py119
-rw-r--r--tools/proxyclient/hv/trace_codecs.py45
-rw-r--r--tools/proxyclient/hv/trace_dart.py27
-rw-r--r--tools/proxyclient/hv/trace_dcp.py1113
-rw-r--r--tools/proxyclient/hv/trace_gpio.py40
-rw-r--r--tools/proxyclient/hv/trace_i2c.py41
-rw-r--r--tools/proxyclient/hv/trace_isp.py5
-rw-r--r--tools/proxyclient/hv/trace_keyboard.py206
-rw-r--r--tools/proxyclient/hv/trace_mesa.py212
-rw-r--r--tools/proxyclient/hv/trace_nvme.py275
-rw-r--r--tools/proxyclient/hv/trace_pmgr.py170
-rw-r--r--tools/proxyclient/hv/trace_prores.py88
-rw-r--r--tools/proxyclient/hv/trace_smc.py108
-rw-r--r--tools/proxyclient/hv/trace_wlan.py446
-rw-r--r--tools/proxyclient/hv/trace_z2.py221
-rw-r--r--tools/proxyclient/m1n1/__init__.py0
-rw-r--r--tools/proxyclient/m1n1/adt.py713
-rw-r--r--tools/proxyclient/m1n1/agx/__init__.py343
-rw-r--r--tools/proxyclient/m1n1/agx/channels.py178
-rw-r--r--tools/proxyclient/m1n1/agx/context.py247
-rw-r--r--tools/proxyclient/m1n1/agx/event.py58
-rw-r--r--tools/proxyclient/m1n1/agx/initdata.py387
-rw-r--r--tools/proxyclient/m1n1/agx/object.py263
-rw-r--r--tools/proxyclient/m1n1/agx/render.py1075
-rw-r--r--tools/proxyclient/m1n1/agx/shim.py244
-rw-r--r--tools/proxyclient/m1n1/agx/uapi.py116
-rw-r--r--tools/proxyclient/m1n1/asm.py133
-rw-r--r--tools/proxyclient/m1n1/constructutils.py879
-rw-r--r--tools/proxyclient/m1n1/find_regs.py99
-rw-r--r--tools/proxyclient/m1n1/fw/__init__.py0
-rw-r--r--tools/proxyclient/m1n1/fw/afk/__init__.py0
-rw-r--r--tools/proxyclient/m1n1/fw/afk/epic.py292
-rw-r--r--tools/proxyclient/m1n1/fw/afk/rbep.py225
-rw-r--r--tools/proxyclient/m1n1/fw/agx/__init__.py98
-rw-r--r--tools/proxyclient/m1n1/fw/agx/channels.py481
-rw-r--r--tools/proxyclient/m1n1/fw/agx/cmdqueue.py516
-rw-r--r--tools/proxyclient/m1n1/fw/agx/handoff.py120
-rw-r--r--tools/proxyclient/m1n1/fw/agx/initdata.py1931
-rw-r--r--tools/proxyclient/m1n1/fw/agx/microsequence.py800
-rw-r--r--tools/proxyclient/m1n1/fw/aop/__init__.py31
-rw-r--r--tools/proxyclient/m1n1/fw/aop/bootargs.py64
-rw-r--r--tools/proxyclient/m1n1/fw/aop/ipc.py365
-rw-r--r--tools/proxyclient/m1n1/fw/asc/__init__.py125
-rw-r--r--tools/proxyclient/m1n1/fw/asc/base.py59
-rw-r--r--tools/proxyclient/m1n1/fw/asc/crash.py248
-rw-r--r--tools/proxyclient/m1n1/fw/asc/ioreporting.py56
-rw-r--r--tools/proxyclient/m1n1/fw/asc/kdebug.py57
-rw-r--r--tools/proxyclient/m1n1/fw/asc/mgmt.py144
-rw-r--r--tools/proxyclient/m1n1/fw/asc/oslog.py30
-rw-r--r--tools/proxyclient/m1n1/fw/asc/syslog.py72
-rw-r--r--tools/proxyclient/m1n1/fw/common.py170
-rw-r--r--tools/proxyclient/m1n1/fw/dcp/__init__.py2
-rw-r--r--tools/proxyclient/m1n1/fw/dcp/client.py14
-rw-r--r--tools/proxyclient/m1n1/fw/dcp/dcpav.py110
-rw-r--r--tools/proxyclient/m1n1/fw/dcp/dcpep.py163
-rw-r--r--tools/proxyclient/m1n1/fw/dcp/iboot.py215
-rw-r--r--tools/proxyclient/m1n1/fw/dcp/ipc.py789
-rw-r--r--tools/proxyclient/m1n1/fw/dcp/manager.py319
-rw-r--r--tools/proxyclient/m1n1/fw/dcp/parse_log.py43
-rw-r--r--tools/proxyclient/m1n1/fw/mtp.py411
-rw-r--r--tools/proxyclient/m1n1/fw/pmp.py168
-rw-r--r--tools/proxyclient/m1n1/fw/smc.py202
-rw-r--r--tools/proxyclient/m1n1/gpiola.py254
-rw-r--r--tools/proxyclient/m1n1/hostutils.py96
-rw-r--r--tools/proxyclient/m1n1/hv/__init__.py1849
-rw-r--r--tools/proxyclient/m1n1/hv/gdbserver/__init__.py480
-rw-r--r--tools/proxyclient/m1n1/hv/gdbserver/features/aarch64-core.xml91
-rw-r--r--tools/proxyclient/m1n1/hv/gdbserver/features/aarch64-fpu.xml160
-rw-r--r--tools/proxyclient/m1n1/hv/gdbserver/features/target.xml8
-rw-r--r--tools/proxyclient/m1n1/hv/types.py60
-rw-r--r--tools/proxyclient/m1n1/hv/virtio.py133
-rw-r--r--tools/proxyclient/m1n1/hv/virtutils.py43
-rw-r--r--tools/proxyclient/m1n1/hw/admac.py416
-rw-r--r--tools/proxyclient/m1n1/hw/aes.py110
-rw-r--r--tools/proxyclient/m1n1/hw/agx.py111
-rw-r--r--tools/proxyclient/m1n1/hw/asc.py121
-rw-r--r--tools/proxyclient/m1n1/hw/atc.py455
-rw-r--r--tools/proxyclient/m1n1/hw/codecs/__init__.py109
-rw-r--r--tools/proxyclient/m1n1/hw/codecs/cs42l84.py365
-rw-r--r--tools/proxyclient/m1n1/hw/dart.py102
-rw-r--r--tools/proxyclient/m1n1/hw/dart8020.py381
-rw-r--r--tools/proxyclient/m1n1/hw/dart8110.py541
-rw-r--r--tools/proxyclient/m1n1/hw/dockchannel.py120
-rw-r--r--tools/proxyclient/m1n1/hw/dwc3.py268
-rw-r--r--tools/proxyclient/m1n1/hw/i2c.py251
-rw-r--r--tools/proxyclient/m1n1/hw/isp.py507
-rw-r--r--tools/proxyclient/m1n1/hw/jpeg.py334
-rw-r--r--tools/proxyclient/m1n1/hw/mca.py110
-rw-r--r--tools/proxyclient/m1n1/hw/nco.py89
-rw-r--r--tools/proxyclient/m1n1/hw/pmgr.py71
-rw-r--r--tools/proxyclient/m1n1/hw/pmu.py36
-rw-r--r--tools/proxyclient/m1n1/hw/prores.py250
-rw-r--r--tools/proxyclient/m1n1/hw/scaler.py212
-rw-r--r--tools/proxyclient/m1n1/hw/sep.py169
-rw-r--r--tools/proxyclient/m1n1/hw/spi.py147
-rw-r--r--tools/proxyclient/m1n1/hw/spmi.py98
-rw-r--r--tools/proxyclient/m1n1/hw/uat.py571
-rw-r--r--tools/proxyclient/m1n1/loadobjs.py178
-rw-r--r--tools/proxyclient/m1n1/macho.py270
-rw-r--r--tools/proxyclient/m1n1/malloc.py98
-rw-r--r--tools/proxyclient/m1n1/proxy.py1104
-rw-r--r--tools/proxyclient/m1n1/proxyutils.py550
-rw-r--r--tools/proxyclient/m1n1/setup.py33
-rw-r--r--tools/proxyclient/m1n1/shell.py213
-rw-r--r--tools/proxyclient/m1n1/sysreg.py383
-rw-r--r--tools/proxyclient/m1n1/tgtypes.py30
-rw-r--r--tools/proxyclient/m1n1/trace/__init__.py220
-rw-r--r--tools/proxyclient/m1n1/trace/agx.py1148
-rw-r--r--tools/proxyclient/m1n1/trace/asc.py271
-rw-r--r--tools/proxyclient/m1n1/trace/dart.py69
-rw-r--r--tools/proxyclient/m1n1/trace/dockchannel.py37
-rw-r--r--tools/proxyclient/m1n1/trace/gpio.py67
-rw-r--r--tools/proxyclient/m1n1/trace/i2c.py225
-rw-r--r--tools/proxyclient/m1n1/trace/isp.py118
-rw-r--r--tools/proxyclient/m1n1/trace/pcie.py108
-rw-r--r--tools/proxyclient/m1n1/trace/spi.py10
-rw-r--r--tools/proxyclient/m1n1/utils.py1144
-rw-r--r--tools/proxyclient/m1n1/xnutools.py117
-rwxr-xr-xtools/proxyclient/tools/admac_stream.py87
-rwxr-xr-xtools/proxyclient/tools/chainload.py137
-rwxr-xr-xtools/proxyclient/tools/codecshell.py83
-rwxr-xr-xtools/proxyclient/tools/dump_pmgr.py138
-rwxr-xr-xtools/proxyclient/tools/freebsd.py136
-rwxr-xr-xtools/proxyclient/tools/linux.py153
-rwxr-xr-xtools/proxyclient/tools/picocom-sec.sh7
-rwxr-xr-xtools/proxyclient/tools/pmgr_adt2dt.py79
-rwxr-xr-xtools/proxyclient/tools/reboot.py11
-rwxr-xr-xtools/proxyclient/tools/reset_panic_counter.py9
-rwxr-xr-xtools/proxyclient/tools/run_guest.py115
-rwxr-xr-xtools/proxyclient/tools/run_guest_kernel.sh58
-rwxr-xr-xtools/proxyclient/tools/second_proxy.py10
-rwxr-xr-xtools/proxyclient/tools/shell.py9
-rwxr-xr-xtools/proxyclient/tools/smccli.py19
-rw-r--r--tools/rust/Cargo.lock56
-rw-r--r--tools/rust/Cargo.toml26
-rw-r--r--tools/rust/src/chainload.rs101
-rw-r--r--tools/rust/src/dlmalloc.rs79
-rw-r--r--tools/rust/src/gpt.rs162
-rw-r--r--tools/rust/src/lib.rs36
-rw-r--r--tools/rust/src/nvme.rs93
-rw-r--r--tools/rust/src/print.rs60
-rw-r--r--tools/src/adt.c375
-rw-r--r--tools/src/adt.h109
-rw-r--r--tools/src/afk.c545
-rw-r--r--tools/src/afk.h17
-rw-r--r--tools/src/aic.c153
-rw-r--r--tools/src/aic.h40
-rw-r--r--tools/src/aic_regs.h53
-rw-r--r--tools/src/arm_cpu_regs.h338
-rw-r--r--tools/src/asc.c126
-rw-r--r--tools/src/asc.h30
-rw-r--r--tools/src/chainload.c148
-rw-r--r--tools/src/chainload.h11
-rw-r--r--tools/src/chainload_asm.S20
-rw-r--r--tools/src/chickens.c118
-rw-r--r--tools/src/chickens.h8
-rw-r--r--tools/src/chickens_avalanche.c50
-rw-r--r--tools/src/chickens_blizzard.c18
-rw-r--r--tools/src/chickens_firestorm.c113
-rw-r--r--tools/src/chickens_icestorm.c30
-rw-r--r--tools/src/clk.c36
-rw-r--r--tools/src/clk.h8
-rw-r--r--tools/src/cpu_regs.h290
-rw-r--r--tools/src/cpufreq.c120
-rw-r--r--tools/src/cpufreq.h8
-rw-r--r--tools/src/dapf.c137
-rw-r--r--tools/src/dapf.h9
-rw-r--r--tools/src/dart.c714
-rw-r--r--tools/src/dart.h33
-rw-r--r--tools/src/dcp.c92
-rw-r--r--tools/src/dcp.h22
-rw-r--r--tools/src/dcp_iboot.c224
-rw-r--r--tools/src/dcp_iboot.h111
-rw-r--r--tools/src/devicetree.c69
-rw-r--r--tools/src/devicetree.h22
-rw-r--r--tools/src/display.c514
-rw-r--r--tools/src/display.h21
-rw-r--r--tools/src/dlmalloc/malloc.c6286
-rw-r--r--tools/src/dlmalloc/malloc_config.h37
-rw-r--r--tools/src/exception.c388
-rw-r--r--tools/src/exception.h55
-rw-r--r--tools/src/exception_asm.S231
-rw-r--r--tools/src/fb.c415
-rw-r--r--tools/src/fb.h57
-rw-r--r--tools/src/firmware.c82
-rw-r--r--tools/src/firmware.h37
-rw-r--r--tools/src/gxf.c114
-rw-r--r--tools/src/gxf.h22
-rw-r--r--tools/src/gxf_asm.S246
-rw-r--r--tools/src/heapblock.c44
-rw-r--r--tools/src/heapblock.h13
-rw-r--r--tools/src/hv.c329
-rw-r--r--tools/src/hv.h112
-rw-r--r--tools/src/hv_aic.c95
-rw-r--r--tools/src/hv_asm.S196
-rw-r--r--tools/src/hv_exc.c515
-rw-r--r--tools/src/hv_virtio.c308
-rw-r--r--tools/src/hv_vm.c1278
-rw-r--r--tools/src/hv_vuart.c125
-rw-r--r--tools/src/hv_wdt.c130
-rw-r--r--tools/src/i2c.c216
-rw-r--r--tools/src/i2c.h22
-rw-r--r--tools/src/iodev.c319
-rw-r--r--tools/src/iodev.h63
-rw-r--r--tools/src/iova.c233
-rw-r--r--tools/src/iova.h18
-rw-r--r--tools/src/kboot.c1937
-rw-r--r--tools/src/kboot.h25
-rw-r--r--tools/src/kboot_gpu.c452
-rw-r--r--tools/src/libfdt/fdt.c327
-rw-r--r--tools/src/libfdt/fdt.h66
-rw-r--r--tools/src/libfdt/fdt_addresses.c101
-rw-r--r--tools/src/libfdt/fdt_empty_tree.c38
-rw-r--r--tools/src/libfdt/fdt_overlay.c882
-rw-r--r--tools/src/libfdt/fdt_ro.c859
-rw-r--r--tools/src/libfdt/fdt_rw.c492
-rw-r--r--tools/src/libfdt/fdt_strerror.c59
-rw-r--r--tools/src/libfdt/fdt_sw.c384
-rw-r--r--tools/src/libfdt/fdt_wip.c94
-rw-r--r--tools/src/libfdt/libfdt.h2080
-rw-r--r--tools/src/libfdt/libfdt_env.h95
-rw-r--r--tools/src/libfdt/libfdt_internal.h173
-rw-r--r--tools/src/main.c205
-rw-r--r--tools/src/math/exp2f_data.c42
-rw-r--r--tools/src/math/exp2f_data.h22
-rw-r--r--tools/src/math/expf.c83
-rw-r--r--tools/src/math/libm.h271
-rw-r--r--tools/src/mcc.c271
-rw-r--r--tools/src/mcc.h19
-rw-r--r--tools/src/memory.c566
-rw-r--r--tools/src/memory.h88
-rw-r--r--tools/src/memory_asm.S166
-rw-r--r--tools/src/minilzlib/dictbuf.c155
-rw-r--r--tools/src/minilzlib/inputbuf.c144
-rw-r--r--tools/src/minilzlib/lzma2dec.c228
-rw-r--r--tools/src/minilzlib/lzma2dec.h91
-rw-r--r--tools/src/minilzlib/lzmadec.c627
-rw-r--r--tools/src/minilzlib/lzmadec.h114
-rw-r--r--tools/src/minilzlib/minlzlib.h88
-rw-r--r--tools/src/minilzlib/minlzma.h33
-rw-r--r--tools/src/minilzlib/rangedec.c395
-rw-r--r--tools/src/minilzlib/xzstream.c547
-rw-r--r--tools/src/minilzlib/xzstream.h123
-rw-r--r--tools/src/nvme.c505
-rw-r--r--tools/src/nvme.h14
-rw-r--r--tools/src/payload.c281
-rw-r--r--tools/src/payload.h8
-rw-r--r--tools/src/pcie.c388
-rw-r--r--tools/src/pcie.h9
-rw-r--r--tools/src/pmgr.c358
-rw-r--r--tools/src/pmgr.h24
-rw-r--r--tools/src/proxy.c575
-rw-r--r--tools/src/proxy.h183
-rw-r--r--tools/src/ringbuffer.c81
-rw-r--r--tools/src/ringbuffer.h22
-rw-r--r--tools/src/rtkit.c710
-rw-r--r--tools/src/rtkit.h43
-rw-r--r--tools/src/sart.c219
-rw-r--r--tools/src/sart.h16
-rw-r--r--tools/src/sep.c68
-rw-r--r--tools/src/sep.h12
-rw-r--r--tools/src/smp.c296
-rw-r--r--tools/src/smp.h41
-rw-r--r--tools/src/soc.h25
-rw-r--r--tools/src/start.S176
-rw-r--r--tools/src/startup.c121
-rw-r--r--tools/src/string.c209
-rw-r--r--tools/src/tinf/adler32.c95
-rw-r--r--tools/src/tinf/crc32.c57
-rw-r--r--tools/src/tinf/tinf.h142
-rw-r--r--tools/src/tinf/tinfgzip.c191
-rw-r--r--tools/src/tinf/tinflate.c648
-rw-r--r--tools/src/tinf/tinfzlib.c99
-rw-r--r--tools/src/tps6598x.c172
-rw-r--r--tools/src/tps6598x.h28
-rw-r--r--tools/src/tunables.c124
-rw-r--r--tools/src/tunables.h42
-rw-r--r--tools/src/tunables_static.c105
-rw-r--r--tools/src/types.h62
-rw-r--r--tools/src/uart.c180
-rw-r--r--tools/src/uart.h29
-rw-r--r--tools/src/uart_regs.h32
-rw-r--r--tools/src/uartproxy.c317
-rw-r--r--tools/src/uartproxy.h45
-rw-r--r--tools/src/usb.c343
-rw-r--r--tools/src/usb.h18
-rw-r--r--tools/src/usb_dwc3.c1416
-rw-r--r--tools/src/usb_dwc3.h33
-rw-r--r--tools/src/usb_dwc3_regs.h625
-rw-r--r--tools/src/usb_types.h209
-rw-r--r--tools/src/utils.c182
-rw-r--r--tools/src/utils.h444
-rw-r--r--tools/src/utils_asm.S182
-rw-r--r--tools/src/vsprintf.c703
-rw-r--r--tools/src/vsprintf.h11
-rw-r--r--tools/src/wdt.c44
-rw-r--r--tools/src/wdt.h9
-rw-r--r--tools/src/xnuboot.h36
-rw-r--r--tools/sysinc/assert.h15
-rw-r--r--tools/sysinc/endian.h12
-rw-r--r--tools/sysinc/errno.h9
-rw-r--r--tools/sysinc/limits.h15
-rw-r--r--tools/sysinc/malloc.h16
-rw-r--r--tools/sysinc/math.h16
-rw-r--r--tools/sysinc/string.h39
-rw-r--r--tools/tools/apple_regs.json319
-rw-r--r--tools/tools/arm_regs.json1
-rw-r--r--tools/tools/gen_reg_class.py33
-rw-r--r--tools/tools/gen_reg_include.py35
-rw-r--r--tools/tools/reg2json.py137
-rw-r--r--tools/tools/reg_filter.py35
-rw-r--r--tools/udev/80-m1n1.rules6
-rwxr-xr-xtools/version.sh17
392 files changed, 87439 insertions, 0 deletions
diff --git a/tools/.clang-format b/tools/.clang-format
new file mode 100644
index 0000000..131397a
--- /dev/null
+++ b/tools/.clang-format
@@ -0,0 +1,32 @@
+BasedOnStyle: LLVM
+IndentWidth: 4
+UseTab: Never
+BreakBeforeBraces: Linux
+AllowShortIfStatementsOnASingleLine: Never
+AllowShortFunctionsOnASingleLine: false
+AlignConsecutiveMacros: true
+IndentCaseLabels: true
+ColumnLimit: 100
+IncludeBlocks: Regroup
+IncludeIsMainRegex: '(_.*)?$'
+
+# Include block order goes like this
+# - config.h style files, including ../config.h
+# - system headers (<>)
+# - All m1n1 headers, starting with the "this file" header, rest sorted
+# - 3rd party code headers
+# - build artifact headers (stuff outside of src/)
+IncludeCategories:
+ - Regex: '^"(\.\./)*build/build_.*\.h"$'
+ Priority: -3
+ - Regex: '^"(\.\./)*config\.h"$'
+ Priority: -2
+ - Regex: '^<'
+ Priority: -1
+ - Regex: '^"\.\./'
+ Priority: 3
+ - Regex: '/'
+ Priority: 2
+ - Regex: '.*'
+ Priority: 0
+ SortPriority: 1
diff --git a/tools/.editorconfig b/tools/.editorconfig
new file mode 100644
index 0000000..0c412a3
--- /dev/null
+++ b/tools/.editorconfig
@@ -0,0 +1,16 @@
+root = true
+
+# Defaults
+[*]
+indent_style = space
+indent_size = 4
+tab_width = 4
+charset = utf-8
+insert_final_newline = true
+trim_trailing_whitespace = true
+max_line_length = 100
+
+[Makefile*]
+indent_style = tab
+tab_width = 8
+indent_size = 8
diff --git a/tools/.gitignore b/tools/.gitignore
new file mode 100644
index 0000000..ccfa3e3
--- /dev/null
+++ b/tools/.gitignore
@@ -0,0 +1,8 @@
+!build/.keep
+build/
+__pycache__
+*.pyc
+*.macho
+*.swp
+*.orig
+*.rej
diff --git a/tools/.gitmodules b/tools/.gitmodules
new file mode 100644
index 0000000..a0a12a8
--- /dev/null
+++ b/tools/.gitmodules
@@ -0,0 +1,27 @@
+[submodule "artwork"]
+ path = artwork
+ url = https://github.com/AsahiLinux/artwork.git
+[submodule "rust/vendor/rust-fatfs"]
+ path = rust/vendor/rust-fatfs
+ url = https://github.com/rafalh/rust-fatfs
+[submodule "rust/vendor/bitflags"]
+ path = rust/vendor/bitflags
+ url = https://github.com/bitflags/bitflags
+[submodule "rust/vendor/cfg-if"]
+ path = rust/vendor/cfg-if
+ url = https://github.com/alexcrichton/cfg-if
+[submodule "rust/vendor/cstr_core"]
+ path = rust/vendor/cstr_core
+ url = https://github.com/Amanieu/cstr_core
+[submodule "rust/vendor/cty"]
+ path = rust/vendor/cty
+ url = https://github.com/japaric/cty
+[submodule "rust/vendor/uuid"]
+ path = rust/vendor/uuid
+ url = https://github.com/uuid-rs/uuid
+[submodule "rust/vendor/log"]
+ path = rust/vendor/log
+ url = https://github.com/rust-lang/log
+[submodule "rust/vendor/memchr"]
+ path = rust/vendor/memchr
+ url = https://github.com/BurntSushi/memchr
diff --git a/tools/3rdparty_licenses/LICENSE.BSD-2.libfdt b/tools/3rdparty_licenses/LICENSE.BSD-2.libfdt
new file mode 100644
index 0000000..292cc54
--- /dev/null
+++ b/tools/3rdparty_licenses/LICENSE.BSD-2.libfdt
@@ -0,0 +1,23 @@
+Copyright (C) 2006 David Gibson, IBM Corporation.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/tools/3rdparty_licenses/LICENSE.BSD-3.arm b/tools/3rdparty_licenses/LICENSE.BSD-3.arm
new file mode 100644
index 0000000..4eaaa1a
--- /dev/null
+++ b/tools/3rdparty_licenses/LICENSE.BSD-3.arm
@@ -0,0 +1,26 @@
+Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+- Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+- Neither the name of Arm nor the names of its contributors may be used to
+endorse or promote products derived from this software without specific
+prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/tools/3rdparty_licenses/LICENSE.BSD-3.dwc3 b/tools/3rdparty_licenses/LICENSE.BSD-3.dwc3
new file mode 100644
index 0000000..fb2640f
--- /dev/null
+++ b/tools/3rdparty_licenses/LICENSE.BSD-3.dwc3
@@ -0,0 +1,33 @@
+Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+
+Authors: Felipe Balbi <balbi@ti.com>,
+ Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions, and the following disclaimer,
+ without modification.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. The names of the above-listed copyright holders may not be used
+ to endorse or promote products derived from this software without
+ specific prior written permission.
+
+ALTERNATIVELY, this software may be distributed under the terms of the
+GNU General Public License ("GPL") version 2, as published by the Free
+Software Foundation.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/tools/3rdparty_licenses/LICENSE.CC0 b/tools/3rdparty_licenses/LICENSE.CC0
new file mode 100644
index 0000000..0e259d4
--- /dev/null
+++ b/tools/3rdparty_licenses/LICENSE.CC0
@@ -0,0 +1,121 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+ HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+ i. the right to reproduce, adapt, distribute, perform, display,
+ communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+ likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+ subject to the limitations in paragraph 4(a), below;
+ v. rights protecting the extraction, dissemination, use and reuse of data
+ in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+ European Parliament and of the Council of 11 March 1996 on the legal
+ protection of databases, and under any national implementation
+ thereof, including any amended or successor version of such
+ directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+ world based on applicable law or treaty, and any national
+ implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+ surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+ warranties of any kind concerning the Work, express, implied,
+ statutory or otherwise, including without limitation warranties of
+ title, merchantability, fitness for a particular purpose, non
+ infringement, or the absence of latent or other defects, accuracy, or
+ the present or absence of errors, whether or not discoverable, all to
+ the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+ that may apply to the Work or any use thereof, including without
+ limitation any person's Copyright and Related Rights in the Work.
+ Further, Affirmer disclaims responsibility for obtaining any necessary
+ consents, permissions or other rights required for any use of the
+ Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+ party to this document and has no duty or obligation with respect to
+ this CC0 or use of the Work.
diff --git a/tools/3rdparty_licenses/LICENSE.GPL-2 b/tools/3rdparty_licenses/LICENSE.GPL-2
new file mode 100644
index 0000000..0e845b5
--- /dev/null
+++ b/tools/3rdparty_licenses/LICENSE.GPL-2
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/tools/3rdparty_licenses/LICENSE.OFL-1.1 b/tools/3rdparty_licenses/LICENSE.OFL-1.1
new file mode 100644
index 0000000..9efccbc
--- /dev/null
+++ b/tools/3rdparty_licenses/LICENSE.OFL-1.1
@@ -0,0 +1,93 @@
+Copyright 2010-2019 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries.
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+
+This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/tools/3rdparty_licenses/LICENSE.minlzma b/tools/3rdparty_licenses/LICENSE.minlzma
new file mode 100644
index 0000000..cea77da
--- /dev/null
+++ b/tools/3rdparty_licenses/LICENSE.minlzma
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 Alex Ionescu
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/tools/3rdparty_licenses/LICENSE.tinf b/tools/3rdparty_licenses/LICENSE.tinf
new file mode 100644
index 0000000..c90a73c
--- /dev/null
+++ b/tools/3rdparty_licenses/LICENSE.tinf
@@ -0,0 +1,22 @@
+The zlib License (Zlib)
+
+Copyright (c) 2003-2019 Joergen Ibsen
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must
+ not claim that you wrote the original software. If you use this
+ software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must
+ not be misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
diff --git a/tools/Dockerfile b/tools/Dockerfile
new file mode 100644
index 0000000..ae454fb
--- /dev/null
+++ b/tools/Dockerfile
@@ -0,0 +1,12 @@
+FROM debian:buster-slim
+ENV DEBIAN_FRONTEND=noninteractive
+
+RUN apt-get update && apt-get install -y build-essential bash git locales gcc-aarch64-linux-gnu libc6-dev-arm64-cross device-tree-compiler \
+ && rm -rf /var/lib/apt/lists/* \
+ && localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8
+ENV LANG en_US.utf8
+
+WORKDIR /m1n1
+COPY . .
+
+CMD ["/bin/bash"]
diff --git a/tools/LICENSE b/tools/LICENSE
new file mode 100644
index 0000000..aff0557
--- /dev/null
+++ b/tools/LICENSE
@@ -0,0 +1,22 @@
+MIT License
+
+Copyright The Asahi Linux Contributors
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/tools/Makefile b/tools/Makefile
new file mode 100644
index 0000000..54e374e
--- /dev/null
+++ b/tools/Makefile
@@ -0,0 +1,239 @@
+RUSTARCH ?= aarch64-unknown-none-softfloat
+
+ifeq ($(shell uname),Darwin)
+USE_CLANG ?= 1
+$(info INFO: Building on Darwin)
+ifeq ($(shell uname -p),arm)
+TOOLCHAIN ?= /opt/homebrew/opt/llvm/bin/
+else
+TOOLCHAIN ?= /usr/local/opt/llvm/bin/
+endif
+$(info INFO: Toolchain path: $(TOOLCHAIN))
+endif
+
+ifeq ($(shell uname -m),aarch64)
+ARCH ?=
+else
+ARCH ?= aarch64-linux-gnu-
+endif
+
+ifeq ($(USE_CLANG),1)
+CC := $(TOOLCHAIN)clang --target=$(ARCH)
+AS := $(TOOLCHAIN)clang --target=$(ARCH)
+LD := $(TOOLCHAIN)ld.lld
+OBJCOPY := $(TOOLCHAIN)llvm-objcopy
+CLANG_FORMAT := $(TOOLCHAIN)clang-format
+EXTRA_CFLAGS ?=
+else
+CC := $(TOOLCHAIN)$(ARCH)gcc
+AS := $(TOOLCHAIN)$(ARCH)gcc
+LD := $(TOOLCHAIN)$(ARCH)ld
+OBJCOPY := $(TOOLCHAIN)$(ARCH)objcopy
+CLANG_FORMAT := clang-format
+EXTRA_CFLAGS ?= -Wstack-usage=1024
+endif
+
+BASE_CFLAGS := -O2 -Wall -g -Wundef -Werror=strict-prototypes -fno-common -fno-PIE \
+ -Werror=implicit-function-declaration -Werror=implicit-int \
+ -Wsign-compare -Wunused-parameter -Wno-multichar \
+ -ffreestanding -fpic -ffunction-sections -fdata-sections \
+ -nostdinc -isystem $(shell $(CC) -print-file-name=include) -isystem sysinc \
+ -fno-stack-protector -mstrict-align -march=armv8.2-a \
+ $(EXTRA_CFLAGS)
+
+CFLAGS := $(BASE_CFLAGS) -mgeneral-regs-only
+
+CFG :=
+ifeq ($(RELEASE),1)
+CFG += RELEASE
+endif
+
+# Required for no_std + alloc for now
+export RUSTUP_TOOLCHAIN=nightly
+RUST_LIB := librust.a
+RUST_LIBS :=
+ifeq ($(CHAINLOADING),1)
+CFG += CHAINLOADING
+RUST_LIBS += $(RUST_LIB)
+endif
+
+LDFLAGS := -EL -maarch64elf --no-undefined -X -Bsymbolic \
+ -z notext --no-apply-dynamic-relocs --orphan-handling=warn \
+ -z nocopyreloc --gc-sections -pie
+
+MINILZLIB_OBJECTS := $(patsubst %,minilzlib/%, \
+ dictbuf.o inputbuf.o lzma2dec.o lzmadec.o rangedec.o xzstream.o)
+
+TINF_OBJECTS := $(patsubst %,tinf/%, \
+ adler32.o crc32.o tinfgzip.o tinflate.o tinfzlib.o)
+
+DLMALLOC_OBJECTS := dlmalloc/malloc.o
+
+LIBFDT_OBJECTS := $(patsubst %,libfdt/%, \
+ fdt_addresses.o fdt_empty_tree.o fdt_ro.o fdt_rw.o fdt_strerror.o fdt_sw.o \
+ fdt_wip.o fdt.o)
+
+OBJECTS := \
+ adt.o \
+ afk.o \
+ aic.o \
+ asc.o \
+ bootlogo_128.o bootlogo_256.o \
+ chainload.o \
+ chainload_asm.o \
+ chickens.o \
+ chickens_avalanche.o \
+ chickens_blizzard.o \
+ chickens_firestorm.o \
+ chickens_icestorm.o \
+ clk.o \
+ cpufreq.o \
+ dapf.o \
+ dart.o \
+ dcp.o \
+ dcp_iboot.o \
+ devicetree.o \
+ display.o \
+ exception.o exception_asm.o \
+ fb.o font.o font_retina.o \
+ firmware.o \
+ gxf.o gxf_asm.o \
+ heapblock.o \
+ hv.o hv_vm.o hv_exc.o hv_vuart.o hv_wdt.o hv_asm.o hv_aic.o hv_virtio.o \
+ i2c.o \
+ iodev.o \
+ iova.o \
+ kboot.o \
+ main.o \
+ mcc.o \
+ memory.o memory_asm.o \
+ nvme.o \
+ payload.o \
+ pcie.o \
+ pmgr.o \
+ proxy.o \
+ ringbuffer.o \
+ rtkit.o \
+ sart.o \
+ sep.o \
+ smp.o \
+ start.o \
+ startup.o \
+ string.o \
+ tunables.o tunables_static.o \
+ tps6598x.o \
+ uart.o \
+ uartproxy.o \
+ usb.o usb_dwc3.o \
+ utils.o utils_asm.o \
+ vsprintf.o \
+ wdt.o \
+ $(MINILZLIB_OBJECTS) $(TINF_OBJECTS) $(DLMALLOC_OBJECTS) $(LIBFDT_OBJECTS) $(RUST_LIBS)
+
+FP_OBJECTS := \
+ kboot_gpu.o \
+ math/expf.o \
+ math/exp2f_data.o \
+
+BUILD_OBJS := $(patsubst %,build/%,$(OBJECTS))
+BUILD_FP_OBJS := $(patsubst %,build/%,$(FP_OBJECTS))
+BUILD_ALL_OBJS := $(BUILD_OBJS) $(BUILD_FP_OBJS)
+NAME := m1n1
+TARGET := m1n1.macho
+TARGET_RAW := m1n1.bin
+
+DEPDIR := build/.deps
+
+.PHONY: all clean format update_tag update_cfg invoke_cc
+all: update_tag update_cfg build/$(TARGET) build/$(TARGET_RAW)
+clean:
+ rm -rf build/*
+format:
+ $(CLANG_FORMAT) -i src/*.c src/math/*.c src/*.h src/math/*.h sysinc/*.h
+format-check:
+ $(CLANG_FORMAT) --dry-run --Werror src/*.c src/*.h sysinc/*.h
+rustfmt:
+ cd rust && cargo fmt
+rustfmt-check:
+ cd rust && cargo fmt --check
+
+build/$(RUST_LIB): rust/src/* rust/*
+ @echo " RS $@"
+ @mkdir -p $(DEPDIR)
+ @mkdir -p "$(dir $@)"
+ @cargo build --target $(RUSTARCH) --lib --release --manifest-path rust/Cargo.toml --target-dir build
+ @cp "build/$(RUSTARCH)/release/${RUST_LIB}" "$@"
+
+build/%.o: src/%.S
+ @echo " AS $@"
+ @mkdir -p $(DEPDIR)
+ @mkdir -p "$(dir $@)"
+ @$(AS) -c $(CFLAGS) -MMD -MF $(DEPDIR)/$(*F).d -MQ "$@" -MP -o $@ $<
+
+$(BUILD_FP_OBJS): build/%.o: src/%.c
+ @echo " CC FP $@"
+ @mkdir -p $(DEPDIR)
+ @mkdir -p "$(dir $@)"
+ @$(CC) -c $(BASE_CFLAGS) -MMD -MF $(DEPDIR)/$(*F).d -MQ "$@" -MP -o $@ $<
+
+build/%.o: src/%.c
+ @echo " CC $@"
+ @mkdir -p $(DEPDIR)
+ @mkdir -p "$(dir $@)"
+ @$(CC) -c $(CFLAGS) -MMD -MF $(DEPDIR)/$(*F).d -MQ "$@" -MP -o $@ $<
+
+# special target for usage by m1n1.loadobjs
+invoke_cc:
+ @$(CC) -c $(CFLAGS) -Isrc -o $(OBJFILE) $(CFILE)
+
+build/$(NAME).elf: $(BUILD_ALL_OBJS) m1n1.ld
+ @echo " LD $@"
+ @$(LD) -T m1n1.ld $(LDFLAGS) -o $@ $(BUILD_ALL_OBJS)
+
+build/$(NAME)-raw.elf: $(BUILD_ALL_OBJS) m1n1-raw.ld
+ @echo " LDRAW $@"
+ @$(LD) -T m1n1-raw.ld $(LDFLAGS) -o $@ $(BUILD_ALL_OBJS)
+
+build/$(NAME).macho: build/$(NAME).elf
+ @echo " MACHO $@"
+ @$(OBJCOPY) -O binary --strip-debug $< $@
+
+build/$(NAME).bin: build/$(NAME)-raw.elf
+ @echo " RAW $@"
+ @$(OBJCOPY) -O binary --strip-debug $< $@
+
+update_tag:
+ @mkdir -p build
+ @./version.sh > build/build_tag.tmp
+ @cmp -s build/build_tag.h build/build_tag.tmp 2>/dev/null || \
+ ( mv -f build/build_tag.tmp build/build_tag.h && echo " TAG build/build_tag.h" )
+
+update_cfg:
+ @mkdir -p build
+ @for i in $(CFG); do echo "#define $$i"; done > build/build_cfg.tmp
+ @cmp -s build/build_cfg.h build/build_cfg.tmp 2>/dev/null || \
+ ( mv -f build/build_cfg.tmp build/build_cfg.h && echo " CFG build/build_cfg.h" )
+
+build/build_tag.h: update_tag
+build/build_cfg.h: update_cfg
+
+build/%.bin: data/%.bin
+ @echo " IMG $@"
+ @mkdir -p "$(dir $@)"
+ @cp $< $@
+
+build/%.o: build/%.bin
+ @echo " BIN $@"
+ @mkdir -p "$(dir $@)"
+ @$(OBJCOPY) -I binary -B aarch64 -O elf64-littleaarch64 $< $@
+
+build/%.bin: font/%.bin
+ @echo " CP $@"
+ @mkdir -p "$(dir $@)"
+ @cp $< $@
+
+build/main.o: build/build_tag.h build/build_cfg.h src/main.c
+build/usb_dwc3.o: build/build_tag.h src/usb_dwc3.c
+build/chainload.o: build/build_cfg.h src/usb_dwc3.c
+
+-include $(DEPDIR)/*
diff --git a/tools/README.md b/tools/README.md
new file mode 100644
index 0000000..30299ac
--- /dev/null
+++ b/tools/README.md
@@ -0,0 +1,138 @@
+# m1n1: an experimentation playground for Apple Silicon
+
+(And to some extent a Linux bootloader)
+
+## Building
+
+You need an `aarch64-linux-gnu-gcc` cross-compiler toolchain (or a native one, if running on ARM64).
+
+```shell
+$ git clone --recursive https://github.com/AsahiLinux/m1n1.git
+$ cd m1n1
+$ make
+```
+
+The output will be in build/m1n1.macho.
+
+To build on a native arm64 machine, use `make ARCH=`.
+
+Building on ARM64 macOS is supported with clang and LLVM; you need to use Homebrew to
+install the required dependencies:
+
+```shell
+$ brew install llvm
+```
+
+After that, just type `make`.
+
+### Building using the container setup
+
+If you have a container runtime installed, like Podman or Docker, you can make use of the compose setup, which contains all build dependencies.
+
+```shell
+$ git clone --recursive https://github.com/AsahiLinux/m1n1.git
+$ cd m1n1
+$ podman-compose run m1n1 make
+$ # or
+$ docker-compose run m1n1 make
+```
+
+## Usage
+
+Our [wiki](https://github.com/AsahiLinux/docs/wiki/m1n1%3AUser-Guide) has more information on how to
+use m1n1.
+
+To install on an OS container based on macOS <12.1, use `m1n1.macho`:
+
+```shell
+kmutil configure-boot -c m1n1.macho -v <path to your OS volume>
+```
+
+To install on an OS container based on macOS >=12.1, use `m1n1.bin`:
+
+```shell
+kmutil configure-boot -c m1n1.bin --raw --entry-point 2048 --lowest-virtual-address 0 -v <path to your OS volume>
+```
+
+## Payloads
+
+m1n1 supports running payloads by simple concatenation:
+
+```shell
+$ cat build/m1n1.macho Image.gz build/dtb/apple-j274.dtb initramfs.cpio.gz > m1n1-payload.macho
+$ cat build/m1n1.bin Image.gz build/dtb/apple-j274.dtb initramfs.cpio.gz > m1n1-payload.bin
+```
+
+Supported payload file formats:
+
+* Kernel images (or compatible). Must be compressed or last payload.
+* Devicetree blobs (FDT). May be uncompressed or compressed.
+* Initramfs cpio images. Must be compressed.
+
+Supported compression formats:
+
+* gzip
+* xz
+
+## License
+
+m1n1 is licensed under the MIT license, as included in the [LICENSE](LICENSE) file.
+
+* Copyright The Asahi Linux Contributors
+
+Please see the Git history for authorship information.
+
+Portions of m1n1 are based on mini:
+
+* Copyright (C) 2008-2010 Hector Martin "marcan" <marcan@marcan.st>
+* Copyright (C) 2008-2010 Sven Peter <sven@svenpeter.dev>
+* Copyright (C) 2008-2010 Andre Heider <a.heider@gmail.com>
+
+m1n1 embeds libfdt, which is dual [BSD](3rdparty_licenses/LICENSE.BSD-2.libfdt) and
+[GPL-2](3rdparty_licenses/LICENSE.GPL-2) licensed and copyright:
+
+* Copyright (C) 2014 David Gibson <david@gibson.dropbear.id.au>
+* Copyright (C) 2018 embedded brains GmbH
+* Copyright (C) 2006-2012 David Gibson, IBM Corporation.
+* Copyright (C) 2012 David Gibson, IBM Corporation.
+* Copyright 2012 Kim Phillips, Freescale Semiconductor.
+* Copyright (C) 2016 Free Electrons
+* Copyright (C) 2016 NextThing Co.
+
+The ADT code in mini is also based on libfdt and subject to the same license.
+
+m1n1 embeds [minlzma](https://github.com/ionescu007/minlzma), which is
+[MIT](3rdparty_licenses/LICENSE.minlzma) licensed and copyright:
+
+* Copyright (c) 2020 Alex Ionescu
+
+m1n1 embeds a slightly modified version of [tinf](https://github.com/jibsen/tinf), which is
+[ZLIB](3rdparty_licenses/LICENSE.tinf) licensed and copyright:
+
+* Copyright (c) 2003-2019 Joergen Ibsen
+
+m1n1 embeds portions taken from
+[arm-trusted-firmware](https://github.com/ARM-software/arm-trusted-firmware), which is
+[BSD](3rdparty_licenses/LICENSE.BSD-3.arm) licensed and copyright:
+
+* Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved.
+
+m1n1 embeds [Doug Lea's malloc](ftp://gee.cs.oswego.edu/pub/misc/malloc.c) (dlmalloc), which is in
+the public domain ([CC0](3rdparty_licenses/LICENSE.CC0)).
+
+m1n1 embeds portions of [PDCLib](https://github.com/DevSolar/pdclib), which is in the public
+domain ([CC0](3rdparty_licenses/LICENSE.CC0)).
+
+m1n1 embeds the [Source Code Pro](https://github.com/adobe-fonts/source-code-pro) font, which is
+licensed under the [OFL-1.1](3rdparty_licenses/LICENSE.OFL-1.1) license and copyright:
+
+* Copyright 2010-2019 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries.
+* This Font Software is licensed under the SIL Open Font License, Version 1.1.
+
+m1n1 embeds portions of the [dwc3 usb linux driver](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/usb/dwc3/core.h?id=7bc5a6ba369217e0137833f5955cf0b0f08b0712), which was [BSD-or-GPLv2 dual-licensed](3rdparty_licenses/LICENSE.BSD-3.dwc3) and copyright
+* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+
+m1n1 embeds portions of [musl-libc](https://musl.libc.org/)'s floating point library, which are MIT licensed and copyright
+* Copyright (c) 2017-2018, Arm Limited.
+
+m1n1 embeds some rust crates. Licenses can be found in the vendor directory for every crate.
diff --git a/tools/config.h b/tools/config.h
new file mode 100644
index 0000000..7ba0156
--- /dev/null
+++ b/tools/config.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+// Enable framebuffer console
+#define USE_FB
+// Disable framebuffer console unless verbose boot is enabled
+//#define FB_SILENT_MODE
+// Initialize USB early and break into proxy if device is opened within this time (sec)
+//#define EARLY_PROXY_TIMEOUT 5
+
+// Minimal build for bring-up
+//#define BRINGUP
+
+// Print RTKit logs to the console
+//#define RTKIT_SYSLOG
+
+// Target for device-specific debug builds
+//#define TARGET T8103
+
+#ifdef RELEASE
+# define FB_SILENT_MODE
+# ifdef CHAINLOADING
+# define EARLY_PROXY_TIMEOUT 5
+# endif
+#endif
+
+#endif
diff --git a/tools/data/bootlogo_128.bin b/tools/data/bootlogo_128.bin
new file mode 100644
index 0000000..6cea73a
--- /dev/null
+++ b/tools/data/bootlogo_128.bin
Binary files differ
diff --git a/tools/data/bootlogo_128.png b/tools/data/bootlogo_128.png
new file mode 120000
index 0000000..ca3d505
--- /dev/null
+++ b/tools/data/bootlogo_128.png
@@ -0,0 +1 @@
+../artwork/logos/png_128/AsahiLinux_logomark.png \ No newline at end of file
diff --git a/tools/data/bootlogo_256.bin b/tools/data/bootlogo_256.bin
new file mode 100644
index 0000000..de38abf
--- /dev/null
+++ b/tools/data/bootlogo_256.bin
Binary files differ
diff --git a/tools/data/bootlogo_256.png b/tools/data/bootlogo_256.png
new file mode 120000
index 0000000..99b04e7
--- /dev/null
+++ b/tools/data/bootlogo_256.png
@@ -0,0 +1 @@
+../artwork/logos/png_256/AsahiLinux_logomark.png \ No newline at end of file
diff --git a/tools/data/makelogo.sh b/tools/data/makelogo.sh
new file mode 100755
index 0000000..79e4491
--- /dev/null
+++ b/tools/data/makelogo.sh
@@ -0,0 +1,2 @@
+convert bootlogo_128.png -background black -flatten -depth 8 rgba:bootlogo_128.bin
+convert bootlogo_256.png -background black -flatten -depth 8 rgba:bootlogo_256.bin
diff --git a/tools/docker-compose.yml b/tools/docker-compose.yml
new file mode 100644
index 0000000..a54981d
--- /dev/null
+++ b/tools/docker-compose.yml
@@ -0,0 +1,5 @@
+services:
+ m1n1:
+ build: .
+ volumes:
+ - .:/m1n1
diff --git a/tools/font/README.md b/tools/font/README.md
new file mode 100644
index 0000000..a16e403
--- /dev/null
+++ b/tools/font/README.md
@@ -0,0 +1,3 @@
+These fonts have been generated with
+ makefont.sh 8 16 12 SourceCodePro-Bold.ttf font.bin
+ makefont.sh 16 32 25 SourceCodePro-Bold.ttf font_retina.bin
diff --git a/tools/font/SourceCodePro-Bold.ttf b/tools/font/SourceCodePro-Bold.ttf
new file mode 100644
index 0000000..dd00982
--- /dev/null
+++ b/tools/font/SourceCodePro-Bold.ttf
Binary files differ
diff --git a/tools/font/font.bin b/tools/font/font.bin
new file mode 100644
index 0000000..17f8338
--- /dev/null
+++ b/tools/font/font.bin
Binary files differ
diff --git a/tools/font/font_retina.bin b/tools/font/font_retina.bin
new file mode 100644
index 0000000..4803b68
--- /dev/null
+++ b/tools/font/font_retina.bin
Binary files differ
diff --git a/tools/font/makefont.sh b/tools/font/makefont.sh
new file mode 100755
index 0000000..37235fa
--- /dev/null
+++ b/tools/font/makefont.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+width=$1
+height=$2
+size=$3
+fontfile=$4
+outfile=$5
+shift 5
+
+(
+for ord in $(seq 32 126); do
+ printf "\\x$(printf %x $ord)\\n"
+done
+) | convert \
+ -page ${width}x$((height*95)) \
+ -background black \
+ -fill white \
+ -antialias \
+ -font $fontfile \
+ -density 72 \
+ -gravity north \
+ -pointsize $size \
+ $* \
+ -define quantum:format=unsigned \
+ -depth 8 \
+ label:\@- \
+ -crop ${width}x$((height*95)) \
+ gray:$outfile
diff --git a/tools/m1n1-raw.ld b/tools/m1n1-raw.ld
new file mode 100644
index 0000000..56ebfe4
--- /dev/null
+++ b/tools/m1n1-raw.ld
@@ -0,0 +1,150 @@
+ENTRY(_start)
+
+_stack_size = 0x20000;
+
+/* We are actually relocatable */
+. = 0;
+
+PHDRS
+{
+ hdr PT_LOAD;
+ text PT_LOAD;
+ rodata PT_LOAD;
+ data PT_LOAD;
+}
+
+SECTIONS {
+ _base = .;
+
+ _text_start = .;
+ .init : ALIGN(0x4000) {
+ *(.init)
+ *(.init.*)
+ } :text
+ .text : ALIGN(0x4000) {
+ *(.text)
+ *(.text.*)
+ . = ALIGN(8);
+ *(.got.plt)
+ . = ALIGN(0x4000);
+ } :text
+ _text_size = . - _text_start;
+ .rodata : ALIGN(0x4000) {
+ *(.rodata)
+ *(.rodata.*)
+ . = ALIGN(8);
+ } :rodata
+ .rela.dyn : {
+ _rela_start = .;
+ *(.rela)
+ *(.rela.text)
+ *(.rela.got)
+ *(.rela.plt)
+ *(.rela.bss)
+ *(.rela.ifunc)
+ *(.rela.text.*)
+ *(.rela.data)
+ *(.rela.data.*)
+ *(.rela.rodata)
+ *(.rela.rodata*)
+ *(.rela.dyn)
+ _rela_end = .;
+ . = ALIGN(0x4000);
+ } :rodata
+ _rodata_end = .;
+ _data_start = .;
+ .data : ALIGN(0x4000) {
+ *(.data)
+ *(.data.*)
+ . = ALIGN(8);
+ _got_start = .;
+ *(.got)
+ _got_end = .;
+ . = ALIGN(0x4000);
+ _file_end = .;
+ } :data
+ .bss : ALIGN(0x4000) {
+ _bss_start = .;
+ *(.bss)
+ *(.bss.*)
+ *(.dynbss)
+ *(COMMON)
+ _bss_end = .;
+ } : data
+ .stack : ALIGN(0x4000) {
+ PROVIDE(_stack_top = .);
+ . += _stack_size - 8;
+ QUAD(0x544f424b43415453);
+ PROVIDE(_stack_bot = .);
+ } :data
+ ASSERT(ALIGN(0x4000) == ., "Stack size is not aligned!")
+ _data_size = . - _data_start;
+ _end = .;
+ _payload_start = .;
+
+ .symtab 0 : { *(.symtab) }
+ .strtab 0 : { *(.strtab) }
+ .shstrtab 0 : { *(.shstrtab) }
+
+ /DISCARD/ : {
+ *(.discard)
+ *(.discard.*)
+ *(.interp .dynamic)
+ *(.dynsym .dynstr .hash .gnu.hash)
+ *(.eh_frame)
+ *(.gnu.version*)
+ *(.note*)
+ *(.comment*)
+ }
+
+ .empty (NOLOAD) : {
+ *(.plt) *(.plt.*) *(.iplt) *(.igot)
+ *(.data.rel.ro)
+ }
+ ASSERT(SIZEOF(.empty) == 0, "Unexpected sections detected!")
+
+ .got.plt (NOLOAD) : {
+ *(.got.plt)
+ }
+ ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0x18, "Unexpected GOT PLT detected!")
+
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ .debug_pubtypes 0 : { *(.debug_pubtypes) }
+ .debug_ranges 0 : { *(.debug_ranges) }
+ .debug_types 0 : { *(.debug_types) }
+ .debug_addr 0 : { *(.debug_addr) }
+ .debug_line_str 0 : { *(.debug_line_str) }
+ .debug_loclists 0 : { *(.debug_loclists) }
+ .debug_macro 0 : { *(.debug_macro) }
+ .debug_names 0 : { *(.debug_names) }
+ .debug_rnglists 0 : { *(.debug_rnglists) }
+ .debug_str_offsets 0 : { *(.debug_str_offsets) }
+ .debug_sup 0 : { *(.debug_sup) }
+}
+
+PROT_READ = 0x01;
+PROT_WRITE = 0x02;
+PROT_EXECUTE = 0x04;
diff --git a/tools/m1n1.ld b/tools/m1n1.ld
new file mode 100644
index 0000000..6ac86d7
--- /dev/null
+++ b/tools/m1n1.ld
@@ -0,0 +1,256 @@
+ENTRY(_start)
+
+/* Fake virtual load address for the mach-o */
+_va_base = 0xFFFFFE0007004000;
+
+_stack_size = 0x20000;
+
+_max_payload_size = 64*1024*1024;
+
+/* We are actually relocatable */
+. = 0;
+
+PHDRS
+{
+ hdr PT_LOAD;
+ text PT_LOAD;
+ rodata PT_LOAD;
+ data PT_LOAD;
+}
+
+SECTIONS {
+ _base = .;
+
+ .header : {
+ _mach_header = .;
+ /* mach-o header */
+ LONG(0xfeedfacf); /* magic */
+ LONG(0x100000c); /* cputype */
+ LONG(0x02); /* cputype */
+ LONG(0x0c); /* filetype */
+ LONG(6); /* ncmds */
+ LONG(_cmd_end - _cmd_start); /* sizeofcmds */
+ LONG(4); /* flags */
+ LONG(0); /* reserved */
+
+ _cmd_start = .;
+
+ /* unix_thread (entrypoint) */
+ LONG(0x5); /* type = UNIX_THREAD */
+ LONG(0x120); /* cmdsize */
+ LONG(6); /* ARM_THREAD64 */
+ LONG(0x44); /* length */
+ . += 32 * 8; /* useless registers */
+ QUAD(_start + _va_off) /* pc */
+ . += 8; /* useless registers */
+
+ ASSERT(. - _cmd_start == 0x120, "Bad unix_thread structure");
+
+ /* segment: mach-o structures */
+ LONG(0x19); /* type = SEGMENT_64 */
+ LONG(0x48); /* cmdsize */
+ LONG(0x5244485f); /* segname = "_HDR" */
+ . += 12;
+ QUAD(ADDR(.header) + _va_off); /* vmaddr */
+ QUAD(SIZEOF(.header)); /* vmsize */
+ QUAD(ADDR(.header) - _base); /* fileoff */
+ QUAD(SIZEOF(.header)); /* filesize */
+ LONG(PROT_READ); /* maxprot */
+ LONG(PROT_READ); /* initprot */
+ LONG(0); /* nsects */
+ LONG(0); /* flags */
+
+ /* segment: text */
+ LONG(0x19); /* type = SEGMENT_64 */
+ LONG(0x48); /* cmdsize */
+ LONG(0x54584554); /* segname = "TEXT" */
+ . += 12;
+ QUAD(ADDR(.init) + _va_off); /* vmaddr */
+ QUAD(_text_size); /* vmsize */
+ QUAD(ADDR(.init) - _base); /* fileoff */
+ QUAD(_text_size); /* filesize */
+ LONG(PROT_READ | PROT_EXECUTE); /* maxprot */
+ LONG(PROT_READ | PROT_EXECUTE); /* initprot */
+ LONG(0); /* nsects */
+ LONG(0); /* flags */
+
+ /* segment: rodata */
+ LONG(0x19); /* type = SEGMENT_64 */
+ LONG(0x48); /* cmdsize */
+ LONG(0x41444F52); /* segname = "RODA" */
+ . += 12;
+ QUAD(ADDR(.rodata) + _va_off); /* vmaddr */
+ QUAD(_rodata_end - ADDR(.rodata)); /* vmsize */
+ QUAD(ADDR(.rodata) - _base); /* fileoff */
+ QUAD(_rodata_end - ADDR(.rodata)); /* filesize */
+ LONG(PROT_READ); /* maxprot */
+ LONG(PROT_READ); /* initprot */
+ LONG(0); /* nsects */
+ LONG(0); /* flags */
+
+ /* segment: data */
+ LONG(0x19); /* type = SEGMENT_64 */
+ LONG(0x48); /* cmdsize */
+ LONG(0x41544144); /* segmname = "DATA" */
+ . += 12;
+ QUAD(ADDR(.data) + _va_off); /* vmaddr */
+ QUAD(_data_size); /* vmsize */
+ QUAD(ADDR(.data) - _base); /* fileoff */
+ QUAD(SIZEOF(.data)); /* filesize */
+ LONG(PROT_READ | PROT_WRITE); /* maxprot */
+ LONG(PROT_READ | PROT_WRITE); /* initprot */
+ LONG(0); /* nsects */
+ LONG(0); /* flags */
+
+ /* segment: payload */
+ LONG(0x19); /* type = SEGMENT_64 */
+ LONG(0x48); /* cmdsize */
+ LONG(0x444C5950); /* segmname = "PYLD" */
+ . += 12;
+ QUAD(_end + _va_off); /* vmaddr */
+ QUAD(_max_payload_size); /* vmsize */
+ QUAD(_file_end - _base); /* fileoff */
+ QUAD(_max_payload_size); /* filesize */
+ LONG(PROT_READ | PROT_WRITE); /* maxprot */
+ LONG(PROT_READ | PROT_WRITE); /* initprot */
+ LONG(0); /* nsects */
+ LONG(0); /* flags */
+
+ _cmd_end = .;
+
+ . = ALIGN(0x4000);
+ _hdr_end = .;
+ } :hdr
+ _text_start = .;
+ .init : ALIGN(0x4000) {
+ *(.init)
+ *(.init.*)
+ } :text
+ .text : ALIGN(0x4000) {
+ *(.text)
+ *(.text.*)
+ . = ALIGN(8);
+ *(.got.plt)
+ . = ALIGN(0x4000);
+ } :text
+ _text_size = . - _text_start;
+ .rodata : ALIGN(0x4000) {
+ *(.rodata)
+ *(.rodata.*)
+ . = ALIGN(8);
+ } :rodata
+ .rela.dyn : {
+ _rela_start = .;
+ *(.rela)
+ *(.rela.text)
+ *(.rela.got)
+ *(.rela.plt)
+ *(.rela.bss)
+ *(.rela.ifunc)
+ *(.rela.text.*)
+ *(.rela.data)
+ *(.rela.data.*)
+ *(.rela.rodata)
+ *(.rela.rodata*)
+ *(.rela.dyn)
+ _rela_end = .;
+ . = ALIGN(0x4000);
+ } :rodata
+ _rodata_end = .;
+ _data_start = .;
+ .data : ALIGN(0x4000) {
+ *(.data)
+ *(.data.*)
+ . = ALIGN(8);
+ _got_start = .;
+ *(.got)
+ _got_end = .;
+ . = ALIGN(0x4000);
+ _file_end = .;
+ } :data
+ .bss : ALIGN(0x4000) {
+ _bss_start = .;
+ *(.bss)
+ *(.bss.*)
+ *(.dynbss)
+ *(COMMON)
+ . = ALIGN(0x4000);
+ _bss_end = .;
+ PROVIDE(_stack_top = .);
+ . += _stack_size;
+ PROVIDE(_stack_bot = .);
+ . = ALIGN(0x4000);
+ } :data
+ _data_size = . - _data_start;
+ _end = .;
+ _payload_start = .;
+ _payload_end = . + _max_payload_size;
+
+ .symtab 0 : { *(.symtab) }
+ .strtab 0 : { *(.strtab) }
+ .shstrtab 0 : { *(.shstrtab) }
+
+ /DISCARD/ : {
+ *(.discard)
+ *(.discard.*)
+ *(.interp .dynamic)
+ *(.dynsym .dynstr .hash .gnu.hash)
+ *(.eh_frame)
+ *(.gnu.version*)
+ *(.note*)
+ *(.comment*)
+ }
+
+ .empty (NOLOAD) : {
+ *(.plt) *(.plt.*) *(.iplt) *(.igot)
+ *(.data.rel.ro)
+ }
+ ASSERT(SIZEOF(.empty) == 0, "Unexpected sections detected!")
+
+ .got.plt (NOLOAD) : {
+ *(.got.plt)
+ }
+ ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0x18, "Unexpected GOT PLT detected!")
+
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ .debug_pubtypes 0 : { *(.debug_pubtypes) }
+ .debug_ranges 0 : { *(.debug_ranges) }
+ .debug_types 0 : { *(.debug_types) }
+ .debug_addr 0 : { *(.debug_addr) }
+ .debug_line_str 0 : { *(.debug_line_str) }
+ .debug_loclists 0 : { *(.debug_loclists) }
+ .debug_macro 0 : { *(.debug_macro) }
+ .debug_names 0 : { *(.debug_names) }
+ .debug_rnglists 0 : { *(.debug_rnglists) }
+ .debug_str_offsets 0 : { *(.debug_str_offsets) }
+ .debug_sup 0 : { *(.debug_sup) }
+}
+
+PROT_READ = 0x01;
+PROT_WRITE = 0x02;
+PROT_EXECUTE = 0x04;
+
+_va_off = _va_base - _base;
diff --git a/tools/proxyclient/experiments/addrdump.py b/tools/proxyclient/experiments/addrdump.py
new file mode 100755
index 0000000..ba23226
--- /dev/null
+++ b/tools/proxyclient/experiments/addrdump.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+
+blacklist = []
+
+print("Dumping address space...")
+of = None
+if len(sys.argv) > 1:
+ of = open(sys.argv[1],"w")
+ print("Also dumping to file %s")
+
+for i in range(0x0000, 0x10000):
+ if i in blacklist:
+ v = "%08x: SKIPPED"%(i<<16)
+ else:
+ a = (i<<16) + 0x1000
+ d = p.read32(a)
+ v = "%08x: %08x"%(a, d)
+ print(v)
+ if of:
+ of.write(v+"\n")
+
+if of:
+ of.close()
diff --git a/tools/proxyclient/experiments/aes.py b/tools/proxyclient/experiments/aes.py
new file mode 100644
index 0000000..77d017a
--- /dev/null
+++ b/tools/proxyclient/experiments/aes.py
@@ -0,0 +1,144 @@
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+from m1n1.shell import run_shell
+from m1n1.hw.dart import DART
+from m1n1.hw.aes import *
+
+def aes_set_custom_key(
+ aes,
+ key,
+ encrypt=True,
+ mode=AES_SET_KEY_BLOCK_MODE.CTR,
+ keyslot=0,
+ keygen=0,
+):
+ keylen = {
+ 16: AES_SET_KEY_LEN.AES128,
+ 24: AES_SET_KEY_LEN.AES192,
+ 32: AES_SET_KEY_LEN.AES256,
+ }[len(key)]
+
+ aes.R_CMD_FIFO = AESSetKeyCommand(
+ KEY_SELECT=0,
+ KEYLEN=keylen,
+ ENCRYPT=1 if encrypt else 0,
+ BLOCK_MODE=mode,
+ SLOT=keyslot,
+ KEYGEN=keygen,
+ ).value
+
+ for i in range(0, len(key), 4):
+ aes.R_CMD_FIFO = struct.unpack(">I", key[i : i + 4])[0]
+
+
+def aes_set_hw_key(
+ aes,
+ key,
+ keylen=AES_SET_KEY_LEN.AES128,
+ encrypt=True,
+ mode=AES_SET_KEY_BLOCK_MODE.CTR,
+ slot=0,
+ keygen=0,
+):
+ aes.R_CMD_FIFO = AESSetKeyCommand(
+ KEY_SELECT=key,
+ KEYLEN=keylen,
+ ENCRYPT=1 if encrypt else 0,
+ BLOCK_MODE=mode,
+ SLOT=slot,
+ KEYGEN=keygen,
+ ).value
+
+
+def aes_set_iv(aes, iv, slot=0):
+ assert len(iv) == 16
+ aes.R_CMD_FIFO = AESSetIVCommand(SLOT=slot)
+
+ for i in range(0, len(iv), 4):
+ aes.R_CMD_FIFO = struct.unpack(">I", iv[i : i + 4])[0]
+
+
+def aes_crypt(aes, dart, data, key_slot=0, iv_slot=0):
+ assert len(data) % 16 == 0
+
+ bfr = p.memalign(0x4000, len(data))
+ iova = dart.iomap(1, bfr, len(data))
+ dart.iowrite(1, iova, data)
+
+ aes.R_CMD_FIFO = AESCryptCommand(LEN=len(data), KEY_SLOT=key_slot, IV_SLOT=iv_slot)
+ aes.R_CMD_FIFO = 0 # actually upper bits of addr
+ aes.R_CMD_FIFO = iova # src
+ aes.R_CMD_FIFO = iova # dst
+
+ aes.R_CMD_FIFO = AESBarrierCommand(IRQ=1).value
+ time.sleep(0.1)
+ # while aes.R_IRQ_STATUS.reg.FLAG != 1:
+ # pass
+ # aes.dump_regs()
+ aes.R_IRQ_STATUS = aes.R_IRQ_STATUS.val
+
+ res = dart.ioread(1, iova, len(data))
+ return res
+
+
+def test_hw_key(key, keylen, keygen=0):
+ aes.R_IRQ_STATUS = aes.R_IRQ_STATUS.val
+ aes.R_CONTROL.set(CLEAR_FIFO=1)
+ aes.R_CONTROL.set(RESET=1)
+ aes.R_CONTROL.set(START=1)
+ # aes.dump_regs()
+ aes_set_hw_key(aes, key, keylen, slot=0, keygen=keygen)
+ # print(aes.R_IRQ_STATUS)
+ aes_set_iv(aes, b"\x00" * 16, slot=0)
+ chexdump(aes_crypt(aes, dart, b"\x00" * 16, key_slot=0, iv_slot=1))
+ # aes.dump_regs()
+ aes.R_CONTROL.set(STOP=1)
+
+
+def test_custom_key(key, keygen=0):
+ aes.R_IRQ_STATUS = aes.R_IRQ_STATUS.val
+ aes.R_CONTROL.set(CLEAR_FIFO=1)
+ aes.R_CONTROL.set(RESET=1)
+ aes.R_CONTROL.set(START=1)
+ # aes.dump_regs()
+ aes_set_custom_key(aes, key, keyslot=0, keygen=keygen)
+ aes_set_iv(aes, b"\x00" * 16)
+ aes_set_iv(aes, b"\x11" * 16, slot=1)
+ chexdump(aes_crypt(aes, dart, b"\x00" * 16, key_slot=0, iv_slot=0))
+ # aes.dump_regs()
+ aes.R_CONTROL.set(STOP=1)
+
+
+p.pmgr_adt_clocks_enable("/arm-io/aes")
+
+dart = DART.from_adt(u, "/arm-io/dart-sio")
+dart.initialize()
+
+aes_base, _ = u.adt["/arm-io/aes"].get_reg(0)
+aes = AESRegs(u, aes_base)
+aes.dump_regs()
+
+dart.dump_all()
+
+for keygen in range(4):
+ print(f"zero key, keygen={keygen}", end="")
+ test_custom_key(b"\x00" * 16, keygen=keygen)
+
+for keygen in range(4):
+ print("#" * 10)
+ for keylen in [
+ AES_SET_KEY_LEN.AES128,
+ AES_SET_KEY_LEN.AES192,
+ AES_SET_KEY_LEN.AES256,
+ ]:
+ for i in (1, 3):
+ print(f"key = {i}, keylen={keylen}, keygen={keygen}", end="")
+ test_hw_key(i, keylen, keygen=keygen)
+
+dart.dump_all()
+
+run_shell(globals(), msg="Have fun!")
diff --git a/tools/proxyclient/experiments/agx_1tri.py b/tools/proxyclient/experiments/agx_1tri.py
new file mode 100644
index 0000000..af06713
--- /dev/null
+++ b/tools/proxyclient/experiments/agx_1tri.py
@@ -0,0 +1,840 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+
+import sys, pathlib, time
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+from m1n1.constructutils import Ver
+from m1n1.utils import *
+
+Ver.set_version(u)
+
+from m1n1.shell import run_shell
+
+from m1n1.agx import AGX
+from m1n1.agx.context import *
+
+p.pmgr_adt_clocks_enable("/arm-io/gfx-asc")
+p.pmgr_adt_clocks_enable("/arm-io/sgx")
+#p.pmgr_adt_clocks_enable("/arm-io/pmp")
+
+# [cpu0] [0xfffffe00124bf5c0] MMIO: R.4 0x204d14000 (sgx, offset 0xd14000) = 0x0
+p.read32(0x204000000 + 0xd14000)
+# [cpu0] [0xfffffe00124bf9a8] MMIO: W.4 0x204d14000 (sgx, offset 0xd14000) = 0x70001
+p.write32(0x204000000 + 0xd14000, 0x70001)
+
+#p.read32(0x204010258)
+
+agx = AGX(u)
+
+mon = RegMonitor(u, ascii=True, bufsize=0x8000000)
+agx.mon = mon
+
+sgx = agx.sgx_dev
+mon.add(sgx.gpu_region_base, sgx.gpu_region_size, "contexts")
+mon.add(sgx.gfx_shared_region_base, sgx.gfx_shared_region_size, "gfx-shared")
+mon.add(sgx.gfx_handoff_base, sgx.gfx_handoff_size, "gfx-handoff")
+
+#addr, size = sgx.get_reg(0)
+#mon.add(addr + 0x600000, size - 0x600000, "sgx")
+
+addr, size = u.adt["/arm-io/aic"].get_reg(0)
+mon.add(addr, size, "aic")
+
+def unswizzle(addr, w, h, psize, dump=None, grid=False):
+ tw = 64
+ th = 64
+ ntx = (w + tw - 1) // 64
+ nty = (h + th - 1) // 64
+ data = iface.readmem(addr, ntx * nty * psize * tw * th)
+ new_data = []
+ for y in range(h):
+ ty = y // th
+ for x in range(w):
+ tx = x // tw
+ toff = tw * th * psize * (ty * ntx + tx)
+ j = x & (tw - 1)
+ i = y & (th - 1)
+ off = (
+ ((j & 1) << 0) | ((i & 1) << 1) |
+ ((j & 2) << 1) | ((i & 2) << 2) |
+ ((j & 4) << 2) | ((i & 4) << 3) |
+ ((j & 8) << 3) | ((i & 8) << 4) |
+ ((j & 16) << 4) | ((i & 16) << 5) |
+ ((j & 32) << 5) | ((i & 32) << 6))
+ r,g,b,a = data[toff + psize*off: toff + psize*(off+1)]
+ if grid:
+ if x % 64 == 0 or y % 64 == 0:
+ r,g,b,a = 255,255,255,255
+ elif x % 32 == 0 or y % 32 == 0:
+ r,g,b,a = 128,128,128,255
+ new_data.append(bytes([b, g, r, a]))
+ data = b"".join(new_data)
+ if dump:
+ open(dump, "wb").write(data)
+ iface.writemem(addr, data)
+
+try:
+ agx.start()
+
+ ctx_id = 3
+ buffer_mgr_slot = 2
+
+ #agx.initdata.regionA.add_to_mon(mon)
+ #agx.initdata.regionB.add_to_mon(mon)
+ #agx.initdata.regionC.add_to_mon(mon)
+
+ #agx.initdata.regionB.unk_170.add_to_mon(mon)
+ #agx.initdata.regionB.unk_178.add_to_mon(mon)
+ #agx.initdata.regionB.unk_180.add_to_mon(mon)
+ #agx.initdata.regionB.unk_190.add_to_mon(mon)
+ #agx.initdata.regionB.unk_198.add_to_mon(mon)
+ ##agx.initdata.regionB.fwlog_ring2.add_to_mon(mon)
+ #agx.initdata.regionB.hwdata_a.add_to_mon(mon)
+ #agx.initdata.regionB.hwdata_b.add_to_mon(mon)
+ mon.poll()
+
+ #agx.asc.work_for(0.3)
+
+ #p.write32(agx.initdata.regionC._paddr + 0x8900, 0xffffffff)
+ #p.write32(agx.initdata.regionC._paddr + 0x8904, 0xffffffff)
+ #mon.poll()
+
+ agx.kick_firmware()
+ #agx.asc.work_for(0.3)
+
+ #mon.poll()
+
+ ##### Initialize context and load data
+
+ ctx = GPUContext(agx)
+ ctx.bind(ctx_id)
+
+ #p.read32(0x204000000 + 0xd14000)
+ #p.write32(0x204000000 + 0xd14000, 0x70001)
+
+ #base = "gpudata/1tri/"
+ #base = "gpudata/mesa-flag/"
+ base = "gpudata/bunny/"
+ ctx.load_blob(0x1100000000, True, base + "mem_0_0.bin")
+ ctx.load_blob(0x1100008000, True, base + "mem_8000_0.bin")
+ ctx.load_blob(0x1100010000, True, base + "mem_10000_0.bin")
+ ctx.load_blob(0x1100058000, True, base + "mem_58000_0.bin")
+ ctx.load_blob(0x1100060000, True, base + "mem_60000_0.bin")
+ #ctx.load_blob(0x1100068000, True, base + "mem_68000_0.bin")
+ ctx.load_blob(0x1500000000, False, base + "mem_1500000000_0.bin")
+ ctx.load_blob(0x1500048000, False, base + "mem_1500048000_0.bin")
+ ctx.load_blob(0x15000d0000, False, base + "mem_15000d0000_0.bin")
+ ctx.load_blob(0x1500158000, False, base + "mem_1500158000_0.bin")
+ ctx.load_blob(0x15001e0000, False, base + "mem_15001e0000_0.bin")
+ ctx.load_blob(0x15001e8000, False, base + "mem_15001e8000_0.bin")
+ ctx.load_blob(0x15001f0000, False, base + "mem_15001f0000_0.bin")
+ ctx.load_blob(0x15001f8000, False, base + "mem_15001f8000_0.bin")
+ #ctx.load_blob(0x1500490000, False, base + "mem_1500490000_0.bin")
+ #ctx.load_blob(0x1500518000, False, base + "mem_1500518000_0.bin")
+ #color = ctx.buf_at(0x1500200000, False, 1310720, "Color", track=False)
+ #depth = ctx.buf_at(0x1500348000, False, 1310720, "Depth", track=False)
+ color = ctx.buf_at(0x1500200000, False, 2129920, "Color", track=False)
+ depth = ctx.buf_at(0x1500410000, False, 2129920, "Depth", track=False)
+ ctx.load_blob(0x1500620000, False, base + "mem_1500620000_0.bin", track=False)
+ ctx.load_blob(0x1500890000, False, base + "mem_1500890000_0.bin", track=False)
+
+ mon.poll()
+
+ p.memset32(color._paddr, 0xdeadbeef, color._size)
+ p.memset32(depth._paddr, 0xdeadbeef, depth._size)
+
+ stencil = ctx.buf_at(0x1510410000, False, 2129920, "Stencil", track=False)
+
+ width = 800
+ height = 600
+
+ width_a = align_up(width, 64)
+ height_a = align_up(height, 64)
+
+ depth_addr = depth._addr
+
+ ##### Initialize buffer manager
+
+ #buffer_mgr = GPUBufferManager(agx, ctx, 26)
+ buffer_mgr = GPUBufferManager(agx, ctx, 8)
+
+ ##### Initialize work queues
+
+ wq_3d = GPU3DWorkQueue(agx, ctx)
+ wq_ta = GPUTAWorkQueue(agx, ctx)
+
+ ##### TA stamps
+
+ #Message 1: DAG: Non Sequential Stamp Updates seen entryIdx 0x41 roots.dag 0x1 stampIdx 0x7 stampValue 0x4100 channel 0xffffffa000163f58 channelRingCommandIndex 0x1
+
+ prev_stamp_value = 0x4000
+ stamp_value = 0x4100
+
+ # start?
+ stamp_ta1 = agx.kshared.new(BarrierCounter, name="TA stamp 1")
+ stamp_ta1.value = prev_stamp_value
+ stamp_ta1.push()
+
+ # complete?
+ stamp_ta2 = agx.kobj.new(BarrierCounter, name="TA stamp 2")
+ stamp_ta2.value = prev_stamp_value
+ stamp_ta2.push()
+
+ ##### 3D stamps
+
+ # start?
+ stamp_3d1 = agx.kshared.new(BarrierCounter, name="3D stamp 1")
+ stamp_3d1.value = prev_stamp_value
+ stamp_3d1.push()
+
+ # complete?
+ stamp_3d2 = agx.kobj.new(BarrierCounter, name="3D stamp 2")
+ stamp_3d2.value = prev_stamp_value
+ stamp_3d2.push()
+
+ ##### Some kind of feedback/status buffer, GPU managed?
+
+ event_control = agx.kobj.new(EventControl)
+ event_control.event_count = agx.kobj.new(Int32ul, "Event Count")
+ event_control.base_stamp = 0x15 #0
+ event_control.unk_c = 0
+ event_control.unk_10 = 0x50
+ event_control.push()
+
+ ##### TVB allocations / Tiler config
+
+ tile_width = 32
+ tile_height = 32
+ tiles_x = ((width + tile_width - 1) // tile_width)
+ tiles_y = ((height + tile_height - 1) // tile_height)
+ tiles = tiles_x * tiles_y
+
+ tile_blocks_x = (tiles_x + 15) // 16
+ tile_blocks_y = (tiles_y + 15) // 16
+ tile_blocks = tile_blocks_x * tile_blocks_y
+
+ tiling_params = TilingParameters()
+ tiling_params.size1 = 0x14 * tile_blocks
+ tiling_params.unk_4 = 0x88
+ tiling_params.unk_8 = 0x202
+ tiling_params.x_max = width - 1
+ tiling_params.y_max = height - 1
+ tiling_params.tile_count = ((tiles_y-1) << 12) | (tiles_x-1)
+ tiling_params.x_blocks = (12 * tile_blocks_x) | (tile_blocks_x << 12) | (tile_blocks_x << 20)
+ tiling_params.y_blocks = (12 * tile_blocks_y) | (tile_blocks_y << 12) | (tile_blocks_y << 20)
+ tiling_params.size2 = 0x10 * tile_blocks
+ tiling_params.size3 = 0x20 * tile_blocks
+ tiling_params.unk_24 = 0x100
+ tiling_params.unk_28 = 0x8000
+
+ tvb_something_size = 0x800 * tile_blocks
+ tvb_something = ctx.uobj.new_buf(tvb_something_size, "TVB Something")
+
+ tvb_tilemap_size = 0x800 * tile_blocks
+ tvb_tilemap = ctx.uobj.new_buf(tvb_tilemap_size, "TVB Tilemap")
+
+ tvb_heapmeta_size = 0x4000
+ tvb_heapmeta = ctx.uobj.new_buf(tvb_heapmeta_size, "TVB Heap Meta")
+
+ ##### Buffer stuff?
+
+ # buffer related?
+ buf_desc = agx.kobj.new(BufferThing)
+ buf_desc.unk_0 = 0x0
+ buf_desc.unk_8 = 0x0
+ buf_desc.unk_10 = 0x0
+ buf_desc.unkptr_18 = ctx.uobj.buf(0x80, "BufferThing.unkptr_18")
+ buf_desc.unk_20 = 0x0
+ buf_desc.bm_misc_addr = buffer_mgr.misc_obj._addr
+ buf_desc.unk_2c = 0x0
+ buf_desc.unk_30 = 0x0
+ buf_desc.unk_38 = 0x0
+ buf_desc.push()
+
+ uuid_3d = 0x4000a14
+ uuid_ta = 0x4000a15
+ encoder_id = 0x30009fb
+
+ ##### 3D barrier command
+
+ ev_ta = 6
+ ev_3d = 7
+
+ barrier_cmd = agx.kobj.new(WorkCommandBarrier)
+ barrier_cmd.stamp = stamp_ta2
+ barrier_cmd.stamp_value1 = 0x4100
+ barrier_cmd.stamp_value2 = 0x4100
+ barrier_cmd.event = ev_ta
+ barrier_cmd.uuid = uuid_3d
+
+
+ #stamp.add_to_mon(mon)
+ #stamp2.add_to_mon(mon)
+
+ print(barrier_cmd)
+
+ wq_3d.submit(barrier_cmd.push())
+
+ ##### 3D execution
+
+ wc_3d = agx.kobj.new(WorkCommand3D)
+ wc_3d.context_id = ctx_id
+ wc_3d.unk_8 = 0
+ wc_3d.event_control = event_control
+ wc_3d.buffer_mgr = buffer_mgr.info
+ wc_3d.buf_thing = buf_desc
+ wc_3d.unk_emptybuf_addr = agx.kobj.buf(0x100, "unk_emptybuf")
+ wc_3d.tvb_tilemap = tvb_tilemap._addr
+ wc_3d.unk_40 = 0x88
+ wc_3d.unk_48 = 0x1
+ wc_3d.tile_blocks_y = tile_blocks_y * 4
+ wc_3d.tile_blocks_x = tile_blocks_x * 4
+ wc_3d.unk_50 = 0x0
+ wc_3d.unk_58 = 0x0
+ wc_3d.uuid1 = 0x3b315cae
+ wc_3d.uuid2 = 0x3b6c7b92
+ wc_3d.unk_68 = 0x0
+ wc_3d.tile_count = tiles
+
+ wc_3d.unk_buf = WorkCommand1_UnkBuf()
+ wc_3d.unk_word = BarrierCounter()
+ wc_3d.unk_buf2 = WorkCommand1_UnkBuf2()
+ wc_3d.unk_buf2.unk_0 = 0
+ wc_3d.unk_buf2.unk_8 = 0
+ wc_3d.unk_buf2.unk_10 = 1
+ wc_3d.ts1 = Timestamp()
+ wc_3d.ts2 = Timestamp()
+ wc_3d.ts3 = Timestamp()
+ wc_3d.unk_914 = 0
+ wc_3d.unk_918 = 0
+ wc_3d.unk_920 = 0
+ wc_3d.unk_924 = 1
+
+ # Structures embedded in WorkCommand3D
+ if True:
+ wc_3d.struct_1 = Start3DStruct1()
+ wc_3d.struct_1.store_pipeline_addr = 0x14004 # CHECKED
+ wc_3d.struct_1.unk_8 = 0x0
+ wc_3d.struct_1.unk_c = 0x0
+ wc_3d.struct_1.uuid1 = wc_3d.uuid1
+ wc_3d.struct_1.uuid2 = wc_3d.uuid2
+ wc_3d.struct_1.unk_18 = 0x0
+ wc_3d.struct_1.tile_blocks_y = tile_blocks_y * 4
+ wc_3d.struct_1.tile_blocks_x = tile_blocks_x * 4
+ wc_3d.struct_1.unk_24 = 0x0
+ wc_3d.struct_1.tile_counts = ((tiles_y-1) << 12) | (tiles_x-1)
+ wc_3d.struct_1.unk_2c = 0x8
+ wc_3d.struct_1.depth_clear_val1 = 1.0 # works
+ wc_3d.struct_1.stencil_clear_val1 = 0x0
+ wc_3d.struct_1.unk_38 = 0x0
+ wc_3d.struct_1.unk_3c = 0x1
+ wc_3d.struct_1.unk_40_padding = bytes(0xb0)
+ wc_3d.struct_1.depth_bias_array = Start3DArrayAddr(0x1500158000)
+ wc_3d.struct_1.scissor_array = Start3DArrayAddr(0x15000d0000)
+ wc_3d.struct_1.unk_110 = 0x0
+ wc_3d.struct_1.unk_118 = 0x0
+ wc_3d.struct_1.unk_120 = [0] * 37
+ wc_3d.struct_1.unk_reload_pipeline = Start3DStorePipelineBinding(0xffff8212, 0xfffffff4)
+ wc_3d.struct_1.unk_258 = 0
+ wc_3d.struct_1.unk_260 = 0
+ wc_3d.struct_1.unk_268 = 0
+ wc_3d.struct_1.unk_270 = 0
+ wc_3d.struct_1.reload_pipeline = Start3DClearPipelineBinding(0xffff8212, 0x13004) # CHECKED
+ wc_3d.struct_1.depth_flags = 0x00000
+ wc_3d.struct_1.unk_290 = 0x0
+ wc_3d.struct_1.depth_buffer_ptr1 = depth_addr
+ wc_3d.struct_1.unk_2a0 = 0x0
+ wc_3d.struct_1.unk_2a8 = 0x0
+ wc_3d.struct_1.depth_buffer_ptr2 = depth_addr
+ wc_3d.struct_1.depth_buffer_ptr3 = depth_addr
+ wc_3d.struct_1.unk_2c0 = 0x0
+ wc_3d.struct_1.stencil_buffer_ptr1 = stencil._addr
+ wc_3d.struct_1.unk_2d0 = 0x0
+ wc_3d.struct_1.unk_2d8 = 0x0
+ wc_3d.struct_1.stencil_buffer_ptr2 = stencil._addr
+ wc_3d.struct_1.stencil_buffer_ptr3 = stencil._addr
+ wc_3d.struct_1.unk_2f0 = [0x0, 0x0, 0x0]
+ wc_3d.struct_1.aux_fb_unk0 = 0x4
+ wc_3d.struct_1.unk_30c = 0x0
+ wc_3d.struct_1.aux_fb = AuxFBInfo(0xc000, 0, width, height)
+ wc_3d.struct_1.unk_320_padding = bytes(0x10)
+ wc_3d.struct_1.unk_partial_store_pipeline = Start3DStorePipelineBinding(0xffff8212, 0xfffffff4)
+ wc_3d.struct_1.partial_store_pipeline = Start3DStorePipelineBinding(0x12, 0x14004) # CHECKED
+ wc_3d.struct_1.depth_clear_val2 = 1.0
+ wc_3d.struct_1.stencil_clear_val2 = 0x0
+ wc_3d.struct_1.context_id = ctx_id
+ wc_3d.struct_1.unk_376 = 0x0
+ wc_3d.struct_1.unk_378 = 0x8
+ wc_3d.struct_1.unk_37c = 0x0
+ wc_3d.struct_1.unk_380 = 0x0
+ wc_3d.struct_1.unk_388 = 0x0
+ wc_3d.struct_1.depth_dimensions = 0x12b831f #0xef827f
+
+ if True:
+ wc_3d.struct_2 = Start3DStruct2()
+ wc_3d.struct_2.unk_0 = 0xa000
+ wc_3d.struct_2.clear_pipeline = Start3DClearPipelineBinding(0xffff8002, 0x12004)
+ wc_3d.struct_2.unk_18 = 0x88
+ wc_3d.struct_2.scissor_array = 0x15000d0000
+ wc_3d.struct_2.depth_bias_array = 0x1500158000
+ wc_3d.struct_2.aux_fb = wc_3d.struct_1.aux_fb
+ wc_3d.struct_2.depth_dimensions = wc_3d.struct_1.depth_dimensions
+ wc_3d.struct_2.unk_48 = 0x0
+ wc_3d.struct_2.depth_flags = wc_3d.struct_1.depth_flags
+ wc_3d.struct_2.depth_buffer_ptr1 = depth_addr
+ wc_3d.struct_2.depth_buffer_ptr2 = depth_addr
+ wc_3d.struct_2.stencil_buffer_ptr1 = stencil._addr
+ wc_3d.struct_2.stencil_buffer_ptr2 = stencil._addr
+ wc_3d.struct_2.unk_68 = [0] * 12
+ wc_3d.struct_2.tvb_tilemap = tvb_tilemap._addr
+ wc_3d.struct_2.tvb_heapmeta_addr = tvb_heapmeta._addr
+ wc_3d.struct_2.unk_e8 = 0x50000000 * tile_blocks
+ wc_3d.struct_2.tvb_heapmeta_addr2 = tvb_heapmeta._addr
+ wc_3d.struct_2.unk_f8 = 0x10280 # TODO: varies 0, 0x280, 0x10000, 0x10280
+ wc_3d.struct_2.aux_fb_ptr = 0x1500006000
+ wc_3d.struct_2.unk_108 = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0]
+ wc_3d.struct_2.pipeline_base = 0x1100000000
+ wc_3d.struct_2.unk_140 = 0x8c60
+ wc_3d.struct_2.unk_148 = 0x0
+ wc_3d.struct_2.unk_150 = 0x0
+ wc_3d.struct_2.unk_158 = 0x1c
+ wc_3d.struct_2.unk_160_padding = bytes(0x1e8)
+
+ if True:
+ wc_3d.struct_6 = Start3DStruct6()
+ wc_3d.struct_6.unk_0 = 0x0
+ wc_3d.struct_6.unk_8 = 0x0
+ wc_3d.struct_6.unk_10 = 0x0
+ wc_3d.struct_6.encoder_id = encoder_id
+ wc_3d.struct_6.unk_1c = 0xffffffff
+ wc_3d.struct_6.unknown_buffer = 0x150000e000
+ wc_3d.struct_6.unk_28 = 0x0
+ wc_3d.struct_6.unk_30 = 0x1
+ wc_3d.struct_6.unk_34 = 0x1
+
+ if True:
+ wc_3d.struct_7 = Start3DStruct7()
+ wc_3d.struct_7.unk_0 = 0x0
+ wc_3d.struct_7.stamp1 = stamp_3d1
+ wc_3d.struct_7.stamp2 = stamp_3d2
+ wc_3d.struct_7.stamp_value = stamp_value
+ wc_3d.struct_7.ev_3d = ev_3d
+ wc_3d.struct_7.unk_20 = 0x0
+ wc_3d.struct_7.unk_24 = 0x0 # check
+ wc_3d.struct_7.uuid = uuid_3d
+ wc_3d.struct_7.prev_stamp_value = 0x0
+ wc_3d.struct_7.unk_30 = 0x0
+
+ wc_3d.set_addr() # Update inner structure addresses
+ print("WC3D", hex(wc_3d._addr))
+ print(" s1", hex(wc_3d.struct_1._addr))
+ print(" s2", hex(wc_3d.struct_2._addr))
+ print(" s6", hex(wc_3d.struct_6._addr))
+ print(" s7", hex(wc_3d.struct_7._addr))
+
+ ms = GPUMicroSequence(agx)
+
+ start_3d = Start3DCmd()
+ start_3d.struct1 = wc_3d.struct_1
+ start_3d.struct2 = wc_3d.struct_2
+ start_3d.buf_thing = buf_desc
+ start_3d.unkptr_1c = agx.initdata.regionB.unkptr_178 + 8
+ start_3d.unkptr_24 = wc_3d.unk_word._addr
+ start_3d.struct6 = wc_3d.struct_6
+ start_3d.struct7 = wc_3d.struct_7
+ start_3d.cmdqueue_ptr = wq_3d.info._addr
+ start_3d.workitem_ptr = wc_3d._addr
+ start_3d.context_id = ctx_id
+ start_3d.unk_50 = 0x1
+ start_3d.unk_54 = 0x0
+ start_3d.unk_58 = 0x2
+ start_3d.unk_5c = 0x0
+ start_3d.prev_stamp_value = 0x0
+ start_3d.unk_68 = 0x0
+ start_3d.unk_buf_ptr = wc_3d.unk_buf._addr
+ start_3d.unk_buf2_ptr = wc_3d.unk_buf2._addr
+ start_3d.unk_7c = 0x0
+ start_3d.unk_80 = 0x0
+ start_3d.unk_84 = 0x0
+ start_3d.uuid = uuid_3d
+ start_3d.attachments = [
+ Attachment(color._addr, 0x2800, 0x10017),
+ Attachment(depth._addr, 0x4100, 0x10017),
+ ] + [Attachment(0, 0, 0)] * 14
+ start_3d.num_attachments = 2
+ start_3d.unk_190 = 0x0
+
+ ms.append(start_3d)
+
+ ts1 = TimestampCmd()
+ ts1.unk_1 = 0x0
+ ts1.unk_2 = 0x0
+ ts1.unk_3 = 0x80
+ ts1.ts0_addr = wc_3d.ts1._addr
+ ts1.ts1_addr = wc_3d.ts2._addr
+ ts1.ts2_addr = wc_3d.ts2._addr
+ ts1.cmdqueue_ptr = wq_3d.info._addr
+ ts1.unk_24 = 0x0
+ ts1.uuid = uuid_3d
+ ts1.unk_30_padding = 0x0
+ ms.append(ts1)
+
+ ms.append(WaitForInterruptCmd(0, 1, 0))
+
+ ts2 = TimestampCmd()
+ ts2.unk_1 = 0x0
+ ts2.unk_2 = 0x0
+ ts2.unk_3 = 0x0
+ ts2.ts0_addr = wc_3d.ts1._addr
+ ts2.ts1_addr = wc_3d.ts2._addr
+ ts2.ts2_addr = wc_3d.ts3._addr
+ ts2.cmdqueue_ptr = wq_3d.info._addr
+ ts2.unk_24 = 0x0
+ ts2.uuid = uuid_3d
+ ts2.unk_30_padding = 0x0
+ ms.append(ts2)
+
+ finish_3d = Finalize3DCmd()
+ finish_3d.uuid = uuid_3d
+ finish_3d.unk_8 = 0
+ finish_3d.stamp = stamp_3d2
+ finish_3d.stamp_value = stamp_value
+ finish_3d.unk_18 = 0
+ finish_3d.buf_thing = buf_desc
+ finish_3d.buffer_mgr = buffer_mgr.info
+ finish_3d.unk_2c = 1
+ finish_3d.unkptr_34 = agx.initdata.regionB.unkptr_178 + 8
+ finish_3d.struct7 = wc_3d.struct_7
+ finish_3d.unkptr_44 = wc_3d.unk_word._addr
+ finish_3d.cmdqueue_ptr = wq_3d.info._addr
+ finish_3d.workitem_ptr = wc_3d._addr
+ finish_3d.unk_5c = ctx_id
+ finish_3d.unk_buf_ptr = wc_3d.unk_buf._addr
+ finish_3d.unk_6c = 0
+ finish_3d.unk_74 = 0
+ finish_3d.unk_7c = 0
+ finish_3d.unk_84 = 0
+ finish_3d.unk_8c = 0
+ finish_3d.startcmd_offset = -0x200
+ finish_3d.unk_98 = 1
+ ms.append(finish_3d)
+ ms.finalize()
+
+ wc_3d.microsequence_ptr = ms.obj._addr
+ wc_3d.microsequence_size = ms.size
+
+ print(wc_3d)
+
+ wc_3d.push()
+ ms.dump()
+ print(wc_3d)
+ wq_3d.submit(wc_3d)
+
+ ##### TA init
+
+ #print(ctx_info)
+
+ wc_initbm = agx.kobj.new(WorkCommandInitBM)
+ wc_initbm.context_id = ctx_id
+ wc_initbm.unk_8 = buffer_mgr_slot
+ wc_initbm.unk_c = 0
+ wc_initbm.unk_10 = buffer_mgr.info.block_count
+ wc_initbm.buffer_mgr = buffer_mgr.info
+ wc_initbm.stamp_value = stamp_value
+ wc_initbm.push()
+
+ print(wc_initbm)
+ wq_ta.submit(wc_initbm)
+
+ ##### TA execution
+
+ wc_ta = agx.kobj.new(WorkCommandTA)
+ wc_ta.context_id = ctx_id
+ wc_ta.unk_8 = 0
+ wc_ta.event_control = event_control
+ wc_ta.unk_14 = buffer_mgr_slot
+ wc_ta.buffer_mgr = buffer_mgr.info
+ wc_ta.buf_thing = buf_desc
+ wc_ta.unk_emptybuf_addr = wc_3d.unk_emptybuf_addr
+ wc_ta.unk_34 = 0x0
+
+ wc_ta.unk_154 = bytes(0x268)
+ wc_ta.unk_3e8 = bytes(0x74)
+ wc_ta.unk_594 = WorkCommand0_UnkBuf()
+
+ wc_ta.ts1 = Timestamp()
+ wc_ta.ts2 = Timestamp()
+ wc_ta.ts3 = Timestamp()
+ wc_ta.unk_5c4 = 0
+ wc_ta.unk_5c8 = 0
+ wc_ta.unk_5cc = 0
+ wc_ta.unk_5d0 = 0
+ wc_ta.unk_5d4 = 0x27 #1
+
+ # Structures embedded in WorkCommandTA
+ if True:
+
+ wc_ta.tiling_params = tiling_params
+ #wc_ta.tiling_params.unk_0 = 0x28
+ #wc_ta.tiling_params.unk_4 = 0x88
+ #wc_ta.tiling_params.unk_8 = 0x202
+ #wc_ta.tiling_params.x_max = 639
+ #wc_ta.tiling_params.y_max = 479
+ #wc_ta.tiling_params.unk_10 = 0xe013
+ #wc_ta.tiling_params.unk_14 = 0x20_20_18
+ #wc_ta.tiling_params.unk_18 = 0x10_10_0c
+ #wc_ta.tiling_params.unk_1c = 0x20
+ #wc_ta.tiling_params.unk_20 = 0x40
+ #wc_ta.tiling_params.unk_24 = 0x100
+ #wc_ta.tiling_params.unk_28 = 0x8000
+
+ if True:
+ wc_ta.struct_2 = StartTACmdStruct2()
+ wc_ta.struct_2.unk_0 = 0x200
+ wc_ta.struct_2.unk_8 = 0x1e3ce508 # fixed
+ wc_ta.struct_2.unk_c = 0x1e3ce508 # fixed
+ wc_ta.struct_2.tvb_tilemap = tvb_tilemap._addr
+ wc_ta.struct_2.unkptr_18 = 0x0
+ wc_ta.struct_2.unkptr_20 = tvb_something._addr
+ wc_ta.struct_2.tvb_heapmeta_addr = tvb_heapmeta._addr | 0x8000000000000000
+ wc_ta.struct_2.iogpu_unk_54 = 0x6b0003 # fixed
+ wc_ta.struct_2.iogpu_unk_55 = 0x3a0012 # fixed
+ wc_ta.struct_2.iogpu_unk_56 = 0x1 # fixed
+ wc_ta.struct_2.unk_40 = 0x0 # fixed
+ wc_ta.struct_2.unk_48 = 0xa000 # fixed
+ wc_ta.struct_2.unk_50 = 0x88 # fixed
+ wc_ta.struct_2.tvb_heapmeta_addr2 = tvb_heapmeta._addr
+ wc_ta.struct_2.unk_60 = 0x0 # fixed
+ wc_ta.struct_2.unk_68 = 0x0 # fixed
+ wc_ta.struct_2.iogpu_deflake_1 = 0x15000052a0
+ wc_ta.struct_2.iogpu_deflake_2 = 0x1500005020
+ wc_ta.struct_2.unk_80 = 0x1 # fixed
+ wc_ta.struct_2.iogpu_deflake_3 = 0x1500005000
+ wc_ta.struct_2.encoder_addr = 0x1500048000
+ wc_ta.struct_2.unk_98 = [0x0, 0x0] # fixed
+ wc_ta.struct_2.unk_a8 = 0xa041 # fixed
+ wc_ta.struct_2.unk_b0 = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0] # fixed
+ wc_ta.struct_2.pipeline_base = 0x1100000000
+ wc_ta.struct_2.unk_e8 = 0x0 # fixed
+ wc_ta.struct_2.unk_f0 = 0x1c # fixed
+ wc_ta.struct_2.unk_f8 = 0x8c60 # fixed
+ wc_ta.struct_2.unk_100 = [0x0, 0x0, 0x0] # fixed
+ wc_ta.struct_2.unk_118 = 0x1c # fixed
+
+ if True:
+ wc_ta.struct_3 = StartTACmdStruct3()
+ wc_ta.struct_3.unk_480 = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0] # fixed
+ wc_ta.struct_3.unk_498 = 0x0 # fixed
+ wc_ta.struct_3.unk_4a0 = 0x0 # fixed
+ wc_ta.struct_3.iogpu_deflake_1 = 0x15000052a0
+ wc_ta.struct_3.unk_4ac = 0x0 # fixed
+ wc_ta.struct_3.unk_4b0 = 0x0 # fixed
+ wc_ta.struct_3.unk_4b8 = 0x0 # fixed
+ wc_ta.struct_3.unk_4bc = 0x0 # fixed
+ wc_ta.struct_3.unk_4c4_padding = bytes(0x48)
+ wc_ta.struct_3.unk_50c = 0x0 # fixed
+ wc_ta.struct_3.unk_510 = 0x0 # fixed
+ wc_ta.struct_3.unk_518 = 0x0 # fixed
+ wc_ta.struct_3.unk_520 = 0x0 # fixed
+ wc_ta.struct_3.unk_528 = 0x0 # fixed
+ wc_ta.struct_3.unk_52c = 0x0 # fixed
+ wc_ta.struct_3.unk_530 = 0x0 # fixed
+ wc_ta.struct_3.encoder_id = encoder_id
+ wc_ta.struct_3.unk_538 = 0x0 # fixed
+ wc_ta.struct_3.unk_53c = 0xffffffff
+ wc_ta.struct_3.unknown_buffer = wc_3d.struct_6.unknown_buffer
+ wc_ta.struct_3.unk_548 = 0x0 # fixed
+ wc_ta.struct_3.unk_550 = [
+ 0x0, 0x0, # fixed
+ 0x0, # 1 for boot stuff?
+ 0x0, 0x0, 0x0] # fixed
+ wc_ta.struct_3.stamp1 = stamp_ta1
+ wc_ta.struct_3.stamp2 = stamp_ta2
+ wc_ta.struct_3.stamp_value = stamp_value
+ wc_ta.struct_3.ev_ta = ev_ta
+ wc_ta.struct_3.unk_580 = 0x0 # fixed
+ wc_ta.struct_3.unk_584 = 0x0 # 1 for boot stuff?
+ wc_ta.struct_3.uuid2 = uuid_ta
+ #wc_ta.struct_3.unk_58c = [0x0, 0x0]
+ wc_ta.struct_3.unk_58c = [0x1, 0x0]
+
+ wc_ta.set_addr() # Update inner structure addresses
+ #print("wc_ta", wc_ta)
+
+ ms = GPUMicroSequence(agx)
+
+ start_ta = StartTACmd()
+ start_ta.tiling_params = wc_ta.tiling_params
+ start_ta.struct2 = wc_ta.struct_2
+ start_ta.buffer_mgr = buffer_mgr.info
+ start_ta.buf_thing = buf_desc
+ start_ta.unkptr_24 = agx.initdata.regionB.unkptr_170 + 4
+ start_ta.cmdqueue_ptr = wq_ta.info._addr
+ start_ta.context_id = ctx_id
+ start_ta.unk_38 = 1
+ start_ta.unk_3c = 1 #0
+ start_ta.unk_40 = buffer_mgr_slot
+ start_ta.unk_48 = 1 #0
+ start_ta.unk_50 = 0
+ start_ta.struct3 = wc_ta.struct_3
+
+ start_ta.unkptr_5c = wc_ta.unk_594._addr
+ start_ta.unk_64 = 0x0 # fixed
+ start_ta.uuid = uuid_ta
+ start_ta.unk_70 = 0x0 # fixed
+ start_ta.unk_74 = [ # fixed
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ]
+ start_ta.unk_15c = 0x0 # fixed
+ start_ta.unk_160 = 0x0 # fixed
+ start_ta.unk_168 = 0x0 # fixed
+ start_ta.unk_16c = 0x0 # fixed
+ start_ta.unk_170 = 0x0 # fixed
+ start_ta.unk_178 = 0x0 # fixed
+ ms.append(start_ta)
+
+ ts1 = TimestampCmd()
+ ts1.unk_1 = 0x0
+ ts1.unk_2 = 0x0
+ ts1.unk_3 = 0x80
+ ts1.ts0_addr = wc_ta.ts1._addr
+ ts1.ts1_addr = wc_ta.ts2._addr
+ ts1.ts2_addr = wc_ta.ts2._addr
+ ts1.cmdqueue_ptr = wq_ta.info._addr
+ ts1.unk_24 = 0x0
+ ts1.uuid = uuid_ta
+ ts1.unk_30_padding = 0x0
+ ms.append(ts1)
+
+ ms.append(WaitForInterruptCmd(1, 0, 0))
+
+ ts2 = TimestampCmd()
+ ts2.unk_1 = 0x0
+ ts2.unk_2 = 0x0
+ ts2.unk_3 = 0x0
+ ts2.ts0_addr = wc_ta.ts1._addr
+ ts2.ts1_addr = wc_ta.ts2._addr
+ ts2.ts2_addr = wc_ta.ts3._addr
+ ts2.cmdqueue_ptr = wq_ta.info._addr
+ ts2.unk_24 = 0x0
+ ts2.uuid = uuid_ta
+ ts2.unk_30_padding = 0x0
+ ms.append(ts2)
+
+ finish_ta = FinalizeTACmd()
+ finish_ta.buf_thing = buf_desc
+ finish_ta.buffer_mgr = buffer_mgr.info
+ finish_ta.unkptr_14 = agx.initdata.regionB.unkptr_170 + 4
+ finish_ta.cmdqueue_ptr = wq_ta.info._addr
+ finish_ta.context_id = ctx_id
+ finish_ta.unk_28 = 0x0 # fixed
+ finish_ta.struct3 = wc_ta.struct_3
+ finish_ta.unk_34 = 0x0 # fixed
+ finish_ta.uuid = uuid_ta
+ finish_ta.stamp = stamp_ta2
+ finish_ta.stamp_value = stamp_value
+ finish_ta.unk_48 = 0x0 # fixed
+ finish_ta.unk_50 = 0x0 # fixed
+ finish_ta.unk_54 = 0x0 # fixed
+ finish_ta.unk_58 = 0x0 # fixed
+ finish_ta.unk_60 = 0x0 # fixed
+ finish_ta.unk_64 = 0x0 # fixed
+ finish_ta.unk_68 = 0x0 # fixed
+ finish_ta.startcmd_offset = -0x1e8 # fixed
+ finish_ta.unk_70 = 0x0 # fixed
+ ms.append(finish_ta)
+
+ ms.finalize()
+
+ wc_ta.unkptr_45c = tvb_something._addr
+ wc_ta.tvb_size = tvb_something_size
+ wc_ta.microsequence_ptr = ms.obj._addr
+ wc_ta.microsequence_size = ms.size
+ wc_ta.ev_3d = ev_3d
+ wc_ta.stamp_value = stamp_value
+
+ wc_ta.push()
+ ms.dump()
+
+ mon.poll()
+
+ print(wc_ta)
+ wq_ta.submit(wc_ta)
+
+ ##### Run queues
+ agx.ch.queue[2].q_3D.run(wq_3d, ev_3d)
+ agx.ch.queue[2].q_TA.run(wq_ta, ev_ta)
+
+ ##### Wait for work
+ agx.asc.work_for(0.3)
+ print("3D:")
+ print(wq_3d.info.pull())
+ print("TA:")
+ print(wq_ta.info.pull())
+ print("Barriers:")
+ print(stamp_ta1.pull())
+ print(stamp_ta2.pull())
+ print(stamp_3d1.pull())
+ print(stamp_3d2.pull())
+
+ event_control.pull()
+ print(event_control)
+
+ print("==")
+ mon.poll()
+ print("==")
+ #agx.kick_firmware()
+ agx.asc.work_for(0.3)
+ p.read32(0x204000000 + 0xd14000)
+ # [cpu0] [0xfffffe00124bf9a8] MMIO: W.4 0x204d14000 (sgx, offset 0xd14000) = 0x70001
+ p.write32(0x204000000 + 0xd14000, 0x70001)
+
+ #agx.uat.dump(ctx_id)
+
+ fault_code = p.read64(0x204017030)
+ fault_addr = fault_code >> 24
+ if fault_addr & 0x8000000000:
+ fault_addr |= 0xffffff8000000000
+ print(f"FAULT CODE: {fault_code:#x}")
+ base, obj = agx.find_object(fault_addr)
+ if obj is not None:
+ print(f"Faulted at : {fault_addr:#x}: {obj!s} + {fault_addr - base:#x}")
+ #agx.kick_firmware()
+ mon.poll()
+
+ #print(buffer_mgr.info.pull())
+ #print(buffer_mgr.counter_obj.pull())
+ #print(buffer_mgr.misc_obj.pull())
+ #print(buffer_mgr.block_ctl_obj.pull())
+
+ width = 800
+ height = 600
+
+ unswizzle(color._paddr, width, height, 4, "fb.bin", grid=True)
+ unswizzle(depth._paddr, width, height, 4, "depth.bin", grid=True)
+
+ p.fb_blit(0, 0, width, height, color._paddr, width)
+
+ print("TVB something:")
+ chexdump(iface.readmem(tvb_something._paddr, tvb_something._size), stride=16, abbreviate=False)
+
+ print("TVB list:")
+ chexdump(iface.readmem(tvb_tilemap._paddr, tvb_tilemap._size), stride=5, abbreviate=False)
+
+ print("Tile params:")
+ print(f"X: {tiles_x} ({tile_blocks_x})")
+ print(f"Y: {tiles_y} ({tile_blocks_y})")
+ print(f"Total: {tiles} ({tile_blocks})")
+
+ agx.stop()
+except:
+ #agx.uat.dump(ctx_id)
+ p.reboot()
+ raise
+ #agx.stop()
+
+#time.sleep(10)
+#p.reboot()
diff --git a/tools/proxyclient/experiments/agx_boot.py b/tools/proxyclient/experiments/agx_boot.py
new file mode 100755
index 0000000..b5d668c
--- /dev/null
+++ b/tools/proxyclient/experiments/agx_boot.py
@@ -0,0 +1,27 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+from m1n1.constructutils import Ver
+from m1n1.utils import *
+
+Ver.set_version(u)
+
+from m1n1.shell import run_shell
+
+from m1n1.fw.agx import Agx
+
+p.pmgr_adt_clocks_enable("/arm-io/gfx-asc")
+p.pmgr_adt_clocks_enable("/arm-io/sgx")
+
+agx = Agx(u)
+agx.verbose = 10
+
+#agx.uat.dump(0)
+
+agx.build_initdata()
+
+run_shell(globals(), msg="Have fun!")
diff --git a/tools/proxyclient/experiments/agx_cancel.py b/tools/proxyclient/experiments/agx_cancel.py
new file mode 100644
index 0000000..af0881d
--- /dev/null
+++ b/tools/proxyclient/experiments/agx_cancel.py
@@ -0,0 +1,246 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+
+import sys, pathlib, time
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+import atexit, sys
+
+from m1n1.setup import *
+from m1n1.constructutils import Ver
+from m1n1.utils import *
+
+Ver.set_version(u)
+
+from m1n1.agx import AGX
+from m1n1.agx.render import *
+
+from m1n1 import asm
+
+from m1n1.gpiola import GPIOLogicAnalyzer
+
+analyzer_cpu = 1
+
+p.pmgr_adt_clocks_enable("/arm-io/gfx-asc")
+p.pmgr_adt_clocks_enable("/arm-io/sgx")
+p.smp_start_secondaries()
+p.mmu_init_secondary(analyzer_cpu)
+iface.dev.timeout = 42
+
+agx = AGX(u)
+
+mon = RegMonitor(u, ascii=True, bufsize=0x8000000)
+agx.mon = mon
+
+sgx = agx.sgx_dev
+
+atexit.register(p.reboot)
+agx.start()
+
+mon.poll()
+agx.poll_objects()
+
+ctx = GPUContext(agx)
+ctx.bind(63)
+
+f = GPUFrame(ctx, sys.argv[1], track=False)
+
+r = GPURenderer(ctx, 128, bm_slot=0x10, queue=1)
+
+dep_stamp = agx.kobj.new(StampCounter, name="Dep stamp")
+dep_stamp.value = 0x100
+dep_stamp.push()
+
+#r.submit(f.cmdbuf, (dep_stamp._addr, 0x200, 0x10))
+r.submit(f.cmdbuf)
+r.submit(f.cmdbuf)
+
+def t(addr):
+ paddr = agx.uat.iotranslate(0, addr, 4)[0][0]
+ if paddr is None:
+ raise Exception(f"Failed to iotranslate {addr:#x}")
+ return paddr
+
+regs = {
+ "ta_cmds": t(agx.initdata.regionB.stats_ta.addrof("total_cmds")),
+ "ta_ts": t(agx.initdata.regionB.stats_ta.stats.addrof("unk_timestamp")),
+}
+
+pend_base = agx.initdata.regionC.addrof("pending_stamps")
+for i in range(5):
+ regs[f"st{i}_info"] = t(pend_base + i*8)
+ regs[f"st{i}_val"] = t(pend_base + i*8 + 4)
+
+for i in range(4):
+ regs[f"ta{i}_cq"] = t(agx.initdata.regionB.stats_ta.stats.queues[i].addrof("cur_cmdqueue"))
+
+regs.update({
+ #"pwr_status": t(agx.initdata.regionB.hwdata_a.addrof("pwr_status")),
+ #"pstate": t(agx.initdata.regionB.hwdata_a.addrof("cur_pstate")),
+ #"temp_c": t(agx.initdata.regionB.hwdata_a.addrof("temp_c")),
+ #"pwr_mw": t(agx.initdata.regionB.hwdata_a.addrof("avg_power_mw")),
+ #"pwr_ts": t(agx.initdata.regionB.hwdata_a.addrof("update_ts")),
+
+ #"unk_10": t(agx.initdata.regionB.hwdata_a.addrof("unk_10")),
+ #"unk_14": t(agx.initdata.regionB.hwdata_a.addrof("unk_14")),
+ #"actual_pstate": t(agx.initdata.regionB.hwdata_a.addrof("actual_pstate")),
+ #"tgt_pstate": t(agx.initdata.regionB.hwdata_a.addrof("tgt_pstate")),
+ #"unk_40": t(agx.initdata.regionB.hwdata_a.addrof("unk_40")),
+ #"unk_44": t(agx.initdata.regionB.hwdata_a.addrof("unk_44")),
+ #"unk_48": t(agx.initdata.regionB.hwdata_a.addrof("unk_48")),
+ #"freq_mhz": t(agx.initdata.regionB.hwdata_a.addrof("freq_mhz")),
+
+ #"unk_748.0": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")),
+ #"unk_748.1": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")+4),
+ #"unk_748.2": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")+8),
+ #"unk_748.3": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")+12),
+ #"use_percent": t(agx.initdata.regionB.hwdata_a.addrof("use_percent")),
+ #"unk_83c": t(agx.initdata.regionB.hwdata_a.addrof("unk_83c")),
+ #"freq_with_off": t(agx.initdata.regionB.hwdata_a.addrof("freq_with_off")),
+ #"unk_ba0": t(agx.initdata.regionB.hwdata_a.addrof("unk_ba0")),
+ #"unk_bb0": t(agx.initdata.regionB.hwdata_a.addrof("unk_bb0")),
+ #"unk_c44": t(agx.initdata.regionB.hwdata_a.addrof("unk_c44")),
+ #"unk_c58": t(agx.initdata.regionB.hwdata_a.addrof("unk_c58")),
+
+ #"unk_3ca0": t(agx.initdata.regionB.hwdata_a.addrof("unk_3ca0")),
+ #"unk_3ca8": t(agx.initdata.regionB.hwdata_a.addrof("unk_3ca8")),
+ #"unk_3cb0": t(agx.initdata.regionB.hwdata_a.addrof("unk_3cb0")),
+ #"ts_last_idle": t(agx.initdata.regionB.hwdata_a.addrof("ts_last_idle")),
+ #"ts_last_poweron": t(agx.initdata.regionB.hwdata_a.addrof("ts_last_poweron")),
+ #"ts_last_poweroff": t(agx.initdata.regionB.hwdata_a.addrof("ts_last_poweroff")),
+ #"unk_3cd0": t(agx.initdata.regionB.hwdata_a.addrof("unk_3cd0")),
+
+ "halt_count": t(agx.initdata.fw_status.addrof("halt_count")),
+ "halted": t(agx.initdata.fw_status.addrof("halted")),
+ "resume": t(agx.initdata.fw_status.addrof("resume")),
+ "unk_40": t(agx.initdata.fw_status.addrof("unk_40")),
+ "unk_ctr": t(agx.initdata.fw_status.addrof("unk_ctr")),
+ "unk_60": t(agx.initdata.fw_status.addrof("unk_60")),
+ "unk_70": t(agx.initdata.fw_status.addrof("unk_70")),
+ "c_118c0": t(agx.initdata.regionC._addr + 0x118c0),
+ "c_118c4": t(agx.initdata.regionC._addr + 0x118c4),
+ "c_118c8": t(agx.initdata.regionC._addr + 0x118c8),
+ "c_118cc": t(agx.initdata.regionC._addr + 0x118cc),
+ "c_118d0": t(agx.initdata.regionC._addr + 0x118d0),
+ "c_118d4": t(agx.initdata.regionC._addr + 0x118d4),
+ "c_118d8": t(agx.initdata.regionC._addr + 0x118d8),
+ "c_118dc": t(agx.initdata.regionC._addr + 0x118dc),
+ "3d_cmds": t(agx.initdata.regionB.stats_3d.addrof("total_cmds")),
+ #"3d_tvb_oflws_1": t(agx.initdata.regionB.stats_3d.stats.addrof("tvb_overflows_1")),
+ #"3d_tvb_oflws_2": t(agx.initdata.regionB.stats_3d.stats.addrof("tvb_overflows_2")),
+ "3d_cur_stamp_id": t(agx.initdata.regionB.stats_3d.stats.addrof("cur_stamp_id")),
+ "3d_ts": t(agx.initdata.regionB.stats_3d.stats.addrof("unk_timestamp")),
+ #"3d_cur_stamp_id": t(agx.initdata.regionB.stats_3d.stats.addrof("cur_stamp_id")),
+})
+
+for i in range(4):
+ regs[f"3d{i}_cq"] = t(agx.initdata.regionB.stats_3d.stats.queues[i].addrof("cur_cmdqueue"))
+
+
+i = 0
+regs.update({
+ f"r{i}_3d_done": t(r.wq_3d.info.pointers.addrof("gpu_doneptr")),
+ #f"r{i}_3d_rptr": t(r.wq_3d.info.pointers.addrof("gpu_rptr")),
+ f"r{i}_3d_busy": t(r.wq_3d.info.addrof("busy")),
+ #f"r{i}_3d_blk": t(r.wq_3d.info.addrof("blocked_on_barrier")),
+ #f"r{i}_3d_2c": t(r.wq_3d.info.addrof("unk_2c")),
+ #f"r{i}_3d_54": t(r.wq_3d.info.addrof("unk_54")),
+
+ f"r{i}_ta_done": t(r.wq_ta.info.pointers.addrof("gpu_doneptr")),
+ #f"r{i}_ta_rptr": t(r.wq_ta.info.pointers.addrof("gpu_rptr")),
+ f"r{i}_ta_busy": t(r.wq_ta.info.addrof("busy")),
+ #f"r{i}_ta_blk": t(r.wq_ta.info.addrof("blocked_on_barrier")),
+ #f"r{i}_ta_2c": t(r.wq_ta.info.addrof("unk_2c")),
+ #f"r{i}_ta_54": t(r.wq_ta.info.addrof("unk_54")),
+ f"r{i}_ta_stamp1": t(r.stamp_ta1._addr),
+ f"r{i}_ta_stamp2":t(r.stamp_ta2._addr),
+ f"r{i}_3d_stamp1": t(r.stamp_3d1._addr),
+ f"r{i}_3d_stamp2":t(r.stamp_3d2._addr),
+
+ f"r{i}_ev_cnt":t(r.event_control.event_count._addr),
+ f"r{i}_ev_cur":t(r.event_control.addrof("cur_count")),
+ f"r{i}_ev_10":t(r.event_control.addrof("unk_10")),
+})
+
+div=4
+ticks = 24000000 // div * 25
+
+la = GPIOLogicAnalyzer(u, regs=regs, cpu=analyzer_cpu, div=div)
+
+print("Queues:")
+print(f" TA: {r.wq_ta.info._addr:#x} (stamp {r.work[0].ev_ta.id})")
+#print(r.wq_ta.info)
+print(f" 3D: {r.wq_3d.info._addr:#x} (stamp {r.work[0].ev_3d.id})")
+#print(r.wq_3d.info)
+
+print("==========================================")
+print("## Run")
+print("==========================================")
+
+la.start(ticks, bufsize=0x8000000)
+
+t = time.time()
+
+buf = agx.kobj.new_buf(0x1000, "foo")
+buf.val = b"A" * 0x1000
+buf.push()
+agx.uat.flush_dirty()
+
+try:
+ r.run()
+
+ #for a in range(8):
+ #for b in range(8):
+ #agx.ch.devctrl.dc_1e(a, b)
+
+ #agx.uat.flush_dirty()
+ #agx.ch.devctrl.write32(dep_stamp._addr, 0x200)
+
+ #data = struct.pack("<QQQQQI", 0xaaaa, buf._addr, 0xbbbb, 0, 0, 0)
+
+
+ #agx.poll_objects()
+ #mon.poll()
+ #agx.ch.devctrl.send_foo(9, data)
+ #agx.ch.devctrl.dc_09(0xaaaa, buf._addr, 0xbbbb)
+
+ #for i in range(0x28, 0xff):
+ #print(hex(i))
+ #data = struct.pack("<QQQQQI", dep_stamp._addr, 0x10_00000200, buf._addr, 0x12_00000010, 0x13_00000010, 0x10)
+ #agx.ch.devctrl.send_foo(i, data)
+ #agx.asc.work()
+ #time.sleep(0.1)
+ #agx.poll_objects()
+ #mon.poll()
+
+ #time.sleep(0.1)
+ #agx.asc.work()
+
+ #chexdump(buf.pull().val)
+
+ agx.kick_firmware()
+
+ while not r.ev_3d.fired:
+ agx.asc.work()
+ agx.poll_channels()
+ print("==========================================")
+ #agx.poll_objects()
+ #mon.poll()
+ agx.kick_firmware()
+ if time.time() > t + 2:
+ raise Exception("Timeout")
+ r.wait()
+
+finally:
+
+ dep_stamp.pull()
+ print(f"Stamp value: {dep_stamp.value:#x}")
+
+ #agx.poll_objects()
+ #mon.poll()
+
+ la.complete()
+ la.show()
+
+time.sleep(2)
+
diff --git a/tools/proxyclient/experiments/agx_deps.py b/tools/proxyclient/experiments/agx_deps.py
new file mode 100644
index 0000000..9a7c196
--- /dev/null
+++ b/tools/proxyclient/experiments/agx_deps.py
@@ -0,0 +1,242 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+
+import sys, pathlib, time
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+import atexit, sys
+
+from m1n1.setup import *
+from m1n1.constructutils import Ver
+from m1n1.utils import *
+
+Ver.set_version(u)
+
+from m1n1.agx import AGX
+from m1n1.agx.render import *
+
+from m1n1 import asm
+
+from m1n1.gpiola import GPIOLogicAnalyzer
+
+analyzer_cpu = 1
+
+p.pmgr_adt_clocks_enable("/arm-io/gfx-asc")
+p.pmgr_adt_clocks_enable("/arm-io/sgx")
+p.smp_start_secondaries()
+p.mmu_init_secondary(analyzer_cpu)
+iface.dev.timeout = 42
+
+agx = AGX(u)
+
+mon = RegMonitor(u, ascii=True, bufsize=0x8000000)
+agx.mon = mon
+
+sgx = agx.sgx_dev
+#mon.add(sgx.gpu_region_base, sgx.gpu_region_size, "contexts")
+#mon.add(sgx.gfx_shared_region_base, sgx.gfx_shared_region_size, "gfx-shared")
+#mon.add(sgx.gfx_handoff_base, sgx.gfx_handoff_size, "gfx-handoff")
+
+#mon.add(agx.initdasgx.gfx_handoff_base, sgx.gfx_handoff_size, "gfx-handoff")
+
+atexit.register(p.reboot)
+agx.start()
+
+print("==========================================")
+print("## After init")
+print("==========================================")
+mon.poll()
+agx.poll_objects()
+
+ctx = GPUContext(agx)
+ctx.bind(63)
+
+f = GPUFrame(ctx, sys.argv[1], track=False)
+
+RENDERERS = 4
+
+renderers = []
+
+for i in range(RENDERERS):
+ r = GPURenderer(ctx, 128, bm_slot=0x10 + i, queue=1)
+ renderers.append(r)
+
+ for q in (r.wq_3d, r.wq_ta):
+ q.info.set_prio(2)
+ q.info.push()
+
+print("==========================================")
+print("## Submitting")
+print("==========================================")
+
+w = renderers[3].submit(f.cmdbuf)
+w = renderers[0].submit(f.cmdbuf, w)
+w = renderers[2].submit(f.cmdbuf, w)
+w = renderers[1].submit(f.cmdbuf, w)
+w = renderers[3].submit(f.cmdbuf, w)
+w = renderers[3].submit(f.cmdbuf, w)
+w = renderers[1].submit(f.cmdbuf, w)
+w = renderers[1].submit(f.cmdbuf, w)
+w = renderers[0].submit(f.cmdbuf, w)
+
+for i, r in enumerate(renderers):
+ r.submit(f.cmdbuf)
+
+print("==========================================")
+print("## Submitted")
+print("==========================================")
+
+def t(addr):
+ paddr = agx.uat.iotranslate(0, addr, 4)[0][0]
+ if paddr is None:
+ raise Exception(f"Failed to iotranslate {addr:#x}")
+ return paddr
+
+regs = {
+ "ta_cmds": t(agx.initdata.regionB.stats_ta.addrof("total_cmds")),
+ "ta_ts": t(agx.initdata.regionB.stats_ta.stats.addrof("unk_timestamp")),
+}
+
+pend_base = agx.initdata.regionC.addrof("pending_stamps")
+for i in range(5):
+ regs[f"st{i}_info"] = t(pend_base + i*8)
+ regs[f"st{i}_val"] = t(pend_base + i*8 + 4)
+
+for i in range(4):
+ regs[f"ta{i}_cq"] = t(agx.initdata.regionB.stats_ta.stats.queues[i].addrof("cur_cmdqueue"))
+
+regs.update({
+ #"pwr_status": t(agx.initdata.regionB.hwdata_a.addrof("pwr_status")),
+ #"pstate": t(agx.initdata.regionB.hwdata_a.addrof("cur_pstate")),
+ #"temp_c": t(agx.initdata.regionB.hwdata_a.addrof("temp_c")),
+ #"pwr_mw": t(agx.initdata.regionB.hwdata_a.addrof("avg_power_mw")),
+ #"pwr_ts": t(agx.initdata.regionB.hwdata_a.addrof("update_ts")),
+
+ #"unk_10": t(agx.initdata.regionB.hwdata_a.addrof("unk_10")),
+ #"unk_14": t(agx.initdata.regionB.hwdata_a.addrof("unk_14")),
+ #"actual_pstate": t(agx.initdata.regionB.hwdata_a.addrof("actual_pstate")),
+ #"tgt_pstate": t(agx.initdata.regionB.hwdata_a.addrof("tgt_pstate")),
+ #"unk_40": t(agx.initdata.regionB.hwdata_a.addrof("unk_40")),
+ #"unk_44": t(agx.initdata.regionB.hwdata_a.addrof("unk_44")),
+ #"unk_48": t(agx.initdata.regionB.hwdata_a.addrof("unk_48")),
+ #"freq_mhz": t(agx.initdata.regionB.hwdata_a.addrof("freq_mhz")),
+
+ #"unk_748.0": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")),
+ #"unk_748.1": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")+4),
+ #"unk_748.2": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")+8),
+ #"unk_748.3": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")+12),
+ #"use_percent": t(agx.initdata.regionB.hwdata_a.addrof("use_percent")),
+ #"unk_83c": t(agx.initdata.regionB.hwdata_a.addrof("unk_83c")),
+ #"freq_with_off": t(agx.initdata.regionB.hwdata_a.addrof("freq_with_off")),
+ #"unk_ba0": t(agx.initdata.regionB.hwdata_a.addrof("unk_ba0")),
+ #"unk_bb0": t(agx.initdata.regionB.hwdata_a.addrof("unk_bb0")),
+ #"unk_c44": t(agx.initdata.regionB.hwdata_a.addrof("unk_c44")),
+ #"unk_c58": t(agx.initdata.regionB.hwdata_a.addrof("unk_c58")),
+
+ #"unk_3ca0": t(agx.initdata.regionB.hwdata_a.addrof("unk_3ca0")),
+ #"unk_3ca8": t(agx.initdata.regionB.hwdata_a.addrof("unk_3ca8")),
+ #"unk_3cb0": t(agx.initdata.regionB.hwdata_a.addrof("unk_3cb0")),
+ #"ts_last_idle": t(agx.initdata.regionB.hwdata_a.addrof("ts_last_idle")),
+ #"ts_last_poweron": t(agx.initdata.regionB.hwdata_a.addrof("ts_last_poweron")),
+ #"ts_last_poweroff": t(agx.initdata.regionB.hwdata_a.addrof("ts_last_poweroff")),
+ #"unk_3cd0": t(agx.initdata.regionB.hwdata_a.addrof("unk_3cd0")),
+
+ "halt_count": t(agx.initdata.fw_status.addrof("halt_count")),
+ "halted": t(agx.initdata.fw_status.addrof("halted")),
+ "resume": t(agx.initdata.fw_status.addrof("resume")),
+ "unk_40": t(agx.initdata.fw_status.addrof("unk_40")),
+ "unk_ctr": t(agx.initdata.fw_status.addrof("unk_ctr")),
+ "unk_60": t(agx.initdata.fw_status.addrof("unk_60")),
+ "unk_70": t(agx.initdata.fw_status.addrof("unk_70")),
+ "c_118c0": t(agx.initdata.regionC._addr + 0x118c0),
+ "c_118c4": t(agx.initdata.regionC._addr + 0x118c4),
+ "c_118c8": t(agx.initdata.regionC._addr + 0x118c8),
+ "c_118cc": t(agx.initdata.regionC._addr + 0x118cc),
+ "c_118d0": t(agx.initdata.regionC._addr + 0x118d0),
+ "c_118d4": t(agx.initdata.regionC._addr + 0x118d4),
+ "c_118d8": t(agx.initdata.regionC._addr + 0x118d8),
+ "c_118dc": t(agx.initdata.regionC._addr + 0x118dc),
+ "3d_cmds": t(agx.initdata.regionB.stats_3d.addrof("total_cmds")),
+ #"3d_tvb_oflws_1": t(agx.initdata.regionB.stats_3d.stats.addrof("tvb_overflows_1")),
+ #"3d_tvb_oflws_2": t(agx.initdata.regionB.stats_3d.stats.addrof("tvb_overflows_2")),
+ "3d_cur_stamp_id": t(agx.initdata.regionB.stats_3d.stats.addrof("cur_stamp_id")),
+ "3d_ts": t(agx.initdata.regionB.stats_3d.stats.addrof("unk_timestamp")),
+ #"3d_cur_stamp_id": t(agx.initdata.regionB.stats_3d.stats.addrof("cur_stamp_id")),
+})
+
+for i in range(4):
+ regs[f"3d{i}_cq"] = t(agx.initdata.regionB.stats_3d.stats.queues[i].addrof("cur_cmdqueue"))
+
+
+for i, r in enumerate(renderers):
+ regs.update({
+ f"r{i}_3d_done": t(r.wq_3d.info.pointers.addrof("gpu_doneptr")),
+ #f"r{i}_3d_rptr": t(r.wq_3d.info.pointers.addrof("gpu_rptr")),
+ f"r{i}_3d_busy": t(r.wq_3d.info.addrof("busy")),
+ #f"r{i}_3d_blk": t(r.wq_3d.info.addrof("blocked_on_barrier")),
+ #f"r{i}_3d_2c": t(r.wq_3d.info.addrof("unk_2c")),
+ #f"r{i}_3d_54": t(r.wq_3d.info.addrof("unk_54")),
+
+ f"r{i}_ta_done": t(r.wq_ta.info.pointers.addrof("gpu_doneptr")),
+ #f"r{i}_ta_rptr": t(r.wq_ta.info.pointers.addrof("gpu_rptr")),
+ f"r{i}_ta_busy": t(r.wq_ta.info.addrof("busy")),
+ #f"r{i}_ta_blk": t(r.wq_ta.info.addrof("blocked_on_barrier")),
+ #f"r{i}_ta_2c": t(r.wq_ta.info.addrof("unk_2c")),
+ #f"r{i}_ta_54": t(r.wq_ta.info.addrof("unk_54")),
+ f"r{i}_ta_stamp1": t(r.stamp_ta1._addr),
+ f"r{i}_ta_stamp2":t(r.stamp_ta2._addr),
+ f"r{i}_3d_stamp1": t(r.stamp_3d1._addr),
+ f"r{i}_3d_stamp2":t(r.stamp_3d2._addr),
+
+ f"r{i}_ev_cnt":t(r.event_control.event_count._addr),
+ f"r{i}_ev_cur":t(r.event_control.addrof("cur_count")),
+ f"r{i}_ev_10":t(r.event_control.addrof("unk_10")),
+ })
+
+div=4
+ticks = 24000000 // div * 25
+
+la = GPIOLogicAnalyzer(u, regs=regs, cpu=analyzer_cpu, div=div)
+
+print("Queues:")
+for i, r in enumerate(renderers):
+ print(f" Renderer {i}")
+ print(f" TA: {r.wq_ta.info._addr:#x} (stamp {r.work[0].ev_ta.id})")
+ #print(r.wq_ta.info)
+ print(f" 3D: {r.wq_3d.info._addr:#x} (stamp {r.work[0].ev_3d.id})")
+ #print(r.wq_3d.info)
+
+print("==========================================")
+print("## Run")
+print("==========================================")
+
+la.start(ticks, bufsize=0x8000000)
+
+t = time.time()
+
+try:
+ for r in renderers[:RENDERERS]:
+ r.run()
+
+ for r in renderers[:RENDERERS]:
+ while not r.ev_3d.fired:
+ agx.asc.work()
+ agx.poll_channels()
+ print("==========================================")
+ #agx.poll_objects()
+ #mon.poll()
+ agx.kick_firmware()
+ if time.time() > t + 10:
+ raise Exception("Timeout")
+
+ r.wait()
+
+finally:
+ #agx.poll_objects()
+ #mon.poll()
+
+ la.complete()
+ la.show()
+
+time.sleep(2)
+
diff --git a/tools/proxyclient/experiments/agx_dumpstructs.py b/tools/proxyclient/experiments/agx_dumpstructs.py
new file mode 100644
index 0000000..08a7327
--- /dev/null
+++ b/tools/proxyclient/experiments/agx_dumpstructs.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+
+import sys, pathlib, time
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.constructutils import *
+from m1n1.fw.agx import microsequence, initdata
+
+#for v in initdata.__all__:
+#for v in initdata.__dict__:
+def dump(module):
+ for v in module.__dict__:
+ struct = getattr(module, v)
+ if isinstance(struct, type) and issubclass(struct, ConstructClass) and struct is not ConstructClass:
+ print(struct.to_rust())
+ print()
+
+dump(microsequence)
diff --git a/tools/proxyclient/experiments/agx_parallel.py b/tools/proxyclient/experiments/agx_parallel.py
new file mode 100644
index 0000000..163497e
--- /dev/null
+++ b/tools/proxyclient/experiments/agx_parallel.py
@@ -0,0 +1,341 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+
+import sys, pathlib, time
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+import atexit, sys
+
+from m1n1.setup import *
+from m1n1.constructutils import Ver
+from m1n1.utils import *
+
+Ver.set_version(u)
+
+from m1n1.agx import AGX
+from m1n1.agx.render import *
+
+from m1n1 import asm
+
+from m1n1.gpiola import GPIOLogicAnalyzer
+
+analyzer_cpu = 1
+
+p.pmgr_adt_clocks_enable("/arm-io/gfx-asc")
+p.pmgr_adt_clocks_enable("/arm-io/sgx")
+p.smp_start_secondaries()
+p.mmu_init_secondary(analyzer_cpu)
+iface.dev.timeout = 42
+
+## heater code
+if True:
+ code = u.malloc(0x1000)
+
+ util = asm.ARMAsm("""
+ bench:
+ mrs x1, CNTPCT_EL0
+ 1:
+ sub x0, x0, #1
+ cbnz x0, 1b
+
+ mrs x2, CNTPCT_EL0
+ sub x0, x2, x1
+ ret
+ """, code)
+ iface.writemem(code, util.data)
+ p.dc_cvau(code, len(util.data))
+ p.ic_ivau(code, len(util.data))
+
+ LOOPS = 80000000000
+ for idx in range(2, 8):
+ print(f"bench {idx}")
+ p.smp_call(idx, util.bench, LOOPS)
+
+agx = AGX(u)
+
+mon = RegMonitor(u, ascii=True, bufsize=0x8000000)
+agx.mon = mon
+
+sgx = agx.sgx_dev
+#mon.add(sgx.gpu_region_base, sgx.gpu_region_size, "contexts")
+#mon.add(sgx.gfx_shared_region_base, sgx.gfx_shared_region_size, "gfx-shared")
+#mon.add(sgx.gfx_handoff_base, sgx.gfx_handoff_size, "gfx-handoff")
+
+#mon.add(agx.initdasgx.gfx_handoff_base, sgx.gfx_handoff_size, "gfx-handoff")
+
+atexit.register(p.reboot)
+agx.start()
+
+print("==========================================")
+print("## After init")
+print("==========================================")
+mon.poll()
+agx.poll_objects()
+
+ctx = GPUContext(agx)
+ctx.bind(63)
+ctx0 = GPUContext(agx)
+ctx0.bind(62)
+
+f = GPUFrame(ctx, sys.argv[1], track=False)
+f2 = GPUFrame(ctx0, sys.argv[1], track=False)
+
+RENDERERS = 4
+FRAMES = 8
+
+renderers = []
+
+fault_cmdbuf = f.cmdbuf.clone()
+#fault_cmdbuf.depth_buffer = 0xdeadb000
+
+for i in range(RENDERERS):
+ c = ctx0 if i == 0 else ctx
+ r = GPURenderer(c, 32, bm_slot=0x10 + i, queue=1)
+ renderers.append(r)
+
+ for q in (r.wq_3d, r.wq_ta):
+ q.info.set_prio(2)
+ q.info.push()
+
+#for r in renderers[2:4]:
+ #for q in (r.wq_3d, r.wq_ta):
+ #q.info.set_prio(3)
+ #q.info.push()
+
+#for r in renderers[4:6]:
+ #for q in (r.wq_3d, r.wq_ta):
+ #q.info.set_prio(0)
+ #q.info.push()
+
+#for r in renderers[6:8]:
+ #for q in (r.wq_3d, r.wq_ta):
+ #q.info.set_prio(1)
+ #q.info.push()
+
+print("==========================================")
+print("## Submitting")
+print("==========================================")
+
+for i, r in enumerate(renderers):
+ for j in range(FRAMES):
+ if (i, j) in ((1, 0), (2, 1), (3, 1)):
+ r.submit(fault_cmdbuf)
+ elif i == 0:
+ r.submit(f2.cmdbuf)
+ else:
+ r.submit(f.cmdbuf)
+
+print("==========================================")
+print("## Submitted")
+print("==========================================")
+
+def t(addr):
+ paddr = agx.uat.iotranslate(0, addr, 4)[0][0]
+ if paddr is None:
+ raise Exception(f"Failed to iotranslate {addr:#x}")
+ return paddr
+
+regs = {
+ "ta_cmds": t(agx.initdata.regionB.stats_ta.addrof("total_cmds")),
+ "ta_ts": t(agx.initdata.regionB.stats_ta.stats.addrof("unk_timestamp")),
+}
+
+pend_base = agx.initdata.regionC.addrof("pending_stamps")
+for i in range(5):
+ regs[f"st{i}_info"] = t(pend_base + i*8)
+ regs[f"st{i}_val"] = t(pend_base + i*8 + 4)
+
+for i in range(4):
+ regs[f"ta{i}_cq"] = t(agx.initdata.regionB.stats_ta.stats.queues[i].addrof("cur_cmdqueue"))
+
+regs.update({
+ "pwr_status": t(agx.initdata.regionB.hwdata_a.addrof("pwr_status")),
+ "pstate": t(agx.initdata.regionB.hwdata_a.addrof("cur_pstate")),
+ "temp_c": t(agx.initdata.regionB.hwdata_a.addrof("temp_c")),
+ "pwr_mw": t(agx.initdata.regionB.hwdata_a.addrof("avg_power_mw")),
+ "pwr_ts": t(agx.initdata.regionB.hwdata_a.addrof("update_ts")),
+
+ #"unk_10": t(agx.initdata.regionB.hwdata_a.addrof("unk_10")),
+ #"unk_14": t(agx.initdata.regionB.hwdata_a.addrof("unk_14")),
+ "actual_pstate": t(agx.initdata.regionB.hwdata_a.addrof("actual_pstate")),
+ "tgt_pstate": t(agx.initdata.regionB.hwdata_a.addrof("tgt_pstate")),
+ #"unk_40": t(agx.initdata.regionB.hwdata_a.addrof("unk_40")),
+ #"unk_44": t(agx.initdata.regionB.hwdata_a.addrof("unk_44")),
+ #"unk_48": t(agx.initdata.regionB.hwdata_a.addrof("unk_48")),
+ "freq_mhz": t(agx.initdata.regionB.hwdata_a.addrof("freq_mhz")),
+
+ #"unk_748.0": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")),
+ #"unk_748.1": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")+4),
+ #"unk_748.2": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")+8),
+ #"unk_748.3": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")+12),
+ #"use_percent": t(agx.initdata.regionB.hwdata_a.addrof("use_percent")),
+ #"unk_83c": t(agx.initdata.regionB.hwdata_a.addrof("unk_83c")),
+ "freq_with_off": t(agx.initdata.regionB.hwdata_a.addrof("freq_with_off")),
+ #"unk_ba0": t(agx.initdata.regionB.hwdata_a.addrof("unk_ba0")),
+ #"unk_bb0": t(agx.initdata.regionB.hwdata_a.addrof("unk_bb0")),
+ #"unk_c44": t(agx.initdata.regionB.hwdata_a.addrof("unk_c44")),
+ #"unk_c58": t(agx.initdata.regionB.hwdata_a.addrof("unk_c58")),
+
+ #"unk_3ca0": t(agx.initdata.regionB.hwdata_a.addrof("unk_3ca0")),
+ #"unk_3ca8": t(agx.initdata.regionB.hwdata_a.addrof("unk_3ca8")),
+ #"unk_3cb0": t(agx.initdata.regionB.hwdata_a.addrof("unk_3cb0")),
+ #"ts_last_idle": t(agx.initdata.regionB.hwdata_a.addrof("ts_last_idle")),
+ #"ts_last_poweron": t(agx.initdata.regionB.hwdata_a.addrof("ts_last_poweron")),
+ #"ts_last_poweroff": t(agx.initdata.regionB.hwdata_a.addrof("ts_last_poweroff")),
+ #"unk_3cd0": t(agx.initdata.regionB.hwdata_a.addrof("unk_3cd0")),
+
+ "halt_count": t(agx.initdata.fw_status.addrof("halt_count")),
+ "halted": t(agx.initdata.fw_status.addrof("halted")),
+ "resume": t(agx.initdata.fw_status.addrof("resume")),
+ "unk_40": t(agx.initdata.fw_status.addrof("unk_40")),
+ "unk_ctr": t(agx.initdata.fw_status.addrof("unk_ctr")),
+ "unk_60": t(agx.initdata.fw_status.addrof("unk_60")),
+ "unk_70": t(agx.initdata.fw_status.addrof("unk_70")),
+ "c_118c0": t(agx.initdata.regionC._addr + 0x118c0),
+ "c_118c4": t(agx.initdata.regionC._addr + 0x118c4),
+ "c_118c8": t(agx.initdata.regionC._addr + 0x118c8),
+ "c_118cc": t(agx.initdata.regionC._addr + 0x118cc),
+ "c_118d0": t(agx.initdata.regionC._addr + 0x118d0),
+ "c_118d4": t(agx.initdata.regionC._addr + 0x118d4),
+ "c_118d8": t(agx.initdata.regionC._addr + 0x118d8),
+ "c_118dc": t(agx.initdata.regionC._addr + 0x118dc),
+ "3d_cmds": t(agx.initdata.regionB.stats_3d.addrof("total_cmds")),
+ #"3d_tvb_oflws_1": t(agx.initdata.regionB.stats_3d.stats.addrof("tvb_overflows_1")),
+ #"3d_tvb_oflws_2": t(agx.initdata.regionB.stats_3d.stats.addrof("tvb_overflows_2")),
+ "3d_cur_stamp_id": t(agx.initdata.regionB.stats_3d.stats.addrof("cur_stamp_id")),
+ "3d_ts": t(agx.initdata.regionB.stats_3d.stats.addrof("unk_timestamp")),
+ #"3d_cur_stamp_id": t(agx.initdata.regionB.stats_3d.stats.addrof("cur_stamp_id")),
+})
+
+for i in range(4):
+ regs[f"3d{i}_cq"] = t(agx.initdata.regionB.stats_3d.stats.queues[i].addrof("cur_cmdqueue"))
+
+
+for i, r in enumerate(renderers):
+ regs.update({
+ f"r{i}_3d_done": t(r.wq_3d.info.pointers.addrof("gpu_doneptr")),
+ #f"r{i}_3d_rptr": t(r.wq_3d.info.pointers.addrof("gpu_rptr")),
+ f"r{i}_3d_busy": t(r.wq_3d.info.addrof("busy")),
+ #f"r{i}_3d_blk": t(r.wq_3d.info.addrof("blocked_on_barrier")),
+ #f"r{i}_3d_2c": t(r.wq_3d.info.addrof("unk_2c")),
+ #f"r{i}_3d_54": t(r.wq_3d.info.addrof("unk_54")),
+
+ f"r{i}_ta_done": t(r.wq_ta.info.pointers.addrof("gpu_doneptr")),
+ #f"r{i}_ta_rptr": t(r.wq_ta.info.pointers.addrof("gpu_rptr")),
+ f"r{i}_ta_busy": t(r.wq_ta.info.addrof("busy")),
+ #f"r{i}_ta_blk": t(r.wq_ta.info.addrof("blocked_on_barrier")),
+ #f"r{i}_ta_2c": t(r.wq_ta.info.addrof("unk_2c")),
+ #f"r{i}_ta_54": t(r.wq_ta.info.addrof("unk_54")),
+ f"r{i}_f{j}_ta_stamp1": t(r.stamp_ta1._addr),
+ f"r{i}_ta_stamp2":t(r.stamp_ta2._addr),
+ f"r{i}_f{j}_3d_stamp1": t(r.stamp_3d1._addr),
+ f"r{i}_3d_stamp2":t(r.stamp_3d2._addr),
+ })
+
+ for j in range(FRAMES):
+ work = r.work[j]
+ regs.update({
+ f"r{i}_f{j}_3d_ts": t(work.wc_3d.ts1._addr),
+ f"r{i}_f{j}_ta_ts": t(work.wc_ta.ts1._addr),
+ })
+
+div=4
+ticks = 24000000 // div * 25
+
+la = GPIOLogicAnalyzer(u, regs=regs, cpu=analyzer_cpu, div=div)
+
+
+print("==========================================")
+print("## Poll prior to job start")
+print("==========================================")
+
+#mon.poll()
+#agx.poll_objects()
+
+
+print("==========================================")
+print("## After start")
+print("==========================================")
+#agx.poll_objects()
+
+#mon.poll()
+print("==========================================")
+print("## Waiting")
+print("==========================================")
+
+print("Queues:")
+for i, r in enumerate(renderers):
+ print(f" Renderer {i}")
+ print(f" TA: {r.wq_ta.info._addr:#x} (stamp {r.work[0].ev_ta.id})")
+ #print(r.wq_ta.info)
+ print(f" 3D: {r.wq_3d.info._addr:#x} (stamp {r.work[0].ev_3d.id})")
+ #print(r.wq_3d.info)
+
+print("==========================================")
+print("## Run")
+print("==========================================")
+
+la.start(ticks, bufsize=0x8000000)
+
+try:
+ for r in renderers[:RENDERERS]:
+ r.run()
+
+ for r in renderers[:RENDERERS]:
+ while not r.ev_3d.fired:
+ agx.asc.work()
+ agx.poll_channels()
+ print("==========================================")
+ agx.poll_objects()
+ mon.poll()
+
+ r.wait()
+
+ #agx.poll_objects()
+
+ #print("==========================================")
+ #print("## Stop ASC")
+ #print("==========================================")
+
+ #agx.asc.stop()
+
+ ##time.sleep(0.1)
+
+ ##agx.poll_objects()
+
+ #print("==========================================")
+ #print("## Start ASC")
+ #print("==========================================")
+
+ #agx.asc.start()
+
+ ##agx.poll_objects()
+
+ #print("==========================================")
+ #print("## Run 2")
+ #print("==========================================")
+
+ #for r in renderers[RENDERERS//2:]:
+ #r.run()
+
+ #for r in renderers[RENDERERS//2:]:
+ #while not r.ev_3d.fired:
+ #agx.asc.work()
+ #agx.poll_channels()
+ #print("==========================================")
+
+ #r.wait()
+
+ #agx.poll_objects()
+
+ #mon.poll()
+
+finally:
+ #agx.poll_objects()
+ #mon.poll()
+
+ la.complete()
+ la.show()
+
+time.sleep(2)
+
diff --git a/tools/proxyclient/experiments/agx_renderframe.py b/tools/proxyclient/experiments/agx_renderframe.py
new file mode 100644
index 0000000..82ac91c
--- /dev/null
+++ b/tools/proxyclient/experiments/agx_renderframe.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+
+import sys, pathlib, time
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+import atexit, sys
+
+from m1n1.setup import *
+from m1n1.constructutils import Ver
+from m1n1.utils import *
+
+Ver.set_version(u)
+
+from m1n1.agx import AGX
+from m1n1.agx.render import *
+
+from m1n1 import asm
+
+p.pmgr_adt_clocks_enable("/arm-io/gfx-asc")
+p.pmgr_adt_clocks_enable("/arm-io/sgx")
+
+agx = AGX(u)
+mon = RegMonitor(u, ascii=True, bufsize=0x8000000)
+agx.mon = mon
+
+sgx = agx.sgx_dev
+
+try:
+ agx.start()
+ agx.uat.dump(0)
+
+ print("==========================================")
+ print("## After init")
+ print("==========================================")
+ mon.poll()
+ agx.poll_objects()
+
+ ctx = GPUContext(agx)
+ ctx.bind(63)
+
+ f = GPUFrame(ctx, sys.argv[1], track=False)
+
+ r = GPURenderer(ctx, 16, bm_slot=0, queue=1)
+ print("==========================================")
+ print("## Submitting")
+ print("==========================================")
+
+ w = r.submit(f.cmdbuf)
+
+ print("==========================================")
+ print("## Submitted")
+ print("==========================================")
+
+ print("==========================================")
+ print("## Run")
+ print("==========================================")
+
+ r.run()
+
+ while not r.ev_3d.fired:
+ agx.asc.work()
+ agx.poll_channels()
+
+ agx.poll_objects()
+ mon.poll()
+ r.wait()
+
+ time.sleep(3)
+
+finally:
+ #agx.poll_objects()
+ p.reboot()
diff --git a/tools/proxyclient/experiments/agx_tlb.py b/tools/proxyclient/experiments/agx_tlb.py
new file mode 100644
index 0000000..5ec86ca
--- /dev/null
+++ b/tools/proxyclient/experiments/agx_tlb.py
@@ -0,0 +1,295 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+
+import sys, pathlib, time
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+import atexit, sys
+
+from m1n1.setup import *
+from m1n1.constructutils import Ver
+from m1n1.utils import *
+
+Ver.set_version(u)
+
+from m1n1.agx import AGX
+from m1n1.agx.render import *
+
+from m1n1 import asm
+
+from m1n1.gpiola import GPIOLogicAnalyzer
+
+analyzer_cpu = 1
+
+p.pmgr_adt_clocks_enable("/arm-io/gfx-asc")
+p.pmgr_adt_clocks_enable("/arm-io/sgx")
+p.smp_start_secondaries()
+p.mmu_init_secondary(analyzer_cpu)
+iface.dev.timeout = 42
+
+agx = AGX(u)
+
+def initdata_hook(agx):
+ agx.initdata.regionC.idle_to_off_timeout_ms = 20000
+ agx.initdata.regionC.push()
+
+agx.initdata_hook = initdata_hook
+
+mon = RegMonitor(u, ascii=True, bufsize=0x8000000)
+agx.mon = mon
+
+sgx = agx.sgx_dev
+#mon.add(sgx.gpu_region_base, sgx.gpu_region_size, "contexts")
+#mon.add(sgx.gfx_shared_region_base, sgx.gfx_shared_region_size, "gfx-shared")
+#mon.add(sgx.gfx_handoff_base, sgx.gfx_handoff_size, "gfx-handoff")
+
+#mon.add(agx.initdasgx.gfx_handoff_base, sgx.gfx_handoff_size, "gfx-handoff")
+
+atexit.register(p.reboot)
+agx.start()
+
+print("==========================================")
+print("## After init")
+print("==========================================")
+mon.poll()
+agx.poll_objects()
+
+ctx = GPUContext(agx)
+ctx.bind(3)
+
+f = GPUFrame(ctx, sys.argv[1], track=False)
+
+RENDERERS = 1
+FRAMES = 1
+
+renderers = []
+
+for i in range(RENDERERS):
+ r = GPURenderer(ctx, 4, bm_slot=0x10 + i, queue=1)
+ renderers.append(r)
+
+ for q in (r.wq_3d, r.wq_ta):
+ q.info.set_prio(2)
+ q.info.push()
+
+print("==========================================")
+print("## Submitting")
+print("==========================================")
+
+for i, r in enumerate(renderers):
+ for j in range(FRAMES):
+ r.submit(f.cmdbuf)
+
+print("==========================================")
+print("## Submitted")
+print("==========================================")
+
+def t(addr):
+ paddr = agx.uat.iotranslate(0, addr, 4)[0][0]
+ if paddr is None:
+ raise Exception(f"Failed to iotranslate {addr:#x}")
+ return paddr
+
+regs = {
+ "ta_cmds": t(agx.initdata.regionB.stats_ta.addrof("total_cmds")),
+ "ta_ts": t(agx.initdata.regionB.stats_ta.stats.addrof("unk_timestamp")),
+}
+
+#pend_base = agx.initdata.regionC.addrof("pending_stamps")
+#for i in range(5):
+ #regs[f"st{i}_info"] = t(pend_base + i*8)
+ #regs[f"st{i}_val"] = t(pend_base + i*8 + 4)
+
+#for i in range(4):
+ #regs[f"ta{i}_cq"] = t(agx.initdata.regionB.stats_ta.stats.queues[i].addrof("cur_cmdqueue"))
+
+regs.update({
+ "pwr_status": t(agx.initdata.regionB.hwdata_a.addrof("pwr_status")),
+ "pstate": t(agx.initdata.regionB.hwdata_a.addrof("cur_pstate")),
+ "temp_c": t(agx.initdata.regionB.hwdata_a.addrof("temp_c")),
+ "pwr_mw": t(agx.initdata.regionB.hwdata_a.addrof("avg_power_mw")),
+ "pwr_ts": t(agx.initdata.regionB.hwdata_a.addrof("update_ts")),
+
+ "unk_10": t(agx.initdata.regionB.hwdata_a.addrof("unk_10")),
+ "unk_14": t(agx.initdata.regionB.hwdata_a.addrof("unk_14")),
+ "actual_pstate": t(agx.initdata.regionB.hwdata_a.addrof("actual_pstate")),
+ "tgt_pstate": t(agx.initdata.regionB.hwdata_a.addrof("tgt_pstate")),
+ "unk_40": t(agx.initdata.regionB.hwdata_a.addrof("unk_40")),
+ "unk_44": t(agx.initdata.regionB.hwdata_a.addrof("unk_44")),
+ "unk_48": t(agx.initdata.regionB.hwdata_a.addrof("unk_48")),
+ "freq_mhz": t(agx.initdata.regionB.hwdata_a.addrof("freq_mhz")),
+
+ "unk_748.0": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")),
+ "unk_748.1": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")+4),
+ "unk_748.2": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")+8),
+ "unk_748.3": t(agx.initdata.regionB.hwdata_a.addrof("unk_748")+12),
+ "use_percent": t(agx.initdata.regionB.hwdata_a.addrof("use_percent")),
+ "unk_83c": t(agx.initdata.regionB.hwdata_a.addrof("unk_83c")),
+ "freq_with_off": t(agx.initdata.regionB.hwdata_a.addrof("freq_with_off")),
+ "unk_ba0": t(agx.initdata.regionB.hwdata_a.addrof("unk_ba0")),
+ "unk_bb0": t(agx.initdata.regionB.hwdata_a.addrof("unk_bb0")),
+ "unk_c44": t(agx.initdata.regionB.hwdata_a.addrof("unk_c44")),
+ "unk_c58": t(agx.initdata.regionB.hwdata_a.addrof("unk_c58")),
+
+ "unk_3ca0": t(agx.initdata.regionB.hwdata_a.addrof("unk_3ca0")),
+ "unk_3ca8": t(agx.initdata.regionB.hwdata_a.addrof("unk_3ca8")),
+ "unk_3cb0": t(agx.initdata.regionB.hwdata_a.addrof("unk_3cb0")),
+ "ts_last_idle": t(agx.initdata.regionB.hwdata_a.addrof("ts_last_idle")),
+ "ts_last_poweron": t(agx.initdata.regionB.hwdata_a.addrof("ts_last_poweron")),
+ "ts_last_poweroff": t(agx.initdata.regionB.hwdata_a.addrof("ts_last_poweroff")),
+ "unk_3cd0": t(agx.initdata.regionB.hwdata_a.addrof("unk_3cd0")),
+
+ "halt_count": t(agx.initdata.fw_status.addrof("halt_count")),
+ "halted": t(agx.initdata.fw_status.addrof("halted")),
+ "resume": t(agx.initdata.fw_status.addrof("resume")),
+ "unk_40": t(agx.initdata.fw_status.addrof("unk_40")),
+ "unk_ctr": t(agx.initdata.fw_status.addrof("unk_ctr")),
+ "unk_60": t(agx.initdata.fw_status.addrof("unk_60")),
+ "unk_70": t(agx.initdata.fw_status.addrof("unk_70")),
+ "c_118c0": t(agx.initdata.regionC._addr + 0x118c0),
+ "c_118c4": t(agx.initdata.regionC._addr + 0x118c4),
+ "c_118c8": t(agx.initdata.regionC._addr + 0x118c8),
+ "c_118cc": t(agx.initdata.regionC._addr + 0x118cc),
+ "c_118d0": t(agx.initdata.regionC._addr + 0x118d0),
+ "c_118d4": t(agx.initdata.regionC._addr + 0x118d4),
+ "c_118d8": t(agx.initdata.regionC._addr + 0x118d8),
+ "c_118dc": t(agx.initdata.regionC._addr + 0x118dc),
+ "3d_cmds": t(agx.initdata.regionB.stats_3d.addrof("total_cmds")),
+ #"3d_cq": t(agx.initdata.regionB.stats_3d.stats.addrof("cur_cmdqueue")),
+ #"3d_tvb_oflws_1": t(agx.initdata.regionB.stats_3d.stats.addrof("tvb_overflows_1")),
+ #"3d_tvb_oflws_2": t(agx.initdata.regionB.stats_3d.stats.addrof("tvb_overflows_2")),
+ #"3d_cur_stamp_id": t(agx.initdata.regionB.stats_3d.stats.addrof("cur_stamp_id")),
+ "3d_ts": t(agx.initdata.regionB.stats_3d.stats.addrof("unk_timestamp")),
+ "hoff_lock": agx.uat.handoff.reg.LOCK_AP.addr,
+ "hoff_ctx": agx.uat.handoff.reg.CUR_CTX.addr,
+ "hoff_unk2": agx.uat.handoff.reg.UNK2.addr,
+ "hoff_unk3_lo": agx.uat.handoff.reg.UNK3.addr,
+ "hoff_unk3_hi": agx.uat.handoff.reg.UNK3.addr + 4,
+})
+
+for i, r in enumerate(renderers):
+ regs.update({
+ f"r{i}_3d_done": t(r.wq_3d.info.pointers.addrof("gpu_doneptr")),
+ #f"r{i}_3d_rptr": t(r.wq_3d.info.pointers.addrof("gpu_rptr")),
+ f"r{i}_3d_busy": t(r.wq_3d.info.addrof("busy")),
+ #f"r{i}_3d_blk": t(r.wq_3d.info.addrof("blocked_on_barrier")),
+ #f"r{i}_3d_2c": t(r.wq_3d.info.addrof("unk_2c")),
+ #f"r{i}_3d_54": t(r.wq_3d.info.addrof("unk_54")),
+
+ f"r{i}_ta_done": t(r.wq_ta.info.pointers.addrof("gpu_doneptr")),
+ #f"r{i}_ta_rptr": t(r.wq_ta.info.pointers.addrof("gpu_rptr")),
+ f"r{i}_ta_busy": t(r.wq_ta.info.addrof("busy")),
+ #f"r{i}_ta_blk": t(r.wq_ta.info.addrof("blocked_on_barrier")),
+ #f"r{i}_ta_2c": t(r.wq_ta.info.addrof("unk_2c")),
+ #f"r{i}_ta_54": t(r.wq_ta.info.addrof("unk_54")),
+ f"r{i}_f{j}_ta_stamp1": t(r.stamp_ta1._addr),
+ f"r{i}_ta_stamp2":t(r.stamp_ta2._addr),
+ f"r{i}_f{j}_3d_stamp1": t(r.stamp_3d1._addr),
+ f"r{i}_3d_stamp2":t(r.stamp_3d2._addr),
+ })
+
+ #for j in range(FRAMES):
+ #work = r.work[j]
+ #regs.update({
+ #f"r{i}_f{j}_3d_ts": t(work.wc_3d.ts1._addr),
+ #f"r{i}_f{j}_ta_ts": t(work.wc_ta.ts1._addr),
+ #})
+
+div=4
+ticks = 24000000 // div * 25
+
+la = GPIOLogicAnalyzer(u, regs=regs, cpu=analyzer_cpu, div=div)
+
+print("==========================================")
+print("## Run")
+print("==========================================")
+
+la.start(ticks, bufsize=0x8000000)
+
+depth_len = align_up(1920*1080*4, 0x4000)
+#agx.uat.iomap_at(ctx.ctx, f.cmdbuf.depth_buffer, 0, depth_len, VALID=0)
+#agx.uat.flush_dirty()
+
+fb = r.work[0].fb
+
+#agx.uat.iomap_at(ctx.ctx, fb, 0, depth_len, VALID=0)
+#agx.uat.flush_dirty()
+
+agx.kick_firmware()
+
+agx.show_stats = False
+count_pa = renderers[0].event_control.event_count._paddr
+
+print(f"count: {p.read32(count_pa)}")
+
+agx.uat.invalidate_cache()
+agx.uat.dump(ctx.ctx)
+
+mon.add(0x9fff74000, 0x4000)
+
+try:
+ for r in renderers:
+ r.run()
+
+ for r in renderers:
+ while not r.ev_ta.fired:
+ agx.asc.work()
+ agx.poll_channels()
+
+ print("TA fired")
+ print(f"count: {p.read32(count_pa)}")
+
+ for r in renderers:
+ while not r.ev_3d.fired:
+ agx.asc.work()
+ agx.poll_channels()
+ #print("==========================================")
+
+ r.wait()
+
+ print("3D fired")
+ print("Timestamps:")
+ print(f" 3D 1: {r.ts3d_1.pull().val}")
+ print(f" 3D 2: {r.ts3d_2.pull().val}")
+ print(f" TA 1: {r.tsta_1.pull().val}")
+ print(f" TA 2: {r.tsta_2.pull().val}")
+ print("CPU flag:", r.buffer_mgr.misc_obj.pull().cpu_flag)
+
+ mon.poll()
+
+ #agx.uat.iomap_at(ctx.ctx, fb, 0, depth_len, VALID=0)
+ #agx.uat.flush_dirty()
+
+ print(f"fb: {fb:#x}")
+
+ for i, r in enumerate(renderers):
+ for j in range(FRAMES):
+ r.submit(f.cmdbuf)
+
+ r.run()
+
+ for r in renderers:
+ while not r.ev_3d.fired:
+ agx.asc.work()
+ r.wait()
+
+ print("3D fired again")
+ print("Timestamps:")
+ print(f" 3D 1: {r.ts3d_1.pull().val}")
+ print(f" 3D 2: {r.ts3d_2.pull().val}")
+ print(f" TA 1: {r.tsta_1.pull().val}")
+ print(f" TA 2: {r.tsta_2.pull().val}")
+ print("CPU flag:", r.buffer_mgr.misc_obj.pull().cpu_flag)
+
+ time.sleep(0.5)
+
+finally:
+ agx.poll_channels()
+ #agx.poll_objects()
+ #mon.poll()
+
+ la.complete()
+ la.show()
+
+time.sleep(2)
+
diff --git a/tools/proxyclient/experiments/agx_tracetimings.py b/tools/proxyclient/experiments/agx_tracetimings.py
new file mode 100644
index 0000000..52ac2c9
--- /dev/null
+++ b/tools/proxyclient/experiments/agx_tracetimings.py
@@ -0,0 +1,314 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+
+import sys, pathlib, time
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+import atexit, sys
+
+from m1n1.setup import *
+from m1n1.constructutils import Ver
+from m1n1.utils import *
+
+Ver.set_version(u)
+
+from m1n1.agx import AGX
+from m1n1.agx.render import *
+
+from m1n1.gpiola import GPIOLogicAnalyzer
+
+analyzer_cpu = 1
+
+p.pmgr_adt_clocks_enable("/arm-io/gfx-asc")
+p.pmgr_adt_clocks_enable("/arm-io/sgx")
+p.smp_start_secondaries()
+p.mmu_init_secondary(analyzer_cpu)
+iface.dev.timeout = 10
+
+agx = AGX(u)
+
+mon = RegMonitor(u, ascii=True, bufsize=0x8000000)
+agx.mon = mon
+
+sgx = agx.sgx_dev
+#mon.add(sgx.gpu_region_base, sgx.gpu_region_size, "contexts")
+#mon.add(sgx.gfx_shared_region_base, sgx.gfx_shared_region_size, "gfx-shared")
+#mon.add(sgx.gfx_handoff_base, sgx.gfx_handoff_size, "gfx-handoff")
+
+#mon.add(agx.initdasgx.gfx_handoff_base, sgx.gfx_handoff_size, "gfx-handoff")
+
+atexit.register(p.reboot)
+agx.start()
+
+print("==========================================")
+print("## After init")
+print("==========================================")
+mon.poll()
+agx.poll_objects()
+
+ctx = GPUContext(agx)
+ctx.bind(1)
+
+renderer = GPURenderer(ctx, 64, bm_slot=0, queue=0)
+renderer2 = GPURenderer(ctx, 64, bm_slot=1, queue=1)
+
+#for q in (renderer.wq_3d, renderer.wq_ta):#, renderer2.wq_3d, renderer2.wq_ta):
+ #q.info.unk_30 = 2
+ #q.info.unk_34 = 2
+ #q.info.unk_38 = 0xffff000000000000
+ #q.info.unk_40 = 0
+ #q.info.unk_44 = 0
+ #q.info.unk_48 = 2
+ #q.info.unk_50 = 0x1
+ #q.info.push()
+
+f = GPUFrame(ctx, sys.argv[1], track=False)
+#f2 = GPUFrame(renderer2.ctx, sys.argv[1], track=False)
+
+print("==========================================")
+print("## Pre submit")
+print("==========================================")
+
+mon.poll()
+agx.poll_objects()
+
+print("==========================================")
+print("## Submitting")
+print("==========================================")
+
+work = renderer.submit(f.cmdbuf)
+work2 = renderer2.submit(f.cmdbuf)
+workb = renderer.submit(f.cmdbuf)
+work2b = renderer2.submit(f.cmdbuf)
+
+print(work.wc_3d)
+print(work.wc_ta)
+print(work2.wc_3d)
+print(work2.wc_ta)
+
+print("==========================================")
+print("## Submitted")
+print("==========================================")
+
+def t(addr):
+ paddr = agx.uat.iotranslate(0, addr, 4)[0][0]
+ if paddr is None:
+ raise Exception(f"Failed to iotranslate {addr:#x}")
+ return paddr
+
+regs = {
+ "ta_cmds": t(agx.initdata.regionB.stats_ta.addrof("total_cmds")),
+ "ta0_busy": t(agx.initdata.regionB.stats_ta.stats.queues[0].addrof("busy")),
+ "ta0_unk4": t(agx.initdata.regionB.stats_ta.stats.queues[0].addrof("unk_4")),
+ "ta0_cq": t(agx.initdata.regionB.stats_ta.stats.queues[0].addrof("cur_cmdqueue")),
+ "ta0_cnt": t(agx.initdata.regionB.stats_ta.stats.queues[0].addrof("cur_count")),
+ "ta1_busy": t(agx.initdata.regionB.stats_ta.stats.queues[1].addrof("busy")),
+ "ta1_unk4": t(agx.initdata.regionB.stats_ta.stats.queues[1].addrof("unk_4")),
+ "ta1_cq": t(agx.initdata.regionB.stats_ta.stats.queues[1].addrof("cur_cmdqueue")),
+ "ta1_cnt": t(agx.initdata.regionB.stats_ta.stats.queues[1].addrof("cur_count")),
+ "ta_ts": t(agx.initdata.regionB.stats_ta.stats.addrof("unk_timestamp")),
+ "3d_cmds": t(agx.initdata.regionB.stats_3d.addrof("total_cmds")),
+ "3d_cq": t(agx.initdata.regionB.stats_3d.stats.addrof("cur_cmdqueue")),
+ "3d_tvb_oflws_1": t(agx.initdata.regionB.stats_3d.stats.addrof("tvb_overflows_1")),
+ "3d_tvb_oflws_2": t(agx.initdata.regionB.stats_3d.stats.addrof("tvb_overflows_2")),
+ "3d_cur_stamp_id": t(agx.initdata.regionB.stats_3d.stats.addrof("cur_stamp_id")),
+ "3d_ts": t(agx.initdata.regionB.stats_3d.stats.addrof("unk_timestamp")),
+
+ "bmctl_0": t(agx.initdata.regionB.buffer_mgr_ctl._addr + 0),
+ "bmctl_8": t(agx.initdata.regionB.buffer_mgr_ctl._addr + 8),
+ "2_bmctl_0": t(agx.initdata.regionB.buffer_mgr_ctl._addr + 16),
+ "2_bmctl_8": t(agx.initdata.regionB.buffer_mgr_ctl._addr + 24),
+
+ "bmmisc_0": t(renderer.buffer_mgr.info.misc.addrof("gpu_0")),
+ "bmmisc_4": t(renderer.buffer_mgr.info.misc.addrof("gpu_4")),
+ "bmmisc_8": t(renderer.buffer_mgr.info.misc.addrof("gpu_8")),
+ "bmmisc_c": t(renderer.buffer_mgr.info.misc.addrof("gpu_c")),
+ "bmi_gpuc": t(renderer.buffer_mgr.info.addrof("gpu_counter")),
+ "bmi_18": t(renderer.buffer_mgr.info.addrof("unk_18")),
+ "bmi_gpuc2": t(renderer.buffer_mgr.info.addrof("gpu_counter2")),
+
+ "2_bmmisc_0": t(renderer2.buffer_mgr.info.misc.addrof("gpu_0")),
+ "2_bmmisc_4": t(renderer2.buffer_mgr.info.misc.addrof("gpu_4")),
+ "2_bmmisc_8": t(renderer2.buffer_mgr.info.misc.addrof("gpu_8")),
+ "2_bmmisc_c": t(renderer2.buffer_mgr.info.misc.addrof("gpu_c")),
+ "2_bmi_gpuc": t(renderer2.buffer_mgr.info.addrof("gpu_counter")),
+ "2_bmi_18": t(renderer2.buffer_mgr.info.addrof("unk_18")),
+ "2_bmi_gpuc2": t(renderer2.buffer_mgr.info.addrof("gpu_counter2")),
+
+ "ctxdat_0": t(renderer.ctx.gpu_context._addr + 0),
+ "ctxdat_4": t(renderer.ctx.gpu_context._addr + 4),
+ "ctxdat_8": t(renderer.ctx.gpu_context._addr + 8),
+ "ctxdat_c": t(renderer.ctx.gpu_context._addr + 0xc),
+
+ "2_ctxdat_0": t(renderer2.ctx.gpu_context._addr + 0),
+ "2_ctxdat_4": t(renderer2.ctx.gpu_context._addr + 4),
+ "2_ctxdat_8": t(renderer2.ctx.gpu_context._addr + 8),
+ "2_ctxdat_c": t(renderer2.ctx.gpu_context._addr + 0xc),
+
+ "evctl_ta": t(renderer.event_control.addrof("has_ta")),
+ "evctl_pta": t(renderer.event_control.addrof("pstamp_ta")),
+ "evctl_3d": t(renderer.event_control.addrof("has_3d")),
+ "evctl_p3d": t(renderer.event_control.addrof("pstamp_3d")),
+ "evctl_in_list": t(renderer.event_control.addrof("in_list")),
+ "evctl_prev": t(renderer.event_control.list_head.addrof("prev")),
+ "evctl_next": t(renderer.event_control.list_head.addrof("next")),
+
+ "2_evctl_ta": t(renderer2.event_control.addrof("has_ta")),
+ "2_evctl_pta": t(renderer2.event_control.addrof("pstamp_ta")),
+ "2_evctl_3d": t(renderer2.event_control.addrof("has_3d")),
+ "2_evctl_p3d": t(renderer2.event_control.addrof("pstamp_3d")),
+ "2_evctl_in_list":t(renderer2.event_control.addrof("in_list")),
+ "2_evctl_prev": t(renderer2.event_control.list_head.addrof("prev")),
+ "2_evctl_next": t(renderer2.event_control.list_head.addrof("next")),
+
+ "jl_first": t(renderer.job_list.addrof("first_job")),
+ "jl_last": t(renderer.job_list.addrof("last_head")),
+ "jl_10": t(renderer.job_list.addrof("unkptr_10")),
+
+ "2_jl_first": t(renderer2.job_list.addrof("first_job")),
+ "2_jl_last": t(renderer2.job_list.addrof("last_head")),
+ "2_jl_10": t(renderer2.job_list.addrof("unkptr_10")),
+
+ "3d_done": t(renderer.wq_3d.info.pointers.addrof("gpu_doneptr")),
+ "3d_rptr": t(renderer.wq_3d.info.pointers.addrof("gpu_rptr")),
+ "3d_rptr1": t(renderer.wq_3d.info.addrof("gpu_rptr1")),
+ "3d_rptr2": t(renderer.wq_3d.info.addrof("gpu_rptr2")),
+ "3d_rptr3": t(renderer.wq_3d.info.addrof("gpu_rptr3")),
+ "3d_busy": t(renderer.wq_3d.info.addrof("busy")),
+ "3d_blk": t(renderer.wq_3d.info.addrof("blocked_on_barrier")),
+ "3d_2c": t(renderer.wq_3d.info.addrof("unk_2c")),
+ "3d_54": t(renderer.wq_3d.info.addrof("unk_54")),
+ "3d_58": t(renderer.wq_3d.info.addrof("unk_58")),
+
+ "2_3d_done": t(renderer2.wq_3d.info.pointers.addrof("gpu_doneptr")),
+ "2_3d_rptr": t(renderer2.wq_3d.info.pointers.addrof("gpu_rptr")),
+ "2_3d_busy": t(renderer2.wq_3d.info.addrof("busy")),
+ "2_3d_blk": t(renderer2.wq_3d.info.addrof("blocked_on_barrier")),
+ "2_3d_2c": t(renderer2.wq_3d.info.addrof("unk_2c")),
+ "2_3d_54": t(renderer2.wq_3d.info.addrof("unk_54")),
+
+ "ta_done": t(renderer.wq_ta.info.pointers.addrof("gpu_doneptr")),
+ "ta_rptr": t(renderer.wq_ta.info.pointers.addrof("gpu_rptr")),
+ "ta_rptr1": t(renderer.wq_ta.info.addrof("gpu_rptr1")),
+ "ta_rptr2": t(renderer.wq_ta.info.addrof("gpu_rptr2")),
+ "ta_rptr3": t(renderer.wq_ta.info.addrof("gpu_rptr3")),
+ "ta_busy": t(renderer.wq_ta.info.addrof("busy")),
+ "ta_blk": t(renderer.wq_ta.info.addrof("blocked_on_barrier")),
+ "ta_2c": t(renderer.wq_ta.info.addrof("unk_2c")),
+ "ta_54": t(renderer.wq_ta.info.addrof("unk_54")),
+ "ta_58": t(renderer.wq_ta.info.addrof("unk_58")),
+
+ "2_ta_done": t(renderer2.wq_ta.info.pointers.addrof("gpu_doneptr")),
+ "2_ta_rptr": t(renderer2.wq_ta.info.pointers.addrof("gpu_rptr")),
+ "2_ta_busy": t(renderer2.wq_ta.info.addrof("busy")),
+ "2_ta_blk": t(renderer2.wq_ta.info.addrof("blocked_on_barrier")),
+ "2_ta_2c": t(renderer2.wq_ta.info.addrof("unk_2c")),
+ "2_ta_54": t(renderer2.wq_ta.info.addrof("unk_54")),
+
+ "3d_ts1": t(work.wc_3d.ts1._addr),
+ "3d_ts1b": t(workb.wc_3d.ts1._addr),
+ "3d_ts2": t(work.wc_3d.ts2._addr),
+ "3d_ts3": t(work.wc_3d.ts3._addr),
+ "ta_ts1": t(work.wc_ta.ts1._addr),
+ "ta_ts1b": t(workb.wc_ta.ts1._addr),
+ "ta_ts2": t(work.wc_ta.ts2._addr),
+ "ta_ts3": t(work.wc_ta.ts3._addr),
+ "2_3d_ts1": t(work2.wc_3d.ts1._addr),
+ "2_3d_ts1b": t(work2b.wc_3d.ts1._addr),
+ "2_3d_ts2": t(work2.wc_3d.ts2._addr),
+ "2_3d_ts3": t(work2.wc_3d.ts3._addr),
+ "2_ta_ts1": t(work2.wc_ta.ts1._addr),
+ "2_ta_ts1b": t(work2b.wc_ta.ts1._addr),
+ "2_ta_ts2": t(work2.wc_ta.ts2._addr),
+ "2_ta_ts3": t(work2.wc_ta.ts3._addr),
+
+ "ta_stamp1": t(renderer.stamp_ta1._addr),
+ "ta_stamp2": t(renderer.stamp_ta2._addr),
+ "3d_stamp1": t(renderer.stamp_3d1._addr),
+ "3d_stamp2": t(renderer.stamp_3d2._addr),
+
+ "2_ta_stamp1": t(renderer2.stamp_ta1._addr),
+ "2_ta_stamp2": t(renderer2.stamp_ta2._addr),
+ "2_3d_stamp1": t(renderer2.stamp_3d1._addr),
+ "2_3d_stamp2": t(renderer2.stamp_3d2._addr),
+}
+
+div=4
+ticks = 24000000 // div * 3
+
+la = GPIOLogicAnalyzer(u, regs=regs, cpu=analyzer_cpu, div=div)
+
+
+print("==========================================")
+print("## Poll prior to job start")
+print("==========================================")
+
+mon.poll()
+agx.poll_objects()
+
+print("==========================================")
+print("## Run")
+print("==========================================")
+
+la.start(ticks, bufsize=0x400000)
+renderer.run()
+
+print("==========================================")
+print("## After r1 start")
+print("==========================================")
+#agx.poll_objects()
+
+#time.sleep(0.1)
+#mon.poll()
+#time.sleep(0.15)
+#mon.poll()
+renderer2.run()
+
+print("==========================================")
+print("## After r2 start")
+print("==========================================")
+agx.poll_objects()
+
+#mon.poll()
+print("==========================================")
+print("## Waiting")
+print("==========================================")
+
+try:
+
+ #while not work.ev_3d.fired:
+ #agx.asc.work()
+ ##mon.poll()
+ #agx.poll_objects()
+ #agx.poll_channels()
+ #print("==========================================")
+ ##time.sleep(0.1)
+
+ #print("==========================================")
+ #print("## Ev1 Fired")
+ #print("==========================================")
+
+ while not work2.ev_3d.fired:
+ agx.asc.work()
+ #mon.poll()
+ agx.poll_objects()
+ agx.poll_channels()
+ print("==========================================")
+ #time.sleep(0.1)
+
+ print("==========================================")
+ print("## Ev2 Fired")
+ print("==========================================")
+
+ renderer.wait()
+ renderer2.wait()
+
+ agx.poll_objects()
+ #mon.poll()
+
+finally:
+ la.complete()
+ la.show()
+
+time.sleep(2)
+
diff --git a/tools/proxyclient/experiments/aic_test.py b/tools/proxyclient/experiments/aic_test.py
new file mode 100755
index 0000000..aad7e2c
--- /dev/null
+++ b/tools/proxyclient/experiments/aic_test.py
@@ -0,0 +1,411 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+from m1n1 import asm
+
+ULCON = 0x235200000
+UCON = 0x235200004
+UFCON = 0x235200008
+UTRSTAT = 0x235200010
+
+AIC = 0x23b100000
+
+AIC_RST = AIC + 0xc
+AIC_CFG = AIC + 0x10
+
+AIC_TB = 0x23b108000
+AIC_TGT_DST = AIC + 0x3000
+AIC_SW_GEN_SET = AIC + 0x4000
+AIC_SW_GEN_CLR = AIC + 0x4080
+AIC_MASK_SET = AIC + 0x4100
+AIC_MASK_CLR = AIC + 0x4180
+AIC_HW_STATE = AIC + 0x4200
+
+AIC_INTERRUPT_ACK = AIC + 0x2004
+AIC_IPI_SET = AIC + 0x2008
+AIC_IPI_CLR = AIC + 0x200c
+
+AIC_IPI_MASK_SET = AIC + 0x2024
+AIC_IPI_MASK_CLR = AIC + 0x2028
+
+daif = u.mrs(DAIF)
+print("DAIF: %x" % daif)
+daif &= ~0x3c0
+#daif |= 0x3c0
+u.msr(DAIF, daif)
+print("DAIF: %x" % u.mrs(DAIF))
+
+def cpoll():
+ mon.poll()
+ print("<")
+ mon.poll()
+ print(">")
+
+p.write32(AIC + 0xc, 1)
+p.write32(AIC + 0x10, 0xe0777971)
+p.write32(AIC + 0x18, 0)
+p.write32(AIC + 0x20, 0xffffffff)
+p.write32(AIC + 0x24, 0xffffffff)
+p.write32(AIC + 0x28, 0xffffffff)
+p.write32(AIC + 0x2c, 0xffffffff)
+p.write32(AIC + 0x30, 0xffffffff)
+p.write32(AIC + 0x34, 0xffffffff)
+p.write32(AIC + 0x38, 0xffffffff)
+p.write32(AIC + 0x3c, 0xffffffff)
+p.write32(AIC + 0x40, 0xffffffff)
+p.write32(AIC + 0x38, 0xffffffff)
+#p.write32(AIC + 0xc, 0)
+
+p.memset32(AIC_MASK_SET, 0xffffffff, 0x80)
+p.memset32(AIC_SW_GEN_CLR, 0xffffffff, 0x80)
+p.memset32(AIC_TGT_DST, 0x1, 0x1000)
+#p.memset32(AIC_MASK_CLR, 0xffffffff, 0x80)
+
+#p.write32(AIC + 0x10, 0xe0777971)
+
+mon.add(AIC + 0x0000, 0x1000)
+mon.add(AIC + 0x2080, 0x040)
+mon.add(AIC + 0x4000, 0x200)
+mon.add(AIC + 0x5000, 0x080)
+mon.add(AIC + 0x5080, 0x080)
+mon.add(AIC + 0x5100, 0x080)
+mon.add(AIC + 0x5180, 0x080)
+mon.add(AIC + 0x5200, 0x080)
+mon.add(AIC + 0x5280, 0x080)
+mon.add(AIC + 0x5300, 0x080)
+mon.add(AIC + 0x5380, 0x080)
+#mon.add(AIC + 0x3000, 0x400)
+#mon.add(AIC + 0x4000, 0x400)
+#mon.add(AIC + 0x8000, 0x20)
+#mon.add(AIC + 0x8030, 0xd0)
+#mon.add(0x235200000, 0x20)
+
+def test_ipi():
+ cpoll()
+
+ print("Set IPI")
+
+ p.write32(AIC_IPI_SET, 1)
+
+ cpoll()
+ cpoll()
+
+ print("Read ACK reg")
+
+ reason = p.read32(AIC_INTERRUPT_ACK)
+ print("reason: 0x%x" % reason)
+
+ cpoll()
+
+ print("Write reason")
+ p.write32(AIC_INTERRUPT_ACK, reason)
+
+ cpoll()
+
+ reason = p.read32(AIC_INTERRUPT_ACK)
+ print("reason: 0x%x" % reason)
+
+ cpoll()
+
+ print("Write ACK reg")
+ p.write32(AIC_INTERRUPT_ACK, reason)
+ cpoll()
+
+ print("Clear IPI")
+
+ p.write32(AIC_IPI_CLR, 1)
+ cpoll()
+
+ print("Read ACK reg")
+
+ reason = p.read32(AIC_INTERRUPT_ACK)
+
+ print("reason: 0x%x" % reason)
+
+ cpoll()
+
+ print("Write IPI ACK")
+
+ p.write32(AIC_IPI_MASK_CLR, 1)
+
+ cpoll()
+
+def test_timer():
+ cpoll()
+
+ freq = u.mrs(CNTFRQ_EL0)
+ print("Timer freq: %d" % freq)
+
+ #u.msr(CNTP_CTL_EL0, 0)
+ #u.msr(CNTP_TVAL_EL0, freq * 2)
+ #u.msr(CNTP_CTL_EL0, 1)
+ #u.msr(CNTV_CTL_EL0, 0)
+ #u.msr(CNTV_TVAL_EL0, freq * 2)
+ #u.msr(CNTV_CTL_EL0, 1)
+ #u.msr(CNTHV_CTL_EL2, 0)
+ #u.msr(CNTHV_TVAL_EL2, freq * 2)
+ #u.msr(CNTHV_CTL_EL2, 1)
+ u.msr(CNTHP_CTL_EL2, 0)
+ u.msr(CNTHP_TVAL_EL2, freq * 2)
+ u.msr(CNTHP_CTL_EL2, 1)
+
+ iface.ttymode()
+
+ #while True:
+ #p.nop()
+ #time.sleep(0.3)
+ #print(". %x" % u.mrs(CNTP_CTL_EL0))
+
+def get_irq_state(irq):
+ v = p.read32(AIC_HW_STATE + 4* (irq//32))
+ return bool(v & 1<<(irq%32))
+
+def test_uart_irq():
+ cpoll()
+ #p.memset32(AIC_MASK_CLR, 0xffffffff, 0x80)
+
+ print("cleanup")
+ p.write32(UCON, 5)
+ p.write32(UFCON, 0x11)
+ p.write32(UTRSTAT, 0xfff)
+
+ cpoll()
+
+ for irq in range(600, 610):
+ #print("S: ", get_irq_state(irq))
+ p.write32(AIC_SW_GEN_CLR + 4* (irq//32), 1<<(irq%32))
+ #print("S: ", get_irq_state(irq))
+ #print("a")
+ #print("S: ", get_irq_state(irq))
+ p.write32(AIC_MASK_CLR + 4* (irq//32), 1<<(irq%32))
+ #print("S: ", get_irq_state(irq))
+ #print("b")
+
+ irq = 605
+
+ cpoll()
+ print("a")
+ print("S: ", get_irq_state(irq))
+ print("ucon: %x" %p.read32(UCON))
+
+ TX_IRQ_EN = 0x1000
+
+ RX_IRQ_ENABLE = 0x20000
+ RX_IRQ_UNMASK = 0x10000
+
+ RX_IRQ_ENA = 0x20000
+ RX_IRQ_MASK = 0x4000 # defer?
+
+ code = u.malloc(0x1000)
+
+ c = asm.ARMAsm("""
+ ldr x1, =0x235200000
+
+ ldr x3, =0xc000000
+1:
+ subs x3, x3, #1
+ bne 1b
+ mov x2, 'A'
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+ #str w2, [x1, #0x20]
+
+ mov x3, #0x3ff
+ str w3, [x1, #0x10]
+ #str w2, [x1, #0x20]
+ str w0, [x1, #4]
+ ldr w0, [x1, #0x10]
+
+ ldr x3, =0xc00000
+1:
+ subs x3, x3, #1
+ bne 1b
+
+ #mov x3, #0x3ff
+ #str w3, [x1, #0x10]
+ #ldr w2, [x1, #4]
+ #mov x2, #0x205
+ #str w2, [x1, #4]
+ #str w0, [x1, #4]
+ ##ldr w0, [x1, #0x10]
+
+ #ldr x3, =0xc00000
+#1:
+ #subs x3, x3, #1
+ #bne 1b
+
+ ldr w0, [x1, #0x10]
+ #mov w0, w2
+ ret
+""", code)
+ iface.writemem(code, c.data)
+ p.dc_cvau(code, len(c.data))
+ p.ic_ivau(code, len(c.data))
+
+ #RX_IRQ_
+
+ """
+UCON UTRSTAT
+00200 TX FIFO thresh IRQ delivery enable
+00080 0200 TX FIFO threshold IRQ unmask
+20000 0100 RX IRQ unmask
+10000 RX IRQ delivery enable
+"""
+
+ # edge triggered
+ TX_FIFO_THRESH_CROSSED_IRQ_UNMASK = 0x2000
+
+ TX_IRQ_UNMASK = 0x200
+ TX_EVENT_ENABLE = 0x80
+ RX_EVENT_ENABLE = 0x20000
+ RX_IRQ_UNMASK = 0x10000
+
+ #flags = 0x7ffc0
+ crash = 0x180000
+ no_irqs = 0x21c5c0
+ instant_irqs = 0x3a00
+ #flags = no_irqs | 0x0000
+ #flags = 0x2e5c0
+ #flags = 0x2000
+
+ #flags = 0x30000
+ #flags = 0x80
+ flags = 0x7ff80
+
+
+ val = flags | 0x005
+ #print("ucon<-%x" % val)
+ #p.write32(UCON, val)
+ p.write32(UTRSTAT, 0xfff)
+ print("utrstat=%x" % p.read32(UTRSTAT))
+ ret = p.call(code, val)
+ print("utrstat::%x" % ret)
+ print("utrstat=%x" % p.read32(UTRSTAT))
+ time.sleep(0.5)
+ iface.dev.write(b'1')
+ #print(iface.dev.read(1))
+ time.sleep(0.1)
+ print("ucon: %x" %p.read32(UCON))
+ print("delay")
+ try:
+ p.udelay(500000)
+ except:
+ pass
+ iface.dev.write(bytes(64))
+ p.nop()
+ print("ucon: %x" %p.read32(UCON))
+ print("S: ", get_irq_state(irq))
+
+ #while True:
+ #print("S: ", get_irq_state(irq))
+ #p.write32(UTRSTAT, 0xfff)
+ #print("utrstat=%x" % p.read32(UTRSTAT))
+ #print("ucon: %x" %p.read32(UCON))
+ #print(">S: ", get_irq_state(irq))
+ #p.write32(UCON, flags | 0x005)
+ #print(">ucon: %x" %p.read32(UCON))
+ #time.sleep(0.1)
+
+
+
+def test_smp_ipi():
+ p.smp_start_secondaries()
+
+ code = u.malloc(0x1000)
+
+ c = asm.ARMAsm("""
+#define sys_reg(op0, op1, CRn, CRm, op2) s##op0##_##op1##_c##CRn##_c##CRm##_##op2
+#define SYS_CYC_OVRD sys_reg(3, 5, 15, 5, 0)
+
+ msr DAIFClr, 7
+ ldr x1, =0x000000
+ msr SYS_CYC_OVRD, x1
+ mrs x0, SYS_CYC_OVRD
+ mov x1, #0x1000000
+1:
+ subs x1, x1, #1
+ mrs x0, HCR_EL2
+ bne 1b
+ ret
+""", code)
+
+ iface.writemem(code, c.data)
+ p.dc_cvau(code, len(c.data))
+ p.ic_ivau(code, len(c.data))
+
+ print("Enable IRQs on secondaries")
+ for i in range(1, 8):
+ ret = p.smp_call_sync(i, code)
+ print("0x%x"%ret)
+
+ #e0477971
+ #p.write32(AIC + 0x10, 0xe0777971)
+ #p.write32(AIC + 0x28, 0xffffffff)
+
+ cpoll()
+
+ print("Clear IPI")
+ p.write32(AIC_IPI_CLR, 0xffffffff)
+ p.write32(AIC_IPI_MASK_CLR, 0xffffffff)
+ for i in range(8):
+ p.write32(AIC_IPI_CLR+0x3000+i*0x80, 0xffffffff)
+ p.write32(AIC_IPI_MASK_CLR+0x3000+i*0x80, 0xffffffff)
+
+ cpoll()
+
+ print("Set IPI")
+ #p.write32(AIC_IPI_SET, 0x00000004)
+ #p.write32(AIC_IPI_SET, 0x00000000)
+
+ cpoll()
+ print("Clear IPI")
+ p.write32(AIC_IPI_CLR, 0xffffffff)
+ p.write32(AIC_IPI_MASK_CLR, 0xffffffff)
+ for i in range(8):
+ p.write32(AIC_IPI_CLR+0x3000+i*0x80, 1)
+ p.write32(AIC_IPI_MASK_CLR+0x3000+i*0x80, 1)
+
+def test_smp_affinity():
+ p.write32(AIC_TGT_DST, 0x6)
+ p.write32(AIC_TGT_DST+4, 0xfe)
+ p.write32(AIC_TGT_DST+8, 0xfe)
+ p.write32(AIC_TGT_DST+12, 0x6)
+ p.write32(AIC_SW_GEN_SET,0x8);
+ p.write32(AIC_MASK_CLR,0x8);
+
+#test_ipi()
+#test_timer()
+#test_uart_irq()
+test_smp_ipi()
+test_smp_affinity()
diff --git a/tools/proxyclient/experiments/amcc_err_handler.py b/tools/proxyclient/experiments/amcc_err_handler.py
new file mode 100644
index 0000000..935a99e
--- /dev/null
+++ b/tools/proxyclient/experiments/amcc_err_handler.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+
+#for i in (0x28e580350, 0x28e580328, 0x28e580380, 0x28e580378):
+ #p.write32(i, 0)
+
+sts = p.read32(0x20002100c)
+print(f"status: {sts:#x}")
+p.write32(0x200021010, 0)
+p.write32(0x20002100c, 0xfff)
+time.sleep(0.1)
+sts = p.read32(0x20002100c)
+print(f"status: {sts:#x}")
+
+print(f"ERRLOG0: {p.read32(0x20000070c):#x}")
+print(f"ERRLOG1: {p.read32(0x200000710):#x}")
+print(f"ERRLOG2: {p.read32(0x200000714):#x}")
+print(f"ERRLOG3: {p.read32(0x200000718):#x}")
+print(f"ERRLOG4: {p.read32(0x20000071c):#x}")
+
+p.write32(0x20000070c, 0xffffffff)
+
+#p.fb_shutdown()
+u.inst("tlbi vmalle1is")
+
+#p.memset32(fb, 0xffffffff, 3024 * 1964 * 4)
+#p.dc_cvac(fb, 3024 * 1964 * 4)
+
+#p.memset32(0x100_80000000, 0xffffffff, 0x80000000)
+
+#p.memcpy32(fb, fb + 0x1000, 0x800)
+#p.memset32(fb, 0xfffff, 3024 * 1964 * 4)
+#p.memset32(fb, 0xffffffff, 3024 * 1964 * 4)
+#p.memcpy32(0x100_80000000, fb + 0x1000, 0x1800)
+#p.read8(fb + 0x10)
+#p.write8(fb + 0x200, 0xdeadbeef)
+#p.memset32(fb, 0xfffffff, 3024 * 1964 * 4)
+
+#p.iodev_write(IODEV.FB, "test\n", 5)
diff --git a/tools/proxyclient/experiments/aop.py b/tools/proxyclient/experiments/aop.py
new file mode 100755
index 0000000..0dba36f
--- /dev/null
+++ b/tools/proxyclient/experiments/aop.py
@@ -0,0 +1,310 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+import struct
+import traceback
+from construct import *
+
+from m1n1.setup import *
+from m1n1.shell import run_shell
+from m1n1.hw.dart import DART, DARTRegs
+from m1n1.fw.asc import StandardASC, ASCDummyEndpoint
+from m1n1.fw.asc.base import *
+from m1n1.fw.aop import *
+from m1n1.fw.aop.ipc import *
+from m1n1.fw.afk.rbep import *
+from m1n1.fw.afk.epic import *
+
+# Set up a secondary proxy channel so that we can stream
+# the microphone samples
+p.usb_iodev_vuart_setup(p.iodev_whoami())
+p.iodev_set_usage(IODEV.USB_VUART, USAGE.UARTPROXY)
+
+p.pmgr_adt_clocks_enable("/arm-io/dart-aop")
+
+adt_dc = u.adt["/arm-io/aop/iop-aop-nub/aop-audio/dc-2400000"]
+
+pdm_config = Container(
+ unk1=2,
+ clockSource=u'pll ',
+ pdmFrequency=2400000,
+ unk3_clk=24000000,
+ unk4_clk=24000000,
+ unk5_clk=24000000,
+ channelPolaritySelect=256,
+ unk7=99,
+ unk8=1013248,
+ unk9=0,
+ ratios=Container(
+ r1=15,
+ r2=5,
+ r3=2,
+ ),
+ filterLengths=0x542c47,
+ coeff_bulk=120,
+ coefficients=GreedyRange(Int32sl).parse(adt_dc.coefficients),
+ unk10=1,
+ micTurnOnTimeMs=20,
+ unk11=1,
+ micSettleTimeMs=50,
+)
+
+decimator_config = Container(
+ latency=15,
+ ratios=Container(
+ r1=15,
+ r2=5,
+ r3=2,
+ ),
+ filterLengths=0x542c47,
+ coeff_bulk=120,
+ coefficients=GreedyRange(Int32sl).parse(adt_dc.coefficients),
+)
+
+class AFKEP_Hello(AFKEPMessage):
+ TYPE = 63, 48, Constant(0x80)
+ UNK = 7, 0
+
+class AFKEP_Hello_Ack(AFKEPMessage):
+ TYPE = 63, 48, Constant(0xa0)
+
+class EPICEndpoint(AFKRingBufEndpoint):
+ BUFSIZE = 0x1000
+
+ def __init__(self, *args, **kwargs):
+ self.seq = 0x0
+ self.wait_reply = False
+ self.ready = False
+ super().__init__(*args, **kwargs)
+
+ @msg_handler(0x80, AFKEP_Hello)
+ def Hello(self, msg):
+ self.rxbuf, self.rxbuf_dva = self.asc.ioalloc(self.BUFSIZE)
+ self.txbuf, self.txbuf_dva = self.asc.ioalloc(self.BUFSIZE)
+
+ self.send(AFKEP_Hello_Ack())
+
+ def handle_hello(self, hdr, sub, fd):
+ if sub.type != 0xc0:
+ return False
+
+ payload = fd.read()
+ name = payload.split(b"\0")[0].decode("ascii")
+ self.log(f"Hello! (endpoint {name})")
+ self.ready = True
+ return True
+
+ def handle_reply(self, hdr, sub, fd):
+ if self.wait_reply:
+ self.pending_call.read_resp(fd)
+ self.wait_reply = False
+ return True
+ return False
+
+ def handle_ipc(self, data):
+ fd = BytesIO(data)
+ hdr = EPICHeader.parse_stream(fd)
+ sub = EPICSubHeaderVer2.parse_stream(fd)
+
+ handled = False
+
+ if sub.category == EPICCategory.REPORT:
+ handled = self.handle_hello(hdr, sub, fd)
+ if sub.category == EPICCategory.REPLY:
+ handled = self.handle_reply(hdr, sub, fd)
+
+ if not handled and getattr(self, 'VERBOSE', False):
+ self.log(f"< 0x{hdr.channel:x} Type {hdr.type} Ver {hdr.version} Tag {hdr.seq}")
+ self.log(f" Len {sub.length} Ver {sub.version} Cat {sub.category} Type {sub.type:#x} Ts {sub.timestamp:#x}")
+ self.log(f" Unk1 {sub.unk1:#x} Unk2 {sub.unk2:#x}")
+ chexdump(fd.read())
+
+ def indirect(self, call, chan=0x1000000d, timeout=0.1):
+ tx = call.ARGS.build(call.args)
+ self.asc.iface.writemem(self.txbuf, tx[4:])
+
+ cmd = self.roundtrip(IndirectCall(
+ txbuf=self.txbuf_dva, txlen=len(tx) - 4,
+ rxbuf=self.rxbuf_dva, rxlen=self.BUFSIZE,
+ retcode=0,
+ ), category=EPICCategory.COMMAND, typ=call.TYPE)
+ fd = BytesIO()
+ fd.write(struct.pack("<I", cmd.rets.retcode))
+ fd.write(self.asc.iface.readmem(self.rxbuf, cmd.rets.rxlen))
+ fd.seek(0)
+ call.read_resp(fd)
+ return call
+
+ def roundtrip(self, call, chan=0x1000000d, timeout=0.3,
+ category=EPICCategory.NOTIFY, typ=None):
+ tx = call.ARGS.build(call.args)
+ fd = BytesIO()
+ fd.write(EPICHeader.build(Container(
+ channel=chan,
+ type=EPICType.NOTIFY,
+ version=2,
+ seq=self.seq,
+ )))
+ self.seq += 1
+ fd.write(EPICSubHeaderVer2.build(Container(
+ length=len(tx),
+ category=category,
+ type=typ or call.TYPE,
+ )))
+ fd.write(tx)
+
+ self.pending_call = call
+ self.wait_reply = True
+ self.send_ipc(fd.getvalue())
+
+ deadline = time.time() + timeout
+ while time.time() < deadline and self.wait_reply:
+ self.asc.work()
+ if self.wait_reply:
+ self.wait_reply = False
+ raise ASCTimeout("ASC reply timed out")
+
+ return call
+
+class SPUAppEndpoint(EPICEndpoint):
+ SHORT = "SPUAppep"
+
+class AccelEndpoint(EPICEndpoint):
+ SHORT = "accelep"
+
+class GyroEndpoint(EPICEndpoint):
+ SHORT = "gyroep"
+
+class UNK23Endpoint(EPICEndpoint):
+ SHORT = "unk23ep"
+
+class LASEndpoint(EPICEndpoint):
+ SHORT = "lasep"
+ #VERBOSE = True # <--- uncomment to see lid angle measurements
+
+class WakehintEndpoint(EPICEndpoint):
+ SHORT = "wakehintep"
+
+class UNK26Endpoint(EPICEndpoint):
+ SHORT = "unk26ep"
+
+class AudioEndpoint(EPICEndpoint):
+ SHORT = "audioep"
+
+
+class OSLogMessage(Register64):
+ TYPE = 63, 56
+
+class OSLog_Init(OSLogMessage):
+ TYPE = 63, 56, Constant(1)
+ UNK = 51, 0
+ DVA = 7, 0
+
+class AOPOSLogEndpoint(ASCBaseEndpoint):
+ BASE_MESSAGE = OSLogMessage
+ SHORT = "oslog"
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.started = False
+
+ @msg_handler(1, OSLog_Init)
+ def Init(self, msg):
+ self.iobuffer, self.iobuffer_dva = self.asc.ioalloc(0x1_0000)
+ self.send(OSLog_Init(DVA=self.iobuffer_dva//0x1000))
+ self.started = True
+ return True
+
+
+class AOPClient(StandardASC, AOPBase):
+ ENDPOINTS = {
+ 8: AOPOSLogEndpoint,
+
+ 0x20: SPUAppEndpoint,
+ 0x21: AccelEndpoint,
+ 0x22: GyroEndpoint,
+ 0x23: UNK23Endpoint,
+ 0x24: LASEndpoint,
+ 0x25: WakehintEndpoint,
+ 0x26: UNK26Endpoint,
+ 0x27: AudioEndpoint,
+ 0x28: EPICEndpoint,
+ 0x29: EPICEndpoint,
+ 0x2a: EPICEndpoint,
+ 0x2b: EPICEndpoint
+ }
+
+ def __init__(self, u, adtpath, dart=None):
+ node = u.adt[adtpath]
+ self.base = node.get_reg(0)[0]
+
+ AOPBase.__init__(self, u, node)
+ super().__init__(u, self.base, dart)
+
+p.dapf_init_all()
+
+dart = DART.from_adt(u, "/arm-io/dart-aop", iova_range=(0x2c000, 0x10_000_000))
+dart.initialize()
+dart.regs.TCR[0].set(BYPASS_DAPF=0, BYPASS_DART=0, TRANSLATE_ENABLE=1)
+dart.regs.TCR[7].set(BYPASS_DAPF=0, BYPASS_DART=0, TRANSLATE_ENABLE=1)
+dart.regs.TCR[15].val = 0x20100
+
+aop = AOPClient(u, "/arm-io/aop", dart)
+
+aop.update_bootargs({
+ 'p0CE': 0x20000,
+# 'laCn': 0x0,
+# 'tPOA': 0x1,
+})
+
+aop.verbose = 4
+
+def set_aop_audio_pstate(devid, pstate):
+ audep.roundtrip(SetDeviceProp(
+ devid=devid,
+ modifier=202,
+ data=Container(
+ devid=devid,
+ cookie=1,
+ target_pstate=pstate,
+ unk2=1,
+ )
+ )).check_retcode()
+
+try:
+ aop.boot()
+ for epno in range(0x20, 0x2c):
+ aop.start_ep(epno)
+
+ timeout = 10
+ while (not aop.audioep.ready) and timeout:
+ aop.work_for(0.1)
+ timeout -= 1
+
+ if not timeout:
+ raise Exception("Timed out waiting on audio endpoint")
+
+ print("Finished boot")
+
+ audep = aop.audioep
+
+ audep.roundtrip(AttachDevice(devid='pdm0')).check_retcode()
+ audep.indirect(SetDeviceProp(
+ devid='pdm0', modifier=200, data=pdm_config)
+ ).check_retcode()
+ audep.indirect(SetDeviceProp(
+ devid='pdm0', modifier=210, data=decimator_config)
+ ).check_retcode()
+ audep.roundtrip(AttachDevice(devid='hpai')).check_retcode()
+ audep.roundtrip(AttachDevice(devid='lpai')).check_retcode()
+ audep.roundtrip(SetDeviceProp(
+ devid='lpai', modifier=301, data=Container(unk1=7, unk2=7, unk3=1, unk4=7))
+ ).check_retcode()
+except KeyboardInterrupt:
+ pass
+except Exception:
+ print(traceback.format_exc())
+
+run_shell(locals(), poll_func=aop.work)
diff --git a/tools/proxyclient/experiments/audio_capture.py b/tools/proxyclient/experiments/audio_capture.py
new file mode 100755
index 0000000..d79cbd4
--- /dev/null
+++ b/tools/proxyclient/experiments/audio_capture.py
@@ -0,0 +1,132 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+import time
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+# audio_capture.py -- capture audio on jack microphone input (on M1 macs with cs42l83)
+#
+# sample usage with sox: (recoding can be loud!)
+#
+# ./audio_capture.py | sox -t raw -r 48000 -c 1 -e signed-int -b 32 -L - OUTPUT_FILE
+
+from m1n1.setup import *
+from m1n1.hw.dart import DART, DARTRegs
+from m1n1.hw.i2c import I2C
+from m1n1.hw.pmgr import PMGR
+from m1n1.hw.nco import NCO
+from m1n1.hw.admac import *
+from m1n1.hw.mca import *
+
+p.pmgr_adt_clocks_enable("/arm-io/i2c2")
+p.pmgr_adt_clocks_enable("/arm-io/admac-sio")
+p.pmgr_adt_clocks_enable("/arm-io/dart-sio")
+p.pmgr_adt_clocks_enable("/arm-io/mca-switch")
+p.pmgr_adt_clocks_enable("/arm-io/mca3")
+
+# reset AUDIO_P
+PS_AUDIO_P = PMGR(u).regs[0].PS4[10]
+PS_AUDIO_P.set(DEV_DISABLE=1)
+PS_AUDIO_P.set(RESET=1)
+PS_AUDIO_P.set(RESET=0)
+PS_AUDIO_P.set(DEV_DISABLE=0)
+
+dart_base, _ = u.adt["/arm-io/dart-sio"].get_reg(0)
+dart = DART(iface, DARTRegs(u, dart_base), util=u)
+dart.initialize()
+
+cl_no = 2
+
+admac = ADMAC(u, "/arm-io/admac-sio", dart, debug=True)
+dmachan = admac.chans[4*cl_no+1]
+dmachan.buswidth = E_BUSWIDTH.W_32BIT
+dmachan.framesize = E_FRAME.F_1_WORD
+
+nco = NCO(u, "/arm-io/nco")
+nco[cl_no].set_rate(6000000)
+nco[cl_no].enable()
+
+mca_switch1_base = u.adt["/arm-io/mca-switch"].get_reg(1)[0]
+mca_cl_base = u.adt["/arm-io/mca-switch"].get_reg(0)[0] + 0x4000*cl_no
+cl = MCACluster(u, mca_cl_base)
+
+regs, serdes = cl.regs, cl.rxa
+
+regs.SYNCGEN_STATUS.set(EN=0)
+regs.SYNCGEN_MCLK_SEL.val =(1 + cl_no)
+regs.SYNCGEN_HI_PERIOD.val = 0 # period minus one
+regs.SYNCGEN_LO_PERIOD.val = 0x7b # period minus one
+
+serdes.STATUS.set(EN=0)
+serdes.CONF.set(
+ NSLOTS=0,
+ SLOT_WIDTH=E_SLOT_WIDTH.W_32BIT,
+ BCLK_POL=1,
+ UNK1=1, UNK2=1,
+ SYNC_SEL=(1 + cl_no)
+)
+serdes.UNK1.val = 0x4
+
+serdes.BITDELAY.val = 1
+
+serdes.CHANMASK[0].val = 0xffff_ffff
+serdes.CHANMASK[1].val = 0xffff_fffe
+
+regs.PORT_ENABLES.set(CLOCK1=1, CLOCK2=1, DATA=0)
+regs.PORT_CLK_SEL.set(SEL=(cl_no + 1))
+regs.MCLK_STATUS.set(EN=1)
+regs.SYNCGEN_STATUS.set(EN=1)
+
+cs42l_addr = 0x48
+i2c2 = I2C(u, "/arm-io/i2c2")
+def cs42l_write(regaddr, val):
+ i2c2.write_reg(cs42l_addr, 0x0, [regaddr >> 8])
+ i2c2.write_reg(cs42l_addr, regaddr & 0xff, [val])
+
+p.write32(0x23d1f002c, 0x76a02)
+p.write32(0x23d1f002c, 0x76a03) # take jack codec out of reset
+
+cs42l_write(0x1009, 0x0) # FS_int = MCLK/250
+cs42l_write(0x1101, 0x7a) # power on
+cs42l_write(0x1103, 0x22) # power on ring sense
+cs42l_write(0x1107, 0x1) # SCLK present
+cs42l_write(0x1121, 0xa6) # Headset Switch Control
+cs42l_write(0x1129, 0x1) # Headset Clamp Disable
+cs42l_write(0x1205, 0x7c) # FSYNC period
+cs42l_write(0x1207, 0x20) # ASP Clock Configuration
+cs42l_write(0x1208, 0x12) # BITDELAY = 1
+cs42l_write(0x120c, 0x1) # SCLK_PREDIV = div-by-2
+cs42l_write(0x150a, 0x55) # PLL
+cs42l_write(0x151b, 0x1) # PLL
+cs42l_write(0x1501, 0x1) # power on PLL
+cs42l_write(0x1b70, 0xc3) # HSBIAS sense
+cs42l_write(0x1b71, 0xe0) # v-- headset
+cs42l_write(0x1b73, 0xc0)
+cs42l_write(0x1b74, 0x1f)
+cs42l_write(0x1b75, 0xb6)
+cs42l_write(0x1b76, 0x8f)
+cs42l_write(0x1b79, 0x0)
+cs42l_write(0x1b7a, 0xfc)
+cs42l_write(0x1c03, 0xc0) # HSBIAS
+cs42l_write(0x2506, 0xc) # ASP TX samp. rate
+cs42l_write(0x2609, 0x4c) # SRC output samp. rate
+cs42l_write(0x2901, 0x1) # ASP TX enable & size
+cs42l_write(0x2902, 0x1) # ASP TX channel enable
+
+time.sleep(0.01)
+
+cs42l_write(0x1201, 0x1) # transition to PLL clock
+
+# drain garbled samples (why are they garbled? i am not sure)
+time.sleep(0.5)
+
+dmachan.submit(buflen=0x4000)
+dmachan.enable()
+
+p.write32(mca_switch1_base + 0x8000*cl_no, 0x24800)
+serdes.STATUS.set(EN=1)
+
+while True:
+ while dmachan.can_submit():
+ dmachan.submit(buflen=0x4000)
+ sys.stdout.buffer.write(dmachan.poll())
diff --git a/tools/proxyclient/experiments/chickens.py b/tools/proxyclient/experiments/chickens.py
new file mode 100755
index 0000000..bc2b6ec
--- /dev/null
+++ b/tools/proxyclient/experiments/chickens.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+
+sys_regs = dict([
+ ("HID0", (3, 0, 15, 0, 0)),
+ ("HID1", (3, 0, 15, 1, 0)),
+ ("EHID20", (3, 0, 15, 1, 2)),
+ ("HID2", (3, 0, 15, 2, 0)),
+ ("HID3", (3, 0, 15, 3, 0)),
+ ("HID4", (3, 0, 15, 4, 0)),
+ ("EHID4", (3, 0, 15, 4, 1)),
+ ("HID5", (3, 0, 15, 5, 0)),
+ ("HID6", (3, 0, 15, 6, 0)),
+ ("HID7", (3, 0, 15, 7, 0)),
+ ("HID8", (3, 0, 15, 8, 0)),
+ ("HID9", (3, 0, 15, 9, 0)),
+ ("EHID9", (3, 0, 15, 9, 1)),
+ ("HID10", (3, 0, 15, 10, 0)),
+ ("EHID10", (3, 0, 15, 10, 1)),
+ ("HID11", (3, 0, 15, 11, 0)),
+])
+
+CYC_OVRD = (3, 5, 15, 5, 0)
+CYC_CFG = (3, 5, 15, 4, 0)
+
+L2C_ERR_STS = (3, 3, 15, 8, 0)
+
+s3_6_c15_c1_0 = (3, 6, 15, 1, 0)
+s3_6_c15_c1_6 = (3, 6, 15, 1, 6)
+
+s3_4_c15_c1_4 = (3, 4, 15, 1, 4)
+s3_4_c15_c5_0 = (3, 4, 15, 5, 0)
+
+h13e_chickenbits = [
+ ("EHID4", 0x100000000800, 0),
+ ("HID5", 0x2000000000000000, 0),
+ ("EHID10", 0x2000100000000, 0),
+ ("EHID20", 0x100, 0),
+ ("EHID9", 0, 0x20),
+ ("EHID20", 0x8000, 0),
+ ("EHID20", 0x10000, 0),
+ ("EHID20", 0x600000, 0),
+
+]
+
+tlbi_vmalle1 = 0xd508871f
+
+def h13e_init():
+ mpidr = u.mrs(MPIDR_EL1)
+ print("mpidr = 0x%x" % mpidr)
+
+ #print("OSLAR")
+ #u.msr(OSLAR_EL1, 0)
+ #print("s3_6_c15_c1_0")
+ #u.msr(s3_6_c15_c1_0, 1)
+ #print("tlbi_vmalle1")
+ #u.inst(tlbi_vmalle1)
+
+ ## This looks like APRR stuff?
+ #v = u.mrs(s3_6_c15_c1_6)
+ #print("s3_6_c15_c1_6 == 0x%x" % v)
+ #v = 0x2020a505f020f0f0
+ #print("s3_6_c15_c1_6 <= 0x%x" % v)
+ #u.msr(s3_6_c15_c1_6, v)
+
+ #u.msr(s3_6_c15_c1_0, 0)
+
+ for reg, setb, clearb in h13e_chickenbits:
+ v = u.mrs(sys_regs[reg])
+ print("%r == 0x%x" % (reg, v))
+ v &= ~clearb
+ v |= setb
+ print("%r <= 0x%x" % (reg, v))
+ u.msr(sys_regs[reg], v)
+
+ v = u.mrs(s3_4_c15_c5_0)
+ print("s3_4_c15_c5_0 == 0x%x" % v)
+ print("s3_4_c15_c5_0 <= 0x%x" % (mpidr & 0xff))
+ u.msr(s3_4_c15_c5_0, mpidr & 0xff)
+
+ u.msr(s3_4_c15_c1_4, 0x100)
+
+ v = u.mrs(CYC_OVRD)
+ print("CYC_OVRD == 0x%x" % v)
+ v &= ~0xf00000
+ print("CYC_OVRD <= 0x%x" % v)
+ u.msr(CYC_OVRD, v)
+
+ v = u.mrs(ACTLR_EL1)
+ print("ACTLR_EL1 == 0x%x" % v)
+ v |= 0x200
+ print("ACTLR_EL1 <= 0x%x" % v)
+ u.msr(ACTLR_EL1, v)
+
+ v = u.mrs(CYC_CFG)
+ print("CYC_CFG == 0x%x" % v)
+ v |= 0xc
+ print("CYC_CFG <= 0x%x" % v)
+ u.msr(CYC_CFG, v)
+
+ print("L2C_ERR_STS = %x" % u.mrs(L2C_ERR_STS))
+ u.msr(L2C_ERR_STS, 0)
+
+h13e_init()
diff --git a/tools/proxyclient/experiments/cpu_pstate_latencies.py b/tools/proxyclient/experiments/cpu_pstate_latencies.py
new file mode 100755
index 0000000..0acfc28
--- /dev/null
+++ b/tools/proxyclient/experiments/cpu_pstate_latencies.py
@@ -0,0 +1,239 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib, time
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+from m1n1 import asm
+
+p.smp_start_secondaries()
+
+tfreq = u.mrs(CNTFRQ_EL0)
+
+TEST_CPUS = [1, 4]
+
+CLUSTER_PSTATE = 0x20020
+CLUSTER_STATUS = 0x20050
+
+if u.adt["/chosen"].chip_id == 0x8103:
+ CREG = [
+ 0x210e00000,
+ 0x211e00000,
+ ]
+
+ MAX_PSTATE = [5, 15]
+
+elif u.adt["/chosen"].chip_id == 0x8112:
+ CREG = [
+ 0x210e00000,
+ 0x211e00000,
+ ]
+
+ MAX_PSTATE = [7, 17]
+
+code = u.malloc(0x1000)
+
+util = asm.ARMAsm(f"""
+bench:
+ mrs x1, CNTPCT_EL0
+1:
+ sub x0, x0, #1
+ cbnz x0, 1b
+
+ mrs x2, CNTPCT_EL0
+ sub x0, x2, x1
+ ret
+
+signal_and_write:
+ sev
+ mrs x2, CNTPCT_EL0
+ add x2, x2, #0x800
+1:
+ mrs x3, CNTPCT_EL0
+ sub x4, x3, x2
+ cbnz x4, 1b
+ str x1, [x0]
+ mov x0, x3
+ ret
+
+timelog:
+ mrs x2, s3_1_c15_c0_0 /* SYS_IMP_APL_PMCR0 */
+ orr x2, x2, #1
+ msr s3_1_c15_c0_0, x2
+ mov x2, #0xffffffffffffffff
+ msr s3_1_c15_c1_0, x2
+ isb
+ wfe
+1:
+ mrs x2, CNTPCT_EL0
+ mrs x3, s3_2_c15_c0_0
+ isb
+ stp x2, x3, [x0], #16
+ mov x4, #0x40
+2:
+ sub x4, x4, #1
+ cbnz x4, 2b
+ sub x1, x1, #1
+ cbnz x1, 1b
+
+ ret
+""", code)
+iface.writemem(code, util.data)
+p.dc_cvau(code, len(util.data))
+p.ic_ivau(code, len(util.data))
+
+def bench_cpu(idx, loops=10000000):
+ if idx == 0:
+ elapsed = p.call(util.bench, loops) / tfreq
+ else:
+ elapsed = p.smp_call_sync(idx, util.bench, loops) / tfreq
+ if elapsed == 0:
+ return 0
+ mhz = (loops / elapsed) / 1000000
+ return mhz
+
+def set_pstate(cluster, pstate):
+ p.mask64(CREG[cluster] + CLUSTER_PSTATE, 0x1f01f, (1<<25) | pstate | (pstate << 12))
+
+print()
+
+LOG_ITERS = 10000
+logbuf = u.malloc(LOG_ITERS * 16)
+
+def bench_latency(cluster, cpu, from_pstate, to_pstate, verbose=False):
+ set_pstate(cluster, from_pstate)
+ bench_cpu(cpu)
+
+ p.smp_call(cpu, util.timelog, logbuf, LOG_ITERS)
+ psreg = (p.read64(CREG[cluster] + CLUSTER_PSTATE) & ~0x1f001f) | (1<<25) | to_pstate | (to_pstate << 12)
+ tval = p.call(util.signal_and_write, CREG[cluster] + CLUSTER_PSTATE, psreg)
+ p.smp_wait(cpu)
+
+ logdata = iface.readmem(logbuf, LOG_ITERS * 16)
+ lts, lcyc = None, None
+
+ log = []
+ for i in range(LOG_ITERS):
+ ts, cyc = struct.unpack("<QQ", logdata [i*16:i*16+16])
+ log.append((ts, cyc))
+
+ off = 256
+
+ ts_0, cyc_0 = log[off]
+ ts_e, cyc_e = log[-1]
+ f_init = None
+ f_end = None
+ lts, lcyc = ts_0, cyc_0
+
+ inc = to_pstate > from_pstate
+
+ blip = 0
+ cnt = dts_sum = 0
+ for i in range(off, len(log)):
+ ts, cyc = log[i]
+ dts = ts - lts
+ dcyc = cyc - lcyc
+
+ cnt += 1
+ dts_sum += dts
+
+ blip = max(blip, dts)
+
+ if f_init is None and ts > tval:
+ tidx = i
+ f_init = (lcyc - cyc_0) / (lts - ts_0) * tfreq / 1000000
+ dts_init = dts_sum / cnt
+ if f_end is None and ts > (tval + ts_e) / 2:
+ f_end = (cyc_e - cyc) / (ts_e - ts) * tfreq / 1000000
+ cnt = dts_sum = 0
+
+ #if lts is not None:
+ #print(f"{i}: {ts}: {cyc} ({ts-lts}: {cyc-lcyc})")
+ #else:
+ #print(f"{i}: {ts}: {cyc}")
+ lts, lcyc = ts, cyc
+
+ dts_end = dts_sum / cnt
+
+ window = 32
+
+ if verbose:
+ print(f"Triggered at {tval}")
+
+ thresh = 2/ (1/f_init + 1/f_end)
+
+ for i in range(tidx, LOG_ITERS - window - 1):
+ ts0, cyc0 = log[i - window]
+ ts1, cyc1 = log[i + window]
+ f = (cyc1 - cyc0) / (ts1 - ts0) * tfreq / 1000000
+ if inc and (f > thresh) or ((not inc) and f < thresh):
+ tts = log[i][0]
+ tidx = i
+ if verbose:
+ print(f"Frequency transition at #{i} {tts}")
+ break
+
+ if verbose:
+ print(f"Initial frequency: {f_init:.2f}")
+ print(f"Final frequency: {f_end:.2f}")
+ print(f"Threshold: {thresh:.2f}")
+
+ for i in range(max(window, tidx - 10 * window), tidx + 10 * window):
+ ts0, cyc0 = log[i - window]
+ ts1, cyc1 = log[i + window]
+ lts, lcyc = log[i - 1]
+ ts, cyc = log[i]
+ f = (cyc1 - cyc0) / (ts1 - ts0) * tfreq / 1000000
+ print(f"{i}: {ts}: {cyc} ({ts-lts}: {cyc-lcyc}): {f:.2f}")
+
+ blip -= min(dts_init, dts_end)
+
+ return (tts - tval) / tfreq * 1000000000, blip / tfreq * 1000000000
+
+for cluster, creg in enumerate(CREG):
+ cpu = TEST_CPUS[cluster]
+
+ freqs = []
+
+ print(f"#### Cluster {cluster} ####")
+ print(" P-States:")
+ print(" ", end="")
+ for pstate in range(MAX_PSTATE[cluster] + 1):
+ set_pstate(cluster, pstate)
+ freq = int(round(bench_cpu(cpu)))
+ freqs.append(freq)
+ print(f"{pstate}:{freq}MHz", end=" ")
+ print()
+ print()
+
+ print(" To-> |", end="")
+ for to_pstate in range(1, MAX_PSTATE[cluster] + 1):
+ print(f" {freqs[to_pstate]:7d} |", end="")
+ print()
+ print(" From |", end="")
+ for to_pstate in range(1, MAX_PSTATE[cluster] + 1):
+ print(f"---------+", end="")
+ print()
+
+ maxblip = 0
+
+ for from_pstate in range(1, MAX_PSTATE[cluster] + 1):
+ print(f" {freqs[from_pstate]:4d} |", end="")
+ for to_pstate in range(1, MAX_PSTATE[cluster] + 1):
+ if from_pstate == to_pstate:
+ print(f" ******* |", end="")
+ continue
+ lat, blip = bench_latency(cluster, cpu, from_pstate, to_pstate)
+ print(f" {lat:7.0f} |", end="")
+ maxblip = max(maxblip, blip)
+ print()
+
+ print()
+ print(f"Maximum execution latency spike: {maxblip:.0f} ns")
+ print()
+
+print()
+
+#bench_latency(1, TEST_CPUS[1], 15, 14, True)
+
+
diff --git a/tools/proxyclient/experiments/cpu_pstates.py b/tools/proxyclient/experiments/cpu_pstates.py
new file mode 100755
index 0000000..7403775
--- /dev/null
+++ b/tools/proxyclient/experiments/cpu_pstates.py
@@ -0,0 +1,150 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib, time
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+from m1n1 import asm
+
+LOOPS = 10000000
+freq = u.mrs(CNTFRQ_EL0)
+
+CREG = [
+ 0x210e00000,
+ 0x211e00000,
+]
+
+CLUSTER_PSTATE = 0x20020
+
+# e-core pstates
+# 600 972 1332 1704 2064
+# p-core pstates
+# 600 828 1056 1284 1500 1728 1956 2184 2388 2592 2772 2988 3096 3144 3204
+
+code = u.malloc(0x1000)
+
+util = asm.ARMAsm("""
+bench:
+ mrs x1, CNTPCT_EL0
+1:
+ sub x0, x0, #1
+ cbnz x0, 1b
+
+ mrs x2, CNTPCT_EL0
+ sub x0, x2, x1
+ ret
+""", code)
+iface.writemem(code, util.data)
+p.dc_cvau(code, len(util.data))
+p.ic_ivau(code, len(util.data))
+
+def bench_cpu(idx):
+ if idx == 0:
+ elapsed = p.call(util.bench, LOOPS) / freq
+ else:
+ elapsed = p.smp_call_sync(idx, util.bench, LOOPS) / freq
+ if elapsed == 0:
+ return 0
+ mhz = (LOOPS / elapsed) / 1000000
+ return mhz
+
+print()
+
+e_pstate = p.read64(CREG[0] + CLUSTER_PSTATE)
+p_pstate = p.read64(CREG[1] + CLUSTER_PSTATE)
+
+print(f"E-Core pstate: {e_pstate:x}")
+print(f"P-Core pstate: {p_pstate:x}")
+
+#for cluster in range(2):
+ #print(f"Initializing cluster {cluster} (early)")
+
+ #p.write64(CREG[cluster] + 0x20660, 0x1000000015)
+ #p.write64(CREG[cluster] + 0x48000, 0)
+ #p.write64(CREG[cluster] + 0x48080, 0xa000000000000000)
+
+ #p.clear64(CREG[cluster] + CLUSTER_PSTATE, 1<<22)
+
+#p.set32(PMGR + 0x48000, 1)
+#p.set32(PMGR + 0x48c00, 1)
+#p.set32(PMGR + 0x48800, 1)
+#p.set32(PMGR + 0x48400, 1)
+
+CLUSTER_DVMR = 0x206b8
+CLUSTER_LIMIT2 = 0x40240
+CLUSTER_LIMIT3 = 0x40250
+CLUSTER_LIMIT1 = 0x48400
+
+PMGR_CPUGATING = 0x1c080
+CLUSTER_CTRL = 0x440f8
+CLUSTER_PSCTRL = 0x200f8
+
+for cluster in range(2):
+ print(f"Initializing cluster {cluster}")
+ ena = (1<<63)
+ val = p.read64(CREG[cluster] + CLUSTER_DVMR)
+ if cluster == 1:
+ ena |= (1<<32) | (1<<31)
+ if (val & ena) != ena:
+ print(f"DVMR: {val:#x} -> {val|ena:#x}")
+ p.set64(CREG[cluster] + CLUSTER_DVMR, ena) # CLUSTER_DVMR
+
+ #p.set64(CREG[cluster] + CLUSTER_LIMIT1, 1<<63)
+ #p.clear64(CREG[cluster] + CLUSTER_LIMIT2, 1<<63)
+ #p.set64(CREG[cluster] + CLUSTER_LIMIT3, 1<<63)
+
+ #p.set64(CREG[cluster] + CLUSTER_PSTATE, 0)
+
+ #p.set32(PMGR + PMGR_CPUGATING + 8 * cluster, 1<<31)
+
+ #p.write64(CREG[cluster] + CLUSTER_CTRL, 1)
+
+ #p.set64(CREG[cluster] + CLUSTER_PSCTRL, 1<<40)
+
+ #pstate = p.read64(CREG[cluster] + CLUSTER_PSTATE) & 0xf
+
+p.smp_start_secondaries()
+
+print("== Initial CPU frequencies ==")
+
+for cpu in range(8):
+ print(f"CPU {cpu}: {bench_cpu(cpu):.2f} MHz")
+
+def set_pstate(cluster, pstate):
+ # This really seems to be all that's needed
+
+ p.mask64(CREG[cluster] + CLUSTER_PSTATE, 0xf00f, (1<<25) | pstate | (pstate << 12))
+
+ # Optionally, adjust MCC performance in higher p-core pstates
+ if cluster == 1:
+ if pstate > 8:
+ p0, p1 = 0x133, 0x55555340
+ else:
+ p0, p1 = 0x813057f, 0x1800180
+
+ for lane in range(8):
+ p.write32(0x200200dc4 + lane * 0x40000, p0)
+ p.write32(0x200200dbc + lane * 0x40000, p1)
+
+ # This seems to be about notifying PMP
+ #p.write32(0x23b738004 + cluster*4, pstate)
+ #p.write32(0x23bc34000, 1 << cluster)
+
+set_pstate(1, 15)
+
+e_pstate = p.read64(CREG[0] + CLUSTER_PSTATE)
+p_pstate = p.read64(CREG[1] + CLUSTER_PSTATE)
+
+print(f"E-Core pstate: {e_pstate:x}")
+print(f"P-Core pstate: {p_pstate:x}")
+
+time.sleep(0.5)
+
+print("== Final CPU frequencies ==")
+
+#elapsed = p.smp_call(7, util.bench, 80000000)
+
+for cpu in range(8):
+ print(f"CPU {cpu}: {bench_cpu(cpu):.2f} MHz")
+
+#elapsed = p.smp_wait(7)
diff --git a/tools/proxyclient/experiments/dart_dump.py b/tools/proxyclient/experiments/dart_dump.py
new file mode 100755
index 0000000..f6e0e08
--- /dev/null
+++ b/tools/proxyclient/experiments/dart_dump.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+import struct
+
+from m1n1.setup import *
+from m1n1 import asm
+from m1n1.hw.dart import DART
+
+if len(sys.argv) > 1:
+ dart_name = sys.argv[1]
+else:
+ dart_name = "dart-disp0"
+
+dart = DART.from_adt(u, "arm-io/" + dart_name)
+dart.dump_all()
+dart.dart.regs.dump_regs()
diff --git a/tools/proxyclient/experiments/dcp.py b/tools/proxyclient/experiments/dcp.py
new file mode 100755
index 0000000..93d6167
--- /dev/null
+++ b/tools/proxyclient/experiments/dcp.py
@@ -0,0 +1,351 @@
+#!/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 m1n1.setup import *
+from m1n1.shell import run_shell
+from m1n1 import asm
+from m1n1.hw.dart import DART, DARTRegs
+from m1n1.fw.dcp.client import DCPClient
+from m1n1.fw.dcp.manager import DCPManager
+from m1n1.fw.dcp.ipc import ByRef
+from m1n1.proxyutils import RegMonitor
+
+disp_name = "/arm-io/disp0"
+
+external = hasattr(u.adt[disp_name], "external") and u.adt[disp_name].external != 0
+compat = u.adt[disp_name].compatible[0].split(",")[-1]
+
+mon = RegMonitor(u)
+
+if compat == 't8103':
+ #mon.add(0x230000000, 0x18000)
+ #mon.add(0x230018000, 0x4000)
+ #mon.add(0x230068000, 0x8000)
+ #mon.add(0x2300b0000, 0x8000)
+ #mon.add(0x2300f0000, 0x4000)
+ #mon.add(0x230100000, 0x10000)
+ #mon.add(0x230170000, 0x10000)
+ #mon.add(0x230180000, 0x1c000)
+ #mon.add(0x2301a0000, 0x10000)
+ #mon.add(0x2301d0000, 0x4000)
+ #mon.add(0x230230000, 0x10000)
+ #mon.add(0x23038c000, 0x10000)
+ #mon.add(0x230800000, 0x10000)
+ #mon.add(0x230840000, 0xc000)
+ #mon.add(0x230850000, 0x2000)
+ ##mon.add(0x230852000, 0x5000) # big curve / gamma table
+ #mon.add(0x230858000, 0x18000)
+ #mon.add(0x230870000, 0x4000)
+ #mon.add(0x230880000, 0x8000)
+ #mon.add(0x230894000, 0x4000)
+ #mon.add(0x2308a8000, 0x8000)
+ #mon.add(0x2308b0000, 0x8000)
+ #mon.add(0x2308f0000, 0x4000)
+ ##mon.add(0x2308fc000, 0x4000) # stats / RGB color histogram
+ #mon.add(0x230900000, 0x10000)
+ #mon.add(0x230970000, 0x10000)
+ #mon.add(0x230980000, 0x10000)
+ #mon.add(0x2309a0000, 0x10000)
+ #mon.add(0x2309d0000, 0x4000)
+ #mon.add(0x230a30000, 0x20000)
+ #mon.add(0x230b8c000, 0x10000)
+ #mon.add(0x231100000, 0x8000)
+ #mon.add(0x231180000, 0x4000)
+ #mon.add(0x2311bc000, 0x10000)
+ #mon.add(0x231300000, 0x8000)
+ ##mon.add(0x23130c000, 0x4000) # - DCP dart
+ #mon.add(0x231310000, 0x8000)
+ #mon.add(0x231340000, 0x8000)
+ ##mon.add(0x231800000, 0x8000) # breaks DCP
+ ##mon.add(0x231840000, 0x8000) # breaks DCP
+ ##mon.add(0x231850000, 0x8000) # something DCP?
+ ##mon.add(0x231920000, 0x8000) # breaks DCP
+ ##mon.add(0x231960000, 0x8000) # breaks DCP
+ ##mon.add(0x231970000, 0x10000) # breaks DCP
+ ##mon.add(0x231c00000, 0x10000) # DCP mailbox
+
+ mon.add(0x230845840, 0x40) # error regs
+
+def get_color_mode(mgr):
+ best_id = None
+ best_score = -1
+ for mode in mgr.dcpav_prop['ColorElements']:
+ if mode['IsVirtual']:
+ continue
+ if mode['Depth'] != 8:
+ continue
+ if mode['Score'] > best_score:
+ best_score = mode['Score']
+ best_id = mode['ID']
+ return best_id
+
+def get_timing_mode(mgr):
+ best_id = None
+ best_score = -1
+ for mode in mgr.dcpav_prop['TimingElements']:
+ if mode['IsVirtual']:
+ continue
+ if int(mode['Score']) > best_score:
+ best_score = int(mode['Score'])
+ best_id = int(mode['ID'])
+ return best_id
+
+mon.poll()
+
+dart = DART.from_adt(u, "arm-io/dart-dcp")
+disp_dart = DART.from_adt(u, "arm-io/dart-disp0")
+
+print("DCP DART:")
+dart.regs.dump_regs()
+print("DISP DART:")
+disp_dart.regs.dump_regs()
+
+dcp_addr = u.adt["arm-io/dcp"].get_reg(0)[0]
+dcp = DCPClient(u, dcp_addr, dart, disp_dart)
+dcp.dva_offset = getattr(u.adt["/arm-io/dcp"][0], "asc_dram_mask", 0)
+
+dcp.start()
+dcp.start_ep(0x37)
+dcp.dcpep.initialize()
+
+mgr = DCPManager(dcp.dcpep, compat)
+
+mon.poll()
+
+mgr.start_signal()
+
+mon.poll()
+
+mgr.get_color_remap_mode(6)
+mgr.enable_disable_video_power_savings(0)
+
+mgr.update_notify_clients_dcp([0,0,0,0,0,0,1,1,1,0,1,1,1,1])
+mgr.first_client_open()
+print(f"keep on: {mgr.isKeepOnScreen()}")
+print(f"main display: {mgr.is_main_display()}")
+assert mgr.setPowerState(1, False, ByRef(0)) == 0
+
+mon.poll()
+
+if external:
+ assert mgr.set_display_device(2) == 0
+else:
+ assert mgr.set_display_device(0) == 2
+assert mgr.set_parameter_dcp(14, [0], 1) == 0
+
+color_mode = get_color_mode(mgr)
+timing_mode = get_timing_mode(mgr)
+mgr.SetDigitalOutMode(color_mode, timing_mode)
+mon.poll()
+
+while mgr.iomfb_prop['DPTimingModeId'] != timing_mode:
+ print("Try re-setting mode")
+ mgr.SetDigitalOutMode(color_mode, timing_mode)
+ mon.poll()
+
+if external:
+ assert mgr.set_display_device(2) == 0
+else:
+ assert mgr.set_display_device(0) == 2
+assert mgr.set_parameter_dcp(14, [0], 1) == 0
+
+t = ByRef(b"\x00" * 0xc0c)
+assert mgr.get_gamma_table(t) == 2
+assert mgr.set_contrast(0) == 0
+assert mgr.setBrightnessCorrection(65536) == 0
+
+if external:
+ assert mgr.set_display_device(2) == 0
+else:
+ assert mgr.set_display_device(0) == 2
+assert mgr.set_parameter_dcp(14, [0], 1) == 0
+
+mon.poll()
+
+swapid = ByRef(0)
+
+def start():
+ # arg: IOUserClient
+ ret = mgr.swap_start(swapid, {
+ "addr": 0xFFFFFE1667BA4A00,
+ "unk": 0,
+ "flag1": 0,
+ "flag2": 1
+ })
+ assert ret == 0
+ print(f"swap ID: {swapid.val:#x}")
+
+start()
+
+mgr.set_matrix(9, [[1<<32, 0, 0],
+ [0, 1<<32, 0],
+ [0, 0, 1<<32]])
+mgr.setBrightnessCorrection(65536)
+mgr.set_parameter_dcp(3, [65536], 1)
+mgr.set_parameter_dcp(6, [65536], 1)
+
+width = mgr.display_width()
+height = mgr.display_height()
+
+surface_id = 3
+
+swap_rec = Container(
+ flags1 = 0x861202,
+ flags2 = 0x04,
+ swap_id = swapid.val,
+ surf_ids = [surface_id, 0, 0, 0],
+ src_rect = [[0, 0, width, height],[0,0,0,0],[0,0,0,0],[0,0,0,0]],
+ surf_flags = [1, 0, 0, 0],
+ surf_unk = [0, 0, 0, 0],
+ dst_rect = [[0, 0, width, height],[0,0,0,0],[0,0,0,0],[0,0,0,0]],
+ swap_enabled = 0x80000007,
+ swap_completed = 0x80000007,
+ bl_unk = 0x1,
+ bl_val = 0x58f058d0, # ~99 nits
+ bl_power = 0x40,
+)
+
+surf = Container(
+ is_tiled = False,
+ unk_1 = False,
+ unk_2 = False,
+ plane_cnt = 0,
+ plane_cnt2 = 0,
+ format = "BGRA",
+ xfer_func = 13,
+ colorspace = 1,
+ stride = width * 4,
+ pix_size = 4,
+ pel_w = 1,
+ pel_h = 1,
+ offset = 0,
+ width = width,
+ height = height,
+ buf_size = width * height * 4,
+ surface_id = surface_id,
+ has_comp = True,
+ has_planes = True,
+ has_compr_info = False,
+ unk_1f5 = 0,
+ unk_1f9 = 0,
+)
+
+compressed_surf = Container(
+ is_tiled = False,
+ unk_1 = False,
+ unk_2 = False,
+ plane_cnt = 2,
+ plane_cnt2 = 2,
+ format = 'b3a8',
+ unk_f = 0x00000000,
+ xfer_func = 13,
+ colorspace = 2,
+ stride = width,
+ pix_size = 1,
+ pel_w = 1,
+ pel_h = 1,
+ offset = 0,
+ width = width,
+ height = height,
+ buf_size = 0x00A36000,
+ unk_2d = 0,
+ unk_31 = 0,
+ surface_id = 5,
+ comp_types = [
+ Container(count = 0, types =[]),
+ Container(count = 0, types =[]),
+ ],
+ has_comp = True,
+ planes = [
+ Container(
+ width = width,
+ height = height,
+ base = 0,
+ offset = 0,
+ stride = 0x1e000,
+ size = 0x818000,
+ tile_size = 1024,
+ tile_w = 16,
+ tile_h = 16,
+ unk2 = 0x05,
+ ),
+ Container(
+ width = width,
+ height = height,
+ base = 0x818000,
+ offset = 0x818000,
+ stride = 0x7800,
+ size = 0x21e000,
+ tile_size = 256,
+ tile_w = 16,
+ tile_h = 16,
+ unk2 = 0x05,
+ )
+ ],
+ has_planes = True,
+ compression_info = [
+ unhex("""
+ 10 00 00 00 10 00 00 00 00 80 7F 00 00 00 00 00
+ 08 00 00 00 78 00 00 00 44 00 00 00 00 00 00 00
+ 03 00 00 00 00 00 00 00 AA AA AA 00 04 00 00 00
+ E0 01 00 AA
+ """),
+ unhex("""
+ 10 00 00 00 10 00 00 00 00 60 A1 00 00 80 81 00
+ 08 00 00 00 78 00 00 00 44 00 00 00 00 00 00 00
+ 03 00 00 00 00 00 00 00 AA AA AA 00 01 00 00 00
+ 78 00 00 AA
+ """),
+ ],
+ has_compr_info = True,
+ unk_1f5 = 0x100000,
+ unk_1f9 = 0x100000,
+)
+
+
+outB = ByRef(False)
+
+swaps = mgr.swaps
+
+mon.poll()
+
+fb_size = align_up(width * height * 4, 8 * 0x4000)
+print(f"Display {width}x{height}, fb size: {fb_size}")
+
+buf = u.memalign(0x4000, fb_size)
+
+colors = [0xDD0000, 0xFE6230, 0xFEF600, 0x00BB00, 0x009BFE, 0x000083, 0x30009B]
+
+
+for i, color in enumerate(colors):
+ lines = height // len(colors)
+ offset = i * lines * width * 4
+ p.memset32(buf + offset, color, lines * width * 4)
+
+iova = disp_dart.iomap(0, buf, fb_size)
+
+surfaces = [surf, None, None, None]
+#surfaces = [compressed_surf, None, None, None]
+surfAddr = [iova, 0, 0, 0]
+
+def submit():
+ swap_rec.swap_id = swapid.val
+ ret = mgr.swap_submit_dcp(swap_rec=swap_rec, surfaces=surfaces, surfAddr=surfAddr,
+ unkBool=False, unkFloat=0.0, unkInt=0, unkOutBool=outB)
+ print(f"swap returned {ret} / {outB}")
+
+ dcp.work()
+
+ if ret == 0:
+ while swaps == mgr.swaps:
+ dcp.work()
+ print("swap complete!")
+
+submit()
+
+run_shell(globals(), msg="Have fun!")
diff --git a/tools/proxyclient/experiments/dcp_iboot.py b/tools/proxyclient/experiments/dcp_iboot.py
new file mode 100755
index 0000000..9aed5d8
--- /dev/null
+++ b/tools/proxyclient/experiments/dcp_iboot.py
@@ -0,0 +1,108 @@
+#!/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 m1n1.setup import *
+from m1n1.shell import run_shell
+from m1n1 import asm
+from m1n1.hw.dart import DART, DARTRegs
+from m1n1.fw.dcp.iboot import DCPIBootClient, SurfaceFormat, EOTF, Transform, AddrFormat, Colorspace
+from m1n1.proxyutils import RegMonitor
+
+print(f"Framebuffer at {u.ba.video.base:#x}")
+
+p.display_shutdown(DCP_SHUTDOWN_MODE.QUIESCED)
+
+dart = DART.from_adt(u, "arm-io/dart-dcp")
+disp_dart = DART.from_adt(u, "arm-io/dart-disp0")
+#disp_dart.dump_all()
+
+dcp_addr = u.adt["arm-io/dcp"].get_reg(0)[0]
+dcp = DCPIBootClient(u, dcp_addr, dart, disp_dart)
+dcp.dva_offset = getattr(u.adt["/arm-io/dcp"][0], "asc_dram_mask", 0)
+
+dcp.start()
+dcp.start_ep(0x23)
+dcp.start_ep(0x24)
+
+dcp.iboot.wait_for("disp0")
+dcp.dptx.wait_for("dcpav0")
+dcp.dptx.wait_for("dcpdp0")
+
+#dcp.dptx.dcpav0.setPower(False)
+#dcp.dptx.dcpav0.forceHotPlugDetect()
+#dcp.dptx.dcpav0.setVirtualDeviceMode(0)
+#dcp.dptx.dcpav0.setPower(True)
+#dcp.dptx.dcpav0.wakeDisplay()
+#dcp.dptx.dcpav0.sleepDisplay()
+#dcp.dptx.dcpav0.wakeDisplay()
+
+print("Waiting for HPD...")
+while True:
+ hpd, ntim, ncolor = dcp.iboot.disp0.getModeCount()
+ if hpd:
+ break
+
+print("HPD asserted")
+
+print(f"Connected:{hpd} Timing modes:{ntim} Color modes:{ncolor}")
+dcp.iboot.disp0.setPower(True)
+
+timing_modes = dcp.iboot.disp0.getTimingModes()
+print("Timing modes:")
+print(timing_modes)
+
+color_modes = dcp.iboot.disp0.getColorModes()
+print("Color modes:")
+print(color_modes)
+
+timing_modes.sort(key=lambda c: (c.valid, c.width <= 1920, c.fps_int <= 60, c.width, c.height, c.fps_int, c.fps_frac))
+timing_mode = timing_modes[-1]
+
+color_modes.sort(key=lambda c: (c.valid, c.bpp <= 32, c.bpp, -int(c.colorimetry), -int(c.encoding), -int(c.eotf)))
+color_mode = color_modes[-1]
+
+print("Chosen timing mode:", timing_mode)
+print("Chosen color mode:", color_mode)
+
+dcp.iboot.disp0.setMode(timing_mode, color_mode)
+
+w, h = timing_mode.width, timing_mode.height
+
+layer = Container(
+ planes = [
+ Container(
+ addr = 0x013ec000,
+ stride = u.ba.video.stride,
+ addr_format = AddrFormat.PLANAR,
+ ),
+ Container(),
+ Container()
+ ],
+ plane_cnt = 1,
+ width = u.ba.video.width,
+ height = u.ba.video.height,
+ surface_fmt = SurfaceFormat.w30r,
+ colorspace = Colorspace.SCRGBFixed,
+ eotf = EOTF.GAMMA_SDR,
+ transform = Transform.NONE,
+)
+
+mw = min(w, u.ba.video.width)
+mh = min(h, u.ba.video.height)
+
+swap = dcp.iboot.disp0.swapBegin()
+print(swap)
+dcp.iboot.disp0.swapSetLayer(0, layer, (mw, mh, 0, 0), (mw, mh, 0, 0))
+dcp.iboot.disp0.swapEnd()
+#dcp.iboot.disp0.swapWait(swap.swap_id)
+
+run_shell(globals(), msg="Have fun!")
+
+# full shutdown!
+dcp.stop(1)
+p.pmgr_reset(0, "DISP0_CPU0")
diff --git a/tools/proxyclient/experiments/dcpext_iboot.py b/tools/proxyclient/experiments/dcpext_iboot.py
new file mode 100644
index 0000000..ceb60bd
--- /dev/null
+++ b/tools/proxyclient/experiments/dcpext_iboot.py
@@ -0,0 +1,126 @@
+#!/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 m1n1.setup import *
+from m1n1.shell import run_shell
+from m1n1 import asm
+from m1n1.hw.dart import DART, DARTRegs
+from m1n1.fw.dcp.iboot import DCPIBootClient, SurfaceFormat, EOTF, Transform, AddrFormat
+from m1n1.fw.dcp.dcpav import *
+from m1n1.proxyutils import RegMonitor
+
+dart = DART.from_adt(u, "arm-io/dart-dcpext0")
+disp_dart = DART.from_adt(u, "arm-io/dart-dispext0")
+#disp_dart.dump_all()
+
+dcp_addr = u.adt["arm-io/dcpext0"].get_reg(0)[0]
+dcp = DCPIBootClient(u, dcp_addr, dart, disp_dart)
+dcp.dva_offset = getattr(u.adt["/arm-io/dcpext0"][0], "asc_dram_mask", 0)
+
+dcp.start()
+dcp.start_ep(0x20)
+dcp.start_ep(0x23)
+dcp.start_ep(0x24)
+dcp.start_ep(0x27)
+dcp.start_ep(0x2a)
+
+dcp.system.wait_for("system")
+dcp.iboot.wait_for("disp0")
+dcp.dptx.wait_for("dcpav0")
+dcp.dptx.wait_for("dcpav1")
+dcp.dptx.wait_for("dcpdp0")
+dcp.dptx.wait_for("dcpdp1")
+dcp.dpport.wait_for("port0")
+dcp.dpport.wait_for("port1")
+
+dcp.system.wait_for("system")
+dcp.system.system.setProperty("gAFKConfigLogMask", 0xffff)
+
+print("Connect...")
+dcp.dpport.port0.open()
+dcp.dpport.port0.getLocation()
+dcp.dpport.port0.getLocation()
+dcp.dpport.port0.getUnit()
+# this triggers the power up message
+dcp.dpport.port0.displayRequest()
+# these seem to not work/do anything?
+dcp.dpport.port0.connectTo(True, ATC0, DPPHY, 0)
+
+#dcp.dcpav.controller.setPower(False)
+#dcp.dcpav.controller.forceHotPlugDetect()
+#dcp.dcpav.controller.setVirtualDeviceMode(0)
+#dcp.dcpav.controller.setPower(True)
+#dcp.dcpav.controller.wakeDisplay()
+#dcp.dcpav.controller.sleepDisplay()
+#dcp.dcpav.controller.wakeDisplay()
+
+print("Waiting for HPD...")
+while True:
+ hpd, ntim, ncolor = dcp.iboot.disp0.getModeCount()
+ if hpd:
+ break
+
+print("HPD asserted")
+
+print(f"Connected:{hpd} Timing modes:{ntim} Color modes:{ncolor}")
+dcp.iboot.disp0.setPower(True)
+
+timing_modes = dcp.iboot.disp0.getTimingModes()
+print("Timing modes:")
+print(timing_modes)
+
+color_modes = dcp.iboot.disp0.getColorModes()
+print("Color modes:")
+print(color_modes)
+
+timing_modes.sort(key=lambda c: (c.valid, c.width <= 1920, c.fps_int <= 60, c.width, c.height, c.fps_int, c.fps_frac))
+timing_mode = timing_modes[-1]
+
+color_modes.sort(key=lambda c: (c.valid, c.bpp <= 32, c.bpp, -int(c.colorimetry), -int(c.encoding), -int(c.eotf)))
+color_mode = color_modes[-1]
+
+print("Chosen timing mode:", timing_mode)
+print("Chosen color mode:", color_mode)
+
+dcp.iboot.disp0.setMode(timing_mode, color_mode)
+
+w, h = timing_mode.width, timing_mode.height
+
+layer = Container(
+ planes = [
+ Container(
+ addr = 0x013ec000,
+ stride = u.ba.video.stride,
+ addr_format = AddrFormat.PLANAR,
+ ),
+ Container(),
+ Container()
+ ],
+ plane_cnt = 1,
+ width = u.ba.video.width,
+ height = u.ba.video.height,
+ surface_fmt = SurfaceFormat.w30r,
+ colorspace = 2,
+ eotf = EOTF.GAMMA_SDR,
+ transform = Transform.NONE,
+)
+
+mw = min(w, u.ba.video.width)
+mh = min(h, u.ba.video.height)
+
+swap = dcp.iboot.disp0.swapBegin()
+print(swap)
+dcp.iboot.disp0.swapSetLayer(0, layer, (mw, mh, 0, 0), (mw, mh, 0, 0))
+dcp.iboot.disp0.swapEnd()
+#dcp.iboot.disp0.swapWait(swap.swap_id)
+
+run_shell(globals(), msg="Have fun!")
+
+# full shutdown!
+dcp.stop(1)
+p.pmgr_reset(0, "DISP0_CPU0")
diff --git a/tools/proxyclient/experiments/find_sprr_regs.py b/tools/proxyclient/experiments/find_sprr_regs.py
new file mode 100755
index 0000000..b3261db
--- /dev/null
+++ b/tools/proxyclient/experiments/find_sprr_regs.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+from m1n1.find_regs import *
+from m1n1 import asm
+
+p.iodev_set_usage(IODEV.FB, 0)
+
+if u.mrs(SPRR_CONFIG_EL1):
+ u.msr(GXF_CONFIG_EL12, 0)
+ u.msr(SPRR_CONFIG_EL12, 0)
+ u.msr(GXF_CONFIG_EL1, 0)
+ u.msr(SPRR_CONFIG_EL1, 0)
+
+# Set up HCR_EL2 for EL1, since we can't do it after enabling GXF
+u.inst("nop", call="el1")
+
+all_regs = set()
+for reg in [SPRR_CONFIG_EL1, GXF_CONFIG_EL1, SPRR_CONFIG_EL12, GXF_CONFIG_EL12]:
+ old_regs = set(find_regs(u, values=False))
+ u.msr(reg, 1)
+ el2_items = set(find_regs(u))
+ el2_vals = dict(el2_items)
+ new_regs = set(k for k, v in el2_items)
+
+ all_regs = all_regs.union(new_regs)
+
+ diff_regs = new_regs - old_regs
+
+ print(reg)
+ for r in sorted(diff_regs):
+ print(" %s --> %lx" % (sysreg_name(r), u.mrs(r)))
+
+gl2_items = list(find_regs(u, regs=static_regs,call="gl2"))
+gl2_vals = dict(gl2_items)
+gl2_regs = set(k for k, v in gl2_items)
+
+print("GL2")
+for reg in sorted(gl2_regs - all_regs):
+ print(" %s -> %lx" % (sysreg_name(reg), gl2_vals[reg]))
+for reg in sorted(gl2_regs):
+ if reg in el2_vals and gl2_vals[reg] != el2_vals[reg]:
+ print(" ! %s %lx -> %lx" % (sysreg_name(reg), el2_vals[reg], gl2_vals[reg]))
+
+u.msr(GXF_CONFIG_EL12, 0)
+u.msr(SPRR_CONFIG_EL12, 0)
+u.msr(GXF_CONFIG_EL1, 0)
+u.msr(SPRR_CONFIG_EL1, 0)
+
+gl1_items = list(find_regs(u, regs=static_regs, call="gl1"))
+gl1_vals = dict(gl1_items)
+gl1_regs = set(k for k, v in gl1_items)
+
+print("GL1")
+for reg in sorted(gl1_regs - all_regs):
+ val = gl1_vals[reg]
+ print(" %s -> %lx" % (sysreg_name(reg), val))
+
+ cval = u.mrs(reg, call="gl1", silent=False)
+ print(" cur: 0x%lx" % (cval))
+
+ try:
+ u.msr(reg, cval, call="gl1", silent=False)
+ except:
+ print(">RO")
+ continue
+
+ gl2_vals = dict(find_regs(u, regs=static_regs,call="gl2"))
+ u.msr(reg, cval ^ 0xffff, call="gl1", silent=True)
+
+ for r, v in find_regs(u, regs=static_regs, call="gl2"):
+ if v != gl2_vals[r]:
+ print(" GL2 access: %s %lx -> %lx" % (sysreg_name(r), gl2_vals[r], v))
+
+ u.msr(reg, cval, call="gl1", silent=True)
+
+for reg in sorted(gl1_regs):
+ if reg in el2_vals and gl1_vals[reg] != el2_vals[reg]:
+ print(" ! %s %lx -> %lx" % (sysreg_name(reg), el2_vals[reg], gl1_vals[reg]))
diff --git a/tools/proxyclient/experiments/fptest.py b/tools/proxyclient/experiments/fptest.py
new file mode 100755
index 0000000..6c45bdc
--- /dev/null
+++ b/tools/proxyclient/experiments/fptest.py
@@ -0,0 +1,114 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+from m1n1 import asm
+
+FPCR_FZ = 1 << 24
+
+ACTLR_DEFAULT = 0xc00
+ACTLR_AFP = 1 << 5
+
+AFPCR = (3,6,15,2,5)
+AFPCR_DAZ = 1 << 0
+AFPCR_FTZ = 1 << 1
+
+code_buffer = p.malloc(0x1000)
+data_buffer = p.malloc(0x1000)
+
+code = asm.ARMAsm("""
+ ldr s0, [x0, #0]
+ ldr s1, [x0, #4]
+ fmul s0, s1, s0
+ str s0, [x0, #8]
+
+ ldr s0, [x0, #12]
+ ldr s1, [x0, #16]
+ fmul s0, s1, s0
+ str s0, [x0, #20]
+
+ # to test EL0 access
+ # mrs x0, s3_6_c15_c2_5
+ ret
+""", code_buffer)
+
+iface.writemem(code_buffer, code.data)
+p.dc_cvau(code_buffer, code.len)
+p.ic_ivau(code_buffer, code.len)
+
+def test_denormals():
+
+ data = [
+ 0x00400000, # a denormal
+ 0x40000000, # 2
+ 0,
+ 0x00800000, # smallest non-denormal
+ 0x3f000000, # 0.5
+ 0,
+ ]
+
+ iface.writemem(data_buffer, struct.pack("<%dI" % len(data), *data))
+
+ p.set_exc_guard(GUARD.SKIP)
+ ret = p.el0_call(code_buffer, data_buffer | REGION_RW_EL0)
+ p.set_exc_guard(GUARD.OFF)
+
+ v1 = p.read32(data_buffer + 8)
+ v2 = p.read32(data_buffer + 20)
+
+ print(" Input:", end=" ")
+ if v1 == 0:
+ print("FLUSH ", end=" ")
+ elif v1 == 0x00800000:
+ print("NORMAL", end=" ")
+ else:
+ print("0x08x?" % v1, end=" ")
+
+ print("Output:", end=" ")
+ if v2 == 0:
+ print("FLUSH ", end=" ")
+ elif v2 == 0x00400000:
+ print("NORMAL", end=" ")
+ else:
+ print("0x08x?" % v2, end=" ")
+ print("r = 0x%x" % ret)
+
+
+print("Testing normal mode")
+u.msr(ACTLR_EL1, ACTLR_DEFAULT)
+u.msr(AFPCR, 0)
+
+u.msr(FPCR, 0)
+print("FPCR.FZ = 0")
+test_denormals()
+
+u.msr(FPCR, FPCR_FZ)
+print("FPCR.FZ = 1")
+test_denormals()
+
+print()
+print("Testing Apple mode")
+u.msr(ACTLR_EL1, ACTLR_DEFAULT | ACTLR_AFP)
+u.msr(AFPCR, 0)
+
+u.msr(FPCR, 0)
+print("FPCR.FZ = 0")
+test_denormals()
+
+u.msr(FPCR, FPCR_FZ)
+print("FPCR.FZ = 1")
+test_denormals()
+
+u.msr(AFPCR, AFPCR_DAZ)
+print("AFPCR.<FTZ, DAZ> = 0, 1")
+test_denormals()
+
+u.msr(AFPCR, AFPCR_FTZ)
+print("AFPCR.<FTZ, DAZ> = 1, 0")
+test_denormals()
+
+u.msr(AFPCR, AFPCR_FTZ | AFPCR_DAZ)
+print("AFPCR.<FTZ, DAZ> = 1, 1")
+test_denormals()
diff --git a/tools/proxyclient/experiments/hacr_trap_bits.py b/tools/proxyclient/experiments/hacr_trap_bits.py
new file mode 100755
index 0000000..7982c33
--- /dev/null
+++ b/tools/proxyclient/experiments/hacr_trap_bits.py
@@ -0,0 +1,128 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+from m1n1 import asm
+
+code_len = 12 * 16 * 8 + 4
+data_len = 8 * 16 * 8
+
+if u.mrs(SPRR_CONFIG_EL1):
+ u.msr(GXF_CONFIG_EL12, 0)
+ u.msr(SPRR_CONFIG_EL12, 0)
+ u.msr(GXF_CONFIG_EL1, 0)
+ u.msr(SPRR_CONFIG_EL1, 0)
+
+u.msr(HACR_EL2, 0)
+
+hcr = HCR(u.mrs(HCR_EL2))
+hcr.TIDCP = 0
+hcr.TGE = 0
+u.msr(HCR_EL2, hcr.value)
+u.inst(0xd5033fdf) # isb
+
+ACTLR_DEFAULT = 0xc00
+ACTLR_AFP = 1 << 5
+u.msr(ACTLR_EL1, ACTLR_DEFAULT | ACTLR_AFP)
+
+code_buffer = p.malloc(code_len)
+data_buffer = p.malloc(data_len)
+
+template = asm.ARMAsm("""
+ mov x2, x0
+ mrs x2, s3_0_c0_c0_0
+ str x2, [x1], #8
+ ret
+""", code_buffer)
+
+mov, mrs, st, ret = struct.unpack("4I", template.data)
+
+data = []
+
+BAD = 0xacce5515abad1dea
+
+AUX = [
+ ACTLR_EL1,
+ ACTLR_EL2,
+ AFSR0_EL1,
+ AFSR0_EL2,
+ AFSR1_EL1,
+ AFSR1_EL2,
+ AIDR_EL1,
+ AIDR2_EL1,
+ AMAIR_EL1,
+ AMAIR_EL2,
+ APCTL_EL1,
+ APSTS_EL1,
+]
+
+def test():
+ u.msr(SPRR_CONFIG_EL1, 1)
+ u.msr(GXF_CONFIG_EL1, 1)
+ u.msr(SPRR_CONFIG_EL12, 1)
+ u.msr(GXF_CONFIG_EL12, 1)
+
+ for op1 in range(1 << 3):
+ for CRn in (0b1011, 0b1111):
+ mrs0 = mrs | (op1 << 16) | (CRn << 12)
+ insns = []
+ for CRm in range(1 << 4):
+ for op2 in range(1 << 3):
+ insns.extend((mov, mrs0 | (CRm << 8) | (op2 << 5), st))
+ insns.append(ret)
+ iface.writemem(code_buffer, struct.pack("<385I", *insns))
+ p.dc_cvau(code_buffer, code_len)
+ p.ic_ivau(code_buffer, code_len)
+
+ p.set_exc_guard(GUARD.SILENT | GUARD.SKIP)
+ p.el1_call(code_buffer, BAD, data_buffer)
+ cnt = p.get_exc_count()
+
+ data = iface.readmem(data_buffer, data_len)
+ d = struct.unpack("<128Q", data)
+ i = 0
+ for CRm in range(1 << 4):
+ for op2 in range(1 << 3):
+ v = d[i]
+ if v != BAD:
+ yield (3, op1, CRn, CRm, op2)
+ i += 1
+ for enc in AUX:
+ try:
+ v = u.mrs(enc, call="el1", silent=True)
+ if v != BAD:
+ yield enc
+ except:
+ continue
+
+ u.msr(GXF_CONFIG_EL12, 0)
+ u.msr(SPRR_CONFIG_EL12, 0)
+ u.msr(GXF_CONFIG_EL1, 0)
+ u.msr(SPRR_CONFIG_EL1, 0)
+
+baseline = set(test())
+
+for bit in range(64):
+ print()
+ print ("## HACR_EL2[%d]" % bit)
+ u.msr(HACR_EL2, 1<<bit)
+ u.inst(0xd5033fdf) # isb
+
+ new = set(test())
+
+ added = new - baseline
+ removed = baseline - new
+
+ if added:
+ print("Untraps:")
+ for enc in sorted(added):
+ print(f"{sysreg_name(enc)} ({', '.join(str(i) for i in enc)})")
+
+ if removed:
+ print("Traps:")
+ for enc in sorted(removed):
+ print(f"{sysreg_name(enc)} ({', '.join(str(i) for i in enc)})")
+
+p.set_exc_guard(GUARD.OFF)
diff --git a/tools/proxyclient/experiments/i2c.py b/tools/proxyclient/experiments/i2c.py
new file mode 100755
index 0000000..4100964
--- /dev/null
+++ b/tools/proxyclient/experiments/i2c.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+import struct
+
+from m1n1.setup import *
+from m1n1 import asm
+
+base = 0x235010000
+
+# register defines from https://github.com/torvalds/linux/blob/master/drivers/i2c/busses/i2c-pasemi.c
+# Copyright (C) 2006-2007 PA Semi, Inc
+# SMBus host driver for PA Semi PWRficient
+REG_MTXFIFO = 0x00
+REG_MRXFIFO = 0x04
+REG_SMSTA = 0x14
+REG_CTL = 0x1c
+
+MTXFIFO_READ = 0x00000400
+MTXFIFO_STOP = 0x00000200
+MTXFIFO_START = 0x00000100
+MTXFIFO_DATA_M = 0x000000ff
+
+MRXFIFO_EMPTY = 0x00000100
+MRXFIFO_DATA_M = 0x000000ff
+
+SMSTA_XEN = 0x08000000
+SMSTA_MTN = 0x00200000
+
+CTL_MRR = 0x00000400
+CTL_MTR = 0x00000200
+CTL_CLK_M = 0x000000ff
+
+CLK_100K_DIV = 84
+CLK_400K_DIV = 21
+
+
+def i2c_read_reg(addr, reg, reg_size):
+ p.set32(base + REG_CTL, CTL_MTR | CTL_MRR)
+ p.write32(base + REG_SMSTA, 0xffffffff)
+
+ p.write32(base + REG_MTXFIFO, MTXFIFO_START | (addr << 1))
+ p.write32(base + REG_MTXFIFO, MTXFIFO_STOP | reg)
+
+ while not (p.read32(base + REG_SMSTA) & SMSTA_XEN):
+ pass
+
+ p.write32(base + REG_MTXFIFO, MTXFIFO_START | (addr << 1) | 1)
+ p.write32(base + REG_MTXFIFO, MTXFIFO_READ | MTXFIFO_STOP | reg_size + 1)
+
+ res = []
+ while len(res) < reg_size+1:
+ v = p.read32(base + REG_MRXFIFO)
+ if v & 0x100:
+ continue
+ res.append(v)
+
+ if res[0] < reg_size:
+ print("only read %d instead of %d bytes" % (res[0], reg_size))
+ return res[1:]
+
+
+def i2c_write_reg(addr, reg, data):
+ p.set32(base + REG_CTL, CTL_MTR | CTL_MRR)
+ p.write32(base + REG_SMSTA, 0xffffffff)
+
+ p.write32(base + REG_MTXFIFO, MTXFIFO_START | (addr << 1))
+ p.write32(base + REG_MTXFIFO, reg)
+ for i in range(len(data)-1):
+ p.write32(base + REG_MTXFIFO, data[i])
+ p.write32(base + REG_MTXFIFO, data[-1] | MTXFIFO_STOP)
+
+ while not (p.read32(base + REG_SMSTA) & SMSTA_XEN):
+ pass
+
+
+def i2c_read16(addr, reg):
+ data = struct.pack(">2b", *i2c_read_reg(addr, reg, 2))
+ return struct.unpack(">H", data)[0]
+
+
+def i2c_read32(addr, reg):
+ data = struct.pack(">4b", *i2c_read_reg(addr, reg, 4))
+ return struct.unpack(">I", data)[0]
+
+
+def tps6598x_exec_cmd(addr, cmd, data_in, out_len):
+ if data_in:
+ data = [len(data_in)] + data_in
+
+ # TPS_REG_DATA1
+ i2c_write_reg(addr, 0x09, data)
+
+ # TPS_REG_CMD1
+ cmd = [4] + list(map(ord, cmd))
+ i2c_write_reg(addr, 0x08, cmd)
+
+ # TPS_REG_CMD1
+ v = i2c_read32(addr, 0x08)
+ while v != 0:
+ if v == 0x21434d44: # !CMD
+ raise Exception("Invalid command!")
+ v = i2c_read32(addr, 0x08)
+
+ if not out_len:
+ return
+
+ # TPS_REG_DATA1
+ return i2c_read_reg(addr, 0x09, out_len)
+
+
+print("make sure to run pmgr_adt_clocks_enable for /arm-io/i2c0 before this script.")
+
+# apple-specific command to bring the power state to zero
+# (or any other value specified as an argument)
+tps6598x_exec_cmd(0x3f, "SSPS", [0], 0)
+tps6598x_exec_cmd(0x38, "SSPS", [0], 0)
+
+tps6598x_exec_cmd(0x3f, "SWDF", None, 0)
+tps6598x_exec_cmd(0x3f, "SWSr", None, 0)
+tps6598x_exec_cmd(0x38, "SWDF", None, 0)
+tps6598x_exec_cmd(0x38, "SWSr", None, 0)
diff --git a/tools/proxyclient/experiments/jpeg.py b/tools/proxyclient/experiments/jpeg.py
new file mode 100644
index 0000000..1e53a6b
--- /dev/null
+++ b/tools/proxyclient/experiments/jpeg.py
@@ -0,0 +1,1222 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+from m1n1.hw.dart import DART, DARTRegs
+from m1n1.hw.jpeg import *
+from m1n1.utils import *
+import argparse
+import struct
+import time
+from enum import IntEnum
+from PIL import Image, ImageDraw
+
+
+def divroundup(val, div):
+ return (val + div - 1) // div
+
+
+def yuv2rgb(y, u, v):
+ y -= 16
+ u -= 128
+ v -= 128
+
+ y /= 255
+ u /= 255
+ v /= 255
+
+ r = y + 1.13983 * v
+ g = y - 0.39465 * u - 0.58060 * v
+ b = y + 2.03211 * u
+
+ r = min(255, max(0, int(r * 255)))
+ g = min(255, max(0, int(g * 255)))
+ b = min(255, max(0, int(b * 255)))
+
+ return (r, g, b)
+
+
+def rgb2yuv(r, g, b):
+ r /= 255
+ g /= 255
+ b /= 255
+
+ y = 0.299*r + 0.587*g + 0.114*b
+ u = -0.14713*r - 0.28886*g + 0.436*b
+ v = 0.615*r - 0.51499*g - 0.10001*b
+
+ y = y * 255 + 16
+ u = u * 255 + 128
+ v = v * 255 + 128
+
+ y = min(255, max(0, int(y)))
+ u = min(255, max(0, int(u)))
+ v = min(255, max(0, int(v)))
+
+ return (y, u, v)
+
+
+ap = argparse.ArgumentParser(description='JPEG block experiment')
+ap.add_argument("--jpeg", dest='which_jpeg', type=str, default='jpeg0',
+ help='which JPEG instance (jpeg0/jpeg1)')
+g = ap.add_mutually_exclusive_group(required=True)
+g.add_argument("-e", "--encode", action='store_true')
+g.add_argument("-d", "--decode", action='store_true')
+ap.add_argument("--raw-output", type=str, required=False)
+ap.add_argument("--decode-scale", type=int, required=False, default=1)
+ap.add_argument("--decode-pixelfmt", type=str, required=False, default='RGBA')
+ap.add_argument("--decode-rgba-alpha", type=int, required=False, default=255)
+ap.add_argument("--encode-subsampling", type=str, required=False, default='444')
+ap.add_argument("--encode-rst-interval", type=int, required=False)
+ap.add_argument("--encode-pixelfmt", type=str, required=False, default='RGB888')
+ap.add_argument("input", type=str)
+ap.add_argument("output", type=str)
+args = ap.parse_args()
+
+# print(args)
+
+# Perform necessary pre-parsing
+if args.decode:
+ assert args.decode_scale in [1, 2, 4, 8]
+ decode_scale = args.decode_scale
+ # FIXME: verify behavior on non-evenly-divisible sizes
+
+ assert args.decode_pixelfmt in [
+ 'RGBA',
+ 'BGRA',
+ 'RGB565',
+ 'YUV422-CbYCrY',
+ 'YUV422-YCbYCr',
+ 'YUV422-planar',
+ 'YUV420-planar',
+ 'YUV444-planar',
+ ]
+ pixfmt = args.decode_pixelfmt
+
+ with open(args.input, 'rb') as f:
+ jpeg_data = f.read()
+
+ found_sof0 = False
+
+ jpeg_work = jpeg_data
+ while jpeg_work:
+ seg_marker = struct.unpack(">H", jpeg_work[:2])[0]
+ print(f"Seg {seg_marker:04X}")
+ if seg_marker == 0xFFD8:
+ # SOI
+ jpeg_work = jpeg_work[2:]
+ elif seg_marker == 0xFFDA:
+ # SOS
+ break
+ else:
+ seg_len = struct.unpack(">H", jpeg_work[2:4])[0]
+ assert seg_len >= 2
+ seg_data = jpeg_work[4:4 + seg_len - 2]
+ jpeg_work = jpeg_work[4 + seg_len - 2:]
+
+ if seg_marker == 0xFFC0:
+ # SOF0
+ assert not found_sof0
+ found_sof0 = True
+ sof0 = struct.unpack(">BHHB", seg_data[:6])
+ (jpeg_bpp, jpeg_H, jpeg_W, jpeg_components_cnt) = sof0
+ # it is not yet verified what the requirements are for inputs
+ assert jpeg_bpp == 8
+ assert jpeg_components_cnt == 1 or jpeg_components_cnt == 3
+ if jpeg_components_cnt == 1:
+ jpeg_MODE = '400'
+ else:
+ jpeg_components = {}
+ for i in range(jpeg_components_cnt):
+ comp_id, comp_sampling, _ = seg_data[6+3*i:6+3*(i+1)]
+ jpeg_components[comp_id] = comp_sampling
+ assert 1 in jpeg_components
+ comp_Y = jpeg_components[1]
+ assert 2 in jpeg_components
+ comp_Cb = jpeg_components[2]
+ assert 3 in jpeg_components
+ comp_Cr = jpeg_components[3]
+
+ if (comp_Y, comp_Cb, comp_Cr) == (0x11, 0x11, 0x11):
+ jpeg_MODE = '444'
+ elif (comp_Y, comp_Cb, comp_Cr) == (0x21, 0x11, 0x11):
+ jpeg_MODE = '422'
+ elif (comp_Y, comp_Cb, comp_Cr) == (0x22, 0x11, 0x11):
+ jpeg_MODE = '420'
+ elif (comp_Y, comp_Cb, comp_Cr) == (0x41, 0x11, 0x11):
+ jpeg_MODE = '411'
+ else:
+ # TODO: 422-vertical, others???
+ # Is it possible to implement them?
+ print("Unsupported subsampling mode")
+ assert False
+
+ assert found_sof0
+ print(f"JPEG is {jpeg_W}x{jpeg_H} with subsampling {jpeg_MODE}")
+
+ if jpeg_MODE == '444' or jpeg_MODE == '400':
+ macroblock_W, macroblock_H = 8, 8
+ elif jpeg_MODE == '422':
+ macroblock_W, macroblock_H = 16, 8
+ elif jpeg_MODE == '420':
+ macroblock_W, macroblock_H = 16, 16
+ elif jpeg_MODE == '411':
+ macroblock_W, macroblock_H = 32, 8
+ else:
+ assert False
+
+ # FIXME: Exactly how much extra memory do we need to allocate?
+ surface_W = divroundup(jpeg_W // decode_scale, macroblock_W) * macroblock_W
+ surface_H = divroundup(jpeg_H // decode_scale, macroblock_H) * macroblock_H
+ if pixfmt in ['RGBA', 'BGRA']:
+ BYTESPP = 4
+ elif pixfmt in ['RGB565', 'YUV422-CbYCrY', 'YUV422-YCbYCr']:
+ BYTESPP = 2
+ elif pixfmt in ['YUV422-planar', 'YUV420-planar', 'YUV444-planar']:
+ BYTESPP = 1
+ else:
+ assert False
+ surface_stride = surface_W * BYTESPP
+ surface_sz = surface_stride*surface_H
+
+ if pixfmt == 'YUV422-planar':
+ P1_MULW = 1 # FIXME UGLY
+ P1_DIVW = 1
+ P1_DIVH = 1
+ elif pixfmt == 'YUV420-planar':
+ P1_MULW = 1
+ P1_DIVW = 1
+ P1_DIVH = 2
+ elif pixfmt == 'YUV444-planar':
+ P1_MULW = 2
+ P1_DIVW = 1
+ P1_DIVH = 1
+ if pixfmt in ['YUV422-planar', 'YUV420-planar', 'YUV444-planar']:
+ surface_P1_W = surface_W * P1_MULW // P1_DIVW
+ surface_P1_H = surface_H // P1_DIVH
+ surface_P1_stride = surface_P1_W
+ surface_P1_off = surface_sz
+ surface_sz += surface_P1_stride*surface_P1_H
+ else:
+ surface_P1_stride = 0
+ surface_P1_off = 0
+
+ input_mem_sz = align_up(len(jpeg_data))
+ print(f"Using size {input_mem_sz:08X} for JPEG data")
+
+ output_mem_sz = align_up(surface_sz)
+ print(f"Using size {output_mem_sz:08X} for output image")
+else:
+ assert args.encode_subsampling in ['444', '422', '420', '400']
+ if args.encode_subsampling == '444' or args.encode_subsampling == '400':
+ macroblock_W, macroblock_H = 8, 8
+ elif args.encode_subsampling == '422':
+ macroblock_W, macroblock_H = 16, 8
+ elif args.encode_subsampling == '420':
+ macroblock_W, macroblock_H = 16, 16
+ else:
+ assert False
+
+ assert args.encode_pixelfmt in [
+ 'RGB888',
+ 'RGB101010',
+ 'RGB565',
+ 'YUV10',
+ 'YUV-linear',
+ 'YUV444-planar',
+ 'YUV422-planar',
+ 'YUV420-planar',
+ ]
+ pixfmt = args.encode_pixelfmt
+
+ # Driver doesn't support this either
+ if pixfmt == 'YUV-linear' and args.encode_subsampling == '444':
+ print("WARNING: This combination does not appear to work!!!")
+ if pixfmt == 'YUV422-planar' and args.encode_subsampling == '444':
+ print("WARNING: This combination does not appear to work!!!")
+ if pixfmt == 'YUV420-planar' and args.encode_subsampling == '444':
+ print("WARNING: This combination does not appear to work!!!")
+
+ image_data = b''
+ image_data_P1 = b''
+ with Image.open(args.input) as im:
+ im_W, im_H = im.size
+
+ if pixfmt != 'YUV420-planar':
+ for y in range(im_H):
+ for x in range(im_W):
+ r, g, b = im.getpixel((x, y))
+ if pixfmt == 'RGB888':
+ image_data += struct.pack("BBBB", r, g, b, 255)
+ elif pixfmt == 'RGB101010':
+ image_data += struct.pack("<I", (r << 2) | (g << 12) | (b << 22))
+ elif pixfmt == 'RGB565':
+ image_data += struct.pack("<H", (r >> 3) | ((g >> 2) << 5) | ((b >> 3) << 11))
+ elif pixfmt == 'YUV10':
+ # absolute garbage color space conversion
+ # for demonstration purposes only
+ y_, u_, v_ = rgb2yuv(r, g, b)
+ image_data += struct.pack("<I", (y_ << 2) | (u_ << 12) | (v_ << 22))
+ elif pixfmt == 'YUV-linear':
+ # garbage color space conversion, garbage subsampling
+ # for demonstration purposes only
+ y_, u_, v_ = rgb2yuv(r, g, b)
+ if x & 1 == 0:
+ color = u_
+ else:
+ color = v_
+ image_data += struct.pack("BB", y_, color)
+ elif pixfmt == 'YUV444-planar':
+ # garbage color space conversion
+ # for demonstration purposes only
+ y_, u_, v_ = rgb2yuv(r, g, b)
+ image_data += struct.pack("B", y_)
+ image_data_P1 += struct.pack("BB", u_, v_)
+ elif pixfmt == 'YUV422-planar':
+ # garbage color space conversion, garbage subsampling
+ # for demonstration purposes only
+ y_, u_, v_ = rgb2yuv(r, g, b)
+ if x & 1 == 0:
+ color = u_
+ else:
+ color = v_
+ image_data += struct.pack("B", y_)
+ image_data_P1 += struct.pack("B", color)
+ else:
+ assert False
+ else:
+ for y in range(im_H):
+ for x in range(im_W):
+ r, g, b = im.getpixel((x, y))
+ # garbage color space conversion, garbage subsampling
+ # for demonstration purposes only
+ y_, u_, v_ = rgb2yuv(r, g, b)
+ if x & 1 == 0:
+ color = u_
+ else:
+ color = v_
+ image_data += struct.pack("B", y_)
+ if y & 1 == 0:
+ image_data_P1 += struct.pack("B", color)
+
+ if pixfmt in ['RGB888', 'RGB101010', 'YUV10']:
+ BYTESPP = 4
+ BYTESPP_P1 = 0
+ P1_DIVH = 1
+ elif pixfmt in ['RGB565', 'YUV-linear']:
+ BYTESPP = 2
+ BYTESPP_P1 = 0
+ P1_DIVH = 1
+ elif pixfmt == 'YUV444-planar':
+ BYTESPP = 1
+ BYTESPP_P1 = 2
+ P1_DIVH = 1
+ elif pixfmt == 'YUV422-planar':
+ BYTESPP = 1
+ BYTESPP_P1 = 1
+ P1_DIVH = 1
+ elif pixfmt == 'YUV420-planar':
+ BYTESPP = 1
+ BYTESPP_P1 = 1
+ P1_DIVH = 2
+ else:
+ assert False
+ surface_stride = im_W * BYTESPP
+ surface_sz = surface_stride * im_H
+ surface_P1_off = surface_sz
+ print(f"Plane 1 offset at {surface_P1_off:08X}")
+ surface_P1_stride = im_W * BYTESPP_P1
+ surface_sz += surface_P1_stride * im_H // P1_DIVH
+ input_mem_sz = align_up(surface_sz)
+
+ output_mem_sz = input_mem_sz
+
+ print(f"Using size {input_mem_sz:08X} for input image")
+ print(f"Using size {output_mem_sz:08X} for output data")
+
+# Turn on the JPEG block
+p.pmgr_adt_clocks_enable(f'/arm-io/dart-{args.which_jpeg}')
+p.pmgr_adt_clocks_enable(f'/arm-io/{args.which_jpeg}')
+
+dart = DART.from_adt(u, f'/arm-io/dart-{args.which_jpeg}')
+dart.initialize()
+
+jpeg_base, _ = u.adt[f'/arm-io/{args.which_jpeg}'].get_reg(0)
+jpeg = JPEGRegs(u, jpeg_base)
+
+
+def reset_block():
+ jpeg.MODE.val = 0x100
+ jpeg.MODE.val = 0x13e
+
+ set_default_regs()
+
+ jpeg.MODE.val = 0x17f
+ for _ in range(10000):
+ v = jpeg.REG_0x1004.val
+ if v == 0:
+ break
+ print(f"reset 1 -- {v}")
+ if (v := jpeg.REG_0x1004.val) != 0:
+ print(f"reset 1 failed! -- {v}")
+ assert False
+
+ jpeg.RST_INTERVAL.val = 1
+ for _ in range(2500):
+ v = jpeg.RST_INTERVAL.val
+ if v == 1:
+ break
+ print(f"reset 2 -- {v}")
+ if (v := jpeg.RST_INTERVAL.val) != 1:
+ print(f"reset 2 failed! -- {v}")
+ assert False
+ jpeg.RST_INTERVAL.val = 0
+
+ jpeg.ENABLE_RST_LOGGING.val = 0
+ jpeg.REG_0x1a8.val = 0
+ jpeg.REG_0x1ac.val = 0
+ jpeg.REG_0x1b0.val = 0
+ jpeg.REG_0x1b4.val = 0
+ jpeg.REG_0x1bc.val = 0
+ jpeg.REG_0x1c0.val = 0
+ jpeg.REG_0x1c4.val = 0
+ jpeg.REG_0x1c8.val = 0
+ jpeg.REG_0x1cc.val = 0
+ jpeg.REG_0x1d0.val = 0
+ jpeg.REG_0x1d4.val = 0
+
+ jpeg.MODE.val = 0x143
+
+
+def set_default_regs(param1=0):
+ jpeg.REG_0x0.val = 0
+ jpeg.REG_0x0.val = 0
+ jpeg.REG_0x4.val = 0
+ jpeg.CODEC.val = 0
+ jpeg.REG_0x2c.val = 0
+ jpeg.REG_0x30.val = 0
+ jpeg.REG_0x34.val = 1
+ jpeg.REG_0x38.val = 1
+ jpeg.CHROMA_HALVE_H_TYPE1.val = 0
+ jpeg.CHROMA_HALVE_H_TYPE2.val = 0
+ jpeg.CHROMA_HALVE_V_TYPE1.val = 0
+ jpeg.CHROMA_HALVE_V_TYPE2.val = 0
+ jpeg.CHROMA_DOUBLE_H.val = 0
+ jpeg.CHROMA_QUADRUPLE_H.val = 0
+ jpeg.CHROMA_DOUBLE_V.val = 0
+ jpeg.PLANAR_CHROMA_HALVING.val = 0
+ jpeg.PX_USE_PLANE1.val = 0
+ jpeg.PX_TILES_W.val = 1
+ jpeg.PX_TILES_H.val = 1
+ jpeg.PX_PLANE0_WIDTH.val = 1
+ jpeg.PX_PLANE0_HEIGHT.val = 1
+ jpeg.PX_PLANE0_TILING_H.val = 1
+ jpeg.PX_PLANE0_TILING_V.val = 1
+ jpeg.PX_PLANE0_STRIDE.val = 1
+ jpeg.PX_PLANE1_WIDTH.val = 1
+ jpeg.PX_PLANE1_HEIGHT.val = 1
+ jpeg.PX_PLANE1_TILING_H.val = 1
+ jpeg.PX_PLANE1_TILING_V.val = 1
+ jpeg.PX_PLANE1_STRIDE.val = 1
+ jpeg.INPUT_START1.val = 0
+ jpeg.INPUT_START2.val = 0
+ jpeg.REG_0x94.val = 1
+ jpeg.REG_0x98.val = 1
+ jpeg.INPUT_END.val = 0xffffffff
+ jpeg.OUTPUT_START1.val = 0
+ jpeg.OUTPUT_START2.val = 0
+ jpeg.OUTPUT_END.val = 0xffffffff
+ for i in range(11):
+ jpeg.MATRIX_MULT[i].val = 0
+ for i in range(10):
+ jpeg.DITHER[i].val = 0xff
+ jpeg.ENCODE_PIXEL_FORMAT.val = 0
+ jpeg.ENCODE_COMPONENT0_POS.val = 0
+ jpeg.ENCODE_COMPONENT1_POS.val = 0
+ jpeg.ENCODE_COMPONENT2_POS.val = 0
+ jpeg.ENCODE_COMPONENT3_POS.val = 0
+ jpeg.CONVERT_COLOR_SPACE.val = 0
+ jpeg.REG_0x118.val = 0
+ jpeg.REG_0x11c.val = 0
+ jpeg.REG_0x120.val = 0
+ jpeg.TILING_ENABLE.val = 0
+ jpeg.TILING_PLANE0.val = 0
+ jpeg.TILING_PLANE1.val = 0
+ jpeg.DECODE_MACROBLOCKS_W.val = 0
+ jpeg.DECODE_MACROBLOCKS_H.val = 0
+ jpeg.SCALE_FACTOR.val = 0
+ jpeg.DECODE_PIXEL_FORMAT.val = 0
+ jpeg.YUV422_ORDER.val = 0
+ jpeg.RGBA_ORDER.val = 0
+ jpeg.RGBA_ALPHA.val = 0
+ jpeg.RIGHT_EDGE_PIXELS.val = 0
+ jpeg.BOTTOM_EDGE_PIXELS.val = 0
+ jpeg.RIGHT_EDGE_SAMPLES.val = 0
+ jpeg.BOTTOM_EDGE_SAMPLES.val = 0
+
+ # this is always done on the m1 max hwrev
+ jpeg.REG_0x1fc.val = 0
+ jpeg.REG_0x200.val = 0
+ jpeg.REG_0x204.val = 0
+ jpeg.REG_0x208.val = 0
+ jpeg.REG_0x214.val = 0
+ jpeg.REG_0x218.val = 0
+ jpeg.REG_0x21c.val = 0
+ jpeg.REG_0x220.val = 0
+ jpeg.REG_0x224.val = 0
+ jpeg.REG_0x228.val = 0
+ jpeg.REG_0x22c.val = 0
+ jpeg.REG_0x230.val = 0
+ jpeg.REG_0x234.val = 0x1f40
+ jpeg.REG_0x244.val = 0
+ jpeg.REG_0x248.val = 0
+ jpeg.REG_0x258.val = 0
+ jpeg.REG_0x25c.val = 0
+ jpeg.REG_0x23c.val = 0
+ jpeg.REG_0x240.val = 0
+ jpeg.REG_0x250.val = 0
+ jpeg.REG_0x254.val = 0
+
+ jpeg.REG_0x160.val = param1
+ jpeg.TIMEOUT.val = 0
+ jpeg.REG_0x20.val = 0xff
+
+
+print(f"HW revision is {jpeg.HWREV}")
+reset_block()
+
+input_buf_phys = u.heap.memalign(0x4000, input_mem_sz)
+output_buf_phys = u.heap.memalign(0x4000, output_mem_sz)
+print(f"buffers (phys) {input_buf_phys:016X} {output_buf_phys:016X}")
+
+input_buf_iova = dart.iomap(0, input_buf_phys, input_mem_sz)
+output_buf_iova = dart.iomap(0, output_buf_phys, output_mem_sz)
+print(f"buffers (iova) {input_buf_iova:08X} {output_buf_iova:08X}")
+# dart.dump_all()
+
+iface.writemem(input_buf_phys, b'\xAA' * input_mem_sz)
+iface.writemem(output_buf_phys, b'\xAA' * output_mem_sz)
+
+
+if args.decode:
+ iface.writemem(input_buf_phys, jpeg_data)
+ print("JPEG uploaded")
+
+ jpeg.REG_0x34 = 1
+ jpeg.REG_0x2c = 0
+ jpeg.REG_0x38 = 0
+ if jpeg_MODE == '444':
+ jpeg.CODEC.set(CODEC=E_CODEC._444)
+ elif jpeg_MODE == '400':
+ jpeg.CODEC.set(CODEC=E_CODEC._400)
+ elif jpeg_MODE == '422':
+ jpeg.CODEC.set(CODEC=E_CODEC._422)
+ elif jpeg_MODE == '420':
+ jpeg.CODEC.set(CODEC=E_CODEC._420)
+ elif jpeg_MODE == '411':
+ jpeg.CODEC.set(CODEC=E_CODEC._411)
+ else:
+ assert False
+ if pixfmt == 'RGBA' or pixfmt == 'BGRA':
+ jpeg.DECODE_PIXEL_FORMAT.set(FORMAT=E_DECODE_PIXEL_FORMAT.RGBA8888)
+ elif pixfmt == 'RGB565':
+ jpeg.DECODE_PIXEL_FORMAT.set(FORMAT=E_DECODE_PIXEL_FORMAT.RGB565)
+ elif pixfmt == 'YUV422-CbYCrY' or pixfmt == 'YUV422-YCbYCr':
+ jpeg.DECODE_PIXEL_FORMAT.set(FORMAT=E_DECODE_PIXEL_FORMAT.YUV422_linear)
+ elif pixfmt == 'YUV422-planar':
+ jpeg.DECODE_PIXEL_FORMAT.set(FORMAT=E_DECODE_PIXEL_FORMAT.YUV422_planar)
+ elif pixfmt == 'YUV420-planar':
+ jpeg.DECODE_PIXEL_FORMAT.set(FORMAT=E_DECODE_PIXEL_FORMAT.YUV420_planar)
+ elif pixfmt == 'YUV444-planar':
+ jpeg.DECODE_PIXEL_FORMAT.set(FORMAT=E_DECODE_PIXEL_FORMAT.YUV444_planar)
+ else:
+ assert False
+
+ if pixfmt in ['YUV422-planar', 'YUV420-planar', 'YUV444-planar']:
+ jpeg.PX_USE_PLANE1 = 1
+ jpeg.PX_PLANE1_WIDTH = jpeg_W * P1_MULW // P1_DIVW // decode_scale - 1
+ jpeg.PX_PLANE1_HEIGHT = jpeg_H // P1_DIVH // decode_scale - 1
+ else:
+ jpeg.PX_USE_PLANE1 = 0
+ jpeg.PX_PLANE0_WIDTH = jpeg_W*BYTESPP // decode_scale - 1
+ jpeg.PX_PLANE0_HEIGHT = jpeg_H // decode_scale - 1
+ jpeg.TIMEOUT = 266000000
+
+ jpeg.REG_0x94 = 0x1f
+ jpeg.REG_0x98 = 1
+
+ jpeg.DECODE_MACROBLOCKS_W = divroundup(jpeg_W, macroblock_W)
+ jpeg.DECODE_MACROBLOCKS_H = divroundup(jpeg_H, macroblock_H)
+ right_edge_px = \
+ jpeg_W - divroundup(jpeg_W, macroblock_W)*macroblock_W + macroblock_W
+ bot_edge_px = \
+ jpeg_H - divroundup(jpeg_H, macroblock_H)*macroblock_H + macroblock_H
+ # XXX changing this does not seem to do anything.
+ # Does it possibly affect scaling down?
+ jpeg.RIGHT_EDGE_PIXELS.val = right_edge_px
+ jpeg.BOTTOM_EDGE_PIXELS.val = bot_edge_px
+ jpeg.RIGHT_EDGE_SAMPLES.val = right_edge_px // (macroblock_W // 8)
+ jpeg.BOTTOM_EDGE_SAMPLES.val = bot_edge_px // (macroblock_H // 8)
+
+ jpeg.PX_TILES_H = divroundup(jpeg_H, macroblock_H)
+ # FIXME explain this
+ if pixfmt in ['RGBA', 'BGRA', 'RGB565', 'YUV444-planar']:
+ jpeg.PX_TILES_W = divroundup(jpeg_W // decode_scale, macroblock_W)
+ else:
+ jpeg.PX_TILES_W = divroundup(jpeg_W // decode_scale, max(macroblock_W, 16))
+ if pixfmt == 'RGBA' or pixfmt == 'BGRA':
+ if jpeg_MODE == '444' or jpeg_MODE == '400':
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 1
+ elif jpeg_MODE == '422':
+ jpeg.PX_PLANE0_TILING_H = 8
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 1
+ elif jpeg_MODE == '420':
+ jpeg.PX_PLANE0_TILING_H = 8
+ jpeg.PX_PLANE0_TILING_V = 16 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 0
+ jpeg.PX_PLANE1_TILING_V = 0
+ elif jpeg_MODE == '411':
+ jpeg.PX_PLANE0_TILING_H = 16
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 0
+ jpeg.PX_PLANE1_TILING_V = 0
+ else:
+ assert False
+ elif pixfmt == 'RGB565':
+ if jpeg_MODE == '444' or jpeg_MODE == '400':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 1
+ elif jpeg_MODE == '422':
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 1
+ elif jpeg_MODE == '420':
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 16 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 0
+ jpeg.PX_PLANE1_TILING_V = 0
+ elif jpeg_MODE == '411':
+ jpeg.PX_PLANE0_TILING_H = 8
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 0
+ jpeg.PX_PLANE1_TILING_V = 0
+ else:
+ assert False
+ elif pixfmt == 'YUV422-CbYCrY' or pixfmt == 'YUV422-YCbYCr':
+ if jpeg_MODE == '444' or jpeg_MODE == '400':
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 1
+ elif jpeg_MODE == '422':
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 1
+ elif jpeg_MODE == '420':
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 16 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 0
+ jpeg.PX_PLANE1_TILING_V = 0
+ elif jpeg_MODE == '411':
+ jpeg.PX_PLANE0_TILING_H = 8
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 0
+ jpeg.PX_PLANE1_TILING_V = 0
+ else:
+ assert False
+ elif pixfmt == 'YUV422-planar':
+ if jpeg_MODE == '444' or jpeg_MODE == '400':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 2
+ jpeg.PX_PLANE1_TILING_V = 8 // decode_scale
+ elif jpeg_MODE == '422':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 2
+ jpeg.PX_PLANE1_TILING_V = 8 // decode_scale
+ elif jpeg_MODE == '420':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 16 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 2
+ jpeg.PX_PLANE1_TILING_V = 16 // decode_scale
+ elif jpeg_MODE == '411':
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 4
+ jpeg.PX_PLANE1_TILING_V = 8 // decode_scale
+ else:
+ assert False
+ elif pixfmt == 'YUV420-planar':
+ if jpeg_MODE == '444' or jpeg_MODE == '400':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 2
+ jpeg.PX_PLANE1_TILING_V = 4 // decode_scale
+ elif jpeg_MODE == '422':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 2
+ jpeg.PX_PLANE1_TILING_V = 4 // decode_scale
+ elif jpeg_MODE == '420':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 16 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 2
+ jpeg.PX_PLANE1_TILING_V = 8 // decode_scale
+ elif jpeg_MODE == '411':
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 4
+ jpeg.PX_PLANE1_TILING_V = 4 // decode_scale
+ else:
+ assert False
+ elif pixfmt == 'YUV444-planar':
+ if jpeg_MODE == '444' or jpeg_MODE == '400':
+ jpeg.PX_PLANE0_TILING_H = 1
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 2
+ jpeg.PX_PLANE1_TILING_V = 8 // decode_scale
+ elif jpeg_MODE == '422':
+ # The driver doesn't use this, but guessing seems to be fine?
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 4
+ jpeg.PX_PLANE1_TILING_V = 8 // decode_scale
+ elif jpeg_MODE == '420':
+ # The driver doesn't use this, but guessing seems to be fine?
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 16 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 4
+ jpeg.PX_PLANE1_TILING_V = 16 // decode_scale
+ elif jpeg_MODE == '411':
+ # The driver doesn't use this, but guessing seems to be fine?
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 8 // decode_scale
+ jpeg.PX_PLANE1_TILING_H = 8
+ jpeg.PX_PLANE1_TILING_V = 8 // decode_scale
+ else:
+ assert False
+ else:
+ assert False
+
+ if pixfmt in ['RGBA', 'BGRA', 'RGB565', 'YUV444-planar']:
+ if jpeg_MODE in ['422', '420']:
+ jpeg.CHROMA_DOUBLE_H = 1
+
+ if jpeg_MODE == '411':
+ jpeg.CHROMA_QUADRUPLE_H = 1
+
+ if jpeg_MODE == '420':
+ jpeg.CHROMA_DOUBLE_V = 1
+ elif pixfmt in ["YUV422-CbYCrY", "YUV422-YCbYCr", "YUV422-planar"]:
+ if jpeg_MODE == '444':
+ jpeg.CHROMA_HALVE_H_TYPE1 = 1
+
+ if jpeg_MODE == '411':
+ jpeg.CHROMA_DOUBLE_H = 1
+
+ if jpeg_MODE == '420':
+ jpeg.CHROMA_DOUBLE_V = 1
+ elif pixfmt in ["YUV420-planar"]:
+ if jpeg_MODE == '444':
+ jpeg.CHROMA_HALVE_H_TYPE1 = 1
+
+ if jpeg_MODE in ['444', '422', '411']:
+ jpeg.CHROMA_HALVE_V_TYPE1 = 1
+
+ if jpeg_MODE == '411':
+ jpeg.CHROMA_DOUBLE_H = 1
+ else:
+ assert False
+
+ jpeg.MATRIX_MULT[0].val = 0x100
+ jpeg.MATRIX_MULT[1].val = 0x0
+ jpeg.MATRIX_MULT[2].val = 0x167
+ jpeg.MATRIX_MULT[3].val = 0x100
+ jpeg.MATRIX_MULT[4].val = 0xffffffa8
+ jpeg.MATRIX_MULT[5].val = 0xffffff49
+ jpeg.MATRIX_MULT[6].val = 0x100
+ jpeg.MATRIX_MULT[7].val = 0x1c6
+ jpeg.MATRIX_MULT[8].val = 0x0
+ jpeg.MATRIX_MULT[9].val = 0x0
+ jpeg.MATRIX_MULT[10].val = 0xffffff80
+
+ jpeg.RGBA_ALPHA = args.decode_rgba_alpha
+ jpeg.RGBA_ORDER = pixfmt == "RGBA"
+ jpeg.YUV422_ORDER = pixfmt == "YUV422-YCbYCr"
+
+ if decode_scale == 1:
+ jpeg.SCALE_FACTOR.set(SCALE=E_SCALE.DIV1)
+ elif decode_scale == 2:
+ jpeg.SCALE_FACTOR.set(SCALE=E_SCALE.DIV2)
+ elif decode_scale == 4:
+ jpeg.SCALE_FACTOR.set(SCALE=E_SCALE.DIV4)
+ elif decode_scale == 8:
+ jpeg.SCALE_FACTOR.set(SCALE=E_SCALE.DIV8)
+ else:
+ assert False
+
+ jpeg.INPUT_START1 = input_buf_iova
+ jpeg.INPUT_START2 = 0xdeadbeef
+ jpeg.INPUT_END = input_buf_iova + input_mem_sz
+ jpeg.OUTPUT_START1 = output_buf_iova
+ jpeg.OUTPUT_START2 = output_buf_iova + surface_P1_off
+ jpeg.OUTPUT_END = output_buf_iova + output_mem_sz
+ jpeg.PX_PLANE0_STRIDE = surface_stride
+ jpeg.PX_PLANE1_STRIDE = surface_P1_stride
+
+ jpeg.REG_0x1ac = 0x0
+ jpeg.REG_0x1b0 = 0x0
+ jpeg.REG_0x1b4 = 0x0
+ jpeg.REG_0x1bc = 0x0
+ jpeg.REG_0x1c0 = 0x0
+ jpeg.REG_0x1c4 = 0x0
+
+ jpeg.REG_0x118 = 0x0
+ jpeg.REG_0x11c = 0x1
+
+ jpeg.MODE = 0x177
+ jpeg.REG_0x1028 = 0x400
+
+ jpeg.JPEG_IO_FLAGS = 0x3f
+ jpeg.REG_0x0 = 0x1
+ jpeg.REG_0x1004 = 0x1
+
+ # FIXME: we don't actually know when it's done
+ time.sleep(1)
+
+ print(jpeg.STATUS.reg)
+ print(jpeg.PERFCOUNTER.reg)
+
+ output_data = iface.readmem(output_buf_phys, output_mem_sz)
+ if args.raw_output is not None:
+ with open(args.raw_output, 'wb') as f:
+ f.write(output_data)
+
+ # Just for demonstration purposes, wrangle everything back into RGB
+ with Image.new(
+ mode='RGBA',
+ size=(jpeg_W // decode_scale, jpeg_H // decode_scale)) as im:
+ if pixfmt in ["RGBA", "BGRA", "RGB565"]:
+ for y in range(jpeg_H // decode_scale):
+ for x in range(jpeg_W // decode_scale):
+ block = output_data[
+ y*surface_stride + x*BYTESPP:
+ y*surface_stride + (x+1)*BYTESPP]
+
+ if pixfmt == "RGBA":
+ r, g, b, a = block
+ elif pixfmt == "BGRA":
+ b, g, r, a = block
+ elif pixfmt == "RGB565":
+ rgb = struct.unpack("<H", block)[0]
+ b = (rgb & 0b11111) << 3
+ g = ((rgb >> 5) & 0b111111) << 2
+ r = ((rgb >> 11) & 0b11111) << 3
+ a = 255
+ else:
+ assert False
+ im.putpixel((x, y), (r, g, b, a))
+ elif pixfmt in ["YUV422-CbYCrY", "YUV422-YCbYCr"]:
+ for y in range(jpeg_H // decode_scale):
+ for x in range(0, jpeg_W // decode_scale, 2):
+ block = output_data[
+ y*surface_stride + x*BYTESPP:
+ y*surface_stride + (x+2)*BYTESPP]
+
+ if pixfmt == "YUV422-CbYCrY":
+ cb, y0, cr, y1 = block
+ elif pixfmt == "YUV422-YCbYCr":
+ y0, cb, y1, cr = block
+
+ r0, g0, b0 = yuv2rgb(y0, cb, cr)
+ r1, g1, b1 = yuv2rgb(y1, cb, cr)
+
+ im.putpixel((x, y), (r0, g0, b0, 255))
+ # XXX this really needs some fixing
+ if x+1 < jpeg_W // decode_scale:
+ im.putpixel((x+1, y), (r1, g1, b1, 255))
+ elif pixfmt == "YUV422-planar":
+ for y in range(jpeg_H // decode_scale):
+ for x in range(jpeg_W // decode_scale):
+ y_ = output_data[y*surface_stride + x]
+ cb = output_data[surface_P1_off + y*surface_P1_stride + x&~1]
+ cr = output_data[surface_P1_off + y*surface_P1_stride + (x&~1)+1]
+
+ r, g, b = yuv2rgb(y_, cb, cr)
+
+ im.putpixel((x, y), (r, g, b, 255))
+ elif pixfmt == "YUV420-planar":
+ for y in range(jpeg_H // decode_scale):
+ for x in range(jpeg_W // decode_scale):
+ y_ = output_data[y*surface_stride + x]
+ cb = output_data[surface_P1_off + (y//2)*surface_P1_stride + x&~1]
+ cr = output_data[surface_P1_off + (y//2)*surface_P1_stride + (x&~1)+1]
+
+ r, g, b = yuv2rgb(y_, cb, cr)
+
+ im.putpixel((x, y), (r, g, b, 255))
+ elif pixfmt == "YUV444-planar":
+ for y in range(jpeg_H // decode_scale):
+ for x in range(jpeg_W // decode_scale):
+ y_ = output_data[y*surface_stride + x]
+ cb = output_data[surface_P1_off + y*surface_P1_stride + x*2]
+ cr = output_data[surface_P1_off + y*surface_P1_stride + x*2+1]
+
+ r, g, b = yuv2rgb(y_, cb, cr)
+
+ im.putpixel((x, y), (r, g, b, 255))
+ else:
+ assert False
+ im.save(args.output)
+
+if args.encode:
+ iface.writemem(input_buf_phys, image_data)
+ iface.writemem(input_buf_phys + surface_P1_off, image_data_P1)
+ print("Pixel data uploaded")
+
+ jpeg.MODE = 0x17f
+ jpeg.REG_0x38 = 0x1 # if not set nothing happens
+ jpeg.REG_0x2c = 0x1 # if not set only header is output
+ jpeg.REG_0x34 = 0x0 # if set output is a JPEG but weird with no footer
+
+ if args.encode_subsampling == '444':
+ jpeg.CODEC.set(CODEC=E_CODEC._444)
+ elif args.encode_subsampling == '422':
+ jpeg.CODEC.set(CODEC=E_CODEC._422)
+ elif args.encode_subsampling == '420':
+ jpeg.CODEC.set(CODEC=E_CODEC._420)
+ elif args.encode_subsampling == '400':
+ jpeg.CODEC.set(CODEC=E_CODEC._400)
+ else:
+ assert False
+
+ if BYTESPP_P1 != 0:
+ jpeg.PX_USE_PLANE1 = 1
+ jpeg.PX_PLANE1_WIDTH = im_W*BYTESPP_P1 - 1
+ jpeg.PX_PLANE1_HEIGHT = im_H // P1_DIVH - 1
+ else:
+ jpeg.PX_USE_PLANE1 = 0
+ jpeg.PX_PLANE1_WIDTH = 0xffffffff
+ jpeg.PX_PLANE1_HEIGHT = 0xffffffff
+ jpeg.PX_PLANE0_WIDTH = im_W*BYTESPP - 1
+ jpeg.PX_PLANE0_HEIGHT = im_H - 1
+ jpeg.TIMEOUT = 266000000
+
+ jpeg.PX_TILES_W = divroundup(im_W, macroblock_W)
+ jpeg.PX_TILES_H = divroundup(im_H, macroblock_H)
+ if pixfmt in ['RGB888', 'RGB101010', 'YUV10']:
+ if args.encode_subsampling == '444' or args.encode_subsampling == '400':
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 8
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 1
+ elif args.encode_subsampling == '422':
+ jpeg.PX_PLANE0_TILING_H = 8
+ jpeg.PX_PLANE0_TILING_V = 8
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 1
+ elif args.encode_subsampling == '420':
+ jpeg.PX_PLANE0_TILING_H = 8
+ jpeg.PX_PLANE0_TILING_V = 16
+ jpeg.PX_PLANE1_TILING_H = 0
+ jpeg.PX_PLANE1_TILING_V = 0
+ else:
+ assert False
+ elif pixfmt == 'RGB565':
+ if args.encode_subsampling == '444' or args.encode_subsampling == '400':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 8
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 1
+ elif args.encode_subsampling == '422':
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 8
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 1
+ elif args.encode_subsampling == '420':
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 16
+ jpeg.PX_PLANE1_TILING_H = 0
+ jpeg.PX_PLANE1_TILING_V = 0
+ else:
+ assert False
+ elif pixfmt == 'YUV-linear':
+ if args.encode_subsampling == '444' or args.encode_subsampling == '400':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 8
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 1
+ elif args.encode_subsampling == '422':
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 8
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 1
+ elif args.encode_subsampling == '420':
+ jpeg.PX_PLANE0_TILING_H = 4
+ jpeg.PX_PLANE0_TILING_V = 16
+ jpeg.PX_PLANE1_TILING_H = 0
+ jpeg.PX_PLANE1_TILING_V = 0
+ else:
+ assert False
+ elif pixfmt == 'YUV444-planar':
+ if args.encode_subsampling == '444' or args.encode_subsampling == '400':
+ jpeg.PX_PLANE0_TILING_H = 1
+ jpeg.PX_PLANE0_TILING_V = 8
+ jpeg.PX_PLANE1_TILING_H = 2
+ jpeg.PX_PLANE1_TILING_V = 8
+ elif args.encode_subsampling == '422':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 8
+ jpeg.PX_PLANE1_TILING_H = 4
+ jpeg.PX_PLANE1_TILING_V = 8
+ elif args.encode_subsampling == '420':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 16
+ jpeg.PX_PLANE1_TILING_H = 4
+ jpeg.PX_PLANE1_TILING_V = 16
+ else:
+ assert False
+ elif pixfmt == 'YUV422-planar':
+ if args.encode_subsampling == '444' or args.encode_subsampling == '400':
+ jpeg.PX_PLANE0_TILING_H = 1
+ jpeg.PX_PLANE0_TILING_V = 8
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 8
+ elif args.encode_subsampling == '422':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 8
+ jpeg.PX_PLANE1_TILING_H = 2
+ jpeg.PX_PLANE1_TILING_V = 8
+ elif args.encode_subsampling == '420':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 16
+ jpeg.PX_PLANE1_TILING_H = 2
+ jpeg.PX_PLANE1_TILING_V = 16
+ else:
+ assert False
+ elif pixfmt == 'YUV420-planar':
+ if args.encode_subsampling == '444' or args.encode_subsampling == '400':
+ jpeg.PX_PLANE0_TILING_H = 1
+ jpeg.PX_PLANE0_TILING_V = 8
+ jpeg.PX_PLANE1_TILING_H = 1
+ jpeg.PX_PLANE1_TILING_V = 4
+ elif args.encode_subsampling == '422':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 8
+ jpeg.PX_PLANE1_TILING_H = 2
+ jpeg.PX_PLANE1_TILING_V = 4
+ elif args.encode_subsampling == '420':
+ jpeg.PX_PLANE0_TILING_H = 2
+ jpeg.PX_PLANE0_TILING_V = 16
+ jpeg.PX_PLANE1_TILING_H = 2
+ jpeg.PX_PLANE1_TILING_V = 8
+ else:
+ assert False
+ else:
+ assert False
+ jpeg.PX_PLANE0_STRIDE = surface_stride
+ jpeg.PX_PLANE1_STRIDE = surface_P1_stride
+
+ if pixfmt in ['RGB888', 'RGB101010', 'RGB565', 'YUV10', 'YUV444-planar']:
+ if args.encode_subsampling in ['422', '420']:
+ jpeg.CHROMA_HALVE_H_TYPE1 = 1
+ if args.encode_subsampling == '420':
+ jpeg.CHROMA_HALVE_V_TYPE1 = 1
+ elif pixfmt in ['YUV-linear', 'YUV422-planar']:
+ if args.encode_subsampling == '420':
+ jpeg.CHROMA_HALVE_V_TYPE1 = 1
+ elif pixfmt == 'YUV420-planar':
+ if args.encode_subsampling in ['422', '444']:
+ jpeg.CHROMA_DOUBLE_V = 1
+ else:
+ assert False
+
+ # none of this seems to affect anything????
+ jpeg.REG_0x94 = 0xc # c/2 for 444; 8/2 for 422; 3/1 for 411; b/2 for 400
+ jpeg.REG_0x98 = 0x2
+ jpeg.REG_0x20c = im_W
+ jpeg.REG_0x210 = im_H
+
+ if pixfmt in ['RGB888', 'RGB101010', 'RGB565']:
+ jpeg.CONVERT_COLOR_SPACE = 1
+ jpeg.MATRIX_MULT[0].val = 0x4d
+ jpeg.MATRIX_MULT[1].val = 0x96
+ jpeg.MATRIX_MULT[2].val = 0x1d
+ jpeg.MATRIX_MULT[3].val = 0xffffffd5
+ jpeg.MATRIX_MULT[4].val = 0xffffffab
+ jpeg.MATRIX_MULT[5].val = 0x80
+ jpeg.MATRIX_MULT[6].val = 0x80
+ jpeg.MATRIX_MULT[7].val = 0xffffff95
+ jpeg.MATRIX_MULT[8].val = 0xffffffeb
+ jpeg.MATRIX_MULT[9].val = 0x0
+ jpeg.MATRIX_MULT[10].val = 0x80
+
+ if pixfmt == 'RGB888':
+ jpeg.ENCODE_PIXEL_FORMAT.set(FORMAT=E_ENCODE_PIXEL_FORMAT.RGB888)
+ elif pixfmt == 'RGB101010':
+ jpeg.ENCODE_PIXEL_FORMAT.set(FORMAT=E_ENCODE_PIXEL_FORMAT.RGB101010)
+ elif pixfmt == 'RGB565':
+ jpeg.ENCODE_PIXEL_FORMAT.set(FORMAT=E_ENCODE_PIXEL_FORMAT.RGB565)
+ elif pixfmt == 'YUV10':
+ jpeg.ENCODE_PIXEL_FORMAT.set(FORMAT=E_ENCODE_PIXEL_FORMAT.YUV10_linear)
+ elif pixfmt == 'YUV-linear':
+ jpeg.ENCODE_PIXEL_FORMAT.set(FORMAT=E_ENCODE_PIXEL_FORMAT.YUV_linear)
+ elif pixfmt in ['YUV444-planar', 'YUV422-planar', 'YUV420-planar']:
+ jpeg.ENCODE_PIXEL_FORMAT.set(FORMAT=E_ENCODE_PIXEL_FORMAT.YUV_planar)
+ else:
+ assert False
+ if pixfmt == 'YUV-linear':
+ jpeg.ENCODE_COMPONENT0_POS = 0
+ jpeg.ENCODE_COMPONENT1_POS = 1
+ jpeg.ENCODE_COMPONENT2_POS = 3
+ jpeg.ENCODE_COMPONENT3_POS = 2
+ elif pixfmt in ['YUV422-planar', 'YUV420-planar', 'YUV444-planar']:
+ jpeg.ENCODE_COMPONENT0_POS = 0
+ jpeg.ENCODE_COMPONENT1_POS = 0
+ jpeg.ENCODE_COMPONENT2_POS = 1
+ jpeg.ENCODE_COMPONENT3_POS = 3
+ else:
+ jpeg.ENCODE_COMPONENT0_POS = 0
+ jpeg.ENCODE_COMPONENT1_POS = 1
+ jpeg.ENCODE_COMPONENT2_POS = 2
+ jpeg.ENCODE_COMPONENT3_POS = 3
+
+ jpeg.INPUT_START1 = input_buf_iova
+ jpeg.INPUT_START2 = input_buf_iova + surface_P1_off
+ jpeg.INPUT_END = input_buf_iova + input_mem_sz + 7 # NOTE +7
+ jpeg.OUTPUT_START1 = output_buf_iova
+ jpeg.OUTPUT_START2 = 0xdeadbeef
+ jpeg.OUTPUT_END = output_buf_iova + output_mem_sz
+
+ jpeg.REG_0x118 = 0x1
+ jpeg.REG_0x11c = 0x0
+
+ jpeg.ENABLE_RST_LOGGING = args.encode_rst_interval is not None
+
+ jpeg.MODE = 0x16f
+ if args.encode_subsampling == '444':
+ jpeg_subsampling = E_JPEG_IO_FLAGS_SUBSAMPLING._444
+ elif args.encode_subsampling == '422':
+ jpeg_subsampling = E_JPEG_IO_FLAGS_SUBSAMPLING._422
+ elif args.encode_subsampling == '420':
+ jpeg_subsampling = E_JPEG_IO_FLAGS_SUBSAMPLING._420
+ elif args.encode_subsampling == '400':
+ jpeg_subsampling = E_JPEG_IO_FLAGS_SUBSAMPLING._400
+ else:
+ assert False
+ jpeg.JPEG_IO_FLAGS.set(
+ OUTPUT_8BYTE_CHUNKS_CORRECTLY=1,
+ OUTPUT_MACROBLOCKS_UNFLIPPED_H=1,
+ SUBSAMPLING_MODE=jpeg_subsampling
+ )
+ jpeg.JPEG_WIDTH = im_W
+ jpeg.JPEG_HEIGHT = im_H
+ if args.encode_rst_interval is not None:
+ jpeg.RST_INTERVAL = args.encode_rst_interval
+ else:
+ jpeg.RST_INTERVAL = 0
+ jpeg.JPEG_OUTPUT_FLAGS = 0
+
+ jpeg.QTBL[0].val = 0xa06e64a0
+ jpeg.QTBL[1].val = 0xf0ffffff
+ jpeg.QTBL[2].val = 0x78788cbe
+ jpeg.QTBL[3].val = 0xffffffff
+ jpeg.QTBL[4].val = 0x8c82a0f0
+ jpeg.QTBL[5].val = 0xffffffff
+ jpeg.QTBL[6].val = 0x8caadcff
+ jpeg.QTBL[7].val = 0xffffffff
+ jpeg.QTBL[8].val = 0xb4dcffff
+ jpeg.QTBL[9].val = 0xffffffff
+ jpeg.QTBL[10].val = 0xf0ffffff
+ jpeg.QTBL[11].val = 0xffffffff
+ jpeg.QTBL[12].val = 0xffffffff
+ jpeg.QTBL[13].val = 0xffffffff
+ jpeg.QTBL[14].val = 0xffffffff
+ jpeg.QTBL[15].val = 0xffffffff
+
+ jpeg.QTBL[16].val = 0xaab4f0ff
+ jpeg.QTBL[17].val = 0xffffffff
+ jpeg.QTBL[18].val = 0xb4d2ffff
+ jpeg.QTBL[19].val = 0xffffffff
+ jpeg.QTBL[20].val = 0xf0ffffff
+ jpeg.QTBL[21].val = 0xffffffff
+ jpeg.QTBL[22].val = 0xffffffff
+ jpeg.QTBL[23].val = 0xffffffff
+ jpeg.QTBL[24].val = 0xffffffff
+ jpeg.QTBL[25].val = 0xffffffff
+ jpeg.QTBL[26].val = 0xffffffff
+ jpeg.QTBL[27].val = 0xffffffff
+ jpeg.QTBL[28].val = 0xffffffff
+ jpeg.QTBL[29].val = 0xffffffff
+ jpeg.QTBL[30].val = 0xffffffff
+ jpeg.QTBL[31].val = 0xffffffff
+
+ jpeg.QTBL[32].val = 0x01010201
+ jpeg.QTBL[33].val = 0x01020202
+ jpeg.QTBL[34].val = 0x02030202
+ jpeg.QTBL[35].val = 0x03030604
+ jpeg.QTBL[36].val = 0x03030303
+ jpeg.QTBL[37].val = 0x07050804
+ jpeg.QTBL[38].val = 0x0608080a
+ jpeg.QTBL[39].val = 0x0908070b
+ jpeg.QTBL[40].val = 0x080a0e0d
+ jpeg.QTBL[41].val = 0x0b0a0a0c
+ jpeg.QTBL[42].val = 0x0a08080b
+ jpeg.QTBL[43].val = 0x100c0c0d
+ jpeg.QTBL[44].val = 0x0f0f0f0f
+ jpeg.QTBL[45].val = 0x090b1011
+ jpeg.QTBL[46].val = 0x0f0e110d
+ jpeg.QTBL[47].val = 0x0e0e0e01
+
+ jpeg.QTBL[48].val = 0x04040405
+ jpeg.QTBL[49].val = 0x04050905
+ jpeg.QTBL[50].val = 0x05090f0a
+ jpeg.QTBL[51].val = 0x080a0f1a
+ jpeg.QTBL[52].val = 0x13090913
+ jpeg.QTBL[53].val = 0x1a1a1a1a
+ jpeg.QTBL[54].val = 0x0d1a1a1a
+ jpeg.QTBL[55].val = 0x1a1a1a1a
+ jpeg.QTBL[56].val = 0x1a1a1a1a
+ jpeg.QTBL[57].val = 0x1a1a1a1a
+ jpeg.QTBL[58].val = 0x1a1a1a1a
+ jpeg.QTBL[59].val = 0x1a1a1a1a
+ jpeg.QTBL[60].val = 0x1a1a1a1a
+ jpeg.QTBL[61].val = 0x1a1a1a1a
+ jpeg.QTBL[62].val = 0x1a1a1a1a
+ jpeg.QTBL[63].val = 0x1a1a1a1a
+
+ jpeg.HUFFMAN_TABLE.val = 0x3c
+ jpeg.QTBL_SEL.val = 0xff
+ jpeg.REG_0x0.val = 0x1
+ jpeg.REG_0x1004.val = 0x1
+
+ # FIXME: we don't actually know when it's done
+ time.sleep(1)
+
+ print(jpeg.STATUS.reg)
+ print(jpeg.PERFCOUNTER.reg)
+ jpeg_out_sz = jpeg.COMPRESSED_BYTES.val
+ print(f"JPEG output is {jpeg_out_sz} bytes")
+
+ rst_log_n = jpeg.RST_LOG_ENTRIES.val
+ for i in range(rst_log_n):
+ print(f"RST log[{i}] = 0x{jpeg.RSTLOG[i].val:X}")
+
+ output_data = iface.readmem(output_buf_phys, output_mem_sz)
+ if args.raw_output is not None:
+ with open(args.raw_output, 'wb') as f:
+ f.write(output_data)
+ with open(args.output, 'wb') as f:
+ f.write(output_data[:jpeg_out_sz])
diff --git a/tools/proxyclient/experiments/jpeg_doc.md b/tools/proxyclient/experiments/jpeg_doc.md
new file mode 100644
index 0000000..afb32f8
--- /dev/null
+++ b/tools/proxyclient/experiments/jpeg_doc.md
@@ -0,0 +1,1019 @@
+# Apple Silicon JPEG encoder/decoder reverse engineering notes
+
+## General
+
+### REG_0x0 (+0x0000)
+
+This register is not fully understood yet. It is set to 1 to kick off an operation.
+
+This register appears to be 1 bit wide. It is readable/writable.
+
+The driver resets this register to 0.
+
+
+### REG_0x4 (+0x0004)
+
+This register is not understood yet.
+
+This register appears to be 1 bit wide. It is readable/writable.
+
+The driver resets this register to 0.
+
+
+### MODE (+0x0008)
+
+This register controls the mode of operation of the hardware. The details of this register are not understood yet.
+
+This register appears to be 10 bit wide. It is readable/writable.
+
+At minimum bits 0x043 need to be set or else reading from some registers above 0x1000 will fault.
+
+This register is set to multiple different values throughout the reset process.
+
+
+### REG_0xc (+0x000C)
+
+This register is not understood yet.
+
+This register is at least 10 bit wide. It appears to be read-only. The power-up state appears to be 0x200.
+
+The driver reads this register and stores the value after an interrupt occurs.
+
+
+### REG_0x10 (+0x0010)
+### REG_0x14 (+0x0014)
+
+No access to this register has been observed.
+
+This register is at least 11 bit wide. It appears to be read-only. The power-up state appears to be 0x400.
+
+
+### REG_0x18 (+0x0018)
+
+No access to this register has been observed.
+
+This register is at least 8 bit wide. It appears to be read-only. The power-up state appears to be 0x55.
+
+
+### (+0x001C)
+
+No access to this register has been observed.
+
+It appears to be read-only. The power-up state appears to be 0.
+
+
+### REG_0x20 (+0x0020)
+
+This register is not understood yet.
+
+This register appears to be 11 bit wide. It is readable/writable.
+
+The driver resets this register to 0xff, and it is written with a 0 after an interrupt occurs.
+
+
+### STATUS (+0x0024)
+
+- bit0: Operation is completed ???
+- bit1: Timeout occurred
+- bit2: Read buffer overflow
+- bit3: Write buffer overflow
+- bit4: Codec buffer overflow
+- bit5: Some kind of error, happens if macroblock settings are messed up
+- bit6: AXI error
+- bit7: The driver checks for this after an interrupt, but the meaning is not understood
+
+
+### CODEC (+0x0028)
+
+This register controls how the JPEG data is processed wrt subsampling mode. It affects both encode and decode.
+
+- 0 = 4:4:4
+- 1 = 4:2:2
+- 2 = 4:1:1
+- 3 = 4:2:0
+- 4 = 4:0:0
+
+### REG_0x2c (+0x002C)
+
+This register is not fully understood yet.
+
+This register appears to be 1 bit wide. It is readable/writable.
+
+The driver sets this register to 0 when decoding and 1 when encoding. If it is not set to 1 when encoding, only headers will be output. The interrupt handler makes a decision based on this register.
+
+
+### REG_0x30 (+0x0030)
+
+This register is not understood yet.
+
+This register appears to be 1 bit wide. It is readable/writable.
+
+The driver resets this register to 0.
+
+
+### REG_0x34 (+0x0034)
+
+This register is not fully understood yet.
+
+This register appears to be 1 bit wide. It is readable/writable.
+
+The driver sets this register to 1 when decoding and 0 when encoding. If it is not set to 0 when encoding, the output will be corrupted in some way.
+
+
+### REG_0x38 (+0x0038)
+
+This register is not fully understood yet.
+
+This register appears to be 1 bit wide. It is readable/writable.
+
+The driver sets this register to 0 when decoding and 1 when encoding. If it is not set to 1 when encoding, nothing will be output. If it is set to 1 when decoding, the output will be a weird tiled format.
+
+
+
+## Chroma control
+
+### CHROMA_HALVE_H_TYPE1 (+0x003c)
+### CHROMA_HALVE_H_TYPE2 (+0x0040)
+
+Setting these register to 1 causes chroma to be subsampled horizontally.
+
+The second register produces a different result from the first register. It is speculated that this is related to chroma siting, but this has not been verified yet. If both the second and the first register are set, the second appears to win.
+
+
+### CHROMA_HALVE_V_TYPE1 (+0x0044)
+### CHROMA_HALVE_V_TYPE2 (+0x0048)
+
+Setting these register to 1 causes chroma to be subsampled vertically.
+
+The second register produces a different result from the first register. It is speculated that this is related to chroma siting, but this has not been verified yet. If both the second and the first register are set, the second appears to win.
+
+
+### CHROMA_DOUBLE_H (+0x004c)
+
+Setting this register to 1 causes chroma to be doubled/interpolated horizontally.
+
+
+### CHROMA_QUADRUPLE_H (+0x0050)
+
+Setting this register to 1 causes chroma to be quadrupled/interpolated horizontally. If both this and the previous register are set, double appears to win.
+
+
+### CHROMA_DOUBLE_V (+0x0054)
+
+Setting this register to 1 causes chroma to be doubled/interpolated vertically.
+
+
+## Pixel data control
+
+### PX_USE_PLANE1 (+0x0058)
+
+Setting this register to 1 enables use of the second pixel plane.
+
+
+### PX_TILES_W (+0x005c)
+
+This register specifies the width of the image in tiles/MCUs/macroblocks, where the macroblock size depends on the chroma subsampling mode, i.e. divroundup by 8 for 4:4:4, by 16 for 4:2:2 and 4:2:0, by 32 for 4:1:1 (FIXME verify this again).
+
+This register is 16 bits wide.
+
+
+### PX_TILES_H (+0x0060)
+
+This register specifies the height of the image in tiles/MCUs/macroblocks, where the macroblock size depends on the chroma subsampling mode, i.e. divroundup by 16 for 4:2:0 or else by 8 (FIXME verify this again).
+
+This register is 16 bits wide.
+
+
+### PX_PLANE0_WIDTH (+0x0064)
+
+This register specifies the width of the image data in plane 0, in bytes, minus 1. When decoding, it is important to set this correctly for the edge to be processed properly.
+
+This register is 20 bits wide, even though the driver will sometimes write 0xffffffff.
+
+
+### PX_PLANE0_HEIGHT (+0x0068)
+
+This register specifies the height of the image data in plane 0, in rows, minus 1. When decoding, it might be important to set this correctly for the edge to be processed properly.
+
+This register is 16 bits wide, even though the driver will sometimes write 0xffffffff.
+
+
+### PX_PLANE0_TILING_H (+0x006c)
+
+This register somehow controls how pixel data matches up with subsampled chroma data, but the details are not understood yet. Valid range 0-31.
+
+
+### PX_PLANE0_TILING_V (+0x0070)
+
+This register somehow controls how pixel data matches up with subsampled chroma data, but the details are not understood yet. Valid range 0-31.
+
+
+### PX_PLANE0_STRIDE (+0x0074)
+
+This is the row stride of plane 0 in bytes.
+
+This register is 24 bits wide.
+
+
+### PX_PLANE1_WIDTH (+0x0078)
+### PX_PLANE1_HEIGHT (+0x007c)
+### PX_PLANE1_TILING_H (+0x0080)
+### PX_PLANE1_TILING_V (+0x0084)
+### PX_PLANE1_STRIDE (+0x0088)
+
+These registers function similarly to the plane 0 registers.
+
+
+## Input/output pointers
+
+### INPUT_START1 (+0x008c)
+
+Input pointer 1 IOVA.
+
+
+### INPUT_START2 (+0x0090)
+
+Input pointer 2 IOVA.
+
+
+### REG_0x94 (+0x0094)
+
+This register is not understood yet.
+
+The driver sets this register to a fixed value of 0x1f when decoding and to a value that depends on the chroma subsampling mode when encoding (0xc for 4:4:4, 0x8 for 4:2:2, 0x3 for 4:2:0, 0xb for 4:0:0), but changing it does not seem to do anything.
+
+This register is 6 bits wide.
+
+
+### REG_0x98 (+0x0098)
+
+This register is not understood yet.
+
+The driver sets this register to a fixed value of 1 when decoding and to a value that depends on the chroma subsampling mode when encoding (2 for 4:4:4/4:2:2/4:0:0, 1 for 4:2:0), but changing it does not seem to do anything.
+
+This register is 6 bits wide.
+
+
+### INPUT_END (+0x009c)
+
+End of input data IOVA.
+
+For reasons that are not understood, this is ORed with 7 when encoding.
+
+
+### OUTPUT_START1 (+0x00a0)
+
+Output pointer 1 IOVA.
+
+
+### OUTPUT_START2 (+0x00a4)
+
+Output pointer 2 IOVA.
+
+
+### OUTPUT_END (+0x00a8)
+
+End of output data IOVA.
+
+
+## MATRIX_MULT (+0x00ac-0x00d7) (11 entries)
+
+Color space conversion matrix.
+
+The full details of the shifting/offset/final two values is not understood yet.
+
+
+## DITHER (+0x00d8-0x00ff) (10 entries)
+
+Dithering when decoding to RGB565.
+
+The full details of this is not understood yet.
+
+
+## Encoding pixel format
+
+### ENCODE_PIXEL_FORMAT (+0x0100)
+
+- 0 = RGB101010
+- 1 = YUV10 4:4:4 linear
+- 2 = RGB888
+- 3 = RGB565
+- 4 = YUV planar (partially tested, details not fully understood)
+- 5 = YUV8 4:2:2 linear
+- 6-9 = do something, may not be useful, maybe invalid, not used by driver
+
+
+### ENCODE_COMPONENT0_POS (+0x0104)
+### ENCODE_COMPONENT1_POS (+0x0108)
+### ENCODE_COMPONENT2_POS (+0x010c)
+### ENCODE_COMPONENT3_POS (+0x0110)
+
+These registers control the positions of each component in the parsed pixel data. It is used to allow e.g. flipping between RGBA and BGRA.
+
+
+### CONVERT_COLOR_SPACE (+0x0114)
+
+Setting this register to 1 enables color space conversion when encoding
+
+
+## Unknown
+
+### REG_0x118 (+0x0118)
+
+This register is not understood yet.
+
+This register appears to be 1 bit wide. It is readable/writable.
+
+This register is set to 0 when decoding and 1 when encoding.
+
+
+### REG_0x11c (+0x011c)
+
+This register is not understood yet.
+
+This register appears to be 1 bit wide. It is readable/writable.
+
+This register is set to 1 when decoding and 0 when encoding.
+
+
+### REG_0x120 (+0x0120)
+
+This register is not understood yet.
+
+This register appears to be 1 bit wide. It is readable/writable.
+
+The driver resets this register to 0.
+
+
+### TILING_ENABLE (+0x0124)
+
+This register enables the functionality of the following two registers.
+
+The driver sets this register to 1 when decoding if the surface "is tiled."
+
+
+### TILING_PLANE0 (+0x0128)
+
+This register is not fully understood yet. A value greater than 8 causes plane 0 to be reformatted (tiled?), but the details are not understood yet.
+
+This register appears to be 5 bit wide. It is readable/writable.
+
+
+### TILING_PLANE1 (+0x012c)
+
+This register is not fully understood yet. A value greater than 8 causes plane 1 to be reformatted (tiled?), but the details are not understood yet.
+
+This register appears to be 5 bit wide. It is readable/writable.
+
+
+## Decoding image size
+
+### DECODE_MACROBLOCKS_W (+0x0130)
+
+Sets the width of the decoded image in macroblocks, where the macroblock size depends on the chroma subsampling mode, i.e. divroundup by 8 for 4:4:4, by 16 for 4:2:2 and 4:2:0, by 32 for 4:1:1.
+
+This register is 16 bits wide.
+
+
+### DECODE_MACROBLOCKS_H (+0x0134)
+
+Sets the height of the decoded image in macroblocks, where the macroblock size depends on the chroma subsampling mode, i.e. divroundup by 16 for 4:2:0 or else by 8.
+
+This register is 16 bits wide.
+
+
+### RIGHT_EDGE_PIXELS (+0x0138)
+
+The driver sets this to the number of pixels that are valid in the rightmost macroblocks, but changing it does not seem to do anything.
+
+This register is 5 bits wide.
+
+
+### BOTTOM_EDGE_PIXELS (+0x013c)
+
+The driver sets this to the number of pixels that are valid in the bottommost macroblocks, but changing it does not seem to do anything.
+
+This register is 4 bits wide.
+
+
+### RIGHT_EDGE_SAMPLES (+0x0140)
+
+The driver sets this to the number of chroma samples that are valid in the rightmost macroblocks, but changing it does not seem to do anything.
+
+This register is 3 bits wide.
+
+
+### BOTTOM_EDGE_SAMPLES (+0x0144)
+
+The driver sets this to the number of chroma samples that are valid in the bottommost macroblocks, but changing it does not seem to do anything.
+
+This register is 3 bits wide.
+
+
+### SCALE_FACTOR (+0x0148)
+
+- 0 = /1
+- 1 = /2
+- 2 = /4
+- 3 = /8
+
+This appears to be ignored when encoding.
+
+
+## Decoding pixel format
+
+### DECODE_PIXEL_FORMAT (+0x014c)
+
+- 0 = YUV 444 (2P)
+- 1 = YUV 422 (2P)
+- 2 = YUV 420 (2P)
+- 3 = YUV 422 (1P)
+- 4 = driver mentions YUV10 444 (1P) but it does not appear to work (driver also says it doesn't work)
+- 5 = RGB888
+- 6 = RGB565
+- 7 = driver mentions RGB101010 but it does not appear to work (driver also says it doesn't work)
+
+
+### YUV422_ORDER (+0x0150)
+
+- 0 = Cb Y'0 Cr Y'1
+- 1 = Y'0 Cb Y'1 Cr
+
+
+### RGBA_ORDER (+0x0154)
+
+- 0 = BGRA
+- 1 = RGBA
+
+
+### RGBA_ALPHA (+0x0158)
+
+This value is filled in to alpha bytes when decoding into RGB888
+
+
+## Unknown/status
+
+### PLANAR_CHROMA_HALVING (+0x015c)
+
+This register is not fully understood yet. Setting it seems to halve the chroma vertically when outputting into planar modes. This is different from the CHROMA_HALVE_V_* registers because it halves the final image, not each macroblock.
+
+The driver seems to use this in some cases when scaling down an image by 8, but the details of this are not understood yet.
+
+The driver resets this register to 0.
+
+
+### REG_0x160 (+0x0160)
+
+This register is not understood yet.
+
+This register appears to be 1 bit wide. It is readable/writable.
+
+The driver resets this register to a configurable value that happens to be 0.
+
+
+### REG_0x164 (+0x0164)
+
+This register is not understood yet.
+
+It appears to be read-only. The power-up state appears to be 0.
+
+The driver reads this register and stores the value after an interrupt occurs.
+
+
+### (+0x0168)
+
+No access to this register has been observed.
+
+It appears to be read-only. The power-up state appears to be 0.
+
+
+### REG_0x16c (+0x016c)
+
+This register is not understood yet.
+
+It appears to be read-only. The power-up state appears to be 0.
+
+The driver reads this register and stores the value after an interrupt occurs.
+
+
+### REG_0x170 (+0x0170)
+
+This register is not understood yet.
+
+It appears to be read-only. The power-up state appears to be 0.
+
+The driver reads this register and stores the value after an interrupt occurs.
+
+
+### (+0x0174)
+
+No access to this register has been observed.
+
+It appears to be read-only. The power-up state appears to be 0.
+
+
+### PERFCOUNTER (+0x0178)
+
+This register appears to be a performance counter. It is not yet understood what is being measured.
+
+It appears to be read-only.
+
+The driver reads this register and accumulates it after an interrupt occurs.
+
+
+### (+0x017c)
+
+No access to this register has been observed.
+
+It appears to be read-only. The power-up state appears to be 0.
+
+
+### (+0x0180)
+
+No access to this register has been observed.
+
+It appears to be read-only. The power-up state appears to be 0.
+
+
+### TIMEOUT (+0x0184)
+
+This register configures the timeout. It is not yet understood what units this is in.
+
+This register is 32 bits wide.
+
+
+### HWREV (+0x0188)
+
+This register contains the hardware revision. On the M1 Max, it is 0xd1013.
+
+
+### REG_0x18c (+0x018c)
+
+No access to this register has been observed.
+
+This register appears to be 2 bits wide. It is readable/writable.
+
+
+### REG_0x190 (+0x0190)
+
+No access to this register has been observed.
+
+This register appears to be 2 bits wide. It is readable/writable.
+
+
+### REG_0x194 (+0x0194)
+
+No access to this register has been observed.
+
+This register appears to be 4 bits wide. It is readable/writable.
+
+
+### REG_0x198 (+0x0198)
+
+No access to this register has been observed.
+
+This register appears to be 4 bits wide. It is readable/writable.
+
+
+### REG_0x19c (+0x019c)
+
+This register is not understood yet.
+
+This register appears to be 1 bit wide. It is readable/writable.
+
+The driver under some conditions writes a 1 here.
+
+
+## RST logging / unknown
+
+### ENABLE_RST_LOGGING (+0x01a0)
+
+If this register is set to 1, some data about RST blocks will be logged when encoding.
+
+
+### RST_LOG_ENTRIES (+0x01a4)
+
+This register will contain the number of RST log entries.
+
+
+### REG_0x1a8 (+0x01a8)
+### REG_0x1ac (+0x01ac)
+### REG_0x1b0 (+0x01b0)
+
+This register is not understood yet.
+
+This register appears to be 1 bit wide. It is readable/writable.
+
+
+### REG_0x1b4 (+0x01b4)
+### REG_0x1b8 (+0x01b8)
+### REG_0x1bc (+0x01bc)
+
+This register is not understood yet.
+
+This register appears to be 32 bit wide. It is readable/writable.
+
+
+### REG_0x1c0 (+0x01c0)
+### REG_0x1c4 (+0x01c4)
+
+This register is not understood yet.
+
+This register appears to be 16 bit wide. It is readable/writable.
+
+
+### REG_0x1c8 (+0x01c8)
+
+This register is not understood yet.
+
+This register appears to be 1 bit wide. It is readable/writable.
+
+
+### REG_0x1cc (+0x01cc)
+### REG_0x1d0 (+0x01d0)
+### REG_0x1d4 (+0x01d4)
+### REG_0x1d8 (+0x01d8)
+
+This register is not understood yet.
+
+This register appears to be 32 bit wide. It is readable/writable.
+
+
+### REG_0x1dc (+0x01dc)
+
+This register is not understood yet.
+
+This register appears to be 1 bit wide. It is readable/writable.
+
+
+### REG_0x1e0 (+0x01e0)
+
+This register is not understood yet.
+
+This register appears to be 14 bit wide. It is readable/writable.
+
+
+### REG_0x1e4 (+0x01e4)
+
+This register is not understood yet.
+
+This register appears to be 13 bit wide. It is readable/writable.
+
+
+### REG_0x1e8 (+0x01e8)
+
+This register is not understood yet.
+
+This register appears to be 9 bit wide. It is readable/writable.
+
+
+### REG_0x1ec (+0x01ec)
+
+This register is not understood yet.
+
+This register appears to be 1 bit wide. It is readable/writable.
+
+
+### REG_0x1f0 (+0x01f0)
+
+This register is not understood yet.
+
+This register appears to be 14 bit wide. It is readable/writable.
+
+
+### REG_0x1f4 (+0x01f4)
+
+This register is not understood yet.
+
+This register appears to be 13 bit wide. It is readable/writable.
+
+
+### REG_0x1f8 (+0x01f8)
+
+This register is not understood yet.
+
+This register appears to be 9 bit wide. It is readable/writable.
+
+
+## Compressed pixel format / Compressed DMA / unknown
+
+### REG_0x1fc (+0x01fc)
+### REG_0x200 (+0x0200)
+
+No access to this register has been observed.
+
+This register appears to be 2 bits wide. It is readable/writable.
+
+
+### REG_0x204 (+0x0204)
+### REG_0x208 (+0x0208)
+
+This register is not understood yet.
+
+This register appears to be 32 bit wide. It is readable/writable.
+
+
+### REG_0x20c (+0x020c)
+### REG_0x210 (+0x0210)
+### REG_0x214 (+0x0214)
+### REG_0x218 (+0x0218)
+
+This register is not understood yet.
+
+This register appears to be 17 bit wide. It is readable/writable.
+
+
+### REG_0x21c (+0x021c)
+### REG_0x220 (+0x0220)
+
+This register is not understood yet.
+
+This register appears to be 16 bit wide. It is readable/writable.
+
+
+### REG_0x224 (+0x0224)
+### REG_0x228 (+0x0228)
+
+This register is not understood yet.
+
+This register appears to be 17 bit wide. It is readable/writable.
+
+
+### REG_0x22c (+0x022c)
+### REG_0x230 (+0x0230)
+### REG_0x234 (+0x0234)
+
+This register is not understood yet.
+
+This register appears to be 16 bit wide. It is readable/writable.
+
+
+### REG_0x238 (+0x0238)
+
+This register is not understood yet.
+
+This register appears to be 1 bit wide. It is readable/writable.
+
+
+### REG_0x23c (+0x023c)
+### REG_0x240 (+0x0240)
+
+This register is not understood yet.
+
+This register appears to be 4 bit wide. It is readable/writable.
+
+
+### REG_0x244 (+0x0244)
+### REG_0x248 (+0x0248)
+
+This register is not understood yet.
+
+This register appears to be 8 bit wide. It is readable/writable.
+
+
+### REG_0x24c (+0x024c)
+
+This register is not understood yet.
+
+This register appears to be 1 bit wide. It is readable/writable.
+
+
+### REG_0x250 (+0x0250)
+### REG_0x254 (+0x0254)
+
+This register is not understood yet.
+
+This register appears to be 4 bit wide. It is readable/writable.
+
+
+### REG_0x258 (+0x0258)
+### REG_0x25c (+0x025c)
+
+This register is not understood yet.
+
+This register appears to be 8 bit wide. It is readable/writable.
+
+
+## REG_0x260 (+0x0260)
+## REG_0x264 (+0x0264)
+
+No access to this register has been observed.
+
+This register appears to be 1 bit wide. It is readable/writable.
+
+
+## REG_0x268 (+0x0268)
+## REG_0x26c (+0x026c)
+
+No access to this register has been observed.
+
+This register appears to be 7 bit wide. It is readable/writable.
+
+
+## (+0x0270-0x027f)
+
+No access to this register has been observed.
+
+It appears to be read-only. The power-up state appears to be 0.
+
+
+## REG_0x280 (+0x0280)
+
+No access to this register has been observed.
+
+This register appears to be 1 bit wide. It is readable/writable.
+
+
+## (+0x0284-0x0fff)
+
+No access to this register has been observed.
+
+It appears to be read-only. The power-up state appears to be 0.
+
+
+## JPEG I/O related
+
+### JPEG_IO_FLAGS (+0x1000)
+
+- bit0-2 control subsampling mode output into the JPEG when encoding
+ - 0 = 4:4:4
+ - 1 = 4:2:2
+ - 2 = 4:2:0
+ - 3 = monochrome
+ - 4 = 4 components ??? seems to work with 422 with 444 tiling params ?????
+ - 6 = indicate 4:1:1 in file, but setting CODEC = 2 doesn't actually work (broken)
+- bit3 needs to be set when decoding. It must be unset when encoding. This is not fully understood yet
+- bit4 causes macroblocks to _not_ be flipped horizontally. It affects both encoding and decoding.
+- bit5 causes chunks of 8 bytes to _not_ be reversed. It affects both encoding and decoding.
+
+
+### REG_0x1004 (+0x1004)
+
+This register is not fully understood yet. It is set to 1 to kick off an operation.
+
+Writing to this register while MODE is set incorrectly can trigger an exception.
+
+
+### REG_0x1008 (+0x1008)
+
+No access to this register has been observed.
+
+This register is at least 1 bit wide. It appears to be read-only. The power-up state appears to be 1.
+
+
+### QTBL_SEL (+0x100c)
+
+This register selects the quantization table in use for each component.
+
+- bit0-1 = component 0
+- bit2-3 = component 1
+- bit4-5 = component 2
+- bit6-7 = component 3?
+
+
+### HUFFMAN_TABLE (+0x1010)
+
+This register controls Huffman tables used. The details of this register are not fully understood yet.
+
+This register is 8 bits wide.
+
+
+### RST_INTERVAL (+0x1014)
+
+This register controls the interval at which RST markers will be generated when encoding.
+
+This register is 16 bits wide.
+
+
+### JPEG_HEIGHT (+0x1018)
+
+This register specifies the height of the JPEG when encoding. It appears to only affect the header.
+
+This register is 16 bits wide.
+
+
+### JPEG_WIDTH (+0x101c)
+
+This register specifies the width of the JPEG when encoding.
+
+This register is 16 bits wide.
+
+
+### COMPRESSED_BYTES (+0x1020)
+
+This register will contain the final size of the JPEG when encoding
+
+
+### JPEG_OUTPUT_FLAGS (+0x1024)
+
+- bit0 doesn't seem to do anything
+- bit1 = output only SOS/EOI, no SOI/DQT/SOF0/DHT
+- bit2 = output SOF0 after DHT instead of before it
+- bit3 doesn't seem to do anything
+- bit4 not sure exactly what this does, but it makes compression worse
+
+
+### REG_0x1028 (+0x1028)
+
+This register is not understood yet.
+
+The driver sets this register to 0x400 when decoding.
+
+This register appears to be 32 bits wide, but writing 0xffffffff results in 0x8000071f.
+
+
+### REG_0x102c (+0x102c)
+
+This register is not understood yet.
+
+The driver reads this register and does something with it after an interrupt occurs.
+
+
+### BITSTREAM_CORRUPTION (+0x1030)
+
+This register is not understood yet. It supposedly contains information about bitstream corruption.
+
+
+## (+0x1034-0x107f)
+
+No access to this register has been observed.
+
+It appears to be read-only. The power-up state appears to be 0.
+
+
+## REG_0x1080 (+0x1080)
+
+No access to this register has been observed.
+
+This register appears to be 5 bit wide. It is readable/writable.
+
+
+## REG_0x1084 (+0x1084)
+
+No access to this register has been observed.
+
+This register appears to be 7 bit wide. It is readable/writable.
+
+
+## (+0x1088)
+
+No access to this register has been observed.
+
+It appears to be read-only. The power-up state appears to be 0.
+
+
+## REG_0x108c (+0x108c)
+
+No access to this register has been observed.
+
+This register appears to be 32 bit wide. It is readable/writable.
+
+
+## REG_0x1090 (+0x1090)
+
+No access to this register has been observed.
+
+This register appears to be 8 bit wide. It is readable/writable.
+
+
+## (+0x1094-0x10df)
+
+No access to this register has been observed.
+
+It appears to be read-only. The power-up state appears to be 0.
+
+
+## SHIKINO_VERSION_MAGIC0 (+0x10e0)
+## SHIKINO_VERSION_MAGIC1 (+0x10e4)
+## SHIKINO_VERSION_MAGIC2 (+0x10e8)
+## SHIKINO_VERSION_MAGIC3 (+0x10ec)
+## SHIKINO_VERSION_MAGIC4 (+0x10f0)
+
+Contains ASCII text 'SHIKINO KJN-7GI 0001'
+
+
+## (+0x10f4-0x10ff)
+
+No access to this register has been observed.
+
+It appears to be read-only. The power-up state appears to be 0.
+
+
+## QTBL (+0x1100-0x11ff)
+
+Quantization tables. The exact layout is not understood yet (zigzag or not?)
+
+
+## (+0x1200-0x1fff)
+
+No access to this register has been observed.
+
+
+## RSTLOG (+0x2000-0x2fff)
+
+RST log. The details of this are not understood yet.
+
+
+## (+0x3000-0x3fff)
+
+No access to this register has been observed.
diff --git a/tools/proxyclient/experiments/memdump.py b/tools/proxyclient/experiments/memdump.py
new file mode 100755
index 0000000..882b79f
--- /dev/null
+++ b/tools/proxyclient/experiments/memdump.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+
+p = 0x800000000
+limit = u.base
+block = 0x40000
+
+while p < limit:
+ f = "mem/0x%x.bin" % p
+ if os.path.exists(f):
+ p += block
+ continue
+
+ print("dumping 0x%x..." % p)
+
+ data = iface.readmem(p, block)
+ open(f, "wb").write(data)
+ p += block
diff --git a/tools/proxyclient/experiments/mmio_sweep.py b/tools/proxyclient/experiments/mmio_sweep.py
new file mode 100755
index 0000000..938ff29
--- /dev/null
+++ b/tools/proxyclient/experiments/mmio_sweep.py
@@ -0,0 +1,212 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+import time
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+from m1n1.loadobjs import *
+import argparse
+import numpy as np
+
+argparser = argparse.ArgumentParser()
+argparser.add_argument("-d", "--domain", type=str,
+ help='Look for MMIO range associated with a particular'
+ ' power domain')
+argparser.add_argument("-p", "--print", action='store_true',
+ help='Print power domain list')
+args = argparser.parse_args()
+
+if args.print:
+ for dev in u.adt["/arm-io/pmgr"].devices:
+ print(dev.name)
+ sys.exit(0)
+
+granule = 0x4000
+
+lp = LinkedProgram(u)
+lp.load_inline_c(f'''
+ #define GRANULE {granule}
+ ''' + '''
+ #include "exception.h"
+ #include "utils.h"
+ #include "soc.h"
+
+ bool is_t6000(void)
+ {
+ return chip_id == T6000;
+ }
+
+ void sweep(u64 from, u64 to, u64 target)
+ {
+ u32 *mask = (u32 *) target;
+ exc_guard = GUARD_MARK | GUARD_SILENT;
+
+ int bitp = 0;
+ for (u64 p = from; p < to; p += GRANULE) {
+ sysop("dsb sy");
+ sysop("isb");
+ bool hit = read32(p) != 0xabad1dea;
+
+ if (hit)
+ *mask |= (1 << bitp);
+ else
+ *mask &= ~(1 << bitp);
+
+ if (++bitp >= 32) {
+ bitp = 0;
+ mask++;
+ }
+ }
+
+ sysop("dsb sy");
+ sysop("isb");
+ }
+ ''')
+
+def do_sweep(maskrange):
+ masklen = (maskrange.stop - maskrange.start) // granule // 32 * 4 + 4
+ mask_base = u.heap.malloc(masklen)
+ lp.sweep(maskrange.start, maskrange.stop, mask_base)
+ mask = iface.readmem(mask_base, masklen)
+ u.heap.free(mask_base)
+ return np.frombuffer(mask, dtype=np.uint8)
+
+def describe_mask(mask, maskrange):
+ '''
+ Describe mask in terms of hot from-to ranges
+ '''
+ ranges = []
+ prev_hit = False
+ mask = np.concatenate((mask, [0]))
+ for i in range(len(mask)*8):
+ hit = mask[i//8] & (1<<(i%8)) != 0
+ if hit and not prev_hit:
+ start = maskrange.start + i*granule
+ if not hit and prev_hit:
+ end = maskrange.start + i*granule
+ ranges.append((start, end))
+ prev_hit = hit
+ return ranges
+
+if lp.is_t6000():
+ maskrange = range(0x2_9000_0000, 0x4_0000_0000)
+else:
+ maskrange = range(0x2_2000_0000, 0x3_0000_0000)
+
+pd_did_enable = set()
+pmgr = u.adt["/arm-io/pmgr"]
+ps_dev_by_id = {dev.id: dev for dev in pmgr.devices}
+ps_deps = dict()
+ps_addrs = dict()
+
+for dev in pmgr.devices:
+ ps = pmgr.ps_regs[dev.psreg]
+ addr = pmgr.get_reg(ps.reg)[0] + ps.offset + dev.psidx * 8
+
+ if lp.is_t6000() and dev.name.startswith("AOP_"):
+ addr = 0x292284000 + (dev.id - 403) * 8
+
+ ps_addrs[dev.name] = addr
+ ps_deps[dev.name] = [
+ ps_dev_by_id[idx].name for idx
+ in dev.parents if idx in ps_dev_by_id
+ ]
+
+if lp.is_t6000():
+ # on t6000, guess the AOP PD hierarchy (undocumented
+ # in ADT) by analogy with t8103
+ ps_deps["AOP_GPIO"] += ["AOP_FILTER"]
+ ps_deps["AOP_BASE"] += ["AOP_FILTER"]
+ ps_deps["AOP_FR"] += ["AOP_FILTER"]
+ ps_deps["AOP_SPMI0"] += ["AOP_FR"]
+ ps_deps["AOP_SPMI1"] += ["AOP_FR"]
+ ps_deps["AOP_LEAP_CLK"] += ["AOP_FILTER"]
+ ps_deps["AOP_SHIM"] += ["AOP_BASE"]
+ ps_deps["AOP_UART0"] += ["AOP_SHIM"]
+ ps_deps["AOP_UART1"] += ["AOP_SHIM"]
+ ps_deps["AOP_UART2"] += ["AOP_SHIM"]
+ ps_deps["AOP_SCM"] += ["AOP_BASE", "AOP_FR"]
+ ps_deps["AOP_CPU"] += ["AOP_BASE"]
+ ps_deps["AOP_I2CM0"] += ["AOP_FR"]
+ ps_deps["AOP_I2CM1"] += ["AOP_FR"]
+ ps_deps["AOP_MCA0"] += ["AOP_FR", "AOP_SHIM"]
+ ps_deps["AOP_MCA1"] += ["AOP_FR", "AOP_SHIM"]
+ ps_deps["AOP_SPI0"] += ["AOP_FR"]
+ ps_deps["AOP_LEAP"] += ["AOP_LEAP_CLK"]
+ ps_deps["AOP_AUDIO_SHIM"] += ["AOP_LEAP_CLK"]
+ ps_deps["AOP_AUDIO_ADMA0"] += ["AOP_FR"]
+ ps_deps["AOP_PDMC_LPD"] += ["AOP_SHIM"]
+ ps_deps["AOP_SRAM"] += ["AOP_SCM", "AOP_CPU"]
+
+def ps_pstate(name):
+ return p.read32(ps_addrs[name]) & 0x0f
+
+def ps_enabled(name):
+ return p.read32(ps_addrs[name]) & 0x0f == 0x0f
+
+def ps_set_pstate(name, desired):
+ p.mask32(ps_addrs[name], 0xf, desired)
+ time.sleep(0.001)
+ actual = p.read32(ps_addrs[name])
+ if actual & 0xf0 != desired << 4:
+ print("WARNING: %s stuck at pstate 0x%x (desired 0x%x)" \
+ % (name, actual >> 4, desired))
+
+def ps_enable(name):
+ print("Enabling %s..." % name)
+ ps_set_pstate(name, 0xf)
+
+def ps_disable(name):
+ p.mask32(ps_addrs[name], 0xf, 0x0)
+
+if args.domain:
+ ps_disable(args.domain)
+
+ to_enable = set([args.domain])
+ for dev in reversed(pmgr.devices):
+ if dev.name not in to_enable \
+ or ps_enabled(dev.name):
+ continue
+
+ for dep in ps_deps[dev.name]:
+ to_enable.add(dep)
+
+ save = dict()
+ for dev in pmgr.devices:
+ if dev.name in to_enable:
+ save[dev.name] = ps_pstate(dev.name)
+ if dev.name != args.domain:
+ ps_enable(dev.name)
+
+ premask = do_sweep(maskrange)
+ ps_enable(args.domain)
+ postmask = do_sweep(maskrange)
+
+ print("Reverting...")
+
+ for dev in reversed(pmgr.devices):
+ if dev.name in to_enable and dev.name:
+ ps_set_pstate(dev.name, save[dev.name])
+
+ hitmask = premask ^ postmask
+ if np.count_nonzero(hitmask & premask):
+ print("Que? Some ranges disappeared?")
+else:
+ # no --domain flag, do a plain sweep
+ hitmask = do_sweep(maskrange)
+
+al = u.adt.build_addr_lookup()
+for start, stop in describe_mask(hitmask, maskrange):
+ # bit ugly but it makes addrlookup do all the heavy lifting for us
+ al.add(range(start, stop), "hit")
+
+print("Hits:")
+for zone, value in al.items():
+ if ((zone.start - 1) // granule + 1) * granule >= zone.stop:
+ continue
+ if not any([v[0] == "hit" for v in value]):
+ continue
+
+ labels = set([v[0] for v in value if v[0] != "hit"])
+ print(f"\t{zone.start:9x} - {zone.stop:9x} | {' '.join(labels)}")
diff --git a/tools/proxyclient/experiments/mtp.py b/tools/proxyclient/experiments/mtp.py
new file mode 100644
index 0000000..56a72b1
--- /dev/null
+++ b/tools/proxyclient/experiments/mtp.py
@@ -0,0 +1,129 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib, fnmatch
+import time
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+import struct
+from m1n1.setup import *
+from m1n1.fw.asc import StandardASC
+from m1n1.hw.dart8110 import DART8110
+from m1n1.hw.dockchannel import DockChannel
+from m1n1.fw.smc import SMCClient, SMCError
+from m1n1.shell import run_shell
+from m1n1.fw.mtp import *
+
+from construct import *
+
+mon = RegMonitor(u)
+
+#mon.add(0x23b700000, 0x10000)
+mon.add(0x23d28c000, 0x4000)
+mon.poll()
+mon.poll()
+mon.poll()
+mon.add(0x24e400000, 0x4000)
+mon.add(0x24e808000, 0x14000)
+
+smc_addr = u.adt["arm-io/smc"].get_reg(0)[0]
+smc = SMCClient(u, smc_addr)
+smc.start()
+smc.start_ep(0x20)
+smc.verbose = 0
+
+p.dapf_init_all()
+
+dart = DART8110.from_adt(u, "/arm-io/dart-mtp", iova_range=(0x8000, 0x100000))
+
+dart.regs.TCR[1].set(BYPASS_DAPF=0, BYPASS_DART=0, TRANSLATE_ENABLE=1)
+
+irq_base = u.adt["/arm-io/dockchannel-mtp"].get_reg(1)[0]
+fifo_base = u.adt["/arm-io/dockchannel-mtp"].get_reg(2)[0]
+dc = DockChannel(u, irq_base, fifo_base, 1)
+
+node = u.adt["/arm-io/dockchannel-mtp/mtp-transport"]
+
+while dc.rx_count:
+ dc.read(dc.rx_count)
+
+mtp_addr = u.adt["/arm-io/mtp"].get_reg(0)[0]
+mtp = StandardASC(u, mtp_addr, dart, stream=1)
+mtp.allow_phys = True
+print("pre start")
+mon.poll()
+mtp.start()
+print("started")
+mon.poll()
+print("ok")
+
+def poll():
+ mtp.work()
+ mp.work_pending()
+
+# 0x40: device reset
+# 0x42:
+
+# 0 -> mbox?
+# 2 -> dapf?
+# 3 -> dart?
+# 3 -> dockchannel?
+# 5 -> mbox?
+def reset(i):
+ reg = 0x23d28c000 + i*8
+ p.set32(reg, 1<<10)
+ p.set32(reg, 1<<31)
+ p.clear32(reg, 1<<31)
+ p.clear32(reg, 1<<10)
+
+try:
+
+ mp = MTPProtocol(u, node, mtp, dc, smc)
+
+ mp.wait_init("keyboard")
+ #mp.wait_init("multi_touch")
+ mp.wait_init("stm")
+
+ mtp.stop()
+ mtp.start()
+ mon.poll()
+
+ for i in range(256):
+ mp.stm.get_report(i)
+ mtp.work()
+ mp.work_pending()
+
+ #for i in range(256):
+ #if i in (0x40, 0x42):
+ #continue
+ #m = UnkDeviceControlMsg()
+ #m.command = i
+ #for args in (b"", b"\x00", b"\x01", b"\x02",
+ #b"\x01\x00", b"\x01\x01", b"\x01\x02",
+ #b"\x00\x01", b"\x00\x02", b"\x00\x00",
+ #b"\x00\x00\x00",
+ #b"\x00\x00\x00\x00",
+ #b"\x00\x00\x00\x00\x00",
+ #b"\x00\x00\x00\x00\x00\x00",
+ #b"\x00\x00\x00\x00\x00\x00\x00",
+ #b"\x00\x00\x00\x00\x00\x00\x00\x00",):
+ #m.args = args
+ #print(f"{m.command:#x} {m.args.hex()}")
+ #mp.comm.device_control(m)
+
+ #mon.poll()
+ #mtp.stop()
+ #mon.poll()
+ #mtp.start()
+
+ #mon.poll()
+ #mtp.stop(1)
+ ##reset(1)
+ ##p.dapf_init_all()
+
+ #mtp.boot()
+
+ run_shell(locals(), poll_func=poll)
+
+finally:
+ #mtp.stop()
+ p.reboot()
diff --git a/tools/proxyclient/experiments/ohmmeter.py b/tools/proxyclient/experiments/ohmmeter.py
new file mode 100755
index 0000000..9ab24dd
--- /dev/null
+++ b/tools/proxyclient/experiments/ohmmeter.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+import time
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+from m1n1.hw.i2c import I2C, I2CRegMapDev
+from m1n1.hw.codecs.cs42l84 import *
+
+class CS42L84(I2CRegMapDev):
+ REGMAP = CS42L84Regs
+ ADDRESSING = (0, 2)
+
+def read_devid():
+ pass
+
+def sense_Z():
+ r = l84.regs
+
+ r.HS_CLAMP_DISABLE.set(HS_CLAMP_DISABLE=1)
+ r.DAC_CTRL2.set(PULLDOWN_R=E_PULLDOWN_R.R_1K1OHMS)
+ r.DCID_CTRL1.set(Z_RANGE=E_DCID_Z_RANGE.UNK2)
+ r.DCID_CTRL2.set(GND_SEL=E_DCID_GND_SEL.HS3)
+ r.MSM_BLOCK_EN3.set(DCID_EN=1)
+ r.DCID_CTRL3.set(START=0)
+ r.DCID_CTRL3.set(START=1)
+
+ while not r.DCID_STATUS.reg.DONE:
+ pass
+
+ reading = r.DCID_STATUS.reg.OVERALL
+ offset_trim = r.DCID_TRIM_OFFSET.val - 128
+ slope_trim = r.DCID_TRIM_SLOPE.val - 128
+ pulldown_trim = r.DCID_PULLDOWN_TRIM.val - 128
+
+ Y_overall = ((reading + 0.5) * 0.01086 - 1.0 / (1 - slope_trim * 0.001375)) \
+ / (614.0 + offset_trim * 0.125)
+ Y_pulldown = 1.0 / (1100 - pulldown_trim*2)
+
+ if Y_overall > Y_pulldown:
+ Z_headphones = 1.0 / (Y_overall - Y_pulldown)
+ else:
+ Z_headphones = float('inf')
+
+ r.MSM_BLOCK_EN3.set(DCID_EN=0)
+ r.DAC_CTRL2.set(PULLDOWN_R=E_PULLDOWN_R.NONE)
+
+ return Z_headphones
+
+def init_ring_tip_sense():
+ l84.regs.MIC_DET_CTRL4.set(LATCH_TO_VP=1)
+ l84.regs.TIP_SENSE_CTRL2.set(CTRL=E_TIP_SENSE_CTRL.SHORT_DET)
+
+ l84.regs.RING_SENSE_CTRL.set(INV=1, UNK1=1,
+ RISETIME=E_DEBOUNCE_TIME.T_125MS, FALLTIME=E_DEBOUNCE_TIME.T_125MS)
+ l84.regs.TIP_SENSE_CTRL.set(INV=1,
+ RISETIME=E_DEBOUNCE_TIME.T_500MS, FALLTIME=E_DEBOUNCE_TIME.T_125MS)
+ l84.regs.MSM_BLOCK_EN3.set(TR_SENSE_EN=1)
+
+def wait_for_plug():
+ while not l84.regs.TR_SENSE_STATUS.reg.TIP_PLUG:
+ time.sleep(0.001)
+
+def wait_for_unplug():
+ while l84.regs.TR_SENSE_STATUS.reg.TIP_UNPLUG:
+ time.sleep(0.001)
+
+
+p.pmgr_adt_clocks_enable("/arm-io/i2c2")
+i2c2 = I2C(u, "/arm-io/i2c2")
+
+p.write32(0x2921f0010, 0x76a02) # invoke reset
+p.write32(0x2921f0010, 0x76a03) # out of reset
+
+l84 = CS42L84(i2c2, 0x4b)
+
+init_ring_tip_sense()
+
+while True:
+ print("Waiting for plug... ", end=""); sys.stdout.flush()
+ wait_for_plug()
+
+ print("measuring... ", end=""); sys.stdout.flush()
+ print(f"{sense_Z():.1f} ohms... ", end=""); sys.stdout.flush()
+
+ wait_for_unplug()
+ print("yanked")
diff --git a/tools/proxyclient/experiments/pcie_enable_devices.py b/tools/proxyclient/experiments/pcie_enable_devices.py
new file mode 100755
index 0000000..6c1efcb
--- /dev/null
+++ b/tools/proxyclient/experiments/pcie_enable_devices.py
@@ -0,0 +1,18 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+from m1n1.fw.smc import SMCClient
+
+smc_addr = u.adt["arm-io/smc"].get_reg(0)[0]
+smc = SMCClient(u, smc_addr, None)
+
+smc.start()
+smc.start_ep(0x20)
+
+smc.smcep.write32("gP0d", 0x800001)
+smc.smcep.write32("gP1a", 1)
+
+smc.stop()
diff --git a/tools/proxyclient/experiments/prores.py b/tools/proxyclient/experiments/prores.py
new file mode 100644
index 0000000..0b4308f
--- /dev/null
+++ b/tools/proxyclient/experiments/prores.py
@@ -0,0 +1,695 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+from m1n1.hw.dart8110 import DART8110, DART8110Regs
+from m1n1.hw.prores import *
+from m1n1.utils import *
+import time
+
+
+def divroundup(val, div):
+ return (val + div - 1) // div
+
+
+def bswp16(x):
+ return (x >> 8) | ((x & 0xFF) << 8)
+
+# ffmpeg -y -i prores-encode-large.png -c:v rawvideo -pix_fmt nv24 prores-encode-large.yuv
+im_W = 1920
+im_H = 1080
+with open('prores-encode-large.yuv', 'rb') as f:
+ im_data = f.read()
+assert len(im_data) == (im_W*im_H) * 3
+image_data_luma = im_data[:im_W*im_H]
+image_data_chroma = im_data[im_W*im_H:]
+
+p.pmgr_adt_clocks_enable(f'/arm-io/dart-apr0')
+p.pmgr_adt_clocks_enable(f'/arm-io/apr0')
+
+dart = DART8110.from_adt(u, f'/arm-io/dart-apr0')
+dart.initialize()
+
+apr_base, _ = u.adt[f'/arm-io/apr0'].get_reg(0)
+apr = ProResRegs(u, apr_base)
+
+print(f"Register 0 (ID?) {apr.REG_0x0}")
+
+# TUNABLES
+apr.MODE = 0x0
+apr.REG_0x118 = apr.REG_0x118.val & ~0x07FF07FF | 0x00000600
+apr.REG_0x148 = apr.REG_0x148.val & ~0x00000001 | 0x00000001
+apr.REG_0x160 = apr.REG_0x160.val & ~0x800F3FFF | 0x800A04FF
+apr.REG_0x164 = apr.REG_0x164.val & ~0x07FF07FF | 0x07800080
+apr.REG_0x170 = apr.REG_0x170.val & ~0x800F3FFF | 0x800404FF
+apr.REG_0x174 = apr.REG_0x174.val & ~0x07FF07FF | 0x06000080
+apr.REG_0x180 = apr.REG_0x180.val & ~0x800F3FFF | 0x800504FF
+apr.REG_0x184 = apr.REG_0x184.val & ~0x07FF07FF | 0x06800080
+apr.REG_0x190 = apr.REG_0x190.val & ~0x800F3FFF | 0x800004FF
+apr.REG_0x194 = apr.REG_0x194.val & ~0x000000FF | 0x00000040
+apr.REG_0x1a0 = apr.REG_0x1a0.val & ~0x800F3FFF | 0x800104FF
+apr.REG_0x1a4 = apr.REG_0x1a4.val & ~0x000000FF | 0x00000080
+apr.REG_0x1b0 = apr.REG_0x1b0.val & ~0x800F3FFF | 0x800204FF
+apr.REG_0x1b4 = apr.REG_0x1b4.val & ~0x000000FF | 0x00000040
+apr.REG_0x1c0 = apr.REG_0x1c0.val & ~0x800F3FFF | 0x800304FF
+apr.REG_0x1c4 = apr.REG_0x1c4.val & ~0x000000FF | 0x00000040
+apr.REG_0x1d0 = apr.REG_0x1d0.val & ~0xBC00FF86 | 0xA4000786
+apr.REG_0x1d4 = apr.REG_0x1d4.val & ~0x000000FF | 0x00000020
+apr.REG_0x1d8 = apr.REG_0x1d8.val & ~0x000000FF | 0x000000FF
+apr.REG_0x1dc = apr.REG_0x1dc.val & ~0x00FFFFFF | 0x00928170
+apr.REG_0x270 = apr.REG_0x270.val & ~0x800F3FFF | 0x800B08FF
+apr.REG_0x274 = apr.REG_0x274.val & ~0x07FF07FF | 0x07000080
+apr.REG_0x280 = apr.REG_0x280.val & ~0xFFFFFFC0 | 0x00180000
+apr.REG_0x290 = apr.REG_0x290.val & ~0x800F3FFF | 0x800004FF
+apr.REG_0x294 = apr.REG_0x294.val & ~0x000000FF | 0x00000080
+apr.REG_0x2a0 = apr.REG_0x2a0.val & ~0x800F3FFF | 0x800104FF
+apr.REG_0x2a4 = apr.REG_0x2a4.val & ~0x000000FF | 0x00000080
+apr.REG_0x2b0 = apr.REG_0x2b0.val & ~0x800F3FFF | 0x800204FF
+apr.REG_0x2b4 = apr.REG_0x2b4.val & ~0x000000FF | 0x00000040
+apr.REG_0x2c0 = apr.REG_0x2c0.val & ~0x800F3FFF | 0x800304FF
+apr.REG_0x2c4 = apr.REG_0x2c4.val & ~0x000000FF | 0x00000040
+apr.REG_0x2d0 = apr.REG_0x2d0.val & ~0x802FF04C | 0x80070040
+apr.REG_0x2d4 = apr.REG_0x2d4.val & ~0x00000001 | 0x00000000
+apr.REG_0x2d8 = apr.REG_0x2d8.val & ~0xFFFF0003 | 0x00FF0003
+apr.REG_0x2e0 = apr.REG_0x2e0.val & ~0x07FF07FF | 0x06000040
+apr.REG_0x2f8 = apr.REG_0x2f8.val & ~0x802FF04C | 0x80081040
+apr.REG_0x2fc = apr.REG_0x2fc.val & ~0x00000001 | 0x00000000
+apr.REG_0x300 = apr.REG_0x300.val & ~0xFFFF0003 | 0x00FF0003
+apr.REG_0x308 = apr.REG_0x308.val & ~0x07FF07FF | 0x06400040
+apr.REG_0x320 = apr.REG_0x320.val & ~0x802FF04C | 0x80092040
+apr.REG_0x324 = apr.REG_0x324.val & ~0x00000001 | 0x00000000
+apr.REG_0x328 = apr.REG_0x328.val & ~0xFFFF0003 | 0x00FF0003
+apr.REG_0x330 = apr.REG_0x330.val & ~0x07FF07FF | 0x06800040
+apr.REG_0x350 = apr.REG_0x350.val & ~0x800F3FFF | 0x800B08FF
+apr.REG_0x354 = apr.REG_0x354.val & ~0x07FF07FF | 0x076000A0
+apr.REG_0x360 = apr.REG_0x360.val & ~0xFFFFFFC0 | 0x00180000
+apr.REG_0x370 = apr.REG_0x370.val & ~0x800F3FFF | 0x800604FF
+apr.REG_0x374 = apr.REG_0x374.val & ~0x07FF07FF | 0x06C000A0
+print("Applied tunables")
+
+# XXX test wrap around behavior
+DESC_RING_SZ = 0x4000
+desc_ring_phys = u.heap.memalign(0x4000, DESC_RING_SZ)
+desc_ring_iova = dart.iomap(0, desc_ring_phys, DESC_RING_SZ)
+print(f"Descriptor ring @ phys {desc_ring_phys:016X} iova {desc_ring_iova:016X}")
+
+apr.DR_HEAD = 0
+apr.DR_TAIL = 0
+apr.DR_SIZE = DESC_RING_SZ
+apr.DR_ADDR_LO = desc_ring_iova & 0xFFFFFFFF
+apr.DR_ADDR_HI = desc_ring_iova >> 32
+
+apr.MODE = 0xd # FIXME: dunno what this means
+
+# MATRICES
+apr.QUANT_LUMA_EHQ[0].val = 0x802802
+apr.QUANT_CHROMA_EHQ[0].val = 0x804804
+apr.QUANT_LUMA_EHQ[1].val = 0x802802
+apr.QUANT_CHROMA_EHQ[1].val = 0x804804
+apr.QUANT_LUMA_EHQ[2].val = 0x802802
+apr.QUANT_CHROMA_EHQ[2].val = 0x804804
+apr.QUANT_LUMA_EHQ[3].val = 0x802802
+apr.QUANT_CHROMA_EHQ[3].val = 0x804804
+apr.QUANT_LUMA_EHQ[4].val = 0x802802
+apr.QUANT_CHROMA_EHQ[4].val = 0x804804
+apr.QUANT_LUMA_EHQ[5].val = 0x802802
+apr.QUANT_CHROMA_EHQ[5].val = 0x804804
+apr.QUANT_LUMA_EHQ[6].val = 0x802802
+apr.QUANT_CHROMA_EHQ[6].val = 0x804804
+apr.QUANT_LUMA_EHQ[7].val = 0x802802
+apr.QUANT_CHROMA_EHQ[7].val = 0x804804
+apr.QUANT_LUMA_EHQ[8].val = 0x802802
+apr.QUANT_CHROMA_EHQ[8].val = 0x804804
+apr.QUANT_LUMA_EHQ[9].val = 0x802802
+apr.QUANT_CHROMA_EHQ[9].val = 0x804804
+apr.QUANT_LUMA_EHQ[10].val = 0x802802
+apr.QUANT_CHROMA_EHQ[10].val = 0x804804
+apr.QUANT_LUMA_EHQ[11].val = 0x802802
+apr.QUANT_CHROMA_EHQ[11].val = 0x804804
+apr.QUANT_LUMA_EHQ[12].val = 0x802802
+apr.QUANT_CHROMA_EHQ[12].val = 0x804804
+apr.QUANT_LUMA_EHQ[13].val = 0x802802
+apr.QUANT_CHROMA_EHQ[13].val = 0x804804
+apr.QUANT_LUMA_EHQ[14].val = 0x802802
+apr.QUANT_CHROMA_EHQ[14].val = 0x804804
+apr.QUANT_LUMA_EHQ[15].val = 0x803802
+apr.QUANT_CHROMA_EHQ[15].val = 0x805804
+apr.QUANT_LUMA_EHQ[16].val = 0x802802
+apr.QUANT_CHROMA_EHQ[16].val = 0x804804
+apr.QUANT_LUMA_EHQ[17].val = 0x802802
+apr.QUANT_CHROMA_EHQ[17].val = 0x804804
+apr.QUANT_LUMA_EHQ[18].val = 0x802802
+apr.QUANT_CHROMA_EHQ[18].val = 0x804804
+apr.QUANT_LUMA_EHQ[19].val = 0x803803
+apr.QUANT_CHROMA_EHQ[19].val = 0x805805
+apr.QUANT_LUMA_EHQ[20].val = 0x802802
+apr.QUANT_CHROMA_EHQ[20].val = 0x804804
+apr.QUANT_LUMA_EHQ[21].val = 0x802802
+apr.QUANT_CHROMA_EHQ[21].val = 0x804804
+apr.QUANT_LUMA_EHQ[22].val = 0x803802
+apr.QUANT_CHROMA_EHQ[22].val = 0x805804
+apr.QUANT_LUMA_EHQ[23].val = 0x803803
+apr.QUANT_CHROMA_EHQ[23].val = 0x806805
+apr.QUANT_LUMA_EHQ[24].val = 0x802802
+apr.QUANT_CHROMA_EHQ[24].val = 0x804804
+apr.QUANT_LUMA_EHQ[25].val = 0x802802
+apr.QUANT_CHROMA_EHQ[25].val = 0x804804
+apr.QUANT_LUMA_EHQ[26].val = 0x803803
+apr.QUANT_CHROMA_EHQ[26].val = 0x805805
+apr.QUANT_LUMA_EHQ[27].val = 0x804803
+apr.QUANT_CHROMA_EHQ[27].val = 0x807806
+apr.QUANT_LUMA_EHQ[28].val = 0x802802
+apr.QUANT_CHROMA_EHQ[28].val = 0x804804
+apr.QUANT_LUMA_EHQ[29].val = 0x802802
+apr.QUANT_CHROMA_EHQ[29].val = 0x804804
+apr.QUANT_LUMA_EHQ[30].val = 0x803803
+apr.QUANT_CHROMA_EHQ[30].val = 0x806805
+apr.QUANT_LUMA_EHQ[31].val = 0x804804
+apr.QUANT_CHROMA_EHQ[31].val = 0x807807
+
+apr.QUANT_LUMA_HQ[0].val = 0x804804
+apr.QUANT_CHROMA_HQ[0].val = 0x804804
+apr.QUANT_LUMA_HQ[1].val = 0x804804
+apr.QUANT_CHROMA_HQ[1].val = 0x804804
+apr.QUANT_LUMA_HQ[2].val = 0x804804
+apr.QUANT_CHROMA_HQ[2].val = 0x804804
+apr.QUANT_LUMA_HQ[3].val = 0x804804
+apr.QUANT_CHROMA_HQ[3].val = 0x804804
+apr.QUANT_LUMA_HQ[4].val = 0x804804
+apr.QUANT_CHROMA_HQ[4].val = 0x804804
+apr.QUANT_LUMA_HQ[5].val = 0x804804
+apr.QUANT_CHROMA_HQ[5].val = 0x804804
+apr.QUANT_LUMA_HQ[6].val = 0x804804
+apr.QUANT_CHROMA_HQ[6].val = 0x804804
+apr.QUANT_LUMA_HQ[7].val = 0x804804
+apr.QUANT_CHROMA_HQ[7].val = 0x804804
+apr.QUANT_LUMA_HQ[8].val = 0x804804
+apr.QUANT_CHROMA_HQ[8].val = 0x804804
+apr.QUANT_LUMA_HQ[9].val = 0x804804
+apr.QUANT_CHROMA_HQ[9].val = 0x804804
+apr.QUANT_LUMA_HQ[10].val = 0x804804
+apr.QUANT_CHROMA_HQ[10].val = 0x804804
+apr.QUANT_LUMA_HQ[11].val = 0x804804
+apr.QUANT_CHROMA_HQ[11].val = 0x804804
+apr.QUANT_LUMA_HQ[12].val = 0x804804
+apr.QUANT_CHROMA_HQ[12].val = 0x804804
+apr.QUANT_LUMA_HQ[13].val = 0x804804
+apr.QUANT_CHROMA_HQ[13].val = 0x804804
+apr.QUANT_LUMA_HQ[14].val = 0x804804
+apr.QUANT_CHROMA_HQ[14].val = 0x804804
+apr.QUANT_LUMA_HQ[15].val = 0x805804
+apr.QUANT_CHROMA_HQ[15].val = 0x805804
+apr.QUANT_LUMA_HQ[16].val = 0x804804
+apr.QUANT_CHROMA_HQ[16].val = 0x804804
+apr.QUANT_LUMA_HQ[17].val = 0x804804
+apr.QUANT_CHROMA_HQ[17].val = 0x804804
+apr.QUANT_LUMA_HQ[18].val = 0x804804
+apr.QUANT_CHROMA_HQ[18].val = 0x804804
+apr.QUANT_LUMA_HQ[19].val = 0x805805
+apr.QUANT_CHROMA_HQ[19].val = 0x805805
+apr.QUANT_LUMA_HQ[20].val = 0x804804
+apr.QUANT_CHROMA_HQ[20].val = 0x804804
+apr.QUANT_LUMA_HQ[21].val = 0x804804
+apr.QUANT_CHROMA_HQ[21].val = 0x804804
+apr.QUANT_LUMA_HQ[22].val = 0x805804
+apr.QUANT_CHROMA_HQ[22].val = 0x805804
+apr.QUANT_LUMA_HQ[23].val = 0x806805
+apr.QUANT_CHROMA_HQ[23].val = 0x806805
+apr.QUANT_LUMA_HQ[24].val = 0x804804
+apr.QUANT_CHROMA_HQ[24].val = 0x804804
+apr.QUANT_LUMA_HQ[25].val = 0x804804
+apr.QUANT_CHROMA_HQ[25].val = 0x804804
+apr.QUANT_LUMA_HQ[26].val = 0x805805
+apr.QUANT_CHROMA_HQ[26].val = 0x805805
+apr.QUANT_LUMA_HQ[27].val = 0x807806
+apr.QUANT_CHROMA_HQ[27].val = 0x807806
+apr.QUANT_LUMA_HQ[28].val = 0x804804
+apr.QUANT_CHROMA_HQ[28].val = 0x804804
+apr.QUANT_LUMA_HQ[29].val = 0x804804
+apr.QUANT_CHROMA_HQ[29].val = 0x804804
+apr.QUANT_LUMA_HQ[30].val = 0x806805
+apr.QUANT_CHROMA_HQ[30].val = 0x806805
+apr.QUANT_LUMA_HQ[31].val = 0x807807
+apr.QUANT_CHROMA_HQ[31].val = 0x807807
+
+apr.QUANT_LUMA_NQ[0].val = 0x804804
+apr.QUANT_CHROMA_NQ[0].val = 0x804804
+apr.QUANT_LUMA_NQ[1].val = 0x805805
+apr.QUANT_CHROMA_NQ[1].val = 0x805805
+apr.QUANT_LUMA_NQ[2].val = 0x807806
+apr.QUANT_CHROMA_NQ[2].val = 0x807806
+apr.QUANT_LUMA_NQ[3].val = 0x809807
+apr.QUANT_CHROMA_NQ[3].val = 0x809807
+apr.QUANT_LUMA_NQ[4].val = 0x804804
+apr.QUANT_CHROMA_NQ[4].val = 0x804804
+apr.QUANT_LUMA_NQ[5].val = 0x806805
+apr.QUANT_CHROMA_NQ[5].val = 0x806805
+apr.QUANT_LUMA_NQ[6].val = 0x807807
+apr.QUANT_CHROMA_NQ[6].val = 0x807807
+apr.QUANT_LUMA_NQ[7].val = 0x809809
+apr.QUANT_CHROMA_NQ[7].val = 0x809809
+apr.QUANT_LUMA_NQ[8].val = 0x805805
+apr.QUANT_CHROMA_NQ[8].val = 0x805805
+apr.QUANT_LUMA_NQ[9].val = 0x807806
+apr.QUANT_CHROMA_NQ[9].val = 0x807806
+apr.QUANT_LUMA_NQ[10].val = 0x809807
+apr.QUANT_CHROMA_NQ[10].val = 0x809807
+apr.QUANT_LUMA_NQ[11].val = 0x80a809
+apr.QUANT_CHROMA_NQ[11].val = 0x80a809
+apr.QUANT_LUMA_NQ[12].val = 0x805805
+apr.QUANT_CHROMA_NQ[12].val = 0x805805
+apr.QUANT_LUMA_NQ[13].val = 0x807806
+apr.QUANT_CHROMA_NQ[13].val = 0x807806
+apr.QUANT_LUMA_NQ[14].val = 0x809807
+apr.QUANT_CHROMA_NQ[14].val = 0x809807
+apr.QUANT_LUMA_NQ[15].val = 0x80a809
+apr.QUANT_CHROMA_NQ[15].val = 0x80a809
+apr.QUANT_LUMA_NQ[16].val = 0x806805
+apr.QUANT_CHROMA_NQ[16].val = 0x806805
+apr.QUANT_LUMA_NQ[17].val = 0x807807
+apr.QUANT_CHROMA_NQ[17].val = 0x807807
+apr.QUANT_LUMA_NQ[18].val = 0x809808
+apr.QUANT_CHROMA_NQ[18].val = 0x809808
+apr.QUANT_LUMA_NQ[19].val = 0x80c80a
+apr.QUANT_CHROMA_NQ[19].val = 0x80c80a
+apr.QUANT_LUMA_NQ[20].val = 0x807806
+apr.QUANT_CHROMA_NQ[20].val = 0x807806
+apr.QUANT_LUMA_NQ[21].val = 0x808807
+apr.QUANT_CHROMA_NQ[21].val = 0x808807
+apr.QUANT_LUMA_NQ[22].val = 0x80a809
+apr.QUANT_CHROMA_NQ[22].val = 0x80a809
+apr.QUANT_LUMA_NQ[23].val = 0x80f80c
+apr.QUANT_CHROMA_NQ[23].val = 0x80f80c
+apr.QUANT_LUMA_NQ[24].val = 0x807806
+apr.QUANT_CHROMA_NQ[24].val = 0x807806
+apr.QUANT_LUMA_NQ[25].val = 0x809807
+apr.QUANT_CHROMA_NQ[25].val = 0x809807
+apr.QUANT_LUMA_NQ[26].val = 0x80b80a
+apr.QUANT_CHROMA_NQ[26].val = 0x80b80a
+apr.QUANT_LUMA_NQ[27].val = 0x81180e
+apr.QUANT_CHROMA_NQ[27].val = 0x81180e
+apr.QUANT_LUMA_NQ[28].val = 0x807807
+apr.QUANT_CHROMA_NQ[28].val = 0x807807
+apr.QUANT_LUMA_NQ[29].val = 0x80a809
+apr.QUANT_CHROMA_NQ[29].val = 0x80a809
+apr.QUANT_LUMA_NQ[30].val = 0x80e80b
+apr.QUANT_CHROMA_NQ[30].val = 0x80e80b
+apr.QUANT_LUMA_NQ[31].val = 0x815811
+apr.QUANT_CHROMA_NQ[31].val = 0x815811
+
+apr.QUANT_LUMA_LT[0].val = 0x805804
+apr.QUANT_CHROMA_LT[0].val = 0x805804
+apr.QUANT_LUMA_LT[1].val = 0x807806
+apr.QUANT_CHROMA_LT[1].val = 0x807806
+apr.QUANT_LUMA_LT[2].val = 0x80b809
+apr.QUANT_CHROMA_LT[2].val = 0x80b809
+apr.QUANT_LUMA_LT[3].val = 0x80f80d
+apr.QUANT_CHROMA_LT[3].val = 0x80f80d
+apr.QUANT_LUMA_LT[4].val = 0x805805
+apr.QUANT_CHROMA_LT[4].val = 0x805805
+apr.QUANT_LUMA_LT[5].val = 0x808807
+apr.QUANT_CHROMA_LT[5].val = 0x808807
+apr.QUANT_LUMA_LT[6].val = 0x80d80b
+apr.QUANT_CHROMA_LT[6].val = 0x80d80b
+apr.QUANT_LUMA_LT[7].val = 0x81180f
+apr.QUANT_CHROMA_LT[7].val = 0x81180f
+apr.QUANT_LUMA_LT[8].val = 0x807806
+apr.QUANT_CHROMA_LT[8].val = 0x807806
+apr.QUANT_LUMA_LT[9].val = 0x80b809
+apr.QUANT_CHROMA_LT[9].val = 0x80b809
+apr.QUANT_LUMA_LT[10].val = 0x80f80d
+apr.QUANT_CHROMA_LT[10].val = 0x80f80d
+apr.QUANT_LUMA_LT[11].val = 0x81180f
+apr.QUANT_CHROMA_LT[11].val = 0x81180f
+apr.QUANT_LUMA_LT[12].val = 0x807807
+apr.QUANT_CHROMA_LT[12].val = 0x807807
+apr.QUANT_LUMA_LT[13].val = 0x80b809
+apr.QUANT_CHROMA_LT[13].val = 0x80b809
+apr.QUANT_LUMA_LT[14].val = 0x80f80d
+apr.QUANT_CHROMA_LT[14].val = 0x80f80d
+apr.QUANT_LUMA_LT[15].val = 0x813811
+apr.QUANT_CHROMA_LT[15].val = 0x813811
+apr.QUANT_LUMA_LT[16].val = 0x809807
+apr.QUANT_CHROMA_LT[16].val = 0x809807
+apr.QUANT_LUMA_LT[17].val = 0x80d80b
+apr.QUANT_CHROMA_LT[17].val = 0x80d80b
+apr.QUANT_LUMA_LT[18].val = 0x81080e
+apr.QUANT_CHROMA_LT[18].val = 0x81080e
+apr.QUANT_LUMA_LT[19].val = 0x817813
+apr.QUANT_CHROMA_LT[19].val = 0x817813
+apr.QUANT_LUMA_LT[20].val = 0x80b809
+apr.QUANT_CHROMA_LT[20].val = 0x80b809
+apr.QUANT_LUMA_LT[21].val = 0x80e80d
+apr.QUANT_CHROMA_LT[21].val = 0x80e80d
+apr.QUANT_LUMA_LT[22].val = 0x813810
+apr.QUANT_CHROMA_LT[22].val = 0x813810
+apr.QUANT_LUMA_LT[23].val = 0x81d817
+apr.QUANT_CHROMA_LT[23].val = 0x81d817
+apr.QUANT_LUMA_LT[24].val = 0x80b809
+apr.QUANT_CHROMA_LT[24].val = 0x80b809
+apr.QUANT_LUMA_LT[25].val = 0x80f80d
+apr.QUANT_CHROMA_LT[25].val = 0x80f80d
+apr.QUANT_LUMA_LT[26].val = 0x815811
+apr.QUANT_CHROMA_LT[26].val = 0x815811
+apr.QUANT_LUMA_LT[27].val = 0x82381c
+apr.QUANT_CHROMA_LT[27].val = 0x82381c
+apr.QUANT_LUMA_LT[28].val = 0x80d80b
+apr.QUANT_CHROMA_LT[28].val = 0x80d80b
+apr.QUANT_LUMA_LT[29].val = 0x811810
+apr.QUANT_CHROMA_LT[29].val = 0x811810
+apr.QUANT_LUMA_LT[30].val = 0x81c815
+apr.QUANT_CHROMA_LT[30].val = 0x81c815
+apr.QUANT_LUMA_LT[31].val = 0x829823
+apr.QUANT_CHROMA_LT[31].val = 0x829823
+
+apr.QUANT_LUMA_PROXY[0].val = 0x807804
+apr.QUANT_CHROMA_PROXY[0].val = 0x807804
+apr.QUANT_LUMA_PROXY[1].val = 0x80b809
+apr.QUANT_CHROMA_PROXY[1].val = 0x80b809
+apr.QUANT_LUMA_PROXY[2].val = 0x80e80d
+apr.QUANT_CHROMA_PROXY[2].val = 0x80e80d
+apr.QUANT_LUMA_PROXY[3].val = 0xfff83f
+apr.QUANT_CHROMA_PROXY[3].val = 0xfff83f
+apr.QUANT_LUMA_PROXY[4].val = 0x807807
+apr.QUANT_CHROMA_PROXY[4].val = 0x807807
+apr.QUANT_LUMA_PROXY[5].val = 0x80c80b
+apr.QUANT_CHROMA_PROXY[5].val = 0x80c80b
+apr.QUANT_LUMA_PROXY[6].val = 0x83f80e
+apr.QUANT_CHROMA_PROXY[6].val = 0x83f80e
+apr.QUANT_LUMA_PROXY[7].val = 0xffffff
+apr.QUANT_CHROMA_PROXY[7].val = 0xffffff
+apr.QUANT_LUMA_PROXY[8].val = 0x80b809
+apr.QUANT_CHROMA_PROXY[8].val = 0x80b809
+apr.QUANT_LUMA_PROXY[9].val = 0x80e80d
+apr.QUANT_CHROMA_PROXY[9].val = 0x80e80d
+apr.QUANT_LUMA_PROXY[10].val = 0xfff83f
+apr.QUANT_CHROMA_PROXY[10].val = 0xfff83f
+apr.QUANT_LUMA_PROXY[11].val = 0xffffff
+apr.QUANT_CHROMA_PROXY[11].val = 0xffffff
+apr.QUANT_LUMA_PROXY[12].val = 0x80b80b
+apr.QUANT_CHROMA_PROXY[12].val = 0x80b80b
+apr.QUANT_LUMA_PROXY[13].val = 0x80e80d
+apr.QUANT_CHROMA_PROXY[13].val = 0x80e80d
+apr.QUANT_LUMA_PROXY[14].val = 0xffffff
+apr.QUANT_CHROMA_PROXY[14].val = 0xffffff
+apr.QUANT_LUMA_PROXY[15].val = 0xffffff
+apr.QUANT_CHROMA_PROXY[15].val = 0xffffff
+apr.QUANT_LUMA_PROXY[16].val = 0x80d80b
+apr.QUANT_CHROMA_PROXY[16].val = 0x80d80b
+apr.QUANT_LUMA_PROXY[17].val = 0xfff80e
+apr.QUANT_CHROMA_PROXY[17].val = 0xfff80e
+apr.QUANT_LUMA_PROXY[18].val = 0xffffff
+apr.QUANT_CHROMA_PROXY[18].val = 0xffffff
+apr.QUANT_LUMA_PROXY[19].val = 0xffffff
+apr.QUANT_CHROMA_PROXY[19].val = 0xffffff
+apr.QUANT_LUMA_PROXY[20].val = 0x80e80d
+apr.QUANT_CHROMA_PROXY[20].val = 0x80e80d
+apr.QUANT_LUMA_PROXY[21].val = 0xffffff
+apr.QUANT_CHROMA_PROXY[21].val = 0xffffff
+apr.QUANT_LUMA_PROXY[22].val = 0xffffff
+apr.QUANT_CHROMA_PROXY[22].val = 0xffffff
+apr.QUANT_LUMA_PROXY[23].val = 0xffffff
+apr.QUANT_CHROMA_PROXY[23].val = 0xffffff
+apr.QUANT_LUMA_PROXY[24].val = 0xfff80d
+apr.QUANT_CHROMA_PROXY[24].val = 0xfff80d
+apr.QUANT_LUMA_PROXY[25].val = 0xffffff
+apr.QUANT_CHROMA_PROXY[25].val = 0xffffff
+apr.QUANT_LUMA_PROXY[26].val = 0xffffff
+apr.QUANT_CHROMA_PROXY[26].val = 0xffffff
+apr.QUANT_LUMA_PROXY[27].val = 0xffffff
+apr.QUANT_CHROMA_PROXY[27].val = 0xffffff
+apr.QUANT_LUMA_PROXY[28].val = 0xffffff
+apr.QUANT_CHROMA_PROXY[28].val = 0xffffff
+apr.QUANT_LUMA_PROXY[29].val = 0xffffff
+apr.QUANT_CHROMA_PROXY[29].val = 0xffffff
+apr.QUANT_LUMA_PROXY[30].val = 0xffffff
+apr.QUANT_CHROMA_PROXY[30].val = 0xffffff
+apr.QUANT_LUMA_PROXY[31].val = 0xffffff
+apr.QUANT_CHROMA_PROXY[31].val = 0xffffff
+
+apr.DC_QUANT_SCALE[0].val = 0x401
+apr.DC_QUANT_SCALE[1].val = 0x803
+apr.DC_QUANT_SCALE[2].val = 0xc05
+apr.DC_QUANT_SCALE[3].val = 0x1007
+apr.DC_QUANT_SCALE[4].val = 0x1409
+apr.DC_QUANT_SCALE[5].val = 0x180b
+apr.DC_QUANT_SCALE[6].val = 0x1c0d
+apr.DC_QUANT_SCALE[7].val = 0x200f
+apr.DC_QUANT_SCALE[8].val = 0x2411
+apr.DC_QUANT_SCALE[9].val = 0x2813
+apr.DC_QUANT_SCALE[10].val = 0x2c15
+apr.DC_QUANT_SCALE[11].val = 0x3017
+apr.DC_QUANT_SCALE[12].val = 0x3419
+apr.DC_QUANT_SCALE[13].val = 0x381b
+apr.DC_QUANT_SCALE[14].val = 0x3c1d
+apr.DC_QUANT_SCALE[15].val = 0x401f
+apr.DC_QUANT_SCALE[16].val = 0x4421
+apr.DC_QUANT_SCALE[17].val = 0x4823
+apr.DC_QUANT_SCALE[18].val = 0x4c25
+apr.DC_QUANT_SCALE[19].val = 0x5027
+apr.DC_QUANT_SCALE[20].val = 0x5429
+apr.DC_QUANT_SCALE[21].val = 0x582b
+apr.DC_QUANT_SCALE[22].val = 0x5c2d
+apr.DC_QUANT_SCALE[23].val = 0x602f
+apr.DC_QUANT_SCALE[24].val = 0x6431
+apr.DC_QUANT_SCALE[25].val = 0x6833
+apr.DC_QUANT_SCALE[26].val = 0x6c35
+apr.DC_QUANT_SCALE[27].val = 0x7037
+apr.DC_QUANT_SCALE[28].val = 0x7439
+apr.DC_QUANT_SCALE[29].val = 0x783b
+apr.DC_QUANT_SCALE[30].val = 0x7c3d
+apr.DC_QUANT_SCALE[31].val = 0x803f
+apr.DC_QUANT_SCALE[32].val = 0x8441
+apr.DC_QUANT_SCALE[33].val = 0x8843
+apr.DC_QUANT_SCALE[34].val = 0x8c45
+apr.DC_QUANT_SCALE[35].val = 0x9047
+apr.DC_QUANT_SCALE[36].val = 0x9449
+apr.DC_QUANT_SCALE[37].val = 0x984b
+apr.DC_QUANT_SCALE[38].val = 0x9c4d
+apr.DC_QUANT_SCALE[39].val = 0xa04f
+apr.DC_QUANT_SCALE[40].val = 0xa451
+apr.DC_QUANT_SCALE[41].val = 0xa853
+apr.DC_QUANT_SCALE[42].val = 0xac55
+apr.DC_QUANT_SCALE[43].val = 0xb057
+apr.DC_QUANT_SCALE[44].val = 0xb459
+apr.DC_QUANT_SCALE[45].val = 0xb85b
+apr.DC_QUANT_SCALE[46].val = 0xbc5d
+apr.DC_QUANT_SCALE[47].val = 0xc05f
+apr.DC_QUANT_SCALE[48].val = 0xc461
+apr.DC_QUANT_SCALE[49].val = 0xc863
+apr.DC_QUANT_SCALE[50].val = 0xcc65
+apr.DC_QUANT_SCALE[51].val = 0xd067
+apr.DC_QUANT_SCALE[52].val = 0xd469
+apr.DC_QUANT_SCALE[53].val = 0xd86b
+apr.DC_QUANT_SCALE[54].val = 0xdc6d
+apr.DC_QUANT_SCALE[55].val = 0xe06f
+apr.DC_QUANT_SCALE[56].val = 0xe471
+apr.DC_QUANT_SCALE[57].val = 0xe873
+apr.DC_QUANT_SCALE[58].val = 0xec75
+apr.DC_QUANT_SCALE[59].val = 0xf077
+apr.DC_QUANT_SCALE[60].val = 0xf479
+apr.DC_QUANT_SCALE[61].val = 0xf87b
+apr.DC_QUANT_SCALE[62].val = 0xfc7d
+apr.DC_QUANT_SCALE[63].val = 0x1007f
+apr.DC_QUANT_SCALE[64].val = 0x11084
+apr.DC_QUANT_SCALE[65].val = 0x1208c
+apr.DC_QUANT_SCALE[66].val = 0x13094
+apr.DC_QUANT_SCALE[67].val = 0x1409c
+apr.DC_QUANT_SCALE[68].val = 0x150a4
+apr.DC_QUANT_SCALE[69].val = 0x160ac
+apr.DC_QUANT_SCALE[70].val = 0x170b4
+apr.DC_QUANT_SCALE[71].val = 0x180bc
+apr.DC_QUANT_SCALE[72].val = 0x190c4
+apr.DC_QUANT_SCALE[73].val = 0x1a0cc
+apr.DC_QUANT_SCALE[74].val = 0x1b0d4
+apr.DC_QUANT_SCALE[75].val = 0x1c0dc
+apr.DC_QUANT_SCALE[76].val = 0x1d0e4
+apr.DC_QUANT_SCALE[77].val = 0x1e0ec
+apr.DC_QUANT_SCALE[78].val = 0x1f0f4
+apr.DC_QUANT_SCALE[79].val = 0x200fc
+apr.DC_QUANT_SCALE[80].val = 0x21104
+apr.DC_QUANT_SCALE[81].val = 0x2210c
+apr.DC_QUANT_SCALE[82].val = 0x23114
+apr.DC_QUANT_SCALE[83].val = 0x2411c
+apr.DC_QUANT_SCALE[84].val = 0x25124
+apr.DC_QUANT_SCALE[85].val = 0x2612c
+apr.DC_QUANT_SCALE[86].val = 0x27134
+apr.DC_QUANT_SCALE[87].val = 0x2813c
+apr.DC_QUANT_SCALE[88].val = 0x29144
+apr.DC_QUANT_SCALE[89].val = 0x2a14c
+apr.DC_QUANT_SCALE[90].val = 0x2b154
+apr.DC_QUANT_SCALE[91].val = 0x2c15c
+apr.DC_QUANT_SCALE[92].val = 0x2d164
+apr.DC_QUANT_SCALE[93].val = 0x2e16c
+apr.DC_QUANT_SCALE[94].val = 0x2f174
+apr.DC_QUANT_SCALE[95].val = 0x3017c
+apr.DC_QUANT_SCALE[96].val = 0x31184
+apr.DC_QUANT_SCALE[97].val = 0x3218c
+apr.DC_QUANT_SCALE[98].val = 0x33194
+apr.DC_QUANT_SCALE[99].val = 0x3419c
+apr.DC_QUANT_SCALE[100].val = 0x351a4
+apr.DC_QUANT_SCALE[101].val = 0x361ac
+apr.DC_QUANT_SCALE[102].val = 0x371b4
+apr.DC_QUANT_SCALE[103].val = 0x381bc
+apr.DC_QUANT_SCALE[104].val = 0x391c4
+apr.DC_QUANT_SCALE[105].val = 0x3a1cc
+apr.DC_QUANT_SCALE[106].val = 0x3b1d4
+apr.DC_QUANT_SCALE[107].val = 0x3c1dc
+apr.DC_QUANT_SCALE[108].val = 0x3d1e4
+apr.DC_QUANT_SCALE[109].val = 0x3e1ec
+apr.DC_QUANT_SCALE[110].val = 0x3f1f4
+apr.DC_QUANT_SCALE[111].val = 0x1fc
+print("Set matrices")
+
+# dunno how this gets calculated
+OUT_SZ = 0x1000000
+out_buf_phys = u.heap.memalign(0x4000, OUT_SZ)
+iface.writemem(out_buf_phys, b'\xAA' * OUT_SZ)
+out_buf_iova = dart.iomap(0, out_buf_phys, OUT_SZ)
+print(f"Output buffer @ phys {out_buf_phys:016X} iova {out_buf_iova:016X}")
+
+IN_SZ_LUMA = align_up(im_W*im_H)
+in_buf_luma_phys = u.heap.memalign(0x4000, IN_SZ_LUMA)
+iface.writemem(in_buf_luma_phys, image_data_luma + b'\xaa' * (IN_SZ_LUMA - len(image_data_luma)))
+in_buf_luma_iova = dart.iomap(0, in_buf_luma_phys, IN_SZ_LUMA)
+print(f"Input buffer luma @ phys {in_buf_luma_phys:016X} iova {in_buf_luma_iova:016X}")
+IN_SZ_CHROMA = align_up(im_W*2*im_H)
+in_buf_chroma_phys = u.heap.memalign(0x4000, IN_SZ_CHROMA)
+iface.writemem(in_buf_chroma_phys, image_data_chroma + b'\xaa' * (IN_SZ_CHROMA - len(image_data_chroma)))
+in_buf_chroma_iova = dart.iomap(0, in_buf_chroma_phys, IN_SZ_CHROMA)
+print(f"Input buffer chroma @ phys {in_buf_chroma_phys:016X} iova {in_buf_chroma_iova:016X}")
+dart.dump_all()
+
+desc = EncodeNotRawDescriptor(
+ flags=0x373c,
+ flags2=0,
+ output_iova=out_buf_iova,
+ max_out_sz=OUT_SZ,
+ offset_x=0,
+ offset_y=0,
+ # this is the important set
+ pix_surface_w_2_=im_W,
+ pix_surface_h_2_=im_H,
+ # changing this doesn't seem to break anything
+ pix_surface_w=im_W,
+ pix_surface_h=im_H,
+ # XXX how does the div work exactly? it's different in "tiled" mode
+ luma_stride=divroundup(im_W, 64),
+ chroma_stride=divroundup(im_W*2, 64),
+ alpha_stride=divroundup(im_W, 64),
+ unk_pad_0x26_=b'\x00\x00',
+
+ luma_iova=in_buf_luma_iova,
+ pix_plane0_tileheader_thing_=0,
+ chroma_iova=in_buf_chroma_iova,
+ pix_plane1_tileheader_thing_=0,
+ alpha_iova=in_buf_luma_iova,
+ pix_plane2_tileheader_thing_=0,
+
+ # changing this does add extra 0 bytes
+ frame_header_sz=bswp16(0x94),
+ unk_pad_0x5a_=b'\x00',
+ bitstream_version=0,
+ encoder_identifier=0xcafeface,
+ # cannot change arbitrily, will break
+ pix_surface_w_byteswap_=bswp16(im_W),
+ pix_surface_h_byteswap_=bswp16(im_H),
+ # seemingly can change arbitrarily
+ chroma_format_interlace_mode=0xc0,
+ aspect_ratio_frame_rate=0,
+ color_primaries=2,
+ transfer_characteristic=2,
+ matrix_coefficients=1,
+ alpha_channel_type=1,
+ # tables will still be output even if bits not set here
+ frame_hdr_reserved14=b'\x00\x03',
+ unk_pad_0x6c_=b'\x00' * 128,
+ deprecated_number_of_slices=0,
+ # this one affects the encoding not just the header
+ log2_desired_slice_size_in_mb=0x30,
+ quantization_index=0x2,
+
+ # this impacts the quality somehow, not quite understood
+ # might be a target bitrate
+ unk_0xf0_=0xffff,
+ unk_0xf2_=0xffff,
+
+ # none of this stuff is understood, and it never seems to change
+ unk_0xf4_=0x8000402015100c0c,
+ unk_0xfc_=0x2c8080,
+ unk_0x100_0_=0x880080,
+ unk_0x100_1_=0x4e00c5,
+ unk_0x100_2_=0x9000d0,
+ unk_0x100_3_=0x200122,
+ unk_0x110_0_=0x400200, # looks like a quant table, but ??? not used ???
+ unk_0x110_1_=0x400200,
+ unk_0x110_2_=0x400200,
+ unk_0x110_3_=0x400200,
+ unk_0x110_4_=0x400200,
+ unk_0x110_5_=0x400200,
+ unk_0x110_6_=0x400200,
+ unk_0x110_7_=0x400200,
+ unk_0x110_8_=0x400200,
+ unk_0x110_9_=0x400200,
+ unk_0x110_10_=0x400200,
+ unk_0x110_11_=0x400200,
+ unk_0x110_12_=0x400200,
+ unk_0x110_13_=0x400200,
+ unk_0x110_14_=0x400200,
+ unk_0x110_15_=0x400200,
+
+ quant_table_sel=0x23,
+ unk_pad_0x154_=b'\x00' * 44,
+)
+desc_bytes = struct.pack(ENCODE_NOT_RAW_STRUCT, *desc)
+chexdump(desc_bytes)
+
+iface.writemem(desc_ring_phys, desc_bytes)
+
+# let's go
+apr.DR_HEAD = len(desc_bytes)
+
+start_time = time.time()
+while apr.IRQ_STATUS.val == 0:
+ if time.time() - start_time > 5:
+ print("TIMED OUT!!!")
+ break
+
+print(f"Done, IRQ status is {apr.IRQ_STATUS}")
+print(f"ST0 = {apr.ST0}")
+print(f"ST1 = {apr.ST1}")
+print(f"REG_0x1c = {apr.REG_0x1c}")
+print(f"REG_0x3c = {apr.REG_0x3c}")
+print(f"REG_0x44 = {apr.REG_0x44}")
+
+print(f"DR_HEAD = {apr.DR_HEAD}")
+print(f"DR_TAIL = {apr.DR_TAIL}")
+
+print(f"unk REG_0x38 = {apr.REG_0x38}")
+print(f"unk REG_0x40 = {apr.REG_0x40}")
+print(f"unk REG_0x48 = {apr.REG_0x48}")
+print(f"unk REG_0x50 = {apr.REG_0x50}")
+print(f"unk REG_0x54 = {apr.REG_0x54}")
+
+apr.IRQ_STATUS = apr.IRQ_STATUS.val
+
+dr_memory_new = iface.readmem(desc_ring_phys, DESC_RING_SZ)
+chexdump(dr_memory_new)
+
+out_buf_new = iface.readmem(out_buf_phys, OUT_SZ)
+with open('prores.bin', 'wb') as f:
+ f.write(out_buf_new)
+
+outlen = struct.unpack(">I", out_buf_new[:4])[0]
+if outlen <= len(out_buf_new):
+ with open('prores.mov', 'wb') as f:
+ f.write(b'\x00\x00\x00\x14\x66\x74\x79\x70\x71\x74\x20\x20\x00\x00\x02\x00\x71\x74\x20\x20\x00\x00\x00\x08\x77\x69\x64\x65')
+ f.write(struct.pack(">I", outlen + 8))
+ f.write(b'mdat')
+ f.write(out_buf_new[:outlen])
+ f.write(b'\x00\x00\x03\x1e\x6d\x6f\x6f\x76\x00\x00\x00\x6c\x6d\x76\x68\x64\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xe8\x00\x00\x00\x11\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x02\x89\x74\x72\x61\x6b\x00\x00\x00\x5c\x74\x6b\x68\x64\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x07\x80\x00\x00\x04\x38\x00\x00\x00\x00\x00\x24\x65\x64\x74\x73\x00\x00\x00\x1c\x65\x6c\x73\x74\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x11\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x02\x01\x6d\x64\x69\x61\x00\x00\x00\x20\x6d\x64\x68\x64\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3c\x00\x00\x00\x01\x00\x7f\xff\x00\x00\x00\x00\x00\x2d\x68\x64\x6c\x72\x00\x00\x00\x00\x6d\x68\x6c\x72\x76\x69\x64\x65\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x56\x69\x64\x65\x6f\x48\x61\x6e\x64\x6c\x65\x72\x00\x00\x01\xac\x6d\x69\x6e\x66\x00\x00\x00\x14\x76\x6d\x68\x64\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x2c\x68\x64\x6c\x72\x00\x00\x00\x00\x64\x68\x6c\x72\x75\x72\x6c\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x44\x61\x74\x61\x48\x61\x6e\x64\x6c\x65\x72\x00\x00\x00\x24\x64\x69\x6e\x66\x00\x00\x00\x1c\x64\x72\x65\x66\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x0c\x75\x72\x6c\x20\x00\x00\x00\x01\x00\x00\x01\x40\x73\x74\x62\x6c\x00\x00\x00\xdc\x73\x74\x73\x64\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\xcc\x61\x70\x63\x6e\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x46\x46\x4d\x50\x00\x00\x02\x00\x00\x00\x02\x00\x07\x80\x04\x38\x00\x48\x00\x00\x00\x48\x00\x00\x00\x00\x00\x00\x00\x01\x1f\x4c\x61\x76\x63\x35\x39\x2e\x32\x35\x2e\x31\x30\x30\x20\x70\x72\x6f\x72\x65\x73\x5f\x76\x69\x64\x65\x6f\x74\x6f\x6f\x6c\x62\x00\x18\xff\xff\x00\x00\x00\x6c\x67\x6c\x62\x6c\x00\x00\x00\x64\x61\x70\x63\x6e\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xff\x07\x80\x04\x38\x00\x48\x00\x00\x00\x48\x00\x00\x00\x00\x00\x00\x00\x01\x10\x41\x70\x70\x6c\x65\x20\x50\x72\x6f\x52\x65\x73\x20\x34\x32\x32\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x18\xff\xff\x00\x00\x00\x0a\x66\x69\x65\x6c\x01\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x66\x69\x65\x6c\x01\x00\x00\x00\x00\x18\x73\x74\x74\x73\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x01\x00\x00\x00\x00\x1c\x73\x74\x73\x63\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x14\x73\x74\x73\x7a\x00\x00\x00\x00')
+ f.write(struct.pack(">I", outlen))
+ f.write(b'\x00\x00\x00\x01\x00\x00\x00\x14\x73\x74\x63\x6f\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x24\x00\x00\x00\x21\x75\x64\x74\x61\x00\x00\x00\x19\xa9\x73\x77\x72\x00\x0d\x55\xc4\x4c\x61\x76\x66\x35\x39\x2e\x32\x30\x2e\x31\x30\x31')
+# ffmpeg -i prores.mov prores-dec%d.png
diff --git a/tools/proxyclient/experiments/scaler.py b/tools/proxyclient/experiments/scaler.py
new file mode 100644
index 0000000..2a319ed
--- /dev/null
+++ b/tools/proxyclient/experiments/scaler.py
@@ -0,0 +1,1527 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+from m1n1.hw.dart import DART
+from m1n1.hw.scaler import *
+from m1n1.utils import *
+import struct
+import time
+from PIL import Image, ImageDraw
+
+SCALER_ADT = '/arm-io/scaler0'
+DART_ADT = '/arm-io/dart-scaler0'
+
+p.pmgr_adt_clocks_enable(DART_ADT)
+p.pmgr_adt_clocks_enable(SCALER_ADT)
+
+dart = DART.from_adt(u, DART_ADT)
+dart.initialize()
+
+scaler_base, _ = u.adt[SCALER_ADT].get_reg(0)
+apiodma_base, _ = u.adt[SCALER_ADT].get_reg(1)
+dpe_ctrl_base, _ = u.adt[SCALER_ADT].get_reg(2)
+
+scaler = ScalerMainRegs(u, scaler_base)
+
+def dpe_start():
+ p.write32(dpe_ctrl_base + 0x400, 0x1)
+ p.write32(dpe_ctrl_base + 0x404, 0x1)
+ p.write32(dpe_ctrl_base + 0x438, 0xf)
+ p.write32(dpe_ctrl_base + 0x43c, 0x5)
+ p.write32(dpe_ctrl_base + 0x408, 0x1)
+ p.write32(dpe_ctrl_base + 0x440, 0x5)
+ p.write32(dpe_ctrl_base + 0x444, 0x4)
+ p.write32(dpe_ctrl_base + 0x40c, 0x1)
+ p.write32(dpe_ctrl_base + 0x448, 0x5)
+ p.write32(dpe_ctrl_base + 0x44c, 0x5)
+ p.write32(dpe_ctrl_base + 0x410, 0x1)
+ p.write32(dpe_ctrl_base + 0x450, 0x7)
+ p.write32(dpe_ctrl_base + 0x454, 0x7)
+ p.write32(dpe_ctrl_base + 0x414, 0x1)
+ p.write32(dpe_ctrl_base + 0x458, 0xd)
+ p.write32(dpe_ctrl_base + 0x45c, 0xc)
+ p.write32(dpe_ctrl_base + 0x418, 0x1)
+ p.write32(dpe_ctrl_base + 0x460, 0x13)
+ p.write32(dpe_ctrl_base + 0x464, 0x12)
+ p.write32(dpe_ctrl_base + 0x41c, 0x1)
+ p.write32(dpe_ctrl_base + 0x468, 0x9)
+ p.write32(dpe_ctrl_base + 0x46c, 0xa)
+ p.write32(dpe_ctrl_base + 0x420, 0x1)
+ p.write32(dpe_ctrl_base + 0x470, 0x33)
+ p.write32(dpe_ctrl_base + 0x474, 0x2c)
+ p.write32(dpe_ctrl_base + 0x424, 0x1)
+ p.write32(dpe_ctrl_base + 0x478, 0x15)
+ p.write32(dpe_ctrl_base + 0x47c, 0x15)
+ p.write32(dpe_ctrl_base + 0x428, 0x1)
+ p.write32(dpe_ctrl_base + 0x480, 0xe)
+ p.write32(dpe_ctrl_base + 0x484, 0x5)
+ p.write32(dpe_ctrl_base + 0x42c, 0x1)
+ p.write32(dpe_ctrl_base + 0x488, 0x27)
+ p.write32(dpe_ctrl_base + 0x48c, 0x15)
+ p.write32(dpe_ctrl_base + 0x430, 0x1)
+ p.write32(dpe_ctrl_base + 0x490, 0x15)
+ p.write32(dpe_ctrl_base + 0x494, 0xe)
+ p.write32(dpe_ctrl_base + 0x434, 0x1)
+ p.write32(dpe_ctrl_base + 0x498, 0x0)
+ p.write32(dpe_ctrl_base + 0x49c, 0x0)
+ p.write32(dpe_ctrl_base + 0x4, 0x1000)
+ p.write32(dpe_ctrl_base + 0x0, 0x101)
+
+def dpe_stop():
+ p.write32(dpe_ctrl_base + 0x0, 0x103)
+ while p.read32(dpe_ctrl_base + 0x0) & 0xC != 4:
+ ...
+ p.write32(dpe_ctrl_base + 0x0, p.read32(dpe_ctrl_base + 0x0) & 0xfffffffc)
+
+print(f"Hardware version {scaler.HW_VERSION.val:08X}")
+
+scaler.RESET = 1
+scaler.RESET = 0
+
+print(f"Hardware version after reset {scaler.HW_VERSION.val:08X}")
+
+if len(sys.argv) < 3:
+ print(f"Usage: {sys.argv[0]} input.png output.png")
+ sys.exit(-1)
+
+input_image_fn = sys.argv[1]
+output_image_fn = sys.argv[2]
+
+in_data = b''
+with Image.open(input_image_fn) as im:
+ in_W, in_H = im.size
+ in_BYTESPP = 4
+ in_STRIDE = in_W * in_BYTESPP
+ in_SZ = in_W * in_H * in_BYTESPP
+
+ for y in range(in_H):
+ for x in range(in_W):
+ r, g, b = im.getpixel((x, y))
+ in_data += struct.pack("BBBB", r, g, b, 255)
+
+out_W = in_W * 5
+out_H = in_H * 3
+out_BYTESPP = 4
+out_STRIDE = out_W * out_BYTESPP
+out_SZ = out_W * out_H * out_BYTESPP * 2 # HACK: double size for testing purposes
+
+for i in range(in_W * in_H):
+ in_data += struct.pack("<I", i & 0xFFFFFFFF)
+# chexdump(in_data)
+
+out_buf_phys = u.heap.memalign(0x4000, out_SZ)
+iface.writemem(out_buf_phys, b'\xAA' * out_SZ)
+out_buf_iova = dart.iomap(0, out_buf_phys, out_SZ)
+print(f"Output buffer @ phys {out_buf_phys:016X} iova {out_buf_iova:016X}")
+
+in_buf_phys = u.heap.memalign(0x4000, in_SZ)
+iface.writemem(in_buf_phys, in_data)
+in_buf_iova = dart.iomap(0, in_buf_phys, in_SZ)
+print(f"Input buffer @ phys {in_buf_phys:016X} iova {in_buf_iova:016X}")
+dart.dump_all()
+
+
+
+dpe_start()
+
+# reset CM
+p.write32(scaler_base + 0x3800, 0x0)
+
+# RDMA control
+p.write32(scaler_base + 0x180, 0x1)
+p.write32(scaler_base + 0x184, 0x1e)
+p.write32(scaler_base + 0x188, 0x0)
+p.write32(scaler_base + 0x18c, 0x0)
+p.write32(scaler_base + 0x190, 0x0)
+
+# transform config (flip/rotate)
+scaler.FLIP_ROTATE.set()
+
+# cache hints
+scaler.CACHE_HINTS_THING0[0].val = 0x7d311
+scaler.CACHE_HINTS_THING0[1].val = 0x7d311
+scaler.CACHE_HINTS_THING0[2].val = 0x7d311
+scaler.CACHE_HINTS_THING0[3].val = 0x7d311
+scaler.CACHE_HINTS_THING2[0].val = 0xbd311
+scaler.CACHE_HINTS_THING2[1].val = 0xbd311
+scaler.CACHE_HINTS_THING2[2].val = 0xbd311
+# scaler.CACHE_HINTS_THING2[3].val = 0xbd311
+scaler.CACHE_HINTS_THING1[0].val = 0x707
+scaler.CACHE_HINTS_THING1[1].val = 0x707
+scaler.CACHE_HINTS_THING1[2].val = 0x707
+scaler.CACHE_HINTS_THING1[3].val = 0x707
+scaler.CACHE_HINTS_THING3[0].val = 0xc0bd307
+scaler.CACHE_HINTS_THING3[1].val = 0xc0bd307
+scaler.CACHE_HINTS_THING3[2].val = 0xc0bd307
+# scaler.CACHE_HINTS_THING3[3].val = 0xc0bd307
+
+# tunables
+scaler.TUNABLES_THING0[0].val = 0x20
+scaler.TUNABLES_THING0[1].val = 0x20
+scaler.TUNABLES_THING0[2].val = 0x20
+scaler.TUNABLES_THING0[3].val = 0x20
+scaler.TUNABLES_THING1[0].val = 0x4000720
+scaler.TUNABLES_THING1[1].val = 0x4000720
+scaler.TUNABLES_THING1[2].val = 0x4000720
+# scaler.TUNABLES_THING1[3].val = 0x4000720
+
+# dest base addresses
+scaler.DST_PLANE1_LO = 0
+scaler.DST_PLANE1_HI = 0
+scaler.DST_PLANE0_LO = out_buf_iova & 0xFFFFFFFF
+scaler.DST_PLANE0_HI = out_buf_iova >> 32
+scaler.DST_PLANE2_LO = 0
+scaler.DST_PLANE2_HI = 0
+
+# src base addresses
+scaler.SRC_PLANE1_LO = 0
+scaler.SRC_PLANE1_HI = 0
+scaler.SRC_PLANE0_LO = in_buf_iova & 0xFFFFFFFF
+scaler.SRC_PLANE0_HI = in_buf_iova >> 32
+scaler.SRC_PLANE2_LO = 0
+scaler.SRC_PLANE2_HI = 0
+
+# dest stride
+scaler.DST_PLANE1_STRIDE = 0
+scaler.DST_PLANE0_STRIDE = out_STRIDE
+scaler.DST_PLANE2_STRIDE = 0
+
+# src stride
+scaler.SRC_PLANE1_STRIDE = 0
+scaler.SRC_PLANE0_STRIDE = in_STRIDE
+scaler.SRC_PLANE2_STRIDE = 0
+
+# dest offset
+scaler.DST_PLANE1_OFFSET = 0
+scaler.DST_PLANE0_OFFSET = 0
+scaler.DST_PLANE2_OFFSET = 0
+
+# src offset
+scaler.SRC_PLANE1_OFFSET = 0
+scaler.SRC_PLANE0_OFFSET = 0
+scaler.SRC_PLANE2_OFFSET = 0
+
+# dest sizes
+scaler.DST_W = out_W
+scaler.DST_H = out_H
+
+scaler.DST_SIZE_THING3 = 0
+scaler.DST_SIZE_THING6 = 0
+scaler.DST_SIZE_THING2 = 0
+scaler.DST_SIZE_THING5 = 0
+scaler.DST_SIZE_THING4 = 0
+scaler.DST_SIZE_THING7 = 0
+
+# src sizes
+scaler.SRC_W = in_W
+scaler.SRC_H = in_H
+
+scaler.SRC_SIZE_THING3 = 0
+scaler.SRC_SIZE_THING6 = 0
+scaler.SRC_SIZE_THING2 = 0
+scaler.SRC_SIZE_THING5 = 0
+scaler.SRC_SIZE_THING4 = 0
+scaler.SRC_SIZE_THING7 = 0
+
+# swizzling
+scaler.SRC_SWIZZLE = 0x03020100
+scaler.DST_SWIZZLE = 0x03020100
+
+# WDMA control
+p.write32(scaler_base + 0x280, 0x1)
+p.write32(scaler_base + 0x284, 0x81e)
+p.write32(scaler_base + 0x288, 0x800)
+p.write32(scaler_base + 0x28c, 0x800)
+
+# pixel averaging
+scaler.PIXEL_AVERAGING = 0
+
+# ASE enhancement
+p.write32(scaler_base + 0x16800, 0x0)
+
+# ASE 3x1 transform
+p.write32(scaler_base + 0x16080, 0x0)
+p.write32(scaler_base + 0x16084, 0xb710367)
+p.write32(scaler_base + 0x16088, 0x128)
+
+# ASE interpolation
+p.write32(scaler_base + 0x16600, 0x15)
+
+# ASE angle detect
+p.write32(scaler_base + 0x16504, 0x2000500)
+p.write32(scaler_base + 0x16508, 0x3200)
+p.write32(scaler_base + 0x16534, 0x8)
+p.write32(scaler_base + 0x1651c, 0x851400)
+p.write32(scaler_base + 0x16568, 0x250500)
+p.write32(scaler_base + 0x16588, 0x496513)
+
+# ASE config
+p.write32(scaler_base + 0x16000, 0x0)
+
+# chroma upsampling
+p.write32(scaler_base + 0x800, 0xc)
+
+# chroma downsampling
+p.write32(scaler_base + 0x900, 0x0)
+
+# DDA init V???
+scaler.SCALE_H_DDA_THING0 = 0
+scaler.SCALE_H_DDA_THING2 = 0
+scaler.SCALE_V_DDA_THING1 = 0
+
+# vertical scaling
+scaler.SCALE_V_RATIO_0 = int(in_H / out_H * 0x400000)
+scaler.SCALE_V_RATIO_4 = 0 # XXX what does this do?
+scaler.SCALE_V_RATIO_1 = 0 # XXX what does this do?
+scaler.SCALE_V_RATIO_2 = 0 # XXX what does this set do?
+scaler.SCALE_V_RATIO_3 = 0 # XXX what does this set do?
+scaler.SCALE_V_RATIO_5 = 0 # XXX what does this set do?
+scaler.SCALE_V_FLAGS.set(EN=1)
+
+# XXX this is a random filter grabbed from a random trace
+scaler.SCALE_FILTER_V_BLOCK0[0].val = 0x0
+scaler.SCALE_FILTER_V_BLOCK0[1].val = 0x50005
+scaler.SCALE_FILTER_V_BLOCK1[0].val = 0x50000
+scaler.SCALE_FILTER_V_BLOCK0[2].val = 0xb000b
+scaler.SCALE_FILTER_V_BLOCK0[3].val = 0x100010
+scaler.SCALE_FILTER_V_BLOCK1[1].val = 0x10000b
+scaler.SCALE_FILTER_V_BLOCK0[4].val = 0x140014
+scaler.SCALE_FILTER_V_BLOCK0[5].val = 0x180018
+scaler.SCALE_FILTER_V_BLOCK1[2].val = 0x180014
+scaler.SCALE_FILTER_V_BLOCK0[6].val = 0x1c001c
+scaler.SCALE_FILTER_V_BLOCK0[7].val = 0x200020
+scaler.SCALE_FILTER_V_BLOCK1[3].val = 0x20001c
+scaler.SCALE_FILTER_V_BLOCK0[8].val = 0x230023
+scaler.SCALE_FILTER_V_BLOCK0[9].val = 0x260026
+scaler.SCALE_FILTER_V_BLOCK1[4].val = 0x260023
+scaler.SCALE_FILTER_V_BLOCK0[10].val = 0x290029
+scaler.SCALE_FILTER_V_BLOCK0[11].val = 0x2c002c
+scaler.SCALE_FILTER_V_BLOCK1[5].val = 0x2c0029
+scaler.SCALE_FILTER_V_BLOCK0[12].val = 0x2e002e
+scaler.SCALE_FILTER_V_BLOCK0[13].val = 0x300030
+scaler.SCALE_FILTER_V_BLOCK1[6].val = 0x30002e
+scaler.SCALE_FILTER_V_BLOCK0[14].val = 0x320032
+scaler.SCALE_FILTER_V_BLOCK0[15].val = 0x330033
+scaler.SCALE_FILTER_V_BLOCK1[7].val = 0x330032
+scaler.SCALE_FILTER_V_BLOCK0[16].val = 0xff87ff87
+scaler.SCALE_FILTER_V_BLOCK0[17].val = 0xff90ff90
+scaler.SCALE_FILTER_V_BLOCK1[8].val = 0xff90ff87
+scaler.SCALE_FILTER_V_BLOCK0[18].val = 0xff99ff99
+scaler.SCALE_FILTER_V_BLOCK0[19].val = 0xffa1ffa1
+scaler.SCALE_FILTER_V_BLOCK1[9].val = 0xffa1ff99
+scaler.SCALE_FILTER_V_BLOCK0[20].val = 0xffaaffaa
+scaler.SCALE_FILTER_V_BLOCK0[21].val = 0xffb2ffb2
+scaler.SCALE_FILTER_V_BLOCK1[10].val = 0xffb2ffaa
+scaler.SCALE_FILTER_V_BLOCK0[22].val = 0xffbaffba
+scaler.SCALE_FILTER_V_BLOCK0[23].val = 0xffc2ffc2
+scaler.SCALE_FILTER_V_BLOCK1[11].val = 0xffc2ffba
+scaler.SCALE_FILTER_V_BLOCK0[24].val = 0xffcaffca
+scaler.SCALE_FILTER_V_BLOCK0[25].val = 0xffd2ffd2
+scaler.SCALE_FILTER_V_BLOCK1[12].val = 0xffd2ffca
+scaler.SCALE_FILTER_V_BLOCK0[26].val = 0xffd9ffd9
+scaler.SCALE_FILTER_V_BLOCK0[27].val = 0xffe0ffe0
+scaler.SCALE_FILTER_V_BLOCK1[13].val = 0xffe0ffd9
+scaler.SCALE_FILTER_V_BLOCK0[28].val = 0xffe7ffe7
+scaler.SCALE_FILTER_V_BLOCK0[29].val = 0xffeeffee
+scaler.SCALE_FILTER_V_BLOCK1[14].val = 0xffeeffe7
+scaler.SCALE_FILTER_V_BLOCK0[30].val = 0xfff4fff4
+scaler.SCALE_FILTER_V_BLOCK0[31].val = 0xfffafffa
+scaler.SCALE_FILTER_V_BLOCK1[15].val = 0xfffafff4
+scaler.SCALE_FILTER_V_BLOCK0[32].val = 0xff06ff06
+scaler.SCALE_FILTER_V_BLOCK0[33].val = 0xff0cff0c
+scaler.SCALE_FILTER_V_BLOCK1[16].val = 0xff0cff06
+scaler.SCALE_FILTER_V_BLOCK0[34].val = 0xff13ff13
+scaler.SCALE_FILTER_V_BLOCK0[35].val = 0xff1aff1a
+scaler.SCALE_FILTER_V_BLOCK1[17].val = 0xff1aff13
+scaler.SCALE_FILTER_V_BLOCK0[36].val = 0xff21ff21
+scaler.SCALE_FILTER_V_BLOCK0[37].val = 0xff28ff28
+scaler.SCALE_FILTER_V_BLOCK1[18].val = 0xff28ff21
+scaler.SCALE_FILTER_V_BLOCK0[38].val = 0xff30ff30
+scaler.SCALE_FILTER_V_BLOCK0[39].val = 0xff38ff38
+scaler.SCALE_FILTER_V_BLOCK1[19].val = 0xff38ff30
+scaler.SCALE_FILTER_V_BLOCK0[40].val = 0xff41ff41
+scaler.SCALE_FILTER_V_BLOCK0[41].val = 0xff49ff49
+scaler.SCALE_FILTER_V_BLOCK1[20].val = 0xff49ff41
+scaler.SCALE_FILTER_V_BLOCK0[42].val = 0xff52ff52
+scaler.SCALE_FILTER_V_BLOCK0[43].val = 0xff5bff5b
+scaler.SCALE_FILTER_V_BLOCK1[21].val = 0xff5bff52
+scaler.SCALE_FILTER_V_BLOCK0[44].val = 0xff63ff63
+scaler.SCALE_FILTER_V_BLOCK0[45].val = 0xff6cff6c
+scaler.SCALE_FILTER_V_BLOCK1[22].val = 0xff6cff63
+scaler.SCALE_FILTER_V_BLOCK0[46].val = 0xff75ff75
+scaler.SCALE_FILTER_V_BLOCK0[47].val = 0xff7eff7e
+scaler.SCALE_FILTER_V_BLOCK1[23].val = 0xff7eff75
+scaler.SCALE_FILTER_V_BLOCK0[48].val = 0xff02ff02
+scaler.SCALE_FILTER_V_BLOCK0[49].val = 0xfefcfefc
+scaler.SCALE_FILTER_V_BLOCK1[24].val = 0xfefcff02
+scaler.SCALE_FILTER_V_BLOCK0[50].val = 0xfef7fef7
+scaler.SCALE_FILTER_V_BLOCK0[51].val = 0xfef3fef3
+scaler.SCALE_FILTER_V_BLOCK1[25].val = 0xfef3fef7
+scaler.SCALE_FILTER_V_BLOCK0[52].val = 0xfeeffeef
+scaler.SCALE_FILTER_V_BLOCK0[53].val = 0xfeedfeed
+scaler.SCALE_FILTER_V_BLOCK1[26].val = 0xfeedfeef
+scaler.SCALE_FILTER_V_BLOCK0[54].val = 0xfeecfeec
+scaler.SCALE_FILTER_V_BLOCK0[55].val = 0xfeebfeeb
+scaler.SCALE_FILTER_V_BLOCK1[27].val = 0xfeebfeec
+scaler.SCALE_FILTER_V_BLOCK0[56].val = 0xfeebfeeb
+scaler.SCALE_FILTER_V_BLOCK0[57].val = 0xfeecfeec
+scaler.SCALE_FILTER_V_BLOCK1[28].val = 0xfeecfeeb
+scaler.SCALE_FILTER_V_BLOCK0[58].val = 0xfeeefeee
+scaler.SCALE_FILTER_V_BLOCK0[59].val = 0xfef1fef1
+scaler.SCALE_FILTER_V_BLOCK1[29].val = 0xfef1feee
+scaler.SCALE_FILTER_V_BLOCK0[60].val = 0xfef4fef4
+scaler.SCALE_FILTER_V_BLOCK0[61].val = 0xfef8fef8
+scaler.SCALE_FILTER_V_BLOCK1[30].val = 0xfef8fef4
+scaler.SCALE_FILTER_V_BLOCK0[62].val = 0xfefcfefc
+scaler.SCALE_FILTER_V_BLOCK0[63].val = 0xff01ff01
+scaler.SCALE_FILTER_V_BLOCK1[31].val = 0xff01fefc
+scaler.SCALE_FILTER_V_BLOCK0[64].val = 0x0
+scaler.SCALE_FILTER_V_BLOCK0[65].val = 0xffe7ffe7
+scaler.SCALE_FILTER_V_BLOCK1[32].val = 0xffe70000
+scaler.SCALE_FILTER_V_BLOCK0[66].val = 0xffcfffcf
+scaler.SCALE_FILTER_V_BLOCK0[67].val = 0xffb9ffb9
+scaler.SCALE_FILTER_V_BLOCK1[33].val = 0xffb9ffcf
+scaler.SCALE_FILTER_V_BLOCK0[68].val = 0xffa4ffa4
+scaler.SCALE_FILTER_V_BLOCK0[69].val = 0xff90ff90
+scaler.SCALE_FILTER_V_BLOCK1[34].val = 0xff90ffa4
+scaler.SCALE_FILTER_V_BLOCK0[70].val = 0xff7dff7d
+scaler.SCALE_FILTER_V_BLOCK0[71].val = 0xff6bff6b
+scaler.SCALE_FILTER_V_BLOCK1[35].val = 0xff6bff7d
+scaler.SCALE_FILTER_V_BLOCK0[72].val = 0xff5bff5b
+scaler.SCALE_FILTER_V_BLOCK0[73].val = 0xff4cff4c
+scaler.SCALE_FILTER_V_BLOCK1[36].val = 0xff4cff5b
+scaler.SCALE_FILTER_V_BLOCK0[74].val = 0xff3eff3e
+scaler.SCALE_FILTER_V_BLOCK0[75].val = 0xff31ff31
+scaler.SCALE_FILTER_V_BLOCK1[37].val = 0xff31ff3e
+scaler.SCALE_FILTER_V_BLOCK0[76].val = 0xff26ff26
+scaler.SCALE_FILTER_V_BLOCK0[77].val = 0xff1bff1b
+scaler.SCALE_FILTER_V_BLOCK1[38].val = 0xff1bff26
+scaler.SCALE_FILTER_V_BLOCK0[78].val = 0xff12ff12
+scaler.SCALE_FILTER_V_BLOCK0[79].val = 0xff0aff0a
+scaler.SCALE_FILTER_V_BLOCK1[39].val = 0xff0aff12
+scaler.SCALE_FILTER_V_BLOCK0[80].val = 0x2210221
+scaler.SCALE_FILTER_V_BLOCK0[81].val = 0x1f901f9
+scaler.SCALE_FILTER_V_BLOCK1[40].val = 0x1f90221
+scaler.SCALE_FILTER_V_BLOCK0[82].val = 0x1d001d0
+scaler.SCALE_FILTER_V_BLOCK0[83].val = 0x1a901a9
+scaler.SCALE_FILTER_V_BLOCK1[41].val = 0x1a901d0
+scaler.SCALE_FILTER_V_BLOCK0[84].val = 0x1820182
+scaler.SCALE_FILTER_V_BLOCK0[85].val = 0x15d015d
+scaler.SCALE_FILTER_V_BLOCK1[42].val = 0x15d0182
+scaler.SCALE_FILTER_V_BLOCK0[86].val = 0x1380138
+scaler.SCALE_FILTER_V_BLOCK0[87].val = 0x1140114
+scaler.SCALE_FILTER_V_BLOCK1[43].val = 0x1140138
+scaler.SCALE_FILTER_V_BLOCK0[88].val = 0xf100f1
+scaler.SCALE_FILTER_V_BLOCK0[89].val = 0xcf00cf
+scaler.SCALE_FILTER_V_BLOCK1[44].val = 0xcf00f1
+scaler.SCALE_FILTER_V_BLOCK0[90].val = 0xae00ae
+scaler.SCALE_FILTER_V_BLOCK0[91].val = 0x8e008e
+scaler.SCALE_FILTER_V_BLOCK1[45].val = 0x8e00ae
+scaler.SCALE_FILTER_V_BLOCK0[92].val = 0x6f006f
+scaler.SCALE_FILTER_V_BLOCK0[93].val = 0x520052
+scaler.SCALE_FILTER_V_BLOCK1[46].val = 0x52006f
+scaler.SCALE_FILTER_V_BLOCK0[94].val = 0x350035
+scaler.SCALE_FILTER_V_BLOCK0[95].val = 0x1a001a
+scaler.SCALE_FILTER_V_BLOCK1[47].val = 0x1a0035
+scaler.SCALE_FILTER_V_BLOCK0[96].val = 0x4e404e4
+scaler.SCALE_FILTER_V_BLOCK0[97].val = 0x4b804b8
+scaler.SCALE_FILTER_V_BLOCK1[48].val = 0x4b804e4
+scaler.SCALE_FILTER_V_BLOCK0[98].val = 0x48b048b
+scaler.SCALE_FILTER_V_BLOCK0[99].val = 0x45f045f
+scaler.SCALE_FILTER_V_BLOCK1[49].val = 0x45f048b
+scaler.SCALE_FILTER_V_BLOCK0[100].val = 0x4320432
+scaler.SCALE_FILTER_V_BLOCK0[101].val = 0x4050405
+scaler.SCALE_FILTER_V_BLOCK1[50].val = 0x4050432
+scaler.SCALE_FILTER_V_BLOCK0[102].val = 0x3d803d8
+scaler.SCALE_FILTER_V_BLOCK0[103].val = 0x3ab03ab
+scaler.SCALE_FILTER_V_BLOCK1[51].val = 0x3ab03d8
+scaler.SCALE_FILTER_V_BLOCK0[104].val = 0x37e037e
+scaler.SCALE_FILTER_V_BLOCK0[105].val = 0x3510351
+scaler.SCALE_FILTER_V_BLOCK1[52].val = 0x351037e
+scaler.SCALE_FILTER_V_BLOCK0[106].val = 0x3240324
+scaler.SCALE_FILTER_V_BLOCK0[107].val = 0x2f802f8
+scaler.SCALE_FILTER_V_BLOCK1[53].val = 0x2f80324
+scaler.SCALE_FILTER_V_BLOCK0[108].val = 0x2cc02cc
+scaler.SCALE_FILTER_V_BLOCK0[109].val = 0x2a102a1
+scaler.SCALE_FILTER_V_BLOCK1[54].val = 0x2a102cc
+scaler.SCALE_FILTER_V_BLOCK0[110].val = 0x2760276
+scaler.SCALE_FILTER_V_BLOCK0[111].val = 0x24b024b
+scaler.SCALE_FILTER_V_BLOCK1[55].val = 0x24b0276
+scaler.SCALE_FILTER_V_BLOCK0[112].val = 0x73b073b
+scaler.SCALE_FILTER_V_BLOCK0[113].val = 0x71e071e
+scaler.SCALE_FILTER_V_BLOCK1[56].val = 0x71e073b
+scaler.SCALE_FILTER_V_BLOCK0[114].val = 0x7000700
+scaler.SCALE_FILTER_V_BLOCK0[115].val = 0x6e106e1
+scaler.SCALE_FILTER_V_BLOCK1[57].val = 0x6e10700
+scaler.SCALE_FILTER_V_BLOCK0[116].val = 0x6c006c0
+scaler.SCALE_FILTER_V_BLOCK0[117].val = 0x69e069e
+scaler.SCALE_FILTER_V_BLOCK1[58].val = 0x69e06c0
+scaler.SCALE_FILTER_V_BLOCK0[118].val = 0x67a067a
+scaler.SCALE_FILTER_V_BLOCK0[119].val = 0x6560656
+scaler.SCALE_FILTER_V_BLOCK1[59].val = 0x656067a
+scaler.SCALE_FILTER_V_BLOCK0[120].val = 0x6300630
+scaler.SCALE_FILTER_V_BLOCK0[121].val = 0x6090609
+scaler.SCALE_FILTER_V_BLOCK1[60].val = 0x6090630
+scaler.SCALE_FILTER_V_BLOCK0[122].val = 0x5e205e2
+scaler.SCALE_FILTER_V_BLOCK0[123].val = 0x5b905b9
+scaler.SCALE_FILTER_V_BLOCK1[61].val = 0x5b905e2
+scaler.SCALE_FILTER_V_BLOCK0[124].val = 0x5900590
+scaler.SCALE_FILTER_V_BLOCK0[125].val = 0x5660566
+scaler.SCALE_FILTER_V_BLOCK1[62].val = 0x5660590
+scaler.SCALE_FILTER_V_BLOCK0[126].val = 0x53b053b
+scaler.SCALE_FILTER_V_BLOCK0[127].val = 0x5100510
+scaler.SCALE_FILTER_V_BLOCK1[63].val = 0x510053b
+scaler.SCALE_FILTER_V_BLOCK0[128].val = 0x82c082c
+scaler.SCALE_FILTER_V_BLOCK0[129].val = 0x82b082b
+scaler.SCALE_FILTER_V_BLOCK1[64].val = 0x82b082c
+scaler.SCALE_FILTER_V_BLOCK0[130].val = 0x8280828
+scaler.SCALE_FILTER_V_BLOCK0[131].val = 0x8200820
+scaler.SCALE_FILTER_V_BLOCK1[65].val = 0x8200828
+scaler.SCALE_FILTER_V_BLOCK0[132].val = 0x81b081b
+scaler.SCALE_FILTER_V_BLOCK0[133].val = 0x8130813
+scaler.SCALE_FILTER_V_BLOCK1[66].val = 0x813081b
+scaler.SCALE_FILTER_V_BLOCK0[134].val = 0x8080808
+scaler.SCALE_FILTER_V_BLOCK0[135].val = 0x7fc07fc
+scaler.SCALE_FILTER_V_BLOCK1[67].val = 0x7fc0808
+scaler.SCALE_FILTER_V_BLOCK0[136].val = 0x7ed07ed
+scaler.SCALE_FILTER_V_BLOCK0[137].val = 0x7dd07dd
+scaler.SCALE_FILTER_V_BLOCK1[68].val = 0x7dd07ed
+scaler.SCALE_FILTER_V_BLOCK0[138].val = 0x7cb07cb
+scaler.SCALE_FILTER_V_BLOCK0[139].val = 0x7b607b6
+scaler.SCALE_FILTER_V_BLOCK1[69].val = 0x7b607cb
+scaler.SCALE_FILTER_V_BLOCK0[140].val = 0x7a207a2
+scaler.SCALE_FILTER_V_BLOCK0[141].val = 0x78a078a
+scaler.SCALE_FILTER_V_BLOCK1[70].val = 0x78a07a2
+scaler.SCALE_FILTER_V_BLOCK0[142].val = 0x7710771
+scaler.SCALE_FILTER_V_BLOCK0[143].val = 0x7570757
+scaler.SCALE_FILTER_V_BLOCK1[71].val = 0x7570771
+scaler.SCALE_FILTER_V_BLOCK0[144].val = 0x73d073d
+scaler.SCALE_FILTER_V_BLOCK0[145].val = 0x7570757
+scaler.SCALE_FILTER_V_BLOCK1[72].val = 0x757073d
+scaler.SCALE_FILTER_V_BLOCK0[146].val = 0x7710771
+scaler.SCALE_FILTER_V_BLOCK0[147].val = 0x78a078a
+scaler.SCALE_FILTER_V_BLOCK1[73].val = 0x78a0771
+scaler.SCALE_FILTER_V_BLOCK0[148].val = 0x7a207a2
+scaler.SCALE_FILTER_V_BLOCK0[149].val = 0x7b607b6
+scaler.SCALE_FILTER_V_BLOCK1[74].val = 0x7b607a2
+scaler.SCALE_FILTER_V_BLOCK0[150].val = 0x7cb07cb
+scaler.SCALE_FILTER_V_BLOCK0[151].val = 0x7dd07dd
+scaler.SCALE_FILTER_V_BLOCK1[75].val = 0x7dd07cb
+scaler.SCALE_FILTER_V_BLOCK0[152].val = 0x7ed07ed
+scaler.SCALE_FILTER_V_BLOCK0[153].val = 0x7fc07fc
+scaler.SCALE_FILTER_V_BLOCK1[76].val = 0x7fc07ed
+scaler.SCALE_FILTER_V_BLOCK0[154].val = 0x8080808
+scaler.SCALE_FILTER_V_BLOCK0[155].val = 0x8130813
+scaler.SCALE_FILTER_V_BLOCK1[77].val = 0x8130808
+scaler.SCALE_FILTER_V_BLOCK0[156].val = 0x81b081b
+scaler.SCALE_FILTER_V_BLOCK0[157].val = 0x8200820
+scaler.SCALE_FILTER_V_BLOCK1[78].val = 0x820081b
+scaler.SCALE_FILTER_V_BLOCK0[158].val = 0x8280828
+scaler.SCALE_FILTER_V_BLOCK0[159].val = 0x82b082b
+scaler.SCALE_FILTER_V_BLOCK1[79].val = 0x82b0828
+scaler.SCALE_FILTER_V_BLOCK0[160].val = 0x4e404e4
+scaler.SCALE_FILTER_V_BLOCK0[161].val = 0x5100510
+scaler.SCALE_FILTER_V_BLOCK1[80].val = 0x51004e4
+scaler.SCALE_FILTER_V_BLOCK0[162].val = 0x53b053b
+scaler.SCALE_FILTER_V_BLOCK0[163].val = 0x5660566
+scaler.SCALE_FILTER_V_BLOCK1[81].val = 0x566053b
+scaler.SCALE_FILTER_V_BLOCK0[164].val = 0x5900590
+scaler.SCALE_FILTER_V_BLOCK0[165].val = 0x5b905b9
+scaler.SCALE_FILTER_V_BLOCK1[82].val = 0x5b90590
+scaler.SCALE_FILTER_V_BLOCK0[166].val = 0x5e205e2
+scaler.SCALE_FILTER_V_BLOCK0[167].val = 0x6090609
+scaler.SCALE_FILTER_V_BLOCK1[83].val = 0x60905e2
+scaler.SCALE_FILTER_V_BLOCK0[168].val = 0x6300630
+scaler.SCALE_FILTER_V_BLOCK0[169].val = 0x6560656
+scaler.SCALE_FILTER_V_BLOCK1[84].val = 0x6560630
+scaler.SCALE_FILTER_V_BLOCK0[170].val = 0x67a067a
+scaler.SCALE_FILTER_V_BLOCK0[171].val = 0x69e069e
+scaler.SCALE_FILTER_V_BLOCK1[85].val = 0x69e067a
+scaler.SCALE_FILTER_V_BLOCK0[172].val = 0x6c006c0
+scaler.SCALE_FILTER_V_BLOCK0[173].val = 0x6e106e1
+scaler.SCALE_FILTER_V_BLOCK1[86].val = 0x6e106c0
+scaler.SCALE_FILTER_V_BLOCK0[174].val = 0x7000700
+scaler.SCALE_FILTER_V_BLOCK0[175].val = 0x71e071e
+scaler.SCALE_FILTER_V_BLOCK1[87].val = 0x71e0700
+scaler.SCALE_FILTER_V_BLOCK0[176].val = 0x2210221
+scaler.SCALE_FILTER_V_BLOCK0[177].val = 0x24b024b
+scaler.SCALE_FILTER_V_BLOCK1[88].val = 0x24b0221
+scaler.SCALE_FILTER_V_BLOCK0[178].val = 0x2760276
+scaler.SCALE_FILTER_V_BLOCK0[179].val = 0x2a102a1
+scaler.SCALE_FILTER_V_BLOCK1[89].val = 0x2a10276
+scaler.SCALE_FILTER_V_BLOCK0[180].val = 0x2cc02cc
+scaler.SCALE_FILTER_V_BLOCK0[181].val = 0x2f802f8
+scaler.SCALE_FILTER_V_BLOCK1[90].val = 0x2f802cc
+scaler.SCALE_FILTER_V_BLOCK0[182].val = 0x3240324
+scaler.SCALE_FILTER_V_BLOCK0[183].val = 0x3510351
+scaler.SCALE_FILTER_V_BLOCK1[91].val = 0x3510324
+scaler.SCALE_FILTER_V_BLOCK0[184].val = 0x37e037e
+scaler.SCALE_FILTER_V_BLOCK0[185].val = 0x3ab03ab
+scaler.SCALE_FILTER_V_BLOCK1[92].val = 0x3ab037e
+scaler.SCALE_FILTER_V_BLOCK0[186].val = 0x3d803d8
+scaler.SCALE_FILTER_V_BLOCK0[187].val = 0x4050405
+scaler.SCALE_FILTER_V_BLOCK1[93].val = 0x40503d8
+scaler.SCALE_FILTER_V_BLOCK0[188].val = 0x4320432
+scaler.SCALE_FILTER_V_BLOCK0[189].val = 0x45f045f
+scaler.SCALE_FILTER_V_BLOCK1[94].val = 0x45f0432
+scaler.SCALE_FILTER_V_BLOCK0[190].val = 0x48b048b
+scaler.SCALE_FILTER_V_BLOCK0[191].val = 0x4b804b8
+scaler.SCALE_FILTER_V_BLOCK1[95].val = 0x4b8048b
+scaler.SCALE_FILTER_V_BLOCK0[192].val = 0x0
+scaler.SCALE_FILTER_V_BLOCK0[193].val = 0x1a001a
+scaler.SCALE_FILTER_V_BLOCK1[96].val = 0x1a0000
+scaler.SCALE_FILTER_V_BLOCK0[194].val = 0x350035
+scaler.SCALE_FILTER_V_BLOCK0[195].val = 0x520052
+scaler.SCALE_FILTER_V_BLOCK1[97].val = 0x520035
+scaler.SCALE_FILTER_V_BLOCK0[196].val = 0x6f006f
+scaler.SCALE_FILTER_V_BLOCK0[197].val = 0x8e008e
+scaler.SCALE_FILTER_V_BLOCK1[98].val = 0x8e006f
+scaler.SCALE_FILTER_V_BLOCK0[198].val = 0xae00ae
+scaler.SCALE_FILTER_V_BLOCK0[199].val = 0xcf00cf
+scaler.SCALE_FILTER_V_BLOCK1[99].val = 0xcf00ae
+scaler.SCALE_FILTER_V_BLOCK0[200].val = 0xf100f1
+scaler.SCALE_FILTER_V_BLOCK0[201].val = 0x1140114
+scaler.SCALE_FILTER_V_BLOCK1[100].val = 0x11400f1
+scaler.SCALE_FILTER_V_BLOCK0[202].val = 0x1380138
+scaler.SCALE_FILTER_V_BLOCK0[203].val = 0x15d015d
+scaler.SCALE_FILTER_V_BLOCK1[101].val = 0x15d0138
+scaler.SCALE_FILTER_V_BLOCK0[204].val = 0x1820182
+scaler.SCALE_FILTER_V_BLOCK0[205].val = 0x1a901a9
+scaler.SCALE_FILTER_V_BLOCK1[102].val = 0x1a90182
+scaler.SCALE_FILTER_V_BLOCK0[206].val = 0x1d001d0
+scaler.SCALE_FILTER_V_BLOCK0[207].val = 0x1f901f9
+scaler.SCALE_FILTER_V_BLOCK1[103].val = 0x1f901d0
+scaler.SCALE_FILTER_V_BLOCK0[208].val = 0xff02ff02
+scaler.SCALE_FILTER_V_BLOCK0[209].val = 0xff0aff0a
+scaler.SCALE_FILTER_V_BLOCK1[104].val = 0xff0aff02
+scaler.SCALE_FILTER_V_BLOCK0[210].val = 0xff12ff12
+scaler.SCALE_FILTER_V_BLOCK0[211].val = 0xff1bff1b
+scaler.SCALE_FILTER_V_BLOCK1[105].val = 0xff1bff12
+scaler.SCALE_FILTER_V_BLOCK0[212].val = 0xff26ff26
+scaler.SCALE_FILTER_V_BLOCK0[213].val = 0xff31ff31
+scaler.SCALE_FILTER_V_BLOCK1[106].val = 0xff31ff26
+scaler.SCALE_FILTER_V_BLOCK0[214].val = 0xff3eff3e
+scaler.SCALE_FILTER_V_BLOCK0[215].val = 0xff4cff4c
+scaler.SCALE_FILTER_V_BLOCK1[107].val = 0xff4cff3e
+scaler.SCALE_FILTER_V_BLOCK0[216].val = 0xff5bff5b
+scaler.SCALE_FILTER_V_BLOCK0[218].val = 0xff6bff6b
+scaler.SCALE_FILTER_V_BLOCK1[108].val = 0xff6bff5b
+scaler.SCALE_FILTER_V_BLOCK0[218].val = 0xff7dff7d
+scaler.SCALE_FILTER_V_BLOCK0[219].val = 0xff90ff90
+scaler.SCALE_FILTER_V_BLOCK1[109].val = 0xff90ff7d
+scaler.SCALE_FILTER_V_BLOCK0[220].val = 0xffa4ffa4
+scaler.SCALE_FILTER_V_BLOCK0[221].val = 0xffb9ffb9
+scaler.SCALE_FILTER_V_BLOCK1[110].val = 0xffb9ffa4
+scaler.SCALE_FILTER_V_BLOCK0[222].val = 0xffcfffcf
+scaler.SCALE_FILTER_V_BLOCK0[223].val = 0xffe7ffe7
+scaler.SCALE_FILTER_V_BLOCK1[111].val = 0xffe7ffcf
+scaler.SCALE_FILTER_V_BLOCK0[224].val = 0xff06ff06
+scaler.SCALE_FILTER_V_BLOCK0[225].val = 0xff01ff01
+scaler.SCALE_FILTER_V_BLOCK1[112].val = 0xff01ff06
+scaler.SCALE_FILTER_V_BLOCK0[226].val = 0xfefcfefc
+scaler.SCALE_FILTER_V_BLOCK0[227].val = 0xfef8fef8
+scaler.SCALE_FILTER_V_BLOCK1[113].val = 0xfef8fefc
+scaler.SCALE_FILTER_V_BLOCK0[228].val = 0xfef4fef4
+scaler.SCALE_FILTER_V_BLOCK0[229].val = 0xfef1fef1
+scaler.SCALE_FILTER_V_BLOCK1[114].val = 0xfef1fef4
+scaler.SCALE_FILTER_V_BLOCK0[230].val = 0xfeeefeee
+scaler.SCALE_FILTER_V_BLOCK0[231].val = 0xfeecfeec
+scaler.SCALE_FILTER_V_BLOCK1[115].val = 0xfeecfeee
+scaler.SCALE_FILTER_V_BLOCK0[232].val = 0xfeebfeeb
+scaler.SCALE_FILTER_V_BLOCK0[233].val = 0xfeebfeeb
+scaler.SCALE_FILTER_V_BLOCK1[116].val = 0xfeebfeeb
+scaler.SCALE_FILTER_V_BLOCK0[234].val = 0xfeecfeec
+scaler.SCALE_FILTER_V_BLOCK0[235].val = 0xfeedfeed
+scaler.SCALE_FILTER_V_BLOCK1[117].val = 0xfeedfeec
+scaler.SCALE_FILTER_V_BLOCK0[236].val = 0xfeeffeef
+scaler.SCALE_FILTER_V_BLOCK0[237].val = 0xfef3fef3
+scaler.SCALE_FILTER_V_BLOCK1[118].val = 0xfef3feef
+scaler.SCALE_FILTER_V_BLOCK0[238].val = 0xfef7fef7
+scaler.SCALE_FILTER_V_BLOCK0[239].val = 0xfefcfefc
+scaler.SCALE_FILTER_V_BLOCK1[119].val = 0xfefcfef7
+scaler.SCALE_FILTER_V_BLOCK0[240].val = 0xff87ff87
+scaler.SCALE_FILTER_V_BLOCK0[241].val = 0xff7eff7e
+scaler.SCALE_FILTER_V_BLOCK1[120].val = 0xff7eff87
+scaler.SCALE_FILTER_V_BLOCK0[242].val = 0xff75ff75
+scaler.SCALE_FILTER_V_BLOCK0[243].val = 0xff6cff6c
+scaler.SCALE_FILTER_V_BLOCK1[121].val = 0xff6cff75
+scaler.SCALE_FILTER_V_BLOCK0[244].val = 0xff63ff63
+scaler.SCALE_FILTER_V_BLOCK0[245].val = 0xff5bff5b
+scaler.SCALE_FILTER_V_BLOCK1[122].val = 0xff5bff63
+scaler.SCALE_FILTER_V_BLOCK0[246].val = 0xff52ff52
+scaler.SCALE_FILTER_V_BLOCK0[247].val = 0xff49ff49
+scaler.SCALE_FILTER_V_BLOCK1[123].val = 0xff49ff52
+scaler.SCALE_FILTER_V_BLOCK0[248].val = 0xff41ff41
+scaler.SCALE_FILTER_V_BLOCK0[249].val = 0xff38ff38
+scaler.SCALE_FILTER_V_BLOCK1[124].val = 0xff38ff41
+scaler.SCALE_FILTER_V_BLOCK0[250].val = 0xff30ff30
+scaler.SCALE_FILTER_V_BLOCK0[251].val = 0xff28ff28
+scaler.SCALE_FILTER_V_BLOCK1[125].val = 0xff28ff30
+scaler.SCALE_FILTER_V_BLOCK0[252].val = 0xff21ff21
+scaler.SCALE_FILTER_V_BLOCK0[253].val = 0xff1aff1a
+scaler.SCALE_FILTER_V_BLOCK1[126].val = 0xff1aff21
+scaler.SCALE_FILTER_V_BLOCK0[254].val = 0xff13ff13
+scaler.SCALE_FILTER_V_BLOCK0[255].val = 0xff0cff0c
+scaler.SCALE_FILTER_V_BLOCK1[127].val = 0xff0cff13
+scaler.SCALE_FILTER_V_BLOCK0[256].val = 0x0
+scaler.SCALE_FILTER_V_BLOCK0[257].val = 0xfffafffa
+scaler.SCALE_FILTER_V_BLOCK1[128].val = 0xfffa0000
+scaler.SCALE_FILTER_V_BLOCK0[258].val = 0xfff4fff4
+scaler.SCALE_FILTER_V_BLOCK0[259].val = 0xffeeffee
+scaler.SCALE_FILTER_V_BLOCK1[129].val = 0xffeefff4
+scaler.SCALE_FILTER_V_BLOCK0[260].val = 0xffe7ffe7
+scaler.SCALE_FILTER_V_BLOCK0[261].val = 0xffe0ffe0
+scaler.SCALE_FILTER_V_BLOCK1[130].val = 0xffe0ffe7
+scaler.SCALE_FILTER_V_BLOCK0[262].val = 0xffd9ffd9
+scaler.SCALE_FILTER_V_BLOCK0[263].val = 0xffd2ffd2
+scaler.SCALE_FILTER_V_BLOCK1[131].val = 0xffd2ffd9
+scaler.SCALE_FILTER_V_BLOCK0[264].val = 0xffcaffca
+scaler.SCALE_FILTER_V_BLOCK0[265].val = 0xffc2ffc2
+scaler.SCALE_FILTER_V_BLOCK1[132].val = 0xffc2ffca
+scaler.SCALE_FILTER_V_BLOCK0[266].val = 0xffbaffba
+scaler.SCALE_FILTER_V_BLOCK0[267].val = 0xffb2ffb2
+scaler.SCALE_FILTER_V_BLOCK1[133].val = 0xffb2ffba
+scaler.SCALE_FILTER_V_BLOCK0[268].val = 0xffaaffaa
+scaler.SCALE_FILTER_V_BLOCK0[269].val = 0xffa1ffa1
+scaler.SCALE_FILTER_V_BLOCK1[134].val = 0xffa1ffaa
+scaler.SCALE_FILTER_V_BLOCK0[270].val = 0xff99ff99
+scaler.SCALE_FILTER_V_BLOCK0[271].val = 0xff90ff90
+scaler.SCALE_FILTER_V_BLOCK1[135].val = 0xff90ff99
+scaler.SCALE_FILTER_V_BLOCK0[272].val = 0x340034
+scaler.SCALE_FILTER_V_BLOCK0[273].val = 0x330033
+scaler.SCALE_FILTER_V_BLOCK1[136].val = 0x330034
+scaler.SCALE_FILTER_V_BLOCK0[274].val = 0x320032
+scaler.SCALE_FILTER_V_BLOCK0[275].val = 0x300030
+scaler.SCALE_FILTER_V_BLOCK1[137].val = 0x300032
+scaler.SCALE_FILTER_V_BLOCK0[276].val = 0x2e002e
+scaler.SCALE_FILTER_V_BLOCK0[277].val = 0x2c002c
+scaler.SCALE_FILTER_V_BLOCK1[138].val = 0x2c002e
+scaler.SCALE_FILTER_V_BLOCK0[278].val = 0x290029
+scaler.SCALE_FILTER_V_BLOCK0[279].val = 0x260026
+scaler.SCALE_FILTER_V_BLOCK1[139].val = 0x260029
+scaler.SCALE_FILTER_V_BLOCK0[280].val = 0x230023
+scaler.SCALE_FILTER_V_BLOCK0[281].val = 0x200020
+scaler.SCALE_FILTER_V_BLOCK1[140].val = 0x200023
+scaler.SCALE_FILTER_V_BLOCK0[282].val = 0x1c001c
+scaler.SCALE_FILTER_V_BLOCK0[283].val = 0x180018
+scaler.SCALE_FILTER_V_BLOCK1[141].val = 0x18001c
+scaler.SCALE_FILTER_V_BLOCK0[284].val = 0x140014
+scaler.SCALE_FILTER_V_BLOCK0[285].val = 0x100010
+scaler.SCALE_FILTER_V_BLOCK1[142].val = 0x100014
+scaler.SCALE_FILTER_V_BLOCK0[286].val = 0xb000b
+scaler.SCALE_FILTER_V_BLOCK0[287].val = 0x50005
+scaler.SCALE_FILTER_V_BLOCK1[143].val = 0x5000b
+
+# DDA init H
+scaler.SCALE_H_DDA_THING0 = 0
+scaler.SCALE_H_DDA_THING2 = 0
+scaler.SCALE_H_DDA_THING1 = 0
+
+# horizontal scaling
+scaler.SCALE_H_RATIO_0 = int(in_W / out_W * 0x400000)
+scaler.SCALE_H_RATIO_4 = 0 # XXX what does this do?
+scaler.SCALE_H_RATIO_1 = 0 # XXX what does this do?
+scaler.SCALE_H_RATIO_2 = int(out_W / in_W * 0x400000) # XXX what does this set do? zeroing this one out doesn't work
+scaler.SCALE_H_RATIO_3 = 0 # XXX what does this set do?
+scaler.SCALE_H_RATIO_5 = 0 # XXX what does this set do?
+scaler.SCALE_H_FLAGS.set(EN=1)
+
+scaler.SCALE_FILTER_H_BLOCK0[0].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[1].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[0].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[2].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[3].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[1].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[4].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[5].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[2].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[6].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[7].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[3].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[8].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[9].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[4].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[10].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[11].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[5].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[12].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[13].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[6].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[14].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[15].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[7].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[16].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[17].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[8].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[18].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[19].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[9].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[20].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[21].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[10].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[22].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[23].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[11].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[24].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[25].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[12].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[26].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[27].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[13].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[28].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[29].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[14].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[30].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[31].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[15].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[32].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[33].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[16].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[34].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[35].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[17].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[36].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[37].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[18].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[38].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[39].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[19].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[40].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[41].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[20].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[42].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[43].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[21].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[44].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[45].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[22].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[46].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[47].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[23].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[48].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[49].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[24].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[50].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[51].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[25].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[52].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[53].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[26].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[54].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[55].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[27].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[56].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[57].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[28].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[58].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[59].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[29].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[60].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[61].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[30].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[62].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[63].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[31].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[64].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[65].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[32].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[66].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[67].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[33].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[68].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[69].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[34].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[70].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[71].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[35].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[72].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[73].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[36].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[74].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[75].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[37].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[76].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[77].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[38].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[78].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[79].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[39].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[80].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[81].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[40].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[82].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[83].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[41].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[84].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[85].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[42].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[86].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[87].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[43].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[88].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[89].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[44].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[90].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[91].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[45].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[92].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[93].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[46].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[94].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[95].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[47].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[96].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[97].val = 0x50005
+scaler.SCALE_FILTER_H_BLOCK1[48].val = 0x50000
+scaler.SCALE_FILTER_H_BLOCK0[98].val = 0xb000b
+scaler.SCALE_FILTER_H_BLOCK0[99].val = 0x100010
+scaler.SCALE_FILTER_H_BLOCK1[49].val = 0x10000b
+scaler.SCALE_FILTER_H_BLOCK0[100].val = 0x140014
+scaler.SCALE_FILTER_H_BLOCK0[101].val = 0x180018
+scaler.SCALE_FILTER_H_BLOCK1[50].val = 0x180014
+scaler.SCALE_FILTER_H_BLOCK0[102].val = 0x1c001c
+scaler.SCALE_FILTER_H_BLOCK0[103].val = 0x200020
+scaler.SCALE_FILTER_H_BLOCK1[51].val = 0x20001c
+scaler.SCALE_FILTER_H_BLOCK0[104].val = 0x230023
+scaler.SCALE_FILTER_H_BLOCK0[105].val = 0x260026
+scaler.SCALE_FILTER_H_BLOCK1[52].val = 0x260023
+scaler.SCALE_FILTER_H_BLOCK0[106].val = 0x290029
+scaler.SCALE_FILTER_H_BLOCK0[107].val = 0x2c002c
+scaler.SCALE_FILTER_H_BLOCK1[53].val = 0x2c0029
+scaler.SCALE_FILTER_H_BLOCK0[108].val = 0x2e002e
+scaler.SCALE_FILTER_H_BLOCK0[109].val = 0x300030
+scaler.SCALE_FILTER_H_BLOCK1[54].val = 0x30002e
+scaler.SCALE_FILTER_H_BLOCK0[110].val = 0x320032
+scaler.SCALE_FILTER_H_BLOCK0[111].val = 0x330033
+scaler.SCALE_FILTER_H_BLOCK1[55].val = 0x330032
+scaler.SCALE_FILTER_H_BLOCK0[112].val = 0xff87ff87
+scaler.SCALE_FILTER_H_BLOCK0[113].val = 0xff90ff90
+scaler.SCALE_FILTER_H_BLOCK1[56].val = 0xff90ff87
+scaler.SCALE_FILTER_H_BLOCK0[114].val = 0xff99ff99
+scaler.SCALE_FILTER_H_BLOCK0[115].val = 0xffa1ffa1
+scaler.SCALE_FILTER_H_BLOCK1[57].val = 0xffa1ff99
+scaler.SCALE_FILTER_H_BLOCK0[116].val = 0xffaaffaa
+scaler.SCALE_FILTER_H_BLOCK0[117].val = 0xffb2ffb2
+scaler.SCALE_FILTER_H_BLOCK1[58].val = 0xffb2ffaa
+scaler.SCALE_FILTER_H_BLOCK0[118].val = 0xffbaffba
+scaler.SCALE_FILTER_H_BLOCK0[119].val = 0xffc2ffc2
+scaler.SCALE_FILTER_H_BLOCK1[59].val = 0xffc2ffba
+scaler.SCALE_FILTER_H_BLOCK0[120].val = 0xffcaffca
+scaler.SCALE_FILTER_H_BLOCK0[121].val = 0xffd2ffd2
+scaler.SCALE_FILTER_H_BLOCK1[60].val = 0xffd2ffca
+scaler.SCALE_FILTER_H_BLOCK0[122].val = 0xffd9ffd9
+scaler.SCALE_FILTER_H_BLOCK0[123].val = 0xffe0ffe0
+scaler.SCALE_FILTER_H_BLOCK1[61].val = 0xffe0ffd9
+scaler.SCALE_FILTER_H_BLOCK0[124].val = 0xffe7ffe7
+scaler.SCALE_FILTER_H_BLOCK0[125].val = 0xffeeffee
+scaler.SCALE_FILTER_H_BLOCK1[62].val = 0xffeeffe7
+scaler.SCALE_FILTER_H_BLOCK0[126].val = 0xfff4fff4
+scaler.SCALE_FILTER_H_BLOCK0[127].val = 0xfffafffa
+scaler.SCALE_FILTER_H_BLOCK1[63].val = 0xfffafff4
+scaler.SCALE_FILTER_H_BLOCK0[128].val = 0xff06ff06
+scaler.SCALE_FILTER_H_BLOCK0[129].val = 0xff0cff0c
+scaler.SCALE_FILTER_H_BLOCK1[64].val = 0xff0cff06
+scaler.SCALE_FILTER_H_BLOCK0[130].val = 0xff13ff13
+scaler.SCALE_FILTER_H_BLOCK0[131].val = 0xff1aff1a
+scaler.SCALE_FILTER_H_BLOCK1[65].val = 0xff1aff13
+scaler.SCALE_FILTER_H_BLOCK0[132].val = 0xff21ff21
+scaler.SCALE_FILTER_H_BLOCK0[133].val = 0xff28ff28
+scaler.SCALE_FILTER_H_BLOCK1[66].val = 0xff28ff21
+scaler.SCALE_FILTER_H_BLOCK0[134].val = 0xff30ff30
+scaler.SCALE_FILTER_H_BLOCK0[135].val = 0xff38ff38
+scaler.SCALE_FILTER_H_BLOCK1[67].val = 0xff38ff30
+scaler.SCALE_FILTER_H_BLOCK0[136].val = 0xff41ff41
+scaler.SCALE_FILTER_H_BLOCK0[137].val = 0xff49ff49
+scaler.SCALE_FILTER_H_BLOCK1[68].val = 0xff49ff41
+scaler.SCALE_FILTER_H_BLOCK0[138].val = 0xff52ff52
+scaler.SCALE_FILTER_H_BLOCK0[139].val = 0xff5bff5b
+scaler.SCALE_FILTER_H_BLOCK1[69].val = 0xff5bff52
+scaler.SCALE_FILTER_H_BLOCK0[140].val = 0xff63ff63
+scaler.SCALE_FILTER_H_BLOCK0[141].val = 0xff6cff6c
+scaler.SCALE_FILTER_H_BLOCK1[70].val = 0xff6cff63
+scaler.SCALE_FILTER_H_BLOCK0[142].val = 0xff75ff75
+scaler.SCALE_FILTER_H_BLOCK0[143].val = 0xff7eff7e
+scaler.SCALE_FILTER_H_BLOCK1[71].val = 0xff7eff75
+scaler.SCALE_FILTER_H_BLOCK0[144].val = 0xff02ff02
+scaler.SCALE_FILTER_H_BLOCK0[145].val = 0xfefcfefc
+scaler.SCALE_FILTER_H_BLOCK1[72].val = 0xfefcff02
+scaler.SCALE_FILTER_H_BLOCK0[146].val = 0xfef7fef7
+scaler.SCALE_FILTER_H_BLOCK0[147].val = 0xfef3fef3
+scaler.SCALE_FILTER_H_BLOCK1[73].val = 0xfef3fef7
+scaler.SCALE_FILTER_H_BLOCK0[148].val = 0xfeeffeef
+scaler.SCALE_FILTER_H_BLOCK0[149].val = 0xfeedfeed
+scaler.SCALE_FILTER_H_BLOCK1[74].val = 0xfeedfeef
+scaler.SCALE_FILTER_H_BLOCK0[150].val = 0xfeecfeec
+scaler.SCALE_FILTER_H_BLOCK0[151].val = 0xfeebfeeb
+scaler.SCALE_FILTER_H_BLOCK1[75].val = 0xfeebfeec
+scaler.SCALE_FILTER_H_BLOCK0[152].val = 0xfeebfeeb
+scaler.SCALE_FILTER_H_BLOCK0[153].val = 0xfeecfeec
+scaler.SCALE_FILTER_H_BLOCK1[76].val = 0xfeecfeeb
+scaler.SCALE_FILTER_H_BLOCK0[154].val = 0xfeeefeee
+scaler.SCALE_FILTER_H_BLOCK0[155].val = 0xfef1fef1
+scaler.SCALE_FILTER_H_BLOCK1[77].val = 0xfef1feee
+scaler.SCALE_FILTER_H_BLOCK0[156].val = 0xfef4fef4
+scaler.SCALE_FILTER_H_BLOCK0[157].val = 0xfef8fef8
+scaler.SCALE_FILTER_H_BLOCK1[78].val = 0xfef8fef4
+scaler.SCALE_FILTER_H_BLOCK0[158].val = 0xfefcfefc
+scaler.SCALE_FILTER_H_BLOCK0[159].val = 0xff01ff01
+scaler.SCALE_FILTER_H_BLOCK1[79].val = 0xff01fefc
+scaler.SCALE_FILTER_H_BLOCK0[160].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[161].val = 0xffe7ffe7
+scaler.SCALE_FILTER_H_BLOCK1[80].val = 0xffe70000
+scaler.SCALE_FILTER_H_BLOCK0[162].val = 0xffcfffcf
+scaler.SCALE_FILTER_H_BLOCK0[163].val = 0xffb9ffb9
+scaler.SCALE_FILTER_H_BLOCK1[81].val = 0xffb9ffcf
+scaler.SCALE_FILTER_H_BLOCK0[164].val = 0xffa4ffa4
+scaler.SCALE_FILTER_H_BLOCK0[165].val = 0xff90ff90
+scaler.SCALE_FILTER_H_BLOCK1[82].val = 0xff90ffa4
+scaler.SCALE_FILTER_H_BLOCK0[166].val = 0xff7dff7d
+scaler.SCALE_FILTER_H_BLOCK0[167].val = 0xff6bff6b
+scaler.SCALE_FILTER_H_BLOCK1[83].val = 0xff6bff7d
+scaler.SCALE_FILTER_H_BLOCK0[168].val = 0xff5bff5b
+scaler.SCALE_FILTER_H_BLOCK0[169].val = 0xff4cff4c
+scaler.SCALE_FILTER_H_BLOCK1[84].val = 0xff4cff5b
+scaler.SCALE_FILTER_H_BLOCK0[170].val = 0xff3eff3e
+scaler.SCALE_FILTER_H_BLOCK0[171].val = 0xff31ff31
+scaler.SCALE_FILTER_H_BLOCK1[85].val = 0xff31ff3e
+scaler.SCALE_FILTER_H_BLOCK0[172].val = 0xff26ff26
+scaler.SCALE_FILTER_H_BLOCK0[173].val = 0xff1bff1b
+scaler.SCALE_FILTER_H_BLOCK1[86].val = 0xff1bff26
+scaler.SCALE_FILTER_H_BLOCK0[174].val = 0xff12ff12
+scaler.SCALE_FILTER_H_BLOCK0[175].val = 0xff0aff0a
+scaler.SCALE_FILTER_H_BLOCK1[87].val = 0xff0aff12
+scaler.SCALE_FILTER_H_BLOCK0[176].val = 0x2210221
+scaler.SCALE_FILTER_H_BLOCK0[177].val = 0x1f901f9
+scaler.SCALE_FILTER_H_BLOCK1[88].val = 0x1f90221
+scaler.SCALE_FILTER_H_BLOCK0[178].val = 0x1d001d0
+scaler.SCALE_FILTER_H_BLOCK0[179].val = 0x1a901a9
+scaler.SCALE_FILTER_H_BLOCK1[89].val = 0x1a901d0
+scaler.SCALE_FILTER_H_BLOCK0[180].val = 0x1820182
+scaler.SCALE_FILTER_H_BLOCK0[181].val = 0x15d015d
+scaler.SCALE_FILTER_H_BLOCK1[90].val = 0x15d0182
+scaler.SCALE_FILTER_H_BLOCK0[182].val = 0x1380138
+scaler.SCALE_FILTER_H_BLOCK0[183].val = 0x1140114
+scaler.SCALE_FILTER_H_BLOCK1[91].val = 0x1140138
+scaler.SCALE_FILTER_H_BLOCK0[184].val = 0xf100f1
+scaler.SCALE_FILTER_H_BLOCK0[185].val = 0xcf00cf
+scaler.SCALE_FILTER_H_BLOCK1[92].val = 0xcf00f1
+scaler.SCALE_FILTER_H_BLOCK0[186].val = 0xae00ae
+scaler.SCALE_FILTER_H_BLOCK0[187].val = 0x8e008e
+scaler.SCALE_FILTER_H_BLOCK1[93].val = 0x8e00ae
+scaler.SCALE_FILTER_H_BLOCK0[188].val = 0x6f006f
+scaler.SCALE_FILTER_H_BLOCK0[189].val = 0x520052
+scaler.SCALE_FILTER_H_BLOCK1[94].val = 0x52006f
+scaler.SCALE_FILTER_H_BLOCK0[190].val = 0x350035
+scaler.SCALE_FILTER_H_BLOCK0[191].val = 0x1a001a
+scaler.SCALE_FILTER_H_BLOCK1[95].val = 0x1a0035
+scaler.SCALE_FILTER_H_BLOCK0[192].val = 0x4e404e4
+scaler.SCALE_FILTER_H_BLOCK0[193].val = 0x4b804b8
+scaler.SCALE_FILTER_H_BLOCK1[96].val = 0x4b804e4
+scaler.SCALE_FILTER_H_BLOCK0[194].val = 0x48b048b
+scaler.SCALE_FILTER_H_BLOCK0[195].val = 0x45f045f
+scaler.SCALE_FILTER_H_BLOCK1[97].val = 0x45f048b
+scaler.SCALE_FILTER_H_BLOCK0[196].val = 0x4320432
+scaler.SCALE_FILTER_H_BLOCK0[197].val = 0x4050405
+scaler.SCALE_FILTER_H_BLOCK1[98].val = 0x4050432
+scaler.SCALE_FILTER_H_BLOCK0[198].val = 0x3d803d8
+scaler.SCALE_FILTER_H_BLOCK0[199].val = 0x3ab03ab
+scaler.SCALE_FILTER_H_BLOCK1[99].val = 0x3ab03d8
+scaler.SCALE_FILTER_H_BLOCK0[200].val = 0x37e037e
+scaler.SCALE_FILTER_H_BLOCK0[201].val = 0x3510351
+scaler.SCALE_FILTER_H_BLOCK1[100].val = 0x351037e
+scaler.SCALE_FILTER_H_BLOCK0[202].val = 0x3240324
+scaler.SCALE_FILTER_H_BLOCK0[203].val = 0x2f802f8
+scaler.SCALE_FILTER_H_BLOCK1[101].val = 0x2f80324
+scaler.SCALE_FILTER_H_BLOCK0[204].val = 0x2cc02cc
+scaler.SCALE_FILTER_H_BLOCK0[205].val = 0x2a102a1
+scaler.SCALE_FILTER_H_BLOCK1[102].val = 0x2a102cc
+scaler.SCALE_FILTER_H_BLOCK0[206].val = 0x2760276
+scaler.SCALE_FILTER_H_BLOCK0[207].val = 0x24b024b
+scaler.SCALE_FILTER_H_BLOCK1[103].val = 0x24b0276
+scaler.SCALE_FILTER_H_BLOCK0[208].val = 0x73b073b
+scaler.SCALE_FILTER_H_BLOCK0[209].val = 0x71e071e
+scaler.SCALE_FILTER_H_BLOCK1[104].val = 0x71e073b
+scaler.SCALE_FILTER_H_BLOCK0[210].val = 0x7000700
+scaler.SCALE_FILTER_H_BLOCK0[211].val = 0x6e106e1
+scaler.SCALE_FILTER_H_BLOCK1[105].val = 0x6e10700
+scaler.SCALE_FILTER_H_BLOCK0[212].val = 0x6c006c0
+scaler.SCALE_FILTER_H_BLOCK0[213].val = 0x69e069e
+scaler.SCALE_FILTER_H_BLOCK1[106].val = 0x69e06c0
+scaler.SCALE_FILTER_H_BLOCK0[214].val = 0x67a067a
+scaler.SCALE_FILTER_H_BLOCK0[215].val = 0x6560656
+scaler.SCALE_FILTER_H_BLOCK1[107].val = 0x656067a
+scaler.SCALE_FILTER_H_BLOCK0[216].val = 0x6300630
+scaler.SCALE_FILTER_H_BLOCK0[217].val = 0x6090609
+scaler.SCALE_FILTER_H_BLOCK1[108].val = 0x6090630
+scaler.SCALE_FILTER_H_BLOCK0[218].val = 0x5e205e2
+scaler.SCALE_FILTER_H_BLOCK0[219].val = 0x5b905b9
+scaler.SCALE_FILTER_H_BLOCK1[109].val = 0x5b905e2
+scaler.SCALE_FILTER_H_BLOCK0[220].val = 0x5900590
+scaler.SCALE_FILTER_H_BLOCK0[221].val = 0x5660566
+scaler.SCALE_FILTER_H_BLOCK1[110].val = 0x5660590
+scaler.SCALE_FILTER_H_BLOCK0[222].val = 0x53b053b
+scaler.SCALE_FILTER_H_BLOCK0[223].val = 0x5100510
+scaler.SCALE_FILTER_H_BLOCK1[111].val = 0x510053b
+scaler.SCALE_FILTER_H_BLOCK0[224].val = 0x82c082c
+scaler.SCALE_FILTER_H_BLOCK0[225].val = 0x82b082b
+scaler.SCALE_FILTER_H_BLOCK1[112].val = 0x82b082c
+scaler.SCALE_FILTER_H_BLOCK0[226].val = 0x8280828
+scaler.SCALE_FILTER_H_BLOCK0[227].val = 0x8200820
+scaler.SCALE_FILTER_H_BLOCK1[113].val = 0x8200828
+scaler.SCALE_FILTER_H_BLOCK0[228].val = 0x81b081b
+scaler.SCALE_FILTER_H_BLOCK0[229].val = 0x8130813
+scaler.SCALE_FILTER_H_BLOCK1[114].val = 0x813081b
+scaler.SCALE_FILTER_H_BLOCK0[230].val = 0x8080808
+scaler.SCALE_FILTER_H_BLOCK0[231].val = 0x7fc07fc
+scaler.SCALE_FILTER_H_BLOCK1[115].val = 0x7fc0808
+scaler.SCALE_FILTER_H_BLOCK0[232].val = 0x7ed07ed
+scaler.SCALE_FILTER_H_BLOCK0[233].val = 0x7dd07dd
+scaler.SCALE_FILTER_H_BLOCK1[116].val = 0x7dd07ed
+scaler.SCALE_FILTER_H_BLOCK0[234].val = 0x7cb07cb
+scaler.SCALE_FILTER_H_BLOCK0[235].val = 0x7b607b6
+scaler.SCALE_FILTER_H_BLOCK1[117].val = 0x7b607cb
+scaler.SCALE_FILTER_H_BLOCK0[236].val = 0x7a207a2
+scaler.SCALE_FILTER_H_BLOCK0[237].val = 0x78a078a
+scaler.SCALE_FILTER_H_BLOCK1[118].val = 0x78a07a2
+scaler.SCALE_FILTER_H_BLOCK0[238].val = 0x7710771
+scaler.SCALE_FILTER_H_BLOCK0[239].val = 0x7570757
+scaler.SCALE_FILTER_H_BLOCK1[119].val = 0x7570771
+scaler.SCALE_FILTER_H_BLOCK0[240].val = 0x73d073d
+scaler.SCALE_FILTER_H_BLOCK0[241].val = 0x7570757
+scaler.SCALE_FILTER_H_BLOCK1[120].val = 0x757073d
+scaler.SCALE_FILTER_H_BLOCK0[242].val = 0x7710771
+scaler.SCALE_FILTER_H_BLOCK0[243].val = 0x78a078a
+scaler.SCALE_FILTER_H_BLOCK1[121].val = 0x78a0771
+scaler.SCALE_FILTER_H_BLOCK0[244].val = 0x7a207a2
+scaler.SCALE_FILTER_H_BLOCK0[245].val = 0x7b607b6
+scaler.SCALE_FILTER_H_BLOCK1[122].val = 0x7b607a2
+scaler.SCALE_FILTER_H_BLOCK0[246].val = 0x7cb07cb
+scaler.SCALE_FILTER_H_BLOCK0[247].val = 0x7dd07dd
+scaler.SCALE_FILTER_H_BLOCK1[123].val = 0x7dd07cb
+scaler.SCALE_FILTER_H_BLOCK0[248].val = 0x7ed07ed
+scaler.SCALE_FILTER_H_BLOCK0[249].val = 0x7fc07fc
+scaler.SCALE_FILTER_H_BLOCK1[124].val = 0x7fc07ed
+scaler.SCALE_FILTER_H_BLOCK0[250].val = 0x8080808
+scaler.SCALE_FILTER_H_BLOCK0[251].val = 0x8130813
+scaler.SCALE_FILTER_H_BLOCK1[125].val = 0x8130808
+scaler.SCALE_FILTER_H_BLOCK0[252].val = 0x81b081b
+scaler.SCALE_FILTER_H_BLOCK0[253].val = 0x8200820
+scaler.SCALE_FILTER_H_BLOCK1[126].val = 0x820081b
+scaler.SCALE_FILTER_H_BLOCK0[254].val = 0x8280828
+scaler.SCALE_FILTER_H_BLOCK0[255].val = 0x82b082b
+scaler.SCALE_FILTER_H_BLOCK1[127].val = 0x82b0828
+scaler.SCALE_FILTER_H_BLOCK0[256].val = 0x4e404e4
+scaler.SCALE_FILTER_H_BLOCK0[257].val = 0x5100510
+scaler.SCALE_FILTER_H_BLOCK1[128].val = 0x51004e4
+scaler.SCALE_FILTER_H_BLOCK0[258].val = 0x53b053b
+scaler.SCALE_FILTER_H_BLOCK0[259].val = 0x5660566
+scaler.SCALE_FILTER_H_BLOCK1[129].val = 0x566053b
+scaler.SCALE_FILTER_H_BLOCK0[260].val = 0x5900590
+scaler.SCALE_FILTER_H_BLOCK0[261].val = 0x5b905b9
+scaler.SCALE_FILTER_H_BLOCK1[130].val = 0x5b90590
+scaler.SCALE_FILTER_H_BLOCK0[262].val = 0x5e205e2
+scaler.SCALE_FILTER_H_BLOCK0[263].val = 0x6090609
+scaler.SCALE_FILTER_H_BLOCK1[131].val = 0x60905e2
+scaler.SCALE_FILTER_H_BLOCK0[264].val = 0x6300630
+scaler.SCALE_FILTER_H_BLOCK0[265].val = 0x6560656
+scaler.SCALE_FILTER_H_BLOCK1[132].val = 0x6560630
+scaler.SCALE_FILTER_H_BLOCK0[266].val = 0x67a067a
+scaler.SCALE_FILTER_H_BLOCK0[267].val = 0x69e069e
+scaler.SCALE_FILTER_H_BLOCK1[133].val = 0x69e067a
+scaler.SCALE_FILTER_H_BLOCK0[268].val = 0x6c006c0
+scaler.SCALE_FILTER_H_BLOCK0[269].val = 0x6e106e1
+scaler.SCALE_FILTER_H_BLOCK1[134].val = 0x6e106c0
+scaler.SCALE_FILTER_H_BLOCK0[270].val = 0x7000700
+scaler.SCALE_FILTER_H_BLOCK0[271].val = 0x71e071e
+scaler.SCALE_FILTER_H_BLOCK1[135].val = 0x71e0700
+scaler.SCALE_FILTER_H_BLOCK0[272].val = 0x2210221
+scaler.SCALE_FILTER_H_BLOCK0[273].val = 0x24b024b
+scaler.SCALE_FILTER_H_BLOCK1[136].val = 0x24b0221
+scaler.SCALE_FILTER_H_BLOCK0[274].val = 0x2760276
+scaler.SCALE_FILTER_H_BLOCK0[275].val = 0x2a102a1
+scaler.SCALE_FILTER_H_BLOCK1[137].val = 0x2a10276
+scaler.SCALE_FILTER_H_BLOCK0[276].val = 0x2cc02cc
+scaler.SCALE_FILTER_H_BLOCK0[277].val = 0x2f802f8
+scaler.SCALE_FILTER_H_BLOCK1[138].val = 0x2f802cc
+scaler.SCALE_FILTER_H_BLOCK0[278].val = 0x3240324
+scaler.SCALE_FILTER_H_BLOCK0[279].val = 0x3510351
+scaler.SCALE_FILTER_H_BLOCK1[139].val = 0x3510324
+scaler.SCALE_FILTER_H_BLOCK0[280].val = 0x37e037e
+scaler.SCALE_FILTER_H_BLOCK0[281].val = 0x3ab03ab
+scaler.SCALE_FILTER_H_BLOCK1[140].val = 0x3ab037e
+scaler.SCALE_FILTER_H_BLOCK0[282].val = 0x3d803d8
+scaler.SCALE_FILTER_H_BLOCK0[283].val = 0x4050405
+scaler.SCALE_FILTER_H_BLOCK1[141].val = 0x40503d8
+scaler.SCALE_FILTER_H_BLOCK0[284].val = 0x4320432
+scaler.SCALE_FILTER_H_BLOCK0[285].val = 0x45f045f
+scaler.SCALE_FILTER_H_BLOCK1[142].val = 0x45f0432
+scaler.SCALE_FILTER_H_BLOCK0[286].val = 0x48b048b
+scaler.SCALE_FILTER_H_BLOCK0[287].val = 0x4b804b8
+scaler.SCALE_FILTER_H_BLOCK1[143].val = 0x4b8048b
+scaler.SCALE_FILTER_H_BLOCK0[288].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[289].val = 0x1a001a
+scaler.SCALE_FILTER_H_BLOCK1[144].val = 0x1a0000
+scaler.SCALE_FILTER_H_BLOCK0[290].val = 0x350035
+scaler.SCALE_FILTER_H_BLOCK0[291].val = 0x520052
+scaler.SCALE_FILTER_H_BLOCK1[145].val = 0x520035
+scaler.SCALE_FILTER_H_BLOCK0[292].val = 0x6f006f
+scaler.SCALE_FILTER_H_BLOCK0[293].val = 0x8e008e
+scaler.SCALE_FILTER_H_BLOCK1[146].val = 0x8e006f
+scaler.SCALE_FILTER_H_BLOCK0[294].val = 0xae00ae
+scaler.SCALE_FILTER_H_BLOCK0[295].val = 0xcf00cf
+scaler.SCALE_FILTER_H_BLOCK1[147].val = 0xcf00ae
+scaler.SCALE_FILTER_H_BLOCK0[296].val = 0xf100f1
+scaler.SCALE_FILTER_H_BLOCK0[297].val = 0x1140114
+scaler.SCALE_FILTER_H_BLOCK1[148].val = 0x11400f1
+scaler.SCALE_FILTER_H_BLOCK0[298].val = 0x1380138
+scaler.SCALE_FILTER_H_BLOCK0[299].val = 0x15d015d
+scaler.SCALE_FILTER_H_BLOCK1[149].val = 0x15d0138
+scaler.SCALE_FILTER_H_BLOCK0[300].val = 0x1820182
+scaler.SCALE_FILTER_H_BLOCK0[301].val = 0x1a901a9
+scaler.SCALE_FILTER_H_BLOCK1[150].val = 0x1a90182
+scaler.SCALE_FILTER_H_BLOCK0[302].val = 0x1d001d0
+scaler.SCALE_FILTER_H_BLOCK0[303].val = 0x1f901f9
+scaler.SCALE_FILTER_H_BLOCK1[151].val = 0x1f901d0
+scaler.SCALE_FILTER_H_BLOCK0[304].val = 0xff02ff02
+scaler.SCALE_FILTER_H_BLOCK0[305].val = 0xff0aff0a
+scaler.SCALE_FILTER_H_BLOCK1[152].val = 0xff0aff02
+scaler.SCALE_FILTER_H_BLOCK0[306].val = 0xff12ff12
+scaler.SCALE_FILTER_H_BLOCK0[307].val = 0xff1bff1b
+scaler.SCALE_FILTER_H_BLOCK1[153].val = 0xff1bff12
+scaler.SCALE_FILTER_H_BLOCK0[308].val = 0xff26ff26
+scaler.SCALE_FILTER_H_BLOCK0[309].val = 0xff31ff31
+scaler.SCALE_FILTER_H_BLOCK1[154].val = 0xff31ff26
+scaler.SCALE_FILTER_H_BLOCK0[310].val = 0xff3eff3e
+scaler.SCALE_FILTER_H_BLOCK0[311].val = 0xff4cff4c
+scaler.SCALE_FILTER_H_BLOCK1[155].val = 0xff4cff3e
+scaler.SCALE_FILTER_H_BLOCK0[312].val = 0xff5bff5b
+scaler.SCALE_FILTER_H_BLOCK0[313].val = 0xff6bff6b
+scaler.SCALE_FILTER_H_BLOCK1[156].val = 0xff6bff5b
+scaler.SCALE_FILTER_H_BLOCK0[314].val = 0xff7dff7d
+scaler.SCALE_FILTER_H_BLOCK0[315].val = 0xff90ff90
+scaler.SCALE_FILTER_H_BLOCK1[157].val = 0xff90ff7d
+scaler.SCALE_FILTER_H_BLOCK0[316].val = 0xffa4ffa4
+scaler.SCALE_FILTER_H_BLOCK0[317].val = 0xffb9ffb9
+scaler.SCALE_FILTER_H_BLOCK1[158].val = 0xffb9ffa4
+scaler.SCALE_FILTER_H_BLOCK0[318].val = 0xffcfffcf
+scaler.SCALE_FILTER_H_BLOCK0[319].val = 0xffe7ffe7
+scaler.SCALE_FILTER_H_BLOCK1[159].val = 0xffe7ffcf
+scaler.SCALE_FILTER_H_BLOCK0[320].val = 0xff06ff06
+scaler.SCALE_FILTER_H_BLOCK0[321].val = 0xff01ff01
+scaler.SCALE_FILTER_H_BLOCK1[160].val = 0xff01ff06
+scaler.SCALE_FILTER_H_BLOCK0[322].val = 0xfefcfefc
+scaler.SCALE_FILTER_H_BLOCK0[323].val = 0xfef8fef8
+scaler.SCALE_FILTER_H_BLOCK1[161].val = 0xfef8fefc
+scaler.SCALE_FILTER_H_BLOCK0[324].val = 0xfef4fef4
+scaler.SCALE_FILTER_H_BLOCK0[325].val = 0xfef1fef1
+scaler.SCALE_FILTER_H_BLOCK1[162].val = 0xfef1fef4
+scaler.SCALE_FILTER_H_BLOCK0[326].val = 0xfeeefeee
+scaler.SCALE_FILTER_H_BLOCK0[327].val = 0xfeecfeec
+scaler.SCALE_FILTER_H_BLOCK1[163].val = 0xfeecfeee
+scaler.SCALE_FILTER_H_BLOCK0[328].val = 0xfeebfeeb
+scaler.SCALE_FILTER_H_BLOCK0[329].val = 0xfeebfeeb
+scaler.SCALE_FILTER_H_BLOCK1[164].val = 0xfeebfeeb
+scaler.SCALE_FILTER_H_BLOCK0[330].val = 0xfeecfeec
+scaler.SCALE_FILTER_H_BLOCK0[331].val = 0xfeedfeed
+scaler.SCALE_FILTER_H_BLOCK1[165].val = 0xfeedfeec
+scaler.SCALE_FILTER_H_BLOCK0[332].val = 0xfeeffeef
+scaler.SCALE_FILTER_H_BLOCK0[333].val = 0xfef3fef3
+scaler.SCALE_FILTER_H_BLOCK1[166].val = 0xfef3feef
+scaler.SCALE_FILTER_H_BLOCK0[334].val = 0xfef7fef7
+scaler.SCALE_FILTER_H_BLOCK0[335].val = 0xfefcfefc
+scaler.SCALE_FILTER_H_BLOCK1[167].val = 0xfefcfef7
+scaler.SCALE_FILTER_H_BLOCK0[336].val = 0xff87ff87
+scaler.SCALE_FILTER_H_BLOCK0[337].val = 0xff7eff7e
+scaler.SCALE_FILTER_H_BLOCK1[168].val = 0xff7eff87
+scaler.SCALE_FILTER_H_BLOCK0[338].val = 0xff75ff75
+scaler.SCALE_FILTER_H_BLOCK0[339].val = 0xff6cff6c
+scaler.SCALE_FILTER_H_BLOCK1[169].val = 0xff6cff75
+scaler.SCALE_FILTER_H_BLOCK0[340].val = 0xff63ff63
+scaler.SCALE_FILTER_H_BLOCK0[341].val = 0xff5bff5b
+scaler.SCALE_FILTER_H_BLOCK1[170].val = 0xff5bff63
+scaler.SCALE_FILTER_H_BLOCK0[342].val = 0xff52ff52
+scaler.SCALE_FILTER_H_BLOCK0[343].val = 0xff49ff49
+scaler.SCALE_FILTER_H_BLOCK1[171].val = 0xff49ff52
+scaler.SCALE_FILTER_H_BLOCK0[344].val = 0xff41ff41
+scaler.SCALE_FILTER_H_BLOCK0[345].val = 0xff38ff38
+scaler.SCALE_FILTER_H_BLOCK1[172].val = 0xff38ff41
+scaler.SCALE_FILTER_H_BLOCK0[346].val = 0xff30ff30
+scaler.SCALE_FILTER_H_BLOCK0[347].val = 0xff28ff28
+scaler.SCALE_FILTER_H_BLOCK1[173].val = 0xff28ff30
+scaler.SCALE_FILTER_H_BLOCK0[348].val = 0xff21ff21
+scaler.SCALE_FILTER_H_BLOCK0[349].val = 0xff1aff1a
+scaler.SCALE_FILTER_H_BLOCK1[174].val = 0xff1aff21
+scaler.SCALE_FILTER_H_BLOCK0[350].val = 0xff13ff13
+scaler.SCALE_FILTER_H_BLOCK0[351].val = 0xff0cff0c
+scaler.SCALE_FILTER_H_BLOCK1[175].val = 0xff0cff13
+scaler.SCALE_FILTER_H_BLOCK0[352].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[353].val = 0xfffafffa
+scaler.SCALE_FILTER_H_BLOCK1[176].val = 0xfffa0000
+scaler.SCALE_FILTER_H_BLOCK0[354].val = 0xfff4fff4
+scaler.SCALE_FILTER_H_BLOCK0[355].val = 0xffeeffee
+scaler.SCALE_FILTER_H_BLOCK1[177].val = 0xffeefff4
+scaler.SCALE_FILTER_H_BLOCK0[356].val = 0xffe7ffe7
+scaler.SCALE_FILTER_H_BLOCK0[357].val = 0xffe0ffe0
+scaler.SCALE_FILTER_H_BLOCK1[178].val = 0xffe0ffe7
+scaler.SCALE_FILTER_H_BLOCK0[358].val = 0xffd9ffd9
+scaler.SCALE_FILTER_H_BLOCK0[359].val = 0xffd2ffd2
+scaler.SCALE_FILTER_H_BLOCK1[179].val = 0xffd2ffd9
+scaler.SCALE_FILTER_H_BLOCK0[360].val = 0xffcaffca
+scaler.SCALE_FILTER_H_BLOCK0[361].val = 0xffc2ffc2
+scaler.SCALE_FILTER_H_BLOCK1[180].val = 0xffc2ffca
+scaler.SCALE_FILTER_H_BLOCK0[362].val = 0xffbaffba
+scaler.SCALE_FILTER_H_BLOCK0[363].val = 0xffb2ffb2
+scaler.SCALE_FILTER_H_BLOCK1[181].val = 0xffb2ffba
+scaler.SCALE_FILTER_H_BLOCK0[364].val = 0xffaaffaa
+scaler.SCALE_FILTER_H_BLOCK0[365].val = 0xffa1ffa1
+scaler.SCALE_FILTER_H_BLOCK1[182].val = 0xffa1ffaa
+scaler.SCALE_FILTER_H_BLOCK0[366].val = 0xff99ff99
+scaler.SCALE_FILTER_H_BLOCK0[367].val = 0xff90ff90
+scaler.SCALE_FILTER_H_BLOCK1[183].val = 0xff90ff99
+scaler.SCALE_FILTER_H_BLOCK0[368].val = 0x340034
+scaler.SCALE_FILTER_H_BLOCK0[369].val = 0x330033
+scaler.SCALE_FILTER_H_BLOCK1[184].val = 0x330034
+scaler.SCALE_FILTER_H_BLOCK0[370].val = 0x320032
+scaler.SCALE_FILTER_H_BLOCK0[371].val = 0x300030
+scaler.SCALE_FILTER_H_BLOCK1[185].val = 0x300032
+scaler.SCALE_FILTER_H_BLOCK0[372].val = 0x2e002e
+scaler.SCALE_FILTER_H_BLOCK0[373].val = 0x2c002c
+scaler.SCALE_FILTER_H_BLOCK1[186].val = 0x2c002e
+scaler.SCALE_FILTER_H_BLOCK0[374].val = 0x290029
+scaler.SCALE_FILTER_H_BLOCK0[375].val = 0x260026
+scaler.SCALE_FILTER_H_BLOCK1[187].val = 0x260029
+scaler.SCALE_FILTER_H_BLOCK0[376].val = 0x230023
+scaler.SCALE_FILTER_H_BLOCK0[377].val = 0x200020
+scaler.SCALE_FILTER_H_BLOCK1[188].val = 0x200023
+scaler.SCALE_FILTER_H_BLOCK0[378].val = 0x1c001c
+scaler.SCALE_FILTER_H_BLOCK0[379].val = 0x180018
+scaler.SCALE_FILTER_H_BLOCK1[189].val = 0x18001c
+scaler.SCALE_FILTER_H_BLOCK0[380].val = 0x140014
+scaler.SCALE_FILTER_H_BLOCK0[381].val = 0x100010
+scaler.SCALE_FILTER_H_BLOCK1[190].val = 0x100014
+scaler.SCALE_FILTER_H_BLOCK0[382].val = 0xb000b
+scaler.SCALE_FILTER_H_BLOCK0[383].val = 0x50005
+scaler.SCALE_FILTER_H_BLOCK1[191].val = 0x5000b
+scaler.SCALE_FILTER_H_BLOCK0[384].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[385].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[192].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[386].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[387].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[193].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[388].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[389].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[194].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[390].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[391].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[195].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[392].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[393].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[196].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[394].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[395].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[197].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[396].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[397].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[198].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[398].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[399].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[199].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[400].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[401].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[200].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[402].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[403].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[201].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[404].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[405].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[202].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[406].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[407].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[203].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[408].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[409].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[204].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[410].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[411].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[205].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[412].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[413].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[206].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[414].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[415].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[207].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[416].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[417].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[208].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[418].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[419].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[209].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[420].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[421].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[210].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[422].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[423].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[211].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[424].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[425].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[212].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[426].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[427].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[213].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[428].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[429].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[214].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[430].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[431].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[215].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[432].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[433].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[216].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[434].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[435].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[217].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[436].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[437].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[218].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[438].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[439].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[219].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[440].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[441].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[220].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[442].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[443].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[221].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[444].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[445].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[222].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[446].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[447].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[223].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[448].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[449].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[224].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[450].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[451].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[225].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[452].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[453].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[226].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[454].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[455].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[227].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[456].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[457].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[228].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[458].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[459].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[229].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[460].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[461].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[230].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[462].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[463].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[231].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[464].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[465].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[232].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[466].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[467].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[233].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[468].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[469].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[234].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[470].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[471].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[235].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[472].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[473].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[236].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[474].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[475].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[237].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[476].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[477].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[238].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[478].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK0[479].val = 0x0
+scaler.SCALE_FILTER_H_BLOCK1[239].val = 0x0
+
+# pseudo linear scaling
+scaler.PSEUDO_LINEAR_SCALING = 0
+
+# reshape
+p.write32(scaler_base + 0xe8, 0x0)
+
+# reset CM 3x
+p.write32(scaler_base + 0x3800, 0x0)
+p.write32(scaler_base + 0x3800, 0x0)
+p.write32(scaler_base + 0x3800, 0x0)
+
+# enable prescaler
+p.write32(scaler_base + 0x824, 0xc)
+
+# alpha override
+p.write32(scaler_base + 0x8c, 0xffff)
+
+# dither
+p.write32(scaler_base + 0xa00, 0x0)
+
+# commit convert
+p.write32(scaler_base + 0x13808, 0x0)
+p.write32(scaler_base + 0x1380c, 0x0)
+p.write32(scaler_base + 0x13800, 0x0)
+p.write32(scaler_base + 0x13804, 0x0)
+
+# convert map
+p.write32(scaler_base + 0x13810, 0x8)
+p.write32(scaler_base + 0x13814, 0x8)
+p.write32(scaler_base + 0x13818, 0x8)
+p.write32(scaler_base + 0x1381c, 0x8)
+p.write32(scaler_base + 0x13804, 0x0)
+p.write32(scaler_base + 0x13c04, 0x0)
+p.write32(scaler_base + 0x13c10, 0x8)
+p.write32(scaler_base + 0x13c14, 0x8)
+p.write32(scaler_base + 0x13c18, 0x8)
+p.write32(scaler_base + 0x13c1c, 0x8)
+
+# commit revert
+p.write32(scaler_base + 0x13c00, 0x1)
+
+# (don't) program histogram
+p.write32(scaler_base + 0x3000, 0x0)
+p.write32(scaler_base + 0x124, 0x0)
+
+# tag transform registers
+p.write32(scaler_base + 0x110, 0x1)
+
+# start
+p.write32(scaler_base + 0x98, 0xfffffffe)
+scaler.START = 1
+
+start_time = time.time()
+while scaler.MSR_GLBL_IRQSTS.reg.DONE == 0:
+ if time.time() - start_time > 5:
+ print("TIMED OUT!!!")
+ break
+
+print(f"IRQ status is now {scaler.MSR_GLBL_IRQSTS}")
+print(f"Debug status is now {scaler.MSR_CTRL_DBGSTS}")
+
+out_buf_new = iface.readmem(out_buf_phys, out_SZ)
+chexdump(out_buf_new)
+
+with Image.new(mode='RGBA', size=(out_W, out_H)) as im:
+ for y in range(out_H):
+ for x in range(out_W):
+ block = out_buf_new[
+ y*out_STRIDE + x*out_BYTESPP:
+ y*out_STRIDE + (x+1)*out_BYTESPP]
+
+ r, g, b, a = block
+ im.putpixel((x, y), (r, g, b, a))
+
+ im.save(output_image_fn)
diff --git a/tools/proxyclient/experiments/smc.py b/tools/proxyclient/experiments/smc.py
new file mode 100755
index 0000000..5e4a383
--- /dev/null
+++ b/tools/proxyclient/experiments/smc.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+import time
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+import struct
+from m1n1.setup import *
+from m1n1.fw.smc import SMCClient, SMCError
+
+smc_addr = u.adt["arm-io/smc"].get_reg(0)[0]
+smc = SMCClient(u, smc_addr)
+smc.start()
+smc.start_ep(0x20)
+
+smc.verbose = 0
+
+smcep = smc.epmap[0x20]
+
+def gpio_key(pin):
+ assert(pin < (1 << 16))
+
+ fourcc = 'gP' + ('00'+(hex(pin)[2:]))[-2:]
+ return fourcc
+
+## Enable wifi/bluetooth
+#RFKILL_PIN = 13
+#smcep.write(gpio_key(RFKILL_PIN), struct.pack('<I', 0x800000 | 0x0))
+#smcep.write(gpio_key(RFKILL_PIN), struct.pack('<I', 0x800000 | 0x1))
+
+count = smcep.read32b("#KEY")
+print(f"Key count: {count}")
+
+for i in range(count):
+ k = smcep.get_key_by_index(i)
+ length, type, flags = smcep.get_key_info(k)
+ if flags & 0x80:
+ try:
+ val = smcep.read_type(k, length, type)
+ print(f"#{i}: {k} = ({type}, {flags:#x}) {val}")
+ except SMCError as e:
+ print(f"#{i}: {k} = ({type}, {flags:#x}) <error {e}>")
+ else:
+ print(f"#{i}: {k} = ({type}, {flags:#x}) <not available>")
+
+
+smc.stop()
diff --git a/tools/proxyclient/experiments/smc_watcher.py b/tools/proxyclient/experiments/smc_watcher.py
new file mode 100755
index 0000000..9d07339
--- /dev/null
+++ b/tools/proxyclient/experiments/smc_watcher.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib, fnmatch, signal
+import time
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+import struct
+from m1n1.setup import *
+from m1n1.shell import run_shell
+from m1n1.fw.smc import SMCClient, SMCError
+
+smc_addr = u.adt["arm-io/smc"].get_reg(0)[0]
+smc = SMCClient(u, smc_addr)
+smc.start()
+smc.start_ep(0x20)
+
+smc.verbose = 0
+
+smcep = smc.epmap[0x20]
+
+count = smcep.read32b("#KEY")
+print(f"Key count: {count}")
+
+print("Scanning keys...")
+
+pats = sys.argv[1:]
+
+vals = {}
+
+fmts = {
+ "D?CR": "#x",
+ "AC-I": "#x",
+ "D?FC": "#x",
+ "D?VM": lambda v: (v>>8) | ((v&0xff)<<8),
+ "D?VX": lambda v: (v>>8) | ((v&0xff)<<8),
+ "B0RM": lambda v: (v>>8) | ((v&0xff)<<8),
+ ##"BAAC": lambda v: ((v&0xff00)>>8) | ((v&0xff)<<8),
+}
+
+smcep.write8("NTAP", 1)
+
+for i in range(count):
+ k = smcep.get_key_by_index(i)
+ if not any(fnmatch.fnmatchcase(k, i) for i in pats):
+ continue
+ if any(fnmatch.fnmatchcase('-' + k, i) for i in pats):
+ continue
+ length, type, flags = smcep.get_key_info(k)
+ if type in ("ch8*", "{jst"):
+ continue
+ if flags & 0x80:
+ try:
+ val = smcep.read_type(k, length, type)
+ fmt = None
+ for fk, fv in fmts.items():
+ if fnmatch.fnmatchcase(k, fk):
+ fmt = fv
+ if fmt is None:
+ fmt = lambda a: ("%.02f" % a) if isinstance(a, float) else a
+ elif isinstance(fmt, str):
+ def ff(fmt):
+ return lambda a: f"{a:{fmt}}"
+ fmt = ff(fmt)
+ vals[k] = val, length, type, fmt
+ print(f"#{i}: {k} = ({type}, {flags:#x}) {fmt(val)}")
+ except SMCError as e:
+ print(f"#{i}: {k} = ({type}, {flags:#x}) <error {e}>")
+ else:
+ print(f"#{i}: {k} = ({type}, {flags:#x}) <not available>")
+
+slots = {}
+
+def poll():
+ global cnt
+ reprint = cnt % 10 == 0
+ changed = set()
+ for k, (oval, length, type, fmt) in vals.items():
+ val = smcep.read_type(k, length, type)
+ if val != oval:
+ if k not in slots:
+ reprint = True
+ slots[k] = fmt(val)
+ changed.add(k)
+ vals[k] = val, length, type, fmt
+ if reprint:
+ print("\x1b[1;4m", end="")
+ for k, v in slots.items():
+ wd = len(f"{v:>8}")
+ print(f"{k:>{wd}s}", end=" ")
+ print("\x1b[m")
+ for k, v in slots.items():
+ if k in changed:
+ print("\x1b[32m", end="")
+ print(f"{v:>8}\x1b[m", end=" ")
+ print()
+ cnt += 1
+ time.sleep(1)
+
+def handle_sigint(signal=None, stack=None):
+ global doshell
+ doshell = True
+
+signal.signal(signal.SIGINT, handle_sigint)
+
+doshell = False
+try:
+ cnt = 0
+ while True:
+ poll()
+ if doshell:
+ run_shell(globals(), msg="Interrupted")
+ doshell = False
+finally:
+ smc.stop()
+
diff --git a/tools/proxyclient/experiments/speaker_amp.py b/tools/proxyclient/experiments/speaker_amp.py
new file mode 100755
index 0000000..7a592c3
--- /dev/null
+++ b/tools/proxyclient/experiments/speaker_amp.py
@@ -0,0 +1,148 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+import time
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+# speaker_amp.py -- play audio through the embedded speaker on Mac mini
+#
+# sample usage with sox:
+#
+# sox INPUT_FILE -t raw -r 48000 -c 1 -e signed-int -b 32 -L - gain -63 | python3 ./speaker_amp.py
+#
+# (expects mono, 24-bit signed samples padded to 32 bits on the msb side)
+
+import argparse
+from m1n1.setup import *
+from m1n1.hw.dart import DART, DARTRegs
+from m1n1.hw.i2c import I2C
+from m1n1.hw.pmgr import PMGR
+from m1n1.hw.nco import NCO
+from m1n1.hw.admac import *
+from m1n1.hw.mca import *
+
+argparser = argparse.ArgumentParser()
+argparser.add_argument("-f", "--file", "--input", "--samples",
+ type=str, default=None,
+ help='input filename to take samples from ' \
+ '(default: standard input)')
+argparser.add_argument("-b", "--bufsize", type=int, default=1024*32,
+ help='size of buffers to keep submitting to DMA')
+args = argparser.parse_args()
+
+inputf = open(args.file, "rb") if args.file is not None \
+ else sys.stdin.buffer
+
+p.pmgr_adt_clocks_enable("/arm-io/i2c1")
+p.pmgr_adt_clocks_enable("/arm-io/admac-sio")
+p.pmgr_adt_clocks_enable("/arm-io/dart-sio")
+p.pmgr_adt_clocks_enable("/arm-io/mca-switch")
+
+# reset AUDIO_P
+PS_AUDIO_P = PMGR(u).regs[0].PS4[5]
+PS_AUDIO_P.set(DEV_DISABLE=1)
+PS_AUDIO_P.set(RESET=1)
+PS_AUDIO_P.set(RESET=0)
+PS_AUDIO_P.set(DEV_DISABLE=0)
+
+i2c1 = I2C(u, "/arm-io/i2c1")
+
+dart_base, _ = u.adt["/arm-io/dart-sio"].get_reg(0) # stream index 2
+dart = DART(iface, DARTRegs(u, dart_base), util=u)
+dart.initialize()
+
+cl_no = 0
+
+admac = ADMAC(u, "/arm-io/admac-sio", dart, debug=True)
+tx_chan = admac.chans[4*cl_no]
+
+tx_chan.disable()
+tx_chan.reset()
+tx_chan.read_reports() # read stale reports
+tx_chan.buswidth = E_BUSWIDTH.W_32BIT
+tx_chan.framesize = E_FRAME.F_1_WORD
+
+nco = NCO(u, "/arm-io/nco")
+nco[cl_no].set_rate(48000 * 256)
+nco[cl_no].enable()
+
+mca_switch1_base = u.adt["/arm-io/mca-switch"].get_reg(1)[0]
+mca_cl_base = u.adt["/arm-io/mca-switch"].get_reg(0)[0] + 0x4000*cl_no
+cl = MCACluster(u, mca_cl_base)
+
+regs, serdes = cl.regs, cl.txa
+
+regs.SYNCGEN_STATUS.set(RST=1, EN=0)
+regs.SYNCGEN_STATUS.set(RST=0)
+regs.SYNCGEN_MCLK_SEL.val =(1 + cl_no)
+regs.SYNCGEN_HI_PERIOD.val = 0
+regs.SYNCGEN_LO_PERIOD.val = 0xfe # full period minus two
+
+serdes.STATUS.set(EN=0)
+serdes.CONF.set(
+ NSLOTS=0,
+ SLOT_WIDTH=E_SLOT_WIDTH.W_32BIT,
+ BCLK_POL=1,
+ UNK1=1, UNK2=1,
+ IDLE_UNDRIVEN=1,
+ SYNC_SEL=(1 + cl_no)
+)
+serdes.BITDELAY.val = 0
+
+serdes.CHANMASK[0].val = 0xffff_fffe
+serdes.CHANMASK[1].val = 0xffff_fffe
+
+regs.PORT_ENABLES.set(CLOCK1=1, CLOCK2=1, DATA=1)
+regs.PORT_CLK_SEL.set(SEL=(cl_no + 1))
+regs.PORT_DATA_SEL.val = cl_no + 1
+regs.MCLK_STATUS.set(EN=1)
+regs.SYNCGEN_STATUS.set(EN=1)
+
+p.write32(mca_switch1_base + 0x8000*cl_no, 0x102048)
+
+# toggle the GPIO line driving the speaker-amp IC reset
+p.write32(0x23c1002d4, 0x76a02) # invoke reset
+p.write32(0x23c1002d4, 0x76a03) # take out of reset
+
+tx_chan.submit(inputf.read(args.bufsize))
+tx_chan.enable()
+while tx_chan.can_submit():
+ tx_chan.submit(inputf.read(args.bufsize))
+
+serdes.STATUS.set(EN=1)
+
+# by ADT and leaked schematic, i2c1 contains TAS5770L,
+# which is not a public part. but there's e.g. TAS2770
+# with similar registers
+#
+# https://www.ti.com/product/TAS2770
+#
+# if the speaker-amp IC loses clock on the serial sample input,
+# it automatically switches to software shutdown.
+#
+
+i2c1.write_reg(0x31, 0x08, [0x40])
+i2c1.write_reg(0x31, 0x0a, [0x06, 0x00, 0x1a])
+i2c1.write_reg(0x31, 0x1b, [0x01, 0x82, 0x06])
+i2c1.write_reg(0x31, 0x16, [0x50, 0x04])
+i2c1.write_reg(0x31, 0x0d, [0x00])
+#i2c1.write_reg(0x31, 0x03, [0x14])
+
+# amplifier gain, presumably this is the lowest setting
+i2c1.write_reg(0x31, 0x03, [0x0])
+
+# take the IC out of software shutdown
+i2c1.write_reg(0x31, 0x02, [0x0c])
+
+while (buf := inputf.read(args.bufsize)):
+ while not tx_chan.can_submit():
+ tx_chan.poll()
+ tx_chan.submit(buf)
+
+# mute
+i2c1.write_reg(0x31, 0x02, [0x0d])
+
+# software shutdown
+i2c1.write_reg(0x31, 0x02, [0x0e])
+
+tx_chan.disable()
diff --git a/tools/proxyclient/experiments/spi.py b/tools/proxyclient/experiments/spi.py
new file mode 100644
index 0000000..82d9528
--- /dev/null
+++ b/tools/proxyclient/experiments/spi.py
@@ -0,0 +1,182 @@
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+from m1n1 import asm
+from m1n1.shell import run_shell
+from m1n1.gpiola import GPIOLogicAnalyzer
+from m1n1.hw.spi import *
+
+p.smp_start_secondaries()
+
+p.set32(0x28e580208, 1<<31)
+p.clear32(0x28e580208, 1<<31)
+
+spi = u.adt["arm-io/spi3"].get_reg(0)[0]
+regs = SPIRegs(u, spi)
+
+mon.add(spi, 0x10)
+mon.add(spi + 0x30, 0x10)
+mon.add(spi + 0x40, 0x400)
+
+aic = u.adt["arm-io/aic"].get_reg(0)[0]
+mon.add(aic + 0x6800 + (1109 // 32) * 4, 4)
+
+gpio = u.adt["arm-io/gpio0"].get_reg(0)[0]
+
+mon.add(gpio, 0x1c8)
+mon.add(gpio+0x1e0, 0x300)
+
+mon.poll()
+
+m = GPIOLogicAnalyzer(u, "arm-io/gpio0",
+ pins={"miso": 0x34, "mosi": 0x35, "clk": 0x36, "cs": 0x37},
+ #pins={"miso": 0xa, "mosi": 0xb, "clk": 0x20, "cs": 0x21},
+ #pins={"clk": 46, "mosi": 47, "miso": 48, "cs": 49},
+ div=1, on_pin_change=False)
+
+#p.write32(spi + 0x100, 0xffffffff)
+
+regs.CTRL.val = 0xc
+regs.PIN.val = 0x2
+regs.CONFIG.val = 0x20 | (1<<15) | 6
+regs.CONFIG.val = 0x20 | (1<<15) | 4
+regs.CONFIG.val = 0x20 | (1<<15) | 2
+regs.CONFIG.val = 0x20 | (3<<15) | 0
+
+def try_all_bits():
+ for i in range(0, 0x200, 4):
+ v = p.read32(spi + i)
+ for j in range(32):
+ p.write32(spi + i, v ^ (1<<j))
+ print(f"{i:4x}:{v:8x}:{j:2d} FIFO level:", regs.FIFO_LEVEL.reg.LEVEL_TX)
+ mon.poll()
+ p.write32(spi + i, v)
+
+
+m.regs = {
+ "CTRL": (spi + 0x00, R_CTRL),
+ "STATUS": (spi + 0x08, R_STATUS),
+ "RXCNT": (spi + 0x34),
+ "TXCNT": (spi + 0x4c),
+ "FIFO_STAT": (spi + 0x10c, R_FIFO_STAT),
+ "ISTATUS1": (spi + 0x134, R_ISTATUS1),
+ "ISTATUS2": (spi + 0x13c, R_ISTATUS2),
+ "XFSTATUS": (spi + 0x1c0),
+ "SHIFTCONFIG": (spi + 0x150),
+ "PINCONFIG": (spi + 0x154),
+ "PIN": (spi + 0xc),
+ "3c": (spi + 0x3c),
+ "DIVSTATUS": (spi + 0x1e0, R_DIVSTATUS)
+}
+
+m.regs = {}
+
+m.start(300000, bufsize=0x80000)
+
+
+regs.STATUS.val = 0xffffffff
+regs.ISTATUS1.val = 0xffffffff
+regs.ISTATUS2.val = 0xffffffff
+
+regs.CLKDIV.val = 0xfff
+regs.INTER_DLY.val = 0x1000
+
+regs.SHIFTCONFIG.val = 0x20fcf7
+
+regs.PIN.val = 0x2
+print("pinconfig", hex(regs.PINCONFIG.val))
+regs.PINCONFIG.val = 0x100
+#regs.PINCONFIG.val = 0x2-7
+print("pinconfig", hex(regs.PINCONFIG.val))
+print("shiftconfig", hex(regs.SHIFTCONFIG.val))
+
+#regs.PIN.val = 0x0
+#regs.PIN.val = 0x2
+# auto_cs OR pin_cs
+
+#p.write32(spi + 0x150, 0x80c07)
+#p.write32(spi + 0x150, 0x88c07)
+print(hex(p.read32(spi + 0x150)))
+
+#p.write32(spi + 0x160, 0)
+p.write32(spi + 0x160, 0xfff0020)
+p.write32(spi + 0x168, 0xffffb20)
+#p.write32(spi + 0x164, 0x06000210)
+#p.write32(spi + 0x180, 0x02000000)
+#p.write32(spi + 0x18c, 0x500)
+#regs.INTER_DLY2 = 0x20000001
+
+p.write32(spi + 0x200, 0x0010)
+
+p.write32(spi + 0x3c, 0xffffffff)
+
+regs.PINCONFIG.val = 0x002
+regs.PINCONFIG.val = 0x200
+
+
+#p.write32(0x28e0380bc, 0x80100000)
+#p.write32(0x28e0380c4, 0x80100000)
+
+data = b"Asahi Linux"
+
+for i in range(2):
+ for j in data:
+ regs.TXDATA.val = j
+ regs.RXCNT.val = len(data)
+ regs.TXCNT.val = len(data)
+
+ regs.STATUS.val = 0xffffffff
+ regs.ISTATUS1.val = 0xffffffff
+ regs.ISTATUS2.val = 0xffffffff
+
+ regs.PIN.val = 0x0
+ regs.CTRL.val = 0x1
+ #regs.TXDATA.val = 0xff
+ #regs.TXDATA.val = 0xff
+
+ i = 0
+ while regs.TXCNT.val != 0:
+ print(f"{regs.TXCNT.val:#x} {regs.FIFO_STAT.reg} {regs.STATUS.val:#x} {regs.ISTATUS2.val:#x} {p.read32(spi + 0x134):#x}")
+ regs.STATUS.val = 0xffffffff
+ regs.ISTATUS1.val = 0xffffffff
+ regs.ISTATUS2.val = 0xffffffff
+ #regs.CTRL.val = 0x0
+ #time.sleep(0.1)
+ #regs.CTRL.val = 0x1[
+ print(hex(i))
+ #p.write32(spi + i, 0xffffffff)
+ #p.write32(spi + i, 0)
+ i += 4
+ if i > 0x100:
+ break
+ time.sleep(0.001)
+ print(f"{regs.RXCNT.val:#x} {regs.FIFO_STAT.reg} {regs.STATUS.val:#x} {regs.ISTATUS2.val:#x}")
+ regs.STATUS.val = 0xffffffff
+ regs.ISTATUS1.val = 0xffffffff
+ regs.ISTATUS2.val = 0xffffffff
+
+ mon.poll()
+
+ while regs.FIFO_STAT.reg.LEVEL_RX:
+ print("RX", hex(regs.RXDATA.val))
+
+ regs.CTRL.val = 0
+
+m.complete()
+m.show()
+
+def poll(count=1000):
+ lval = None
+ for i in range(count):
+ pins = 0x35, 0x36, 0x37
+ vals = [p.read32(gpio + 4 * pin) & 1 for pin in pins]
+ if vals != lval:
+ print(f"{i:6d}: {vals}")
+ lval = vals
+
+mon.poll()
+
+#run_shell(globals(), msg="Have fun!")
+
+
diff --git a/tools/proxyclient/experiments/sprr_test_permissions.py b/tools/proxyclient/experiments/sprr_test_permissions.py
new file mode 100755
index 0000000..9dff141
--- /dev/null
+++ b/tools/proxyclient/experiments/sprr_test_permissions.py
@@ -0,0 +1,366 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from contextlib import contextmanager
+
+from m1n1.setup import *
+from m1n1.find_regs import *
+from m1n1 import asm
+
+p.smp_start_secondaries()
+
+class ARMPageTable:
+ PAGESIZE = 0x4000
+
+ def __init__(self, memalign, free):
+ self.memalign = memalign
+ self.free = free
+
+ self.l0 = self.memalign(self.PAGESIZE, self.PAGESIZE)
+ self.l1 = [self.memalign(self.PAGESIZE, self.PAGESIZE), self.memalign(
+ self.PAGESIZE, self.PAGESIZE)]
+ self.l2 = {}
+
+ p.write64(self.l0, self.make_table_pte(self.l1[0]))
+ p.write64(self.l0+8, self.make_table_pte(self.l1[1]))
+
+ def make_table_pte(self, addr):
+ # table mapping, access bit set
+ return addr | 0b11 | (1 << 10)
+
+ def map_page(self, vaddr, paddr, access_bits):
+ ap = (access_bits & 0b1100) >> 2
+ pxn = (access_bits & 0b0010) >> 1
+ uxn = (access_bits & 0b0001)
+
+ # block mapping, access bit set
+ pte = paddr | 0b01 | (1 << 10)
+
+ # move access bits in place
+ pte |= ap << 6
+ pte |= pxn << 54
+ pte |= uxn << 53
+
+ l0_idx = (vaddr >> (25+11+11)) & 1
+ l1_idx = (vaddr >> (25+11)) & 0x7ff
+ l2_idx = (vaddr >> 25) & 0x7ff
+
+ tbl = self.l2.get((l0_idx, l1_idx), None)
+ if not tbl:
+ tbl = self.memalign(self.PAGESIZE, self.PAGESIZE)
+ self.l2[(l0_idx, l1_idx)] = tbl
+ p.write64(self.l1[l0_idx] + 8*l1_idx, self.make_table_pte(tbl))
+
+ p.write64(tbl + 8*l2_idx, pte)
+
+ def map(self, vaddr, paddr, sz, access_bits):
+ assert sz % 0x2000000 == 0
+ assert vaddr % 0x2000000 == 0
+ assert paddr % 0x2000000 == 0
+ assert access_bits <= 0b1111
+
+ while sz > 0:
+ self.map_page(vaddr, paddr, access_bits)
+ sz -= 0x2000000
+ vaddr += 0x2000000
+ paddr += 0x2000000
+
+
+def build_and_write_code(heap, code):
+ page = heap.memalign(0x4000, 0x4000)
+ compiled = asm.ARMAsm(code, page).data
+ iface.writemem(page, compiled)
+ p.dc_cvau(page, len(compiled))
+ p.ic_ivau(page, len(compiled))
+ return page
+
+
+def setup_exception_vectors(heap, gxf=False):
+ if gxf:
+ elr = "S3_6_C15_C10_6"
+ eret = ".long 0x201400"
+ indicator = 0xf2
+ else:
+ elr = "ELR_EL1"
+ eret = "eret"
+ indicator = 0xf0
+
+ return build_and_write_code(heap, """
+ .rept 16
+ b 1f
+ .align 7
+ .endr
+
+ 1:
+ // store that we failed
+ mov x10, 0x{indicator:x}
+
+ // move PC two instruction further and clear 0xf0 0000 0000 to
+ // make sure we end up in the r-x mapping either way and don't
+ // repeat the instruction that just faulted
+ // we skip the second instruction since that one is used to
+ // indicate success
+ ldr x11, =0x0fffffffff
+ mrs x12, {elr}
+ add x12, x12, #8
+ and x12, x12, x11
+ msr {elr}, x12
+
+ isb
+ {eret}
+ """.format(eret=eret, elr=elr, indicator=indicator))
+
+
+print("Setting up..")
+pagetable = ARMPageTable(u.memalign, u.free)
+pagetable.map(0x800000000, 0x800000000, 0xc00000000, 0)
+pagetable.map(0xf800000000, 0x800000000, 0xc00000000, 1)
+
+el2_vectors = setup_exception_vectors(u.heap, gxf=False)
+gl2_vectors = setup_exception_vectors(u.heap, gxf=True)
+
+probe_page = build_and_write_code(u.heap, "mov x10, 0x80\nret\nret\nret\n")
+probe_page_vaddr = probe_page | 0xf000000000
+
+code_page = build_and_write_code(u.heap, """
+ #define SPRR_PERM_EL0 S3_6_C15_C1_5
+ #define SPRR_PERM_EL1 S3_6_C15_C1_6
+ #define SPRR_PERM_EL2 S3_6_C15_C1_7
+
+ #define GXF_CONFIG_EL2 s3_6_c15_c1_2
+ #define GXF_ENTER_EL2 s3_6_c15_c8_1
+ #define GXF_ABORT_EL2 s3_6_c15_c8_2
+
+ #define MPIDR_GL2 S3_6_C15_C10_1
+ #define VBAR_GL2 S3_6_C15_C10_2
+ #define SPSR_GL2 S3_6_C15_C10_3
+ #define ELR_GL2 S3_6_C15_C10_6
+ #define FAR_GL2 S3_6_C15_C10_7
+
+ #define genter .long 0x00201420
+ #define gexit .long 0x201400
+
+ // just store everything since i'm too lazy to think about
+ // register assignments
+ str x30, [sp, #-16]!
+ stp x28, x29, [sp, #-16]!
+ stp x26, x27, [sp, #-16]!
+ stp x24, x25, [sp, #-16]!
+ stp x22, x23, [sp, #-16]!
+ stp x20, x21, [sp, #-16]!
+ stp x18, x19, [sp, #-16]!
+ stp x16, x17, [sp, #-16]!
+ stp x14, x15, [sp, #-16]!
+ stp x12, x13, [sp, #-16]!
+ stp x10, x11, [sp, #-16]!
+ stp x8, x9, [sp, #-16]!
+ stp x6, x7, [sp, #-16]!
+ stp x4, x5, [sp, #-16]!
+ stp x2, x3, [sp, #-16]!
+ stp x0, x1, [sp, #-16]!
+
+ mov x20, x0 // store SPRR value for later
+ mov x21, 0 // clear result
+
+ // setup exception vectors
+ ldr x0, =0x{vectors:x}
+ msr VBAR_EL2, x0
+ isb
+
+ // prepare MMU
+ ldr x0, =0x0400ff
+ msr MAIR_EL1, x0
+ ldr x0, =0x27510b510
+ msr TCR_EL1, x0
+ ldr x0, =0x{ttbr:x}
+ msr TTBR0_EL2, x0
+
+ // enable SPPR
+ mov x0, 1
+ msr s3_6_c15_c1_0, x0
+ msr s3_6_c15_c1_3, xzr
+ isb
+
+ // clear all SPPR registers
+ // (note that reads from/writes to EL1 will be redirected to EL2 anyway)
+ ldr x0, =0
+ msr SPRR_PERM_EL0, x0
+ msr SPRR_PERM_EL1, x0
+ msr SPRR_PERM_EL2, x0
+ msr s3_6_c15_c1_3, x0
+
+ // setup SPPR_EL2
+ msr SPRR_PERM_EL2, x20
+ isb
+ dsb ishst
+ tlbi vmalle1is
+ dsb ish
+ isb
+
+
+ msr s3_6_c15_c1_3, xzr
+ isb
+
+ // enable MMU
+ ldr x1, =0x1005
+ mrs x0, SCTLR_EL1
+ mov x3, x0
+ orr x0, x0, x1
+ msr SCTLR_EL1, x0
+ isb
+
+ // configure and enable GXF
+ mov x0, 1
+ msr GXF_CONFIG_EL2, x0
+ isb
+ ldr x0, =gxf_entry
+ msr GXF_ENTER_EL2, x0
+ ldr x0, =gxf_abort
+ msr GXF_ABORT_EL2, x0
+ isb
+
+ // test GXF access
+ genter
+
+ // test execute access
+ ldr x1, =0x{probe_page:x}
+ mov x10, 0
+ blr x1
+ lsl x21, x21, #8
+ orr x21, x21, x10
+
+ // test read access
+ ldr x1, =0x{probe_page:x}
+ mov x10, 0
+ ldr x1, [x1]
+ mov x10, 0x80
+ lsl x21, x21, #8
+ orr x21, x21, x10
+
+ // test write access
+ ldr x1, =0x{probe_page:x}
+ add x1, x1, 0x20
+ mov x10, 0
+ str x1, [x1]
+ mov x10, 0x80
+ lsl x21, x21, #8
+ orr x21, x21, x10
+
+ // disable MMU again
+ dsb ish
+ isb
+ msr SCTLR_EL1, x3
+ isb
+
+ mov x0, 0
+ msr GXF_CONFIG_EL2, x0
+ msr s3_6_c15_c1_0, x0
+
+ mov x0, x21
+
+ // restore everything except for x0
+ add sp, sp, #8
+ ldr x1, [sp], #8
+ ldp x2, x3, [sp], #16
+ ldp x4, x5, [sp], #16
+ ldp x6, x7, [sp], #16
+ ldp x8, x9, [sp], #16
+ ldp x10, x11, [sp], #16
+ ldp x12, x13, [sp], #16
+ ldp x14, x15, [sp], #16
+ ldp x16, x17, [sp], #16
+ ldp x18, x19, [sp], #16
+ ldp x20, x21, [sp], #16
+ ldp x22, x23, [sp], #16
+ ldp x24, x25, [sp], #16
+ ldp x26, x27, [sp], #16
+ ldp x28, x29, [sp], #16
+ ldr x30, [sp], #16
+
+ ret
+
+
+ gxf_entry:
+ // setup GL exception vectors
+ ldr x0, =0x{gxf_vectors:x}
+ msr VBAR_GL2, x0
+ isb
+
+ // we might double fault -> store state here
+ mrs x14, S3_6_C15_C10_3
+ mrs x15, S3_6_C15_C10_4
+ mrs x16, S3_6_C15_C10_5
+ mrs x17, ELR_GL2
+ mrs x18, FAR_GL2
+
+ // test execute access
+ ldr x1, =0x{probe_page:x}
+ mov x10, 0
+ blr x1
+ lsl x21, x21, #8
+ orr x21, x21, x10
+
+ // test read access
+ ldr x1, =0x{probe_page:x}
+ mov x10, 0
+ ldr x1, [x1]
+ mov x10, 0x80
+ lsl x21, x21, #8
+ orr x21, x21, x10
+
+ // test write access
+ ldr x1, =0x{probe_page:x}
+ add x1, x1, #0x20
+ mov x10, 0
+ str x1, [x1]
+ mov x10, 0x80
+ lsl x21, x21, #8
+ orr x21, x21, x10
+
+ // restore state in case we faulted in here
+ msr S3_6_C15_C10_3, x14
+ msr S3_6_C15_C10_4, x15
+ msr S3_6_C15_C10_5, x16
+ msr ELR_GL2, x17
+ msr FAR_GL2, x18
+
+ isb
+ gexit
+
+ gxf_abort:
+ // store that we failed
+ mov x10, 0xf1
+
+ // move PC two instruction further and clear 0xf0 0000 0000 to
+ // make sure we end up in the r-x mapping either way and don't
+ // repeat the instruction that just faulted
+ // we skip the second instruction since that one is used to
+ // indicate success
+ ldr x11, =0x0fffffffff
+ mrs x12, ELR_GL2
+ add x12, x12, #8
+ and x12, x12, x11
+ msr ELR_GL2, x12
+
+ isb
+ gexit
+ """.format(ttbr=pagetable.l0, vectors=el2_vectors, probe_page=probe_page_vaddr, gxf_vectors=gl2_vectors))
+
+print("Running code now...")
+for i in range(0x10):
+ sprr_val = 0x5 | ((i & 0xf) << 4)
+ ret = p.smp_call_sync(1, code_page, sprr_val)
+
+ glret = ret >> 24
+ glx = 'x' if (glret >> 16) & 0xff == 0x80 else '-'
+ glr = 'r' if (glret >> 8) & 0xff == 0x80 else '-'
+ glw = 'w' if glret & 0xff == 0x80 else '-'
+
+ x = 'x' if (ret >> 16) & 0xff == 0x80 else '-'
+ r = 'r' if (ret >> 8) & 0xff == 0x80 else '-'
+ w = 'w' if ret & 0xff == 0x80 else '-'
+
+ print("SPRR: {0:04b} result: {1:x} GL: {2}{3}{4} EL: {5}{6}{7}".format(
+ i, ret, glr, glw, glx, r, w, x))
diff --git a/tools/proxyclient/experiments/timer_test.py b/tools/proxyclient/experiments/timer_test.py
new file mode 100755
index 0000000..b3422c9
--- /dev/null
+++ b/tools/proxyclient/experiments/timer_test.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+
+HV_VTMR_CTL = (3, 5, 15, 1, 3)
+HV_VTMR_CTL_VMASK = (1 << 0)
+HV_VTMR_CTL_PMASK = (1 << 1)
+
+HV_VTMR_LIST = (3, 5, 15, 1, 2)
+
+TGE = (1<<27)
+
+u.msr(CNTHCTL_EL2, 3 << 10) # EL1PTEN | EL1PCTEN
+
+def run_test(ctl, tval):
+ u.inst(0xd5033fdf) # isb
+
+ u.msr(ctl, 0)
+ u.msr(tval, int(freq * 0.8))
+ u.msr(ctl, 1)
+
+ for i in range(6):
+ p.nop()
+ time.sleep(0.2)
+ #u.inst(0xd5033fdf, call=p.el1_call)
+ print(" . (ISR_EL1=%d) CTL=%x VTMR_LIST=%x" % (u.mrs(ISR_EL1), u.mrs(ctl), u.mrs(HV_VTMR_LIST)))
+
+ u.msr(ctl, 0)
+
+def test_hv_timers():
+ u.msr(DAIF, 0x3c0)
+ print("Testing HV timers...")
+ print(" TGE = 1")
+
+ u.msr(HCR_EL2, u.mrs(HCR_EL2) | TGE | (1 << 3) | (1 << 4))
+
+ print(" P:")
+ run_test(CNTP_CTL_EL0, CNTP_TVAL_EL0)
+ print(" V:")
+ run_test(CNTV_CTL_EL0, CNTV_TVAL_EL0)
+
+def test_guest_timers():
+ u.msr(DAIF, 0)
+ print("Testing guest timers...")
+
+ print(" TGE = 1, vGIC mode=0, timers unmasked")
+ u.msr(HCR_EL2, (u.mrs(HCR_EL2) | TGE) | (1 << 3) | (1 << 4))
+ u.msr(HACR_EL2, 0)
+ u.msr(HV_VTMR_CTL, 3)
+
+ print(" P:")
+ #run_test(CNTP_CTL_EL02, CNTP_TVAL_EL02)
+ print(" V:")
+ #run_test(CNTV_CTL_EL02, CNTV_TVAL_EL02)
+
+ print(" TGE = 1, vGIC mode=0, timers masked")
+ u.msr(HV_VTMR_CTL, 0)
+
+ print(" P:")
+ run_test(CNTP_CTL_EL02, CNTP_TVAL_EL02)
+ print(" V:")
+ run_test(CNTV_CTL_EL02, CNTV_TVAL_EL02)
+
+ print(" TGE = 0, vGIC mode=0, timers unmasked")
+ u.msr(HCR_EL2, (u.mrs(HCR_EL2) & ~TGE) | (1 << 3) | (1 << 4))
+ u.msr(HACR_EL2, 0)
+ u.msr(HV_VTMR_CTL, 3)
+
+ print(" P:")
+ run_test(CNTP_CTL_EL02, CNTP_TVAL_EL02)
+ print(" V:")
+ run_test(CNTV_CTL_EL02, CNTV_TVAL_EL02)
+
+ print(" TGE = 0, vGIC mode=0, timers masked")
+ u.msr(HV_VTMR_CTL, 0)
+
+ print(" P:")
+ run_test(CNTP_CTL_EL02, CNTP_TVAL_EL02)
+ print(" V:")
+ run_test(CNTV_CTL_EL02, CNTV_TVAL_EL02)
+
+ print(" TGE = 0, vGIC mode=1, timers unmasked")
+ u.msr(HCR_EL2, (u.mrs(HCR_EL2) & ~TGE) | (1 << 3) | (1 << 4))
+ u.msr(HACR_EL2, 1<<20)
+ u.msr(HV_VTMR_CTL, 3)
+
+ print(" P:")
+ run_test(CNTP_CTL_EL02, CNTP_TVAL_EL02)
+ print(" V:")
+ run_test(CNTV_CTL_EL02, CNTV_TVAL_EL02)
+
+ print(" TGE = 0, vGIC mode=1, timers masked")
+ u.msr(HV_VTMR_CTL, 0)
+
+ print(" P:")
+ run_test(CNTP_CTL_EL02, CNTP_TVAL_EL02)
+ print(" V:")
+ run_test(CNTV_CTL_EL02, CNTV_TVAL_EL02)
+
+ return
+
+freq = u.mrs(CNTFRQ_EL0)
+print("Timer freq: %d" % freq)
+
+test_hv_timers()
+test_guest_timers()
diff --git a/tools/proxyclient/hv/README.md b/tools/proxyclient/hv/README.md
new file mode 100644
index 0000000..c4fce23
--- /dev/null
+++ b/tools/proxyclient/hv/README.md
@@ -0,0 +1,4 @@
+## m1n1 hypervisor scripts
+
+This directory contains scripts that can be executed to configure the hypervisor using the `-m`
+option to `run_guest.py`.
diff --git a/tools/proxyclient/hv/trace_agx.py b/tools/proxyclient/hv/trace_agx.py
new file mode 100644
index 0000000..024130c
--- /dev/null
+++ b/tools/proxyclient/hv/trace_agx.py
@@ -0,0 +1,107 @@
+# SPDX-License-Identifier: MIT
+import datetime
+
+from m1n1.constructutils import show_struct_trace, Ver
+from m1n1.utils import *
+
+Ver.set_version(hv.u)
+
+trace_device("/arm-io/sgx", False)
+trace_device("/arm-io/pmp", False)
+trace_device("/arm-io/gfx-asc", False)
+
+from m1n1.trace.agx import AGXTracer
+AGXTracer = AGXTracer._reloadcls(True)
+
+agx_tracer = AGXTracer(hv, "/arm-io/gfx-asc", verbose=1)
+
+#agx_tracer.encoder_id_filter = lambda i: (i >> 16) == 0xc0de
+agx_tracer.start()
+
+def resume_tracing(ctx):
+ fname = f"{datetime.datetime.now().isoformat()}.log"
+ hv.set_logfile(open(f"gfxlogs/{fname}", "a"))
+ agx_tracer.resume()
+ return True
+
+def pause_tracing(ctx):
+ agx_tracer.pause()
+ hv.set_logfile(None)
+ return True
+
+hv.add_hvcall(100, resume_tracing)
+hv.add_hvcall(101, pause_tracing)
+
+mode = TraceMode.SYNC
+trace_range(irange(agx_tracer.gpu_region, agx_tracer.gpu_region_size), mode=mode, name="gpu_region")
+trace_range(irange(agx_tracer.gfx_shared_region, agx_tracer.gfx_shared_region_size), mode=mode, name="gfx_shared_region")
+
+## Trace the entire mmio range around the GPU
+node = hv.adt["/arm-io/sgx"]
+addr, size = node.get_reg(0)
+hv.trace_range(irange(addr, 0x1000000), TraceMode.SYNC, name="sgx")
+#hv.trace_range(irange(addr, 0x1000000), TraceMode.OFF, name="sgx")
+hv.trace_range(irange(0x204017030, 8), TraceMode.SYNC, name="faultcode")
+
+trace_device("/arm-io/sgx", True)
+trace_device("/arm-io/gfx-asc", False)
+
+def trace_all_gfx_io():
+ # These are all the IO ranges that get mapped into the UAT iommu pagetable
+ # Trace them so we can see if any of them are being written by the CPU
+
+ # page (8): fa010020000 ... fa010023fff -> 000000020e100000 [8000020e100447]
+ hv.trace_range(irange(0x20e100000, 0x4000), mode=TraceMode.SYNC)
+
+ # page (10): fa010028000 ... fa01002bfff -> 000000028e104000 [c000028e104447]
+ hv.trace_range(irange(0x20e100000, 0x4000), mode=TraceMode.SYNC)
+
+ # page (22): fa010058000 ... fa01005bfff -> 000000028e494000 [8000028e494447]
+ hv.trace_range(irange(0x28e494000, 0x4000), mode=TraceMode.SYNC)
+
+ # page (28): fa010070000 ... fa010073fff -> 0000000204d60000 [c0000204d60447]
+ hv.trace_range(irange(0x204d60000, 0x4000), mode=TraceMode.SYNC)
+
+ # page (30): fa010078000 ... fa01007bfff -> 0000000200000000 [c0000200000447]
+ # to
+ # page (83): fa01014c000 ... fa01014ffff -> 00000002000d4000 [c00002000d4447]
+ hv.trace_range(irange(0x200000000, 0xd5000), mode=TraceMode.SYNC)
+
+ # page (84): fa010150000 ... fa010153fff -> 0000000201000000 [c0000201000447]
+ #page (137): fa010224000 ... fa010227fff -> 00000002010d4000 [c00002010d4447]
+ hv.trace_range(irange(0x201000000, 0xd5000), mode=TraceMode.SYNC)
+
+ # page (138): fa010228000 ... fa01022bfff -> 0000000202000000 [c0000202000447]
+ # page (191): fa0102fc000 ... fa0102fffff -> 00000002020d4000 [c00002020d4447]
+ hv.trace_range(irange(0x202000000, 0xd5000), mode=TraceMode.SYNC)
+
+ # page (192): fa010300000 ... fa010303fff -> 0000000203000000 [c0000203000447]
+ hv.trace_range(irange(0x203000000, 0xd5000), mode=TraceMode.SYNC)
+ hv.trace_range(irange(0x204000000, 0xd5000), mode=TraceMode.SYNC)
+ hv.trace_range(irange(0x205000000, 0xd5000), mode=TraceMode.SYNC)
+ hv.trace_range(irange(0x206000000, 0xd5000), mode=TraceMode.SYNC)
+ hv.trace_range(irange(0x207000000, 0xd5000), mode=TraceMode.SYNC)
+
+ # page (464): fa010740000 ... fa010743fff -> 00000002643c4000 [c00002643c4447]
+ hv.trace_range(irange(0x2643c4000, 0x4000), mode=TraceMode.SYNC)
+ # page (466): fa010748000 ... fa01074bfff -> 000000028e3d0000 [c000028e3d0447]
+ hv.trace_range(irange(0x28e3d0000, 0x4000), mode=TraceMode.SYNC)
+ # page (468): fa010750000 ... fa010753fff -> 000000028e3c0000 [8000028e3c0447]
+ hv.trace_range(irange(0x28e3c0000, 0x4000), mode=TraceMode.SYNC)
+
+ # page (8): f9100020000 ... f9100023fff -> 0000000406000000 [60000406000447]
+ # page (263): f910041c000 ... f910041ffff -> 00000004063fc000 [600004063fc447]
+ hv.trace_range(irange(0x2643c4000, 0x63fc000), mode=TraceMode.SYNC)
+
+def trace_gpu_irqs():
+ # Trace sgx interrupts
+ node = hv.adt["/arm-io/sgx"]
+ for irq in getattr(node, "interrupts"):
+ hv.trace_irq(f"{node.name} {irq}", irq, 1, hv.IRQTRACE_IRQ)
+
+ ## Trace gfx-asc interrupts
+ #node = hv.adt["/arm-io/gfx-asc"]
+ #for irq in getattr(node, "interrupts"):
+ #hv.trace_irq(f"{node.name} {irq}", irq, 1, hv.IRQTRACE_IRQ)
+
+trace_gpu_irqs()
diff --git a/tools/proxyclient/hv/trace_agx_defer.py b/tools/proxyclient/hv/trace_agx_defer.py
new file mode 100644
index 0000000..257fdc5
--- /dev/null
+++ b/tools/proxyclient/hv/trace_agx_defer.py
@@ -0,0 +1,35 @@
+# SPDX-License-Identifier: MIT
+import datetime
+
+from m1n1.constructutils import show_struct_trace, Ver
+from m1n1.utils import *
+
+Ver.set_version(hv.u)
+
+from m1n1.trace.agx import AGXTracer
+AGXTracer = AGXTracer._reloadcls(True)
+
+agx_tracer = AGXTracer(hv, "/arm-io/gfx-asc", verbose=1)
+
+agx_tracer.pause_after_init = True
+agx_tracer.trace_usermap = False
+agx_tracer.trace_kernmap = False
+agx_tracer.redump = True
+
+agx_tracer.start()
+
+def resume_tracing(ctx):
+ fname = f"{datetime.datetime.now().isoformat()}.log"
+ hv.set_logfile(open(f"gfxlogs/{fname}", "a"))
+ agx_tracer.start()
+ agx_tracer.resume()
+ return True
+
+def pause_tracing(ctx):
+ agx_tracer.pause()
+ agx_tracer.stop()
+ hv.set_logfile(None)
+ return True
+
+hv.add_hvcall(100, resume_tracing)
+hv.add_hvcall(101, pause_tracing)
diff --git a/tools/proxyclient/hv/trace_agx_pwr.py b/tools/proxyclient/hv/trace_agx_pwr.py
new file mode 100644
index 0000000..dd5acf4
--- /dev/null
+++ b/tools/proxyclient/hv/trace_agx_pwr.py
@@ -0,0 +1,158 @@
+# SPDX-License-Identifier: MIT
+import datetime
+
+from m1n1.constructutils import show_struct_trace, Ver
+from m1n1.utils import *
+
+Ver.set_version(hv.u)
+
+trace_device("/arm-io/sgx", True)
+#trace_device("/arm-io/pmp", True)
+#trace_device("/arm-io/gfx-asc", False)
+
+from m1n1.trace.agx import AGXTracer
+AGXTracer = AGXTracer._reloadcls(True)
+
+agx_tracer = AGXTracer(hv, "/arm-io/gfx-asc", verbose=1)
+agx_tracer.trace_kernmap = False
+agx_tracer.trace_kernva = False
+agx_tracer.trace_usermap = False
+
+sgx = hv.adt["/arm-io/sgx"]
+
+freqs = []
+voltages = []
+
+for j in range(8):
+ for i, v in enumerate(voltages):
+ if j != 0:
+ v = 1
+ sgx.perf_states[i+j*len(voltages)].freq = freqs[i] * 1000000
+ sgx.perf_states[i+j*len(voltages)].volt = v
+ sgx.perf_states_sram[i+j*len(voltages)].freq = freqs[i] * 1000000
+ sgx.perf_states_sram[i+j*len(voltages)].volt = 1
+ if j >= 1:
+ getattr(sgx, f"perf_states{j}")[i].freq = freqs[i] * 1000000
+ getattr(sgx, f"perf_states{j}")[i].volt = v
+ getattr(sgx, f"perf_states_sram{j}")[i].freq = freqs[i] * 1000000
+ getattr(sgx, f"perf_states_sram{j}")[i].volt = 1
+
+def after_init():
+ plat = hv.adt.compatible[0].lower()
+ fname = f"initdata/{datetime.datetime.now().isoformat()}-{plat}.log"
+ idlog = open(fname, "w")
+ print(f"Platform: {plat}", file=idlog)
+ fw = hv.adt["/chosen"].firmware_version.split(b"\0")[0].decode("ascii")
+ print(f"Firmware: {fw}", file=idlog)
+ sfw = hv.adt["/chosen"].system_firmware_version
+ print(f"System firmware: {sfw}", file=idlog)
+ print(file=idlog)
+
+ print("ADT SGX:", file=idlog)
+ print(sgx, file=idlog)
+ open("adt_hv.txt","w").write(str(hv.adt))
+
+ print("InitData:", file=idlog)
+ print(agx_tracer.state.initdata, file=idlog)
+
+ power = [int(i) for i in agx_tracer.state.initdata.regionB.hwdata_b.rel_max_powers]
+ volt = [int(i[0]) for i in agx_tracer.state.initdata.regionB.hwdata_b.voltages]
+ freq = [int(i) for i in agx_tracer.state.initdata.regionB.hwdata_b.frequencies]
+
+ print("p/v", [p/max(1, v) for p,v in zip(power,volt)])
+ print("p/f", [p/max(1, f) for p,f in zip(power,freq)])
+ print("p/v2", [p/max(1, (v*v)) for p,v in zip(power,volt)])
+ hv.reboot()
+
+agx_tracer.after_init_hook = after_init
+
+#agx_tracer.encoder_id_filter = lambda i: (i >> 16) == 0xc0de
+agx_tracer.start()
+
+def resume_tracing(ctx):
+ fname = f"{datetime.datetime.now().isoformat()}.log"
+ hv.set_logfile(open(f"gfxlogs/{fname}", "a"))
+ agx_tracer.resume()
+ return True
+
+def pause_tracing(ctx):
+ agx_tracer.pause()
+ hv.set_logfile(None)
+ return True
+
+hv.add_hvcall(100, resume_tracing)
+hv.add_hvcall(101, pause_tracing)
+
+mode = TraceMode.SYNC
+trace_range(irange(agx_tracer.gpu_region, agx_tracer.gpu_region_size), mode=mode, name="gpu_region")
+trace_range(irange(agx_tracer.gfx_shared_region, agx_tracer.gfx_shared_region_size), mode=mode, name="gfx_shared_region")
+
+## Trace the entire mmio range around the GPU
+node = hv.adt["/arm-io/sgx"]
+addr, size = node.get_reg(0)
+hv.trace_range(irange(addr, 0x1000000), TraceMode.SYNC, name="sgx")
+#hv.trace_range(irange(addr, 0x1000000), TraceMode.OFF, name="sgx")
+hv.trace_range(irange(0x204017030, 8), TraceMode.SYNC, name="faultcode")
+
+trace_device("/arm-io/sgx", True)
+trace_device("/arm-io/gfx-asc", False)
+
+def trace_all_gfx_io():
+ # These are all the IO ranges that get mapped into the UAT iommu pagetable
+ # Trace them so we can see if any of them are being written by the CPU
+
+ # page (8): fa010020000 ... fa010023fff -> 000000020e100000 [8000020e100447]
+ hv.trace_range(irange(0x20e100000, 0x4000), mode=TraceMode.SYNC)
+
+ # page (10): fa010028000 ... fa01002bfff -> 000000028e104000 [c000028e104447]
+ hv.trace_range(irange(0x20e100000, 0x4000), mode=TraceMode.SYNC)
+
+ # page (22): fa010058000 ... fa01005bfff -> 000000028e494000 [8000028e494447]
+ hv.trace_range(irange(0x28e494000, 0x4000), mode=TraceMode.SYNC)
+
+ # page (28): fa010070000 ... fa010073fff -> 0000000204d60000 [c0000204d60447]
+ hv.trace_range(irange(0x204d60000, 0x4000), mode=TraceMode.SYNC)
+
+ # page (30): fa010078000 ... fa01007bfff -> 0000000200000000 [c0000200000447]
+ # to
+ # page (83): fa01014c000 ... fa01014ffff -> 00000002000d4000 [c00002000d4447]
+ hv.trace_range(irange(0x200000000, 0xd5000), mode=TraceMode.SYNC)
+
+ # page (84): fa010150000 ... fa010153fff -> 0000000201000000 [c0000201000447]
+ #page (137): fa010224000 ... fa010227fff -> 00000002010d4000 [c00002010d4447]
+ hv.trace_range(irange(0x201000000, 0xd5000), mode=TraceMode.SYNC)
+
+ # page (138): fa010228000 ... fa01022bfff -> 0000000202000000 [c0000202000447]
+ # page (191): fa0102fc000 ... fa0102fffff -> 00000002020d4000 [c00002020d4447]
+ hv.trace_range(irange(0x202000000, 0xd5000), mode=TraceMode.SYNC)
+
+ # page (192): fa010300000 ... fa010303fff -> 0000000203000000 [c0000203000447]
+ hv.trace_range(irange(0x203000000, 0xd5000), mode=TraceMode.SYNC)
+ hv.trace_range(irange(0x204000000, 0xd5000), mode=TraceMode.SYNC)
+ hv.trace_range(irange(0x205000000, 0xd5000), mode=TraceMode.SYNC)
+ hv.trace_range(irange(0x206000000, 0xd5000), mode=TraceMode.SYNC)
+ hv.trace_range(irange(0x207000000, 0xd5000), mode=TraceMode.SYNC)
+
+ # page (464): fa010740000 ... fa010743fff -> 00000002643c4000 [c00002643c4447]
+ hv.trace_range(irange(0x2643c4000, 0x4000), mode=TraceMode.SYNC)
+ # page (466): fa010748000 ... fa01074bfff -> 000000028e3d0000 [c000028e3d0447]
+ hv.trace_range(irange(0x28e3d0000, 0x4000), mode=TraceMode.SYNC)
+ # page (468): fa010750000 ... fa010753fff -> 000000028e3c0000 [8000028e3c0447]
+ hv.trace_range(irange(0x28e3c0000, 0x4000), mode=TraceMode.SYNC)
+
+ # page (8): f9100020000 ... f9100023fff -> 0000000406000000 [60000406000447]
+ # page (263): f910041c000 ... f910041ffff -> 00000004063fc000 [600004063fc447]
+ hv.trace_range(irange(0x2643c4000, 0x63fc000), mode=TraceMode.SYNC)
+
+def trace_gpu_irqs():
+ # Trace sgx interrupts
+ node = hv.adt["/arm-io/sgx"]
+ for irq in getattr(node, "interrupts"):
+ hv.trace_irq(f"{node.name} {irq}", irq, 1, hv.IRQTRACE_IRQ)
+
+ ## Trace gfx-asc interrupts
+ #node = hv.adt["/arm-io/gfx-asc"]
+ #for irq in getattr(node, "interrupts"):
+ #hv.trace_irq(f"{node.name} {irq}", irq, 1, hv.IRQTRACE_IRQ)
+
+trace_gpu_irqs()
diff --git a/tools/proxyclient/hv/trace_all.py b/tools/proxyclient/hv/trace_all.py
new file mode 100644
index 0000000..392dd21
--- /dev/null
+++ b/tools/proxyclient/hv/trace_all.py
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: MIT
+
+from m1n1.utils import irange
+
+# Map the entire MMIO range as traceable
+for r in hv.adt["/arm-io"].ranges:
+ trace_range(irange(r.parent_addr, r.size), mode=TraceMode.ASYNC)
+
+# Skip some noisy devices
+try:
+ trace_device("/arm-io/usb-drd0", False)
+except KeyError:
+ pass
+try:
+ trace_device("/arm-io/usb-drd1", False)
+except KeyError:
+ pass
+try:
+ trace_device("/arm-io/uart2", False)
+except KeyError:
+ pass
+trace_device("/arm-io/error-handler", False)
+trace_device("/arm-io/aic", False)
+trace_device("/arm-io/spi1", False)
+trace_device("/arm-io/pmgr", False)
diff --git a/tools/proxyclient/hv/trace_all_more.py b/tools/proxyclient/hv/trace_all_more.py
new file mode 100644
index 0000000..7296075
--- /dev/null
+++ b/tools/proxyclient/hv/trace_all_more.py
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: MIT
+
+from m1n1.utils import irange
+
+# Map the entire MMIO range as traceable
+for r in hv.adt["/arm-io"].ranges:
+ trace_range(irange(r.parent_addr, r.size), mode=TraceMode.ASYNC)
+
+# Skip some noisy devices
+try:
+ trace_device("/arm-io/usb-drd0", False)
+except KeyError:
+ pass
+try:
+ trace_device("/arm-io/usb-drd1", False)
+except KeyError:
+ pass
+try:
+ trace_device("/arm-io/uart2", False)
+except KeyError:
+ pass
+trace_device("/arm-io/aic", False)
+trace_device("/arm-io/spi1", False)
diff --git a/tools/proxyclient/hv/trace_aop.py b/tools/proxyclient/hv/trace_aop.py
new file mode 100644
index 0000000..489c9b3
--- /dev/null
+++ b/tools/proxyclient/hv/trace_aop.py
@@ -0,0 +1,349 @@
+# SPDX-License-Identifier: MIT
+
+from m1n1.trace import Tracer
+from m1n1.trace.dart import DARTTracer
+from m1n1.trace.asc import ASCTracer, EP, EPState, msg, msg_log, DIR, EPContainer
+from m1n1.utils import *
+from m1n1.constructutils import *
+from m1n1.fw.afk.rbep import *
+from m1n1.fw.afk.epic import *
+from m1n1.fw.aop import *
+from m1n1.fw.aop.ipc import *
+
+import sys
+
+class AFKRingBufSniffer(AFKRingBuf):
+ def __init__(self, ep, state, base, size):
+ super().__init__(ep, base, size)
+ self.state = state
+ self.rptr = getattr(state, "rptr", 0)
+
+ def update_rptr(self, rptr):
+ self.state.rptr = rptr
+
+ def update_wptr(self):
+ raise NotImplementedError()
+
+ def get_wptr(self):
+ return struct.unpack("<I", self.read_buf(2 * self.BLOCK_SIZE, 4))[0]
+
+ def read_buf(self, off, size):
+ return self.ep.dart.ioread(0, self.base + off, size)
+
+class AFKEp(EP):
+ BASE_MESSAGE = AFKEPMessage
+
+ def __init__(self, tracer, epid):
+ super().__init__(tracer, epid)
+ self.txbuf = None
+ self.rxbuf = None
+ self.state.txbuf = EPState()
+ self.state.rxbuf = EPState()
+ self.state.shmem_iova = None
+ self.state.txbuf_info = None
+ self.state.rxbuf_info = None
+ self.state.verbose = 1
+
+ def start(self):
+ self.create_bufs()
+
+ def create_bufs(self):
+ if not self.state.shmem_iova:
+ return
+ if not self.txbuf and self.state.txbuf_info:
+ off, size = self.state.txbuf_info
+ self.txbuf = AFKRingBufSniffer(self, self.state.txbuf,
+ self.state.shmem_iova + off, size)
+ if not self.rxbuf and self.state.rxbuf_info:
+ off, size = self.state.rxbuf_info
+ self.rxbuf = AFKRingBufSniffer(self, self.state.rxbuf,
+ self.state.shmem_iova + off, size)
+
+ Init = msg_log(0x80, DIR.TX)
+ Init_Ack = msg_log(0xa0, DIR.RX)
+
+ GetBuf = msg_log(0x89, DIR.RX)
+
+ Shutdown = msg_log(0xc0, DIR.TX)
+ Shutdown_Ack = msg_log(0xc1, DIR.RX)
+
+ @msg(0xa1, DIR.TX, AFKEP_GetBuf_Ack)
+ def GetBuf_Ack(self, msg):
+ self.state.shmem_iova = msg.DVA
+ self.txbuf = None
+ self.rxbuf = None
+ self.state.txbuf = EPState()
+ self.state.rxbuf = EPState()
+ self.state.txbuf_info = None
+ self.state.rxbuf_info = None
+
+ @msg(0xa2, DIR.TX, AFKEP_Send)
+ def Send(self, msg):
+ for data in self.txbuf.read():
+ #if self.state.verbose >= 3:
+ if True:
+ self.log(f"===TX DATA=== epid={self.epid} rptr={self.txbuf.state.rptr:#x}")
+ chexdump(data)
+ self.log(f"===END DATA===")
+ self.log("Backtrace on TX data:")
+ self.hv.bt()
+ self.handle_ipc(data, dir=">")
+ return True
+
+ Hello = msg_log(0xa3, DIR.TX)
+
+ @msg(0x85, DIR.RX, AFKEPMessage)
+ def Recv(self, msg):
+ for data in self.rxbuf.read():
+ #if self.state.verbose >= 3:
+ if True:
+ self.log(f"===RX DATA=== epid={self.epid} rptr={self.rxbuf.state.rptr:#x}")
+ chexdump(data)
+ self.log(f"===END DATA===")
+ self.handle_ipc(data, dir="<")
+ return True
+
+ def handle_ipc(self, data, dir=None):
+ pass
+
+ @msg(0x8a, DIR.RX, AFKEP_InitRB)
+ def InitTX(self, msg):
+ off = msg.OFFSET * AFKRingBuf.BLOCK_SIZE
+ size = msg.SIZE * AFKRingBuf.BLOCK_SIZE
+ self.state.txbuf_info = (off, size)
+ self.create_bufs()
+
+ @msg(0x8b, DIR.RX, AFKEP_InitRB)
+ def InitRX(self, msg):
+ off = msg.OFFSET * AFKRingBuf.BLOCK_SIZE
+ size = msg.SIZE * AFKRingBuf.BLOCK_SIZE
+ self.state.rxbuf_info = (off, size)
+ self.create_bufs()
+
+class DummyAFKEp(AFKEp):
+ def handle_ipc(self, data, dir=None):
+ pass
+
+class EPICEp(AFKEp):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.pending_call = None
+ self.pending_cmd = None
+
+ def handle_hello(self, hdr, sub, fd):
+ if sub.type != 0xc0:
+ return False
+
+ payload = fd.read()
+ name = payload.split(b"\0")[0].decode("ascii")
+ self.log(f"Hello! (endpoint {name})")
+ return True
+
+ def handle_notify(self, hdr, sub, fd):
+ for calltype in CALLTYPES:
+ if calltype.matches(hdr, sub):
+ call = calltype.from_stream(fd)
+ self.trace_call_early(call)
+ self.pending_call = call
+ return True
+
+ return False
+
+ def handle_reply(self, hdr, sub, fd):
+ if self.pending_call is None:
+ return False
+
+ call = self.pending_call
+ call.read_resp(fd)
+ self.trace_call(call)
+ self.pending_call = None
+ return True
+
+ def dispatch_ipc(self, dir, hdr, sub, fd):
+ if sub.category == EPICCategory.COMMAND:
+ return self.handle_notify(hdr, sub, fd)
+ if dir == "<" and sub.category == EPICCategory.REPORT:
+ return self.handle_hello(hdr, sub, fd)
+ if dir == ">" and sub.category == EPICCategory.NOTIFY:
+ return self.handle_notify(hdr, sub, fd)
+ if dir == "<" and sub.category == EPICCategory.REPLY:
+ return self.handle_reply(hdr, sub, fd)
+
+ def handle_ipc(self, data, dir=None):
+ fd = BytesIO(data)
+ hdr = EPICHeader.parse_stream(fd)
+ sub = EPICSubHeaderVer2.parse_stream(fd)
+
+ if not getattr(self, 'VERBOSE', False):
+ return
+
+ self.log(f"{dir} 0x{hdr.channel:x} Type {hdr.type} Ver {hdr.version} Tag {hdr.seq}")
+ self.log(f" Len {sub.length} Ver {sub.version} Cat {sub.category} Type {sub.type:#x} Ts {sub.timestamp:#x}")
+ self.log(f" Unk1 {sub.unk1:#x} Unk2 {sub.unk2:#x}")
+
+ if self.dispatch_ipc(dir, hdr, sub, fd):
+ return
+
+ def trace_call_early(self, call):
+ # called at TX time
+ if isinstance(call, IndirectCall):
+ call.read_txbuf(self)
+
+ def trace_call(self, call):
+ if isinstance(call, IndirectCall):
+ call.read_rxbuf(self)
+ call = call.unwrap()
+ call.dump(self.log)
+
+class SPUAppEp(EPICEp):
+ SHORT = "SPUApp"
+
+class AccelEp(EPICEp):
+ SHORT = "accel"
+
+class GyroEp(EPICEp):
+ SHORT = "gyro"
+
+class LASEp(EPICEp):
+ SHORT = "las"
+
+class WakeHintEp(EPICEp):
+ SHORT = "wakehint"
+
+class UNK26Ep(EPICEp):
+ SHORT = "unk26"
+
+class AudioEp(EPICEp):
+ SHORT = "aop-audio"
+ VERBOSE = True
+
+class VoiceTriggerEp(EPICEp):
+ SHORT = "aop-voicetrigger"
+ VERBOSE = True
+
+
+class AOPTracer(ASCTracer, AOPBase):
+ ENDPOINTS = {
+ 0x20: SPUAppEp,
+ 0x21: AccelEp,
+ 0x22: GyroEp,
+ 0x24: LASEp,
+ 0x25: WakeHintEp,
+ 0x26: UNK26Ep,
+ 0x27: AudioEp,
+ 0x28: VoiceTriggerEp,
+ }
+
+ def __init__(self, hv, devpath, verbose=False):
+ self.default_bootargs = None
+ super().__init__(hv, devpath, verbose)
+ self.u = hv.u
+ AOPBase.__init__(self, hv.u, self.dev)
+
+ def start(self, *args):
+ self.default_bootargs = self.read_bootargs()
+ super().start(*args)
+
+ def w_CPU_CONTROL(self, val):
+ if val.RUN:
+ self.bootargs = self.read_bootargs()
+ self.log("Bootargs patched by AP:")
+ self.default_bootargs.dump_diff(self.bootargs, self.log)
+ self.log("(End of list)")
+ super().w_CPU_CONTROL(val)
+
+ @classmethod
+ def replay(cls, f, passthru=False):
+ epmap = dict()
+ epcont = EPContainer()
+
+ class FakeASCTracer:
+ def __init__(self):
+ self.hv = None
+
+ def log(self, str):
+ print(str)
+ asc_tracer = FakeASCTracer()
+
+ for cls in cls.mro():
+ eps = getattr(cls, "ENDPOINTS", None)
+ if eps is None:
+ break
+ for k, v in eps.items():
+ if k in epmap:
+ continue
+ ep = v(asc_tracer, k)
+ epmap[k] = ep
+ if getattr(epcont, ep.name, None):
+ ep.name = f"{ep.name}{k:02x}"
+ setattr(epcont, ep.name, ep)
+ ep.start()
+
+ def readdump(firstline, hdr, f):
+ l = firstline
+ assert hdr in l
+ postscribe = l[l.index(hdr) + len(hdr):]
+ annotation = dict([s.split("=") for s \
+ in postscribe.strip().split(" ")])
+
+ dump = []
+ for l in f:
+ if "===END DATA===" in l:
+ break
+ dump.append(l)
+ return chexundump("".join(dump)), annotation
+
+ def read_txbuf(icall, ep):
+ hdr = "===COMMAND TX DATA==="
+ for l in f:
+ if hdr in l:
+ break
+ data, annot = readdump(l, hdr, f)
+ assert int(annot["addr"], 16) == icall.args.txbuf
+ icall.txbuf = data
+ def read_rxbuf(icall, ep):
+ hdr = "===COMMAND RX DATA==="
+ for l in f:
+ if hdr in l:
+ break
+ data, annot = readdump(l, hdr, f)
+ assert int(annot["addr"], 16) == icall.rets.rxbuf
+ icall.rxbuf = data
+ IndirectCall.read_rxbuf = read_rxbuf
+ IndirectCall.read_txbuf = read_txbuf
+
+ for l in f:
+ if (rxhdr := "===RX DATA===") in l:
+ dir = "<"
+ hdr = rxhdr
+ elif (txhdr := "===TX DATA===") in l:
+ dir = ">"
+ hdr = txhdr
+ else:
+ if passthru:
+ print(l, end="")
+ continue
+ data, annot = readdump(l, hdr, f)
+ epid = int(annot["epid"])
+ epmap[epid].handle_ipc(data, dir)
+
+
+if __name__ == "__main__":
+ # We can replay traces by saving the textual output of live tracing
+ # and then passing it to this script.
+ with open(sys.argv[1]) as f:
+ AOPTracer.replay(f)
+ sys.exit(0)
+
+dart_aop_tracer = DARTTracer(hv, "/arm-io/dart-aop", verbose=4)
+dart_aop_tracer.start()
+
+dart_aop_base = u.adt["/arm-io/dart-aop"].get_reg(0)[0]
+
+#hv.trace_range(irange(*u.adt["/arm-io/dart-aop"].get_reg(1)))
+#hv.trace_range(irange(*u.adt["/arm-io/aop"].get_reg(1)))
+#hv.trace_range(irange(*u.adt["/arm-io/aop"].get_reg(3)))
+#hv.trace_range(irange(*u.adt["/arm-io/admac-aop-audio"].get_reg(0)))
+
+aop_tracer = AOPTracer(hv, "/arm-io/aop", verbose=1)
+aop_tracer.start(dart_aop_tracer.dart)
diff --git a/tools/proxyclient/hv/trace_atc.py b/tools/proxyclient/hv/trace_atc.py
new file mode 100644
index 0000000..f301803
--- /dev/null
+++ b/tools/proxyclient/hv/trace_atc.py
@@ -0,0 +1,61 @@
+# SPDX-License-Identifier: MIT
+
+from m1n1.hv import TraceMode
+from m1n1.hw.dwc3 import XhciRegs, Dwc3CoreRegs, PipehandlerRegs
+from m1n1.hw.atc import Usb2PhyRegs, AtcPhyRegs
+from m1n1.trace import ADTDevTracer
+from m1n1.utils import *
+
+
+class PhyTracer(ADTDevTracer):
+ REGMAPS = [
+ Usb2PhyRegs,
+ None,
+ (AtcPhyRegs, 0x20000),
+ (AtcPhyRegs, 0x0),
+ (AtcPhyRegs, 0x2000),
+ (AtcPhyRegs, 0x2200),
+ (AtcPhyRegs, 0x2800),
+ (AtcPhyRegs, 0x2A00),
+ (AtcPhyRegs, 0x7000),
+ (AtcPhyRegs, 0xA00),
+ (AtcPhyRegs, 0x800),
+ (AtcPhyRegs, 0xD000),
+ (AtcPhyRegs, 0x14000),
+ (AtcPhyRegs, 0xC000),
+ (AtcPhyRegs, 0x13000),
+ (AtcPhyRegs, 0xB000),
+ (AtcPhyRegs, 0x12000),
+ (AtcPhyRegs, 0x9000),
+ (AtcPhyRegs, 0x10000),
+ (AtcPhyRegs, 0x1000),
+ (AtcPhyRegs, 0x50000),
+ (AtcPhyRegs, 0x50200),
+ (AtcPhyRegs, 0x54000),
+ None,
+ None,
+ None,
+ (AtcPhyRegs, 0xA000),
+ (AtcPhyRegs, 0x11000),
+ ]
+
+
+class Dwc3VerboseTracer(ADTDevTracer):
+ REGMAPS = [XhciRegs, None, Dwc3CoreRegs, PipehandlerRegs]
+ NAMES = ["xhci", None, "dwc-core", "pipehandler"]
+
+
+class Dwc3Tracer(ADTDevTracer):
+ REGMAPS = [None, None, Dwc3CoreRegs, PipehandlerRegs]
+ NAMES = [None, None, "dwc-core", "pipehandler"]
+
+
+PhyTracer = PhyTracer._reloadcls()
+Dwc3Tracer = Dwc3Tracer._reloadcls()
+Dwc3VerboseTracer = Dwc3VerboseTracer._reloadcls()
+
+phy_tracer = PhyTracer(hv, "/arm-io/atc-phy1", verbose=2)
+dwc3_tracer = Dwc3Tracer(hv, "/arm-io/usb-drd1", verbose=2)
+
+phy_tracer.start()
+dwc3_tracer.start()
diff --git a/tools/proxyclient/hv/trace_cd3217.py b/tools/proxyclient/hv/trace_cd3217.py
new file mode 100644
index 0000000..cea79f3
--- /dev/null
+++ b/tools/proxyclient/hv/trace_cd3217.py
@@ -0,0 +1,119 @@
+# SPDX-License-Identifier: MIT
+
+from enum import Enum
+
+from m1n1.trace.i2c import I2CTracer, I2CDevTracer
+
+class HpmTracer(I2CDevTracer):
+ class State(Enum):
+ UNKNOWN = 0
+ REQUEST = 1
+ WRITE = 2
+ READ = 3
+
+ def __init__(self, addr=128, name=None, verbose=True):
+ print(f"CD3217Tracer.__init__(addr={addr}, name={name}, verbose={verbose})")
+ super().__init__(addr, name, verbose)
+ self.reset()
+
+ def reset(self):
+ self.reg = None
+ self.state = CD3217Tracer.State.UNKNOWN
+ self.length = None
+ self.data = []
+
+ def start(self, addr, read):
+ if addr != self.addr:
+ return
+
+ if self.state == CD3217Tracer.State.UNKNOWN:
+ if read:
+ self.state = CD3217Tracer.State.READ
+ else:
+ self.state = CD3217Tracer.State.REQUEST
+ elif self.state == CD3217Tracer.State.REQUEST and read:
+ pass
+ else:
+ self.log(f"unexpected state in start(read={read}): state:{self.state} reg:{self.reg} data:{self.data}")
+
+ def stop(self):
+ if self.state == CD3217Tracer.State.REQUEST and len(self.data) == 0:
+ return
+
+ msg = f"Txn: {self.addr:02x}."
+ if self.state == CD3217Tracer.State.REQUEST:
+ msg += f"r [{self.reg:02x}]"
+ elif self.state == CD3217Tracer.State.WRITE:
+ msg += f"w [{self.reg:02x}]"
+ elif self.state == CD3217Tracer.State.READ:
+ msg += f"r [xx]"
+ else:
+ self.log(f"unexpected state in stop(): state:{self.state} reg:{self.reg} data:{self.data}")
+ self.reset()
+ return
+
+ # only for debugging as some mismatches are expected as
+ # cd3217 seems to report the register size and not the number
+ # of requested bytes (or I2CDevTracer truncates reads).
+ #if self.length is not None and self.length > len(self.data):
+ # self.log(f"length {self.length:02x} mismatch received data: {len(self.data):02x}")
+
+ for data in self.data:
+ msg += f" {data:02x}"
+
+ self.log(msg)
+ self.reset()
+
+ def read(self, data):
+ self.data.append(data)
+
+ def write(self, data):
+ if self.reg is None:
+ self.reg = data
+ elif self.length is None:
+ self.length = data
+ self.state = CD3217Tracer.State.WRITE
+ else:
+ self.data.append(data)
+
+class CD3217Tracer(HpmTracer):
+ def read(self, data):
+ if self.length is None:
+ self.length = data - 1
+ else:
+ self.data.append(data)
+
+
+i2c_tracers = {}
+
+for node in hv.adt["/arm-io"]:
+ if not node.name.startswith("i2c"):
+ continue
+
+ n = int(node.name[3:])
+ bus = I2CTracer(hv, f"/arm-io/{node.name}")
+
+ for mngr_node in node:
+ if "compatible" not in mngr_node._properties: # thanks Apple
+ continue
+
+ if mngr_node.compatible[0] != "usbc,manager":
+ continue
+
+ addr = mngr_node.reg[0] & 0xff
+ bus.add_device(addr, HpmTracer(addr=addr, name=mngr_node.name))
+
+ for devnode in mngr_node:
+
+ dcls = {
+ "usbc,cd3217": CD3217Tracer,
+ }.get(devnode.compatible[0], None)
+ if dcls:
+ addr = devnode.hpm_iic_addr & 0xff
+ bus.add_device(addr, dcls(addr=addr, name=devnode.name))
+
+ if len(bus.state.devices) > 1:
+ i2c_tracers[n] = bus
+
+for bus in i2c_tracers.values():
+ bus.start()
diff --git a/tools/proxyclient/hv/trace_codecs.py b/tools/proxyclient/hv/trace_codecs.py
new file mode 100644
index 0000000..4cd9730
--- /dev/null
+++ b/tools/proxyclient/hv/trace_codecs.py
@@ -0,0 +1,45 @@
+# SPDX-License-Identifier: MIT
+
+from m1n1.utils import RegMap
+from m1n1.trace.i2c import I2CTracer, I2CRegMapTracer
+from m1n1.hw.codecs import *
+
+hv.p.hv_set_time_stealing(0, 1)
+
+class SN012776Tracer(I2CRegMapTracer):
+ REGMAP = SN012776Regs
+ ADDRESSING = (1, 1)
+
+class TAS5770Tracer(I2CRegMapTracer):
+ REGMAP = TAS5770Regs
+ ADDRESSING = (1, 1)
+
+class CS42L84Tracer(I2CRegMapTracer):
+ REGMAP = CS42L84Regs
+ ADDRESSING = (0, 2)
+
+i2c_tracers = {}
+
+for node in hv.adt["/arm-io"]:
+ if not node.name.startswith("i2c"):
+ continue
+
+ n = int(node.name[3:])
+ i2c_tracers[n] = bus = I2CTracer(hv, f"/arm-io/{node.name}")
+
+ for devnode in node:
+ if "compatible" not in devnode._properties: # thanks Apple
+ continue
+
+ dcls = {
+ "audio-control,tas5770": TAS5770Tracer,
+ "audio-control,sn012776": SN012776Tracer,
+ "audio-control,cs42l84": CS42L84Tracer,
+ }.get(devnode.compatible[0], None)
+ if dcls:
+ bus.add_device(
+ devnode.reg[0] & 0xff,
+ dcls(name=devnode.name)
+ )
+
+ bus.start()
diff --git a/tools/proxyclient/hv/trace_dart.py b/tools/proxyclient/hv/trace_dart.py
new file mode 100644
index 0000000..d983145
--- /dev/null
+++ b/tools/proxyclient/hv/trace_dart.py
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: MIT
+
+from m1n1.trace.dart import DARTTracer
+
+DARTTracer = DARTTracer._reloadcls()
+
+DEVICES = [
+ "/arm-io/dart-pmp",
+ "/arm-io/dart-sep",
+ "/arm-io/dart-sio",
+ "/arm-io/dart-usb1",
+ "/arm-io/dart-disp0",
+ "/arm-io/dart-dcp",
+ "/arm-io/dart-dispext0",
+ "/arm-io/dart-dcpext",
+ "/arm-io/dart-scaler",
+]
+
+dart_tracers = {}
+
+for i in DEVICES:
+ tracer = DARTTracer(hv, i, verbose=3)
+ tracer.start()
+
+ dart_tracers[i.split("-")[-1]] = tracer
+
+del tracer
diff --git a/tools/proxyclient/hv/trace_dcp.py b/tools/proxyclient/hv/trace_dcp.py
new file mode 100644
index 0000000..367a990
--- /dev/null
+++ b/tools/proxyclient/hv/trace_dcp.py
@@ -0,0 +1,1113 @@
+# SPDX-License-Identifier: MIT
+
+import struct
+from io import BytesIO
+
+from enum import IntEnum
+
+from m1n1.proxyutils import RegMonitor
+from m1n1.utils import *
+from m1n1.trace.dart import DARTTracer
+from m1n1.trace.asc import ASCTracer, EP, EPState, msg, msg_log, DIR
+from m1n1.fw.afk.rbep import *
+from m1n1.fw.afk.epic import *
+
+if True:
+ dcp_adt_path = "/arm-io/dcp"
+ dcp_dart_adt_path = "/arm-io/dart-dcp"
+ dcp_dart_mapper_adt_path = "/arm-io/dart-dcp/mapper-dcp"
+ disp0_dart_adt_path = "/arm-io/dart-disp0"
+else:
+ dcp_adt_path = "/arm-io/dcpext"
+ dcp_dart_adt_path = "/arm-io/dart-dcpext"
+ dcp_dart_mapper_adt_path = "/arm-io/dart-dcpext/mapper-dcpext"
+ disp0_dart_adt_path = "/arm-io/dart-dispext0"
+
+trace_device(dcp_adt_path, True, ranges=[1])
+
+DARTTracer = DARTTracer._reloadcls()
+ASCTracer = ASCTracer._reloadcls()
+
+iomon = RegMonitor(hv.u, ascii=True)
+
+class AFKRingBufSniffer(AFKRingBuf):
+ def __init__(self, ep, state, base, size):
+ super().__init__(ep, base, size)
+ self.state = state
+ self.rptr = getattr(state, "rptr", 0)
+
+ def update_rptr(self, rptr):
+ self.state.rptr = rptr
+
+ def update_wptr(self):
+ raise NotImplementedError()
+
+ def get_wptr(self):
+ return struct.unpack("<I", self.read_buf(2 * self.BLOCK_SIZE, 4))[0]
+
+ def read_buf(self, off, size):
+ return self.ep.dart.ioread(self.ep.stream, self.base + off, size)
+
+class AFKEp(EP):
+ BASE_MESSAGE = AFKEPMessage
+
+ def __init__(self, tracer, epid):
+ super().__init__(tracer, epid)
+ self.txbuf = None
+ self.rxbuf = None
+ self.state.txbuf = EPState()
+ self.state.rxbuf = EPState()
+ self.state.shmem_iova = None
+ self.state.txbuf_info = None
+ self.state.rxbuf_info = None
+ self.state.verbose = 1
+
+ def start(self):
+ #self.add_mon()
+ self.create_bufs()
+
+ def create_bufs(self):
+ if not self.state.shmem_iova:
+ return
+ if not self.txbuf and self.state.txbuf_info:
+ off, size = self.state.txbuf_info
+ self.txbuf = AFKRingBufSniffer(self, self.state.txbuf,
+ self.state.shmem_iova + off, size)
+ if not self.rxbuf and self.state.rxbuf_info:
+ off, size = self.state.rxbuf_info
+ self.rxbuf = AFKRingBufSniffer(self, self.state.rxbuf,
+ self.state.shmem_iova + off, size)
+
+ def add_mon(self):
+ if self.state.shmem_iova:
+ iomon.add(self.state.shmem_iova, 32768,
+ name=f"{self.name}.shmem@{self.state.shmem_iova:08x}", offset=0)
+
+ Init = msg_log(0x80, DIR.TX)
+ Init_Ack = msg_log(0xa0, DIR.RX)
+
+ GetBuf = msg_log(0x89, DIR.RX)
+
+ Shutdown = msg_log(0xc0, DIR.TX)
+ Shutdown_Ack = msg_log(0xc1, DIR.RX)
+
+ @msg(0xa1, DIR.TX, AFKEP_GetBuf_Ack)
+ def GetBuf_Ack(self, msg):
+ self.state.shmem_iova = msg.DVA
+ self.txbuf = None
+ self.rxbuf = None
+ self.state.txbuf = EPState()
+ self.state.rxbuf = EPState()
+ self.state.txbuf_info = None
+ self.state.rxbuf_info = None
+ #self.add_mon()
+
+ @msg(0xa2, DIR.TX, AFKEP_Send)
+ def Send(self, msg):
+ for data in self.txbuf.read():
+ if self.state.verbose >= 3:
+ self.log(f">TX rptr={self.txbuf.state.rptr:#x}")
+ chexdump(data, print_fn=self.log)
+ self.handle_ipc(data, dir=">")
+ return True
+
+ Hello = msg_log(0xa3, DIR.TX)
+
+ @msg(0x85, DIR.RX, AFKEPMessage)
+ def Recv(self, msg):
+ for data in self.rxbuf.read():
+ if self.state.verbose >= 3:
+ self.log(f"<RX rptr={self.rxbuf.state.rptr:#x}")
+ chexdump(data, print_fn=self.log)
+ self.handle_ipc(data, dir="<")
+ return True
+
+ def handle_ipc(self, data, dir=None):
+ pass
+
+ @msg(0x8a, DIR.RX, AFKEP_InitRB)
+ def InitTX(self, msg):
+ off = msg.OFFSET * AFKRingBuf.BLOCK_SIZE
+ size = msg.SIZE * AFKRingBuf.BLOCK_SIZE
+ self.state.txbuf_info = (off, size)
+ self.create_bufs()
+
+ @msg(0x8b, DIR.RX, AFKEP_InitRB)
+ def InitRX(self, msg):
+ off = msg.OFFSET * AFKRingBuf.BLOCK_SIZE
+ size = msg.SIZE * AFKRingBuf.BLOCK_SIZE
+ self.state.rxbuf_info = (off, size)
+ self.create_bufs()
+
+class SilentEp(AFKEp):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.state.verbose = 0
+
+ def log(self, msg):
+ pass
+
+def epic_service_cmd(group, cmd):
+ def f(x):
+ x.is_cmd = True
+ x.group = group
+ x.cmd = cmd
+ return x
+ return f
+
+def epic_service_reply(group, cmd):
+ def f(x):
+ x.is_reply = True
+ x.group = group
+ x.cmd = cmd
+ return x
+ return f
+
+class EPICServiceTracer(Reloadable):
+ def __init__(self, tracer, ep, key):
+ self.tracer = tracer
+ self.ep = ep
+ self.key = key
+
+ self.cmdmap = {}
+ self.replymap = {}
+ for name in dir(self):
+ i = getattr(self, name)
+ if not callable(i):
+ continue
+ if getattr(i, "is_cmd", False):
+ self.cmdmap[i.group, i.cmd] = getattr(self, name)
+ if getattr(i, "is_reply", False):
+ self.replymap[i.group, i.cmd] = getattr(self, name)
+
+ def log(self, msg):
+ self.ep.log(f"[{self.key}] {msg}")
+
+ def init(self, props):
+ pass
+
+ def handle_cmd(self, sgroup, scmd, sdata):
+ cmdfn = self.cmdmap.get((sgroup, scmd), None)
+ if cmdfn:
+ cmdfn(sdata)
+ else:
+ self.log(f"> unknown group {sgroup}; command {scmd}")
+ if sdata:
+ chexdump(sdata, print_fn=self.log)
+
+ def handle_reply(self, sgroup, scmd, sdata):
+ replyfn = self.replymap.get((sgroup, scmd), None)
+ if replyfn:
+ replyfn(sdata)
+ else:
+ self.log(f"< unknown group {sgroup}; command {scmd}")
+ if sdata:
+ chexdump(sdata, print_fn=self.log)
+
+ @epic_service_cmd(4, 4)
+ def getLocation(self, data):
+ self.log("> getLocation")
+ @epic_service_reply(4, 4)
+ def getLocation_reply(self, data):
+ self.log("< getLocation")
+
+ @epic_service_cmd(4, 5)
+ def getUnit(self, data):
+ self.log("> getUnit")
+ @epic_service_reply(4, 5)
+ def getUnit_reply(self, data):
+ self.log("< getUnit")
+
+ @epic_service_cmd(4, 6)
+ def open(self, data):
+ self.log("> open")
+ @epic_service_reply(4, 6)
+ def open_reply(self, data):
+ self.log("< open")
+
+ @epic_service_cmd(4, 7)
+ def close(self, data):
+ self.log("> close")
+ @epic_service_reply(4, 7)
+ def close_reply(self, data):
+ self.log("< close")
+
+class EPICEp(AFKEp):
+ SERVICES = []
+
+ def __init__(self, tracer, epid):
+ super().__init__(tracer, epid)
+
+ self.serv_map = {}
+ self.chan_map = {}
+ self.serv_names = {}
+ for i in self.SERVICES:
+ self.serv_names[i.NAME] = i
+
+ def handle_ipc(self, data, dir=None):
+ fd = BytesIO(data)
+ hdr = EPICHeader.parse_stream(fd)
+ sub = EPICSubHeader.parse_stream(fd)
+
+ if sub.category == EPICCategory.REPORT:
+ self.handle_report(hdr, sub, fd)
+ if sub.category == EPICCategory.NOTIFY:
+ self.handle_notify(hdr, sub, fd)
+ elif sub.category == EPICCategory.REPLY:
+ self.handle_reply(hdr, sub, fd)
+ elif sub.category == EPICCategory.COMMAND:
+ self.handle_cmd(hdr, sub, fd)
+ else:
+ self.log(f"{dir}Ch {hdr.channel} Type {hdr.type} Ver {hdr.version} Tag {hdr.seq}")
+ self.log(f" Len {sub.length} Ver {sub.version} Cat {sub.category} Type {sub.type:#x} Seq {sub.seq}")
+ chexdump(data, print_fn=self.log)
+
+ def handle_report_init(self, hdr, sub, fd):
+ init = EPICAnnounce.parse_stream(fd)
+ self.log(f"Init: {init.name}")
+ self.log(f" Props: {init.props}")
+
+ if not init.props:
+ init.props = {}
+
+ name = init.props.get("EPICName", init.name)
+ key = name + str(init.props.get("EPICUnit", ""))
+ self.log(f"New service: {key} on channel {hdr.channel}")
+
+ srv_cls = self.serv_names.get(name, EPICServiceTracer)
+ srv = srv_cls(self.tracer, self, key)
+ srv.init(init.props)
+ srv.chan = hdr.channel
+ self.chan_map[hdr.channel] = srv
+ self.serv_map[key] = srv
+
+ def handle_report(self, hdr, sub, fd):
+ if sub.type == 0x30:
+ self.handle_report_init(hdr, sub, fd)
+ else:
+ self.log(f"Report {sub.type:#x}")
+ chexdump(fd.read(), print_fn=self.log)
+
+ def handle_notify(self, hdr, sub, fd):
+ self.log(f"Notify:")
+ chexdump(fd.read(), print_fn=self.log)
+
+ def handle_reply(self, hdr, sub, fd):
+ if sub.inline_len:
+ payload = fd.read()
+ self.log("Inline payload:")
+ chexdump(payload, print_fn=self.log)
+ else:
+ cmd = EPICCmd.parse_stream(fd)
+ if not cmd.rxbuf:
+ self.log(f"Response {sub.type:#x}: {cmd.retcode:#x}")
+ return
+
+ data = self.dart.ioread(self.stream, cmd.rxbuf, cmd.rxlen)
+ rgroup, rcmd, rlen, rmagic = struct.unpack("<2xHIII", data[:16])
+ if rmagic != 0x69706378:
+ self.log("Warning: Invalid EPICStandardService response magic")
+
+ srv = self.chan_map.get(hdr.channel, None)
+ if srv:
+ srv.handle_reply(rgroup, rcmd, data[64:64+rlen] if rlen else None)
+ else:
+ self.log(f"[???] < group {rgroup} command {rcmd}")
+ chexdump(data[64:64+rlen], print_fn=lambda msg: self.log(f"[???] {msg}"))
+
+ def handle_cmd(self, hdr, sub, fd):
+ cmd = EPICCmd.parse_stream(fd)
+ payload = fd.read()
+
+ if sub.type == 0xc0 and cmd.txbuf:
+ data = self.dart.ioread(self.stream, cmd.txbuf, cmd.txlen)
+ sgroup, scmd, slen, sfooter = struct.unpack("<2xHIII48x", data[:64])
+ sdata = data[64:64+slen] if slen else None
+
+ srv = self.chan_map.get(hdr.channel, None)
+ if srv:
+ srv.handle_cmd(sgroup, scmd, sdata)
+ else:
+ self.log(f"[???] > group {sgroup} command {scmd}")
+ chexdump(data[64:64+slen], print_fn=lambda msg: self.log(f"[???] {msg}"))
+ else:
+ self.log(f"Command {sub.type:#x}: {cmd.retcode:#x}")
+ if payload:
+ chexdump(payload, print_fn=self.log)
+ if cmd.txbuf:
+ self.log(f"TX buf @ {cmd.txbuf:#x} ({cmd.txlen:#x} bytes):")
+ chexdump(self.dart.ioread(self.stream, cmd.txbuf, cmd.txlen), print_fn=self.log)
+
+KNOWN_MSGS = {
+ "A000": "IOMFB::UPPipeAP_H13P::late_init_signal()",
+ "A001": "IOMFB::UPPipeAP_H13P::init_ipa(unsigned long long, unsigned long)",
+ "A002": "IOMFB::UPPipeAP_H13P::alss_supported()",
+ "A003": "IOMFB::UPPipeAP_H13P::reset_dsc()",
+ "A004": "IOMFB::UPPipeAP_H13P::display_edr_factor_changed(float)",
+ "A005": "IOMFB::UPPipeAP_H13P::set_contrast(float)",
+ "A006": "IOMFB::UPPipeAP_H13P::set_csc_mode(IOMFB_CSCMode)",
+ "A007": "IOMFB::UPPipeAP_H13P::set_op_mode(IOMFB_CSCMode)",
+ "A008": "IOMFB::UPPipeAP_H13P::set_op_gamma_mode(IOMFB_TFMode)",
+ "A009": "IOMFB::UPPipeAP_H13P::set_video_out_mode(IOMFB_Output_Mode)",
+ "A010": "IOMFB::UPPipeAP_H13P::set_meta_allowed(bool)",
+ "A011": "IOMFB::UPPipeAP_H13P::set_tunneled_color_mode(bool)",
+ "A012": "IOMFB::UPPipeAP_H13P::set_bwr_line_time_us(double)",
+ "A013": "IOMFB::UPPipeAP_H13P::performance_feedback(double)",
+ "A014": "IOMFB::UPPipeAP_H13P::notify_swap_complete(unsigned int)",
+ "A015": "IOMFB::UPPipeAP_H13P::is_run_mode_change_pending() const",
+ "A016": "IOMFB::UPPipeAP_H13P::ready_for_run_mode_change(IOMFB::AppleRegisterStream*)",
+ "A017": "IOMFB::UPPipeAP_H13P::set_thermal_throttle_cap(unsigned int)",
+ "A018": "IOMFB::UPPipeAP_H13P::emergency_shutdown_normal_mode(IOMFB::AppleRegisterStream*)",
+ "A019": "IOMFB::UPPipeAP_H13P::set_target_run_mode(IOMFB::AppleRegisterStream*)",
+ "A020": "IOMFB::UPPipeAP_H13P::rt_bandwidth_setup()",
+ "A021": "IOMFB::UPPipeAP_H13P::rt_bandwidth_update(IOMFB::AppleRegisterStream*, float, float, bool, bool)",
+ "A022": "IOMFB::UPPipeAP_H13P::rt_bandwidth_update_downgrade(IOMFB::AppleRegisterStream*)",
+ "A023": "IOMFB::UPPipeAP_H13P::rt_bandwidth_write_update(IOMFB::AppleRegisterStream*, RealtimeBandwithWritebackBlock, bool)",
+ "A024": "IOMFB::UPPipeAP_H13P::cif_blending_eco_present()",
+ "A025": "IOMFB::UPPipeAP_H13P::request_bic_update()",
+ "A026": "IOMFB::UPPipeAP_H13P::early_power_off_warning(IOMFB::AppleRegisterStream*)",
+ "A027": "IOMFB::UPPipeAP_H13P::get_max_frame_size(unsigned int*, unsigned int*)",
+ "A028": "IOMFB::UPPipeAP_H13P::shadow_FIFO_empty(IOMFB::AppleRegisterStream*) const",
+ "A029": "IOMFB::UPPipeAP_H13P::setup_video_limits()",
+ "A030": "IOMFB::UPPipeAP_H13P::can_program_swap() const",
+ "A031": "IOMFB::UPPipeAP_H13P::in_auto_mode() const",
+ "A032": "IOMFB::UPPipeAP_H13P::push_black_frame(IOMFB::AppleRegisterStream*)",
+ "A033": "IOMFB::UPPipeAP_H13P::read_crc(Notify_Info_Index, unsigned int)",
+ "A034": "IOMFB::UPPipeAP_H13P::update_notify_clients_dcp(unsigned int const*)",
+ "A035": "IOMFB::UPPipeAP_H13P::is_hilo() const",
+ "A036": "IOMFB::UPPipeAP_H13P::apt_supported() const",
+ "A037": "IOMFB::UPPipeAP_H13P::get_dfb_info(unsigned int*, unsigned long long*, unsigned int*)",
+ "A038": "IOMFB::UPPipeAP_H13P::get_dfb_compression_info(unsigned int*)",
+ "A039": "IOMFB::UPPipeAP_H13P::get_frame_done_time() const",
+ "A040": "IOMFB::UPPipeAP_H13P::get_performance_headroom() const",
+ "A041": "IOMFB::UPPipeAP_H13P::are_stats_active() const",
+ "A042": "IOMFB::UPPipeAP_H13P::supports_odd_h_blanking() const",
+ "A043": "IOMFB::UPPipeAP_H13P::is_first_hw_version() const",
+ "A044": "IOMFB::UPPipeAP_H13P::set_blendout_CSC_mode()",
+
+ "A100": "IOMFB::UPPipe2::get_gamma_table_gated(IOMFBGammaTable*)",
+ "A101": "IOMFB::UPPipe2::set_gamma_table_gated(IOMFBGammaTable const*)",
+ "A102": "IOMFB::UPPipe2::test_control(IOMFB_TC_Cmd, unsigned int)",
+ "A103": "IOMFB::UPPipe2::get_config_frame_size(unsigned int*, unsigned int*) const",
+ "A104": "IOMFB::UPPipe2::set_config_frame_size(unsigned int, unsigned int) const",
+ "A105": "IOMFB::UPPipe2::program_config_frame_size() const",
+ "A106": "IOMFB::UPPipe2::read_blend_crc() const",
+ "A107": "IOMFB::UPPipe2::read_config_crc() const",
+ "A108": "IOMFB::UPPipe2::disable_wpc_calibration(bool)",
+ "A109": "IOMFB::UPPipe2::vftg_is_running(IOMFB::AppleRegisterStream*) const",
+ "A110": "IOMFB::UPPipe2::vftg_debug(IOMFB::AppleRegisterStream*, unsigned int) const",
+ "A111": "IOMFB::UPPipe2::vftg_set_color_channels(unsigned int, unsigned int, unsigned int)",
+ "A112": "IOMFB::UPPipe2::set_color_filter_scale(int)",
+ "A113": "IOMFB::UPPipe2::set_corner_temps(int const*)",
+ "A114": "IOMFB::UPPipe2::reset_aot_enabled() const",
+ "A115": "IOMFB::UPPipe2::aot_enabled() const",
+ "A116": "IOMFB::UPPipe2::aot_active() const",
+ "A117": "IOMFB::UPPipe2::set_timings_enabled(IOMFB::AppleRegisterStream*, bool)",
+ "A118": "IOMFB::UPPipe2::get_frame_size(IOMFB::AppleRegisterStream*, unsigned int*, unsigned int*)",
+ "A119": "IOMFB::UPPipe2::set_block(unsigned long long, unsigned int, unsigned int, unsigned long long const*, unsigned int, unsigned char const*, unsigned long, bool)",
+ "A121": "IOMFB::UPPipe2::get_buf_block(unsigned long long, unsigned int, unsigned int, unsigned long long const*, unsigned int, unsigned char const*, unsigned long, bool)",
+ "A122": "IOMFB::UPPipe2::get_matrix(IOMFB_MatrixLocation, IOMFBColorFixedMatrix*) const",
+ "A123": "IOMFB::UPPipe2::set_matrix(IOMFB_MatrixLocation, IOMFBColorFixedMatrix const*)",
+ "A124": "IOMFB::UPPipe2::get_internal_timing_attributes_gated(IOMFB::RefreshTimingAttributes*) const",
+ "A125": "IOMFB::UPPipe2::display_edr_factor_changed(float)",
+ "A126": "IOMFB::UPPipe2::set_contrast(float)",
+ "A127": "IOMFB::UPPipe2::p3_to_disp_cs(float const*, float const (*) [2])",
+ "A128": "IOMFB::UPPipe2::max_panel_brightness() const",
+ "A129": "IOMFB::UPPipe2::swap_flush_stream_replay(IOMFB::AppleRegisterStream*)",
+ "A130": "IOMFB::UPPipe2::init_ca_pmu()",
+ "A131": "IOMFB::UPPipe2::pmu_service_matched()",
+ "A132": "IOMFB::UPPipe2::backlight_service_matched()",
+
+ "A200": "IOMFB::PropRelay::setBool(IOMFB::RuntimeProperty, bool)",
+ "A201": "IOMFB::PropRelay::setInt(IOMFB::RuntimeProperty, unsigned int)",
+ "A202": "IOMFB::PropRelay::setFx(IOMFB::RuntimeProperty, int)",
+ "A203": "IOMFB::PropRelay::setPropDynamic(IOMFB::RuntimeProperty, unsigned int)",
+ "A204": "IOMFB::PropRelay::getBool(IOMFB::RuntimeProperty)",
+ "A205": "IOMFB::PropRelay::getInt(IOMFB::RuntimeProperty)",
+ "A206": "IOMFB::PropRelay::getFx(IOMFB::RuntimeProperty)",
+
+ "A350": "UnifiedPipeline2::displayHeight()",
+ "A351": "UnifiedPipeline2::displayWidth()",
+ "A352": "UnifiedPipeline2::applyProperty(unsigned int, unsigned int)",
+ "A353": "UnifiedPipeline2::get_system_type() const",
+ "A354": "UnifiedPipeline2::headless() const",
+ "A355": "UnifiedPipeline2::export_idle_method(unsigned int)",
+ "A357": "UnifiedPipeline2::set_create_DFB()",
+ "A358": "UnifiedPipeline2::vi_set_temperature_hint()",
+
+ "A400": "IOMobileFramebufferAP::free_signal()",
+ "A401": "IOMobileFramebufferAP::start_signal()",
+ "A402": "IOMobileFramebufferAP::stop_signal()",
+ "A403": "IOMobileFramebufferAP::systemWillShutdown()",
+ "A404": "IOMobileFramebufferAP::swap_begin()",
+ "A405": "IOMobileFramebufferAP::rotate_surface(unsigned int, unsigned int, unsigned int)",
+ "A406": "IOMobileFramebufferAP::get_framebuffer_id()",
+ "A407": "IOMobileFramebufferAP::swap_start(unsigned int*, IOUserClient*)",
+ "A408": "IOMobileFramebufferAP::swap_submit_dcp(IOMFBSwapRec const*, IOSurface**, unsigned int const*, bool, double, unsigned int, bool*)",
+ "A409": "IOMobileFramebufferAP::swap_signal(unsigned int, unsigned int)",
+ "A410": "IOMobileFramebufferAP::set_display_device(unsigned int)",
+ "A411": "IOMobileFramebufferAP::is_main_display() const",
+ "A412": "IOMobileFramebufferAP::set_digital_out_mode(unsigned int, unsigned int)",
+ "A413": "IOMobileFramebufferAP::get_digital_out_state(unsigned int*)",
+ "A414": "IOMobileFramebufferAP::get_display_area(DisplayArea*)",
+ "A415": "IOMobileFramebufferAP::set_tvout_mode(unsigned int)",
+ "A416": "IOMobileFramebufferAP::set_tvout_signaltype(unsigned int)",
+ "A417": "IOMobileFramebufferAP::set_wss_info(unsigned int, unsigned int)",
+ "A418": "IOMobileFramebufferAP::set_content_flags(unsigned int)",
+ "A419": "IOMobileFramebufferAP::get_gamma_table(IOMFBGammaTable*)",
+ "A420": "IOMobileFramebufferAP::set_gamma_table(IOMFBGammaTable*)",
+ "A421": "IOMobileFramebufferAP::get_matrix(unsigned int, unsigned long long (*) [3][3]) const",
+ "A422": "IOMobileFramebufferAP::set_matrix(unsigned int, unsigned long long const (*) [3][3])",
+ "A423": "IOMobileFramebufferAP::set_contrast(float*)",
+ "A424": "IOMobileFramebufferAP::set_white_on_black_mode(unsigned int)",
+ "A425": "IOMobileFramebufferAP::set_color_remap_mode(DisplayColorRemapMode)",
+ "A426": "IOMobileFramebufferAP::get_color_remap_mode(DisplayColorRemapMode*) const",
+ "A427": "IOMobileFramebufferAP::setBrightnessCorrection(unsigned int)",
+ "A428": "IOMobileFramebufferAP::temp_queue_swap_cancel(unsigned int)",
+ "A429": "IOMobileFramebufferAP::swap_cancel(unsigned int)",
+ "A430": "IOMobileFramebufferAP::swap_cancel_all_dcp(unsigned long long)",
+ "A431": "IOMobileFramebufferAP::surface_is_replaceable(unsigned int, bool*)",
+ "A432": "IOMobileFramebufferAP::kernel_tests(IOMFBKernelTestsArguments*)",
+ "A433": "IOMobileFramebufferAP::splc_set_brightness(unsigned int)",
+ "A434": "IOMobileFramebufferAP::splc_get_brightness(unsigned int*)",
+ "A435": "IOMobileFramebufferAP::set_block_dcp(task*, unsigned int, unsigned int, unsigned long long const*, unsigned int, unsigned char const*, unsigned long)",
+ "A436": "IOMobileFramebufferAP::get_block_dcp(task*, unsigned int, unsigned int, unsigned long long const*, unsigned int, unsigned char*, unsigned long) const",
+ "A438": "IOMobileFramebufferAP::swap_set_color_matrix(IOMFBColorFixedMatrix*, IOMFBColorMatrixFunction, unsigned int)",
+ "A439": "IOMobileFramebufferAP::set_parameter_dcp(IOMFBParameterName, unsigned long long const*, unsigned int)",
+ "A440": "IOMobileFramebufferAP::display_width() const",
+ "A441": "IOMobileFramebufferAP::display_height() const",
+ "A442": "IOMobileFramebufferAP::get_display_size(unsigned int*, unsigned int*) const",
+ "A443": "IOMobileFramebufferAP::do_create_default_frame_buffer() const",
+ "A444": "IOMobileFramebufferAP::printRegs()",
+ "A445": "IOMobileFramebufferAP::enable_disable_dithering(unsigned int)",
+ "A446": "IOMobileFramebufferAP::set_underrun_color(unsigned int)",
+ "A447": "IOMobileFramebufferAP::enable_disable_video_power_savings(unsigned int)",
+ "A448": "IOMobileFramebufferAP::set_video_dac_gain(unsigned int)",
+ "A449": "IOMobileFramebufferAP::set_line21_data(unsigned int)",
+ "A450": "IOMobileFramebufferAP::enableInternalToExternalMirroring(bool)",
+ "A451": "IOMobileFramebufferAP::getExternalMirroringCapability(IOMFBMirroringCapability*)",
+ "A452": "IOMobileFramebufferAP::setRenderingAngle(float*)",
+ "A453": "IOMobileFramebufferAP::setOverscanSafeRegion(IOMFBOverscanSafeRect*)",
+ "A454": "IOMobileFramebufferAP::first_client_open()",
+ "A455": "IOMobileFramebufferAP::last_client_close_dcp(unsigned int*)",
+ "A456": "IOMobileFramebufferAP::writeDebugInfo(unsigned long)",
+ "A457": "IOMobileFramebufferAP::flush_debug_flags(unsigned int)",
+ "A458": "IOMobileFramebufferAP::io_fence_notify(unsigned int, unsigned int, unsigned long long, IOMFBStatus)",
+ "A459": "IOMobileFramebufferAP::swap_wait_dcp(bool, unsigned int, unsigned int, unsigned int)",
+ "A460": "IOMobileFramebufferAP::setDisplayRefreshProperties()",
+ "A461": "IOMobileFramebufferAP::exportProperty(unsigned int, unsigned int)",
+ "A462": "IOMobileFramebufferAP::applyProperty(unsigned int, unsigned int)",
+ "A463": "IOMobileFramebufferAP::flush_supportsPower(bool)",
+ "A464": "IOMobileFramebufferAP::abort_swaps_dcp(IOMobileFramebufferUserClient*)",
+ "A465": "IOMobileFramebufferAP::swap_signal_gated(unsigned int, unsigned int)",
+ "A466": "IOMobileFramebufferAP::update_dfb(IOSurface*, unsigned int, unsigned int, unsigned long long)",
+ "A467": "IOMobileFramebufferAP::update_dfb(IOSurface*)",
+ "A468": "IOMobileFramebufferAP::setPowerState(unsigned long, bool, unsigned int*)",
+ "A469": "IOMobileFramebufferAP::isKeepOnScreen() const",
+ "A470": "IOMobileFramebufferAP::resetStats()",
+ "A471": "IOMobileFramebufferAP::set_has_frame_swap_function(bool)",
+ "A472": "IOMobileFramebufferAP::getPerformanceStats(unsigned int*, unsigned int*)",
+
+ "D000": "bool IOMFB::UPPipeAP_H13P::did_boot_signal()",
+ "D001": "bool IOMFB::UPPipeAP_H13P::did_power_on_signal()",
+ "D002": "void IOMFB::UPPipeAP_H13P::will_power_off_signal()",
+ "D003": "void IOMFB::UPPipeAP_H13P::rt_bandwidth_setup_ap(inout rt_bw_config_t*)",
+ "D004": "void IOMFB::UPPipeAP_H13P::mcc_report_replay(bool, unsigned int)",
+ "D005": "void IOMFB::UPPipeAP_H13P::mcc_report_bics(bool, unsigned int)",
+
+ "D100": "void UnifiedPipeline2::match_pmu_service()",
+ #"D101": "", # get some uint32_t, inlined
+ "D102": "void UnifiedPipeline2::set_number_property(char const*, unsigned int)",
+ "D103": "void UnifiedPipeline2::set_boolean_property(char const*, bool)",
+ "D104": "void UnifiedPipeline2::set_string_property(char const*, char const*)",
+ "D105": "IOReturn IOService::acknowledgeSetPowerState()",
+ "D106": "void IORegistryEntry::removeProperty(char const*)",
+ "D107": "bool UnifiedPipeline2::create_provider_service",
+ "D108": "bool UnifiedPipeline2::create_product_service()",
+ "D109": "bool UnifiedPipeline2::create_PMU_service()",
+ "D110": "bool UnifiedPipeline2::create_iomfb_service()",
+ "D111": "bool UnifiedPipeline2::create_backlight_service()",
+ "D112": "void UnifiedPipeline2::set_idle_caching_state_ap(IdleCachingState, unsigned int)",
+ "D113": "bool UnifiedPipeline2::upload_trace_start(IOMFB::FrameInfoBuffer::FrameInfoConstants const*)",
+ "D114": "bool UnifiedPipeline2::upload_trace_chunk(IOMFB::FrameInfoBuffer::FrameInfoData const*, unsigned int, unsigned int)",
+ "D115": "bool UnifiedPipeline2::upload_trace_end(char const*)",
+ "D116": "bool UnifiedPipeline2::start_hardware_boot()",
+ "D117": "bool UnifiedPipeline2::is_dark_boot()",
+ "D118": "bool UnifiedPipeline2::is_waking_from_hibernate()",
+ "D119": "bool UnifiedPipeline2::detect_fastsim()",
+ "D120": "bool UnifiedPipeline2::read_edt_data(char const*, unsigned int, unsigned int*) const",
+ "D121": "bool UnifiedPipeline2::read_edt_string(char const*, char*, unsigned int*) const",
+ "D122": "bool UnifiedPipeline2::setDCPAVPropStart(unsigned int)",
+ "D123": "bool UnifiedPipeline2::setDCPAVPropChunk(unsigned char const*, unsigned int, unsigned int)",
+ "D124": "bool UnifiedPipeline2::setDCPAVPropEnd(char const*)",
+
+ "D200": "uint64_t IOMFB::UPPipe2::get_default_idle_caching_method()",
+ "D201": "IOMFBStatus IOMFB::UPPipe2::map_buf(IOMFB::BufferDescriptor*, unsigned long*, unsigned long long*, bool)",
+ "D202": "void IOMFB::UPPipe2::unmap_buf(IOMFB::BufferDescriptor*, unsigned long, unsigned long long, bool)",
+ "D203": "bool IOMFB::UPPipe2::aot_supported_peek()",
+ "D204": "uint64_t IOMFB::UPPipe2::get_ideal_screen_space()",
+ "D205": "bool IOMFB::UPPipe2::read_carveout(unsigned char*, unsigned int, unsigned int) const",
+ "D206": "bool IOMFB::UPPipe2::match_pmu_service()",
+ "D207": "bool IOMFB::UPPipe2::match_backlight_service()",
+ "D208": "uint64_ IOMFB::UPPipe2::get_calendar_time_ms()",
+ "D209": "void IOMFB::UPPipe2::plc_enable(bool)",
+ "D210": "void IOMFB::UPPipe2::plc_init()",
+ "D211": "void IOMFB::UPPipe2::update_backlight_factor_prop(int)",
+
+ "D300": "void IOMFB::PropRelay::publish(IOMFB::RuntimeProperty, unsigned int)",
+
+ "D400": "void IOMFB::ServiceRelay::get_property(unsigned int, in char const[0x40], out unsigned char[0x200], inout unsigned int*)",
+ "D401": "bool IOMFB::ServiceRelay::get_uint_prop(unsigned int, in char const[0x40], inout unsigned long long*)",
+ "D402": "void IOMFB::ServiceRelay::set_uint_prop(unsigned int, in char const[0x40], unsigned long long)",
+ "D403": "bool IOMFB::ServiceRelay::get_uint_prop(unsigned int, in char const[0x40], inout unsigned int*)",
+ "D404": "void IOMFB::ServiceRelay::set_uint_prop(unsigned int, in char const[0x40], unsigned int)",
+ "D405": "bool IOMFB::ServiceRelay::get_fx_prop(unsigned int, in char const[0x40], inout int*)",
+ "D406": "void IOMFB::ServiceRelay::set_fx_prop(unsigned int, in char const[0x40], int)",
+ "D407": "void IOMFB::ServiceRelay::set_bool_prop(unsigned int, in char const[0x40], bool)",
+ "D408": "unsigned long long IOMFB::ServiceRelay::getClockFrequency(unsigned int, unsigned int)",
+ "D409": "IOMFBStatus IOMFB::ServiceRelay::enableDeviceClock(unsigned int, unsigned int, unsigned int)",
+ "D410": "IOMFBStatus IOMFB::ServiceRelay::enableDevicePower(unsigned int, unsigned int, inout unsigned int*, unsigned int)",
+ "D411": "IOMFBStatus IOMFB::ServiceRelay::mapDeviceMemoryWithIndex(unsigned int, unsigned int, unsigned int, inout unsigned long*, inout unsigned long long*)",
+ "D412": "bool IOMFB::ServiceRelay::setProperty(unsigned int, OSString<0x40> const*, OSArray const*)",
+ "D413": "bool IOMFB::ServiceRelay::setProperty(unsigned int, OSString<0x40> const*, OSDictionary const*)",
+ "D414": "bool IOMFB::ServiceRelay::setProperty(unsigned int, OSString<0x40> const*, OSNumber const*)",
+ "D415": "bool IOMFB::ServiceRelay::setProperty(unsigned int, OSString<0x40> const*, OSBoolean const*)",
+ "D416": "bool IOMFB::ServiceRelay::setProperty(unsigned int, OSString<0x40> const*, OSString const*)",
+ "D417": "bool IOMFB::ServiceRelay::setProperty(unsigned int, char const[0x40], OSArray const*)",
+ "D418": "bool IOMFB::ServiceRelay::setProperty(unsigned int, char const[0x40], OSDictionary const*)",
+ "D419": "bool IOMFB::ServiceRelay::setProperty(unsigned int, char const[0x40], OSNumber const*)",
+ "D420": "bool IOMFB::ServiceRelay::setProperty(unsigned int, char const[0x40], OSBoolean const*)",
+ "D421": "bool IOMFB::ServiceRelay::setProperty(unsigned int, char const[0x40], OSString const*)",
+ "D422": "bool IOMFB::ServiceRelay::setProperty(unsigned int, char const[0x40], bool)",
+ "D423": "void IOMFB::ServiceRelay::removeProperty(unsigned int, char const[0x40])",
+ "D424": "void IOMFB::ServiceRelay::removeProperty(unsigned int, OSString<0x40> const*)",
+
+ "D450": "bool IOMFB::MemDescRelay::from_id(unsigned int, unsigned long*, unsigned long*, unsigned long long*)",
+ "D451": "MemDescRelay::desc_id_t IOMFB::MemDescRelay::allocate_buffer(unsigned int, unsigned long long, unsigned int, unsigned long*, unsigned long*, unsigned long long*)",
+ "D452": "MemDescRelay::desc_id_t IOMFB::MemDescRelay::map_physical(unsigned long long, unsigned long long, unsigned int, unsigned long*, unsigned long long*)",
+ "D453": "MemDescRelay::desc_id_t IOMFB::MemDescRelay::withAddressRange(unsigned long long, unsigned long long, unsigned int, task*, unsigned long*, unsigned long long*)",
+ "D454": "IOMFBStatus IOMFB::MemDescRelay::prepare(unsigned int, unsigned int)",
+ "D455": "IOMFBStatus IOMFB::MemDescRelay::complete(unsigned int, unsigned int)",
+ "D456": "bool IOMFB::MemDescRelay::release_descriptor(unsigned int)",
+
+ "D500": "IOMFBStatus IOMFB::PlatformFunctionRelay::allocate_record(unsigned int, char const*, unsigned int, bool)",
+ "D501": "IOMFBStatus IOMFB::PlatformFunctionRelay::release_record(unsigned int)",
+ "D502": "IOMFBStatus IOMFB::PlatformFunctionRelay::callFunctionLink(unsigned int, unsigned long, unsigned long, unsigned long)",
+
+ "D550": "bool IORegistryEntry::setProperty(OSString *, OSArray *)",
+ "D551": "bool IORegistryEntry::setProperty(OSString *, IOMFB::AFKArray *)",
+ "D552": "bool IORegistryEntry::setProperty(OSString *, OSDictionary *)",
+ "D553": "bool IORegistryEntry::setProperty(OSString *, IOMFB::AFKDictionary *)",
+ "D554": "bool IORegistryEntry::setProperty(OSString *, OSNumber *)",
+ "D555": "bool IORegistryEntry::setProperty(OSString *, IOMFB::AFKNumber *)",
+ "D556": "bool IORegistryEntry::setProperty(OSString *, OSBoolean *)",
+ "D557": "bool IORegistryEntry::setProperty(OSString *, OSString *)",
+ "D558": "bool IORegistryEntry::setProperty(OSString *, IOMFB::AFKString *)",
+ "D559": "bool IORegistryEntry::setProperty(char const*, OSArray *)",
+ "D560": "bool IORegistryEntry::setProperty(char const*, IOMFB::AFKArray *)",
+ "D561": "bool IORegistryEntry::setProperty(char const*, OSDictionary *)",
+ "D562": "bool IORegistryEntry::setProperty(char const*, IOMFB::AFKDictionary *)",
+ "D563": "bool IORegistryEntry::setProperty(char const*, OSNumber *)",
+ "D564": "bool IORegistryEntry::setProperty(char const*, IOMFB::AFKNumber *)",
+ "D565": "bool IORegistryEntry::setProperty(char const*, OSBoolean *)",
+ "D566": "bool IORegistryEntry::setProperty(char const*, OSString *)",
+ "D567": "bool IORegistryEntry::setProperty(char const*, IOMFB::AFKString *)",
+ "D568": "bool IORegistryEntry::setProperty(char const*, char const*)",
+ "D569": "bool IORegistryEntry::setProperty(char const*, bool)",
+ "D570": "IOMFBStatus IOMobileFramebufferAP::setProperties(OSDictionary*)",
+ "D571": "void IOMobileFramebufferAP::swapping_client_did_start(IOMobileFramebufferUserClient*)",
+ "D572": "void IOMobileFramebufferAP::swapping_client_will_stop(IOMobileFramebufferUserClient*)",
+ "D573": "IOMFBStatus IOMobileFramebufferAP::set_canvas_size(unsigned int, unsigned int)",
+ "D574": "IOMFBStatus IOMobileFramebufferAP::powerUpDART(bool)",
+ "D575": "IOMFBStatus IOMobileFramebufferAP::get_dot_pitch(unsigned int*)",
+ "D576": "void IOMobileFramebufferAP::hotPlug_notify_gated(unsigned long long)",
+ "D577": "void IOMobileFramebufferAP::powerstate_notify(bool, bool)",
+ "D578": "bool IOMobileFramebufferAP::idle_fence_create(IdleCachingState)",
+ "D579": "void IOMobileFramebufferAP::idle_fence_complete()",
+ "D580": "void IOMobileFramebufferAP::idle_surface_release_ap()",
+ "D581": "void IOMobileFramebufferAP::swap_complete_head_of_line(unsigned int, bool, unsigned int, bool)",
+ "D582": "bool IOMobileFramebufferAP::create_default_fb_surface(unsigned int, unsigned int)",
+ "D583": "bool IOMobileFramebufferAP::serializeDebugInfoCb(unsigned long, IOMFB::BufferDescriptor const*, unsigned int)",
+ "D584": "void IOMobileFramebufferAP::clear_default_surface()",
+ "D585": "void IOMobileFramebufferAP::swap_notify_gated(unsigned long long, unsigned long long, unsigned long long)",
+ "D586": "void IOMobileFramebufferAP::swap_info_notify_dispatch(SwapInfoBlob const*)",
+ "D587": "void IOMFBStatus IOMobileFramebufferAP::updateBufferMappingCount_gated(bool)",
+ "D588": "void IOMobileFramebufferAP::resize_default_fb_surface_gated()",
+ "D589": "void IOMobileFramebufferAP::swap_complete_ap_gated(unsigned int, bool, SwapCompleteData const*, SwapInfoBlob const*, unsigned int)",
+ "D590": "void IOMobileFramebufferAP::batched_swap_complete_ap_gated(unsigned int*, unsigned int, bool, bool const*, SwapCompleteData const*)",
+ "D591": "void IOMobileFramebufferAP::swap_complete_intent_gated(unsigned int, bool, IOMFBSwapRequestIntent, unsigned int, unsigned int)",
+ "D592": "void IOMobileFramebufferAP::abort_swap_ap_gated(unsigned int)",
+ "D593": "void IOMobileFramebufferAP::enable_backlight_message_ap_gated(bool)",
+ "D594": "void IOMobileFramebufferAP::setSystemConsoleMode(bool)",
+ "D595": "void IOMobileFramebufferAP::setSystemConsoleMode_gated(bool)",
+ "D596": "bool IOMobileFramebufferAP::isDFBAllocated()",
+ "D597": "bool IOMobileFramebufferAP::preserveContents()",
+ "D598": "void IOMobileFramebufferAP::find_swap_function_gated()",
+
+ "D700": "int IOMFB::DCPPowerManager::set_kernel_power_assert(bool, bool)",
+}
+
+# iboot interface
+"""
+0: setResource
+1: setSurface
+2: setPower
+3: getHpdStatus
+4: getTimingModes
+5: getColorModes
+6: setMode
+7: setBrightness
+8: rwBCONRegsRequest
+9: setParameter
+10: setMatrix
+11: setProperty
+12: getProperty
+13: setBlock
+14: getBlock
+15: swapBegin
+16: setSwapLayer
+17: setSwapTimestamp
+18: setSwapEnd
+19: setSwapWait
+20: setBrightnessCfg
+21: setNamedProperty
+22: getNamedProperty
+"""
+
+from m1n1.fw.dcp.dcpep import DCPMessage, DCPEp_SetShmem, CallContext, DCPEp_Msg
+
+class DCPCallState:
+ pass
+
+class DCPCallChannel(Reloadable):
+ def __init__(self, dcpep, name, buf, bufsize):
+ self.dcpep = dcpep
+ self.name = name
+ self.buf = buf
+ self.bufsize = bufsize
+ self.log = self.dcpep.log
+ self.state = self.dcpep.state
+
+ def call(self, msg, dir):
+ ident = f"{self.name}.{msg.OFF:x}"
+
+ if any(msg.OFF == s.off for s in self.state.ch.get(self.name, [])):
+ self.log(f"{dir}{self.name}.{msg.OFF:x} !!! Overlapping call ({msg})")
+ assert False
+
+ state = DCPCallState()
+
+ data = self.dcpep.dart.ioread(self.dcpep.stream, self.state.shmem_iova + self.buf + msg.OFF, msg.LEN)
+ tag = data[:4][::-1].decode("ascii")
+ in_len, out_len = struct.unpack("<II", data[4:12])
+ data_in = data[12:12 + in_len]
+
+ state.off = msg.OFF
+ state.tag = tag
+ state.in_len = in_len
+ state.out_addr = self.buf + msg.OFF + 12 + in_len
+ state.out_len = out_len
+
+ verb = self.dcpep.get_verbosity(tag)
+ if verb >= 1:
+ self.log(f"{dir}{self.name}.{msg.OFF:x} {tag}:{KNOWN_MSGS.get(tag, 'unk')} ({msg})")
+ if verb >= 2:
+ print(f"Message: {tag} ({KNOWN_MSGS.get(tag, 'unk')}): (in {in_len:#x}, out {out_len:#x})")
+ if data_in:
+ print(f"{dir} Input ({len(data_in):#x} bytes):")
+ chexdump(data_in[:self.state.max_len])
+
+ #if tag not in KNOWN_MSGS:
+ #hv.run_shell()
+
+ if self.state.dumpfile:
+ dump = f"CALL {dir} {msg.value:#018x} {self.name} {state.off:#x} {state.tag} {in_len:#x} {out_len:#x} {data_in.hex()}\n"
+ self.state.dumpfile.write(dump)
+ self.state.dumpfile.flush()
+
+ self.state.ch.setdefault(self.name, []).append(state)
+
+ def ack(self, msg, dir):
+ assert msg.LEN == 0
+
+ states = self.state.ch.get(self.name, None)
+ if not states:
+ self.log(f"{dir}ACK {self.name}.{msg.OFF:x} !!! ACK without call ({msg})")
+ return
+
+ state = states[-1]
+
+ if self.state.show_acks:
+ self.log(f"{dir}ACK {self.name}.{msg.OFF:x} ({msg})")
+
+ data_out = self.dcpep.dart.ioread(self.dcpep.stream, self.state.shmem_iova + state.out_addr, state.out_len)
+
+ verb = self.dcpep.get_verbosity(state.tag)
+ if verb >= 3 and state.out_len > 0:
+ print(f"{dir}{self.name}.{msg.OFF:x} Output buffer ({len(data_out):#x} bytes):")
+ chexdump(data_out[:self.state.max_len])
+
+ if self.state.dumpfile:
+ dump = f"ACK {dir} {msg.value:#018x} {self.name} {state.off:#x} {data_out.hex()}\n"
+ self.state.dumpfile.write(dump)
+ self.state.dumpfile.flush()
+
+ states.pop()
+
+class DCPEp(EP):
+ BASE_MESSAGE = DCPMessage
+
+ def __init__(self, tracer, epid):
+ super().__init__(tracer, epid)
+ self.state.shmem_iova = None
+ self.state.show_globals = True
+ self.state.show_acks = True
+ self.state.max_len = 1024 * 1024
+ self.state.verbosity = 3
+ self.state.op_verb = {}
+ self.state.ch = {}
+ self.state.dumpfile = None
+
+ self.ch_cb = DCPCallChannel(self, "CB", 0x60000, 0x8000)
+ self.ch_cmd = DCPCallChannel(self, "CMD", 0, 0x8000)
+ self.ch_async = DCPCallChannel(self, "ASYNC", 0x40000, 0x20000)
+ self.ch_oobcb = DCPCallChannel(self, "OOBCB", 0x68000, 0x8000)
+ self.ch_oobcmd = DCPCallChannel(self, "OOBCMD", 0x8000, 0x8000)
+
+ self.cmd_ch = {
+ CallContext.CB: self.ch_cmd,
+ CallContext.CMD: self.ch_cmd,
+ CallContext.ASYNC: None, # unknown
+ CallContext.OOBCB: self.ch_oobcmd,
+ CallContext.OOBCMD: self.ch_oobcmd,
+ }
+
+ self.cb_ch = {
+ CallContext.CB: self.ch_cb,
+ CallContext.CMD: None,
+ CallContext.ASYNC: self.ch_async,
+ CallContext.OOBCB: self.ch_oobcb,
+ CallContext.OOBCMD: None,
+ }
+
+ def start(self):
+ self.add_mon()
+
+ def add_mon(self):
+ if self.state.shmem_iova and self.state.show_globals:
+ addr = self.state.shmem_iova + 0x80000
+ iomon.add(addr, 128,
+ name=f"{self.name}.shmem@{addr:08x}", offset=addr)
+
+ #addr = self.state.shmem_iova
+ #iomon.add(addr, 0x80080,
+ #name=f"{self.name}.shmem@{addr:08x}", offset=addr)
+
+ InitComplete = msg_log(1, DIR.RX)
+
+ @msg(0, DIR.TX, DCPEp_SetShmem)
+ def SetShmem(self, msg):
+ self.log(f"Shared memory DVA: {msg.DVA:#x}")
+ self.state.shmem_iova = msg.DVA & 0xfffffffff
+ self.add_mon()
+
+ @msg(2, DIR.TX, DCPEp_Msg)
+ def Tx(self, msg):
+ if msg.ACK:
+ self.cb_ch[msg.CTX].ack(msg, ">")
+ else:
+ self.cmd_ch[msg.CTX].call(msg, ">")
+
+ if self.state.show_globals:
+ iomon.poll()
+
+ return True
+
+ @msg(2, DIR.RX, DCPEp_Msg)
+ def Rx(self, msg):
+ self.log(msg)
+ if msg.ACK:
+ self.cmd_ch[msg.CTX].ack(msg, "<")
+ else:
+ self.cb_ch[msg.CTX].call(msg, "<")
+
+ if self.state.show_globals:
+ iomon.poll()
+
+ return True
+
+ def get_verbosity(self, tag):
+ return self.state.op_verb.get(tag, self.state.verbosity)
+
+ def set_verb_known(self, verb):
+ for i in KNOWN_MSGS:
+ if verb is None:
+ self.state.op_verb.pop(i, None)
+ else:
+ self.state.op_verb[i] = verb
+
+class SystemService(EPICEp):
+ NAME = "system"
+
+class TestService(EPICEp):
+ NAME = "test"
+
+class DCPExpertService(EPICEp):
+ NAME = "dcpexpert"
+
+class Disp0Service(EPICEp):
+ NAME = "disp0"
+
+class DCPAVControllerEpicTracer(EPICServiceTracer):
+ NAME = "dcpav-controller-epic"
+
+ @epic_service_cmd(0, 14)
+ def getParticipatesPowerManagement(self, data):
+ self.log("> getParticipatesPowerManagement")
+ @epic_service_reply(0, 14)
+ def getParticipatesPowerManagement_reply(self, data):
+ self.log("< getParticipatesPowerManagement")
+ chexdump(data, print_fn=self.log)
+
+class DPAVController(EPICEp):
+ NAME = "dpavctrl"
+ SERVICES = [
+ DCPAVControllerEpicTracer
+ ]
+
+class DPSACService(EPICEp):
+ NAME = "dpsac"
+
+
+class DCPDPDeviceEpicTracer(EPICServiceTracer):
+ NAME = "dcpdp-device-epic"
+
+ @epic_service_cmd(0, 15)
+ def getDeviceMatchingData(self, data):
+ self.log("> getDeviceMatchingData")
+ @epic_service_reply(0, 15)
+ def getDeviceMatchingData_reply(self, data):
+ self.log("< getDeviceMatchingData")
+ chexdump(data, print_fn=self.log)
+
+class DPDevService(EPICEp):
+ NAME = "dpdev"
+ SERVICES = [
+ DCPDPDeviceEpicTracer
+ ]
+
+class DPAVService(EPICEp):
+ NAME = "dpavserv"
+
+class DCPAVAudioInterfaceEpicTracer(EPICServiceTracer):
+ NAME = "dcpav-audio-interface-epic"
+
+ # usually 4, 6 but apparently also 0, 6 here?
+ # or maybe a different open?
+ @epic_service_cmd(0, 6)
+ def open2(self, data):
+ self.log("> open")
+ @epic_service_reply(0, 6)
+ def open2_reply(self, data):
+ self.log("< open")
+ chexdump(data, print_fn=self.log)
+
+ @epic_service_cmd(0, 8)
+ def prepareLink(self, data):
+ self.log("> prepareLink")
+ @epic_service_reply(0, 8)
+ def prepareLink_reply(self, data):
+ self.log("< prepareLink")
+ chexdump(data, print_fn=self.log)
+
+ @epic_service_cmd(0, 9)
+ def startLink(self, data):
+ self.log("> startLink")
+ @epic_service_reply(0, 9)
+ def startLink_reply(self, data):
+ self.log("< startLink")
+ chexdump(data, print_fn=self.log)
+
+ @epic_service_cmd(0, 15)
+ def getLinkStatus(self, data):
+ self.log("> getLinkStatus")
+ @epic_service_reply(0, 15)
+ def getLinkStatus_reply(self, data):
+ self.log("< getLinkStatus")
+ chexdump(data, print_fn=self.log)
+
+ @epic_service_cmd(0, 16)
+ def getTransport(self, data):
+ self.log("> getTransport")
+ @epic_service_reply(0, 16)
+ def getTransport_reply(self, data):
+ self.log("< getTransport")
+ chexdump(data, print_fn=self.log)
+
+ @epic_service_cmd(0, 17)
+ def getPortID(self, data):
+ self.log("> getPortID")
+ @epic_service_reply(0, 17)
+ def getPortID_reply(self, data):
+ self.log("< getPortID")
+ chexdump(data, print_fn=self.log)
+
+ @epic_service_cmd(1, 18)
+ def getElements(self, data):
+ self.log("> getElements")
+ @epic_service_reply(1, 18)
+ def getElements_reply(self, data):
+ self.log("< getElements")
+ chexdump(data, print_fn=self.log)
+
+ @epic_service_cmd(1, 20)
+ def getProductAttributes(self, data):
+ self.log("> getProductAttributes")
+ @epic_service_reply(1, 20)
+ def getProductAttributes_reply(self, data):
+ self.log("< getProductAttributes")
+ chexdump(data, print_fn=self.log)
+
+ @epic_service_cmd(1, 21)
+ def getEDIDUUID(self, data):
+ self.log("> getEDIDUUID")
+ @epic_service_reply(1, 21)
+ def getEDIDUUID_reply(self, data):
+ self.log("< getEDIDUUID")
+ chexdump(data, print_fn=self.log)
+
+ @epic_service_cmd(0, 22)
+ def getDataLatency(self, data):
+ self.log("> getDataLatency")
+ @epic_service_reply(0, 22)
+ def getDataLatency_reply(self, data):
+ self.log("< getDataLatency")
+ chexdump(data, print_fn=self.log)
+
+
+class AVService(EPICEp):
+ NAME = "av"
+ SERVICES = [
+ DCPAVAudioInterfaceEpicTracer
+ ]
+
+class DCPDPTXHDCPAuthSessionTracer(EPICServiceTracer):
+ NAME = "dcpdptx-hdcp-auth-session"
+
+ @epic_service_cmd(4, 8)
+ def getProtocol(self, data):
+ self.log("> getProtocol")
+ @epic_service_reply(4, 8)
+ def getProtocol_reply(self, data):
+ self.log("< getProtocol")
+ chexdump(data, print_fn=self.log)
+
+class HDCPService(EPICEp):
+ NAME = "hdcp"
+ SERVICES = [
+ DCPDPTXHDCPAuthSessionTracer
+ ]
+
+class RemoteAllocService(EPICEp):
+ NAME = "remotealloc"
+
+class DCPDPTXRemotePortTarget(Register32):
+ CORE = 3, 0
+ ATC = 7, 4
+ DIE = 11, 8
+ CONNECTED = 15, 15
+
+class DCPDPTXPortEpicTracer(EPICServiceTracer):
+ NAME = "dcpdptx-port-epic"
+
+ @epic_service_cmd(0, 8)
+ def setPowerState(self, data):
+ self.log("> setPowerState")
+ @epic_service_reply(0, 8)
+ def setPowerState_reply(self, data):
+ self.log("< setPowerState")
+
+ @epic_service_cmd(0, 13)
+ def connectTo(self, data):
+ unk1, target = struct.unpack("<II24x", data)
+ target = DCPDPTXRemotePortTarget(target)
+ self.log(f"> connectTo(target={target}, unk1=0x{unk1:x})")
+ @epic_service_reply(0, 13)
+ def connectTo_reply(self, data):
+ unk1, target = struct.unpack("<II24x", data)
+ target = DCPDPTXRemotePortTarget(target)
+ self.log(f"< connectTo(target={target}, unk1=0x{unk1:x})")
+
+ @epic_service_cmd(0, 14)
+ def validateConnection(self, data):
+ unk1, target = struct.unpack("<II40x", data)
+ target = DCPDPTXRemotePortTarget(target)
+ self.log(f"> validateConnection(target={target}, unk1=0x{unk1:x})")
+ @epic_service_reply(0, 14)
+ def validateConnection_reply(self, data):
+ unk1, target = struct.unpack("<II40x", data)
+ target = DCPDPTXRemotePortTarget(target)
+ self.log(f"< validateConnection(target={target}, unk1=0x{unk1:x})")
+
+ @epic_service_cmd(8, 10)
+ def hotPlugDetectChangeOccurred(self, data):
+ unk = struct.unpack("<16x?15x", data)[0]
+ self.log(f"> hotPlugDetectChangeOccurred(unk={unk})")
+ @epic_service_reply(8, 10)
+ def hotPlugDetectChangeOccurred_reply(self, data):
+ unk = struct.unpack("<16x?15x", data)[0]
+ self.log(f"< hotPlugDetectChangeOccurred(unk={unk})")
+
+class DPTXPortService(EPICEp):
+ NAME = "dptxport"
+ SERVICES = [
+ DCPDPTXPortEpicTracer
+ ]
+
+class DCPTracer(ASCTracer):
+ ENDPOINTS = {
+ 0x20: SystemService,
+ 0x21: TestService,
+ 0x22: DCPExpertService,
+ # Disp0 / DCP iboot as used by m1n1 is incompatible with the generic
+ # EPICEp tracer, disable it for now
+ #0x23: Disp0Service,
+ 0x24: DPAVController,
+ 0x25: EPICEp, # dcpav-power-ep
+ 0x26: DPSACService,
+ 0x27: DPDevService,
+ 0x28: DPAVService,
+ 0x29: AVService,
+ 0x2a: DPTXPortService, # dcpdptx-port-ep
+ 0x2b: HDCPService,
+ 0x2c: EPICEp, # cb-ap-to-dcp-service-ep
+ 0x2d: RemoteAllocService,
+ 0x37: DCPEp, # iomfb-link
+ }
+
+ def handle_msg(self, direction, r0, r1):
+ super().handle_msg(direction, r0, r1)
+ #iomon.poll()
+
+
+dcp_sid = u.adt[dcp_dart_mapper_adt_path].reg
+
+dart_dcp_tracer = DARTTracer(hv, dcp_dart_adt_path)
+dart_dcp_tracer.start()
+
+dart_disp0_tracer = DARTTracer(hv, disp0_dart_adt_path)
+dart_disp0_tracer.start()
+
+def readmem_iova(addr, size, readfn):
+ try:
+ return dart_dcp_tracer.dart.ioread(dcp_sid, addr, size)
+ except Exception as e:
+ print(e)
+ return None
+
+iomon.readmem = readmem_iova
+
+dcp_tracer = DCPTracer(hv, dcp_adt_path, verbose=1)
+dcp_tracer.start(dart_dcp_tracer.dart, stream=dcp_sid)
+
+#dcp_tracer.ep.dcpep.state.dumpfile = open("dcp.log", "a")
diff --git a/tools/proxyclient/hv/trace_gpio.py b/tools/proxyclient/hv/trace_gpio.py
new file mode 100644
index 0000000..4560bf0
--- /dev/null
+++ b/tools/proxyclient/hv/trace_gpio.py
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: MIT
+
+from m1n1.trace.gpio import GPIOTracer
+
+#trace_device("/arm-io/gpio", True)
+
+# trace gpio interrups, useful to follow the cascaded interrupts
+aic_phandle = getattr(hv.adt["/arm-io/aic"], "AAPL,phandle")
+try:
+ node = hv.adt["/arm-io/gpio0"]
+ path = "/arm-io/gpio0"
+except:
+ node = hv.adt["/arm-io/gpio"]
+ path = "/arm-io/gpio"
+
+if getattr(node, "interrupt-parent") == aic_phandle:
+ for irq in getattr(node, "interrupts"):
+ hv.trace_irq(node.name, irq, 1, hv.IRQTRACE_IRQ)
+
+PIN_NAMES_j274 = {
+ 0xC0: "i2c0:scl",
+ 0xBC: "i2c0:sda",
+ 0xC9: "i2c1:scl",
+ 0xC7: "i2c1:sda",
+ 0xA3: "i2c2:scl",
+ 0xA2: "i2c2:sda",
+ 106: "hpm:irq",
+ 136: "bluetooth:irq",
+ 196: "wlan:irq",
+ 183: "cs42l83:irq",
+ 182: "tas5770:irq",
+ 152: "pci@0,0",
+ 153: "pci@1,0",
+ 33: "pci@2,0",
+ #0x2D: "spi_nor:CS",
+}
+
+GPIOTracer = GPIOTracer._reloadcls()
+gpio_tracer = GPIOTracer(hv, path, PIN_NAMES_j274, verbose=0)
+gpio_tracer.start()
diff --git a/tools/proxyclient/hv/trace_i2c.py b/tools/proxyclient/hv/trace_i2c.py
new file mode 100644
index 0000000..f64fd24
--- /dev/null
+++ b/tools/proxyclient/hv/trace_i2c.py
@@ -0,0 +1,41 @@
+# SPDX-License-Identifier: MIT
+
+from m1n1.trace.i2c import I2CTracer
+
+I2CTracer = I2CTracer._reloadcls()
+
+i2c_tracers = {}
+
+for node in hv.adt["/arm-io"]:
+ if node.name.startswith("i2c"):
+ n = int(node.name[3:])
+ i2c_tracers[n] = I2CTracer(hv, f"/arm-io/i2c{n}", verbose=0)
+ i2c_tracers[n].stop()
+ i2c_tracers[n].start()
+ if hv.ctx:
+ for irq in getattr(node, "interrupts"):
+ hv.trace_irq(node.name, irq, 1, hv.IRQTRACE_IRQ)
+
+from m1n1.gpiola import GPIOLogicAnalyzer
+
+if not hv.started:
+ for cpu in list(hv.adt["cpus"]):
+ if cpu.name == "cpu3":
+ print(f"Removing ADT node {cpu._path}")
+ del hv.adt["cpus"][cpu.name]
+
+if not hv.started or hv.ctx is not None:
+ m = GPIOLogicAnalyzer(u, "arm-io/gpio",
+ pins={"scl": 0xc9, "sda": 0xc7},
+ div=1, on_pin_change=True, cpu=3)
+
+ m.load_regmap(list(i2c_tracers[1].regmaps.values())[0],
+ regs={"SMSTA", "XFSTA"})
+
+def start_la():
+ m.start(1000000, bufsize=0x80000)
+ hv.cont()
+
+def stop_la():
+ m.complete()
+ m.show()
diff --git a/tools/proxyclient/hv/trace_isp.py b/tools/proxyclient/hv/trace_isp.py
new file mode 100644
index 0000000..b930095
--- /dev/null
+++ b/tools/proxyclient/hv/trace_isp.py
@@ -0,0 +1,5 @@
+from m1n1.trace.isp import ISPTracer
+
+hv.log('ISP: Registering ISP ASC tracer...')
+isp_asc_tracer = ISPTracer(hv, "/arm-io/isp", "/arm-io/dart-isp", verbose=4)
+isp_asc_tracer.start() \ No newline at end of file
diff --git a/tools/proxyclient/hv/trace_keyboard.py b/tools/proxyclient/hv/trace_keyboard.py
new file mode 100644
index 0000000..d8cae66
--- /dev/null
+++ b/tools/proxyclient/hv/trace_keyboard.py
@@ -0,0 +1,206 @@
+# SPDX-License-Identifier: MIT
+
+import struct
+from construct import *
+
+from m1n1.hv import TraceMode
+from m1n1.proxyutils import RegMonitor
+from m1n1.utils import *
+
+from m1n1.trace import ADTDevTracer
+from m1n1.trace.asc import ASCTracer, ASCRegs, EP, EPState, msg, msg_log, DIR
+from m1n1.trace.dart import DARTTracer
+from m1n1.trace.gpio import GPIOTracer
+from m1n1.trace.spi import SPITracer
+
+DARTTracer = DARTTracer._reloadcls()
+ASCTracer = ASCTracer._reloadcls()
+GPIOTracer = GPIOTracer._reloadcls()
+SPITracer = SPITracer._reloadcls()
+
+# SPI HID transport tracer for 2021 macbook models
+
+kbd_node = None
+for node in hv.adt.walk_tree():
+ try:
+ if node.compatible[0] == "spi-1,spimc":
+ for c in node:
+ try:
+ if c.compatible[0] == "hid-transport,spi":
+ kbd_node = c
+ break
+ except AttributeError:
+ continue
+ except AttributeError:
+ continue
+ if kbd_node is not None:
+ break
+
+# trace interrupts
+aic_phandle = getattr(hv.adt["/arm-io/aic"], "AAPL,phandle")
+spi_node = kbd_node._parent
+
+if getattr(spi_node, "interrupt-parent") == aic_phandle:
+ for irq in getattr(spi_node, "interrupts"):
+ hv.trace_irq(node.name, irq, 1, hv.IRQTRACE_IRQ)
+
+spi_pins = {
+ 0x37: "spi3_cs",
+ 0xc2: "ipd_en",
+}
+spi_pins_nub = {
+ 0x6: "ipd_irq",
+}
+
+SPI_HID_PKT = Struct(
+ "flags" / Int8ub,
+ "dev" / Int8ub,
+ "offset" / Int16ul,
+ "remain" / Int16ul,
+ "length" / Int16ul,
+ "data" / Bytes(246),
+ "crc16" / Int16ul
+)
+
+#gpio_tracer = GPIOTracer(hv, "/arm-io/gpio0", spi_pins, verbose=0)
+#gpio_tracer.start()
+
+#gpio_tracer_nub = GPIOTracer(hv, "/arm-io/nub-gpio0", spi_pins_nub, verbose=0)
+#gpio_tracer_nub.start()
+
+dart_sio_tracer = DARTTracer(hv, "/arm-io/dart-sio", verbose=1)
+dart_sio_tracer.start()
+
+iomon = RegMonitor(hv.u, ascii=True)
+
+def readmem_iova(addr, size):
+ try:
+ return dart_sio_tracer.dart.ioread(0, addr, size)
+ except Exception as e:
+ print(e)
+ return None
+
+iomon.readmem = readmem_iova
+
+class SIOMessage(Register64):
+ EP = 7, 0 # matches device's ADT dma-channels, j314c spi3 0x1a and 0x1b
+ TAG = 13, 8 # counts for ipd spi transfers from 0x02 to 0x20
+ TYPE = 23, 16 # OP
+ PARAM = 31, 24
+ DATA = 63, 32
+
+class SIOStart(SIOMessage):
+ TYPE = 23, 16, Constant(2)
+
+class SIOSetup(SIOMessage):
+ TYPE = 23, 16, Constant(3)
+
+class SIOConfig(SIOMessage): #???
+ TYPE = 23, 16, Constant(5)
+
+class SIOAck(SIOMessage):
+ TYPE = 23, 16, Constant(0x65)
+
+class SIOSetupIO(SIOMessage):
+ TYPE = 23, 16, Constant(6)
+
+class SIOCompleteIO(SIOMessage):
+ TYPE = 23, 16, Constant(0x68)
+
+class SIOEp(EP):
+ BASE_MESSAGE = SIOMessage
+ SHORT = "sioep"
+
+ def __init__(self, tracer, epid):
+ super().__init__(tracer, epid)
+ self.state.iova = None
+ self.state.iova_cfg = None
+ self.state.dumpfile = None
+
+ @msg(2, DIR.TX, SIOStart)
+ def Start(self, msg):
+ self.log("Start SIO")
+
+ @msg(3, DIR.TX, SIOSetup)
+ def m_Setup(self, msg):
+ iomon.poll()
+ if msg.EP == 0 and msg.PARAM == 0x1:
+ self.state.iova = msg.DATA << 12
+ elif msg.EP == 0 and msg.PARAM == 0x2:
+ # size for PARAM == 0x1?
+ if self.state.iova is not None and self.tracer.verbose > 1:
+ iomon.add(self.state.iova, msg.DATA * 8, name=f"sio.shmem@{self.state.iova:08x}",
+ offset=self.state.iova)
+ elif msg.EP == 0 and msg.PARAM == 0xb:
+ # second iova block, maybe config
+ self.state.iova_cfg = msg.DATA << 12
+ elif msg.EP == 0 and msg.PARAM == 0xc:
+ # size for PARAM == 0xb?
+ if self.state.iova is not None and self.tracer.verbose > 1:
+ iomon.add(self.state.iova_cfg, msg.DATA * 8,
+ name=f"sio.shmem@{self.state.iova_cfg:08x}", offset=self.state.iova_cfg)
+
+ @msg(0x65, DIR.RX, SIOAck)
+ def m_Ack(self, msg):
+ iomon.poll()
+
+ @msg(6, DIR.TX, SIOSetupIO)
+ def m_SetupIO(self, msg):
+ iomon.poll()
+ if self.state.iova is None:
+ return
+ if msg.EP == 0x1a:
+ buf = struct.unpack("<I", self.tracer.ioread(self.state.iova + 0xa8, 4))[0]
+ size = struct.unpack("<I", self.tracer.ioread(self.state.iova + 0xb0, 4))[0]
+ self.log_spihid_pkt("SPI3 TX", self.tracer.ioread(buf, size))
+
+ @msg(0x68, DIR.RX, SIOCompleteIO)
+ def m_CompleteIO(self, msg):
+ iomon.poll()
+ if self.state.iova is None:
+ return
+ if msg.EP == 0x1b:
+ buf = struct.unpack("<I", self.tracer.ioread(self.state.iova + 0x48, 4))[0]
+ size = struct.unpack("<I", self.tracer.ioread(self.state.iova + 0x50, 4))[0]
+ self.log_spihid_pkt("SPI3 RX", self.tracer.ioread(buf, size))
+
+ def log_spihid_pkt(self, label, data):
+ if len(data) != 256:
+ self.log(f"{label}: unexpected data length: {len(data):d}")
+ chexdump(data)
+ return
+
+ crc16 = crc16USB(0, data[:254])
+ pkt = SPI_HID_PKT.parse(data)
+ #self.log(f"pkt.crc16:{pkt.crc16:#04x} crc16:{crc16:#04x}")
+ if pkt.length == 0:
+ return
+
+ if pkt.flags == 0x80 and pkt.dev == 0x11 and pkt.length == 849 and pkt.offset == 256 and pkt.remain == 834 and crc16 == 0x1489:
+ return
+ if pkt.crc16 != crc16:
+ self.log(f"{label}: CRC mismatch: pkt.crc16:{pkt.crc16:#04x} crc16:{crc16:#04x}")
+ chexdump(data)
+ return
+ self.log(f"{label}: flags:{pkt.flags:#2x} dev:{pkt.dev:#2x} length:{pkt.length:4d} offset:{pkt.offset:3d} remain:{pkt.remain:3d}")
+ chexdump(pkt.data[:min(246, pkt.length)])
+
+ if self.state.dumpfile:
+ dump = f"{label}: flags:{pkt.flags:#2x} dev:{pkt.dev:#2x} length:{pkt.length:4d} {pkt.data[:min(246, pkt.length)].hex()}\n"
+ self.state.dumpfile.write(dump)
+ self.state.dumpfile.flush()
+
+
+class SIOTracer(ASCTracer):
+ ENDPOINTS = {
+ 0x20: SIOEp
+ }
+
+
+sio_tracer = SIOTracer(hv, "/arm-io/sio", verbose=False)
+sio_tracer.start(dart_sio_tracer.dart)
+
+sio_tracer.ep.sioep.state.dumpfile = open("spi_hid.log", "a")
+
+spi_tracer = SPITracer(hv, "/arm-io/" + spi_node.name, verbose=1)
+spi_tracer.start()
diff --git a/tools/proxyclient/hv/trace_mesa.py b/tools/proxyclient/hv/trace_mesa.py
new file mode 100644
index 0000000..37fb32f
--- /dev/null
+++ b/tools/proxyclient/hv/trace_mesa.py
@@ -0,0 +1,212 @@
+# SPDX-License-Identifier: MIT
+"""
+Things to note:
+ The command buffer is encrypted after the poweron sequence, and I
+ can't find the key in the SEP using sven's old SEP tracer.
+"""
+import struct
+from construct import *
+
+from m1n1.hv import TraceMode
+from m1n1.proxyutils import RegMonitor
+from m1n1.utils import *
+
+from m1n1.trace import ADTDevTracer
+from m1n1.trace.asc import ASCTracer, ASCRegs, EP, EPState, msg, msg_log, DIR
+from m1n1.trace.dart import DARTTracer
+from m1n1.trace.gpio import GPIOTracer
+from m1n1.trace.spi import SPITracer
+
+DARTTracer = DARTTracer._reloadcls()
+ASCTracer = ASCTracer._reloadcls()
+GPIOTracer = GPIOTracer._reloadcls()
+SPITracer = SPITracer._reloadcls()
+
+mesa_node = None
+for node in hv.adt.walk_tree():
+ try:
+ if node.compatible[0] == "spi-1,spimc":
+ for c in node:
+ try:
+ if c.compatible[0] == "biosensor,mesa":
+ mesa_node = c
+ break
+ except AttributeError:
+ continue
+ except AttributeError:
+ continue
+ if mesa_node is not None:
+ break
+
+# trace interrupts
+aic_phandle = getattr(hv.adt["/arm-io/aic"], "AAPL,phandle")
+spi_node = mesa_node._parent
+
+if getattr(spi_node, "interrupt-parent") == aic_phandle:
+ for irq in getattr(spi_node, "interrupts"):
+ hv.trace_irq("/arm-io/" + spi_node.name, irq, 1, hv.IRQTRACE_IRQ)
+
+if getattr(mesa_node, "interrupt-parent") == aic_phandle:
+ for irq in getattr(mesa_node, "interrupts"):
+ hv.trace_irq("/arm-io/" + mesa_node.name, irq, 1, hv.IRQTRACE_IRQ)
+
+mesa_pins = {
+ 0xc4: "mesa_pwr",
+
+}
+
+
+gpio_tracer = GPIOTracer(hv, "/arm-io/gpio0", mesa_pins, verbose=1)
+gpio_tracer.start()
+
+dart_sio_tracer = DARTTracer(hv, "/arm-io/dart-sio", verbose=1)
+dart_sio_tracer.start()
+
+iomon = RegMonitor(hv.u, ascii=True)
+
+def readmem_iova(addr, size, readfn=None):
+ try:
+ return dart_sio_tracer.dart.ioread(0, addr, size)
+ except Exception as e:
+ print(e)
+ return None
+
+iomon.readmem = readmem_iova
+
+
+
+class SIOMessage(Register64):
+ EP = 7, 0 # SPI2 DMA channels 0x18, 0x19
+ TAG = 13, 8 # counts up, message ID?
+ TYPE = 23, 16 # SIO message type
+ PARAM = 31, 24
+ DATA = 63, 32
+
+class SIOStart(SIOMessage):
+ TYPE = 23, 16, Constant(2)
+
+class SIOSetup(SIOMessage):
+ TYPE = 23, 16, Constant(3)
+
+class SIOConfig(SIOMessage): #???
+ TYPE = 23, 16, Constant(5)
+
+class SIOAck(SIOMessage):
+ TYPE = 23, 16, Constant(0x65)
+
+class SIOSetupIO(SIOMessage):
+ TYPE = 23, 16, Constant(6)
+
+class SIOCompleteIO(SIOMessage):
+ TYPE = 23, 16, Constant(0x68)
+
+class SIOEp(EP):
+ BASE_MESSAGE = SIOMessage
+ SHORT = "sioep"
+
+ def __init__(self, tracer, epid):
+ super().__init__(tracer, epid)
+ self.state.iova = None
+ self.state.iova_cfg = None
+ self.state.iova_unk = None
+ self.state.dumpfile = None
+
+ @msg(2, DIR.TX, SIOStart)
+ def Start(self, msg):
+ self.log("Start SIO")
+
+ @msg(3, DIR.TX, SIOSetup)
+ def m_Setup(self, msg):
+ if msg.EP == 0 and msg.PARAM == 0x1:
+ self.state.iova = msg.DATA << 12
+
+ elif msg.EP == 0 and msg.PARAM == 0x2:
+ # size for PARAM == 0x1?
+ iomon.add(self.state.iova, msg.DATA * 8,
+ name=f"SIO IOVA region at 0x{self.state.iova:08x}",
+ offset=self.state.iova)
+
+ #elif msg.EP == 0 and msg.PARAM == 0xb:
+ ## second iova block, maybe config
+ #self.state.iova_cfg = msg.DATA << 12
+
+ #elif msg.EP == 0 and msg.PARAM == 0xc:
+ ## size for PARAM == 0xb?
+ #iomon.add(self.state.iova_cfg, msg.DATA * 8,
+ #name=f"SIO IOVA CFG region at 0x{self.state.iova_cfg:08x}",
+ #offset=self.state.iova_cfg)
+
+ if msg.EP == 0 and msg.PARAM == 0xd:
+ # possible fingerprint sensor IOVA region
+ self.state.iova_unk = msg.DATA << 12
+
+ elif msg.EP == 0 and msg.PARAM == 0xe:
+ iomon.add(self.state.iova_unk, msg.DATA * 8,
+ name=f"SIO IOVA UNK region at {self.state.iova_unk:08x}",
+ offset=self.state.iova_unk)
+
+ @msg(5, DIR.TX, SIOConfig)
+ def m_Config(self, msg):
+ return
+
+ @msg(0x65, DIR.RX, SIOAck)
+ def m_Ack(self, msg):
+ return
+
+ @msg(6, DIR.TX, SIOSetupIO)
+ def m_SetupIO(self, msg):
+ if msg.EP == 0x18 or 0x19:
+ iomon.poll()
+ return
+
+ @msg(0x68, DIR.RX, SIOCompleteIO)
+ def m_CompleteIO(self, msg):
+ if msg.EP == 0x18:
+ if self.state.iova is None:
+ return
+
+ buf = struct.unpack("<I", self.tracer.ioread(self.state.iova + 0xa8, 4))[0]
+ size = struct.unpack("<I", self.tracer.ioread(self.state.iova + 0xb0, 4))[0]
+ self.log(f"SetupIO 0x18: buf {buf:#x}, size {size:#x}")
+ # XXX: Do not try to log messages going to 0x2
+ if buf == 0x2:
+ self.log("Mesa command interrupted!")
+ return
+ self.log_mesa("EP 0x18", self.tracer.ioread(buf, size))
+ return
+ if msg.EP == 0x19:
+ if self.state.iova is None:
+ return
+
+ buf = struct.unpack("<I", self.tracer.ioread(self.state.iova + 0x48, 4))[0]
+ size = struct.unpack("<I", self.tracer.ioread(self.state.iova + 0x50, 4))[0]
+ self.log(f"CompleteIO 0x19: buf {buf:#x}, size {size:#x}")
+ # XXX: Do not try to log messages going to 0x2
+ if buf == 0x2:
+ self.log("Mesa command interrupted!")
+ return
+ if size >= 0x7200:
+ with open("large_message.bin", "wb") as fd:
+ fd.write(self.tracer.ioread(buf, size))
+ print("Fingerprint record message dumped.")
+ return
+ self.log_mesa("EP 0x19", self.tracer.ioread(buf, size))
+ return
+
+ def log_mesa(self, label, data):
+ self.log(f"{label}: {len(data):d} byte message: ")
+ chexdump(data)
+ print("\n")
+
+
+class SIOTracer(ASCTracer):
+ ENDPOINTS = {
+ 0x20: SIOEp
+ }
+
+
+sio_tracer = SIOTracer(hv, "/arm-io/sio", verbose=1)
+sio_tracer.start(dart=dart_sio_tracer.dart)
+
+spi_tracer = SPITracer(hv, "/arm-io/spi2", verbose=2)
+spi_tracer.start()
diff --git a/tools/proxyclient/hv/trace_nvme.py b/tools/proxyclient/hv/trace_nvme.py
new file mode 100644
index 0000000..c10b3bc
--- /dev/null
+++ b/tools/proxyclient/hv/trace_nvme.py
@@ -0,0 +1,275 @@
+# SPDX-License-Identifier: MIT
+
+from construct import *
+from construct.core import Int16ul, Int32ul, Int64ul, Int8ul
+
+from m1n1.hv import TraceMode
+from m1n1.utils import *
+from m1n1.trace import ADTDevTracer
+from m1n1.trace.asc import ASCRegs
+from m1n1.trace.asc import ASCTracer
+
+ASCTracer = ASCTracer._reloadcls()
+
+class NVMERegs(RegMap):
+ APPLE_NVMMU_NUM = 0x28100, Register32
+ APPLE_NVMMU_BASE_ASQ = 0x28108, Register32
+ APPLE_NVMMU_BASE_ASQ1 = 0x2810C, Register32
+ APPLE_NVMMU_BASE_IOSQ = 0x28110, Register32
+ APPLE_NVMMU_BASE_IOSQ1 = 0x28114, Register32
+ APPLE_NVMMU_TCB_INVAL = 0x28118, Register32
+ APPLE_NVMMU_TCB_STAT = 0x28120, Register32
+ APPLE_ANS2_LINEAR_SQ_CTRL = 0x24908, Register32
+ APPLE_ANS2_UNKNOWN_CTRL = 0x24008, Register32
+ APPLE_ANS2_BOOT_STATUS = 0x1300, Register32
+ APPLE_ANS2_MAX_PEND_CMDS_CTRL = 0x1210, Register32
+ APPLE_ANS2_LINEAR_ASQ_DB = 0x2490C, Register32
+ APPLE_ANS2_LINEAR_IOSQ_DB = 0x24910, Register32
+
+ NVME_REG_CAP = 0x0000, Register32
+ NVME_REG_VS = 0x0008, Register32
+ NVME_REG_INTMS = 0x000C, Register32
+ NVME_REG_INTMC = 0x0010, Register32
+ NVME_REG_CC = 0x0014, Register32
+ NVME_REG_CSTS = 0x001C, Register32
+ NVME_REG_NSSR = 0x0020, Register32
+ NVME_REG_AQA = 0x0024, Register32
+ NVME_REG_ASQ = 0x0028, Register32
+ NVME_REG_ASQ1 = 0x002C, Register32
+ NVME_REG_ACQ = 0x0030, Register32
+ NVME_REG_CMBLOC = 0x0038, Register32
+ NVME_REG_CMBSZ = 0x003C, Register32
+ NVME_REG_BPINFO = 0x0040, Register32
+ NVME_REG_BPRSEL = 0x0044, Register32
+ NVME_REG_BPMBL = 0x0048, Register32
+ NVME_REG_CMBMSC = 0x0050, Register32
+ NVME_REG_PMRCAP = 0x0E00, Register32
+ NVME_REG_PMRCTL = 0x0E04, Register32
+ NVME_REG_PMRSTS = 0x0E08, Register32
+ NVME_REG_PMREBS = 0x0E0C, Register32
+ NVME_REG_PMRSWTP = 0x0E10, Register32
+ NVME_REG_DBS = 0x1000, Register32
+ NVME_REG_DBS_ASQ = 0x1004, Register32
+ NVME_REG_DBS_IOSQ = 0x100C, Register32
+
+
+AppleTunnelSetTime = Struct(
+ "unk" / Int32ul,
+ "unix_timestamp" / Int32ul,
+ "time_0" / Int64ul,
+ "time_1" / Int64ul,
+)
+
+NVMECommand = Struct(
+ "opcode" / Int8ul,
+ "flags" / Int8ul,
+ "command_id" / Int16ul,
+ "nsid" / Int32ul,
+ "cdw0" / Int32ul,
+ "cdw1" / Int32ul,
+ "metadata" / Int64ul,
+ "prp1" / Int64ul,
+ "prp2" / Int64ul,
+ "cdw10" / Int32ul,
+ "cdw11" / Int32ul,
+ "cdw12" / Int32ul,
+ "cdw13" / Int32ul,
+ "cdw14" / Int32ul,
+ "cdw16" / Int32ul,
+)
+
+NVME_IO_COMMANDS = {
+ 0x00: "nvme_cmd_flush",
+ 0x01: "nvme_cmd_write",
+ 0x02: "nvme_cmd_read",
+ 0x04: "nvme_cmd_write_uncor",
+ 0x05: "nvme_cmd_compare",
+ 0x08: "nvme_cmd_write_zeroes",
+ 0x09: "nvme_cmd_dsm",
+ 0x0C: "nvme_cmd_verify",
+ 0x0D: "nvme_cmd_resv_register",
+ 0x0E: "nvme_cmd_resv_report",
+ 0x11: "nvme_cmd_resv_acquire",
+ 0x15: "nvme_cmd_resv_release",
+ 0x79: "nvme_cmd_zone_mgmt_send",
+ 0x7A: "nvme_cmd_zone_mgmt_recv",
+ 0x7D: "nvme_cmd_zone_append",
+}
+
+NVME_ADMIN_COMMANDS = {
+ 0x00: "nvme_admin_delete_sq",
+ 0x01: "nvme_admin_create_sq",
+ 0x02: "nvme_admin_get_log_page",
+ 0x04: "nvme_admin_delete_cq",
+ 0x05: "nvme_admin_create_cq",
+ 0x06: "nvme_admin_identify",
+ 0x08: "nvme_admin_abort_cmd",
+ 0x09: "nvme_admin_set_features",
+ 0x0A: "nvme_admin_get_features",
+ 0x0C: "nvme_admin_async_event",
+ 0x0D: "nvme_admin_ns_mgmt",
+ 0x10: "nvme_admin_activate_fw",
+ 0x11: "nvme_admin_download_fw",
+ 0x14: "nvme_admin_dev_self_test",
+ 0x15: "nvme_admin_ns_attach",
+ 0x18: "nvme_admin_keep_alive",
+ 0x19: "nvme_admin_directive_send",
+ 0x1A: "nvme_admin_directive_recv",
+ 0x1C: "nvme_admin_virtual_mgmt",
+ 0x1D: "nvme_admin_nvme_mi_send",
+ 0x1E: "nvme_admin_nvme_mi_recv",
+ 0x7C: "nvme_admin_dbbuf",
+ 0x80: "nvme_admin_format_nvm",
+ 0x81: "nvme_admin_security_send",
+ 0x82: "nvme_admin_security_recv",
+ 0x84: "nvme_admin_sanitize_nvm",
+ 0x86: "nvme_admin_get_lba_status",
+ 0xC0: "nvme_admin_vendor_start",
+}
+
+APPLE_TUNNEL_CMDS = {0x06: "set_time", 0x38: "get_nand_id", 0xBA: "get_nand_geometry"}
+
+NVMMUTcb = Struct(
+ "opcode" / Int8ul,
+ "dma_flags" / Int8ul,
+ "command_id" / Int8ul,
+ "unk0" / Int8ul,
+ "length" / Int32ul,
+ "unk1a" / Int64ul,
+ "unk1b" / Int64ul,
+ "prp0" / Int64ul,
+ "prp1" / Int64ul,
+ "unk2a" / Int64ul,
+ "unk2b" / Int64ul,
+ # aes_iv, u8[8]
+ # aes_data, u8[64]
+)
+
+
+class NVMETracer(ASCTracer):
+ DEFAULT_MODE = TraceMode.SYNC
+
+ REGMAPS = [ASCRegs, None, None, NVMERegs]
+ NAMES = ["asc", None, None, "nvme"]
+
+ ENDPOINTS = {}
+
+ def init_state(self):
+ self.state.ep = {}
+ self.state.cmd_cache = {}
+ self.state.nvmmu_asq_base = None
+ self.state.nvmmu_iosq_base = None
+ self.state.asq = None
+
+ def r_APPLE_NVMMU_TCB_STAT(self, r):
+ pass
+
+ def w_APPLE_NVMMU_BASE_ASQ(self, r):
+ self.state.nvmmu_asq_base = r.value
+
+ def w_APPLE_NVMMU_BASE_ASQ1(self, r):
+ self.state.nvmmu_asq_base |= r.value << 32
+
+ def w_APPLE_NVMMU_BASE_IOSQ(self, r):
+ self.state.nvmmu_iosq_base = r.value
+
+ def w_APPLE_NVMMU_BASE_IOSQ1(self, r):
+ self.state.nvmmu_iosq_base |= r.value << 32
+
+ def w_NVME_REG_ASQ(self, r):
+ self.state.asq = r.value
+
+ def w_NVME_REG_ASQ1(self, r):
+ self.state.asq |= r.value << 32
+
+ def w_APPLE_ANS2_LINEAR_ASQ_DB(self, r):
+ tag = r.value
+ cmd = NVMECommand.parse(self.hv.iface.readmem(self.state.asq + 64 * tag, 0x40))
+ tcb = NVMMUTcb.parse(
+ self.hv.iface.readmem(self.state.nvmmu_asq_base + 0x80 * tag, 0x80)
+ )
+
+ self.state.cmd_cache[tag] = (True, cmd, tcb)
+
+ if cmd.opcode == 0xD8:
+ self.log("apple_tunnel_cmd:")
+ self.parse_apple_tunnel_cmd(cmd, False)
+ return
+
+ cmdname = NVME_ADMIN_COMMANDS.get(cmd.opcode, "unknown")
+ self.log(f"{cmdname}:")
+ self.log(f" {repr(cmd)}")
+ self.log(f" {repr(tcb)}")
+
+ if cmd.opcode == 1:
+ self.state.iosq = cmd.prp1
+
+ def w_APPLE_ANS2_LINEAR_IOSQ_DB(self, r):
+ tag = r.value
+ cmd = NVMECommand.parse(self.hv.iface.readmem(self.state.iosq + 64 * tag, 0x40))
+ tcb = NVMMUTcb.parse(
+ self.hv.iface.readmem(self.state.nvmmu_iosq_base + 0x80 * tag, 0x80)
+ )
+ cmdname = NVME_IO_COMMANDS.get(cmd.opcode, "unknown")
+ self.log(f"{cmdname}:")
+ self.log(f" {repr(cmd)}")
+ self.log(f" {repr(tcb)}")
+
+ self.state.cmd_cache[tag] = (False, cmd, tcb)
+
+ def parse_apple_tunnel_cmd(self, cmd, done):
+ ptr0 = (cmd.cdw12 << 32) | cmd.cdw11
+ ptr1 = (cmd.cdw14 << 32) | cmd.cdw13
+
+ data = self.hv.iface.readmem(ptr0, 0x4000)
+ if ptr1 > 0:
+ data1 = self.hv.iface.readmem(ptr1, 0x4000)
+
+ apple_cmd_opcode = data[12]
+ apple_cmd = APPLE_TUNNEL_CMDS.get(apple_cmd_opcode, "Unknown")
+
+ if apple_cmd_opcode == 0x06:
+ self.log(
+ f" apple_tunnel_cmd: set_time: {repr(AppleTunnelSetTime.parse(data[0x18:0x30]))}"
+ )
+ elif apple_cmd_opcode == 0x38:
+ self.log(f" apple_tunnel_cmd: get_nand_id")
+ if done:
+ self.log(f" manufacturer id: {hexdump(data1[:8])}")
+ else:
+ self.log(f" apple_tunnel_cmd: {apple_cmd} ({apple_cmd_opcode})")
+ chexdump(data, print_fn=self.log)
+ if ptr1 > 0:
+ chexdump(self.hv.iface.readmem(ptr1, 0x4000), print_fn=self.log)
+
+ def w_APPLE_NVMMU_TCB_INVAL(self, r):
+ self.log(f" NVMMU inval for {r.value}")
+ tag = r.value
+ if tag not in self.state.cmd_cache:
+ self.log(" NVMMU tag not found in cmd_cache")
+ return
+
+ is_admin, cmd, tcb = self.state.cmd_cache[tag]
+ del self.state.cmd_cache[tag]
+
+ if is_admin:
+ if cmd.opcode == 0xD8:
+ self.log(f" done apple_tunnel_cmd")
+ self.parse_apple_tunnel_cmd(cmd, True)
+ else:
+ cmdname = NVME_ADMIN_COMMANDS.get(cmd.opcode, "unknown")
+ self.log(f" done {cmdname}")
+ else:
+ cmdname = NVME_IO_COMMANDS.get(cmd.opcode, "unknown")
+ self.log(f" done {cmdname}")
+
+ def start(self):
+ self.state.cmd_cache = {}
+ super().start()
+
+
+NVMETracer = NVMETracer._reloadcls()
+nvme_tracer = NVMETracer(hv, "/arm-io/ans", verbose=1)
+nvme_tracer.start()
+
+trace_device("/arm-io/sart-ans")
diff --git a/tools/proxyclient/hv/trace_pmgr.py b/tools/proxyclient/hv/trace_pmgr.py
new file mode 100644
index 0000000..f04d8db
--- /dev/null
+++ b/tools/proxyclient/hv/trace_pmgr.py
@@ -0,0 +1,170 @@
+# SPDX-License-Identifier: MIT
+
+from m1n1 import asm
+from m1n1.trace import Tracer
+from m1n1.utils import *
+from m1n1.proxy import *
+from m1n1.sysreg import *
+from m1n1.proxyutils import RegMonitor
+from m1n1.trace.dart import DARTTracer
+from m1n1.trace.asc import ASCTracer, EP, msg, msg_log, DIR
+from m1n1.fw.pmp import *
+
+#trace_device("/arm-io/pmgr", False)
+#trace_device("/arm-io/jpeg0")
+#trace_device("/arm-io/jpeg1")
+
+#for reg in (0, 1, 2, 3, 4, 23):
+ #addr, size = hv.adt["/arm-io/pmgr"].get_reg(reg)
+ #hv.trace_range(irange(addr, 0x20000))
+
+#hv.trace_range(irange(0x210e00000, 0x80000), read=False)
+#hv.trace_range(irange(0x211e00000, 0x80000), read=False)
+
+#hv.trace_range(irange(0x23b040000, 0x1000))
+#hv.trace_range(irange(0x23b044000, 0x14000))
+
+Tracer = Tracer._reloadcls()
+ASCTracer = ASCTracer._reloadcls()
+
+iomon = RegMonitor(hv.u, ascii=True)
+
+def readmem_iova(addr, size):
+ try:
+ return dart_tracer.dart.ioread(0, addr, size)
+ except Exception as e:
+ print(e)
+ return None
+
+iomon.readmem = readmem_iova
+
+class PMPEpTracer(EP):
+ BASE_MESSAGE = PMPMessage
+
+ def __init__(self, tracer, epid):
+ super().__init__(tracer, epid)
+ self.state.shmem_iova = None
+ self.state.verbose = 1
+
+ def start(self):
+ self.add_mon()
+
+ def add_mon(self):
+ if self.state.shmem_iova:
+ iomon.add(self.state.shmem_iova, 0x10000,
+ name=f"{self.name}.shmem@{self.state.shmem_iova:08x}", offset=0)
+
+ @msg(1, DIR.TX, PMP_Configure)
+ def Configure(self, msg):
+ self.state.shmem_iova = msg.DVA
+ self.add_mon()
+
+class PMPTracer(ASCTracer):
+ ENDPOINTS = {
+ 0x20: PMPEpTracer
+ }
+
+ def handle_msg(self, direction, r0, r1):
+ super().handle_msg(direction, r0, r1)
+ iomon.poll()
+
+ def start(self, dart=None):
+ super().start()
+ # noisy doorbell
+ self.trace(0x23bc34000, 4, TraceMode.OFF)
+
+#dart_tracer = DARTTracer(hv, "/arm-io/dart-pmp", verbose=2)
+#dart_tracer.start()
+
+#pmp_tracer = PMPTracer(hv, "/arm-io/pmp", verbose=1)
+#pmp_tracer.start(dart_tracer.dart)
+
+class PMGRTracer(Tracer):
+ IGNORED = set(["SPI1", "I2C2"])
+ def __init__(self, hv):
+ super().__init__(hv)
+ self.dev = hv.adt["/arm-io/pmgr"]
+ self.ignored_ranges = [
+ (0x23b738004, 4), # ecpu state report
+ (0x23b738008, 4), # pcpu state report
+ (0x23d2b9000, 0x30),
+ (0x23d2dc100, 4),
+ ]
+ self.build_table(hv)
+ self.reg_cache = {}
+
+ def hook_w(self, addr, val, width, **kwargs):
+ self.hv.log(f"PMGR: W {addr:#x} <- {val:#x}")
+ #print("-> ignored")
+ super().hook_w(addr, val, width, **kwargs)
+
+ def hook_r(self, addr, width, **kwargs):
+ val = super().hook_r(addr, width, **kwargs)
+ self.hv.log(f"PMGR: R {addr:#x} = {val:#x}")
+ return val
+
+ def evt_rw(self, evt):
+ if not evt.flags.WRITE:
+ self.reg_cache[evt.addr] = evt.data
+ cb = self.ranges.lookup(evt.addr)
+ cb[0](evt, *cb[1:])
+ self.reg_cache[evt.addr] = evt.data
+
+ def event_default(self, evt, start, name):
+ t = "W" if evt.flags.WRITE else "R"
+ m = "+" if evt.flags.MULTI else " "
+ data = f"{evt.data:#x}"
+ if evt.flags.WRITE:
+ data = f"{evt.data:#x}"
+ old = self.reg_cache.get(evt.addr, None)
+ if old is not None:
+ data = f"{old:#x} -> {evt.data:#x}"
+ self.hv.log(f"[cpu{evt.flags.CPU}][0x{evt.pc:016x}] PMGR: {t}.{1<<evt.flags.WIDTH:<2}{m} " +
+ f"0x{evt.addr:x} ({name} + {evt.addr - start:#04x}) = {data}", show_cpu=False)
+
+ def build_table(self, hv):
+ self.ranges = ScalarRangeMap()
+ self.state_regs = {}
+
+ starts = {}
+ for reg in (0, 1):
+ addr, size = self.dev.get_reg(reg)
+ self.ranges[addr:addr + size] = self.event_default, addr, f"reg[{reg}]"
+
+ for i, ps in enumerate(self.dev.ps_regs):
+ addr = self.dev.get_reg(ps.reg)[0] + ps.offset
+ for idx in range(32):
+ ps_addr = addr + idx * 8
+ self.ranges[ps_addr:ps_addr + 8] = self.event_default, ps_addr, f"ps[{i}][{idx}]"
+
+ for i, dev in enumerate(self.dev.devices):
+ ps = self.dev.ps_regs[dev.psreg]
+ if dev.psidx or dev.psreg:
+ addr = self.dev.get_reg(ps.reg)[0] + ps.offset + dev.psidx * 8
+ self.state_regs[addr] = dev.name
+ if dev.name in self.IGNORED:
+ self.ignored_ranges.append((addr, 8))
+ self.ranges[addr:addr + 8] = self.event_default, addr, f"{dev.name}.pstate"
+
+ def start(self):
+ self.hv.clear_tracers(self.ident)
+
+ for reg in (0, 1):
+ addr, size = self.dev.get_reg(reg)
+ self.trace(addr, size, TraceMode.WSYNC, read=False)
+
+ for ps in self.dev.ps_regs:
+ addr = self.dev.get_reg(ps.reg)[0] + ps.offset
+ self.trace(addr, 0x100, TraceMode.WSYNC)
+
+ for lane in range(8):
+ addr = 0x200200000 + 0x40000 * lane
+ self.trace(addr, 0x40000, TraceMode.HOOK)
+ #for reg in (23,):
+ #addr, size = self.dev.get_reg(reg)
+ #self.trace(addr, 0x20000, TraceMode.SYNC)
+ for addr, size in self.ignored_ranges:
+ self.trace(addr, size, TraceMode.OFF)
+
+pmgr_tracer = PMGRTracer(hv)
+pmgr_tracer.start()
diff --git a/tools/proxyclient/hv/trace_prores.py b/tools/proxyclient/hv/trace_prores.py
new file mode 100644
index 0000000..05acb4a
--- /dev/null
+++ b/tools/proxyclient/hv/trace_prores.py
@@ -0,0 +1,88 @@
+from m1n1.trace import ADTDevTracer
+from m1n1.trace.dart8110 import DART8110Tracer
+from m1n1.hw.prores import *
+from m1n1.utils import *
+import struct
+
+p.pmgr_adt_clocks_enable('/arm-io/dart-apr0')
+p.pmgr_adt_clocks_enable('/arm-io/dart-apr1')
+
+dart0_tracer = DART8110Tracer(hv, "/arm-io/dart-apr0", verbose=1)
+dart0_tracer.start()
+print(dart0_tracer)
+dart1_tracer = DART8110Tracer(hv, "/arm-io/dart-apr1", verbose=1)
+dart1_tracer.start()
+print(dart1_tracer)
+
+
+class ProResTracer(ADTDevTracer):
+ DEFAULT_MODE = TraceMode.SYNC
+ REGMAPS = [ProResRegs]
+ NAMES = ['prores']
+
+ def __init__(self, hv, devpath, dart_tracer):
+ super().__init__(hv, devpath, verbose=3)
+ self.dart_tracer = dart_tracer
+
+ def w_DR_SIZE(self, val):
+ self._dr_size = val
+
+ def w_DR_ADDR_HI(self, val):
+ self._dr_addr_hi = val
+
+ def w_DR_ADDR_LO(self, val):
+ self._dr_addr_lo = val
+
+ def w_DR_TAIL(self, val):
+ self.log(f"DR_TAIL = {val}")
+ self._dr_tail = val
+
+ def w_DR_HEAD(self, val):
+ self.log(f"DR_HEAD = {val}")
+ self.dart_tracer.dart.dump_all()
+
+ dr_addr = int(self._dr_addr_hi) << 32 | int(self._dr_addr_lo)
+ dr_size = int(self._dr_size)
+ self.log(f"desc ring @ {dr_addr:016X} sz {dr_size:08X}")
+
+ dr = self.dart_tracer.dart.ioread(0, dr_addr, dr_size)
+ chexdump(dr)
+
+ # FIXME there are other descriptor types
+ # also, what if there are multiple in the ring?
+ dr_head = int(val)
+ dr_tail = int(self._dr_tail)
+
+ if dr_head - dr_tail == 0x180:
+ desc = EncodeNotRawDescriptor._make(struct.unpack(ENCODE_NOT_RAW_STRUCT, dr[dr_tail:dr_head]))
+ print(desc)
+
+ p0_iova = desc.luma_iova
+ p1_iova = desc.chroma_iova
+ p2_iova = desc.alpha_iova
+
+ if p0_iova:
+ print(f"P0 iova {p0_iova:016X}")
+ data = self.dart_tracer.dart.ioread(0, p0_iova, 0x1000)
+ chexdump(data)
+ if p1_iova:
+ print(f"P1 iova {p1_iova:016X}")
+ data = self.dart_tracer.dart.ioread(0, p1_iova, 0x1000)
+ chexdump(data)
+ if p2_iova:
+ print(f"P2 iova {p2_iova:016X}")
+ data = self.dart_tracer.dart.ioread(0, p2_iova, 0x1000)
+ chexdump(data)
+
+
+ProResTracer = ProResTracer._reloadcls()
+
+p.pmgr_adt_clocks_enable('/arm-io/apr0')
+p.pmgr_adt_clocks_enable('/arm-io/apr1')
+
+tracer0 = ProResTracer(hv, '/arm-io/apr0', dart0_tracer)
+tracer0.start()
+print(tracer0)
+tracer1 = ProResTracer(hv, '/arm-io/apr1', dart1_tracer)
+tracer1.start()
+print(tracer1)
diff --git a/tools/proxyclient/hv/trace_smc.py b/tools/proxyclient/hv/trace_smc.py
new file mode 100644
index 0000000..2a1efa6
--- /dev/null
+++ b/tools/proxyclient/hv/trace_smc.py
@@ -0,0 +1,108 @@
+# SPDX-License-Identifier: MIT
+
+import struct
+
+from enum import IntEnum
+
+from m1n1.proxyutils import RegMonitor
+from m1n1.utils import *
+from m1n1.trace.dart import DARTTracer
+from m1n1.trace.asc import ASCTracer, EP, EPState, msg, msg_log, DIR
+from m1n1.fw.smc import *
+
+ASCTracer = ASCTracer._reloadcls()
+
+class SMCEpTracer(EP):
+ BASE_MESSAGE = SMCMessage
+
+ def __init__(self, tracer, epid):
+ super().__init__(tracer, epid)
+ self.state.sram_addr = None
+ self.state.verbose = 1
+ self.state.rb = {}
+
+ Initialize = msg_log(SMC_INITIALIZE, DIR.TX, SMCInitialize)
+ Notification = msg_log(SMC_NOTIFICATION, DIR.RX)
+
+ @msg(SMC_WRITE_KEY, DIR.TX, SMCWriteKey)
+ def WriteKey(self, msg):
+ key = msg.KEY.to_bytes(4, byteorder="big").decode("ascii")
+ self.state.rb[msg.ID] = msg.TYPE, key, msg.SIZE
+ data = self.hv.iface.readmem(self.state.sram_addr, msg.SIZE)
+ self.log(f"[{msg.ID:x}] >W: <{key}> = {data.hex()} ({msg.SIZE})")
+ return True
+
+ @msg(SMC_READ_KEY, DIR.TX, SMCReadKey)
+ def ReadKey(self, msg):
+ key = msg.KEY.to_bytes(4, byteorder="big").decode("ascii")
+ self.state.rb[msg.ID] = msg.TYPE, key, msg.SIZE
+ self.log(f"[{msg.ID:x}] >R: <{key}> = ... ({msg.SIZE})")
+ return True
+
+ @msg(SMC_RW_KEY, DIR.TX, SMCReadWriteKey)
+ def ReadKeyPayload(self, msg):
+ key = msg.KEY.to_bytes(4, byteorder="big").decode("ascii")
+ self.state.rb[msg.ID] = msg.TYPE, key, msg.RSIZE
+ data = self.hv.iface.readmem(self.state.sram_addr, msg.WSIZE)
+ self.log(f"[{msg.ID:x}] >RP: <{key}> = {data.hex()} ({msg.WSIZE, msg.RSIZE})")
+ return True
+
+ @msg(SMC_GET_KEY_INFO, DIR.TX, SMCGetKeyInfo)
+ def GetInfo(self, msg):
+ key = msg.KEY.to_bytes(4, byteorder="big").decode("ascii")
+ self.state.rb[msg.ID] = msg.TYPE, key, None
+ self.log(f"[{msg.ID:x}] >KInfo: <{key}>")
+ return True
+
+ @msg(SMC_GET_KEY_BY_INDEX, DIR.TX, SMCGetKeyByIndex)
+ def GetKeyByIndex(self, msg):
+ self.state.rb[msg.ID] = msg.TYPE, msg.INDEX, None
+ self.log(f"[{msg.ID:x}] >KIdx: <{msg.INDEX}>")
+ return True
+
+ @msg(None, DIR.RX, Register64)
+ def RXMsg(self, msg):
+ if self.state.sram_addr is None:
+ self.log(f"SRAM address: {msg.value:#x}")
+ self.state.sram_addr = msg.value
+ return True
+
+ msg = SMCResult(msg.value)
+ if msg.RESULT != 0:
+ self.log(f"[{msg.ID:x}] <Err: 0x{msg.RESULT:02x}")
+ return True
+
+ if msg.ID in self.state.rb:
+ msgtype, key, size = self.state.rb.pop(msg.ID)
+ if msgtype in (SMC_READ_KEY, SMC_RW_KEY):
+ if size <= 4:
+ data = hex(msg.VALUE)
+ else:
+ data = self.hv.iface.readmem(self.state.sram_addr, msg.SIZE).hex()
+ self.log(f"[{msg.ID:x}] <R: <{key}> = {data}")
+ return True
+
+ elif msgtype == SMC_GET_KEY_INFO:
+ data = self.hv.iface.readmem(self.state.sram_addr, 6)
+ size, type, flags = struct.unpack("B4sB", data)
+ self.log(f"[{msg.ID:x}] <Info: <{key}>: size={size} type={type.decode('ascii')} flags={flags:#x}")
+ return True
+
+ elif msgtype == SMC_GET_KEY_BY_INDEX:
+ kname = msg.VALUE.to_bytes(4, byteorder="little").decode("ascii")
+ self.log(f"[{msg.ID:x}] <Key @{key}: <{kname}>")
+ return True
+
+ self.log(f"[{msg.ID:x}] <OK {msg!r}")
+ return True
+
+class SMCTracer(ASCTracer):
+ ENDPOINTS = {
+ 0x20: SMCEpTracer
+ }
+
+ def handle_msg(self, direction, r0, r1):
+ super().handle_msg(direction, r0, r1)
+
+smc_tracer = SMCTracer(hv, "/arm-io/smc", verbose=1)
+smc_tracer.start()
diff --git a/tools/proxyclient/hv/trace_wlan.py b/tools/proxyclient/hv/trace_wlan.py
new file mode 100644
index 0000000..1e4c4b1
--- /dev/null
+++ b/tools/proxyclient/hv/trace_wlan.py
@@ -0,0 +1,446 @@
+import struct
+from construct import *
+
+from m1n1.utils import irange
+from m1n1.hw.dart import DART
+from m1n1.utils import chexdump
+from m1n1.proxyutils import RegMonitor
+from m1n1.constructutils import *
+
+from m1n1.trace.pcie import *
+
+PCIeDevTracer = PCIeDevTracer._reloadcls()
+
+mon = RegMonitor(hv.u)
+
+class WLANCfgSpace(PCICfgSpace):
+ BAR0_WINDOW = 0x80, Register32
+ WRAPPERBASE = 0x70, Register32
+ INTSTATUS = 0x90, Register32
+ INTMASK = 0x94, Register32
+ SBMBX = 0x98, Register32
+ LINK_STATUS_CTRL = 0xbc, Register32
+
+class WLANBAR0(RegMap):
+ INTMASK = 0x2024, Register32
+ MAILBOXINT = 0x2048, Register32
+ MAILBOXMASK = 0x204c, Register32
+ CONFIGADDR = 0x2120, Register32
+ CONFIGDATA = 0x2124, Register32
+ H2D_MAILBOX_0 = 0x2140, Register32
+ H2D_MAILBOX_1 = 0x2144, Register32
+
+ # Linux uses these, via offset 0 instead of 0x2000
+ H2D_MAILBOX_0_ALT = 0x140, Register32
+ H2D_MAILBOX_1_ALT = 0x144, Register32
+ H2D_MAILBOX_0_64 = 0xa20, Register32
+ H2D_MAILBOX_1_64 = 0xa24, Register32
+ INTMASK_64 = 0xc14, Register32
+ MAILBOXINT_64 = 0xc30, Register32
+ MAILBOXMASK_64 = 0xc34, Register32
+
+class WLANSRAMEnd(RegMap):
+ PAD = 0x00, Register32
+ SHARED_BASE = 0x04, Register32
+
+class WLANSRAMShared(RegMap):
+ FLAGS = 0, Register32
+ CONSOLE_ADDR = 20, Register32
+ FWID = 28, Register32
+ MAX_RXBUFPOST = 34, Register16
+ RX_DATAOFFSET = 36, Register32
+ HTOD_MB_DATA_ADDR = 40, Register32
+ DTOH_MB_DATA_ADDR = 44, Register32
+ RING_INFO_ADDR = 48, Register32
+ DMA_SCRATCH_LEN = 52, Register32
+ DMA_SCRATCH_ADDR = 56, Register64
+ HOST_SCB_ADDR = 64, Register64
+ HOST_SCB_SIZE = 72, Register32
+ BUZZ_DBG_PTR = 76, Register32
+ FLAGS2 = 80, Register32
+ HOST_CAP = 84, Register32
+ HOST_TRAP_ADDR = 88, Register64
+ DEVICE_FATAL_LOGBUF_START = 96, Register32
+ HOFFLOAD_ADDR = 100, Register64
+ FLAGS3 = 108, Register32
+ HOST_CAP2 = 112, Register32
+ HOST_CAP3 = 116, Register32
+
+class WLANSRAMRingInfo(RegMap):
+ RINGMEM = 0x00, Register32
+ H2D_W_IDX_PTR = 0x04, Register32
+ H2D_R_IDX_PTR = 0x08, Register32
+ D2H_W_IDX_PTR = 0x0c, Register32
+ D2H_R_IDX_PTR = 0x10, Register32
+ H2D_W_IDX_HOSTADDR = 0x14, Register64
+ H2D_R_IDX_HOSTADDR = 0x1c, Register64
+ D2H_W_IDX_HOSTADDR = 0x24, Register64
+ D2H_R_IDX_HOSTADDR = 0x2c, Register64
+ MAX_FLOWRINGS = 0x34, Register32
+ MAX_SUBMISSIONRINGS = 0x38, Register32
+ MAX_COMPLETIONRINGS = 0x3c, Register32
+
+COMMON_RING_CNT = 5
+
+class WLANSRAMRingMem(RegMap):
+ MAX_ITEM = irange(0x04, COMMON_RING_CNT, 0x10), Register16
+ LEN_ITEMS = irange(0x06, COMMON_RING_CNT, 0x10), Register16
+ BASE_ADDR = irange(0x08, COMMON_RING_CNT, 0x10), Register32
+
+class MsgHeader(ConstructClass):
+ subcon = Struct(
+ "msg_type" / Int8ul,
+ "if_id" / Int8sl,
+ "flags" / Int8ul,
+ "epoch" / Int8ul,
+ "request_id" / Int32ul,
+ )
+
+class ComplHeader(ConstructClass):
+ subcon = Struct(
+ "status" / Int16ul,
+ "ring_id" / Int16ul,
+ )
+
+class IOCtlPtrReq(ConstructClass):
+ subcon = Struct(
+ "cmd" / Int32ul,
+ "trans_id" / Int16ul,
+ "input_buf_len" / Int16ul,
+ "output_buf_len" / Int16ul,
+ "rsvd" / Array(3, Int16ul),
+ "host_input_buf_addr" / Int64ul,
+ )
+
+class IOCtlResp(ConstructClass):
+ subcon = Struct(
+ "compl" / ComplHeader,
+ "resp_len" / Int16ul,
+ "trans_id" / Int16ul,
+ "cmd" / Int32ul,
+ )
+
+class H2DMailboxData(ConstructClass):
+ subcon = Struct(
+ "data" / Int32ul
+ )
+
+class D2HMailboxData(ConstructClass):
+ subcon = Struct(
+ "compl" / ComplHeader,
+ "data" / Int32ul,
+ )
+
+class RingMessage(ConstructClass):
+ subcon = Struct(
+ "hdr" / MsgHeader,
+ "payload" / Switch(this.hdr.msg_type, {
+ 0x09: IOCtlPtrReq,
+ 0x0c: IOCtlResp,
+ 0x23: H2DMailboxData,
+ 0x24: D2HMailboxData,
+
+ }, default=HexDump(GreedyBytes))
+ )
+
+class RingState:
+ pass
+
+class WLANRingTracer(PCIeDevTracer):
+ def __init__(self, wlan, info):
+ self.wlan = wlan
+ self.hv = wlan.hv
+ self.p = wlan.hv.p
+ self.u = wlan.hv.u
+ self.ringid = self.RX, self.PTR_IDX
+ if self.ringid in wlan.state.rings:
+ self.state = wlan.state.rings[self.ringid]
+ else:
+ self.state = wlan.state.rings[self.ringid] = RingState()
+ self.state.rptr = 0
+
+ self.info = info
+ assert info.item_size == self.ITEM_SIZE
+ self.base_addr = info.base_addr
+ self.count = info.count
+
+ if self.RX:
+ d2h_paddr = self.wlan.iotranslate(self.wlan.state.d2h_w_idx_ha + 4 * self.PTR_IDX, 4)[0][0]
+ assert d2h_paddr is not None
+ self.hv.add_tracer(irange(d2h_paddr, 4), self.wlan.ident, TraceMode.SYNC,
+ read=self.d2h_w_idx_readhook)
+
+ def d2h_w_idx_readhook(self, evt):
+ self.log("W idx read")
+ self.poll()
+
+ def poll(self):
+ if self.RX:
+ wptr = self.wlan.ioread(self.wlan.state.d2h_w_idx_ha + 4 * self.PTR_IDX, 4)
+ else:
+ wptr = self.wlan.ioread(self.wlan.state.h2d_w_idx_ha + 4 * self.PTR_IDX, 4)
+
+ wptr = struct.unpack("<I", wptr)[0]
+
+ while wptr != self.state.rptr:
+ off = self.state.rptr * self.ITEM_SIZE
+ addr = self.base_addr + off
+ data = self.wlan.ioread(addr, self.ITEM_SIZE)
+ self.pkt(data)
+ self.state.rptr = (self.state.rptr + 1) % self.count
+
+ def pkt(self, data):
+ self.log("Got packet:")
+ pkt = RingMessage.parse(data)
+ self.log(pkt)
+ if pkt.hdr.msg_type == 0x09:
+ self.wlan.ioctlptr_req(pkt)
+ if pkt.hdr.msg_type == 0x0c:
+ self.wlan.ioctlresp(pkt)
+
+ def log(self, msg):
+ self.wlan.log(f"[{self.NAME}]{msg!s}")
+
+class WLANControlSubmitRingTracer(WLANRingTracer):
+ NAME = "CTLSubmit"
+ PTR_IDX = 0
+ RX = False
+ ITEM_SIZE = 0x28
+
+class WLANControlCompleteRingTracer(WLANRingTracer):
+ NAME = "CTLCompl"
+ PTR_IDX = 0
+ RX = True
+ ITEM_SIZE = 0x18
+
+class RingInfo:
+ def __init__(self):
+ self.count = None
+ self.item_size = None
+ self.base_addr = None
+
+ def ready(self):
+ return self.count is not None and self.item_size is not None and self.base_addr is not None
+
+class WLANTracer(PCIeDevTracer):
+ DEFAULT_MODE = TraceMode.SYNC
+
+ SRAM_BASE = 0x740000
+ SRAM_SIZE = 0x1f9000
+
+ BARMAPS = [WLANBAR0, None, None]
+ CFGMAP = WLANCfgSpace
+
+ RINGS = [
+ WLANControlSubmitRingTracer,
+ None, # RXPost
+ WLANControlCompleteRingTracer,
+ None, # TX complete
+ None, # RX complete
+ ]
+
+ CMDS = {
+ 1: "GET_VERSION",
+ 2: "UP",
+ 3: "DOWN",
+ 262: "GET_VAR",
+ 263: "SET_VAR",
+ }
+
+ def __init__(self, hv, apcie, bus, dev, fn, dart_path=None, verbose=False):
+ super().__init__(hv, apcie, bus, dev, fn, verbose=verbose)
+ self.u = hv.u
+ self.p = hv.p
+ self.dart_path = dart_path
+ self.dart_dev = None
+ self.dart = None
+ self.rings = {}
+
+ def init_state(self):
+ super().init_state()
+ self.state.shared_base = None
+ self.state.ring_info_base = None
+ self.state.ring_mem_base = None
+ self.state.tcm_base = None
+ self.state.tcm_size = None
+ self.state.ring_info = None
+ self.state.ring_mem = None
+ self.state.ring_info_data = {}
+ self.state.rings = {}
+ self.state.ioctls = {}
+ self.state.h2d_w_idx_ha = None
+ self.state.h2d_r_idx_ha = None
+ self.state.d2h_w_idx_ha = None
+ self.state.d2h_r_idx_ha = None
+
+ def config_dart(self):
+ # Ugly...
+ if self.dart_dev is None:
+ for i in range (16):
+ ttbr = self.dart.regs.TTBR[i, 0].reg
+ if ttbr.VALID:
+ self.log(f"DART device: {i}")
+ self.dart_dev = i
+ break
+ else:
+ raise Exception("Failed to find DART device")
+
+ def ioread(self, addr, size):
+ self.config_dart()
+ return self.dart.ioread(self.dart_dev, addr, size)
+
+ def iotranslate(self, addr, size):
+ self.config_dart()
+ return self.dart.iotranslate(self.dart_dev, addr, size)
+
+ def r_SHARED_BASE(self, base):
+ if base.value & 0xffff == (base.value >> 16) ^ 0xffff:
+ return
+
+ self.state.shared_base = base.value
+ self.update_shared()
+
+ def w_H2D_W_IDX_HOSTADDR(self, addr):
+ self.state.h2d_w_idx_ha = addr.value
+
+ def w_H2D_R_IDX_HOSTADDR(self, addr):
+ self.state.h2d_r_idx_ha = addr.value
+
+ def w_D2H_W_IDX_HOSTADDR(self, addr):
+ self.state.d2h_w_idx_ha = addr.value
+
+ def w_D2H_R_IDX_HOSTADDR(self, addr):
+ self.state.d2h_r_idx_ha = addr.value
+
+ def w_MAX_ITEM(self, val, index):
+ info = self.state.ring_info_data.setdefault(index, RingInfo())
+ info.count = val.value
+ self.update_ring(index)
+
+ def w_LEN_ITEMS(self, val, index):
+ info = self.state.ring_info_data.setdefault(index, RingInfo())
+ info.item_size = val.value
+ self.update_ring(index)
+
+ def w_BASE_ADDR(self, val, index):
+ info = self.state.ring_info_data.setdefault(index, RingInfo())
+ info.base_addr = val.value
+ self.update_ring(index)
+
+ def update_ring(self, idx):
+ if idx not in self.state.ring_info_data:
+ return
+ info = self.state.ring_info_data[idx]
+ if not info.ready():
+ return
+
+ if idx in self.rings:
+ return
+
+ if idx > len(self.RINGS):
+ return
+
+ ringcls = self.RINGS[idx]
+
+ if ringcls is None:
+ return
+
+ self.rings[idx] = ringcls(self, info)
+
+ def w_H2D_MAILBOX_0(self, val):
+ ring = self.rings.get(2, None)
+ if ring is not None:
+ ring.poll()
+
+ ring = self.rings.get(0, None)
+ if ring is not None:
+ ring.poll()
+
+ w_H2D_MAILBOX_0_64 = w_H2D_MAILBOX_0
+ w_H2D_MAILBOX_0_ALT = w_H2D_MAILBOX_0
+
+ def ioctlptr_req(self, pkt):
+ data = self.ioread(pkt.payload.host_input_buf_addr, pkt.payload.input_buf_len)
+ cmd = self.CMDS.get(pkt.payload.cmd, "unk")
+ self.log(f"IOCTL request ({cmd}):")
+ chexdump(data, print_fn = self.log)
+ self.state.ioctls[pkt.payload.trans_id] = pkt
+
+ def ioctlresp(self, pkt):
+ req = self.state.ioctls.get(pkt.payload.trans_id, None)
+ if req is None:
+ self.log(f"ERROR: unknown transaction ID {pkt.payload.trans_id:#x}")
+ return
+
+ data = self.ioread(req.payload.host_input_buf_addr, req.payload.output_buf_len)
+ cmd = self.CMDS.get(pkt.payload.cmd, "unk")
+ self.log(f"IOCTL response ({cmd}):")
+ chexdump(data, print_fn = self.log)
+ del self.state.ioctls[pkt.payload.trans_id]
+
+ def trace_bar(self, idx, start, size):
+ if idx != 2:
+ return super().trace_bar(idx, start, size)
+
+ self.state.tcm_base = start
+ self.state.tcm_size = size
+
+ self.update_tcm_tracers()
+
+ def update_tcm_tracers(self):
+ if self.state.tcm_base is None:
+ return
+
+ if self.dart is None:
+ self.dart = DART.from_adt(self.u, self.dart_path)
+
+ self.trace_regmap(self.state.tcm_base + self.SRAM_BASE + self.SRAM_SIZE - 8, 8,
+ WLANSRAMEnd, name="sram")
+
+ def update_shared(self):
+ base = self.state.shared_base
+ if base is None:
+ return
+
+ if self.state.ring_info_base is None:
+ self.shared = WLANSRAMShared(self.hv.u, self.state.tcm_base + base)
+
+ self.log("Reading shared info")
+ self.shared.dump_regs()
+
+ self.state.ring_info_base = self.shared.RING_INFO_ADDR.val
+
+ if self.state.ring_mem_base is None:
+ self.ring_info = WLANSRAMRingInfo(self.hv.u,
+ self.state.tcm_base + self.state.ring_info_base)
+ self.log("Reading ring info")
+ self.ring_info.dump_regs()
+
+ self.state.ring_mem_base = self.ring_info.RINGMEM.val
+
+ self.trace_regmap(self.state.tcm_base + base, 0x100,
+ WLANSRAMShared, name="shared")
+
+ self.trace_regmap(self.state.tcm_base + self.state.ring_info_base, 0x40,
+ WLANSRAMRingInfo, name="ringinfo")
+
+ self.ring_mem = WLANSRAMRingMem(self.hv.u,
+ self.state.tcm_base + self.state.ring_mem_base)
+ self.log("Reading ring mem")
+ self.ring_mem.dump_regs()
+
+ self.trace_regmap(self.state.tcm_base + self.state.ring_mem_base,
+ COMMON_RING_CNT * 0x10, WLANSRAMRingMem, name="ringmem")
+
+ def start(self):
+ super().start()
+
+ self.update_tcm_tracers()
+ self.update_shared()
+ for i in range(len(self.RINGS)):
+ self.update_ring(i)
+
+wlan_tracer = WLANTracer(hv, "/arm-io/apcie",
+ 4, 0, 0, "/arm-io/dart-apcie0")
+
+wlan_tracer.start()
diff --git a/tools/proxyclient/hv/trace_z2.py b/tools/proxyclient/hv/trace_z2.py
new file mode 100644
index 0000000..c52e80e
--- /dev/null
+++ b/tools/proxyclient/hv/trace_z2.py
@@ -0,0 +1,221 @@
+# SPDX-License-Identifier: MIT
+
+import struct
+from construct import *
+
+from m1n1.hv import TraceMode
+from m1n1.proxyutils import RegMonitor
+from m1n1.utils import *
+
+from m1n1.trace import ADTDevTracer
+from m1n1.trace.asc import ASCTracer, ASCRegs, EP, EPState, msg, msg_log, DIR
+from m1n1.trace.dart import DARTTracer
+from m1n1.trace.gpio import GPIOTracer
+from m1n1.trace.spi import SPITracer
+
+DARTTracer = DARTTracer._reloadcls()
+ASCTracer = ASCTracer._reloadcls()
+SPITracer = SPITracer._reloadcls()
+
+# SPI HID transport tracer for 2021 macbook models
+
+kbd_node = None
+for node in hv.adt.walk_tree():
+ try:
+ if node.compatible[0] == "spi-1,spimc":
+ for c in node:
+ try:
+ if c.compatible[0] == "hid-transport,k1":
+ kbd_node = c
+ break
+ except AttributeError:
+ continue
+ except AttributeError:
+ continue
+ if kbd_node is not None:
+ break
+
+
+class Z2Tracer(SPITracer):
+ def start(self):
+ super().start()
+ self.txbuffer = []
+ self.rxbuffer = []
+ self.want_bytes = 0
+ self.state = Z2Tracer.preboot
+ def w_TXDATA(self, data):
+ self.txbuffer.append(data.value)
+ self.check_msg_finished()
+ def r_RXDATA(self, data):
+ self.rxbuffer.append(data.value)
+ self.check_msg_finished()
+ def check_msg_finished(self):
+ if min(len(self.txbuffer), len(self.rxbuffer)) < self.want_bytes:
+ return
+ self.state(self)
+ def bad_state(self):
+ pass
+ def error(self):
+ self.log(f"RXBUF {' '.join(hex(x) for x in self.rxbuffer)}")
+ self.log(f"TXBUF {' '.join(hex(x) for x in self.txbuffer)}")
+ self.log(f"state: {self.state}")
+ self.log("Tracer desynchronized, shutting down")
+ self.state = Z2Tracer.bad_state
+ def consume_bytes(self, n):
+ self.txbuffer = self.txbuffer[n:]
+ self.rxbuffer = self.rxbuffer[n:]
+ def preboot(self):
+ if self.txbuffer[0] == 0:
+ self.want_bytes = 4
+ self.state = Z2Tracer.init_zeros
+ elif self.txbuffer[0] == 0x1e:
+ self.want_bytes = 16
+ self.state = Z2Tracer.processing_init_data
+ else:
+ self.error()
+ def init_zeros(self):
+ self.log("sent 4 zeroes")
+ self.consume_bytes(4)
+ self.state = Z2Tracer.preboot
+ def processing_init_data(self):
+ self.log("Sent init data")
+ self.want_bytes = 2
+ self.consume_bytes(16)
+ self.state = Z2Tracer.main_hbpp
+ def main_hbpp(self):
+ if self.txbuffer[0] == 0x1a and self.txbuffer[1] == 0xa1:
+ self.log("Sent int ack")
+ self.consume_bytes(2)
+ elif self.txbuffer[0] == 0x18 and self.txbuffer[1] == 0xe1:
+ self.log("Sent nop")
+ self.consume_bytes(2)
+ elif self.txbuffer[0] == 0x1f and self.txbuffer[1] == 0x01:
+ self.log("Sent request cal")
+ self.consume_bytes(2)
+ elif self.txbuffer[0] == 0x30 and self.txbuffer[1] == 0x01:
+ self.state = Z2Tracer.send_blob_cmd
+ self.want_bytes = 10
+ elif self.txbuffer[0] == 0x1e and self.txbuffer[1] == 0x33:
+ self.state = Z2Tracer.send_rmw_cmd
+ self.want_bytes = 16
+ elif self.txbuffer[0] == 0xee and self.txbuffer[1] == 0x00:
+ self.state = Z2Tracer.main_z2
+ self.want_bytes = 16
+ else:
+ self.error()
+ def send_blob_cmd(self):
+ length = (self.txbuffer[2] << 8 | self.txbuffer[3]) * 4
+ self.consume_bytes(10)
+ self.want_bytes = length + 4
+ self.log(f"Sending blob of length {length}")
+ self.state = Z2Tracer.send_blob_tail
+ def send_blob_tail(self):
+ self.log("Finished sendind blob")
+ self.consume_bytes(self.want_bytes)
+ self.want_bytes = 2
+ self.state = Z2Tracer.main_hbpp
+ def send_rmw_cmd(self):
+ self.log('Sent RMW command')
+ self.want_bytes = 2
+ self.consume_bytes(16)
+ self.state = Z2Tracer.main_hbpp
+ def main_z2(self):
+ if self.txbuffer[0] == 0xee:
+ self.log("sent wake cmd")
+ self.consume_bytes(16)
+ elif self.txbuffer[0] == 0xe2:
+ self.log("sent get device info cmd")
+ self.consume_bytes(16)
+ self.state = Z2Tracer.read_device_info_reply
+ elif self.txbuffer[0] == 0xeb:
+ length = (self.rxbuffer[1] | (self.rxbuffer[2] << 8)) + 5
+ length = (length + 3) & (-4)
+ self.consume_bytes(16)
+ self.want_bytes = length
+ self.state = Z2Tracer.read_interrupt_data
+ elif self.txbuffer[0] == 0xe3:
+ self.log(f"got report info for {self.txbuffer[1]}, len is {self.rxbuffer[3]}")
+ self.consume_bytes(16)
+ elif self.txbuffer[0] == 0xe7:
+ self.want_bytes = self.txbuffer[3] + 5
+ self.consume_bytes(16)
+ self.state = Z2Tracer.reading_report_long
+ elif self.txbuffer[0] == 0xe6:
+ self.consume_bytes(16)
+ self.state = Z2Tracer.read_report_reply
+ else:
+ self.error()
+ def reading_report_long(self):
+ self.log(f"got report {' '.join(hex(x) for x in self.rxbuffer)}")
+ self.consume_bytes(self.want_bytes)
+ self.want_bytes = 16
+ self.state = Z2Tracer.main_z2
+ def read_interrupt_data(self):
+ data = self.rxbuffer[5:]
+ tstamp2 = data[4] | (data[5] << 8) | (data[6] << 16)
+ tx = [f"TS1 {hex(data[1])} TS2 {tstamp2} UNK1: {mxformat(data[7:16])} UNK2: {mxformat(data[17:24])}"]
+ if len(data) >= 16:
+ ntouch = data[16]
+ for i in range(ntouch):
+ ptr = 24 + 30 * i
+ finger = data[ptr]
+ state = data[ptr + 1]
+ x = data[ptr + 4] | (data[ptr + 5] << 8)
+ y = data[ptr + 6] | (data[ptr + 7] << 8)
+ wj = data[ptr + 12] | (data[ptr + 13] << 8)
+ wn = data[ptr + 14] | (data[ptr + 15] << 8)
+ dg = data[ptr + 16] | (data[ptr + 17] << 8)
+ prs = data[ptr + 18] | (data[ptr + 19] << 8)
+ tx.append(f"F: {hex(finger)} S: {hex(state)} X: {x} Y: {y} MAJ: {wj} MIN: {wn} ANG: {dg} PRS: {prs} UNK1: {mxformat(data[ptr + 2:ptr+4])} UNK2: {mxformat(data[ptr + 8:ptr+12])} UNK3: {mxformat(data[ptr + 20:ptr+30])}")
+ self.log(';'.join(tx))
+ else:
+ self.log(f"??? {mxformat(data)}")
+ self.consume_bytes(self.want_bytes)
+ self.want_bytes = 16
+ self.state = Z2Tracer.main_z2
+ def read_device_info_reply(self):
+ self.log(f"got device info {' '.join(hex(x) for x in self.rxbuffer[:16])}")
+ self.consume_bytes(16)
+ self.state = Z2Tracer.main_z2
+ def read_report_reply(self):
+ self.log(f"got report {' '.join(hex(x) for x in self.rxbuffer[:16])}")
+ self.consume_bytes(16)
+ self.state = Z2Tracer.main_z2
+
+def mxformat(ls):
+ return ''.join(xformat(x) for x in ls)
+def xformat(x):
+ x = hex(x)[2:]
+ if len(x) == 1:
+ x = '0' + x
+ return x
+
+
+
+# trace interrupts
+aic_phandle = getattr(hv.adt["/arm-io/aic"], "AAPL,phandle")
+spi_node = kbd_node._parent
+
+#if getattr(spi_node, "interrupt-parent") == aic_phandle:
+# for irq in getattr(spi_node, "interrupts"):
+# hv.trace_irq(node.name, irq, 1, hv.IRQTRACE_IRQ)
+#for irq in hv.adt['/arm-io/gpio'].interrupts:
+# hv.trace_irq('/arm-io/gpio', irq, 1, hv.IRQTRACE_IRQ)
+
+spi_tracer = Z2Tracer(hv, "/arm-io/" + spi_node.name)
+spi_tracer.start()
+
+spi_pins_nub = {
+ 0x0: "clock32khz",
+}
+
+#gpio_tracer_nub = GPIOTracer(hv, "/arm-io/nub-gpio", spi_pins_nub, verbose=0)
+#gpio_tracer_nub.start()
+
+spi_pins = {
+ 0x6d: "enable_cs",
+ 0x8b: "reset"
+}
+
+#gpio_tracer = GPIOTracer(hv, "/arm-io/gpio", spi_pins, verbose=0)
+#gpio_tracer.start()
diff --git a/tools/proxyclient/m1n1/__init__.py b/tools/proxyclient/m1n1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/proxyclient/m1n1/__init__.py
diff --git a/tools/proxyclient/m1n1/adt.py b/tools/proxyclient/m1n1/adt.py
new file mode 100644
index 0000000..8cc7ebe
--- /dev/null
+++ b/tools/proxyclient/m1n1/adt.py
@@ -0,0 +1,713 @@
+# SPDX-License-Identifier: MIT
+import itertools, fnmatch, sys
+from construct import *
+import sys
+
+from .utils import AddrLookup, FourCC, SafeGreedyRange
+
+__all__ = ["load_adt"]
+
+ADTPropertyStruct = Struct(
+ "name" / PaddedString(32, "ascii"),
+ "size" / Int32ul,
+ "value" / Bytes(this.size & 0x7fffffff)
+)
+
+ADTNodeStruct = Struct(
+ "property_count" / Int32ul,
+ "child_count" / Int32ul,
+ "properties" / Array(this.property_count, Aligned(4, ADTPropertyStruct)),
+ "children" / Array(this.child_count, LazyBound(lambda: ADTNodeStruct))
+)
+
+ADTStringList = SafeGreedyRange(CString("ascii"))
+
+ADT2Tuple = Array(2, Hex(Int64ul))
+ADT3Tuple = Array(3, Hex(Int64ul))
+
+Function = Struct(
+ "phandle" / Int32ul,
+ "name" / FourCC,
+ "args" / SafeGreedyRange(Int32ul),
+)
+
+STD_PROPERTIES = {
+ "cpu-impl-reg": ADT2Tuple,
+ "name": CString("ascii"),
+ "compatible": ADTStringList,
+ "model": CString("ascii"),
+ "#size-cells": Int32ul,
+ "#address-cells": Int32ul,
+ "clock-ids": SafeGreedyRange(Int32ul),
+ "clock-gates": SafeGreedyRange(Int32ul),
+ "power-gates": SafeGreedyRange(Int32ul),
+}
+
+PMAPIORanges = SafeGreedyRange(Struct(
+ "addr" / Hex(Int64ul),
+ "size" / Hex(Int64ul),
+ "flags" / Hex(Int32ul),
+ "name" / FourCC,
+))
+
+PMGRPSRegs = SafeGreedyRange(Struct(
+ "reg" / Int32ul,
+ "offset" / Hex(Int32ul),
+ "mask" / Hex(Int32ul),
+))
+
+PMGRPerfRegs = SafeGreedyRange(Struct(
+ "reg" / Int32ul,
+ "offset" / Hex(Int32ul),
+ "size" / Hex(Int32ul),
+ "unk" / Int32ul,
+))
+
+PMGRPWRGateRegs = SafeGreedyRange(Struct(
+ "reg" / Int32ul,
+ "offset" / Hex(Int32ul),
+ "mask" / Hex(Int32ul),
+ "unk" / Hex(Int32ul),
+))
+
+PMGRDeviceFlags = BitStruct(
+ "b7" / Flag,
+ "b6" / Flag,
+ "perf" / Flag,
+ "no_ps" / Flag,
+ "critical" / Flag,
+ "b2" / Flag,
+ "notify_pmp" / Flag,
+ "on" / Flag,
+)
+
+PMGRDevices = SafeGreedyRange(Struct(
+ "flags" / PMGRDeviceFlags,
+ "unk1_0" / Int8ul,
+ "unk1_1" / Int8ul,
+ "unk1_2" / Int8ul,
+ "parents" / Array(2, Int16ul),
+ "perf_idx" / Int8ul,
+ "perf_block" / Int8ul,
+ "psidx" / Int8ul,
+ "psreg" / Int8ul,
+ "unk2_0" / Int16ul,
+ "pd" / Int8ul,
+ "ps_cfg16" / Int8ul,
+ Const(0, Int32ul),
+ Const(0, Int32ul),
+ "unk2_3" / Int16ul,
+ "id" / Int16ul,
+ "unk3" / Int32ul,
+ "name" / PaddedString(16, "ascii")
+))
+
+PMGRClocks = SafeGreedyRange(Struct(
+ "perf_idx" / Int8ul,
+ "perf_block" / Int8ul,
+ "unk" / Int8ul,
+ "id" / Int8ul,
+ Const(0, Int32ul),
+ "name" / PaddedString(16, "ascii"),
+))
+
+PMGRPowerDomains = SafeGreedyRange(Struct(
+ "unk" / Const(0, Int8ul),
+ "perf_idx" / Int8ul,
+ "perf_block" / Int8ul,
+ "id" / Int8ul,
+ Const(0, Int32ul),
+ "name" / PaddedString(16, "ascii"),
+))
+
+PMGRDeviceBridges = SafeGreedyRange(Struct(
+ "idx" / Int32ub,
+ "subdevs" / HexDump(Bytes(0x48)),
+))
+
+PMGREvents = SafeGreedyRange(Struct(
+ "unk1" / Int8ul,
+ "unk2" / Int8ul,
+ "unk3" / Int8ul,
+ "id" / Int8ul,
+ "perf2_idx" / Int8ul,
+ "perf2_block" / Int8ul,
+ "perf_idx" / Int8ul,
+ "perf_block" / Int8ul,
+ "name" / PaddedString(16, "ascii"),
+))
+
+GPUPerfState = Struct(
+ "freq" / Int32ul,
+ "volt" / Int32ul,
+)
+
+SpeakerConfig = Struct(
+ "rx_slot" / Int8ul,
+ "amp_gain" / Int8ul,
+ "vsense_slot" / Int8ul,
+ "isense_slot" / Int8ul,
+)
+
+DCBlockerConfig = Struct(
+ "dc_blk0" / Hex(Int8ul),
+ "dc_blk1" / Hex(Int8ul),
+ "pad" / Hex(Int16ul),
+)
+
+Coef = ExprAdapter(Int32ul,
+ lambda x, ctx: (x - ((x & 0x1000000) << 1)) / 65536,
+ lambda x, ctx: int(round(x * 65536)) & 0x1ffffff)
+
+MTRPolynomFuseAGX = GreedyRange(Struct(
+ "id" / Int32ul,
+ "data" / Prefixed(Int32ul, GreedyRange(Coef)),
+))
+
+DEV_PROPERTIES = {
+ "pmgr": {
+ "*": {
+ "clusters": SafeGreedyRange(Int32ul),
+ "devices": PMGRDevices,
+ "ps-regs": PMGRPSRegs,
+ "perf-regs": PMGRPerfRegs,
+ "pwrgate-regs": PMGRPWRGateRegs,
+ "power-domains": PMGRPowerDomains,
+ "clocks": PMGRClocks,
+ "device-bridges": PMGRDeviceBridges,
+ "voltage-states*": SafeGreedyRange(Int32ul),
+ "events": PMGREvents,
+ "mtr-polynom-fuse-agx": MTRPolynomFuseAGX,
+ }
+ },
+ "clpc": {
+ "*": {
+ "events": SafeGreedyRange(Int32ul),
+ "devices": SafeGreedyRange(Int32ul),
+ }
+ },
+ "soc-tuner": {
+ "*": {
+ "device-set-*": SafeGreedyRange(Int32ul),
+ "mcc-configs": SafeGreedyRange(Int32ul),
+ }
+ },
+ "mcc": {
+ "*": {
+ "dramcfg-data": SafeGreedyRange(Int32ul),
+ "config-data": SafeGreedyRange(Int32ul),
+ }
+ },
+ "*pmu*": {
+ "*": {
+ "info-*name*": CString("ascii"),
+ "info-*": SafeGreedyRange(Hex(Int32ul)),
+ },
+ },
+ "stockholm-spmi": {
+ "*": {
+ "required-functions": ADTStringList,
+ },
+ },
+ "sgx": {
+ "*": {
+ "perf-states*": SafeGreedyRange(GPUPerfState),
+ "*-kp": Float32l,
+ "*-ki": Float32l,
+ "*-ki-*": Float32l,
+ "*-gain*": Float32l,
+ "*-scale*": Float32l,
+ }
+ },
+ "arm-io": {
+ "*": {
+ "clock-frequencies": SafeGreedyRange(Int32ul),
+ "clock-frequencies-regs": SafeGreedyRange(Hex(Int64ul)),
+ "clock-frequencies-nclk": SafeGreedyRange(Int32ul),
+ },
+ },
+ "defaults": {
+ "*": {
+ "pmap-io-ranges": PMAPIORanges,
+ }
+ },
+ "audio-*": {
+ "*": {
+ "speaker-config": SafeGreedyRange(SpeakerConfig),
+ "amp-dcblocker-config": DCBlockerConfig,
+ },
+ },
+ "*aop-audio*": {
+ "*": {
+ "clockSource": FourCC,
+ "identifier": FourCC,
+ },
+ },
+ "*alc?/audio-leap-mic*": {
+ "*": {
+ "audio-stream-formatter": FourCC,
+ }
+ }
+}
+
+def parse_prop(node, path, node_name, name, v, is_template=False):
+ t = None
+
+ if is_template:
+ t = CString("ascii")
+
+ dev_props = None
+ for k, pt in DEV_PROPERTIES.items():
+ if fnmatch.fnmatch(path, k):
+ dev_props = pt
+ break
+
+ if not dev_props:
+ for k, pt in DEV_PROPERTIES.items():
+ if fnmatch.fnmatch(node_name, k):
+ dev_props = pt
+ break
+
+ possible_match = False
+ if dev_props:
+ for compat_match, cprops in dev_props.items():
+ for k, pt in cprops.items():
+ if fnmatch.fnmatch(name, k):
+ possible_match = True
+ break
+
+ if possible_match:
+ try:
+ compat = node.compatible[0]
+ except AttributeError:
+ compat = ""
+
+ for compat_match, cprops in dev_props.items():
+ if fnmatch.fnmatch(compat, compat_match):
+ for k, pt in cprops.items():
+ if fnmatch.fnmatch(name, k):
+ t = pt
+ break
+ else:
+ continue
+ break
+
+ if v == b'' or v is None:
+ return None, None
+
+ if name.startswith("function-"):
+ if len(v) == 4:
+ t = FourCC
+ else:
+ t = Function
+
+ if name == "reg" and path != "/device-tree/memory":
+ n = node._parent
+ while n is not None and n._parent is not None:
+ if "ranges" not in n._properties:
+ break
+ n = n._parent
+ else:
+ rs = node._reg_struct
+ if len(v) % rs.sizeof() == 0:
+ t = SafeGreedyRange(rs)
+
+ elif name == "ranges":
+ try:
+ ac, sc = node.address_cells, node.size_cells
+ except AttributeError:
+ return None, v
+ pac, _ = node._parent.address_cells, node._parent.size_cells
+ at = Hex(Int64ul) if ac == 2 else Array(ac, Hex(Int32ul))
+ pat = Hex(Int64ul) if pac == 2 else Array(pac, Hex(Int32ul))
+ st = Hex(Int64ul) if sc == 2 else Array(sc, Hex(Int32ul))
+ t = SafeGreedyRange(Struct("bus_addr" / at, "parent_addr" / pat, "size" / st))
+
+ elif name == "interrupts":
+ # parse "interrupts" as Array of Int32ul, wrong for nodes whose
+ # "interrupt-parent" has "interrupt-cells" = 2
+ # parsing this correctly would require a second pass
+ t = Array(len(v) // 4, Int32ul)
+
+ if t is not None:
+ v = Sequence(t, Terminated).parse(v)[0]
+ return t, v
+
+ if name in STD_PROPERTIES:
+ t = STD_PROPERTIES[name]
+ elif v and v[-1] == 0 and all(0x20 <= i <= 0x7e for i in v[:-1]):
+ t = CString("ascii")
+ elif len(v) == 4:
+ t = Int32ul
+ elif len(v) == 8:
+ t = Int64ul
+ elif len(v) == 16 and all(v[i] == 0 for i in (6, 7, 14, 15)):
+ t = ADT2Tuple
+
+ if t is not None:
+ try:
+ v = Sequence(t, Terminated).parse(v)[0]
+ except:
+ print("Failed to parse:", path, name, v.hex())
+ raise
+
+ return t, v
+
+def build_prop(path, name, v, t=None):
+ if v is None:
+ return b''
+ if t is not None:
+ return t.build(v)
+
+ if isinstance(v, bytes):
+ return v
+
+ if name in STD_PROPERTIES:
+ t = STD_PROPERTIES[name]
+ elif isinstance(v, str):
+ t = CString("ascii")
+ elif isinstance(v, int):
+ if v > 0xffffffff:
+ t = Int64ul
+ else:
+ t = Int32ul
+ elif isinstance(v, float):
+ t = Float32l
+ elif isinstance(v, tuple) and all(isinstance(i, int) for i in v):
+ t = Array(len(v), Int32ul)
+
+ return t.build(v)
+
+class ADTNode:
+ def __init__(self, val=None, path="/", parent=None):
+ self._children = []
+ self._properties = {}
+ self._types = {}
+ self._parent_path = path
+ self._parent = parent
+
+ if val is not None:
+ for p in val.properties:
+ if p.name == "name":
+ _name = p.value.decode("ascii").rstrip("\0")
+ break
+ else:
+ raise ValueError(f"Node in {path} has no name!")
+
+ path = self._parent_path + _name
+
+ for p in val.properties:
+ is_template = bool(p.size & 0x80000000)
+ try:
+ t, v = parse_prop(self, path, _name, p.name, p.value, is_template)
+ self._types[p.name] = t, is_template
+ self._properties[p.name] = v
+ except Exception as e:
+ print(f"Exception parsing {path}.{p.name} value {p.value.hex()}:", file=sys.stderr)
+ raise
+
+ # Second pass
+ for k, (t, is_template) in self._types.items():
+ if t is None:
+ t, v = parse_prop(self, path, _name, k, self._properties[k], is_template)
+ self._types[k] = t, is_template
+ self._properties[k] = v
+
+ for c in val.children:
+ node = ADTNode(c, f"{self._path}/", parent=self)
+ self._children.append(node)
+
+ @property
+ def _path(self):
+ return self._parent_path + self.name
+
+ def __getitem__(self, item):
+ if isinstance(item, str):
+ while item.startswith("/"):
+ item = item[1:]
+ if "/" in item:
+ a, b = item.split("/", 1)
+ return self[a][b]
+ for i in self._children:
+ if i.name == item:
+ return i
+ raise KeyError(f"Child node '{item}' not found")
+ return self._children[item]
+
+ def __setitem__(self, item, value):
+ if isinstance(item, str):
+ while item.startswith("/"):
+ item = item[1:]
+ if "/" in item:
+ a, b = item.split("/", 1)
+ self[a][b] = value
+ return
+ for i, c in enumerate(self._children):
+ if c.name == item:
+ self._children[i] = value
+ break
+ else:
+ self._children.append(value)
+ else:
+ self._children[item] = value
+
+ def __delitem__(self, item):
+ if isinstance(item, str):
+ while item.startswith("/"):
+ item = item[1:]
+ if "/" in item:
+ a, b = item.split("/", 1)
+ del self[a][b]
+ return
+ for i, c in enumerate(self._children):
+ if c.name == item:
+ del self._children[i]
+ return
+ raise KeyError(f"Child node '{item}' not found")
+
+ del self._children[item]
+
+ def __contains__(self, item):
+ if isinstance(item, str):
+ while item.startswith("/"):
+ item = item[1:]
+ if "/" in item:
+ a, b = item.split("/", 1)
+ return b in self[a]
+ for c in self._children:
+ if c.name == item:
+ return True
+ return False
+
+ return item in self._children
+
+ def __getattr__(self, attr):
+ attr = attr.replace("_", "-")
+ attr = attr.replace("--", "_")
+ if attr in self._properties:
+ return self._properties[attr]
+ raise AttributeError(attr)
+
+ def __setattr__(self, attr, value):
+ if attr[0] == "_":
+ self.__dict__[attr] = value
+ return
+ attr = attr.replace("_", "-")
+ attr = attr.replace("--", "_")
+ self._properties[attr] = value
+
+ def __delattr__(self, attr):
+ if attr[0] == "_":
+ del self.__dict__[attr]
+ return
+ del self._properties[attr]
+
+ def getprop(self, name, default=None):
+ return self._properties.get(name, default)
+
+ @property
+ def address_cells(self):
+ try:
+ return self._properties["#address-cells"]
+ except KeyError:
+ raise AttributeError("#address-cells")
+
+ @property
+ def size_cells(self):
+ try:
+ return self._properties["#size-cells"]
+ except KeyError:
+ raise AttributeError("#size-cells")
+
+ @property
+ def interrupt_cells(self):
+ try:
+ return self._properties["#interrupt-cells"]
+ except KeyError:
+ raise AttributeError("#interrupt-cells")
+
+ def _fmt_prop(self, k, v):
+ t, is_template = self._types.get(k, (None, False))
+ if is_template:
+ return f"<< {v} >>"
+ elif isinstance(v, ListContainer):
+ return f"[{', '.join(self._fmt_prop(k, i) for i in v)}]"
+ elif isinstance(v, bytes):
+ if all(i == 0 for i in v):
+ return f"zeroes({len(v):#x})"
+ else:
+ return v.hex()
+ elif k.startswith("function-"):
+ if isinstance(v, str):
+ return f"{v}()"
+ elif v is None:
+ return f"None"
+ else:
+ args = []
+ for arg in v.args:
+ b = arg.to_bytes(4, "big")
+ is_ascii = all(0x20 <= c <= 0x7e for c in b)
+ args.append(f"{arg:#x}" if not is_ascii else f"'{b.decode('ascii')}'")
+ return f"{v.phandle}:{v.name}({', '.join(args)})"
+ name.startswith("function-")
+ else:
+ return str(v)
+
+ def __str__(self, t=""):
+ return "\n".join([
+ t + f"{self.name} {{",
+ *(t + f" {k} = {self._fmt_prop(k, v)}" for k, v in self._properties.items() if k != "name"),
+ "",
+ *(i.__str__(t + " ") for i in self._children),
+ t + "}"
+ ])
+
+ def __repr__(self):
+ return f"<ADTNode {self.name}>"
+
+ def __iter__(self):
+ return iter(self._children)
+
+ @property
+ def _reg_struct(self):
+ ac, sc = self._parent.address_cells, self._parent.size_cells
+ return Struct(
+ "addr" / Hex(Int64ul) if ac == 2 else Array(ac, Hex(Int32ul)),
+ "size" / Hex(Int64ul) if sc == 2 else Array(sc, Hex(Int32ul))
+ )
+
+ def get_reg(self, idx):
+ reg = self.reg[idx]
+ addr = reg.addr
+ size = reg.size
+
+ return self._parent.translate(addr), size
+
+ def translate(self, addr):
+ node = self
+ while node is not None:
+ if "ranges" not in node._properties:
+ break
+ for r in node.ranges:
+ ba = r.bus_addr
+ # PCIe special case, because Apple really broke
+ # the spec here with their little endian antics
+ if isinstance(ba, list) and len(ba) == 3:
+ ba = (ba[0] << 64) | (ba[2] << 32) | ba[1]
+ if ba <= addr < (ba + r.size):
+ addr = addr - ba + r.parent_addr
+ break
+ node = node._parent
+
+ return addr
+
+ def to_bus_addr(self, addr):
+ node = self._parent
+
+ descend = []
+ while node is not None:
+ if "ranges" not in node._properties:
+ break
+ descend.append(node)
+ node = node._parent
+
+ for node in reversed(descend):
+ for r in node.ranges:
+ if r.parent_addr <= addr < (r.parent_addr + r.size):
+ addr = addr - r.parent_addr + r.bus_addr
+ break
+ return addr
+
+ def tostruct(self):
+ properties = []
+ for k,v in itertools.chain(self._properties.items()):
+ t, is_template = self._types.get(k, (None, False))
+ value = build_prop(self._path, k, v, t=t)
+ properties.append({
+ "name": k,
+ "size": len(value) | (0x80000000 if is_template else 0),
+ "value": value
+ })
+
+ data = {
+ "property_count": len(self._properties),
+ "child_count": len(self._children),
+ "properties": properties,
+ "children": [c.tostruct() for c in self._children]
+ }
+ return data
+
+ def build(self):
+ return ADTNodeStruct.build(self.tostruct())
+
+ def walk_tree(self):
+ yield self
+ for child in self:
+ yield from child
+
+ def build_addr_lookup(self):
+ lookup = AddrLookup()
+ for node in self.walk_tree():
+ reg = getattr(node, 'reg', None)
+ if not isinstance(reg, list):
+ continue
+
+ for index in range(len(reg)):
+ try:
+ addr, size = node.get_reg(index)
+ except AttributeError:
+ continue
+ if size == 0:
+ continue
+ lookup.add(range(addr, addr + size), node.name + f"[{index}]")
+
+ return lookup
+
+ def create_node(self, name):
+ while name.startswith("/"):
+ name = name[1:]
+ if "/" in name:
+ a, b = name.split("/", 1)
+ return self[a].create_node(b)
+
+ node = ADTNode(path=self._path + "/", parent=self)
+ node.name = name
+ node._types["reg"] = (SafeGreedyRange(node._reg_struct), False)
+ self[name] = node
+ return node
+
+def load_adt(data):
+ return ADTNode(ADTNodeStruct.parse(data))
+
+if __name__ == "__main__":
+ import sys, argparse, pathlib
+
+ parser = argparse.ArgumentParser(description='ADT test for m1n1')
+ parser.add_argument('input', type=pathlib.Path)
+ parser.add_argument('output', nargs='?', type=pathlib.Path)
+ parser.add_argument('-r', '--retrieve', help='retrieve and store the adt from m1n1', action='store_true')
+ parser.add_argument('-a', '--dump-addr', help='dump address lookup table', action='store_true')
+ args = parser.parse_args()
+
+ if args.retrieve:
+ if args.input.exists():
+ print('Error "{}" exists!'.format(args.input))
+ sys.exit()
+
+ from .setup import *
+ adt_data = u.get_adt()
+ args.input.write_bytes(adt_data)
+ else:
+ adt_data = args.input.read_bytes()
+
+ adt = load_adt(adt_data)
+ print(adt)
+ new_data = adt.build()
+ if args.output is not None:
+ args.output.write_bytes(new_data)
+ assert new_data == adt_data[:len(new_data)]
+ assert adt_data[len(new_data):] == bytes(len(adt_data) - len(new_data))
+
+ if args.dump_addr:
+ print("Address lookup table:")
+ print(adt.build_addr_lookup())
diff --git a/tools/proxyclient/m1n1/agx/__init__.py b/tools/proxyclient/m1n1/agx/__init__.py
new file mode 100644
index 0000000..26368ce
--- /dev/null
+++ b/tools/proxyclient/m1n1/agx/__init__.py
@@ -0,0 +1,343 @@
+# SPDX-License-Identifier: MIT
+import bisect, time
+
+from .object import GPUObject, GPUAllocator
+from .initdata import build_initdata
+from .channels import *
+from .event import GPUEventManager
+from ..proxy import IODEV
+from ..malloc import Heap
+from ..hw.uat import UAT, MemoryAttr
+from ..hw.agx import *
+from ..fw.agx import AGXASC
+from ..fw.agx.channels import ChannelInfoSet, ChannelInfo
+
+class AGXChannels:
+ pass
+
+class AGXQueue:
+ pass
+
+class AGX:
+ PAGE_SIZE = 0x4000
+ MAX_EVENTS = 128
+
+ def __init__(self, u):
+ self.start_time = time.time()
+ self.u = u
+ self.p = u.proxy
+
+ self.iface = u.iface
+ self.show_stats = False
+
+ self.asc_dev = u.adt["/arm-io/gfx-asc"]
+ self.sgx_dev = u.adt["/arm-io/sgx"]
+ self.sgx = SGXRegs(u, self.sgx_dev.get_reg(0)[0])
+
+ self.log("Initializing allocations")
+
+ self.aic_base = u.adt["/arm-io/aic"].get_reg(0)[0]
+
+ self.all_objects = {}
+ self.tracked_objects = {}
+
+ # Memory areas
+ self.fw_va_base = self.sgx_dev.rtkit_private_vm_region_base
+ self.fw_va_size = self.sgx_dev.rtkit_private_vm_region_size
+ self.kern_va_base = self.fw_va_base + self.fw_va_size
+
+ # Set up UAT
+ self.uat = UAT(self.u.iface, self.u)
+
+ # Allocator for RTKit/ASC objects
+ self.uat.allocator = Heap(self.kern_va_base + 0x80000000,
+ self.kern_va_base + 0x81000000,
+ self.PAGE_SIZE)
+
+ self.asc = AGXASC(self.u, self.asc_dev.get_reg(0)[0], self, self.uat)
+ self.asc.verbose = 0
+ self.asc.mgmt.verbose = 0
+
+ self.kobj = GPUAllocator(self, "kernel",
+ self.kern_va_base, 0x10000000,
+ AttrIndex=MemoryAttr.Shared, AP=1, guard_pages=4)
+ self.cmdbuf = GPUAllocator(self, "cmdbuf",
+ self.kern_va_base + 0x10000000, 0x10000000,
+ AttrIndex=MemoryAttr.Shared, AP=0, guard_pages=4)
+ self.kshared = GPUAllocator(self, "kshared",
+ self.kern_va_base + 0x20000000, 0x10000000,
+ AttrIndex=MemoryAttr.Shared, AP=1, guard_pages=4)
+ self.kshared2 = GPUAllocator(self, "kshared2",
+ self.kern_va_base + 0x30000000, 0x100000,
+ AttrIndex=MemoryAttr.Shared, AP=0, PXN=1, guard_pages=4)
+
+ self.io_allocator = Heap(self.kern_va_base + 0x38000000,
+ self.kern_va_base + 0x40000000,
+ block=self.PAGE_SIZE)
+
+ self.mon = None
+ self.event_mgr = GPUEventManager(self)
+
+ self.p.iodev_set_usage(IODEV.FB, 0)
+
+ self.initdata_hook = None
+
+ # Early init, needed?
+ self.poke_sgx()
+
+ def poke_sgx(self):
+ self.sgx_base = self.sgx_dev.get_reg(0)[0]
+ self.p.read32(self.sgx_base + 0xd14000)
+ self.p.write32(self.sgx_base + 0xd14000, 0x70001)
+
+ def find_object(self, addr, ctx=0):
+ all_objects = list(self.all_objects.items())
+ all_objects.sort()
+
+ idx = bisect.bisect_left(all_objects, ((ctx, addr + 1), "")) - 1
+ if idx < 0 or idx >= len(all_objects):
+ return None, None
+
+ (ctx, base), obj = all_objects[idx]
+ return base, obj
+
+ def reg_object(self, obj, track=True):
+ self.all_objects[(obj._ctx, obj._addr)] = obj
+ if track:
+ if self.mon is not None:
+ obj.add_to_mon(self.mon)
+ self.tracked_objects[(obj._ctx, obj._addr)] = obj
+
+ def unreg_object(self, obj):
+ del self.all_objects[(obj._ctx, obj._addr)]
+ if obj._addr in self.tracked_objects:
+ del self.tracked_objects[(obj._ctx, obj._addr)]
+
+ def poll_objects(self):
+ for obj in self.tracked_objects.values():
+ diff = obj.poll()
+ if diff is not None:
+ self.log(diff)
+
+ def alloc_channels(self, cls, name, channel_id, count=1, ring_size=0x100, rx=False):
+
+ # All channels have 0x100 items
+ item_count = ring_size
+ item_size = cls.item_size
+ ring_size = item_count * item_size
+
+ self.log(f"Allocating {count} channel(s) for {name} ({item_count} * {item_size:#x} bytes each)")
+
+ state_obj = self.kshared.new_buf(0x30 * count, f"Channel.{name}.state", track=False)
+ if rx:
+ ring_buf = self.kshared.new_buf(ring_size * count, f"Channel.{name}.ring", track=False)
+ else:
+ ring_buf = self.kobj.new_buf(ring_size * count, f"Channel.{name}.ring", track=False)
+
+ info = ChannelInfo()
+ info.state_addr = state_obj._addr
+ info.ringbuffer_addr = ring_buf._addr
+ if name == "FWCtl":
+ self.fwctl_chinfo = info
+ else:
+ setattr(self.ch_info, name, info)
+
+ return [cls(self, name + ("" if count == 1 else f"[{i}]"), channel_id,
+ state_obj._paddr + 0x30 * i,
+ ring_buf._paddr + ring_size * i, item_count)
+ for i in range(count)]
+
+ def init_channels(self):
+ self.log("Initializing channels...")
+ self.ch_info = ChannelInfoSet()
+ self.ch = AGXChannels()
+ self.ch.queue = []
+
+ # Command queue submission channels
+ for index in range(4):
+ queue = AGXQueue()
+ self.ch.queue.append(queue)
+ for typeid, chtype in enumerate(("TA", "3D", "CL")):
+ name = f"{chtype}_{index}"
+ chan = self.alloc_channels(GPUCmdQueueChannel, name,
+ (index << 2) | typeid)[0]
+ setattr(queue, "q_" + chtype, chan)
+
+ # Device control channel
+ self.ch.devctrl = self.alloc_channels(GPUDeviceControlChannel, "DevCtrl", 0x11)[0]
+
+ # GPU -> CPU channels
+ self.ch.event = self.alloc_channels(GPUEventChannel, "Event", None, rx=True)[0]
+ self.ch.log = self.alloc_channels(GPULogChannel, "FWLog", None, 6, rx=True)
+ self.ch.ktrace = self.alloc_channels(GPUKTraceChannel, "KTrace", None, ring_size=0x200, rx=True)[0]
+ self.ch.stats = self.alloc_channels(GPUStatsChannel, "Stats", None, rx=True)[0]
+
+ self.ch.fwctl = self.alloc_channels(GPUFWCtlChannel, "FWCtl", None, rx=False)[0]
+
+ # For some reason, the FWLog channels have their rings in a different place...
+ self.fwlog_ring = self.ch_info.FWLog.ringbuffer_addr
+ self.ch_info.FWLog.ringbuffer_addr = self.kshared.buf(0x150000, "FWLog_Dummy")
+
+ def poll_channels(self):
+ for chan in self.ch.log:
+ chan.poll()
+ self.ch.ktrace.poll()
+ if self.show_stats:
+ self.ch.stats.poll()
+ self.ch.event.poll()
+
+ def kick_firmware(self):
+ self.asc.db.doorbell(0x10)
+
+ def show_irqs(self):
+ hw_state = self.aic_base + 0x4200
+ irqs = []
+ for irq in self.sgx_dev.interrupts:
+ v = int(bool((self.p.read32(hw_state + (irq // 32) * 4) & (1 << (irq % 32)))))
+ irqs.append(v)
+ self.log(f' SGX IRQ state: {irqs}')
+
+ def timeout(self, msg):
+ if self.mon:
+ self.mon.poll()
+ self.poll_objects()
+ self.log(msg)
+ self.log(r' (\________/) ')
+ self.log(r' | | ')
+ self.log(r"'.| \ , / |.'")
+ self.log(r'--| / (( \ |--')
+ self.log(r".'| _-_- |'.")
+ self.log(r' |________| ')
+ self.log(r'')
+ self.log(r' Timeout nya~!!!!!')
+ self.log(r'')
+ self.log(f' Stamp index: {int(msg.stamp_index)}')
+ self.show_pending_stamps()
+ self.log(f' Fault info:')
+ self.log(self.initdata.regionC.fault_info)
+
+ self.show_irqs()
+ self.check_fault()
+ self.recover()
+
+ def faulted(self, msg):
+ if self.mon:
+ self.mon.poll()
+ self.poll_objects()
+ self.log(msg)
+ self.log(r' (\________/) ')
+ self.log(r' | | ')
+ self.log(r"'.| \ , / |.'")
+ self.log(r'--| / (( \ |--')
+ self.log(r".'| _-_- |'.")
+ self.log(r' |________| ')
+ self.log(r'')
+ self.log(r' Fault nya~!!!!!')
+ self.log(r'')
+ self.show_pending_stamps()
+ self.log(f' Fault info:')
+ self.log(self.initdata.regionC.fault_info)
+
+ self.show_irqs()
+ self.check_fault()
+ self.recover()
+
+ def show_pending_stamps(self):
+ self.initdata.regionC.pull()
+ self.log(f' Pending stamps:')
+ for i in self.initdata.regionC.pending_stamps:
+ if i.info or i.wait_value:
+ self.log(f" - #{i.info >> 3:3d}: {i.info & 0x7}/{i.wait_value:#x}")
+ i.info = 0
+ i.wait_value = 0
+ tmp = i.regmap()
+ tmp.info.val = 0
+ tmp.wait_value.val = 0
+
+ #self.initdata.regionC.push()
+
+ def check_fault(self):
+ fault_info = self.sgx.FAULT_INFO.reg
+ if fault_info.value == 0xacce5515abad1dea:
+ raise Exception("Got fault notification, but fault address is unreadable")
+
+ self.log(f" Fault info: {fault_info}")
+
+ if not fault_info.FAULTED:
+ return
+
+ fault_addr = fault_info.ADDR
+ if fault_addr & 0x8000000000:
+ fault_addr |= 0xffffff8000000000
+ base, obj = self.find_object(fault_addr)
+ info = ""
+ if obj is not None:
+ info = f" ({obj!s} + {fault_addr - base:#x})"
+ self.log(f" GPU fault at {fault_addr:#x}{info}")
+ self.log(f" Faulting unit: {agx_decode_unit(fault_info.UNIT)}")
+
+ def recover(self):
+ status = self.fw_status
+ self.log(f" Halt count: {status.halt_count.val}")
+ halted = bool(status.halted.val)
+ self.log(f" Halted: {halted}")
+ if halted:
+ self.log(f" Attempting recovery...")
+ status.halted.val = 0
+ status.resume.val = 1
+ else:
+ raise Exception("Cannot recover")
+ self.show_irqs()
+
+ def resume(self):
+ self.log("Starting ASC")
+ self.asc.start()
+
+ self.log("Starting endpoints")
+ self.asc.start_ep(0x20)
+ self.asc.start_ep(0x21)
+
+ def start(self):
+ self.resume()
+
+ self.init_channels()
+
+ self.log("Building initdata")
+ self.initdata = build_initdata(self)
+ if self.initdata_hook:
+ self.initdata_hook(self)
+
+ self.fw_status = self.initdata.fw_status.regmap()
+ self.uat.flush_dirty()
+
+ self.log("Sending initdata")
+ self.asc.fw.send_initdata(self.initdata._addr & 0xfff_ffffffff)
+ self.asc.work()
+
+ self.log("Sending DC_Init")
+ self.ch.devctrl.send_init()
+ self.asc.work()
+
+ self.log("Sending DC_UpdateIdleTS")
+ self.ch.devctrl.update_idle_ts()
+ self.asc.work()
+
+ def stop(self):
+ self.asc.stop()
+
+ def work(self):
+ self.asc.work()
+
+ def wait_for_events(self, timeout=1.0):
+ now = time.time()
+ deadline = now + timeout
+ cnt = self.event_mgr.event_count
+ while now < deadline and self.event_mgr.event_count == cnt:
+ self.asc.work()
+ now = time.time()
+ if self.event_mgr.event_count == cnt:
+ raise Exception("Timed out waiting for events")
+
+ def log(self, msg):
+ t = time.time() - self.start_time
+ print(f"[AGX][{t:10.03f}] " + str(msg))
diff --git a/tools/proxyclient/m1n1/agx/channels.py b/tools/proxyclient/m1n1/agx/channels.py
new file mode 100644
index 0000000..c91f347
--- /dev/null
+++ b/tools/proxyclient/m1n1/agx/channels.py
@@ -0,0 +1,178 @@
+# SPDX-License-Identifier: MIT
+
+from construct import *
+from ..fw.agx.channels import *
+from ..fw.agx.cmdqueue import *
+
+class GPUChannel:
+ STATE_FIELDS = ChannelStateFields
+
+ def __init__(self, agx, name, channel_id, state_addr, ring_addr, ring_size):
+ self.agx = agx
+ self.u = agx.u
+ self.name = name
+ self.channel_id = channel_id
+ self.iface = agx.u.iface
+ self.state_addr = state_addr
+ self.ring_addr = ring_addr
+ self.ring_size = ring_size
+ self.state = self.STATE_FIELDS(self.u, self.state_addr)
+ self.state.READ_PTR.val = 0
+ self.state.WRITE_PTR.val = 0
+
+ @classmethod
+ @property
+ def item_size(cls):
+ return cls.MSG_CLASS.sizeof()
+
+ def log(self, msg):
+ self.agx.log(f"[{self.name}] {msg}")
+
+class GPUTXChannel(GPUChannel):
+ def doorbell(self):
+ self.agx.asc.db.doorbell(self.channel_id)
+
+ def send_message(self, msg):
+ wptr = self.state.WRITE_PTR.val
+ self.iface.writemem(self.ring_addr + self.item_size * wptr,
+ msg.build())
+ self.state.WRITE_PTR.val = (wptr + 1) % self.ring_size
+ self.doorbell()
+
+class GPURXChannel(GPUChannel):
+ def poll(self):
+ wptr = self.state.WRITE_PTR.val
+ rptr = self.state.READ_PTR.val
+
+ if wptr >= self.ring_size:
+ raise Exception(f"wptr = {wptr:#x} > {self.ring_size:#x}")
+
+ while rptr != wptr:
+ msg = self.iface.readmem(self.ring_addr + self.item_size * rptr,
+ self.item_size)
+ self.handle_message(self.MSG_CLASS.parse(msg))
+ rptr = (rptr + 1) % self.ring_size
+ self.state.READ_PTR.val = rptr
+
+ def handle_message(self, msg):
+ self.log(f"Message: {msg}")
+
+class GPUCmdQueueChannel(GPUTXChannel):
+ MSG_CLASS = RunCmdQueueMsg
+
+ def run(self, queue, event):
+ msg = RunCmdQueueMsg()
+ msg.queue_type = queue.TYPE
+ msg.cmdqueue = queue.info
+ msg.cmdqueue_addr = queue.info._addr
+ msg.head = queue.wptr
+ msg.event_number = event
+ msg.new_queue = 1 if queue.first_time else 0
+ queue.first_time = False
+ #print(msg)
+ self.send_message(msg)
+
+class GPUDeviceControlChannel(GPUTXChannel):
+ MSG_CLASS = DeviceControlMsg
+
+ def send_init(self):
+ self.send_message(DC_Init())
+
+ def dc_09(self, a, ptr, b):
+ # Writes to InitData.RegionB
+ msg = DC_09()
+ msg.unk_4 = a
+ msg.unkptr_c = ptr
+ msg.unk_14 = b
+ self.send_message(msg)
+
+ def send_foo(self, t, d=None):
+ msg = DC_Any()
+ msg.msg_type = t
+ if d is not None:
+ msg.data = d
+ self.send_message(msg)
+
+ def update_idle_ts(self):
+ self.send_message(DC_UpdateIdleTS())
+
+ def destroy_context(self, ctx):
+ msg = DC_DestroyContext()
+ msg.unk_4 = 0
+ msg.unk_8 = 2
+ msg.unk_c = 0
+ msg.unk_10 = 0
+ msg.unk_14 = 0xffff
+ msg.unk_18 = 0
+ msg.context_addr = ctx.gpu_context._addr
+ print(msg)
+ self.send_message(msg)
+
+ # Maybe related to stamps?
+ def write32(self, addr, val):
+ msg = DC_Write32()
+ msg.addr = addr
+ msg.data = val
+ msg.unk_10 = 0
+ msg.unk_14 = 0
+ msg.unk_18 = 0
+ msg.unk_1c = 0
+ print(msg)
+ self.send_message(msg)
+
+ def dc_1e(self, a, b):
+ msg = DC_1e()
+ msg.unk_4 = a
+ msg.unk_c = b
+ print(msg)
+ self.send_message(msg)
+
+class GPUFWCtlChannel(GPUTXChannel):
+ STATE_FIELDS = FWControlStateFields
+ MSG_CLASS = FWCtlMsg
+
+ def doorbell(self):
+ self.agx.asc.db.fwctl_doorbell()
+
+ def send_inval(self, ctx, addr=0):
+ msg = FWCtlMsg()
+ msg.addr = addr
+ msg.unk_8 = 0
+ msg.context_id = ctx
+ msg.unk_10 = 1
+ msg.unk_12 = 2
+ print(msg)
+ self.send_message(msg)
+
+class GPUEventChannel(GPURXChannel):
+ MSG_CLASS = EventMsg
+
+ def handle_message(self, msg):
+ if isinstance(msg, FlagMsg):
+ self.agx.event_mgr.fired(msg.firing)
+ elif isinstance(msg, FaultMsg):
+ self.agx.faulted(msg)
+ elif isinstance(msg, TimeoutMsg):
+ self.agx.timeout(msg)
+ else:
+ self.log(f"Unknown event: {msg}")
+
+class GPULogChannel(GPURXChannel):
+ MSG_CLASS = FWLogMsg
+
+ def handle_message(self, msg):
+ ts = msg.timestamp / 24000000
+ self.log(f"[{msg.seq_no:<4d}{ts:14.7f}] {msg.msg}")
+
+class GPUKTraceChannel(GPURXChannel):
+ MSG_CLASS = KTraceMsg
+
+ def handle_message(self, msg):
+ self.log(f"{msg}")
+
+class GPUStatsChannel(GPURXChannel):
+ MSG_CLASS = HexDump(Bytes(0x60))
+
+ def handle_message(self, msg):
+ if self.agx.show_stats:
+ self.log(f"stat {msg}")
diff --git a/tools/proxyclient/m1n1/agx/context.py b/tools/proxyclient/m1n1/agx/context.py
new file mode 100644
index 0000000..41ebed5
--- /dev/null
+++ b/tools/proxyclient/m1n1/agx/context.py
@@ -0,0 +1,247 @@
+# SPDX-License-Identifier: MIT
+from ..utils import chexdump
+from ..malloc import Heap
+from construct.core import *
+from ..fw.agx.channels import *
+from ..fw.agx.cmdqueue import *
+from ..fw.agx.microsequence import *
+from ..hw.uat import MemoryAttr
+from .object import *
+import textwrap
+
+class GPUContext:
+ def __init__(self, agx):
+ self.agx = agx
+ self.uat = self.agx.uat
+ self.u = self.agx.u
+ self.p = self.agx.p
+ self.verbose = False
+
+ #self.job_list = agx.kshared.new(JobList)
+ #self.job_list.first_job = 0
+ #self.job_list.last_head = self.job_list._addr # Empty list has self as last_head
+ #self.job_list.unkptr_10 = 0
+ #self.job_list.push()
+
+ self.gpu_context = agx.kobj.new(GPUContextData).push()
+
+ self.ttbr0_base = self.u.memalign(self.agx.PAGE_SIZE, self.agx.PAGE_SIZE)
+ self.p.memset32(self.ttbr0_base, 0, self.agx.PAGE_SIZE)
+
+ self.objects = {}
+
+ # 32K VA pages since buffer manager needs that
+ self.uobj = GPUAllocator(agx, "Userspace", 0x1600000000, 0x100000000, ctx=None,
+ guard_pages=16,
+ va_block=32768, nG=1, AP=0, PXN=1, UXN=1)
+
+ self.gobj = GPUAllocator(agx, "GEM", 0x1500000000, 0x100000000, ctx=None,
+ guard_pages=16, nG=1, AP=0, PXN=1, UXN=1)
+
+ self.pipeline_base = 0x1100000000
+ self.pipeline_size = 1 << 32
+ self.pobj = GPUAllocator(agx, "Pipelines", self.pipeline_base + 0x10000, self.pipeline_size,
+ ctx=None, guard_pages=1, nG=1, AP=0, PXN=1, UXN=1)
+
+ def bind(self, ctx_id):
+ self.ctx = ctx_id
+ self.uobj.ctx = ctx_id
+ self.gobj.ctx = ctx_id
+ self.pobj.ctx = ctx_id
+ self.uat.bind_context(ctx_id, self.ttbr0_base)
+ self.thing = self.buf_at(0x6fffff8000, 0, 0x4000, "thing")
+
+ def make_stream(self, base):
+ return self.uat.iostream(self.ctx, base, recurse=False)
+
+ def new_at(self, addr, objtype, name=None, track=True, **flags):
+ obj = GPUObject(self, objtype)
+ obj._stream = self.make_stream
+ if name is not None:
+ obj._name = name
+
+ size_align = align_up(obj._size, self.agx.PAGE_SIZE)
+ obj._addr = addr
+
+ obj._paddr = self.agx.u.memalign(self.agx.PAGE_SIZE, size_align)
+ #if isinstance(obj.val, ConstructClassBase):
+ #obj.val._addr = obj._addr
+
+ self.agx.log(f"[Context@{self.gpu_context._addr:#x}] Map {obj._name} size {obj._size:#x} @ {obj._addr:#x} ({obj._paddr:#x})")
+
+ flags2 = {"AttrIndex": MemoryAttr.Shared}
+ flags2.update(flags)
+ obj._map_flags = flags2
+
+ obj._size_align = size_align
+ self.agx.uat.iomap_at(self.ctx, obj._addr, obj._paddr, size_align, **flags2)
+ self.objects[obj._addr] = obj
+ self.agx.reg_object(obj, track=track)
+
+ return obj
+
+ def buf_at(self, addr, is_pipeline, size, name=None, track=True):
+ return self.new_at(addr, Bytes(size), name, track=track,
+ AttrIndex=MemoryAttr.Shared, PXN=1,
+ nG=1, AP=(1 if is_pipeline else 0))
+
+ def load_blob(self, addr, is_pipeline, filename, track=True):
+ data = open(filename, "rb").read()
+ obj = self.new_at(addr, Bytes(len(data)), filename, track=track,
+ AttrIndex=MemoryAttr.Shared, PXN=1,
+ nG=1, AP=(1 if is_pipeline else 0))
+ obj.val = data
+ obj.push()
+
+ return obj
+
+ def free(self, obj):
+ obj._dead = True
+ self.agx.uat.iomap_at(self.ctx, obj._addr, 0, obj._size_align, VALID=0)
+ del self.objects[obj._addr]
+ self.agx.unreg_object(obj)
+
+ def free_at(self, addr):
+ self.free(self.objects[obj._addr])
+
+class GPUWorkQueue:
+ def __init__(self, agx, context, job_list):
+ self.agx = agx
+ self.u = agx.u
+ self.p = agx.p
+ self.context = context
+
+ self.info = agx.kobj.new(CommandQueueInfo)
+
+ self.pointers = agx.kshared.new(CommandQueuePointers).push()
+ self.pmap = CommandQueuePointerMap(self.u, self.pointers._paddr)
+
+ self.rb_size = self.pointers.rb_size
+ self.ring = agx.kobj.new_buf(8 * self.rb_size, "GPUWorkQueue.RB")
+
+ self.info.pointers = self.pointers
+ self.info.rb_addr = self.ring._addr
+ self.info.job_list = job_list
+ self.info.gpu_buf_addr = agx.kobj.buf(0x2c18, "GPUWorkQueue.gpu_buf")
+ self.info.gpu_context = context.gpu_context
+ self.info.push()
+
+ self.wptr = 0
+ self.first_time = True
+
+ self.agx.uat.flush_dirty()
+
+ def submit(self, work):
+ work.push()
+
+ self.p.write64(self.ring._paddr + 8 * self.wptr, work._addr)
+ self.wptr = (self.wptr + 1) % self.rb_size
+ self.agx.uat.flush_dirty()
+ self.pmap.CPU_WPTR.val = self.wptr
+
+ def wait_empty(self):
+ while self.wptr != self.pmap.GPU_DONEPTR.val:
+ self.agx.work()
+
+class GPU3DWorkQueue(GPUWorkQueue):
+ TYPE = 1
+
+class GPUTAWorkQueue(GPUWorkQueue):
+ TYPE = 0
+
+class GPUMicroSequence:
+ def __init__(self, agx):
+ self.agx = agx
+ self.off = 0
+ self.ops = []
+ self.obj = None
+
+ def append(self, op):
+ off = self.off
+ self.ops.append(op)
+ self.off += op.sizeof()
+ return off
+
+ def finalize(self):
+ self.ops.append(EndCmd())
+ self.size = sum(i.sizeof() for i in self.ops)
+ self.obj = self.agx.kobj.new_buf(self.size, "GPUMicroSequence", track=False)
+ self.obj.val = b"".join(i.build() for i in self.ops)
+ self.obj.push()
+ return self.obj
+
+ def dump(self):
+ chexdump(self.agx.iface.readmem(self.obj._paddr, self.size))
+ print(MicroSequence.parse_stream(self.agx.uat.iostream(0, self.obj._addr)))
+
+ def __str__(self):
+ s = f"GPUMicroSequence: {len(self.ops)} ops\n"
+ for i, op in enumerate(self.ops):
+ op_s = textwrap.indent(str(op), ' ' * 4)
+ s += f"[{i:2}:{op.sizeof():#x}] = {op!s}\n"
+ return s
+
+class GPUBufferManager:
+ def __init__(self, agx, context, blocks=8):
+ self.agx = agx
+ self.ctx = context
+
+ self.block_ctl_obj = agx.kshared.new(BufferManagerBlockControl)
+ self.block_ctl_obj.total = blocks
+ self.block_ctl_obj.wptr = 0
+ self.block_ctl_obj.unk = 0
+ self.block_ctl = self.block_ctl_obj.push().regmap()
+
+ self.counter_obj = agx.kshared.new(BufferManagerCounter)
+ self.counter_obj.count = 0
+ self.counter = self.counter_obj.push().regmap()
+
+ self.misc_obj = agx.kshared.new(BufferManagerMisc)
+ self.misc_obj.cpu_flag = 1
+ self.misc = self.misc_obj.push().regmap()
+
+ self.page_size = 0x8000
+ self.pages_per_block = 4
+ self.block_size = self.pages_per_block * self.page_size
+
+ self.page_list = context.uobj.new(Array(0x10000 // 4, Int32ul), "BM PageList", track=False)
+ self.block_list = context.uobj.new(Array(0x8000 // 4, Int32ul), "BM BlockList", track=False)
+
+ self.info = info = agx.kobj.new(BufferManagerInfo)
+ info.page_list_addr = self.page_list._addr
+ info.page_list_size = self.page_list._size
+ info.page_count = self.block_ctl_obj.total * 4
+ info.block_count = self.block_ctl_obj.total
+
+ info.block_list_addr = self.block_list._addr
+ info.block_ctl = self.block_ctl_obj
+ info.last_page = info.page_count - 1
+ info.block_size = self.block_size
+
+ info.counter = self.counter_obj
+
+ self.populate()
+ self.block_ctl_obj.pull()
+ self.block_list.push()
+ self.page_list.push()
+
+ info.push()
+
+ def increment(self):
+ self.counter_obj.count += 1
+ self.counter_obj.push()
+
+ def populate(self):
+ idx = self.block_ctl.wptr.val
+ total = self.block_ctl.total.val
+ while idx < total:
+ block = self.ctx.uobj.new_buf(self.block_size, "BM Block", track=False)
+ self.block_list[idx * 2] = block._addr // self.page_size
+
+ page_idx = idx * self.pages_per_block
+ for i in range(self.pages_per_block):
+ self.page_list[page_idx + i] = block._addr // self.page_size + i
+
+ idx += 1
+ self.block_ctl.wptr.val = idx
+
diff --git a/tools/proxyclient/m1n1/agx/event.py b/tools/proxyclient/m1n1/agx/event.py
new file mode 100644
index 0000000..693f3a5
--- /dev/null
+++ b/tools/proxyclient/m1n1/agx/event.py
@@ -0,0 +1,58 @@
+# SPDX-License-Identifier: MIT
+from ..utils import chexdump
+from ..malloc import Heap
+from construct.core import *
+from ..fw.agx.channels import *
+from ..fw.agx.cmdqueue import *
+from ..fw.agx.microsequence import *
+from ..hw.uat import MemoryAttr
+from .object import *
+import textwrap
+
+class GPUEventManager:
+ MAX_EVENTS = 128
+
+ def __init__(self, agx):
+ self.agx = agx
+
+ self.event_count = 0
+ self.free_events = set(range(self.MAX_EVENTS))
+ self.events = [None] * self.MAX_EVENTS
+
+ def allocate_event(self):
+ if not self.free_events:
+ raise Exception("No free events")
+ ev_id = self.free_events.pop()
+
+ ev = GPUEvent(ev_id)
+ self.events[ev_id] = ev
+
+ return ev
+
+ def free_event(self, ev):
+ self.events[ev.id] = None
+ self.free_events.add(ev.id)
+
+ def fired(self, flags):
+ self.agx.log("= Events fired =")
+ for i, v in enumerate(flags):
+ for j in range(64):
+ if v & (1 << j):
+ ev_id = i * 64 + j
+ ev = self.events[ev_id]
+ self.agx.log(f"Event fired: {ev_id}")
+ if ev is None:
+ raise Exception("Received spurious notification for event ID {ev}")
+ ev.fire()
+ self.event_count += 1
+
+class GPUEvent:
+ def __init__(self, ev_id):
+ self.id = ev_id
+ self.fired = False
+
+ def fire(self):
+ self.fired = True
+
+ def rearm(self):
+ self.fired = False
diff --git a/tools/proxyclient/m1n1/agx/initdata.py b/tools/proxyclient/m1n1/agx/initdata.py
new file mode 100644
index 0000000..d6fa76a
--- /dev/null
+++ b/tools/proxyclient/m1n1/agx/initdata.py
@@ -0,0 +1,387 @@
+# SPDX-License-Identifier: MIT
+from ..fw.agx.initdata import *
+from ..fw.agx.channels import ChannelInfo
+from ..hw.uat import MemoryAttr
+
+from construct import Container
+
+def build_iomappings(agx, chip_id):
+ def iomap(phys, size, range_size, rw):
+ off = phys & 0x3fff
+ virt = agx.io_allocator.malloc(size + 0x4000 + off)
+ agx.uat.iomap_at(0, virt, phys - off, size + off, AttrIndex=MemoryAttr.Device)
+ return IOMapping(phys, virt + off, size, range_size, rw)
+
+ # for t8103
+ if chip_id == 0x8103:
+ return [
+ iomap(0x204d00000, 0x1c000, 0x1c000, 1), # Fender
+ iomap(0x20e100000, 0x4000, 0x4000, 0), # AICTimer
+ iomap(0x23b104000, 0x4000, 0x4000, 1), # AICSWInt
+ iomap(0x204000000, 0x20000, 0x20000, 1), # RGX
+ IOMapping(), # UVD
+ IOMapping(), # unused
+ IOMapping(), # DisplayUnderrunWA
+ iomap(0x23b2e8000, 0x1000, 0x1000, 0), # AnalogTempSensorControllerRegs
+ iomap(0x23bc00000, 0x1000, 0x1000, 1), # PMPDoorbell
+ iomap(0x204d80000, 0x5000, 0x5000, 1), # MetrologySensorRegs
+ iomap(0x204d61000, 0x1000, 0x1000, 1), # GMGIFAFRegs
+ iomap(0x200000000, 0xd6400, 0xd6400, 1), # MCache registers
+ IOMapping(), # AICBankedRegisters
+ iomap(0x23b738000, 0x1000, 0x1000, 1), # PMGRScratch
+ IOMapping(), # NIA Special agent idle register die 0
+ IOMapping(), # NIA Special agent idle register die 1
+ IOMapping(), # CRE registers
+ IOMapping(), # Streaming codec registers
+ IOMapping(), #
+ IOMapping(), #
+ ]
+ elif chip_id == 0x8112:
+ return [
+ iomap(0x204d00000, 0x14000, 0x14000, 1), # Fender
+ iomap(0x20e100000, 0x4000, 0x4000, 0), # AICTimer
+ iomap(0x23b0c4000, 0x4000, 0x4000, 1), # AICSWInt
+ iomap(0x204000000, 0x20000, 0x20000, 1), # RGX
+ IOMapping(), # UVD
+ IOMapping(), # unused
+ IOMapping(), # DisplayUnderrunWA
+ iomap(0x23b2c0000, 0x1000, 0x1000, 0), # AnalogTempSensorControllerRegs
+ IOMapping(), # PMPDoorbell
+ iomap(0x204d80000, 0x8000, 0x8000, 1), # MetrologySensorRegs
+ iomap(0x204d61000, 0x1000, 0x1000, 1), # GMGIFAFRegs
+ iomap(0x200000000, 0xd6400, 0xd6400, 1), # MCache registers
+ IOMapping(), # AICBankedRegisters
+ IOMapping(), # PMGRScratch
+ IOMapping(), # NIA Special agent idle register die 0
+ IOMapping(), # NIA Special agent idle register die 1
+ iomap(0x204e00000, 0x10000, 0x10000, 0), # CRE registers
+ iomap(0x27d050000, 0x4000, 0x4000, 0), # Streaming codec registers
+ iomap(0x23b3d0000, 0x1000, 0x1000, 0), #
+ iomap(0x23b3c0000, 0x1000, 0x1000, 0), #
+ ]
+ elif chip_id in (0x6000, 0x6001, 0x6002):
+ mcc_cnt = {0x6002: 16, 0x6001: 8, 0x6000: 4}
+ return [
+ iomap(0x404d00000, 0x1c000, 0x1c000, 1), # Fender
+ iomap(0x20e100000, 0x4000, 0x4000, 0), # AICTimer
+ iomap(0x28e104000, 0x4000, 0x4000, 1), # AICSWInt
+ iomap(0x404000000, 0x20000, 0x20000, 1), # RGX
+ IOMapping(), # UVD
+ IOMapping(), # unused
+ IOMapping(), # DisplayUnderrunWA
+ iomap(0x28e494000, 0x1000, 0x1000, 0), # AnalogTempSensorControllerRegs
+ IOMapping(), # PMPDoorbell
+ iomap(0x404d80000, 0x8000, 0x8000, 1), # MetrologySensorRegs
+ iomap(0x204d61000, 0x1000, 0x1000, 1), # GMGIFAFRegs
+ iomap(0x200000000, mcc_cnt[chip_id] * 0xd8000, 0xd8000, 1), # MCache registers
+ IOMapping(), # AICBankedRegisters
+ IOMapping(), # PMPDoorbell
+ iomap(0x2643c4000, 0x1000, 0x1000, 1), # NIA Special agent idle register die 0
+ iomap(0x22643c4000, 0x1000, 0x1000, 1) if chip_id == 0x6002 else IOMapping(), # NIA Special agent idle register die 1
+ IOMapping(), # CRE registers
+ IOMapping(), # Streaming codec registers
+ iomap(0x28e3d0000, 0x1000, 0x1000, 1),
+ iomap(0x28e3c0000, 0x2000, 0x2000, 0),
+ ]
+
+
+CHIP_INFO = {
+ 0x8103: Container(
+ chip_id = 0x8103,
+ min_sram_volt = 850,
+ max_power = 19551,
+ max_freq_mhz = 1278,
+ unk_87c = -220,
+ unk_8cc = 9880,
+ unk_924 = [[0] * 8] * 8,
+ unk_e48 = [[0] * 8] * 8,
+ unk_e24 = 112,
+ gpu_fast_die0_sensor_mask64 = 0x12,
+ gpu_fast_die0_sensor_mask64_alt = 0x12,
+ gpu_fast_die0_sensor_present = 0x01,
+ shared1_tab = [
+ -1, 0x7282, 0x50ea, 0x370a, 0x25be, 0x1c1f, 0x16fb
+ ] + ([-1] * 10),
+ shared1_a4 = 0xffff,
+ shared2_tab = [0x800, 0x1555, -1, -1, -1, -1, -1, -1, 0, 0],
+ shared2_unk_508 = 0xc0007,
+ unk_3cf4 = [1000.0, 0, 0, 0, 0, 0, 0, 0],
+ unk_3d14 = [45.0, 0, 0, 0, 0, 0, 0, 0],
+ unk_118ec = None,
+ hwdb_4e0 = 0,
+ hwdb_534 = 0,
+ num_cores = 8,
+ gpu_core = 11,
+ gpu_rev = 4,
+ hwdb_ab8 = 0x48,
+ hwdb_abc = 0x8,
+ hwdb_b30 = 0,
+ rel_max_powers = [0, 19, 26, 38, 60, 87, 100],
+ ),
+ 0x6001: Container(
+ chip_id = 0x6001,
+ min_sram_volt = 790,
+ max_power = 81415,
+ max_freq_mhz = 1296,
+ unk_87c = 900,
+ unk_8cc = 11000,
+ unk_924 = [[i, *([0] * 7)] for i in [
+ 9.838, 9.819, 9.826, 9.799,
+ 0, 0, 0, 0,
+ ]],
+ unk_e48 = [[i, *([0] * 7)] for i in [
+ 13, 13, 13, 13, 0, 0, 0, 0,
+ ]],
+ unk_e24 = 125,
+ gpu_fast_die0_sensor_mask64 = 0x80808080,
+ gpu_fast_die0_sensor_mask64_alt = 0x90909090,
+ gpu_fast_die0_sensor_present = 0x0f,
+ shared1_tab = [0] + ([0xffff] * 16),
+ shared1_a4 = 0xffff,
+ shared2_tab = [-1, -1, -1, -1, 0x2aa, 0xaaa, -1, -1, 0, 0],
+ shared2_unk_508 = 0xcc00001,
+ unk_3cf4 = [1314.0, 1330.0, 1314.0, 1288.0, 0, 0, 0, 0],
+ unk_3d14 = [21.0, 21.0, 22.0, 21.0, 0, 0, 0, 0],
+ unk_118ec = [
+ 0, 1, 2,
+ 1, 1, 90, 75, 1, 1,
+ 1, 2, 90, 75, 1, 1,
+ 1, 1, 90, 75, 1, 1
+ ],
+ hwdb_4e0 = 4,
+ hwdb_534 = 1,
+ num_cores = 32,
+ gpu_core = 13,
+ gpu_rev = 5,
+ hwdb_ab8 = 0x2084,
+ hwdb_abc = 0x80,
+ hwdb_b30 = 0,
+ rel_max_powers = [0, 15, 20, 27, 36, 52, 100],
+ ),
+ 0x6002: Container(
+ chip_id = 0x6002,
+ min_sram_volt = 790,
+ max_power = 166743,
+ max_freq_mhz = 1296,
+ unk_87c = 900,
+ unk_8cc = 11000,
+ unk_924 = [[i, *([0] * 7)] for i in [
+ 9.838, 9.819, 9.826, 9.799,
+ 9.799, 9.826, 9.819, 9.838,
+ ]],
+ unk_c30 = 0,
+ unk_e48 = [[i, *([0] * 7)] for i in [
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ ]],
+ unk_e24 = 125,
+ gpu_fast_die0_sensor_mask64 = 0x8080808080808080,
+ gpu_fast_die0_sensor_mask64_alt = 0x9090909090909090,
+ gpu_fast_die0_sensor_present = 0xff,
+ shared1_tab = [0] + ([0xffff] * 16),
+ shared1_a4 = 0xffff,
+ shared2_tab = [-1, -1, -1, -1, 0x2aa, 0xaaa, -1, -1, 0, 0],
+ shared2_unk_508 = 0xcc00001,
+ unk_3cf4 = [1244.0, 1260.0, 1242.0, 1214.0,
+ 1072.0, 1066.0, 1044.0, 1042.0],
+ unk_3d14 = [18.0, 18.0, 18.0, 17.0, 15.0, 15.0, 15.0, 14.0],
+ unk_8924 = 0,
+ unk_118ec = [
+ 0, 1, 2,
+ 1, 1, 90, 75, 1, 1,
+ 1, 2, 90, 75, 1, 1,
+ 1, 1, 90, 75, 1, 1
+ ],
+ hwdb_4e0 = 4,
+ hwdb_534 = 1,
+ num_cores = 64,
+ gpu_core = 13,
+ gpu_rev = 5,
+ hwdb_ab8 = 0x2084,
+ hwdb_abc = 0x80,
+ hwdb_b30 = 0,
+ rel_max_powers = [0, 15, 19, 25, 34, 50, 100],
+ ),
+ 0x8112: Container(
+ chip_id = 0x8112,
+ min_sram_volt = 780,
+ max_power = 22800,
+ max_freq_mhz = 1398,
+ unk_87c = 900,
+ unk_8cc = 11000,
+ unk_924 = [[
+ 0.0, 0.0, 0.0, 0.0,
+ 5.3, 0.0, 5.3, 6.6,
+ ]] + ([[0] * 8] * 7),
+ unk_e48 = [[
+ 0.0, 0.0, 0.0, 0.0,
+ 5.3, 0.0, 5.3, 6.6,
+ ]] + ([[0] * 8] * 7),
+ unk_e24 = 125,
+ gpu_fast_die0_sensor_mask64 = 0x6800,
+ gpu_fast_die0_sensor_mask64_alt = 0x6800,
+ gpu_fast_die0_sensor_present = 0x02,
+ shared1_tab = [0] + ([0xffff] * 16),
+ shared1_a4 = 0,
+ shared2_tab = [-1, -1, -1, -1, -1, -1, -1, -1, 0xaa5aa, 0],
+ shared2_unk_508 = 0xc00000,
+ unk_3cf4 = [1920.0, 0, 0, 0, 0, 0, 0, 0],
+ unk_3d14 = [74.0, 0, 0, 0, 0, 0, 0, 0],
+ unk_118ec = None,
+ hwdb_4e0 = 4,
+ hwdb_534 = 0,
+ num_cores = 10,
+ gpu_core = 15,
+ gpu_rev = 3,
+ hwdb_ab8 = 0x2048,
+ hwdb_abc = 0x4000,
+ hwdb_b30 = 1,
+ rel_max_powers = [0, 18, 27, 37, 52, 66, 82, 96, 100],
+ ),
+}
+def build_initdata(agx):
+ sgx = agx.u.adt["/arm-io/sgx"]
+ chosen = agx.u.adt["/chosen"]
+ chip_info = CHIP_INFO[chosen.chip_id]
+
+ initdata = agx.kshared.new(InitData)
+
+ initdata.ver_info = (1, 1, 16, 1)
+
+ initdata.regionA = agx.kshared.new_buf(0x4000, "InitData_RegionA").push()
+
+ regionB = agx.kobj.new(InitData_RegionB)
+
+ regionB.channels = agx.ch_info
+
+ regionB.stats_ta = agx.kobj.new(InitData_GPUGlobalStatsTA).push()
+ regionB.stats_3d = agx.kobj.new(InitData_GPUGlobalStats3D).push()
+
+ # size: 0x180, Empty
+ # 13.0: grew
+ #regionB.stats_cp = agx.kobj.new_buf(0x180, "RegionB.unkptr_180").push()
+ regionB.stats_cp = agx.kobj.new_buf(0x980, "RegionB.unkptr_180").push()
+
+ # size: 0x3b80, few floats, few ints, needed for init
+ regionB.hwdata_a = agx.kobj.new(AGXHWDataA(sgx, chip_info), track=False)
+
+ # size: 0x80, empty
+ regionB.unk_190 = agx.kobj.new_buf(0x80, "RegionB.unkptr_190").push()
+
+ # size: 0xc0, fw writes timestamps into this
+ regionB.unk_198 = agx.kobj.new_buf(0xc0, "RegionB.unkptr_198").push()
+
+ # size: 0xb80, io stuff
+ hwdata = agx.kobj.new(AGXHWDataB(sgx, chip_info), track=False)
+ hwdata.io_mappings = build_iomappings(agx, chosen.chip_id)
+
+ k = 1.02 #?
+ count = sgx.perf_state_count
+ table_count = sgx.perf_state_table_count
+ base_pstate = sgx.getprop("gpu-perf-base-pstate", 3)
+ base_freq = sgx.perf_states[base_pstate].freq
+ max_freq = sgx.perf_states[count - 1].freq
+ for i in range(count):
+ ps = sgx.perf_states[i]
+ hwdata.frequencies[i] = ps.freq // 1000000
+
+ volt = [ps.volt] * 8
+ for j in range(1, table_count):
+ volt[j] = sgx.perf_states[count * j + i].volt
+ sram_volt = [max(chip_info.min_sram_volt, i) for i in volt]
+
+ hwdata.voltages[i] = volt
+ hwdata.voltages_sram[i] = sram_volt
+
+ regionB.hwdata_a.unk_74[i] = k
+ hwdata.unk_9b4[i] = k
+ hwdata.rel_max_powers[i] = chip_info.rel_max_powers[i]
+ hwdata.rel_boost_freqs[i] = max(0, int((ps.freq - base_freq) / (max_freq - base_freq) * 100))
+
+ regionB.hwdata_a.push()
+
+ regionB.hwdata_b = hwdata.push()
+ regionB.hwdata_b_addr2 = hwdata._addr
+
+ regionB.fwlog_ring2 = agx.fwlog_ring
+
+ # Unallocated, Size 0x1000
+ regionB.unk_1b8 = agx.kobj.new_buf(0x1000, "RegionB.unkptr_1b8").push()
+
+ # Unallocated, size 0x300
+ regionB.unk_1c0 = agx.kobj.new_buf(0x300, "RegionB.unkptr_1c0").push()
+
+ # Unallocated, unknown size
+ regionB.unk_1c8 = agx.kobj.new_buf(0x1000, "RegionB.unkptr_1c8").push()
+
+ # Size: 0x4000
+ regionB.buffer_mgr_ctl = agx.kshared2.new(InitData_BufferMgrCtl).push()
+ regionB.buffer_mgr_ctl_addr2 = regionB.buffer_mgr_ctl._addr
+
+ regionB.unk_6a80 = 0
+ regionB.gpu_idle = 0
+ regionB.unk_6a9c = 0
+ regionB.unk_ctr0 = 0
+ regionB.unk_ctr1 = 0
+ regionB.unk_6aa8 = 0
+ regionB.unk_6aac = 0
+ regionB.unk_ctr2 = 0
+ regionB.unk_6ab4 = 0
+ regionB.unk_6ab8 = 0
+ regionB.unk_6abc = 0
+ regionB.unk_6ac0 = 0
+ regionB.unk_6ac4 = 0
+ regionB.unk_ctr3 = 0
+ regionB.unk_6acc = 0
+ regionB.unk_6ad0 = 0
+ regionB.unk_6ad4 = 0
+ regionB.unk_6ad8 = 0
+ regionB.unk_6adc = 0
+ regionB.unk_6ae0 = 0
+ regionB.unk_6ae4 = 0
+ regionB.unk_6ae8 = 0
+ regionB.unk_6aec = 0
+ regionB.unk_6af0 = 0
+ regionB.unk_ctr4 = 0
+ regionB.unk_ctr5 = 0
+ regionB.unk_6afc = 0
+
+ initdata.regionB = regionB.push()
+
+ initdata.regionC = agx.kshared.new(InitData_RegionC(sgx, chip_info), track=False).push()
+
+ #self.regionC_addr = agx.ksharedshared_heap.malloc(0x88000)
+
+ initdata.fw_status = agx.kobj.new(InitData_FWStatus)
+ initdata.fw_status.fwctl_channel = agx.fwctl_chinfo
+ initdata.fw_status.push()
+
+ ## This section seems to be data that would be used by firmware side page allocation
+ ## But the current firmware doesn't have this functionality enabled, so it's not used?
+ initdata.uat_num_levels = 3
+ initdata.uat_page_bits = 14
+ initdata.uat_page_size = 0x4000
+
+ if chip_info.chip_id in (0x8103, 0x8112):
+ phys_mask = 0xffffffc000
+ else:
+ phys_mask = 0x3ffffffc000
+
+ initdata.uat_level_info = [
+ UatLevelInfo(36, 8, phys_mask),
+ UatLevelInfo(25, 2048, phys_mask),
+ UatLevelInfo(14, 2048, phys_mask),
+ ]
+
+ # Host handles FW allocations for existing firmware versions
+ initdata.host_mapped_fw_allocations = 1
+
+
+ #initdata.regionC.idle_ts = agx.u.mrs("CNTPCT_EL0") + 24000000
+ #initdata.regionC.idle_unk = 0x5b2e8
+ #initdata.regionC.idle_to_off_timeout_ms = 20000
+
+ initdata.regionC.push()
+ initdata.push()
+
+ #print(InitData.parse_stream(agx.uat.iostream(0, initdata._addr)))
+ return initdata
diff --git a/tools/proxyclient/m1n1/agx/object.py b/tools/proxyclient/m1n1/agx/object.py
new file mode 100644
index 0000000..8f382f9
--- /dev/null
+++ b/tools/proxyclient/m1n1/agx/object.py
@@ -0,0 +1,263 @@
+# SPDX-License-Identifier: MIT
+import io, time
+
+from ..malloc import Heap
+from ..utils import *
+from ..constructutils import ConstructClassBase, str_value
+from construct import Bytes, Container, HexDump
+from ..hw.uat import MemoryAttr
+
+class GPUObject:
+ def __init__(self, allocator, objtype):
+ self._raw = False
+ if isinstance(objtype, int):
+ self.val = bytes(objtype)
+ self._size = objtype
+ self._name = b"Bytes({objtype})"
+ self._raw = True
+ elif isinstance(objtype, ConstructClassBase):
+ self.val = objtype
+ objtype = type(objtype)
+ self._size = objtype.sizeof()
+ self._name = objtype.__name__
+ elif isinstance(objtype, type) and issubclass(objtype, ConstructClassBase):
+ self._size = objtype.sizeof()
+ self.val = objtype()
+ self._name = objtype.__name__
+ else:
+ self._size = objtype.sizeof()
+ self.val = objtype.parse(bytes(self._size))
+ self._name = type(objtype).__name__
+
+ self._alloc = allocator
+ self._type = objtype
+ self._addr = None
+ self._data = None
+ self._dead = False
+ self._map_flags = {}
+ self._mon_val = None
+ self._skipped_pushes = 0
+ self._compress_threshold = 65536
+ self._strm = None
+ self._read_phys = False
+
+ def push(self, if_needed=False):
+ self._mon_val = self.val
+ assert self._addr is not None
+
+ if self._raw:
+ data = self.val
+ else:
+ context = Container()
+ context._parsing = False
+ context._building = True
+ context._sizing = False
+ context._params = context
+ # build locally and push as a block for efficiency
+ ios = io.BytesIO()
+ self._type._build(self.val, ios, context, "(pushing)")
+ data = ios.getvalue()
+
+ #if self._alloc.verbose:
+ #t = time.time()
+ #self._alloc.agx.log(f"[{self._name} @{self._addr:#x}] chk {self._size} bytes")
+ if if_needed and data[:] == self._data:
+ self._skipped_pushes += 1
+ #if self._alloc.verbose:
+ #t2 = time.time()
+ #mbs = self._size / (t2 - t) / 1000000
+ #self._alloc.agx.log(f"[{self._name} @{self._addr:#x}] chk done ({mbs:.02f} MB/s)")
+ return self
+
+ self._skipped_pushes = 0
+
+ t = time.time()
+ if data == bytes(self._size):
+ if self._alloc.verbose:
+ self._alloc.agx.log(f"[{self._name} @{self._addr:#x}] zeroing {self._size} bytes")
+ self._alloc.agx.p.memset8(self._paddr, 0, self._size)
+ elif self._size > self._compress_threshold:
+ if self._alloc.verbose:
+ self._alloc.agx.log(f"[{self._name} @{self._addr:#x}] pushing {self._size} bytes (compressed)")
+ self._alloc.agx.u.compressed_writemem(self._paddr, data)
+ else:
+ if self._alloc.verbose:
+ self._alloc.agx.log(f"[{self._name} @{self._addr:#x}] pushing {self._size} bytes")
+ self._alloc.agx.iface.writemem(self._paddr, data)
+ if self._alloc.verbose:
+ t2 = time.time()
+ mbs = self._size / (t2 - t) / 1000000
+ self._alloc.agx.log(f"[{self._name} @{self._addr:#x}] push done ({mbs:.02f} MB/s)")
+ #stream.write(data)
+ if isinstance(self._type, type) and issubclass(self._type, ConstructClassBase):
+ if self._strm is None:
+ self._strm = self._alloc.make_stream(self._addr)
+ self.val.set_addr(self._addr, self._strm)
+
+ self._data = bytes(data)
+ return self
+
+ def _pull(self):
+ if self._raw:
+ assert self._paddr is not None
+ return self._alloc.agx.iface.readmem(self._paddr, self._size)
+
+ assert self._addr is not None
+ context = Container()
+ context._parsing = True
+ context._building = False
+ context._sizing = False
+ context._params = context
+ if self._alloc.verbose:
+ self._alloc.agx.log(f"[{self._name} @{self._addr:#x}] pulling {self._size} bytes")
+ if self._read_phys:
+ stream = io.BytesIO()
+ stream.write(self._alloc.agx.iface.readmem(self._paddr, self._size))
+ stream.seek(0)
+ else:
+ stream = self._alloc.make_stream(self._addr)
+ return self._type._parse(stream, context, f"(pulling {self._name})")
+
+ def pull(self):
+ self._mon_val = self.val = self._pull()
+ return self
+
+ def poll(self):
+ prev_val = self._mon_val
+ self._mon_val = cur_val = self._pull()
+ if not hasattr(cur_val, "diff"):
+ return None
+ if cur_val != prev_val:
+ diff = cur_val.diff(prev_val)
+ assert diff is not None
+ return f"GPUObject {self._name} ({self._size:#x} @ {self._addr:#x}): " + diff
+ else:
+ return None
+
+ @property
+ def _ctx(self):
+ return self._alloc.ctx
+
+ def add_to_mon(self, mon):
+ mon.add(self._addr, self._size, self._name, offset=0,
+ readfn=lambda a, s: self._alloc.agx.iface.readmem(a - self._addr + self._paddr, s))
+
+ def _set_addr(self, addr, paddr=None):
+ self._addr = addr
+ self._paddr = paddr
+ if isinstance(self.val, ConstructClassBase):
+ self.val.set_addr(addr)
+
+ def __getitem__(self, item):
+ return self.val[item]
+ def __setitem__(self, item, value):
+ self.val[item] = value
+
+ def __getattr__(self, attr):
+ return getattr(self.val, attr)
+
+ def __setattr__(self, attr, val):
+ if attr.startswith("_") or attr == "val":
+ self.__dict__[attr] = val
+ return
+
+ setattr(self.val, attr, val)
+
+ def __str__(self):
+ if isinstance(self.val, bytes) and len(self.val) > 128:
+ s_val = f"<{len(self.val)} bytes>"
+ else:
+ s_val = str_value(self.val)
+ return f"GPUObject {self._name} ({self._size:#x} @ {self._addr:#x}): " + s_val
+
+ def free(self):
+ if self._dead:
+ return
+ self._dead = True
+ self._alloc.free(self)
+
+class GPUAllocator:
+ def __init__(self, agx, name, start, size,
+ ctx=0, page_size=16384, va_block=None, guard_pages=1, **kwargs):
+ self.page_size = page_size
+ if va_block is None:
+ va_block = page_size
+ self.agx = agx
+ self.ctx = ctx
+ self.name = name
+ self.va = Heap(start, start + size, block=va_block)
+ self.verbose = 0
+ self.guard_pages = guard_pages
+ self.objects = {}
+ self.flags = kwargs
+ self.align_to_end = True
+
+ def make_stream(self, base):
+ return self.agx.uat.iostream(self.ctx, base, recurse=False)
+
+ def new(self, objtype, name=None, track=True, **kwargs):
+ obj = GPUObject(self, objtype)
+ obj._stream = self.make_stream
+ if name is not None:
+ obj._name = name
+
+ guard_size = self.page_size * self.guard_pages
+
+ size_align = align_up(obj._size, self.page_size)
+ addr = self.va.malloc(size_align + guard_size)
+ paddr = self.agx.u.memalign(self.page_size, size_align)
+ off = 0
+ if self.align_to_end:
+ off = size_align - obj._size
+
+ flags = dict(self.flags)
+ flags.update(kwargs)
+
+ obj._addr_align = addr
+ obj._paddr_align = paddr
+ obj._size_align = size_align
+ self.agx.uat.iomap_at(self.ctx, addr, paddr, size_align, **flags)
+ obj._set_addr(addr + off, paddr + off)
+ obj._map_flags = flags
+
+ self.objects[obj._addr] = obj
+
+ if self.verbose:
+ self.agx.log(f"[{self.name}] Alloc {obj._name} size {obj._size:#x} @ {obj._addr:#x} ({obj._paddr:#x})")
+
+ self.agx.reg_object(obj, track=track)
+ return obj
+
+ def new_buf(self, size, name, track=True):
+ return self.new(HexDump(Bytes(size)), name=name, track=track)
+
+ def buf(self, size, name, track=True):
+ return self.new_buf(size, name, track).push()._addr
+
+ def free(self, obj):
+ obj._dead = True
+ is_private = obj._map_flags.get("AttrIndex", MemoryAttr.Normal) != MemoryAttr.Shared
+ if is_private and obj._addr_align > 0xf8000000000:
+ flags2 = dict(obj._map_flags)
+ flags2["AttrIndex"] = MemoryAttr.Shared
+ self.agx.uat.iomap_at(self.ctx, obj._addr_align, obj._paddr_align,
+ obj._size_align, **flags2)
+ self.agx.uat.flush_dirty()
+ self.agx.uat.handoff.prepare_cacheflush(obj._addr_align, obj._size_align)
+ self.agx.ch.fwctl.send_inval(0x40, obj._addr_align)
+ self.agx.uat.handoff.wait_cacheflush()
+
+ self.agx.uat.iomap_at(self.ctx, obj._addr_align, 0,
+ obj._size_align, VALID=0)
+
+ if is_private and obj._addr_align > 0xf8000000000:
+ self.agx.uat.flush_dirty()
+ self.agx.uat.handoff.complete_cacheflush()
+
+ self.agx.u.free(obj._paddr_align)
+ self.va.free(obj._addr_align)
+ del self.objects[obj._addr]
+ self.agx.unreg_object(obj)
+
+ if self.verbose:
+ self.agx.log(f"[{self.name}] Free {obj._name} size {obj._size:#x} @ {obj._addr:#x} ({obj._paddr:#x})")
diff --git a/tools/proxyclient/m1n1/agx/render.py b/tools/proxyclient/m1n1/agx/render.py
new file mode 100644
index 0000000..b29683b
--- /dev/null
+++ b/tools/proxyclient/m1n1/agx/render.py
@@ -0,0 +1,1075 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, json, zipfile
+
+json.c_make_encoder = None
+
+from m1n1.proxy import *
+from .context import *
+from .event import GPUEventManager
+from .uapi import *
+from m1n1.constructutils import ConstructClass, Ver
+
+def unswizzle(agx, addr, w, h, psize, dump=None, grid=False):
+ iface = agx.u.iface
+
+ tw = 64
+ th = 64
+ ntx = (w + tw - 1) // 64
+ nty = (h + th - 1) // 64
+ data = iface.readmem(addr, ntx * nty * psize * tw * th)
+ new_data = []
+ for y in range(h):
+ ty = y // th
+ for x in range(w):
+ tx = x // tw
+ toff = tw * th * psize * (ty * ntx + tx)
+ j = x & (tw - 1)
+ i = y & (th - 1)
+ off = (
+ ((j & 1) << 0) | ((i & 1) << 1) |
+ ((j & 2) << 1) | ((i & 2) << 2) |
+ ((j & 4) << 2) | ((i & 4) << 3) |
+ ((j & 8) << 3) | ((i & 8) << 4) |
+ ((j & 16) << 4) | ((i & 16) << 5) |
+ ((j & 32) << 5) | ((i & 32) << 6))
+ r,g,b,a = data[toff + psize*off: toff + psize*(off+1)]
+ if grid:
+ if x % 64 == 0 or y % 64 == 0:
+ r,g,b,a = 255,255,255,255
+ elif x % 32 == 0 or y % 32 == 0:
+ r,g,b,a = 128,128,128,255
+ new_data.append(bytes([b, g, r, a]))
+ data = b"".join(new_data)
+ if dump:
+ open(dump, "wb").write(data[:w*h*psize])
+ #iface.writemem(addr, data)
+
+class GPUFrame:
+ def __init__(self, context, filename=None, track=False):
+ self.ctx = context
+ self.agx = context.agx
+ self.objects = []
+ self.cmdbuf = None
+ self.track = track
+ if filename is not None:
+ self.load(filename)
+
+ def add_object(self, obj):
+ self.objects.append(obj)
+
+ def save(self, filename):
+ cmdbuf = self.cmdbuf
+ with zipfile.ZipFile(filename, "w") as zf:
+ cmdbuf_data = json.dumps(cmdbuf, indent=4).encode("utf-8")
+ zf.writestr("cmdbuf.json", cmdbuf_data)
+
+ obj_info = []
+ for obj in self.objects:
+ if obj._data == bytes(obj._size):
+ filename = None
+ else:
+ filename = f"obj_{obj._addr:x}.bin"
+ zf.writestr(filename, obj._data)
+ obj_info.append({
+ "file": filename,
+ "name": obj._name,
+ "addr": obj._addr,
+ "size": obj._size,
+ "map_flags": obj._map_flags,
+ })
+
+ obj_info_data = json.dumps(obj_info, indent=4).encode("utf-8")
+ zf.writestr("objects.json", obj_info_data)
+
+ def load(self, filename):
+ with zipfile.ZipFile(filename, "r") as zf:
+ with zf.open("cmdbuf.json", "r") as fd:
+ self.cmdbuf = drm_asahi_cmdbuf_t.from_json(fd)
+ with zf.open("objects.json", "r") as fd:
+ obj_info = json.load(fd)
+
+ self.objects = []
+ for i in obj_info:
+ filename = i["file"]
+ obj = self.ctx.new_at(i["addr"], Bytes(i["size"]), name=i["name"], track=self.track,
+ **i["map_flags"])
+ if filename is not None:
+ with zf.open(i["file"], "r") as fd:
+ data = fd.read()
+ obj.val = data
+ obj.push()
+ else:
+ obj.val = bytes(i["size"])
+ obj.push()
+ self.objects.append(obj)
+
+class GPUWork:
+ def __init__(self, renderer):
+ self.objects = []
+ self.renderer = renderer
+
+ def add(self, obj):
+ self.objects.append(obj)
+
+ def free(self):
+ for obj in self.objects:
+ obj.free()
+ self.objects = []
+
+class GPURenderer:
+ def __init__(self, ctx, buffers=16, bm_slot=0, queue=0):
+ self.agx = agx = ctx.agx
+ self.queue = queue
+
+ # 0..63
+ self.ctx = ctx
+ self.ctx_id = ctx.ctx
+
+ # 0..255
+ self.buffers = buffers
+ self.buffer_mgr_slot = bm_slot
+
+ ## These MUST go together
+ self.buffer_mgr = GPUBufferManager(agx, ctx, buffers)
+ self.buffer_mgr_initialized = False
+ self.unk_emptybuf = agx.kobj.new_buf(0x40, "unk_emptybuf")
+ self.tpc_size = 0
+
+ ##### Job group
+
+ self.job_list = agx.kshared.new(JobList)
+ self.job_list.first_job = 0
+ self.job_list.last_head = self.job_list._addr # Empty list has self as last_head
+ self.job_list.unkptr_10 = 0
+ self.job_list.push()
+
+ ##### Work Queues
+
+ self.ts3d_1 = agx.kshared.new(Int64ul, name="3D timestamp 1")
+ self.ts3d_2 = agx.kshared.new(Int64ul, name="3D timestamp 2")
+ self.tsta_1 = agx.kshared.new(Int64ul, name="TA timestamp 1")
+ self.tsta_2 = agx.kshared.new(Int64ul, name="TA timestamp 2")
+
+ self.wq_3d = GPU3DWorkQueue(agx, ctx, self.job_list)
+ self.wq_ta = GPUTAWorkQueue(agx, ctx, self.job_list)
+
+ self.wq_3d.info.uuid = 0x3D0000 | bm_slot
+ self.wq_3d.info.push()
+ self.wq_ta.info.uuid = 0x7A0000 | bm_slot
+ self.wq_ta.info.push()
+
+ self.stamp_value_3d = 0x3D000000 | (bm_slot << 16)
+ self.stamp_value_ta = 0x7A000000 | (bm_slot << 16)
+
+ ##### TA stamps
+
+ # start?
+ self.stamp_ta1 = agx.kshared.new(StampCounter, name="TA stamp 1")
+ self.stamp_ta1.value = self.stamp_value_ta
+ self.stamp_ta1.push()
+
+ # complete?
+ self.stamp_ta2 = agx.kobj.new(StampCounter, name="TA stamp 2")
+ self.stamp_ta2.value = self.stamp_value_ta
+ self.stamp_ta2.push()
+
+ ##### 3D stamps
+
+ # start?
+ self.stamp_3d1 = agx.kshared.new(StampCounter, name="3D stamp 1")
+ self.stamp_3d1.value = self.stamp_value_3d
+ self.stamp_3d1.push()
+
+ # complete?
+ self.stamp_3d2 = agx.kobj.new(StampCounter, name="3D stamp 2")
+ self.stamp_3d2.value = self.stamp_value_3d
+ self.stamp_3d2.push()
+
+
+ ##### Things userspace deals with for macOS
+
+ #self.aux_fb = ctx.uobj.new_buf(0x8000, "Aux FB thing")
+ ##self.deflake_1 = ctx.uobj.new_buf(0x20, "Deflake 1")
+ ##self.deflake_2 = ctx.uobj.new_buf(0x280, "Deflake 2")
+ ##self.deflake_3 = ctx.uobj.new_buf(0x540, "Deflake 3")
+ #self.deflake = ctx.uobj.new_buf(0x7e0, "Deflake")
+ #self.unk_buf = ctx.uobj.new(Array(0x800, Int64ul), "Unknown Buffer")
+ #self.unk_buf.val = [0, *range(1, 0x400), *(0x400 * [0])]
+ #self.unk_buf.push()
+
+ ##### Some kind of feedback/status buffer, GPU managed?
+
+ self.event_control = agx.kobj.new(EventControl)
+ self.event_control.event_count = agx.kobj.new(Int32ul, "event_count")
+ self.event_control.event_count.val = 0
+ self.event_control.event_count.push()
+
+ self.event_control.generation = 0
+ self.event_control.cur_count = 0
+ self.event_control.unk_10 = 0x50
+ self.event_control.push()
+
+ self.frames = 0
+
+ self.ev_ta = ev_ta = self.agx.event_mgr.allocate_event()
+ self.ev_3d = ev_3d = self.agx.event_mgr.allocate_event()
+
+ self.work = []
+
+ def submit(self, cmdbuf, wait_for=None):
+ nclusters = 8
+
+ work = GPUWork(self)
+ self.work.append(work)
+
+ self.buffer_mgr.increment()
+
+ aux_fb = self.ctx.uobj.new_buf(0x20000, "Aux FB thing", track=False)
+ work.add(aux_fb)
+
+ # t8103
+ deflake_1_size = 0x540
+ deflake_2_size = 0x280
+ deflake_3_size = 0x20
+
+ # t6002 - 9 times larger instead of 8? works with 8...
+ deflake_1_size *= nclusters
+ deflake_2_size *= nclusters
+ deflake_3_size *= nclusters
+
+ deflake_1 = self.ctx.uobj.new_buf(deflake_1_size, "Deflake 1", track=True)
+ deflake_2 = self.ctx.uobj.new_buf(deflake_2_size, "Deflake 2", track=True)
+ deflake_3 = self.ctx.uobj.new_buf(deflake_3_size, "Deflake 3", track=True)
+ work.add(deflake_1)
+ work.add(deflake_2)
+ work.add(deflake_3)
+
+ unk_buf = self.ctx.uobj.new(Array(0x800, Int64ul), "Unknown Buffer", track=False)
+ work.add(unk_buf)
+
+ unk_buf.val = [0, *range(2, 0x401), *(0x400 * [0])]
+ unk_buf.push()
+
+ work.cmdbuf = cmdbuf
+
+ self.frames += 1
+
+ work.ev_ta = ev_ta = self.ev_ta
+ work.ev_3d = ev_3d = self.ev_3d
+
+ self.ev_ta.rearm()
+ self.ev_3d.rearm()
+
+ self.agx.log(f"ev_ta: {ev_ta.id}")
+ self.agx.log(f"ev_3d: {ev_3d.id}")
+
+ #self.event_control.base_stamp = self.stamp_value >> 8
+ #self.event_control.push()
+
+ self.prev_stamp_value_3d = self.stamp_value_3d
+ self.prev_stamp_value_ta = self.stamp_value_ta
+ self.stamp_value_3d += 0x100
+ self.stamp_value_ta += 0x100
+ self.event_control.event_count.val += 2
+ self.event_control.event_count.push()
+
+ work.stamp_value_3d = self.stamp_value_3d
+ work.stamp_value_ta = self.stamp_value_ta
+
+ agx = self.agx
+ ctx = self.ctx
+
+ work.width = width = cmdbuf.fb_width
+ work.height = height = cmdbuf.fb_height
+
+ ##### TVB allocations / Tiler config
+
+ tile_width = 32
+ tile_height = 32
+ tiles_x = ((width + tile_width - 1) // tile_width)
+ tiles_y = ((height + tile_height - 1) // tile_height)
+ tiles = tiles_x * tiles_y
+
+ mtiles_x = 4
+ mtiles_y = 4
+
+ mtile_x1 = align(((tiles_x + mtiles_x - 1) // mtiles_x), 4)
+ mtile_x2 = 2 * mtile_x1
+ mtile_x3 = 3 * mtile_x1
+ mtile_y1 = align(((tiles_y + mtiles_y - 1) // mtiles_y), 4)
+ mtile_y2 = 2 * mtile_y1
+ mtile_y3 = 3 * mtile_y1
+
+ mtile_stride = mtile_x1 * mtile_y1
+
+ ## TODO: *samples
+ tiles_per_mtile_x = mtile_x1
+ tiles_per_mtile_y = mtile_y1
+
+ tile_blocks_x = (tiles_x + 15) // 16
+ tile_blocks_y = (tiles_y + 15) // 16
+ tile_blocks = tile_blocks_x * tile_blocks_y
+
+ tiling_params = TilingParameters()
+ # rgn_header_size
+ rgn_entry_size = 5
+ tiling_params.size1 = (rgn_entry_size * tiles_per_mtile_x * tiles_per_mtile_y + 3) // 4
+ # PPP_MULTISAMPLECTL
+ tiling_params.unk_4 = 0x88
+ # PPP_CTRL
+ tiling_params.unk_8 = 0x203 # bit 0: GL clip mode
+ # PPP_SCREEN
+ tiling_params.x_max = width - 1
+ tiling_params.y_max = height - 1
+ # TE_SCREEN
+ tiling_params.tile_count = ((tiles_y-1) << 12) | (tiles_x-1)
+ # TE_MTILE1
+ tiling_params.x_blocks = mtile_x3 | (mtile_x2 << 9) | (mtile_x1 << 18)
+ # TE_MTILE2
+ tiling_params.y_blocks = mtile_y3 | (mtile_y2 << 9) | (mtile_y1 << 18)
+ tiling_params.size2 = mtile_stride
+ tiling_params.size3 = 2 * mtile_stride
+ tiling_params.unk_24 = 0x100
+ tiling_params.unk_28 = 0x8000
+
+ tilemap_size = (4 * tiling_params.size1 * mtiles_x * mtiles_y)
+
+ tmtiles_x = tiles_per_mtile_x * mtiles_x
+ tmtiles_y = tiles_per_mtile_y * mtiles_y
+
+ tpc_entry_size = 8
+ tpc_size = tpc_entry_size * tmtiles_x * tmtiles_y * nclusters
+
+ if self.tpc_size < tpc_size:
+ self.tpc = ctx.uobj.new_buf(tpc_size, "TPC", track=True).push()
+ self.tpc_size = tpc_size
+
+ depth_aux_buffer_addr = 0
+ if cmdbuf.depth_buffer:
+ size = align_pot(max(width, tile_width)) * align_pot(max(height, tile_width)) // 32
+ depth_aux_buffer = self.ctx.uobj.new_buf(size, "Depth Aux", track=True)
+ work.add(depth_aux_buffer)
+ depth_aux_buffer_addr = depth_aux_buffer._addr
+
+ stencil_aux_buffer_addr = 0
+ if cmdbuf.stencil_buffer:
+ size = align_pot(max(width, tile_width)) * align_pot(max(height, tile_width)) // 32
+ stencil_aux_buffer = self.ctx.uobj.new_buf(size, "Stencil Aux", track=False)
+ work.add(stencil_aux_buffer)
+ stencil_aux_buffer_addr = stencil_aux_buffer._addr
+
+ #tvb_tilemap_size = 0x80 * mtile_stride
+ tvb_tilemap_size = tilemap_size
+ tvb_tilemap = ctx.uobj.new_buf(tvb_tilemap_size, "TVB Tilemap", track=True).push()
+ work.tvb_tilemap_size = tvb_tilemap_size
+ work.tvb_tilemap = tvb_tilemap
+ work.add(tvb_tilemap)
+
+ # rogue: 0x180 * 4?
+ tvb_heapmeta_size = 0x200
+ #tvb_heapmeta_size = 0x600
+ tvb_heapmeta = ctx.uobj.new_buf(tvb_heapmeta_size, "TVB Heap Meta", track=False).push()
+ work.add(tvb_heapmeta)
+
+ unk_tile_buf1 = self.ctx.uobj.new_buf(tvb_tilemap_size * nclusters, "Unk tile buf 1", track=True)
+ print("tvb_tilemap_size", hex(tvb_tilemap_size))
+ unk_tile_buf2 = self.ctx.uobj.new_buf(0x4 * nclusters, "Unk tile buf 2", track=True)
+ #size = 0xc0 * nclusters
+ size = 0xc80
+ unk_tile_buf3 = self.ctx.uobj.new_buf(size, "Unk tile buf 3", track=True)
+ unk_tile_buf4 = self.ctx.uobj.new_buf(0x280 * nclusters, "Unk tile buf 4", track=True)
+ unk_tile_buf5 = self.ctx.uobj.new_buf(0x30 * nclusters, "Unk tile buf 5", track=True)
+ work.add(unk_tile_buf1)
+ work.add(unk_tile_buf2)
+ work.add(unk_tile_buf3)
+ work.add(unk_tile_buf4)
+ work.add(unk_tile_buf5)
+
+ ##### Buffer stuff?
+
+ # buffer related?
+ bufferthing_buf = ctx.uobj.new_buf(0x80, "BufferThing.unkptr_18", track=True)
+ work.add(bufferthing_buf)
+
+ work.buf_desc = buf_desc = agx.kobj.new(BufferThing, track=False)
+ work.add(buf_desc)
+ buf_desc.unk_0 = 0x0
+ buf_desc.unk_8 = 0x0
+ buf_desc.unk_10 = 0x0
+ buf_desc.unkptr_18 = bufferthing_buf._addr
+ buf_desc.unk_20 = 0x0
+ buf_desc.bm_misc_addr = self.buffer_mgr.misc_obj._addr
+ buf_desc.unk_2c = 0x0
+ buf_desc.unk_30 = 0x0
+ buf_desc.unk_38 = 0x0
+ buf_desc.push()
+
+ uuid_3d = cmdbuf.cmd_3d_id
+ uuid_ta = cmdbuf.cmd_ta_id
+ encoder_id = cmdbuf.encoder_id
+
+ #print(barrier_cmd)
+
+ #self.wq_ta.submit(ta_barrier_cmd)
+
+ ##### 3D barrier command
+
+ barrier_cmd = agx.kobj.new(WorkCommandBarrier, track=False)
+ work.add(barrier_cmd)
+ barrier_cmd.stamp = self.stamp_ta2
+ barrier_cmd.wait_value = self.stamp_value_ta
+ barrier_cmd.stamp_self = self.stamp_value_3d
+ barrier_cmd.event = ev_ta.id
+ barrier_cmd.uuid = uuid_3d
+
+ #print(barrier_cmd)
+
+ self.wq_3d.submit(barrier_cmd)
+
+ ##### 3D execution
+
+ work.wc_3d = wc_3d = agx.kobj.new(WorkCommand3D, track=False)
+ work.add(work.wc_3d)
+ wc_3d.counter = 0
+ wc_3d.context_id = self.ctx_id
+ wc_3d.unk_8 = 0
+ wc_3d.event_control = self.event_control
+ wc_3d.buffer_mgr = self.buffer_mgr.info
+ wc_3d.buf_thing = buf_desc
+ wc_3d.unk_emptybuf_addr = self.unk_emptybuf._addr
+ wc_3d.tvb_tilemap = tvb_tilemap._addr
+ wc_3d.unk_40 = 0x88
+ wc_3d.unk_48 = 0x1
+ wc_3d.tile_blocks_y = mtile_y1
+ wc_3d.tile_blocks_x = mtile_x1
+ wc_3d.unk_50 = 0x0
+ wc_3d.unk_58 = 0x0
+
+ TAN_60 = 1.732051
+ wc_3d.merge_upper_x = TAN_60 / width
+ wc_3d.merge_upper_y = TAN_60 / height
+ wc_3d.unk_68 = 0x0
+ wc_3d.tile_count = tiles
+
+ wc_3d.unk_758 = Flag()
+ wc_3d.unk_75c = Flag()
+ wc_3d.unk_buf = WorkCommand1_UnkBuf()
+ wc_3d.busy_flag = Flag()
+ wc_3d.unk_buf2 = WorkCommand1_UnkBuf2()
+ wc_3d.unk_buf2.unk_0 = 0
+ wc_3d.unk_buf2.unk_8 = 0
+ wc_3d.unk_buf2.unk_10 = 1
+ wc_3d.ts1 = TimeStamp(0)
+ wc_3d.ts2 = TimeStamp(self.ts3d_1._addr)
+ wc_3d.ts3 = TimeStamp(self.ts3d_2._addr)
+ wc_3d.unk_914 = 0
+ wc_3d.unk_918 = 0
+ wc_3d.unk_920 = 0
+ wc_3d.unk_924 = 1
+ # Ventura
+ wc_3d.unk_928_0 = 0
+ wc_3d.unk_928_4 = 0
+ wc_3d.ts_flag = TsFlag()
+
+ # cmdbuf.ds_flags
+ # 0 - no depth
+ # 0x80000 - depth store enable
+ # 0x08000 - depth load enable
+
+ # 0x00044 - compressed depth
+
+ # 0x40000 - stencil store enable
+ # 0x04000 - stencil load enable
+ # 0x00110 - compressed stencil
+
+ # Z store format
+ # 0x4000000 - Depth16Unorm
+
+ # For Depth16Unorm: 0x40000 here also
+ # AFBI.[ 0. 4] unk1 = 0x4c000
+
+ # ASAHI_CMDBUF_SET_WHEN_RELOADING_Z_OR_S
+ # Actually set when loading *and* storing Z, OR loading *and* storing S
+
+ # Structures embedded in WorkCommand3D
+ if True:
+ wc_3d.struct_1 = Start3DStruct1()
+ wc_3d.struct_1.store_pipeline_bind = cmdbuf.store_pipeline_bind
+ wc_3d.struct_1.store_pipeline_addr = cmdbuf.store_pipeline | 4
+ wc_3d.struct_1.unk_8 = 0x0
+ wc_3d.struct_1.unk_c = 0x0
+
+ TAN_60 = 1.732051
+ wc_3d.struct_1.merge_upper_x = TAN_60 / width
+ wc_3d.struct_1.merge_upper_y = TAN_60 / height
+
+ wc_3d.struct_1.unk_18 = 0x0
+ # ISP_MTILE_SIZE
+ wc_3d.struct_1.tile_blocks_y = mtile_y1
+ wc_3d.struct_1.tile_blocks_x = mtile_x1
+ wc_3d.struct_1.unk_24 = 0x0
+ wc_3d.struct_1.tile_counts = ((tiles_y-1) << 12) | (tiles_x-1)
+ wc_3d.struct_1.unk_2c = 0x8
+ wc_3d.struct_1.depth_clear_val1 = cmdbuf.depth_clear_value
+ wc_3d.struct_1.stencil_clear_val1 = cmdbuf.stencil_clear_value
+ wc_3d.struct_1.unk_35 = 0x7 # clear flags? 2 = depth 4 = stencil?
+ wc_3d.struct_1.unk_36 = 0x0
+ wc_3d.struct_1.unk_38 = 0x0
+ wc_3d.struct_1.unk_3c = 0x1
+ wc_3d.struct_1.unk_40 = 0
+ wc_3d.struct_1.unk_44_padding = bytes(0xac)
+ wc_3d.struct_1.depth_bias_array = Start3DArrayAddr(cmdbuf.depth_bias_array)
+ wc_3d.struct_1.scissor_array = Start3DArrayAddr(cmdbuf.scissor_array)
+ wc_3d.struct_1.visibility_result_buffer = 0x0
+ wc_3d.struct_1.unk_118 = 0x0
+ wc_3d.struct_1.unk_120 = [0] * 37
+ wc_3d.struct_1.unk_reload_pipeline = Start3DClearPipelineBinding(
+ cmdbuf.partial_reload_pipeline_bind, cmdbuf.partial_reload_pipeline | 4)
+ wc_3d.struct_1.unk_258 = 0
+ wc_3d.struct_1.unk_260 = 0
+ wc_3d.struct_1.unk_268 = 0
+ wc_3d.struct_1.unk_270 = 0
+ wc_3d.struct_1.reload_pipeline = Start3DClearPipelineBinding(
+ cmdbuf.partial_reload_pipeline_bind, cmdbuf.partial_reload_pipeline | 4)
+ wc_3d.struct_1.depth_flags = cmdbuf.ds_flags | 0x44
+ wc_3d.struct_1.unk_290 = 0x0
+ wc_3d.struct_1.depth_buffer_ptr1 = cmdbuf.depth_buffer
+ wc_3d.struct_1.unk_2a0 = 0x0
+ wc_3d.struct_1.unk_2a8 = 0x0
+ wc_3d.struct_1.depth_buffer_ptr2 = cmdbuf.depth_buffer
+ wc_3d.struct_1.depth_buffer_ptr3 = cmdbuf.depth_buffer
+ wc_3d.struct_1.depth_aux_buffer_ptr = depth_aux_buffer_addr
+ wc_3d.struct_1.stencil_buffer_ptr1 = cmdbuf.stencil_buffer
+ wc_3d.struct_1.unk_2d0 = 0x0
+ wc_3d.struct_1.unk_2d8 = 0x0
+ wc_3d.struct_1.stencil_buffer_ptr2 = cmdbuf.stencil_buffer
+ wc_3d.struct_1.stencil_buffer_ptr3 = cmdbuf.stencil_buffer
+ wc_3d.struct_1.stencil_aux_buffer_ptr = stencil_aux_buffer_addr
+ wc_3d.struct_1.unk_2f8 = [0x0, 0x0]
+ wc_3d.struct_1.aux_fb_unk0 = 4 #0x8 # sometimes 4
+ wc_3d.struct_1.unk_30c = 0x0
+ wc_3d.struct_1.aux_fb = AuxFBInfo(0xc000, 0, width, height)
+ wc_3d.struct_1.unk_320_padding = bytes(0x10)
+ wc_3d.struct_1.unk_partial_store_pipeline = Start3DStorePipelineBinding(
+ cmdbuf.partial_store_pipeline_bind, cmdbuf.partial_store_pipeline | 4)
+ wc_3d.struct_1.partial_store_pipeline = Start3DStorePipelineBinding(
+ cmdbuf.partial_store_pipeline_bind, cmdbuf.partial_store_pipeline | 4)
+ wc_3d.struct_1.depth_clear_val2 = cmdbuf.depth_clear_value
+ wc_3d.struct_1.stencil_clear_val2 = cmdbuf.stencil_clear_value
+ wc_3d.struct_1.unk_375 = 3
+ wc_3d.struct_1.unk_376 = 0x0
+ wc_3d.struct_1.unk_378 = 0x10
+ wc_3d.struct_1.unk_37c = 0x0
+ wc_3d.struct_1.unk_380 = 0x0
+ wc_3d.struct_1.unk_388 = 0x0
+ wc_3d.struct_1.unk_390_0 = 0x0 # Ventura
+ wc_3d.struct_1.depth_dimensions = (width - 1) | ((height - 1) << 15)
+
+ if True:
+ wc_3d.struct_2 = Start3DStruct2()
+ wc_3d.struct_2.unk_0 = 0xa000
+ wc_3d.struct_2.clear_pipeline = Start3DClearPipelineBinding(
+ cmdbuf.load_pipeline_bind, cmdbuf.load_pipeline | 4)
+ wc_3d.struct_2.unk_18 = 0x88
+ wc_3d.struct_2.scissor_array = cmdbuf.scissor_array
+ wc_3d.struct_2.depth_bias_array = cmdbuf.depth_bias_array
+ wc_3d.struct_2.aux_fb = wc_3d.struct_1.aux_fb
+ # ISP_ZLS_PIXELS
+ wc_3d.struct_2.depth_dimensions = wc_3d.struct_1.depth_dimensions
+ wc_3d.struct_2.visibility_result_buffer = 0x0
+ # ISP_ZLSCTL
+ wc_3d.struct_2.depth_flags = cmdbuf.ds_flags
+ wc_3d.struct_2.unk_58_g14_0 = 0x4040404
+ wc_3d.struct_2.unk_58_g14_8 = 0
+ wc_3d.struct_2.depth_buffer_ptr1 = cmdbuf.depth_buffer
+ wc_3d.struct_2.depth_buffer_ptr2 = cmdbuf.depth_buffer
+ wc_3d.struct_2.unk_68_g14_0 = 0
+ wc_3d.struct_2.stencil_buffer_ptr1 = cmdbuf.stencil_buffer
+ wc_3d.struct_2.stencil_buffer_ptr2 = cmdbuf.stencil_buffer
+ wc_3d.struct_2.unk_78 = [0] * 4
+ wc_3d.struct_2.depth_aux_buffer_ptr1 = depth_aux_buffer_addr
+ wc_3d.struct_2.unk_a0 = 0
+ wc_3d.struct_2.depth_aux_buffer_ptr2 = depth_aux_buffer_addr
+ wc_3d.struct_2.unk_b0 = 0
+ wc_3d.struct_2.stencil_aux_buffer_ptr1 = stencil_aux_buffer_addr
+ wc_3d.struct_2.unk_c0 = 0
+ wc_3d.struct_2.stencil_aux_buffer_ptr2 = stencil_aux_buffer_addr
+ wc_3d.struct_2.unk_d0 = 0
+ wc_3d.struct_2.tvb_tilemap = tvb_tilemap._addr
+ wc_3d.struct_2.tvb_heapmeta_addr = tvb_heapmeta._addr
+ wc_3d.struct_2.unk_e8 = tiling_params.size1 << 24
+ wc_3d.struct_2.tvb_heapmeta_addr2 = tvb_heapmeta._addr
+ # 0x10000 - clear empty tiles
+ # ISP_CTL (but bits seem to have moved)
+ wc_3d.struct_2.unk_f8 = 0x10280 #0x10280 # TODO: varies 0, 0x280, 0x10000, 0x10280
+ wc_3d.struct_2.aux_fb_ptr = aux_fb._addr
+ wc_3d.struct_2.unk_108 = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0]
+ wc_3d.struct_2.pipeline_base = self.ctx.pipeline_base
+ wc_3d.struct_2.unk_140 = 0x8c60
+ wc_3d.struct_2.unk_148 = 0x0
+ wc_3d.struct_2.unk_150 = 0x0
+ wc_3d.struct_2.unk_158 = 0x1c
+ wc_3d.struct_2.unk_160 = 0
+ wc_3d.struct_2.unk_168_padding = bytes(0x1d8)
+ wc_3d.struct_2.unk_198_padding = bytes(0x1a8)
+
+ if True:
+ wc_3d.struct_6 = Start3DStruct6()
+ wc_3d.struct_6.tvb_overflow_count = 0x0
+ wc_3d.struct_6.unk_8 = 0x0 # 1?
+ wc_3d.struct_6.unk_c = 0x0 # 1?
+ wc_3d.struct_6.unk_10 = 0x0
+ wc_3d.struct_6.encoder_id = cmdbuf.encoder_id
+ wc_3d.struct_6.unk_1c = 0xffffffff
+ wc_3d.struct_6.unknown_buffer = unk_buf._addr
+ wc_3d.struct_6.unk_28 = 0x0
+ wc_3d.struct_6.unk_30 = 0x0
+ wc_3d.struct_6.unk_34 = 0x0
+
+ if True:
+ wc_3d.struct_7 = Start3DStruct7()
+ wc_3d.struct_7.unk_0 = 0x0
+ wc_3d.struct_7.stamp1 = self.stamp_3d1
+ wc_3d.struct_7.stamp2 = self.stamp_3d2
+ wc_3d.struct_7.stamp_value = self.stamp_value_3d
+ wc_3d.struct_7.ev_3d = ev_3d.id
+ wc_3d.struct_7.evctl_index = 0x0
+ wc_3d.struct_7.unk_24 = 1
+ wc_3d.struct_7.uuid = uuid_3d
+ wc_3d.struct_7.prev_stamp_value = self.prev_stamp_value_3d >> 8
+ wc_3d.struct_7.unk_30 = 0x0
+
+ wc_3d.set_addr() # Update inner structure addresses
+ #print("WC3D", hex(wc_3d._addr))
+ #print(" s1", hex(wc_3d.struct_1._addr))
+ #print(" s2", hex(wc_3d.struct_2._addr))
+ #print(" s6", hex(wc_3d.struct_6._addr))
+ #print(" s7", hex(wc_3d.struct_7._addr))
+
+ ms = GPUMicroSequence(agx)
+
+ start_3d = Start3DCmd()
+ start_3d.struct1 = wc_3d.struct_1 # 0x44 bytes!
+ start_3d.struct2 = wc_3d.struct_2 # 0x168 bytes!
+ start_3d.buf_thing = buf_desc
+ start_3d.stats_ptr = agx.initdata.regionB.stats_3d.stats._addr
+ start_3d.busy_flag_ptr = wc_3d.busy_flag._addr
+ start_3d.struct6 = wc_3d.struct_6 # 4 bytes!
+ start_3d.struct7 = wc_3d.struct_7 # 4 bytes!
+ start_3d.cmdqueue_ptr = self.wq_3d.info._addr
+ start_3d.workitem_ptr = wc_3d._addr
+ start_3d.context_id = self.ctx_id
+ start_3d.unk_50 = 0x1
+ start_3d.event_generation = self.event_control.generation
+ start_3d.buffer_mgr_slot = self.buffer_mgr_slot
+ start_3d.unk_5c = 0x0
+ start_3d.prev_stamp_value = self.prev_stamp_value_3d >> 8
+ start_3d.unk_68 = 0x0
+ start_3d.unk_buf_ptr = wc_3d.unk_758._addr
+ start_3d.unk_buf2_ptr = wc_3d.unk_buf2._addr
+ start_3d.unk_7c = 0x0
+ start_3d.unk_80 = 0x0
+ start_3d.unk_84 = 0x0
+ start_3d.uuid = uuid_3d
+ start_3d.attachments = []
+ start_3d.unk_194 = 0
+ start_3d.unkptr_19c = self.event_control.unk_buf._addr
+
+ work.fb = None
+ work.depth = None
+
+ for i in cmdbuf.attachments[:cmdbuf.attachment_count]:
+ cache_lines = align_up(i.size, 128) // 128
+ order = 1 # FIXME
+ start_3d.attachments.append(Attachment(i.pointer, cache_lines, 0x17, order)) # FIXME check
+ if work.fb is None and i.type == ASAHI_ATTACHMENT_C:
+ work.fb = i.pointer
+ if work.depth is None and i.type == ASAHI_ATTACHMENT_Z:
+ work.depth = i.pointer
+ start_3d.attachments += [Attachment(0, 0, 0, 0)] * (16 - len(start_3d.attachments))
+ start_3d.num_attachments = cmdbuf.attachment_count
+ start_3d.unk_190 = 0x0
+
+ start_3d_offset = ms.append(start_3d)
+
+ ts1 = TimestampCmd()
+ ts1.unk_1 = 0x0
+ ts1.unk_2 = 0x0
+ ts1.unk_3 = 0x80
+ ts1.ts0_addr = wc_3d.ts1._addr
+ ts1.ts1_addr = wc_3d.ts2._addr
+ ts1.ts2_addr = wc_3d.ts2._addr
+ ts1.cmdqueue_ptr = self.wq_3d.info._addr
+ ts1.unk_24 = 0x0
+ if Ver.check("V >= V13_0B4"):
+ ts1.unkptr_2c_0 = wc_3d.ts_flag._addr
+ ts1.uuid = uuid_3d
+ ts1.unk_30_padding = 0x0
+ ms.append(ts1)
+
+ ms.append(WaitForInterruptCmd(0, 1, 0))
+
+ ts2 = TimestampCmd()
+ ts2.unk_1 = 0x0
+ ts2.unk_2 = 0x0
+ ts2.unk_3 = 0x0
+ ts2.ts0_addr = wc_3d.ts1._addr
+ ts2.ts1_addr = wc_3d.ts2._addr
+ ts2.ts2_addr = wc_3d.ts3._addr
+ ts2.cmdqueue_ptr = self.wq_3d.info._addr
+ ts2.unk_24 = 0x0
+ if Ver.check("V >= V13_0B4"):
+ ts2.unkptr_2c_0 = wc_3d.ts_flag._addr
+ ts2.uuid = uuid_3d
+ ts2.unk_30_padding = 0x0
+ ms.append(ts2)
+
+ finish_3d = Finalize3DCmd()
+ finish_3d.uuid = uuid_3d
+ finish_3d.unk_8 = 0
+ finish_3d.stamp = self.stamp_3d2
+ finish_3d.stamp_value = self.stamp_value_3d
+ finish_3d.unk_18 = 0
+ finish_3d.buf_thing = buf_desc
+ finish_3d.buffer_mgr = self.buffer_mgr.info
+ finish_3d.unk_2c = 1
+ finish_3d.stats_ptr = agx.initdata.regionB.stats_3d.stats._addr
+ finish_3d.struct7 = wc_3d.struct_7
+ finish_3d.busy_flag_ptr = wc_3d.busy_flag._addr
+ finish_3d.cmdqueue_ptr = self.wq_3d.info._addr
+ finish_3d.workitem_ptr = wc_3d._addr
+ finish_3d.unk_5c = self.ctx_id
+ finish_3d.unk_buf_ptr = wc_3d.unk_758._addr
+ finish_3d.unk_6c = 0
+ finish_3d.unk_74 = 0
+ finish_3d.unk_7c = 0
+ finish_3d.unk_84 = 0
+ finish_3d.unk_8c = 0
+ finish_3d.unk_8c_g14 = 0
+ finish_3d.restart_branch_offset = start_3d_offset - ms.off
+ finish_3d.unk_98 = 0
+ finish_3d.unk_9c = bytes(0x10)
+ ms.append(finish_3d)
+ ms.finalize()
+
+ work.add(ms.obj)
+
+ wc_3d.microsequence_ptr = ms.obj._addr
+ wc_3d.microsequence_size = ms.size
+
+ print(wc_3d)
+ self.wq_3d.submit(wc_3d)
+
+ ##### TA init
+
+ #print(ctx_info)
+ if wait_for is not None:
+ barrier_cmd = agx.kobj.new(WorkCommandBarrier, track=False)
+ work.add(barrier_cmd)
+ if not isinstance(wait_for, tuple):
+ barrier_cmd.stamp = wait_for.renderer.stamp_3d2
+ barrier_cmd.wait_value = wait_for.stamp_value_3d
+ barrier_cmd.event = wait_for.ev_3d.id
+ else:
+ barrier_cmd.stamp_addr = wait_for[0]
+ barrier_cmd.wait_value = wait_for[1]
+ barrier_cmd.event = wait_for[2]
+
+ barrier_cmd.stamp_self = self.stamp_value_ta
+ barrier_cmd.uuid = uuid_ta
+
+ self.wq_ta.submit(barrier_cmd)
+
+ if not self.buffer_mgr_initialized:
+ wc_initbm = agx.kobj.new(WorkCommandInitBM, track=False)
+ work.add(wc_initbm)
+ wc_initbm.context_id = self.ctx_id
+ wc_initbm.buffer_mgr_slot = self.buffer_mgr_slot
+ wc_initbm.unk_c = 0
+ wc_initbm.unk_10 = self.buffer_mgr.info.block_count
+ wc_initbm.buffer_mgr = self.buffer_mgr.info
+ wc_initbm.stamp_value = self.stamp_value_ta
+
+ self.wq_ta.submit(wc_initbm)
+
+ self.buffer_mgr_initialized = True
+
+ ##### TA execution
+
+ work.wc_ta = wc_ta = agx.kobj.new(WorkCommandTA, track=False)
+ work.add(work.wc_ta)
+ wc_ta.context_id = self.ctx_id
+ wc_ta.counter = 1
+ wc_ta.unk_8 = 0
+ wc_ta.event_control = self.event_control
+ wc_ta.buffer_mgr_slot = self.buffer_mgr_slot
+ wc_ta.buffer_mgr = self.buffer_mgr.info
+ wc_ta.buf_thing = buf_desc
+ wc_ta.unk_emptybuf_addr = wc_3d.unk_emptybuf_addr
+ wc_ta.unk_34 = 0x0
+
+ wc_ta.unk_154 = bytes(0x268)
+ wc_ta.unk_3e8 = bytes(0x74)
+ wc_ta.unk_594 = WorkCommand0_UnkBuf()
+
+ wc_ta.ts1 = TimeStamp(0)
+ wc_ta.ts2 = TimeStamp(self.tsta_1._addr)
+ wc_ta.ts3 = TimeStamp(self.tsta_2._addr)
+ wc_ta.unk_5c4 = 0
+ wc_ta.unk_5c8 = 0
+ wc_ta.unk_5cc = 0
+ wc_ta.unk_5d0 = 0
+ wc_ta.unk_5d4 = 1 #0x27 #1
+ # Ventura
+ wc_ta.unk_5e0 = 0
+ wc_ta.unk_5e4 = 0
+ wc_ta.ts_flag = TsFlag()
+
+ # Structures embedded in WorkCommandTA
+ if True:
+ wc_ta.tiling_params = tiling_params
+
+ if True:
+ wc_ta.struct_2 = StartTACmdStruct2()
+ wc_ta.struct_2.unk_0 = 0x200
+ wc_ta.struct_2.unk_8 = 0x1e3ce508 # fixed
+ wc_ta.struct_2.unk_c = 0x1e3ce508 # fixed
+ wc_ta.struct_2.tvb_tilemap = tvb_tilemap._addr
+ wc_ta.struct_2.tvb_cluster_tilemaps = unk_tile_buf1._addr
+ wc_ta.struct_2.tpc = self.tpc._addr
+ wc_ta.struct_2.tvb_heapmeta_addr = tvb_heapmeta._addr | 0x8000_0000_0000_0000
+ wc_ta.struct_2.iogpu_unk_54 = 0x6b0003 # fixed
+ wc_ta.struct_2.iogpu_unk_55 = 0x3a0012 # fixed
+ wc_ta.struct_2.iogpu_unk_56 = 0x1 # fixed
+ wc_ta.struct_2.tvb_cluster_meta1 = unk_tile_buf2._addr | 0x4_0000_0000_0000
+ wc_ta.struct_2.unk_48 = 0xa000
+ wc_ta.struct_2.unk_50 = 0x88 # fixed
+ wc_ta.struct_2.tvb_heapmeta_addr2 = tvb_heapmeta._addr
+ wc_ta.struct_2.unk_60 = 0x0 # fixed
+ wc_ta.struct_2.core_mask = 0xffffffffffffffff
+ #wc_ta.struct_2.unk_68 = 0xff << (8 * (self.buffer_mgr_slot % 8))
+ wc_ta.struct_2.iogpu_deflake_1 = deflake_1._addr
+ wc_ta.struct_2.iogpu_deflake_2 = deflake_2._addr
+ wc_ta.struct_2.unk_80 = 0x1 # fixed
+ wc_ta.struct_2.iogpu_deflake_3 = deflake_3._addr | 0x4_0000_0000_0000 # check
+ wc_ta.struct_2.encoder_addr = cmdbuf.encoder_ptr
+ wc_ta.struct_2.tvb_cluster_meta2 = unk_tile_buf3._addr
+ wc_ta.struct_2.tvb_cluster_meta3 = unk_tile_buf4._addr
+ wc_ta.struct_2.tiling_control = 0xa040 #0xa041 # fixed
+ wc_ta.struct_2.unk_b0 = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0] # fixed
+ wc_ta.struct_2.pipeline_base = self.ctx.pipeline_base
+ wc_ta.struct_2.tvb_cluster_meta4 = unk_tile_buf5._addr | 0x3000_0000_0000_0000
+ wc_ta.struct_2.unk_f0 = 0x20 # fixed
+ wc_ta.struct_2.unk_f8 = 0x8c60 # fixed
+ wc_ta.struct_2.unk_100 = [0x0, 0x0, 0x0] # fixed
+ wc_ta.struct_2.unk_118 = 0x1c # fixed
+
+ if True:
+ wc_ta.struct_3 = StartTACmdStruct3()
+ wc_ta.struct_3.unk_480 = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0] # fixed
+ wc_ta.struct_3.unk_498 = 0x0 # fixed
+ wc_ta.struct_3.unk_4a0 = 0x0 # fixed
+ wc_ta.struct_3.iogpu_deflake_1 = deflake_1._addr
+ wc_ta.struct_3.unk_4ac = 0x0 # fixed
+ wc_ta.struct_3.unk_4b0 = 0x0 # fixed
+ wc_ta.struct_3.unk_4b8 = 0x0 # fixed
+ wc_ta.struct_3.unk_4bc = 0x0 # fixed
+ wc_ta.struct_3.unk_4c4_padding = bytes(0x48)
+ wc_ta.struct_3.unk_50c = 0x0 # fixed
+ wc_ta.struct_3.unk_510 = 0x0 # fixed
+ wc_ta.struct_3.unk_518 = 0x0 # fixed
+ wc_ta.struct_3.unk_520 = 0x0 # fixed
+ wc_ta.struct_3.unk_528 = 0x0 # fixed
+ wc_ta.struct_3.unk_52c = 0x0 # fixed
+ wc_ta.struct_3.unk_530 = 0x0 # fixed
+ wc_ta.struct_3.encoder_id = cmdbuf.encoder_id
+ wc_ta.struct_3.unk_538 = 0x0 # fixed
+ wc_ta.struct_3.unk_53c = 0xffffffff
+ wc_ta.struct_3.unknown_buffer = wc_3d.struct_6.unknown_buffer
+ wc_ta.struct_3.unk_548 = 0x0 # fixed
+ wc_ta.struct_3.unk_550 = [
+ 0x0, 0x0, # fixed
+ 0x0, # 1 for boot stuff?
+ 0x0, 0x0, 0x0] # fixed
+ wc_ta.struct_3.stamp1 = self.stamp_ta1
+ wc_ta.struct_3.stamp2 = self.stamp_ta2
+ wc_ta.struct_3.stamp_value = self.stamp_value_ta
+ wc_ta.struct_3.ev_ta = ev_ta.id
+ wc_ta.struct_3.evctl_index = 0
+ wc_ta.struct_3.unk_584 = 0x0 # 1 for boot stuff?
+ wc_ta.struct_3.uuid2 = uuid_ta
+ wc_ta.struct_3.prev_stamp_value = self.prev_stamp_value_ta >> 8
+ wc_ta.struct_3.unk_590 = 0 # sometimes 1?
+
+ wc_ta.set_addr() # Update inner structure addresses
+ #print("wc_ta", wc_ta)
+
+ ms = GPUMicroSequence(agx)
+
+ start_ta = StartTACmd()
+ start_ta.tiling_params = wc_ta.tiling_params
+ start_ta.struct2 = wc_ta.struct_2 # len 0x120
+ start_ta.buffer_mgr = self.buffer_mgr.info
+ start_ta.buf_thing = buf_desc
+ start_ta.stats_ptr = agx.initdata.regionB.stats_ta.stats._addr
+ start_ta.cmdqueue_ptr = self.wq_ta.info._addr
+ start_ta.context_id = self.ctx_id
+ start_ta.unk_38 = 1
+ start_ta.event_generation = self.event_control.generation
+ start_ta.buffer_mgr_slot = self.buffer_mgr_slot
+ start_ta.unk_48 = 0#1 #0
+ start_ta.unk_50 = 0
+ start_ta.struct3 = wc_ta.struct_3
+
+ start_ta.unkptr_5c = wc_ta.unk_594._addr
+ start_ta.unk_64 = 0x0 # fixed
+ start_ta.unk_68 = 0x0 # sometimes 1?
+ start_ta.uuid = uuid_ta
+ start_ta.unk_70 = 0x0 # fixed
+ start_ta.unk_74 = [ # fixed
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ ]
+ start_ta.unk_15c = 0x0 # fixed
+ start_ta.unk_160 = 0x0 # fixed
+ start_ta.unk_168 = 0x0 # fixed
+ start_ta.unk_16c = 0x0 # fixed
+ start_ta.unk_170 = 0x0 # fixed
+ start_ta.unk_178 = 0x0 # fixed?
+ start_ta.unk_17c = 0x0
+ start_ta.unkptr_180 = self.event_control.unk_buf._addr
+ start_ta.unk_188 = 0x0
+
+ start_ta_offset = ms.append(start_ta)
+
+ ts1 = TimestampCmd()
+ ts1.unk_1 = 0x0
+ ts1.unk_2 = 0x0
+ ts1.unk_3 = 0x80
+ ts1.ts0_addr = wc_ta.ts1._addr
+ ts1.ts1_addr = wc_ta.ts2._addr
+ ts1.ts2_addr = wc_ta.ts2._addr
+ ts1.cmdqueue_ptr = self.wq_ta.info._addr
+ ts1.unk_24 = 0x0
+ if Ver.check("V >= V13_0B4"):
+ ts1.unkptr_2c_0 = wc_ta.ts_flag._addr
+ ts1.uuid = uuid_ta
+ ts1.unk_30_padding = 0x0
+ ms.append(ts1)
+
+ ms.append(WaitForInterruptCmd(1, 0, 0))
+
+ ts2 = TimestampCmd()
+ ts2.unk_1 = 0x0
+ ts2.unk_2 = 0x0
+ ts2.unk_3 = 0x0
+ ts2.ts0_addr = wc_ta.ts1._addr
+ ts2.ts1_addr = wc_ta.ts2._addr
+ ts2.ts2_addr = wc_ta.ts3._addr
+ ts2.cmdqueue_ptr = self.wq_ta.info._addr
+ ts2.unk_24 = 0x0
+ if Ver.check("V >= V13_0B4"):
+ ts2.unkptr_2c_0 = wc_ta.ts_flag._addr
+ ts2.uuid = uuid_ta
+ ts2.unk_30_padding = 0x0
+ ms.append(ts2)
+
+ finish_ta = FinalizeTACmd()
+ finish_ta.buf_thing = buf_desc
+ finish_ta.buffer_mgr = self.buffer_mgr.info
+ finish_ta.stats_ptr = agx.initdata.regionB.stats_ta.stats._addr
+ finish_ta.cmdqueue_ptr = self.wq_ta.info._addr
+ finish_ta.context_id = self.ctx_id
+ finish_ta.unk_28 = 0x0 # fixed
+ finish_ta.struct3 = wc_ta.struct_3
+ finish_ta.unk_34 = 0x0 # fixed
+ finish_ta.uuid = uuid_ta
+ finish_ta.stamp = self.stamp_ta2
+ finish_ta.stamp_value = self.stamp_value_ta
+ finish_ta.unk_48 = 0x0 # fixed
+ finish_ta.unk_50 = 0x0 # fixed
+ finish_ta.unk_54 = 0x0 # fixed
+ finish_ta.unk_58 = 0x0 # fixed
+ finish_ta.unk_60 = 0x0 # fixed
+ finish_ta.unk_64 = 0x0 # fixed
+ finish_ta.unk_68 = 0x0 # fixed
+ finish_ta.unk_6c_g14 = 0 # fixed
+ finish_ta.restart_branch_offset = start_ta_offset - ms.off
+ finish_ta.unk_70 = 0x0 # fixed
+ finish_ta.unk_74 = bytes(0x10) # Ventura
+ ms.append(finish_ta)
+
+ ms.finalize()
+
+ work.add(ms.obj)
+
+ wc_ta.unkptr_45c = self.tpc._addr
+ wc_ta.tvb_size = tpc_size
+ wc_ta.microsequence_ptr = ms.obj._addr
+ wc_ta.microsequence_size = ms.size
+ wc_ta.ev_3d = ev_3d.id
+ wc_ta.stamp_value = self.stamp_value_ta
+
+ print(wc_ta)
+ self.wq_ta.submit(wc_ta)
+
+ self.agx.log("Submit done")
+ return work
+
+ def run(self):
+ ##### Run queues
+ self.agx.log("Run queues")
+ self.agx.ch.queue[self.queue].q_3D.run(self.wq_3d, self.ev_3d.id)
+ self.agx.ch.queue[self.queue].q_TA.run(self.wq_ta, self.ev_ta.id)
+ self.agx.log("Run done")
+
+ def wait(self):
+ self.agx.log("Waiting...")
+ work = self.work[-1]
+
+ ##### Wait for work completion
+ while not self.ev_3d.fired:
+ self.agx.wait_for_events(timeout=2.0)
+
+ if not self.ev_3d.fired:
+ self.agx.log("3D event didn't fire")
+
+ self.agx.log(f"Event {self.ev_3d.id} fired")
+ #print("Stamps:")
+ #print(self.stamp_ta1.pull())
+ #print(self.stamp_ta2.pull())
+ #print(self.stamp_3d1.pull())
+ #print(self.stamp_3d2.pull())
+
+ #print("WCs:")
+ #print(work.wc_3d.pull())
+ #print(work.wc_ta.pull())
+
+ #if work.fb is not None and work.width and work.height:
+ if work.fb is not None and work.width and work.height and work.width == 1920:
+ agx = self.agx
+ self.agx.log(f"Render {work.width}x{work.height} @ {work.fb:#x}")
+ base, obj = self.agx.find_object(work.fb, self.ctx_id)
+
+ #unswizzle(agx, obj._paddr, work.width, work.height, 4, "fb.bin", grid=False)
+ #open("fb.bin", "wb").write(self.agx.u.iface.readmem(obj._paddr, work.width*work.height*4))
+ #os.system(f"convert -size {work.width}x{work.height} -depth 8 rgba:fb.bin -alpha off frame{self.frames}.png")
+ self.agx.p.fb_blit(0, 0, work.width, work.height, obj._paddr, work.width, PIX_FMT.XBGR)
+
+ if False: #work.depth is not None:
+ base, obj = self.agx.find_object(work.depth, self.ctx_id)
+
+ width = align_up(work.width, 64)
+ height = align_up(work.height, 64)
+
+ obj.pull()
+ chexdump(obj.val)
+
+ unswizzle(self.agx, obj._paddr, work.width, work.height, 4, "depth.bin", grid=False)
+ os.system(f"convert -size {work.width}x{work.height} -depth 8 rgba:depth.bin -alpha off depth.png")
+
+ for i in self.work:
+ i.free()
+
+ self.work = []
diff --git a/tools/proxyclient/m1n1/agx/shim.py b/tools/proxyclient/m1n1/agx/shim.py
new file mode 100644
index 0000000..253812a
--- /dev/null
+++ b/tools/proxyclient/m1n1/agx/shim.py
@@ -0,0 +1,244 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+
+import errno, ctypes, sys, atexit, os, os.path, mmap
+from construct import *
+
+from m1n1 import malloc
+from m1n1.utils import Register32
+from m1n1.agx import AGX
+from m1n1.agx.render import *
+from m1n1.agx.uapi import *
+from m1n1.proxyutils import *
+from m1n1.utils import *
+
+PAGE_SIZE = 32768
+SHIM_MEM_SIZE = 4 * 1024 * 1024 * 1024
+
+class IOCTL(Register32):
+ NR = 7, 0
+ TYPE = 15, 8
+ SIZE = 29, 16
+ DIR = 31, 30
+
+_IOC_NONE = 0
+_IOC_WRITE = 1
+_IOC_READ = 2
+
+_IO = lambda type, nr: IOCTL(TYPE=type, NR=nr, SIZE=0, DIR=_IOC_NONE)
+_IOR = lambda type, nr, size: IOCTL(TYPE=type, NR=nr, SIZE=size, DIR=_IOC_READ)
+_IOW = lambda type, nr, size: IOCTL(TYPE=type, NR=nr, SIZE=size, DIR=_IOC_WRITE)
+_IOWR = lambda type, nr, size: IOCTL(TYPE=type, NR=nr, SIZE=size, DIR=_IOC_READ|_IOC_WRITE)
+
+DRM_IOCTL_BASE = ord('d')
+
+def IO(nr):
+ def dec(f):
+ f._ioctl = _IO(DRM_IOCTL_BASE, nr)
+ return f
+ return dec
+
+def IOR(nr, cls):
+ def dec(f):
+ f._ioctl = _IOR(DRM_IOCTL_BASE, nr, cls.sizeof())
+ f._arg_cls = cls
+ return f
+ return dec
+
+def IOW(nr, cls):
+ def dec(f):
+ f._ioctl = _IOW(DRM_IOCTL_BASE, nr, cls.sizeof())
+ f._arg_cls = cls
+ return f
+ return dec
+
+def IOWR(nr, cls):
+ def dec(f):
+ f._ioctl = _IOWR(DRM_IOCTL_BASE, nr, cls.sizeof())
+ f._arg_cls = cls
+ return f
+ return dec
+
+class DRMAsahiShim:
+ def __init__(self, memfd):
+ self.memfd = memfd
+ self.initialized = False
+ self.ioctl_map = {}
+ for key in dir(self):
+ f = getattr(self, key)
+ ioctl = getattr(f, "_ioctl", None)
+ if ioctl is not None:
+ self.ioctl_map[ioctl.value] = ioctl, f
+ self.bos = {}
+ self.pull_buffers = bool(os.getenv("ASAHI_SHIM_PULL"))
+ self.dump_frames = bool(os.getenv("ASAHI_SHIM_DUMP"))
+ self.frame = 0
+ self.agx = None
+
+ def read_buf(self, ptr, size):
+ return ctypes.cast(ptr, ctypes.POINTER(ctypes.c_ubyte * size))[0]
+
+ def init_agx(self):
+ from m1n1.setup import p, u, iface
+
+ p.pmgr_adt_clocks_enable("/arm-io/gfx-asc")
+ p.pmgr_adt_clocks_enable("/arm-io/sgx")
+
+ self.agx = agx = AGX(u)
+
+ mon = RegMonitor(u, ascii=True, bufsize=0x8000000)
+ agx.mon = mon
+
+ sgx = agx.sgx_dev
+ #mon.add(sgx.gpu_region_base, sgx.gpu_region_size, "contexts")
+ #mon.add(sgx.gfx_shared_region_base, sgx.gfx_shared_region_size, "gfx-shared")
+ #mon.add(sgx.gfx_handoff_base, sgx.gfx_handoff_size, "gfx-handoff")
+
+ #mon.add(agx.initdasgx.gfx_handoff_base, sgx.gfx_handoff_size, "gfx-handoff")
+
+ atexit.register(p.reboot)
+ agx.start()
+
+ def init(self):
+ if self.initialized:
+ return
+
+ self.init_agx()
+ self.ctx = GPUContext(self.agx)
+ self.ctx.bind(0x17)
+ self.renderer = GPURenderer(self.ctx, 0x40, bm_slot=10, queue=1)
+
+ self.initialized = True
+
+ @IOW(DRM_COMMAND_BASE + 0x00, drm_asahi_submit_t)
+ def submit(self, fd, args):
+ sys.stdout.write(".")
+ sys.stdout.flush()
+
+ size = drm_asahi_cmdbuf_t.sizeof()
+ cmdbuf = drm_asahi_cmdbuf_t.parse(self.read_buf(args.cmdbuf, size))
+
+ self.log("Pushing objects...")
+ for obj in self.bos.values():
+ #if obj._skipped_pushes > 64:# and obj._addr > 0x1200000000 and obj._size > 131072:
+ #continue
+ obj.push(True)
+ self.log("Push done")
+
+ attachment_objs = []
+ for i in cmdbuf.attachments:
+ for obj in self.bos.values():
+ if obj._addr == i.pointer:
+ attachment_objs.append(obj)
+
+ if self.dump_frames:
+ name = f"shim_frame{self.frame:03d}.agx"
+ f = GPUFrame(self.renderer.ctx)
+ f.cmdbuf = cmdbuf
+ for obj in self.bos.values():
+ f.add_object(obj)
+ f.save(name)
+
+ self.renderer.submit(cmdbuf)
+ self.renderer.run()
+ self.renderer.wait()
+
+ if self.pull_buffers:
+ self.log("Pulling buffers...")
+ for obj in attachment_objs:
+ obj.pull()
+ obj._map[:] = obj.val
+ obj.val = obj._map
+ self.log("Pull done")
+
+ #print("HEAP STATS")
+ #self.ctx.uobj.va.check()
+ #self.ctx.gobj.va.check()
+ #self.ctx.pobj.va.check()
+ #self.agx.kobj.va.check()
+ #self.agx.cmdbuf.va.check()
+ #self.agx.kshared.va.check()
+ #self.agx.kshared2.va.check()
+
+ self.frame += 1
+ return 0
+
+ @IOW(DRM_COMMAND_BASE + 0x01, drm_asahi_wait_bo_t)
+ def wait_bo(self, fd, args):
+ self.log("Wait BO!", args)
+ return 0
+
+ @IOWR(DRM_COMMAND_BASE + 0x02, drm_asahi_create_bo_t)
+ def create_bo(self, fd, args):
+ memfd_offset = args.offset
+
+ if args.flags & ASAHI_BO_PIPELINE:
+ alloc = self.renderer.ctx.pobj
+ else:
+ alloc = self.renderer.ctx.gobj
+
+ obj = alloc.new(args.size, name=f"GBM offset {memfd_offset:#x}", track=False)
+ obj._memfd_offset = memfd_offset
+ obj._pushed = False
+ obj.val = obj._map = mmap.mmap(self.memfd, args.size, offset=memfd_offset)
+ self.bos[memfd_offset] = obj
+ args.offset = obj._addr
+
+ if args.flags & ASAHI_BO_PIPELINE:
+ args.offset -= self.renderer.ctx.pipeline_base
+
+ self.log(f"Create BO @ {memfd_offset:#x}")
+ return 0
+
+ @IOWR(DRM_COMMAND_BASE + 0x04, drm_asahi_get_param_t)
+ def get_param(self, fd, args):
+ self.log("Get Param!", args)
+ return 0
+
+ @IOWR(DRM_COMMAND_BASE + 0x05, drm_asahi_get_bo_offset_t)
+ def get_bo_offset(self, fd, args):
+ self.log("Get BO Offset!", args)
+ return 0
+
+ def bo_free(self, memfd_offset):
+ self.log(f"Free BO @ {memfd_offset:#x}")
+ self.bos[memfd_offset].free()
+ del self.bos[memfd_offset]
+ sys.stdout.flush()
+
+ def ioctl(self, fd, request, p_arg):
+ self.init()
+
+ p_arg = ctypes.c_void_p(p_arg)
+
+ if request not in self.ioctl_map:
+ self.log(f"Unknown ioctl: fd={fd} request={IOCTL(request)} arg={p_arg:#x}")
+ return -errno.ENOSYS
+
+ ioctl, f = self.ioctl_map[request]
+
+ size = ioctl.SIZE
+ if ioctl.DIR & _IOC_WRITE:
+ args = f._arg_cls.parse(self.read_buf(p_arg, size))
+ ret = f(fd, args)
+ elif ioctl.DIR & _IOC_READ:
+ args = f._arg_cls.parse(bytes(size))
+ ret = f(fd, args)
+ else:
+ ret = f(fd)
+
+ if ioctl.DIR & _IOC_READ:
+ data = args.build()
+ assert len(data) == size
+ ctypes.memmove(p_arg, data, size)
+
+ sys.stdout.flush()
+ return ret
+
+ def log(self, s):
+ if self.agx is None:
+ print("[Shim] " + s)
+ else:
+ self.agx.log("[Shim] " + s)
+
+Shim = DRMAsahiShim
diff --git a/tools/proxyclient/m1n1/agx/uapi.py b/tools/proxyclient/m1n1/agx/uapi.py
new file mode 100644
index 0000000..75850cb
--- /dev/null
+++ b/tools/proxyclient/m1n1/agx/uapi.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+
+from construct import *
+from m1n1.constructutils import ConstructClass
+
+__all__ = []
+
+DRM_COMMAND_BASE = 0x40
+
+ASAHI_BO_PIPELINE = 1
+
+class drm_asahi_submit_t(ConstructClass):
+ subcon = Struct(
+ "cmdbuf" / Int64ul,
+ "in_syncs" / Int64ul,
+ "in_sync_count" / Int32ul,
+ "out_sync" / Int32ul,
+ )
+
+class drm_asahi_wait_bo_t(ConstructClass):
+ subcon = Struct(
+ "handle" / Int32ul,
+ Padding(4),
+ "timeout_ns" / Int64sl,
+ )
+
+class drm_asahi_create_bo_t(ConstructClass):
+ subcon = Struct(
+ "size" / Int32ul,
+ "flags" / Int32ul,
+ "handle" / Int32ul,
+ Padding(4),
+ "offset" / Int64ul,
+ )
+
+#class drm_asahi_mmap_bo_t(ConstructClass):
+ #subcon = Struct(
+ #"handle" / Int32ul,
+ #"flags" / Int32ul,
+ #"offset" / Int64ul,
+ #)
+
+class drm_asahi_get_param_t(ConstructClass):
+ subcon = Struct(
+ "param" / Int32ul,
+ Padding(4),
+ "value" / Int64ul,
+ )
+
+class drm_asahi_get_bo_offset_t(ConstructClass):
+ subcon = Struct(
+ "handle" / Int32ul,
+ Padding(4),
+ "offset" / Int64ul,
+ )
+
+ASAHI_MAX_ATTACHMENTS = 16
+
+ASAHI_ATTACHMENT_C = 0
+ASAHI_ATTACHMENT_Z = 1
+ASAHI_ATTACHMENT_S = 2
+
+class drm_asahi_attachment_t(ConstructClass):
+ subcon = Struct(
+ "type" / Int32ul,
+ "size" / Int32ul,
+ "pointer" / Int64ul,
+ )
+
+ASAHI_CMDBUF_LOAD_C = (1 << 0)
+ASAHI_CMDBUF_LOAD_Z = (1 << 1)
+ASAHI_CMDBUF_LOAD_S = (1 << 2)
+
+class drm_asahi_cmdbuf_t(ConstructClass):
+ subcon = Struct(
+ "flags" / Int64ul,
+
+ "encoder_ptr" / Int64ul,
+ "encoder_id" / Int32ul,
+
+ "cmd_ta_id" / Int32ul,
+ "cmd_3d_id" / Int32ul,
+
+ "ds_flags" / Int32ul,
+ "depth_buffer" / Int64ul,
+ "stencil_buffer" / Int64ul,
+
+ "scissor_array" / Int64ul,
+ "depth_bias_array" / Int64ul,
+
+ "fb_width" / Int32ul,
+ "fb_height" / Int32ul,
+
+ "load_pipeline" / Int32ul,
+ "load_pipeline_bind" / Int32ul,
+
+ "store_pipeline" / Int32ul,
+ "store_pipeline_bind" / Int32ul,
+
+ "partial_reload_pipeline" / Int32ul,
+ "partial_reload_pipeline_bind" / Int32ul,
+
+ "partial_store_pipeline" / Int32ul,
+ "partial_store_pipeline_bind" / Int32ul,
+
+ "depth_clear_value" / Float32l,
+ "stencil_clear_value" / Int8ul,
+ Padding(3),
+
+ "attachments" / Array(ASAHI_MAX_ATTACHMENTS, drm_asahi_attachment_t),
+ "attachment_count" / Int32ul,
+ )
+
+__all__.extend(k for k, v in globals().items()
+ if ((callable(v) or isinstance(v, type)) and v.__module__ == __name__) or isinstance(v, int))
diff --git a/tools/proxyclient/m1n1/asm.py b/tools/proxyclient/m1n1/asm.py
new file mode 100644
index 0000000..ef1f3af
--- /dev/null
+++ b/tools/proxyclient/m1n1/asm.py
@@ -0,0 +1,133 @@
+# SPDX-License-Identifier: MIT
+import os, tempfile, shutil, subprocess
+
+__all__ = ["AsmException", "ARMAsm"]
+
+uname = os.uname()
+
+if uname.sysname == "Darwin":
+ DEFAULT_ARCH = "aarch64-linux-gnu-"
+ if uname.machine == "arm64":
+ TOOLCHAIN = "/opt/homebrew/opt/llvm/bin/"
+ else:
+ TOOLCHAIN = "/usr/local/opt/llvm/bin/"
+ USE_CLANG = "1"
+else:
+ if uname.machine == "aarch64":
+ DEFAULT_ARCH = ""
+ else:
+ DEFAULT_ARCH = "aarch64-linux-gnu-"
+ USE_CLANG = "0"
+ TOOLCHAIN = ""
+
+use_clang = os.environ.get("USE_CLANG", USE_CLANG).strip() == "1"
+toolchain = os.environ.get("TOOLCHAIN", TOOLCHAIN)
+
+if use_clang:
+ CC = toolchain + "clang --target=%ARCH"
+ LD = toolchain + "ld.lld"
+ OBJCOPY = toolchain + "llvm-objcopy"
+ OBJDUMP = toolchain + "llvm-objdump"
+ NM = toolchain + "llvm-nm"
+else:
+ CC = toolchain + "%ARCHgcc"
+ LD = toolchain + "%ARCHld"
+ OBJCOPY = toolchain + "%ARCHobjcopy"
+ OBJDUMP = toolchain + "%ARCHobjdump"
+ NM = toolchain + "%ARCHnm"
+
+class AsmException(Exception):
+ pass
+
+class BaseAsm(object):
+ def __init__(self, source, addr = 0):
+ self.source = source
+ self._tmp = tempfile.mkdtemp() + os.sep
+ self.addr = addr
+ self.compile(source)
+
+ def _call(self, program, args):
+ subprocess.check_call(program.replace("%ARCH", self.ARCH) + " " + args, shell=True)
+
+ def _get(self, program, args):
+ return subprocess.check_output(program.replace("%ARCH", self.ARCH) + " " + args, shell=True).decode("ascii")
+
+ def compile(self, source):
+ self.sfile = self._tmp + "b.S"
+ with open(self.sfile, "w") as fd:
+ fd.write(self.HEADER + "\n")
+ fd.write(source + "\n")
+ fd.write(self.FOOTER + "\n")
+
+ self.ofile = self._tmp + "b.o"
+ self.elffile = self._tmp + "b.elf"
+ self.bfile = self._tmp + "b.b"
+ self.nfile = self._tmp + "b.n"
+
+ self._call(CC, f"{self.CFLAGS} -c -o {self.ofile} {self.sfile}")
+ self._call(LD, f"{self.LDFLAGS} --Ttext={self.addr:#x} -o {self.elffile} {self.ofile}")
+ self._call(OBJCOPY, f"-j.text -O binary {self.elffile} {self.bfile}")
+ self._call(NM, f"{self.elffile} > {self.nfile}")
+
+ with open(self.bfile, "rb") as fd:
+ self.data = fd.read()
+
+ with open(self.nfile) as fd:
+ for line in fd:
+ line = line.replace("\n", "")
+ addr, type, name = line.split()
+ addr = int(addr, 16)
+ setattr(self, name, addr)
+ self.start = self._start
+ self.len = len(self.data)
+ self.end = self.start + self.len
+
+ def objdump(self):
+ self._call(OBJDUMP, f"-rd {self.elffile}")
+
+ def disassemble(self):
+ output = self._get(OBJDUMP, f"-zd {self.elffile}")
+
+ for line in output.split("\n"):
+ if not line or line.startswith("/"):
+ continue
+ sl = line.split()
+ if not sl or sl[0][-1] != ":":
+ continue
+ yield line
+
+ def __del__(self):
+ if self._tmp:
+ shutil.rmtree(self._tmp)
+ self._tmp = None
+
+class ARMAsm(BaseAsm):
+ ARCH = os.path.join(os.environ.get("ARCH", DEFAULT_ARCH))
+ CFLAGS = "-pipe -Wall -march=armv8.4-a"
+ LDFLAGS = "-maarch64elf"
+ HEADER = """
+ .text
+ .globl _start
+_start:
+ """
+ FOOTER = """
+ .pool
+ """
+
+if __name__ == "__main__":
+ import sys
+ code = """
+ ldr x0, =0xDEADBEEF
+ b test
+ mrs x0, spsel
+ svc 1
+ %s
+test:
+ b test
+ ret
+""" % (" ".join(sys.argv[1:]))
+ c = ARMAsm(code, 0x1238)
+ c.objdump()
+ assert c.start == 0x1238
+ if not sys.argv[1:]:
+ assert c.test == 0x1248
diff --git a/tools/proxyclient/m1n1/constructutils.py b/tools/proxyclient/m1n1/constructutils.py
new file mode 100644
index 0000000..ff0c5b7
--- /dev/null
+++ b/tools/proxyclient/m1n1/constructutils.py
@@ -0,0 +1,879 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import inspect, textwrap, json, re, sys, os
+
+from construct import *
+from construct.core import evaluate
+from construct.lib import HexDisplayedInteger
+from .utils import *
+
+g_struct_trace = set()
+g_struct_addrmap = {}
+g_depth = 0
+
+def ZPadding(size):
+ return Const(bytes(size), Bytes(size))
+
+def recusive_reload(obj, token=None):
+ global g_depth
+
+ if token is None:
+ g_depth = 0
+ token = object()
+
+ cur_token = getattr(obj, "_token", None)
+ if cur_token is token:
+ return
+
+ g_depth += 1
+ #print(" " * g_depth + f"> {obj}", id(obj), id(token))
+ if isinstance(obj, Construct) and hasattr(obj, 'subcon'):
+ # Single subcon types
+ if inspect.isclass(obj.subcon):
+ #print("> isclass")
+ if hasattr(obj.subcon, "_reloadcls"):
+ #print("> Recursive (subcon)")
+ obj.subcon = obj.subcon._reloadcls(token=token)
+ else:
+ if isinstance(obj.subcon, Construct):
+ recusive_reload(obj.subcon, token)
+ if isinstance(obj, Construct) and hasattr(obj, 'subcons'):
+ # Construct types that have lists
+ new_subcons = []
+ for i, item in enumerate(obj.subcons):
+ if inspect.isclass(item):
+ if hasattr(item, "_reloadcls"):
+ #print("> Recursive (subcons)")
+ item = item._reloadcls()
+ else:
+ if isinstance(item, Construct):
+ recusive_reload(item, token)
+ new_subcons.append(item)
+ obj.subcons = new_subcons
+
+ if isinstance(obj, Construct) and hasattr(obj, 'cases'):
+ # Construct types that have lists
+ for i, item in list(obj.cases.items()):
+ if inspect.isclass(item):
+ if hasattr(item, "_reloadcls"):
+ #print("> Recursive (cases)")
+ obj.cases[i] = item._reloadcls(token=token)
+ else:
+ if isinstance(item, Construct):
+ recusive_reload(item, token)
+
+ for field in dir(obj):
+ value = getattr(obj, field)
+ if inspect.isclass(value):
+ if hasattr(value, "_reloadcls"):
+ #print("> Recursive (value)")
+ setattr(obj, field, value._reloadcls(token=token))
+ else:
+ if isinstance(value, Construct):
+ recusive_reload(value, token)
+
+ obj._token = token
+
+ g_depth -= 1
+
+def str_value(value, repr=False):
+ if isinstance(value, bytes) and value == bytes(len(value)):
+ return f"bytes({len(value):#x})"
+ if isinstance(value, bytes) and repr:
+ return f"bytes.fromhex('{value.hex()}')"
+ if isinstance(value, DecDisplayedInteger):
+ return str(value)
+ if isinstance(value, int):
+ if value in g_struct_addrmap:
+ desc = g_struct_addrmap[value]
+ return f"{value:#x} ({desc})"
+ else:
+ return f"{value:#x}"
+ if isinstance(value, ListContainer):
+ om = ""
+ while len(value) > 1 and not value[-1]:
+ value = value[:-1]
+ om = " ..."
+ if len(value) <= 16:
+ return "[" + ", ".join(map(str_value, value)) + f"{om}]"
+ else:
+ sv = ["[\n"]
+ for off in range(0, len(value), 16):
+ sv.append(" " + ", ".join(map(str_value, value[off:off+16])) + ",\n")
+ sv.append(f"{om}]\n")
+ return "".join(sv)
+
+ return str(value)
+
+class DecDisplayedInteger(int):
+ @staticmethod
+ def new(intvalue):
+ obj = DecDisplayedInteger(intvalue)
+ return obj
+
+class Dec(Adapter):
+ def _decode(self, obj, context, path):
+ try:
+ if isinstance(obj, int):
+ return DecDisplayedInteger.new(obj)
+ return obj
+ except Exception as e:
+ print(e)
+ raise
+
+ def _encode(self, obj, context, path):
+ return obj
+
+ def _emitparse(self, code):
+ return self.subcon._compileparse(code)
+
+ def _emitseq(self, ksy, bitwise):
+ return self.subcon._compileseq(ksy, bitwise)
+
+ def _emitprimitivetype(self, ksy, bitwise):
+ return self.subcon._compileprimitivetype(ksy, bitwise)
+
+ def _emitfulltype(self, ksy, bitwise):
+ return self.subcon._compilefulltype(ksy, bitwise)
+
+class ConstructClassException(Exception):
+ pass
+
+
+# We need to inherrit Construct as a metaclass so things like If and Select will work
+class ReloadableConstructMeta(ReloadableMeta, Construct):
+
+ def __new__(cls, name, bases, attrs):
+ cls = super().__new__(cls, name, bases, attrs)
+ cls.name = name
+ if cls.SHORT_NAME is not None:
+ cls.short_name = cls.SHORT_NAME
+ else:
+ cls.short_name = re.sub('[a-z]', '', cls.name)
+ if len(cls.short_name) > 5:
+ cls.short_name = cls.short_name[:3] + cls.short_name[-2:]
+
+ try:
+ cls.flagbuildnone = cls.subcon.flagbuildnone
+ except AttributeError:
+ cls.flagbuildnone = False
+
+ cls.docs = None
+
+ cls._off = {}
+ if "subcon" not in attrs:
+ return cls
+
+ subcon = attrs["subcon"]
+ if isinstance(subcon, Struct):
+ off = 0
+ for subcon in subcon.subcons:
+ try:
+ sizeof = subcon.sizeof()
+ except:
+ sizeof = None
+ if isinstance(subcon, Ver):
+ if not subcon._active():
+ cls._off[subcon.name] = -1, 0
+ continue
+ subcon = subcon.subcon
+ if isinstance(subcon, Renamed):
+ name = subcon.name
+ subcon = subcon.subcon
+ cls._off[name] = off, sizeof
+ if sizeof is None:
+ break
+ off += sizeof
+ return cls
+
+class ConstructClassBase(Reloadable, metaclass=ReloadableConstructMeta):
+ """ Offers two benifits over regular construct
+
+ 1. It's reloadable, and can recusrivly reload other refrenced ConstructClasses
+ 2. It's a class, so you can define methods
+
+ Currently only supports parsing, but could be extended to support building
+
+ Example:
+ Instead of:
+ MyStruct = Struct(
+ "field1" / Int32ul
+ )
+
+ class MyClass(ConstructClass):
+ subcon = Struct(
+ "field1" / Int32ul
+ )
+
+ """
+ SHORT_NAME = None
+
+ parsed = None
+
+ def __init__(self):
+ self._pointers = set()
+ self._addr = None
+ self._meta = {}
+
+ def regmap(self):
+ return ConstructRegMap(type(self), self._stream.to_accessor(), self._addr)
+
+ @classmethod
+ def sizeof(cls, **contextkw):
+ context = Container(**contextkw)
+ context._parsing = False
+ context._building = False
+ context._sizing = True
+ context._params = context
+ return cls._sizeof(context, "(sizeof)")
+
+ def Apply(self, dict=None, **kwargs):
+ if dict is None:
+ dict = kwargs
+
+ for key in dict:
+ if not key.startswith('_'):
+ setattr(self, key, dict[key])
+ self._keys += [key]
+
+ def set_addr(self, addr=None, stream=None):
+ #print("set_addr", type(self), addr)
+ if addr is not None:
+ self._addr = addr
+ self._set_meta(self, stream)
+
+ @classmethod
+ def _build(cls, obj, stream, context, path):
+ cls._build_prepare(obj)
+
+ addr = stream.tell()
+ try:
+ new_obj = cls.subcon._build(obj, stream, context, f"{path} -> {cls.name}")
+ except ConstructClassException:
+ raise
+ except ConstructError:
+ raise
+ except Exception as e:
+ raise ConstructClassException(f"at {path} -> {cls.name}") from e
+
+ # if obj is a raw value or Container, instance a proper object for it
+ if not isinstance(obj, ConstructClassBase):
+ obj = cls.__new__(cls)
+
+ # update the object with anything that build updated (such as defaults)
+ obj._apply(new_obj)
+
+ obj._addr = addr
+ cls._set_meta(obj, stream)
+ return obj
+
+ @classmethod
+ def _sizeof(cls, context, path):
+ return cls.subcon._sizeof(context, f"{path} -> {cls.name}")
+
+ @classmethod
+ def _reloadcls(cls, force=False, token=None):
+ #print(f"_reloadcls({cls})", id(cls))
+ newcls = Reloadable._reloadcls.__func__(cls, force)
+ if hasattr(newcls, "subcon"):
+ recusive_reload(newcls.subcon, token)
+ return newcls
+
+ def _apply(self, obj):
+ raise NotImplementedError()
+
+ @classmethod
+ def _set_meta(cls, self, stream=None):
+ if stream is not None:
+ self._pointers = set()
+ self._meta = {}
+ self._stream = stream
+
+ if isinstance(cls.subcon, Struct):
+ subaddr = int(self._addr)
+ for subcon in cls.subcon.subcons:
+ try:
+ sizeof = subcon.sizeof()
+ except:
+ break
+ if isinstance(subcon, Ver):
+ subcon = subcon.subcon
+ if isinstance(subcon, Renamed):
+ name = subcon.name
+ #print(name, subcon)
+ subcon = subcon.subcon
+ if stream is not None and getattr(stream, "meta_fn", None):
+ meta = stream.meta_fn(subaddr, sizeof)
+ if meta is not None:
+ self._meta[name] = meta
+ if isinstance(subcon, Pointer):
+ self._pointers.add(name)
+ continue
+ try:
+ #print(name, subcon)
+ val = self[name]
+ except:
+ pass
+ else:
+ if isinstance(val, ConstructClassBase):
+ val.set_addr(subaddr)
+ if isinstance(val, list):
+ for i in val:
+ if isinstance(i, ConstructClassBase):
+ i.set_addr(subaddr)
+ subaddr += i.sizeof()
+
+ subaddr += sizeof
+
+ @classmethod
+ def _parse(cls, stream, context, path):
+ #print(f"parse {cls} @ {stream.tell():#x} {path}")
+ addr = stream.tell()
+ obj = cls.subcon._parse(stream, context, path)
+ size = stream.tell() - addr
+
+ # Don't instance Selects
+ if isinstance(cls.subcon, Select):
+ return obj
+
+ # Skip calling the __init__ constructor, so that it can be used for building
+ # Use parsed instead, if you need a post-parsing constructor
+ self = cls.__new__(cls)
+ self._addr = addr
+ self._path = path
+ self._meta = {}
+ cls._set_meta(self, stream)
+
+ self._apply(obj)
+
+ if self._addr > 0x10000:
+ g_struct_trace.add((self._addr, f"{cls.name} (end: {self._addr + size:#x})"))
+ g_struct_addrmap[self._addr] = f"{cls.name}"
+ return self
+
+ @classmethod
+ def _build_prepare(cls, obj):
+ pass
+
+ def build_stream(self, obj=None, stream=None, **contextkw):
+ assert stream != None
+ if obj is None:
+ obj = self
+
+ return Construct.build_stream(self, obj, stream, **contextkw)
+
+ def build(self, obj=None, **contextkw):
+ if obj is None:
+ obj = self
+
+ return Construct.build(self, obj, **contextkw)
+
+class ROPointer(Pointer):
+ def _build(self, obj, stream, context, path):
+ return obj
+
+ def _parse(self, stream, context, path):
+ recurse = getattr(stream, "recurse", False)
+ if not recurse:
+ return None
+
+ return Pointer._parse(self, stream, context, path)
+
+class ConstructClass(ConstructClassBase, Container):
+ """ Offers two benifits over regular construct
+
+ 1. It's reloadable, and can recusrivly reload other refrenced ConstructClasses
+ 2. It's a class, so you can define methods
+
+ Currently only supports parsing, but could be extended to support building
+
+ Example:
+ Instead of:
+ MyStruct = Struct(
+ "field1" / Int32ul
+ )
+
+ class MyClass(ConstructClass):
+ subcon = Struct(
+ "field1" / Int32ul
+ )
+ """
+
+ def diff(self, other, show_all=False):
+ return self.__str__(other=other, show_all=show_all)
+
+ def __eq__(self, other):
+ return all(self[k] == other[k] for k in self
+ if (not k.startswith("_"))
+ and (k not in self._pointers)
+ and not callable(self[k]))
+
+ def __str__(self, ignore=[], other=None, show_all=False) -> str:
+
+ str = self.__class__.__name__
+ if self._addr is not None:
+ str += f" @ 0x{self._addr:x}:"
+
+ str += "\n"
+
+ keys = list(self)
+ keys.sort(key = lambda x: self._off.get(x, (-1, 0))[0])
+
+ for key in keys:
+ if key in self._off:
+ offv, sizeof = self._off[key]
+ if offv == -1:
+ print(key, offv, sizeof)
+ continue
+ if key in ignore or key.startswith('_'):
+ continue
+ value = getattr(self, key)
+ need_diff = False
+ if other is not None:
+ if key in self._pointers or callable(value):
+ continue
+ other_value = getattr(other, key)
+ if not show_all and other_value == value:
+ continue
+ offv, sizeof = self._off[key]
+ if sizeof == 0:
+ continue
+ def _valdiff(value, other_value):
+ if hasattr(value, "diff"):
+ return value.diff(other_value)
+ elif isinstance(value, bytes) and isinstance(other_value, bytes):
+ pad = bytes()
+ if len(value) & 3:
+ pad = bytes(4 - (len(value) & 3))
+ return chexdiff32(other_value+pad, value+pad, offset=offv, offset2=0)
+ else:
+ val_repr = str_value(value)
+ if other_value != value:
+ other_repr = str_value(other_value)
+ return f"\x1b[33;1;4m{val_repr}\x1b[m ← \x1b[34m{other_repr}\x1b[m"
+ return val_repr
+
+ if isinstance(value, list):
+ val_repr = "{\n"
+ for i, (a, b) in enumerate(zip(value, other_value)):
+ if a == b:
+ continue
+ val_repr += f"[{i}] = " + textwrap.indent(_valdiff(a, b), " ") + "\n"
+ offv += sizeof // len(value)
+ val_repr += "}\n"
+ else:
+ val_repr = _valdiff(value, other_value)
+
+ else:
+ val_repr = str_value(value)
+ off = ""
+ meta = ""
+ if key in self._off:
+ offv, sizeof = self._off[key]
+ if sizeof is not None:
+ sizeofs = f"{sizeof:3x}"
+ else:
+ sizeofs = " *"
+ off = f"\x1b[32m[{offv:3x}.{sizeofs}]\x1b[m "
+ if key in self._meta:
+ meta = f" \x1b[34m{self._meta[key]}\x1b[m"
+ if '\n' in val_repr:
+ val_repr = textwrap.indent(val_repr, f'\x1b[90m{self.short_name:>5s}.\x1b[m')
+ if not val_repr.endswith('\n'):
+ val_repr += '\n'
+ str += f"\x1b[90m{self.short_name:>5s}.{off}\x1b[95m{key}\x1b[m ={meta}\n{val_repr}"
+ else:
+ str += f"\x1b[90m{self.short_name:>5s}.{off}\x1b[95m{key}\x1b[m = {val_repr}{meta}\n"
+
+ return str
+
+ def _dump(self):
+ print(f"# {self.__class__.__name__}")
+ if self._addr is not None:
+ print(f"# Address: 0x{self._addr:x}")
+
+ keys = list(self)
+ keys.sort(key = lambda x: self._off.get(x, (-1, 0))[0])
+ for key in keys:
+ if key.startswith('_'):
+ continue
+ value = getattr(self, key)
+ val_repr = str_value(value, repr=True)
+ print(f"self.{key} = {val_repr}")
+
+
+ @classmethod
+ def _build_prepare(cls, obj):
+ if isinstance(cls.subcon, Struct):
+ for subcon in cls.subcon.subcons:
+ if isinstance(subcon, Ver):
+ subcon = subcon.subcon
+ if not isinstance(subcon, Renamed):
+ continue
+ name = subcon.name
+ subcon = subcon.subcon
+ if isinstance(subcon, Lazy):
+ subcon = subcon.subcon
+ if not isinstance(subcon, Pointer):
+ continue
+ addr_field = subcon.offset.__getfield__()
+ # Ugh.
+ parent = subcon.offset._Path__parent(obj)
+ if not hasattr(obj, name) and hasattr(parent, addr_field):
+ # No need for building
+ setattr(obj, name, None)
+ elif hasattr(obj, name):
+ subobj = getattr(obj, name)
+ try:
+ addr = subobj._addr
+ except (AttributeError, KeyError):
+ addr = None
+ if addr is not None:
+ setattr(parent, addr_field, addr)
+
+ @classmethod
+ def _parse(cls, stream, context, path):
+ self = ConstructClassBase._parse.__func__(cls, stream, context, path)
+
+ for key in self:
+ if key.startswith('_'):
+ continue
+ try:
+ val = int(self[key])
+ except:
+ continue
+ if (0x1000000000 <= val <= 0x1f00000000 or
+ 0xf8000000000 <= val <= 0xff000000000 or
+ 0xffffff8000000000 <= val <= 0xfffffff000000000):
+ g_struct_trace.add((val, f"{cls.name}.{key}"))
+ return self
+
+ def _apply_classful(self, obj):
+ obj2 = dict(obj)
+ if isinstance(self.__class__.subcon, Struct):
+ for subcon in self.__class__.subcon.subcons:
+ name = subcon.name
+ if name is None:
+ continue
+ subcon = subcon.subcon
+ if isinstance(subcon, Lazy):
+ continue
+ if isinstance(subcon, Pointer):
+ subcon = subcon.subcon
+ if isinstance(subcon, Ver):
+ subcon = subcon.subcon
+ if isinstance(subcon, Array):
+ subcon = subcon.subcon
+
+ if name not in obj2:
+ continue
+
+ val = obj2[name]
+ if not isinstance(subcon, type) or not issubclass(subcon, ConstructClassBase):
+ continue
+
+ def _map(v):
+ if not isinstance(v, subcon):
+ sc = subcon()
+ sc._apply(v)
+ return sc
+ return v
+
+ if isinstance(val, list):
+ obj2[name] = list(map(_map, val))
+ else:
+ obj2[name] = _map(val)
+
+ self._apply(obj2)
+
+ def _apply(self, obj):
+ self.update(obj)
+
+ def items(self):
+ for k in list(self):
+ if k.startswith("_"):
+ continue
+ yield k, self[k]
+
+ def addrof(self, name):
+ return self._addr + self._off[name][0]
+
+ @classmethod
+ def offsetof(cls, name):
+ return cls._off[name][0]
+
+ def clone(self):
+ obj = type(self)()
+ obj.update(self)
+ return obj
+
+ @classmethod
+ def from_json(cls, fd):
+ d = json.load(fd)
+ obj = cls()
+ obj._apply_classful(d)
+ return obj
+
+ @classmethod
+ def is_versioned(cls):
+ for subcon in cls.subcon.subcons:
+ if isinstance(subcon, Ver):
+ return True
+ while True:
+ try:
+ subcon = subcon.subcon
+ if isinstance(subcon, type) and issubclass(subcon, ConstructClass) and subcon.is_versioned():
+ return True
+ except:
+ break
+ return False
+
+ @classmethod
+ def to_rust(cls):
+ assert isinstance(cls.subcon, Struct)
+ s = []
+ if cls.is_versioned():
+ s.append("#[versions(AGX)]"),
+
+ s += [
+ "#[derive(Debug, Clone, Copy)]",
+ "#[repr(C, packed(4))]",
+ f"struct {cls.__name__} {{",
+ ]
+ pad = 0
+ has_ver = False
+ for subcon in cls.subcon.subcons:
+ if isinstance(subcon, Ver):
+ if not has_ver:
+ s.append("")
+ s.append(f" #[ver({subcon.cond})]")
+ subcon = subcon.subcon
+ has_ver = True
+ else:
+ has_ver = False
+
+ name = subcon.name
+ if name is None:
+ name = f"__pad{pad}"
+ pad += 1
+
+ array_len = []
+ skip = False
+ while subcon:
+ if isinstance(subcon, Lazy):
+ skip = True
+ break
+ elif isinstance(subcon, Pointer):
+ skip = True
+ break
+ elif isinstance(subcon, Array):
+ array_len.append(subcon.count)
+ elif isinstance(subcon, (HexDump, Default, Renamed, Dec, Hex, Const)):
+ pass
+ else:
+ break
+ subcon = subcon.subcon
+
+ if isinstance(subcon, Bytes):
+ array_len.append(subcon.length)
+ subcon = Int8ul
+
+ if skip:
+ #s.append(f" // {name}: {subcon}")
+ continue
+
+ TYPE_MAP = {
+ Int64ul: "u64",
+ Int32ul: "u32",
+ Int16ul: "u16",
+ Int8ul: "u8",
+ Int64sl: "i64",
+ Int32sl: "i32",
+ Int16sl: "i16",
+ Int8sl: "i8",
+ Float32l: "f32",
+ Float64l: "f64",
+ }
+
+ t = TYPE_MAP.get(subcon, repr(subcon))
+
+ if isinstance(subcon, type) and issubclass(subcon, ConstructClass):
+ t = subcon.__name__
+ if subcon.is_versioned():
+ t += "::ver"
+
+ for n in array_len[::-1]:
+ t = f"[{t}; {n:#x}]"
+
+ s.append(f" {name}: {t},")
+
+ if has_ver:
+ s.append("")
+
+ s += ["}"]
+ return "\n".join(s)
+
+class ConstructValueClass(ConstructClassBase):
+ """ Same as Construct, but for subcons that are single values, rather than containers
+
+ the value is stored as .value
+ """
+
+ def __eq__(self, other):
+ return self.value == other.value
+
+ def __str__(self) -> str:
+ str = f"{self.__class__.__name__} @ 0x{self._addr:x}:"
+ str += f"\t{str_value(self.value)}"
+ return str
+
+ def __getitem__(self, i):
+ if i == "value":
+ return self.value
+ raise Exception(f"Invalid index {i}")
+
+ @classmethod
+ def _build(cls, obj, stream, context, path):
+ return super()._build(obj.value, stream, context, path)
+
+ def _apply(self, obj):
+ self.value = obj
+ _apply_classful = _apply
+
+class ConstructRegMap(BaseRegMap):
+ TYPE_MAP = {
+ Int8ul: Register8,
+ Int16ul: Register16,
+ Int32ul: Register32,
+ Int64ul: Register64,
+ }
+
+ def __init__(self, cls, backend, base):
+ self._addrmap = {}
+ self._rngmap = SetRangeMap()
+ self._namemap = {}
+ assert isinstance(cls.subcon, Struct)
+ for subcon in cls.subcon.subcons:
+ if isinstance(subcon, Ver):
+ subcon = subcon.subcon
+ if not isinstance(subcon, Renamed):
+ continue
+ name = subcon.name
+ subcon = subcon.subcon
+ if subcon not in self.TYPE_MAP:
+ continue
+ rtype = self.TYPE_MAP[subcon]
+ if name not in cls._off:
+ continue
+ addr, size = cls._off[name]
+ self._addrmap[addr] = name, rtype
+ self._namemap[name] = addr, rtype
+ super().__init__(backend, base)
+
+ def __getattr__(self, k):
+ if k.startswith("_"):
+ return self.__dict__[k]
+ return self._accessor[k]
+
+ def __setattr__(self, k, v):
+ if k.startswith("_"):
+ self.__dict__[k] = v
+ return
+ self._accessor[k].val = v
+
+class Ver(Subconstruct):
+ # Ugly hack to make this survive across reloads...
+ try:
+ _version = sys.modules["m1n1.constructutils"].Ver._version
+ except (KeyError, AttributeError):
+ _version = {"V": os.environ.get("AGX_FWVER", "V12_3"),
+ "G": os.environ.get("AGX_GPU", "G13")}
+
+ MATRIX = {
+ "V": ["V12_1", "V12_3", "V12_4", "V13_0B4"],
+ "G": ["G13", "G14"],
+ }
+
+ def __init__(self, version, subcon):
+ self.cond = version
+ self.vcheck = self.parse_ver(version)
+ self._name = subcon.name
+ self.subcon = subcon
+ self.flagbuildnone = True
+ self.docs = ""
+ self.parsed = None
+
+ @property
+ def name(self):
+ if self._active():
+ return self._name
+ else:
+ return None
+
+ @staticmethod
+ def _split_ver(s):
+ if not s:
+ return None
+ parts = re.split(r"[-,. ]", s)
+ parts2 = []
+ for i in parts:
+ try:
+ parts2.append(int(i))
+ except ValueError:
+ parts2.append(i)
+ if len(parts2) > 3 and parts2[-2] == "beta":
+ parts2[-3] -= 1
+ parts2[-2] = 99
+ return tuple(parts2)
+
+ @classmethod
+ def parse_ver(cls, version):
+ expr = version.replace("&&", " and ").replace("||", " or ")
+
+ base_loc = {j: i for row in cls.MATRIX.values() for i, j in enumerate(row)}
+
+ def check_ver(ver):
+ loc = dict(base_loc)
+ for k, v in ver.items():
+ loc[k] = cls.MATRIX[k].index(v)
+ return eval(expr, None, loc)
+
+ return check_ver
+
+ @classmethod
+ def check(cls, version):
+ return cls.parse_ver(version)(cls._version)
+
+ def _active(self):
+ return self.vcheck(self._version)
+
+ def _parse(self, stream, context, path):
+ if not self._active():
+ return None
+ obj = self.subcon._parse(stream, context, path)
+ return obj
+
+ def _build(self, obj, stream, context, path):
+ if not self._active():
+ return None
+ return self.subcon._build(obj, stream, context, path)
+
+ def _sizeof(self, context, path):
+ if not self._active():
+ return 0
+ return self.subcon._sizeof(context, path)
+
+ @classmethod
+ def set_version_key(cls, key, version):
+ cls._version[key] = version
+
+ @classmethod
+ def set_version(cls, u):
+ cls.set_version_key("V", u.version)
+ cls.set_version_key("G", u.adt["/arm-io"].soc_generation.replace("H", "G"))
+
+def show_struct_trace(log=print):
+ for addr, desc in sorted(list(g_struct_trace)):
+ log(f"{addr:>#18x}: {desc}")
+
+__all__ = ["ConstructClass", "ConstructValueClass", "Dec", "ROPointer", "show_struct_trace", "ZPadding", "Ver"]
diff --git a/tools/proxyclient/m1n1/find_regs.py b/tools/proxyclient/m1n1/find_regs.py
new file mode 100644
index 0000000..9982661
--- /dev/null
+++ b/tools/proxyclient/m1n1/find_regs.py
@@ -0,0 +1,99 @@
+# SPDX-License-Identifier: MIT
+import struct
+
+from . import asm, sysreg
+from .proxyutils import GuardedHeap
+
+__all__ = ["dynamic_regs", "impdef_regs", "static_regs", "find_regs"]
+
+def _all():
+ for op1 in range(1 << 3):
+ for CRn in (0b1011, 0b1111):
+ for CRm in range(1 << 4):
+ for op2 in range(1 << 3):
+ yield 3, op1, CRn, CRm, op2
+
+dynamic_regs = [
+ sysreg.CNTVCT_ALIAS_EL0,
+ sysreg.CNTPCT_ALIAS_EL0,
+]
+
+impdef_regs = list(_all())
+static_regs = [i for i in _all() if i not in dynamic_regs]
+
+def find_regs(u, regs=None, block=1024, call=None, values=True):
+ if regs is None:
+ regs = impdef_regs
+
+ p = u.proxy
+ iface = u.iface
+
+ data_len = 8 * block
+
+ with GuardedHeap(u.heap) as heap:
+ data_buffer = heap.malloc(data_len)
+
+ template = asm.ARMAsm("""
+ mov x2, x0
+ mrs x2, s3_0_c0_c0_0
+ str x2, [x1], #8
+ """, 0x1000)
+
+ mov, mrs, st = struct.unpack("3I", template.data)
+
+
+ BAD = 0xacce5515abad1dea
+ OOPS = 0xdeadc0dedeadc0de
+
+ iregs = iter(regs)
+
+ while True:
+ insns = []
+ bregs = []
+ for i in iregs:
+ op0, op1, CRn, CRm, op2 = enc = sysreg.sysreg_parse(i)
+ bregs.append(enc)
+ assert op0 == 3
+
+ insns.extend((mov, mrs | (op1 << 16) | (CRn << 12) | (CRm << 8) | (op2 << 5), st))
+
+ if len(bregs) >= block:
+ break
+
+ if not bregs:
+ break
+
+ p.memset64(data_buffer, OOPS, data_len)
+ u.exec(insns, BAD, data_buffer, call=call, silent=True, ignore_exceptions=True)
+ data = iface.readmem(data_buffer, 8 * len(bregs))
+ for reg, val in zip(bregs, struct.unpack(f"<{len(bregs)}Q", data)):
+ if val == OOPS:
+ raise Exception(f"Failed to execute reg-finder code at {reg}")
+ if val != BAD:
+ if values:
+ yield reg, val
+ else:
+ yield reg
+
+if __name__ == "__main__":
+ from m1n1.setup import *
+
+ p.iodev_set_usage(IODEV.FB, 0)
+
+ for reg, val in find_regs(u):
+ print(f"{sysreg_name(reg)} ({', '.join(map(str, reg))}) = 0x{val:x}")
+
+ try:
+ u.msr(reg, val, silent=True)
+ except:
+ print(" - READONLY")
+ try:
+ u.mrs(reg, silent=True, call="el1")
+ except:
+ print(" - ### EL2 only ###")
+ try:
+ u.mrs(reg, silent=True, call="el0")
+ except:
+ pass
+ else:
+ print(" - *** EL0 accessible ***")
diff --git a/tools/proxyclient/m1n1/fw/__init__.py b/tools/proxyclient/m1n1/fw/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/__init__.py
diff --git a/tools/proxyclient/m1n1/fw/afk/__init__.py b/tools/proxyclient/m1n1/fw/afk/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/afk/__init__.py
diff --git a/tools/proxyclient/m1n1/fw/afk/epic.py b/tools/proxyclient/m1n1/fw/afk/epic.py
new file mode 100644
index 0000000..e95281d
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/afk/epic.py
@@ -0,0 +1,292 @@
+# SPDX-License-Identifier: MIT
+
+import struct
+from io import BytesIO
+from construct import *
+from ..common import *
+from ...utils import *
+from ..asc import StandardASC
+from ..asc.base import *
+from .rbep import AFKRingBufEndpoint
+
+EPICType = "EPICType" / Enum(Int32ul,
+ NOTIFY = 0,
+ COMMAND = 3,
+ REPLY = 4,
+ NOTIFY_ACK = 8,
+)
+
+EPICCategory = "EPICCategory" / Enum(Int8ul,
+ REPORT = 0x00,
+ NOTIFY = 0x10,
+ REPLY = 0x20,
+ COMMAND = 0x30,
+)
+
+EPICHeader = Struct(
+ "channel" / Int32ul,
+ "type" / EPICType,
+ "version" / Const(2, Int8ul),
+ "seq" / Int16ul,
+ "pad" / Const(0, Int8ul),
+ "unk" / Const(0, Int32ul),
+ "timestamp" / Default(Int64ul, 0),
+)
+
+EPICSubHeader = Struct(
+ "length" / Int32ul,
+ "version" / Default(Int8ul, 4),
+ "category" / EPICCategory,
+ "type" / Hex(Int16ul),
+ "timestamp" / Default(Int64ul, 0),
+ "seq" / Int16ul,
+ "unk" / Default(Hex(Int16ul), 0),
+ "inline_len" / Hex(Int32ul),
+)
+
+EPICAnnounce = Struct(
+ "name" / Padded(32, CString("utf8")),
+ "props" / Optional(OSSerialize())
+)
+
+EPICSetProp = Struct(
+ "name_len" / Int32ul,
+ "name" / Aligned(4, CString("utf8")),
+ "value" / OSSerialize()
+)
+
+EPICCmd = Struct(
+ "retcode" / Default(Hex(Int32ul), 0),
+ "rxbuf" / Hex(Int64ul),
+ "txbuf" / Hex(Int64ul),
+ "rxlen" / Hex(Int32ul),
+ "txlen" / Hex(Int32ul),
+ "rxcookie" / Optional(Default(Bool(Int8ul), False)),
+ "txcookie" / Optional(Default(Bool(Int8ul), False)),
+)
+
+
+class EPICError(Exception):
+ pass
+
+
+class EPICService:
+ RX_BUFSIZE = 0x4000
+ TX_BUFSIZE = 0x4000
+
+ def __init__(self, ep):
+ self.iface = ep.asc.iface
+ self.ep = ep
+ self.ready = False
+ self.chan = None
+ self.seq = 0
+
+ def log(self, msg):
+ print(f"[{self.ep.name}.{self.SHORT}] {msg}")
+
+ def init(self, props):
+ self.log(f"Init: {props}")
+ self.props = props
+ self.rxbuf, self.rxbuf_dva = self.ep.asc.ioalloc(self.RX_BUFSIZE)
+ self.txbuf, self.txbuf_dva = self.ep.asc.ioalloc(self.TX_BUFSIZE)
+ self.ready = True
+
+ def wait(self):
+ while not self.ready:
+ self.ep.asc.work()
+
+ def handle_report(self, category, type, seq, fd):
+ self.log(f"Report {category}/{type} #{seq}")
+ chexdump(fd.read())
+
+ def handle_notify(self, category, type, seq, fd):
+ retcode = struct.unpack("<I", fd.read(4))[0]
+ self.log(f"Notify {category}/{type} #{seq} ({retcode})")
+ data = fd.read()
+ chexdump(data)
+ print("Send ACK")
+
+ data = data[:0x50] + b"\x01\x00\x00\x00" + data[0x54:]
+
+ pkt = struct.pack("<I", 0) + data
+ self.ep.send_epic(self.chan, EPICType.NOTIFY_ACK, EPICCategory.REPLY, type, seq, pkt, len(data))
+
+ def handle_reply(self, category, type, seq, fd):
+ off = fd.tell()
+ data = fd.read()
+ if len(data) == 4:
+ retcode = struct.unpack("<I", data)[0]
+ if retcode:
+ raise EPICError(f"IOP returned errcode {retcode:#x}")
+ else:
+ self.reply = retcode
+ return
+ fd.seek(off)
+ cmd = EPICCmd.parse_stream(fd)
+ payload = fd.read()
+ self.log(f"Response {type:#x} #{seq}: {cmd.retcode:#x}")
+ if cmd.retcode != 0:
+ raise EPICError(f"IOP returned errcode {cmd.retcode:#x}")
+ if payload:
+ self.log("Inline payload:")
+ chexdump(payload)
+ assert cmd.rxbuf == self.rxbuf_dva
+ self.reply = self.iface.readmem(self.rxbuf, cmd.rxlen)
+
+ def handle_cmd(self, category, type, seq, fd):
+ cmd = EPICCmd.parse_stream(fd)
+ self.log(f"Command {type:#x} #{seq}: {cmd.retcode:#x}")
+
+ def send_cmd(self, type, data, retlen=None):
+ if retlen is None:
+ retlen = len(data)
+ cmd = Container()
+ cmd.rxbuf = self.rxbuf_dva
+ cmd.txbuf = self.txbuf_dva
+ cmd.txlen = len(data)
+ cmd.rxlen = retlen
+ self.iface.writemem(self.txbuf, data)
+ self.reply = None
+ pkt = EPICCmd.build(cmd)
+ self.ep.send_epic(self.chan, EPICType.COMMAND, EPICCategory.COMMAND, type, self.seq, pkt)
+ self.seq += 1
+ while self.reply is None:
+ self.ep.asc.work()
+ return self.reply
+
+class EPICStandardService(EPICService):
+ def call(self, group, cmd, data=b'', replen=None):
+ msg = struct.pack("<2xHIII48x", group, cmd, len(data), 0x69706378) + data
+ if replen is not None:
+ replen += 64
+ resp = self.send_cmd(0xc0, msg, replen)
+ if not resp:
+ return
+ rgroup, rcmd, rlen, rmagic = struct.unpack("<2xHIII", resp[:16])
+ assert rmagic == 0x69706378
+ assert rgroup == group
+ assert rcmd == cmd
+ return resp[64:64+rlen]
+
+ def getLocation(self, unk=0):
+ return struct.unpack("<16xI12x", self.call(4, 4, bytes(32)))
+
+ def getUnit(self, unk=0):
+ return struct.unpack("<16xI12x", self.call(4, 5, bytes(32)))
+
+ def open(self, unk=0):
+ self.call(4, 6, struct.pack("<16xI12x", unk))
+
+ def close(self):
+ self.call(4, 7, bytes(16))
+
+class AFKSystemService(EPICService):
+ NAME = "system"
+ SHORT = "system"
+
+ def getProperty(self, prop, val):
+ pass
+ #self.send_cmd(0x40, msg, 0)
+
+ def setProperty(self, prop, val):
+ msg = {
+ "name_len": (len(prop) + 3) & ~3,
+ "name": prop,
+ "value": val,
+ }
+ msg = EPICSetProp.build(msg)
+ self.send_cmd(0x43, msg, 0)
+
+class EPICEndpoint(AFKRingBufEndpoint):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.serv_map = {}
+ self.chan_map = {}
+ self.serv_names = {}
+ self.hseq = 0
+
+ for i in self.SERVICES:
+ self.serv_names[i.NAME] = i
+
+ def handle_ipc(self, data):
+ fd = BytesIO(data)
+ hdr = EPICHeader.parse_stream(fd)
+ sub = EPICSubHeader.parse_stream(fd)
+
+ if self.verbose > 2:
+ self.log(f"Ch {hdr.channel} Type {hdr.type} Ver {hdr.version} Seq {hdr.seq}")
+ self.log(f" Len {sub.length} Ver {sub.version} Cat {sub.category} Type {sub.type:#x} Seq {sub.seq}")
+
+ if sub.category == EPICCategory.REPORT:
+ self.handle_report(hdr, sub, fd)
+ if sub.category == EPICCategory.NOTIFY:
+ self.handle_notify(hdr, sub, fd)
+ elif sub.category == EPICCategory.REPLY:
+ self.handle_reply(hdr, sub, fd)
+ elif sub.category == EPICCategory.COMMAND:
+ self.handle_cmd(hdr, sub, fd)
+
+ def wait_for(self, name):
+ while True:
+ srv = getattr(self, name, None)
+ if srv is not None and srv.ready:
+ break
+ self.asc.work()
+
+ def handle_report(self, hdr, sub, fd):
+ if sub.type == 0x30:
+ init = EPICAnnounce.parse_stream(fd)
+ if init.props is None:
+ init.props = {}
+ name = init.name
+ if "EPICName" in init.props:
+ name = init.props["EPICName"]
+ key = name + str(init.props.get("EPICUnit", ""))
+ if name in self.serv_names:
+ srv = self.serv_names[name](self)
+ short = srv.SHORT + str(init.props.get("EPICUnit", ""))
+ setattr(self, short, srv)
+ srv.init(init.props)
+ srv.chan = hdr.channel
+ self.chan_map[hdr.channel] = srv
+ self.serv_map[key] = srv
+ self.log(f"New service: {key} on channel {hdr.channel} (short name: {short})")
+ else:
+ self.log(f"Unknown service {key} on channel {hdr.channel}")
+ else:
+ if hdr.channel not in self.chan_map:
+ self.log(f"Ignoring report on channel {hdr.channel}")
+ else:
+ self.chan_map[hdr.channel].handle_report(sub.category, sub.type, sub.seq, fd)
+
+ def handle_notify(self, hdr, sub, fd):
+ self.chan_map[hdr.channel].handle_notify(sub.category, sub.type, sub.seq, fd)
+
+ def handle_reply(self, hdr, sub, fd):
+ self.chan_map[hdr.channel].handle_reply(sub.category, sub.type, sub.seq, fd)
+
+ def handle_cmd(self, hdr, sub, fd):
+ self.chan_map[hdr.channel].handle_cmd(sub.category, sub.type, sub.seq, fd)
+
+ def send_epic(self, chan, ptype, category, type, seq, data, inline_len=0):
+ hdr = Container()
+ hdr.channel = chan
+ hdr.type = ptype
+ hdr.seq = self.hseq
+ self.hseq += 1
+
+ sub = Container()
+ sub.length = len(data)
+ sub.category = category
+ sub.type = type
+ sub.seq = seq
+ sub.inline_len = inline_len
+ pkt = EPICHeader.build(hdr) + EPICSubHeader.build(sub) + data
+ super().send_ipc(pkt)
+
+class AFKSystemEndpoint(EPICEndpoint):
+ SHORT = "system"
+
+ SERVICES = [
+ AFKSystemService,
+ ]
diff --git a/tools/proxyclient/m1n1/fw/afk/rbep.py b/tools/proxyclient/m1n1/fw/afk/rbep.py
new file mode 100644
index 0000000..872d75f
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/afk/rbep.py
@@ -0,0 +1,225 @@
+# SPDX-License-Identifier: MIT
+
+import struct
+
+from ..common import *
+from ...utils import *
+from ..asc.base import *
+
+
+class AFKEPMessage(Register64):
+ TYPE = 63, 48
+
+class AFKEP_GetBuf(AFKEPMessage):
+ TYPE = 63, 48, Constant(0x89)
+ SIZE = 31, 16
+ TAG = 15, 0
+
+class AFKEP_GetBuf_Ack(AFKEPMessage):
+ TYPE = 63, 48, Constant(0xa1)
+ DVA = 47, 0
+
+class AFKEP_InitRB(AFKEPMessage):
+ OFFSET = 47, 32
+ SIZE = 31, 16
+ TAG = 15, 0
+
+class AFKEP_Send(AFKEPMessage):
+ TYPE = 63, 48, Constant(0xa2)
+ WPTR = 31, 0
+
+class AFKEP_Recv(AFKEPMessage):
+ TYPE = 63, 48, Constant(0x85)
+ WPTR = 31, 0
+
+class AFKEP_Init(AFKEPMessage):
+ TYPE = 63, 48, Constant(0x80)
+
+class AFKEP_Init_Ack(AFKEPMessage):
+ TYPE = 63, 48, Constant(0xa0)
+
+class AFKEP_Start(AFKEPMessage):
+ TYPE = 63, 48, Constant(0xa3)
+
+class AFKEP_Start_Ack(AFKEPMessage):
+ TYPE = 63, 48, Constant(0x86)
+
+class AFKEP_Shutdown(AFKEPMessage):
+ TYPE = 63, 48, Constant(0xc0)
+
+class AFKEP_Shutdown_Ack(AFKEPMessage):
+ TYPE = 63, 48, Constant(0xc1)
+
+
+class AFKError(Exception):
+ pass
+
+class AFKRingBuf(Reloadable):
+ BLOCK_SIZE = 0x40
+
+ def __init__(self, ep, base, size):
+ self.ep = ep
+ self.base = base
+
+ bs, unk = struct.unpack("<II", self.read_buf(0, 8))
+ assert (bs + 3 * self.BLOCK_SIZE) == size
+ self.bufsize = bs
+ self.rptr = 0
+ self.wptr = 0
+
+ def read_buf(self, off, size):
+ return self.ep.iface.readmem(self.base + off, size)
+
+ def write_buf(self, off, data):
+ return self.ep.iface.writemem(self.base + off, data)
+
+ def get_rptr(self):
+ return self.ep.asc.p.read32(self.base + self.BLOCK_SIZE)
+
+ def get_wptr(self):
+ return self.ep.asc.p.read32(self.base + 2 * self.BLOCK_SIZE)
+
+ def update_rptr(self, rptr):
+ self.ep.asc.p.write32(self.base + self.BLOCK_SIZE, self.rptr)
+
+ def update_wptr(self, rptr):
+ self.ep.asc.p.write32(self.base + 2 * self.BLOCK_SIZE, self.wptr)
+
+ def read(self):
+ self.wptr = self.get_wptr()
+
+ while self.wptr != self.rptr:
+ hdr = self.read_buf(3 * self.BLOCK_SIZE + self.rptr, 16)
+ self.rptr += 16
+ magic, size = struct.unpack("<4sI", hdr[:8])
+ assert magic in [b"IOP ", b"AOP "]
+ if size > (self.bufsize - self.rptr):
+ hdr = self.read_buf(3 * self.BLOCK_SIZE, 16)
+ self.rptr = 16
+ magic, size = struct.unpack("<4sI", hdr[:8])
+ assert magic in [b"IOP ", b"AOP "]
+
+ payload = self.read_buf(3 * self.BLOCK_SIZE + self.rptr, size)
+ self.rptr = (align_up(self.rptr + size, self.BLOCK_SIZE)) % self.bufsize
+ self.update_rptr(self.rptr)
+ yield hdr[8:] + payload
+ self.wptr = self.get_wptr()
+
+ self.update_rptr(self.rptr)
+
+ def write(self, data):
+ hdr2, data = data[:8], data[8:]
+ self.rptr = self.get_rptr()
+
+ if self.wptr < self.rptr and self.wptr + 0x10 >= self.rptr:
+ raise AFKError("Ring buffer is full")
+
+ hdr = struct.pack("<4sI", b"IOP ", len(data)) + hdr2
+ self.write_buf(3 * self.BLOCK_SIZE + self.wptr, hdr)
+
+ if len(data) > (self.bufsize - self.wptr - 16):
+ if self.rptr < 0x10:
+ raise AFKError("Ring buffer is full")
+ self.write_buf(3 * self.BLOCK_SIZE, hdr)
+ self.wptr = 0
+
+ if self.wptr < self.rptr and self.wptr + 0x10 + len(data) >= self.rptr:
+ raise AFKError("Ring buffer is full")
+
+ self.write_buf(3 * self.BLOCK_SIZE + self.wptr + 0x10, data)
+ self.wptr = align_up(self.wptr + 0x10 + len(data), self.BLOCK_SIZE) % self.bufsize
+
+ self.update_wptr(self.wptr)
+ return self.wptr
+
+class AFKRingBufEndpoint(ASCBaseEndpoint):
+ BASE_MESSAGE = AFKEPMessage
+ SHORT = "afkep"
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.txq = None
+ self.rxq = None
+ self.iface = self.asc.iface
+ self.alive = False
+ self.started = False
+ self.iobuffer = None
+ self.verbose = 2
+ self.msgid = 0
+
+ def start(self):
+ self.send(AFKEP_Init())
+
+ @msg_handler(0xa0, AFKEP_Init_Ack)
+ def Init_Ack(self, msg):
+ self.alive = True
+ return True
+
+ @msg_handler(0x89, AFKEP_GetBuf)
+ def GetBuf(self, msg):
+ size = msg.SIZE * AFKRingBuf.BLOCK_SIZE
+
+ if self.iobuffer:
+ print("WARNING: trying to reset iobuffer!")
+
+ self.iobuffer, self.iobuffer_dva = self.asc.ioalloc(size)
+ self.asc.p.write32(self.iobuffer, 0xdeadbeef)
+ self.send(AFKEP_GetBuf_Ack(DVA=self.iobuffer_dva))
+ self.log(f"Buffer: phys={self.iobuffer:#x} dva={self.iobuffer_dva:#x} size={size:#x}")
+ return True
+
+ def stop(self):
+ self.log("Shutting down")
+ self.send(AFKEP_Shutdown())
+ while self.alive:
+ self.asc.work()
+
+ @msg_handler(0xc1, AFKEP_Shutdown_Ack)
+ def Shutdown_Ack(self, msg):
+ self.alive = False
+ self.log("Shutdown ACKed")
+ return True
+
+ @msg_handler(0x8a, AFKEP_InitRB)
+ def InitTX(self, msg):
+ self.txq = self.init_rb(msg)
+ if self.rxq and self.txq:
+ self.start_queues()
+ return True
+
+ @msg_handler(0x8b, AFKEP_InitRB)
+ def InitRX(self, msg):
+ self.rxq = self.init_rb(msg)
+ if self.rxq and self.txq:
+ self.start_queues()
+ return True
+
+ def init_rb(self, msg):
+ off = msg.OFFSET * AFKRingBuf.BLOCK_SIZE
+ size = msg.SIZE * AFKRingBuf.BLOCK_SIZE
+
+ return AFKRingBuf(self, self.iobuffer + off, size)
+
+ def start_queues(self):
+ self.send(AFKEP_Start())
+
+ @msg_handler(0x86, AFKEP_Start_Ack)
+ def Start_Ack(self, msg):
+ self.started = True
+ return True
+
+ @msg_handler(0x85, AFKEP_Recv)
+ def Recv(self, msg):
+ for data in self.rxq.read():
+ if self.verbose >= 3:
+ self.log(f"<RX rptr={self.rxq.rptr:#x}")
+ chexdump(data)
+ self.handle_ipc(data)
+ return True
+
+ def handle_ipc(self, data):
+ pass
+
+ def send_ipc(self, data):
+ wptr = self.txq.write(data)
+ self.send(AFKEP_Send(WPTR = wptr))
diff --git a/tools/proxyclient/m1n1/fw/agx/__init__.py b/tools/proxyclient/m1n1/fw/agx/__init__.py
new file mode 100644
index 0000000..e436720
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/agx/__init__.py
@@ -0,0 +1,98 @@
+# SPDX-License-Identifier: MIT
+from ...utils import *
+from ...malloc import Heap
+
+from ..asc import StandardASC
+from ..asc.base import ASCBaseEndpoint, msg_handler
+
+from .initdata import InitData, IOMapping
+
+__all__ = []
+
+class GpuMsg(Register64):
+ TYPE = 63, 48
+
+class InitMsg(GpuMsg):
+ TYPE = 63, 48, Constant(0x81)
+ UNK = 47, 44, Constant(0)
+ INITDATA = 43, 0
+
+class EventMsg(GpuMsg):
+ TYPE = 63, 48, Constant(0x42)
+ UNK = 47, 0, Constant(0)
+
+class DoorbellMsg(GpuMsg):
+ TYPE = 63, 48, Constant(0x83)
+ CHANNEL = 15, 0
+
+class FWCtlMsg(GpuMsg):
+ TYPE = 63, 48, Constant(0x84)
+
+class HaltMsg(GpuMsg):
+ TYPE = 63, 48, Constant(0x85)
+
+class FirmwareEP(ASCBaseEndpoint):
+ BASE_MESSAGE = GpuMsg
+ SHORT = "fw"
+
+ @msg_handler(0x42)
+ def event(self, msg):
+ #self.log("Received event")
+ self.asc.agx.poll_channels()
+ return True
+
+ def send_initdata(self, addr):
+ self.log(f"Sending initdata @ {addr:#x}")
+ msg = InitMsg(INITDATA=addr)
+ self.send(msg)
+
+class DoorbellEP(ASCBaseEndpoint):
+ BASE_MESSAGE = DoorbellMsg
+ SHORT = "db"
+
+ def doorbell(self, channel):
+ #self.log(f"Sending doorbell ch={channel}")
+ msg = DoorbellMsg(CHANNEL = channel)
+ self.send(msg)
+
+ def fwctl_doorbell(self):
+ msg = FWCtlMsg()
+ self.send(msg)
+
+class AGXASC(StandardASC):
+ ENDPOINTS = {
+ 0x20: FirmwareEP,
+ 0x21: DoorbellEP,
+ }
+
+ def __init__(self, u, base, agx, uat):
+ super().__init__(u, base)
+ self.agx = agx
+ self.uat = uat
+
+ def addr(self, addr):
+ base, obj = self.agx.find_object(addr)
+ if base is None:
+ return super().addr(addr)
+
+ return f"{addr:#x} ({obj._name} [{obj._size:#x}] @ {base:#x} + {addr - base:#x})"
+
+ def iomap(self, addr, size):
+ return self.uat.iomap(0, addr, size)
+
+ def ioalloc(self, size):
+ paddr = self.u.memalign(0x4000, size)
+ dva = self.iomap(paddr, size)
+ return paddr, dva
+
+ def ioread(self, dva, size, ctx=0):
+ return self.uat.ioread(ctx, dva & 0xFFFFFFFFFF, size)
+
+ def iowrite(self, dva, data, ctx=0):
+ return self.uat.iowrite(ctx, dva & 0xFFFFFFFFFF, data)
+
+ def iotranslate(self, dva, size, ctx=0):
+ return self.uat.iotranslate(ctx, dva & 0xFFFFFFFFFF, size)
+
+__all__.extend(k for k, v in globals().items()
+ if (callable(v) or isinstance(v, type)) and v.__module__ == __name__)
diff --git a/tools/proxyclient/m1n1/fw/agx/channels.py b/tools/proxyclient/m1n1/fw/agx/channels.py
new file mode 100644
index 0000000..6704d20
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/agx/channels.py
@@ -0,0 +1,481 @@
+
+import random
+
+from m1n1.utils import *
+from m1n1.constructutils import *
+from construct import *
+from .cmdqueue import *
+
+__all__ = ["channelNames", "channelRings", "DeviceControlMsg", "EventMsg", "StatsMsg"]
+
+class RunCmdQueueMsg(ConstructClass):
+ subcon = Struct (
+ "queue_type" / Default(Int32ul, 0),
+ "cmdqueue_addr" / Default(Hex(Int64ul), 0),
+ "cmdqueue" / Lazy(ROPointer(this.cmdqueue_addr, CommandQueueInfo)),
+ "head" / Default(Int32ul, 0),
+ "event_number" / Default(Int32ul, 0),
+ "new_queue" / Default(Int32ul, 0),
+ "data" / HexDump(Default(Bytes(0x18), bytes(0x18))),
+ )
+
+ TYPES = {
+ 0: "SubmitTA",
+ 1: "Submit3D",
+ 2: "SubmitCompute",
+ }
+
+ def __str__(self, *args, **kwargs):
+ s = super().__str__(*args, **kwargs) + "\n"
+
+ if self.cmdqueue_addr == 0:
+ return s + "<Empty RunCmdQueueMsg>"
+
+ r = random.randrange(2**64)
+ s += f"{self.TYPES[self.queue_type]}(0x{self.cmdqueue_addr & 0xfff_ffffffff:x}, {self.head}, ev={self.event_number}, new={self.new_queue}) //{r:x}"
+ return s
+
+class DC_DestroyContext(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Const(0x17, Int32ul),
+ "unk_4" / Hex(Int32ul),
+ "unk_8" / Hex(Int32ul),
+ "unk_c" / Hex(Int32ul),
+ "unk_10" / Hex(Int32ul),
+ "unk_14" / Hex(Int32ul),
+ "unk_18" / Hex(Int32ul),
+ "context_addr" / Hex(Int64ul),
+ "rest" / HexDump(Default(Bytes(0xc), bytes(0xc)))
+ )
+
+class DC_Write32(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Const(0x18, Int32ul),
+ "addr" / Hex(Int64ul),
+ "data" / Int32ul,
+ "unk_10" / Int32ul,
+ "unk_14" / Int32ul,
+ "unk_18" / Int32ul,
+ "unk_1c" / Int32ul,
+ "rest" / HexDump(Default(Bytes(0x10), bytes(0x10)))
+ )
+
+class DC_Write32B(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Const(0x13, Int32ul),
+ "addr" / Hex(Int64ul),
+ "data" / Int32ul,
+ "unk_10" / Int32ul,
+ "unk_14" / Int32ul,
+ "unk_18" / Int32ul,
+ "unk_1c" / Int32ul,
+ "rest" / HexDump(Default(Bytes(0x10), bytes(0x10)))
+ )
+
+class DC_Init(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Const(0x19, Int32ul),
+ "data" / HexDump(Default(Bytes(0x2c), bytes(0x2c)))
+ )
+
+class DC_09(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Const(0x9, Int32ul),
+ "unk_4" / Int64ul,
+ "unkptr_c" / Int64ul,
+ "unk_14" / Int64ul,
+ "data" / HexDump(Default(Bytes(0x14), bytes(0x14)))
+ )
+
+class DC_Any(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Int32ul,
+ "data" / HexDump(Default(Bytes(0x2c), bytes(0x2c)))
+ )
+
+class DC_1e(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Const(0x1e, Int32ul),
+ "unk_4" / Int64ul,
+ "unk_c" / Int64ul,
+ "data" / HexDump(Default(Bytes(0x1c), bytes(0x1c)))
+ )
+
+class DC_UpdateIdleTS(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Const(0x23, Int32ul),
+ "data" / HexDump(Default(Bytes(0x2c), bytes(0x2c))),
+ )
+
+class UnknownMsg(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Int32ul),
+ "data" / HexDump(Bytes(0x2c)),
+ )
+
+DeviceControlMsg = FixedSized(0x30, Select(
+ DC_DestroyContext,
+ DC_Init,
+ DC_UpdateIdleTS,
+ DC_1e,
+ DC_Write32,
+ UnknownMsg,
+))
+
+class StatsMsg_Power(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(0x00, Int32ul)),
+ ZPadding(0x18), # ??? why the hole? never written...
+ "power" / Hex(Int64ul),
+ ZPadding(0xc), # Confirmed padding
+ )
+
+ def __str__(self):
+ return f"Power: {self.power / 8192.0:.3f} mW"
+
+class StatsMsg_PowerOn(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(0x02, Int32ul)),
+ "power_off_ticks" / Dec(Int64ul),
+ ZPadding(0x24), # Confirmed padding
+ )
+ def __str__(self):
+ t = self.power_off_ticks / 24000000
+ return f"Power ON: spent {t:.04}s powered off ({self.power_off_ticks} ticks)"
+
+class StatsMsg_PowerOff(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(0x03, Int32ul)),
+ "power_on_ticks" / Dec(Int64ul),
+ ZPadding(0x24), # Confirmed padding
+ )
+ def __str__(self):
+ t = self.power_on_ticks / 24000000
+ return f"Power OFF: spent {t:.04}s powered on ({self.power_on_ticks} ticks)"
+
+class StatsMsg_Util(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(0x04, Int32ul)),
+ "timestamp" / Hex(Int64ul),
+ "util1" / Dec(Int32ul),
+ "util2" / Dec(Int32ul),
+ "util3" / Dec(Int32ul),
+ "util4" / Dec(Int32ul),
+ ZPadding(0x14), # Confirmed padding
+ )
+ def __str__(self):
+ return f"Utilization: {self.util1:>3d}% {self.util2:>3d}% {self.util3:>3d}% {self.util4:>3d}%"
+
+class StatsMsg_AvgPower(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(0x09, Int32ul)),
+ "active_cs" / Dec(Int64ul),
+ "unk2" / Hex(Int32ul),
+ "unk3" / Hex(Int32ul),
+ "unk4" / Hex(Int32ul),
+ "avg_power" / Dec(Int32ul),
+ ZPadding(0x14), # Confirmed padding
+ )
+
+ def __str__(self):
+ return f"Activity: Active {self.active_cs * 10:6d} ms Avg Pwr {self.avg_power:4d} mW ({self.unk2:d} {self.unk3:d} {self.unk4:d})"
+
+class StatsMsg_Temp(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(0x0a, Int32ul)),
+ ZPadding(8), # Not written
+ "raw_value" / Hex(Int32ul),
+ "scale" / Hex(Int32ul),
+ "tmin" / Hex(Int32ul),
+ "tmax" / Hex(Int32ul),
+ ZPadding(0x14), # Confirmed padding
+ )
+
+ def __str__(self):
+ temp = self.raw_value / float(self.scale) / 64.0
+ return f"Temp: {temp:.2f}°C s={self.scale:d} tmin={self.tmin:d} tmax={self.tmax:d}"
+
+class StatsMsg_PowerState(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(0x0b, Int32ul)),
+ "timestamp" / Hex(Int64ul),
+ "last_busy_ts" / Hex(Int64ul),
+ "active" / Hex(Int32ul),
+ "poweroff" / Dec(Int32ul),
+ "unk2" / Dec(Int32ul),
+ "pstate" / Dec(Int32ul),
+ "unk4" / Dec(Int32ul),
+ "unk5" / Dec(Int32ul),
+ ZPadding(4), # Confirmed padding
+ )
+
+ def __str__(self):
+ act = "ACT" if self.active else " "
+ off = "OFF" if self.poweroff else " "
+
+ return f"PowerState: {act} {off} ps={int(self.pstate)} {self.unk4} {self.unk2} {self.unk5}"
+
+class StatsMsg_FWBusy(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(0x0c, Int32ul)),
+ "timestamp" / Hex(Int64ul),
+ "flag" / Int32ul,
+ ZPadding(0x20), # Confirmed padding
+ )
+
+ def __str__(self):
+ return f"FW active: {bool(self.flag)}"
+
+class StatsMsg_PState(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(0x0d, Int32ul)),
+ ZPadding(8), # Not written
+ "ps_min" / Dec(Int32ul),
+ "unk1" / Dec(Int32ul),
+ "ps_max" / Dec(Int32ul),
+ "unk3" / Dec(Int32ul),
+ ZPadding(0x14), # Confirmed padding
+ )
+ def __str__(self):
+ return f"PState: {self.ps_min:d}..{self.ps_max:d} ({self.unk1:d}/{self.unk3:d})"
+
+class StatsMsg_TempSensor(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(0x0e, Int32ul)),
+ ZPadding(4), # Not written
+ "sensor_id" / Hex(Int32ul),
+ "raw_value" / Hex(Int32ul),
+ "scale" / Dec(Int32ul),
+ "tmin" / Dec(Int32ul),
+ "tmax" / Dec(Int32ul),
+ ZPadding(0x14), # Confirmed padding
+ )
+ def __str__(self):
+ temp = self.raw_value / float(self.scale) / 64.0
+ return f"TempSensor: #{self.sensor_id:d} {temp:.2f}°C s={self.scale:d} tmin={self.tmin:d} tmax={self.tmax:d}"
+
+StatsMsg = FixedSized(0x30, Select(
+ StatsMsg_Power,
+ StatsMsg_PowerOn,
+ StatsMsg_PowerOff,
+ StatsMsg_Util,
+ StatsMsg_AvgPower,
+ StatsMsg_Temp,
+ StatsMsg_PowerState,
+ StatsMsg_FWBusy,
+ StatsMsg_PState,
+ StatsMsg_TempSensor,
+ UnknownMsg,
+))
+
+class FWLogMsg(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(0x03, Int32ul)),
+ "seq_no" / Hex(Int32ul),
+ "timestamp" / Hex(Int64ul),
+ "msg" / PaddedString(0xc8, "ascii")
+ )
+
+class FaultMsg(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(0, Int32ul)),
+ "unk_4" / HexDump(Bytes(0x34)),
+ )
+
+class FlagMsg(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(1, Int32ul)),
+ "firing" / Array(2, Hex(Int64ul)),
+ "unk_14" / Hex(Int16ul),
+ "tail" / Bytes(0x38 - 0x18),
+ )
+
+class TimeoutMsg(ConstructClass):
+ subcon = Struct (
+ "msg_type" / Hex(Const(4, Int32ul)),
+ "counter" / Hex(Int32ul),
+ "unk_8" / Hex(Int32ul),
+ "stamp_index" / Hex(Int32sl),
+ "unkpad_16" / HexDump(Bytes(0x38 - 0x10)),
+ )
+
+EventMsg = FixedSized(0x38, Select(
+ FaultMsg,
+ FlagMsg,
+ TimeoutMsg,
+ HexDump(Bytes(0x38)),
+))
+
+TRACE_MSGS = {
+ (0x00, 0x00, 0): ("StartTA", "uuid", None, "unk", "cmdqueue"),
+ (0x00, 0x01, 0): ("FinishTA", "uuid", None, "unk", "cmdqueue"),
+ (0x00, 0x04, 0): ("Start3D", "uuid", "partial_render", "unk", "cmdqueue"),
+ (0x00, 0x05, 0): ("Finish3D_unk", "uuid", "unk", "flag", "buf_related"),
+ (0x00, 0x06, 0): ("Finish3D", "uuid", None, "unk", "cmdqueue"),
+ (0x00, 0x07, 0): ("StartCP", "uuid", None, "unk", "cmdqueue"),
+ (0x00, 0x08, 0): ("FinishCP", "uuid", None, "unk", "cmdqueue"),
+ (0x00, 0x0a, 0): ("StampUpdateTA", "value", "ev_id", "addr", "uuid"),
+ (0x00, 0x0c, 0): ("StampUpdate3D", "value", "ev_id", "addr", "uuid"),
+ (0x00, 0x0e, 0): ("StampUpdateCL", "value", "ev_id", "addr", "uuid"),
+ (0x00, 0x10, 1): ("TAPreproc1", "unk"),
+ (0x00, 0x10, 2): ("TAPreproc2", "unk1", "unk2"),
+ (0x00, 0x17, 0): ("Finish3D2", "uuid", None, "unk", "cmdqueue"),
+ (0x00, 0x28, 0): ("EvtNotify", "firing0", "firing1", "firing2", "firing3"),
+ (0x00, 0x2f, 0): ("Finish3D_unk2", "uuid", "unk"),
+ (0x00, 0x1e, 0): ("CleanupPB", "uuid", "unk2", "slot"),
+ (0x01, 0x0a, 0): ("Postproc", "cmdid", "event_ctl", "stamp_val", "uuid"),
+ (0x01, 0x0b, 0): ("EvtComplete", None, "event_ctl"),
+ (0x01, 0x0d, 0): ("EvtDequeued", "next", "event_ctl"),
+ (0x01, 0x16, 0): ("InitAttachment", "idx", "flags", "addr", "size"),
+ (0x01, 0x18, 0): ("ReInitAttachment", "idx", "flags", "addr", "size"),
+}
+
+class KTraceMsg(ConstructClass):
+ THREADS = [
+ "irq",
+ "bg",
+ "smpl",
+ "pwr",
+ "rec",
+ "kern",
+ ]
+ subcon = Struct (
+ "msg_type" / Hex(Const(5, Int32ul)),
+ "timestamp" / Hex(Int64ul),
+ "args" / Array(4, Int64ul),
+ "code" / Int8ul,
+ "channel" / Int8ul,
+ "pad" / Const(0, Int8ul),
+ "thread" / Int8ul,
+ "unk_flag" / Int64ul,
+ )
+ def __str__(self):
+ ts = self.timestamp / 24000000
+ code = (self.channel, self.code, self.unk_flag)
+ if code in TRACE_MSGS:
+ info = TRACE_MSGS[code]
+ args = info[0] + ": " + " ".join(f"{k}={v:#x}" for k, v in zip(info[1:], self.args) if k is not None)
+ else:
+ args = "UNK: " + ", ".join(hex(i) for i in self.args)
+ return f"TRACE: [{ts:10.06f}][{self.THREADS[self.thread]:4s}] {self.channel:2x}:{self.code:2x} ({self.unk_flag}) {args}"
+
+class FWCtlMsg(ConstructClass):
+ subcon = Struct (
+ "addr" / Int64ul,
+ "unk_8" / Int32ul,
+ "context_id" / Int32ul,
+ "unk_10" / Int16ul,
+ "unk_12" / Int16ul,
+ )
+
+channelNames = [
+ "TA_0", "3D_0", "CL_0",
+ "TA_1", "3D_1", "CL_1",
+ "TA_2", "3D_2", "CL_2",
+ "TA_3", "3D_3", "CL_3",
+ "DevCtrl",
+ "Event", "FWLog", "KTrace", "Stats",
+
+ ## Not really in normal order
+ "FWCtl"
+]
+
+# Exclude FWCtl
+CHANNEL_COUNT = len(channelNames) - 1
+
+channelRings = (
+ [[(RunCmdQueueMsg, 0x30, 0x100)]] * 12 + [
+ [(DeviceControlMsg, 0x30, 0x100)],
+ [(EventMsg, 0x38, 0x100)],
+ [
+ (FWLogMsg, 0xd8, 0x100), # unk 0
+ (FWLogMsg, 0xd8, 0x100), # init log
+ (FWLogMsg, 0xd8, 0x100), # unk 2
+ (FWLogMsg, 0xd8, 0x100), # warnings?
+ (FWLogMsg, 0xd8, 0x100), # unk 4
+ (FWLogMsg, 0xd8, 0x100), # unk 5
+ ],
+ [(KTraceMsg, 0x38, 0x200)],
+ [(HexDump(Bytes(0x60)), 0x60, 0x100)],
+ [(FWCtlMsg, 0x14, 0x100)],
+ ]
+)
+
+class ChannelStateFields(RegMap):
+ _SIZE = 0x30
+
+ READ_PTR = 0x00, Register32
+ WRITE_PTR = 0x20, Register32
+
+class FWControlStateFields(RegMap):
+ _SIZE = 0x20
+
+ READ_PTR = 0x00, Register32
+ WRITE_PTR = 0x10, Register32
+
+class Channel(Reloadable):
+ def __init__(self, u, uat, info, ring_defs, base=None, state_fields=ChannelStateFields):
+ self.uat = uat
+ self.u = u
+ self.p = u.proxy
+ self.iface = u.iface
+
+ self.ring_defs = ring_defs
+ self.info = info
+
+ self.accessor = uat.ioaccessor(0)
+ self.state_addr = info.state_addr
+ self.state = []
+ self.rb_base = []
+ self.rb_maps = []
+
+ if base is None:
+ p = info.ringbuffer_addr
+ else:
+ p = base
+ for i, (msg, size, count) in enumerate(ring_defs):
+ assert msg.sizeof() == size
+
+ self.state.append(state_fields(self.accessor, self.state_addr + 0x30 * i))
+ m = uat.iotranslate(0, p, size * count)
+ self.rb_base.append(p)
+ self.rb_maps.append(m)
+ p += size * count
+
+ def get_message(self, ring, index, meta_fn=None):
+ msgcls, size, count = self.ring_defs[ring]
+
+ assert index < count
+ addr = self.rb_base[ring] + index * size
+ stream = self.uat.iostream(0, addr)
+ stream.meta_fn = meta_fn
+ return msgcls.parse_stream(stream)
+
+ def clear_message(self, ring, index):
+ msgcls, size, count = self.ring_defs[ring]
+
+ self.put_message(ring, index, b"\xef\xbe\xad\xde" * (size // 4))
+
+ def put_message(self, ring, index, obj):
+ msgcls, size, count = self.ring_defs[ring]
+
+ assert index < count
+ if isinstance(obj, bytes):
+ data = obj
+ else:
+ data = obj.build()
+ self.uat.iowrite(0, self.rb_base[ring] + index * size, data)
+
+class ChannelInfo(ConstructClass):
+ subcon = Struct(
+ "state_addr" / Hex(Int64ul),
+ "ringbuffer_addr" / Hex(Int64ul),
+ )
+
+class ChannelInfoSet(ConstructClass):
+ CHAN_COUNT = CHANNEL_COUNT
+
+ subcon = Struct(*[ name / ChannelInfo for name in channelNames[:CHAN_COUNT]])
+
+__all__.extend(k for k, v in globals().items()
+ if (callable(v) or isinstance(v, type)) and v.__module__ == __name__)
diff --git a/tools/proxyclient/m1n1/fw/agx/cmdqueue.py b/tools/proxyclient/m1n1/fw/agx/cmdqueue.py
new file mode 100644
index 0000000..bd90a05
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/agx/cmdqueue.py
@@ -0,0 +1,516 @@
+# SPDX-License-Identifier: MIT
+from m1n1.constructutils import *
+from construct import *
+from .microsequence import *
+from ...utils import RegMap, Register32
+
+__all__ = []
+
+class WorkCommandBarrier(ConstructClass):
+ """
+ sent before WorkCommand3D on the Submit3d queue.
+ Might be for initilzing the tile buckets?
+
+ Example:
+ 00000004 0c378018 ffffffa0 00000c00 00000006 00000900 08002c9a 00000000
+ 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+ """
+ subcon = Struct(
+ "magic" / Const(0x4, Int32ul),
+ "stamp_addr" / Int64ul,
+ "stamp" / ROPointer(this.stamp_addr, StampCounter),
+ "wait_value" / Int32ul,
+ "event" / Int32ul, # Event number that signals a stamp check
+ "stamp_self" / Int32ul,
+ "uuid" / Int32ul,
+ "unk" / Default(Int32ul, 0),
+ )
+
+class WorkCommandInitBM(ConstructClass):
+ """
+ occationally sent before WorkCommandTA on the SubmitTA queue.
+
+ Example:
+ 00000004 0c378018 ffffffa0 00000c00 00000006 00000900 08002c9a 00000000
+ 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+ """
+ subcon = Struct(
+ "magic" / Const(0x6, Hex(Int32ul)),
+ "context_id" / Hex(Int32ul), # Might be context?
+ "buffer_mgr_slot" / Hex(Int32ul), # 0
+ "unk_c" / Hex(Int32ul), # 0
+ "unk_10" / Hex(Int32ul), # 0x30
+ "buffer_mgr_addr" / Int64ul,
+ "buffer_mgr" / ROPointer(this.buffer_mgr_addr, BufferManagerInfo),
+ "stamp_value" / Hex(Int32ul), # 0x100
+ )
+
+class LinkedListHead(ConstructClass):
+ subcon = Struct(
+ "prev" / Int64ul,
+ "next" / Int64ul,
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.prev = 0
+ self.next = 0
+
+class EventControlUnkBuf(ConstructValueClass):
+ subcon = HexDump(Bytes(0x8))
+
+ def __init__(self):
+ super().__init__()
+ self.value = b"\xff" * 8
+
+class EventControl(ConstructClass):
+ subcon = Struct(
+ "event_count_addr" / Int64ul,
+ "event_count" / ROPointer(this.event_count_addr, Int32ul),
+ "generation" / Int32ul,
+ "cur_count" / Int32ul,
+ "unk_10" / Int32ul,
+ "unk_14" / Int32ul,
+ "unk_18" / Int64ul,
+ "unk_20" / Int32ul,
+ "vm_slot" / Int32ul,
+ "has_ta" / Int32ul,
+ "pstamp_ta" / Array(4, Int64ul),
+ "has_3d" / Int32ul,
+ "pstamp_3d" / Array(4, Int64ul),
+ "has_cp" / Int32ul,
+ "pstamp_cp" / Array(4, Int64ul),
+ "in_list" / Int32ul,
+ Ver("G >= G14", "unk_98_g14_0" / HexDump(Bytes(0x14))),
+ "list_head" / LinkedListHead,
+ Ver("G >= G14", "unk_a8_g14_0" / Padding(4)),
+ Ver("V >= V13_0B4", "unk_buf" / EventControlUnkBuf),
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.unk_14 = 0
+ self.unk_18 = 0
+ self.unk_20 = 0
+ self.vm_slot = 0
+ self.has_ta = 0
+ self.pstamp_ta = [0]*4
+ self.has_3d = 0
+ self.pstamp_3d = [0]*4
+ self.has_cp = 0
+ self.pstamp_cp = [0]*4
+ self.in_list = 0
+ self.unk_98_g14_0 = bytes(0x14)
+ self.list_head = LinkedListHead()
+ self.unk_buf = EventControlUnkBuf()
+
+class WorkCommandCP(ConstructClass):
+ """
+ For compute
+
+ Example:
+ 00000000 00000003 00000000 00000004 0c3d80c0 ffffffa0 00000000 00000000 00000000
+ 00000020 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+ 00000040 *
+ 00000060 00000000 00000000 00088000 00000015 00078000 00000015 000a6300 00000015
+ 00000080 000a6308 00000015 000a6310 00000015 000a6318 00000015 00000000 00000011
+ 000000a0 00008c60 00000000 00000041 00000000 000e8000 00000015 00000040 00000000
+ 000000c0 00000001 00000000 0000001c 00000000 00000000 00000000 00000000 00000000
+ 000000e0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+ 00000100 *
+ 000001e0 00000000 00000000 0c311cc0 ffffffa0 00000240 00000000 00000000 00000000
+ 00000200 00000000 00000000 00000000 00000000 00000000 00000000 00088000 00000015
+ 00000220 00078024 00000015 00000000 00000000 00000000 00000000 00000000 00000000
+ 00000240 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+ 00000260 110022b3 00000000 ffffffff 00000500 00000015 00000000 00000000 00000000
+ 00000280 000c8014 ffffffa0 0c378014 ffffffa0 00003b00 00000005 00000000 00000000
+ 000002a0 120022b8 00000000 00000000 00000000 00029030 ffffffa0 00029038 ffffffa0
+ 000002c0 00000000 00000000 00000000 00000000 00000015 00000000 00000000 00000000
+ 000002e0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+ """
+
+ subcon = Struct(
+ "addr" / Tell,
+ "magic" / Const(0x3, Hex(Int32ul)),
+ "unk_4" / Hex(Int32ul),
+ "context_id" / Hex(Int32ul),
+ "event_control_addr" / Hex(Int64ul),
+ "event_control" / ROPointer(this.event_control_addr, EventControl),
+
+ # This struct embeeds some data that the Control List has pointers back to, but doesn't
+ # seem to be actually part of this struct
+ Padding(0x1e8 - 0x14),
+
+ # offset 000001e8
+ "microsequence_ptr" / Hex(Int64ul),
+ "microsequence_size" / Hex(Int32ul),
+ "microsequence" / ROPointer(this.microsequence_ptr, MicroSequence),
+ )
+
+class WorkCommand0_UnkBuf(ConstructValueClass):
+ subcon = HexDump(Bytes(0x18))
+
+ def __init__(self):
+ self.value = bytes(0x18)
+
+class WorkCommand1_UnkBuf(ConstructValueClass):
+ subcon = HexDump(Bytes(0x110))
+
+ def __init__(self):
+ self.value = bytes(0x110)
+
+class WorkCommand1_UnkBuf2(ConstructClass):
+ subcon = Struct(
+ "unk_0" / Int64ul,
+ "unk_8" / Int64ul,
+ "unk_10" / Int64ul,
+ )
+
+class Flag(ConstructValueClass):
+ subcon = Hex(Int32ul)
+
+ def __init__(self):
+ self.value = 0
+
+class WorkCommand3D(ConstructClass):
+ """
+ For 3D
+
+ Example: 0xfa00c095640
+ 00000000 00000001 00000004 00000000 0c2d5f00 ffffffa0 000002c0 0c3d80c0 ffffffa0
+ 00000020 0c3e0000 ffffffa0 0c3e0100 ffffffa0 0c3e09c0 ffffffa0 01cb0000 00000015
+ 00000040 00000088 00000000 00000001 0010000c 00000000 00000000 00000000 00000000
+ 00000060 3a8de3be 3abd2fa8 00000000 00000000 0000076c 00000000 0000a000 00000000
+ 00000080 ffff8002 00000000 00028044 00000000 00000088 00000000 005d0000 00000015
+ 000000a0 00758000 00000015 0000c000 00000000 00000640 000004b0 0257863f 00000000
+ 000000c0 00000000 00000000 00000154 00000000 011d0000 00000015 011d0000 00000015
+ 000000e0 0195c000 00000015 0195c000 00000015 00000000 00000000 00000000 00000000
+ 00000100 00000000 00000000 00000000 00000000 0193c000 00000015 00000000 00000000
+ 00000120 0193c000 00000015 00000000 00000000 01b64000 00000015 00000000 00000000
+ 00000140 01b64000 00000015 00000000 00000000 01cb0000 00000015 01cb4000 00000015
+ 00000160 c0000000 00000003 01cb4000 00000015 00010280 00000000 00a38000 00000015
+ 00000180 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+ 000001a0 00000000 00000000 00000000 00000000 00000000 00000011 00008c60 00000000
+ 000001c0 00000000 00000000 00000000 00000000 0000001c 00000000 00000000 00000000
+ 000001e0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+ 00000200 *
+ 000003c0 00000012 00028084 00000000 00000000 3a8de3be 3abd2fa8 00000000 00000000
+ 000003e0 0010000c 00000000 00025031 00000004 3f800000 00000700 00000000 00000001
+ """
+
+ subcon = Struct(
+ "addr" / Tell,
+ "magic" / Const(0x1, Hex(Int32ul)),
+ Ver("V >= V13_0B4", "counter" / Int64ul),
+ "context_id" / Hex(Int32ul),
+ "unk_8" / Hex(Int32ul),
+ "microsequence_ptr" / Hex(Int64ul), # Command list
+ "microsequence_size" / Hex(Int32ul),
+ "microsequence" / ROPointer(this.microsequence_ptr, MicroSequence),
+ "event_control_addr" / Hex(Int64ul),
+ "event_control" / ROPointer(this.event_control_addr, EventControl),
+ "buffer_mgr_addr" / Int64ul,
+ "buffer_mgr" / ROPointer(this.buffer_mgr_addr, BufferManagerInfo),
+ "buf_thing_addr" / Int64ul,
+ "buf_thing" / ROPointer(this.buf_thing_addr, BufferThing),
+ "unk_emptybuf_addr" / Hex(Int64ul),
+ "tvb_tilemap" / Hex(Int64ul),
+ "unk_40" / Hex(Int64ul),
+ "unk_48" / Hex(Int32ul),
+ "tile_blocks_y" / Hex(Int16ul), # * 4
+ "tile_blocks_x" / Hex(Int16ul), # * 4
+ "unk_50" / Hex(Int64ul),
+ "unk_58" / Hex(Int64ul),
+ "merge_upper_x" / Hex(Float32l),
+ "merge_upper_y" / Hex(Float32l),
+ "unk_68" / Hex(Int64ul),
+ "tile_count" / Hex(Int64ul),
+
+ # Embedded structures that are also pointed to by other stuff
+ "struct_2" / Start3DStruct2,
+ "struct_1" / Start3DStruct1,
+ "unk_758" / Flag,
+ "unk_75c" / Flag,
+ "unk_buf" / WorkCommand1_UnkBuf,
+ "busy_flag" / Flag,
+ "struct_6" / Start3DStruct6,
+ "struct_7" / Start3DStruct7,
+ "unk_buf2" / WorkCommand1_UnkBuf2,
+ "ts1" / TimeStamp,
+ "ts2" / TimeStamp,
+ "ts3" / TimeStamp,
+ "unk_914" / Int32ul,
+ "unk_918" / Int64ul,
+ "unk_920" / Int32ul,
+ "unk_924" / Int32ul,
+ Ver("V >= V13_0B4", "unk_928_0" / Int32ul),
+ Ver("V >= V13_0B4", "unk_928_4" / Int8ul),
+ Ver("V >= V13_0B4", "ts_flag" / TsFlag),
+ Ver("V >= V13_0B4", "unk_5e6" / Default(Int16ul, 0)),
+ Ver("V >= V13_0B4", "unk_5e8" / Default(HexDump(Bytes(0x20)), bytes(0x20))),
+ "pad_928" / Default(HexDump(Bytes(0x18)), bytes(0x18)),
+ )
+
+class WorkCommand0_UnkBuf(ConstructValueClass):
+ subcon = HexDump(Bytes(0x18))
+
+ def __init__(self):
+ super().__init__()
+ self.value = bytes(0x18)
+
+class WorkCommandTA(ConstructClass):
+ """
+ For TA
+
+ Example:
+ 00000000 00000000 00000004 00000000 0c3d80c0 ffffffa0 00000002 00000000 0c3e0000
+ 00000020 ffffffa0 0c3e0100 ffffffa0 0c3e09c0 ffffffa0 00000000 00000200 00000000
+ 00000040 1e3ce508 1e3ce508 01cb0000 00000015 00000000 00000000 00970000 00000015
+ 00000060 01cb4000 80000015 006b0003 003a0012 00000001 00000000 00000000 00000000
+ 00000080 0000a000 00000000 00000088 00000000 01cb4000 00000015 00000000 00000000
+ 000000a0 0000ff00 00000000 007297a0 00000015 00728120 00000015 00000001 00000000
+ 000000c0 00728000 00040015 009f8000 00000015 00000000 00000000 00000000 00000000
+ 000000e0 0000a441 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+ 00000100 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000011
+ 00000120 00000000 00000000 0000001c 00000000 00008c60 00000000 00000000 00000000
+ 00000140 00000000 00000000 00000000 00000000 0000001c 00000000 00000000 00000000
+ 00000160 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+ 00000180 *
+ 000003a0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 000000f0
+ 000003c0 00000088 00000202 04af063f 00025031 00404030 00303024 000000c0 00000180
+ 000003e0 00000100 00008000 00000000 00000000 00000000 00000000 00000000 00000000
+ """
+
+ subcon = Struct(
+ "addr" / Tell,
+ "magic" / Const(0x0, Hex(Int32ul)),
+ Ver("V >= V13_0B4", "counter" / Int64ul),
+ "context_id" / Hex(Int32ul),
+ "unk_8" / Hex(Int32ul),
+ "event_control_addr" / Hex(Int64ul),
+ "event_control" / ROPointer(this.event_control_addr, EventControl),
+ "buffer_mgr_slot" / Hex(Int64ul),
+ "buffer_mgr_addr" / Int64ul,
+ "buffer_mgr" / ROPointer(this.buffer_mgr_addr, BufferManagerInfo),
+ "buf_thing_addr" / Int64ul,
+ "buf_thing" / ROPointer(this.buf_thing_addr, BufferThing),
+ "unk_emptybuf_addr" / Hex(Int64ul),
+ "unk_34" / Hex(Int32ul),
+
+ # Embedded structures that are also pointed to by other stuff
+ "struct_2" / StartTACmdStruct2, # 0x11c bytes
+ "unk_154" / HexDump(Bytes(0x268)), # unknown
+ "tiling_params" / TilingParameters, # 0x2c bytes
+ "unk_3e8" / HexDump(Bytes(0x74)), # unknown
+
+ "unkptr_45c" / Int64ul,
+ "tvb_size" / Int64ul,
+ "microsequence_ptr" / Hex(Int64ul),
+ "microsequence_size" / Hex(Int32ul),
+ "microsequence" / ROPointer(this.microsequence_ptr, MicroSequence),
+ "ev_3d" / Int32ul,
+ "stamp_value" / Int32ul,
+
+ "struct_3" / StartTACmdStruct3, # 0x114 bytes
+
+ "unk_594" / WorkCommand0_UnkBuf,
+
+ "ts1" / TimeStamp,
+ "ts2" / TimeStamp,
+ "ts3" / TimeStamp,
+
+ "unk_5c4" / Int32ul,
+ "unk_5c8" / Int32ul,
+ "unk_5cc" / Int32ul,
+ "unk_5d0" / Int32ul,
+ "unk_5d4" / Int8ul,
+ "pad_5d5" / Default(HexDump(Bytes(0x3)), bytes(0x3)),
+ Ver("V >= V13_0B4", "unk_5e0" / Int32ul),
+ Ver("V >= V13_0B4", "unk_5e4" / Int8ul),
+ Ver("V >= V13_0B4", "ts_flag" / TsFlag),
+ Ver("V >= V13_0B4", "unk_5e6" / Default(Int16ul, 0)),
+ Ver("V >= V13_0B4", "unk_5e8" / Default(HexDump(Bytes(0x18)), bytes(0x18))),
+ "pad_5d8" / Default(HexDump(Bytes(0x8)), bytes(0x8)),
+ Ver("V >= V13_0B4", "pad_5e0" / Default(HexDump(Bytes(0x18)), bytes(0x18))),
+ )
+
+class UnknownWorkCommand(ConstructClass):
+ subcon = Struct(
+ "magic" / Hex(Int32ul),
+ "unk_4" / Hex(Int32ul),
+ "unk_8" / Hex(Int32ul),
+ "unk_c" / Hex(Int32ul),
+ "unk_10" / Hex(Int32ul),
+ "unk_14" / Hex(Int32ul),
+ "unk_18" / Hex(Int32ul),
+ "unk_1c" / Hex(Int32ul),
+ )
+
+class CmdBufWork(ConstructClass):
+ subcon = Struct(
+ "cmdid" / Peek(Int32ul),
+ "cmd" / Switch(this.cmdid, {
+ 0: WorkCommandTA,
+ 1: WorkCommand3D,
+ 3: WorkCommandCP,
+ 4: WorkCommandBarrier,
+ 6: WorkCommandInitBM,
+ })
+ )
+
+class JobList(ConstructClass):
+ subcon = Struct(
+ "first_job" / Default(Int64ul, 0),
+ "last_head" / Int64ul,
+ "unkptr_10" / Default(Int64ul, 0),
+ )
+
+class GPUContextData(ConstructClass):
+ subcon = Struct(
+ "unk_0" / Int8ul,
+ "unk_1" / Int8ul,
+ "unk_2" / Default(Bytes(3), bytes(3)),
+ "unk_5" / Int8ul,
+ "unk_6" / Default(Bytes(0x18), bytes(0x18)),
+ "unk_1e" / Int8ul,
+ "unk_1f" / Int8ul,
+ "unk_20" / Default(Bytes(3), bytes(3)),
+ "unk_23" / Int8ul,
+ "unk_24" / Default(Bytes(0x1c), bytes(0x1c)),
+ )
+
+ def __init__(self):
+ self.unk_0 = 0xff
+ self.unk_1 = 0xff
+ self.unk_5 = 1
+ self.unk_1e = 0xff
+ self.unk_1f = 0
+ self.unk_23 = 2
+
+class CommandQueuePointerMap(RegMap):
+ GPU_DONEPTR = 0x00, Register32
+ GPU_RPTR = 0x30, Register32
+ CPU_WPTR = 0x40, Register32
+
+class CommandQueuePointers(ConstructClass):
+ subcon = Struct(
+ "gpu_doneptr" / Int32ul,
+ ZPadding(12),
+ "unk_10" / Int32ul,
+ ZPadding(12),
+ "unk_20" / Int32ul,
+ ZPadding(12),
+ "gpu_rptr" / Int32ul,
+ ZPadding(12),
+ "cpu_wptr" / Int32ul,
+ ZPadding(12),
+ "rb_size" / Int32ul,
+ ZPadding(12),
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.gpu_doneptr = 0
+ self.unk_10 = 0
+ self.unk_20 = 0
+ self.gpu_rptr = 0
+ self.cpu_wptr = 0
+ self.rb_size = 0x500
+
+class CommandQueueInfo(ConstructClass):
+ """ Structure type shared by Submit3D, SubmitTA and SubmitCompute
+ Applications have multiple of these, one of each submit type
+ TODO: Can applications have more than one of each type? One per encoder?
+ Mostly managed by GPU, only intialize by CPU
+
+ """
+ subcon = Struct(
+ "pointers_addr" / Hex(Int64ul),
+ "pointers" / ROPointer(this.pointers_addr, CommandQueuePointers),
+ "rb_addr" / Hex(Int64ul), # 0x4ff pointers
+ "job_list_addr" / Hex(Int64ul), # ffffffa000000000, size 0x18 (shared by 3D and TA)
+ "job_list" / ROPointer(this.job_list_addr, JobList),
+ "gpu_buf_addr" / Hex(Int64ul), # GPU space for this queue, 0x2c18 bytes?
+ #"gpu_buf" / ROPointer(this.gpu_buf_addr, HexDump(Bytes(0x2c18))),
+ "gpu_rptr1" / Hex(Int32ul),
+ "gpu_rptr2" / Hex(Int32ul),
+ "gpu_rptr3" / Hex(Int32ul),
+ "event_id" / Int32sl,
+ "unk_30" / Hex(Int32ul), # read by CPU
+ "unk_34" / Hex(Int32ul),
+ "unk_38" / Hex(Int64ul),
+ "unk_40" / Hex(Int32ul), # 1
+ "unk_44" / Hex(Int32ul), # 0
+ "unk_48" / Hex(Int32ul), # 1, 2
+ "unk_4c" / Int32sl, # -1
+ "uuid" / Hex(Int32ul), # Counts up for each new process or command queue
+ "unk_54" / Int32sl,
+ "unk_58" / Hex(Int64ul), # 0
+ "busy" / Hex(Int32ul), # 1 = gpu busy
+ "pad1" / ZPadding(0x20),
+ "blocked_on_barrier" / Hex(Int32ul),
+ "unk_88" / Int32ul,
+ "unk_8c" / Int32ul,
+ "unk_90" / Int32ul,
+ "unk_94" / Int32ul,
+ "pending" / Int32ul,
+ "unk_9c" / Int32ul,
+ "gpu_context_addr" / Hex(Int64ul), # GPU managed context, shared between 3D and TA. Passed to DC_DestroyContext
+ "gpu_context" / ROPointer(this.gpu_context_addr, GPUContextData),
+ "unk_a8" / Int64ul
+ # End of struct
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.gpu_rptr1 = 0
+ self.gpu_rptr2 = 0
+ self.gpu_rptr3 = 0
+ self.event_id = -1
+ self.unk_4c = -1
+ self.uuid = 0xdeadbeef # some kind of ID
+ self.unk_54 = -1
+ self.unk_58 = 0x0
+ self.busy = 0x0
+ self.blocked_on_barrier = 0x0
+ self.unk_88 = 0
+ self.unk_8c = 0
+ self.unk_90 = 0
+ self.unk_94 = 0
+ self.pending = 0
+ self.unk_9c = 0
+ self.set_prio(0)
+ self.unk_a8 = 0
+
+ def set_prio(self, p):
+ if p == 0:
+ self.unk_30 = 0
+ self.unk_34 = 0 # 0-3?
+ self.unk_38 = 0xffff_ffff_ffff_0000
+ self.unk_40 = 1
+ self.unk_44 = 0
+ self.unk_48 = 1
+ elif p == 1:
+ self.unk_30 = 1
+ self.unk_34 = 1
+ self.unk_38 = 0xffff_ffff_0000_0000
+ self.unk_40 = 0
+ self.unk_44 = 0
+ self.unk_48 = 0
+ elif p == 2:
+ self.unk_30 = 2
+ self.unk_34 = 2
+ self.unk_38 = 0xffff_0000_0000_0000
+ self.unk_40 = 0
+ self.unk_44 = 0
+ self.unk_48 = 2
+ else:
+ self.unk_30 = 3
+ self.unk_34 = 3
+ self.unk_38 = 0x0000_0000_0000_0000
+ self.unk_40 = 0
+ self.unk_44 = 0
+ self.unk_48 = 3
+
+__all__.extend(k for k, v in globals().items()
+ if (callable(v) or isinstance(v, type)) and v.__module__ == __name__)
diff --git a/tools/proxyclient/m1n1/fw/agx/handoff.py b/tools/proxyclient/m1n1/fw/agx/handoff.py
new file mode 100644
index 0000000..4f9acf0
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/agx/handoff.py
@@ -0,0 +1,120 @@
+# SPDX-License-Identifier: MIT
+from ...utils import *
+from contextlib import contextmanager
+
+PPL_MAGIC = 0x4b1d000000000002
+
+class GFXHandoffStruct(RegMap):
+ MAGIC_AP = 0x0, Register64
+ MAGIC_FW = 0x8, Register64
+
+ LOCK_AP = 0x10, Register8
+ LOCK_FW = 0x11, Register8
+ TURN = 0x14, Register32
+
+ CUR_CTX = 0x18, Register32
+
+ FLUSH_STATE = irange(0x20, 0x41, 0x18), Register64
+ FLUSH_ADDR = irange(0x28, 0x41, 0x18), Register64
+ FLUSH_SIZE = irange(0x30, 0x41, 0x18), Register64
+
+ UNK2 = 0x638, Register8
+ UNK3 = 0x640, Register64
+
+class GFXHandoff:
+ def __init__(self, u):
+ self.u = u
+ self.sgx_dev = self.u.adt["/arm-io/sgx"]
+ self.base = self.sgx_dev.gfx_handoff_base
+ self.reg = GFXHandoffStruct(u, self.base)
+ self.is_locked = False
+ self.initialized = False
+
+ @contextmanager
+ def lock(self):
+ """Dekker's algorithm lock"""
+ assert not self.is_locked
+
+ # Note: This *absolutely* needs barriers everywhere.
+ # Those are implicit in proxyclient for every operation.
+
+ self.reg.LOCK_AP.val = 1
+ while self.reg.LOCK_FW.val != 0:
+ if self.reg.TURN != 0:
+ self.reg.LOCK_AP = 0
+ while self.reg.TURN != 0:
+ pass
+ self.reg.LOCK_AP = 1
+
+ self.is_locked = True
+ try:
+ yield
+ finally:
+ self.reg.TURN.val = 1
+ self.reg.LOCK_AP.val = 0
+ self.is_locked = False
+
+ def initialize(self):
+ if self.initialized:
+ return
+
+ print("[Handoff] Initializing...")
+
+ self.reg.MAGIC_AP.val = PPL_MAGIC
+ self.reg.UNK = 0xffffffff
+ self.reg.UNK3 = 0
+
+ with self.lock():
+ print("[Handoff] Waiting for FW PPL init...")
+ while self.reg.MAGIC_FW.val != PPL_MAGIC:
+ pass
+
+ for i in range(0x41):
+ self.reg.FLUSH_STATE[i].val = 0
+ self.reg.FLUSH_ADDR[i].val = 0
+ self.reg.FLUSH_SIZE[i].val = 0
+
+ self.initialized = True
+ print("[Handoff] Initialized!")
+
+ # The order here is:
+ # - Remap memory as shared
+ # - TLBI
+ # - prepare_cacheflush()
+ # - issue FWCtl request
+ # - wait for completion (ring or wait_cacheflush?)
+ # - Unmap memory
+ # - TLBI
+ # - complete_cacheflush()
+ def prepare_cacheflush(self, base, size, context=0x40):
+ assert self.reg.FLUSH_STATE[context].val == 0
+
+ self.reg.FLUSH_ADDR[context].val = base
+ self.reg.FLUSH_SIZE[context].val = size
+ self.reg.FLUSH_STATE[context].val = 1
+
+ def wait_cacheflush(self, context=0x40):
+ while self.reg.FLUSH_STATE[context].val == 1:
+ pass
+
+ def complete_cacheflush(self, context=0x40):
+ assert self.reg.FLUSH_STATE[context].val == 2
+ self.reg.FLUSH_STATE[context].val = 0
+
+ # probably not necessary?
+ # order is:
+ # - Remap memory as shared
+ # - (no TLBI?)
+ # - prepare_unmap()
+ # - unmap
+ # - TLBI
+ # - complete_unmap()
+ def prepare_unmap(self, base, size, context):
+ assert self.reg.FLUSH_STATE[context].val == 0
+ self.reg.FLUSH_ADDR[context].val = 0xdead000000000000 | (base & 0xffffffffffff)
+ self.reg.FLUSH_SIZE[context].val = size
+ self.reg.FLUSH_STATE[context].val = 2
+
+ def complete_unmap(self, context):
+ assert self.reg.FLUSH_STATE[context].val == 2
+ self.reg.FLUSH_STATE[context].val = 0
diff --git a/tools/proxyclient/m1n1/fw/agx/initdata.py b/tools/proxyclient/m1n1/fw/agx/initdata.py
new file mode 100644
index 0000000..ea8d4df
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/agx/initdata.py
@@ -0,0 +1,1931 @@
+from m1n1.utils import *
+from m1n1.constructutils import *
+from construct import *
+from construct.lib import hexundump
+
+from .channels import ChannelInfoSet, ChannelInfo
+
+__all__ = []
+
+class InitData_FWStatus(ConstructClass):
+ subcon = Struct(
+ "fwctl_channel" / ChannelInfo,
+ "halt_count" / Int32ul,
+ ZPadding(0xc),
+ "halted" / Int32ul,
+ ZPadding(0xc),
+ "resume" / Int32ul,
+ ZPadding(0xc),
+ "unk_40" / Int32ul,
+ ZPadding(0xc),
+ "unk_ctr" / Int32ul,
+ ZPadding(0xc),
+ "unk_60" / Int32ul,
+ ZPadding(0xc),
+ "unk_70" / Int32ul,
+ ZPadding(0xc),
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.halt_count = 0
+ self.halted = 0
+ self.resume = 0
+ self.unk_40 = 0
+ self.unk_ctr = 0
+ self.unk_60 = 0
+ self.unk_70 = 0
+
+class AGXHWDataShared1(ConstructClass):
+ subcon = Struct(
+ "table" / Array(17, Int32sl),
+ "unk_44" / HexDump(Bytes(0x60)),
+ "unk_a4" / Int32ul,
+ "unk_a8" / Int32ul,
+ )
+
+ def __init__(self, chip_info):
+ super().__init__()
+ self.table = chip_info.shared1_tab
+ self.unk_44 = bytes(0x60)
+ self.unk_a4 = chip_info.shared1_a4
+ self.unk_a8 = 0
+
+class AGXHWDataShared2Curve(ConstructClass):
+ subcon = Struct(
+ "unk_0" / Int32ul,
+ "unk_4" / Int32ul,
+ "t1" / Array(16, Int16sl),
+ "t2" / Array(16, Int16sl),
+ "t3" / Array(8, Array(16, Int32sl)),
+ )
+
+ def __init__(self, unk_0=0, unk_4=0, t1=None, t2=None, t3=None):
+ self.unk_0 = unk_0
+ self.unk_4 = unk_4
+ if not t1:
+ self.t1 = [0] * 16
+ else:
+ self.t1 = t1 + [t1[0]] * (16 - len(t1))
+ if not t2:
+ self.t2 = [0] * 16
+ else:
+ self.t2 = t2 + [t2[0]] * (16 - len(t2))
+ if t3 is None:
+ self.t3 = [[0] * 16] * 8
+ else:
+ self.t3 = ([(i + [0x3ffffff] * (16 - len(i))) for i in t3] +
+ [[0x3ffffff] * 16] * (8 - len(t3)))
+
+
+class AGXHWDataShared2T8112(ConstructClass):
+ subcon = Struct(
+ "unk_0" / Array(5, Int32ul),
+ "unk_14" / Int32ul,
+ "unk_18" / Array(8, Int32ul),
+ "curve1" / AGXHWDataShared2Curve,
+ "curve2" / AGXHWDataShared2Curve,
+ )
+
+ def __init__(self, chip_info):
+ self.unk_0 = [0] * 5
+ self.unk_18 = [0] * 8
+
+ if chip_info.chip_id == 0x8112:
+ self.unk_14 = 0x6000000
+ self.curve1 = AGXHWDataShared2Curve(
+ 0, 0x20000000,
+ [-1], [0x0f07], [[]]
+ )
+ self.curve2 = AGXHWDataShared2Curve(
+ 7, 0x80000000,
+ [
+ -1, 25740, 17429, 12550, 9597, 7910, 6657, 5881, 5421
+ ], [
+ 0x0F07, 0x04C0, 0x06C0, 0x08C0,
+ 0x0AC0, 0x0C40, 0x0DC0, 0x0EC0,
+ 0x0F80
+ ], [
+ [0x03FFFFFF, 107, 101, 94, 87, 82, 77, 73, 71],
+ [0x03FFFFFF, 38240, 36251, 33562,
+ 31368, 29379, 27693, 26211, 25370],
+ [0x03FFFFFF, 123933, 117485, 108771,
+ 101661, 95217, 89751, 84948, 82222],
+ ]
+ )
+ else:
+ self.unk_14 = 0
+ self.curve1 = AGXHWDataShared2Curve()
+ self.curve2 = AGXHWDataShared2Curve()
+
+class AGXHWDataShared3(ConstructClass):
+ subcon = Struct(
+ "unk_0" / Int32ul,
+ "unk_4" / Int32ul,
+ "unk_8" / Int32ul,
+ "table" / Array(16, Int32ul),
+ "unk_4c" / Int32ul,
+ )
+
+ def __init__(self, chip_info):
+ if chip_info.chip_id == 0x8112:
+ self.unk_0 = 1
+ self.unk_4 = 500
+ self.unk_8 = 5
+ self.table = [
+ 10700, 10700, 10700, 10700,
+ 10700, 6000, 1000, 1000,
+ 1000, 10700, 10700, 10700,
+ 10700, 10700, 10700, 10700,
+ ]
+ self.unk_4c = 1
+ else:
+ self.unk_0 = 0
+ self.unk_4 = 0
+ self.unk_8 = 0
+ self.table = [0] * 16
+ self.unk_4c = 0
+
+class AGXHWDataShared2(ConstructClass):
+ subcon = Struct(
+ "table" / Array(10, Int32sl),
+ "unk_28" / HexDump(Bytes(0x10)),
+ "unk_38" / AGXHWDataShared2T8112,
+ "unk_500" / Int32ul,
+ "unk_504" / Int32ul,
+ "unk_508" / Int32ul,
+ "unk_50c" / Int32ul,
+ "unk_510" / Int32ul,
+ )
+
+ def __init__(self, chip_info):
+ super().__init__()
+ self.table = chip_info.shared2_tab
+ self.unk_20 = bytes(8)
+ self.unk_28 = b"\xff" * 16
+ self.unk_38 = AGXHWDataShared2T8112(chip_info)
+ self.unk_500 = 0
+ self.unk_504 = 0
+ self.unk_508 = chip_info.shared2_unk_508
+ self.unk_50c = 0
+ self.unk_510 = 0
+
+class AGXHWDataA130Extra(ConstructClass):
+ subcon = Struct(
+ "unk_0" / HexDump(Bytes(0x38)),
+ "unk_38" / Dec(Int32ul),
+ "unk_3c" / Dec(Int32ul),
+ "unk_40" / Dec(Int32ul),
+ "unk_44" / Int32ul,
+ "unk_48" / Int32ul,
+ "unk_4c" / Dec(Int32ul),
+ "unk_50" / Int32ul,
+ "unk_54" / Dec(Int32ul),
+ "unk_58" / Int32ul,
+ "unk_5c" / Int32ul,
+ "unk_60" / Float32l,
+ "unk_64" / Float32l,
+ "unk_68" / Float32l,
+ "unk_6c" / Float32l,
+ "unk_70" / Float32l,
+ "unk_74" / Float32l,
+ "unk_78" / Float32l,
+ "unk_7c" / Float32l,
+ "unk_80" / Float32l,
+ "unk_84" / Float32l,
+ "unk_88" / Int32ul,
+ "unk_8c" / Dec(Int32ul),
+ "unk_90" / Dec(Int32ul),
+ "unk_94" / Int32ul,
+ "unk_98" / Int32ul,
+ "unk_9c" / Float32l,
+ "unk_a0" / Dec(Int32ul),
+ "unk_a4" / Int32ul,
+ "unk_a8" / Dec(Int32ul),
+ "unk_ac" / Dec(Int32ul),
+ "unk_b0" / Dec(Int32ul),
+ "unk_b4" / Int32ul,
+ "unk_b8" / Dec(Int32ul),
+ "unk_bc" / Int32ul,
+ "unk_c0" / Int32ul,
+ "unk_c4" / Float32l,
+ "unk_c8" / HexDump(Bytes(0x4c)),
+ "unk_114" / Float32l,
+ "unk_118" / Int32ul,
+ "unk_11c" / Int32ul,
+ "unk_120" / Int32ul,
+ "unk_124" / Dec(Int32ul),
+ "unk_128" / Dec(Int32ul),
+ "unk_12c" / HexDump(Bytes(0x8c)),
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.unk_0 = bytes(0x38)
+ self.unk_38 = 4
+ self.unk_3c = 8000
+ self.unk_40 = 2500
+ self.unk_44 = 0x0
+ self.unk_48 = 0xffffffff
+ self.unk_4c = 50
+ self.unk_50 = 0x0
+ self.unk_54 = 50
+ self.unk_58 = 0x1
+ self.unk_5c = 0x0
+ self.unk_60 = 0.88888888
+ self.unk_64 = 0.66666666
+ self.unk_68 = 0.111111111
+ self.unk_6c = 0.33333333
+ self.unk_70 = -0.4
+ self.unk_74 = -0.8
+ self.unk_78 = 0.0
+ self.unk_7c = 65536.0
+ self.unk_80 = -5.0
+ self.unk_84 = -10.0
+ self.unk_88 = 0x0
+ self.unk_8c = 40
+ self.unk_90 = 600
+ self.unk_94 = 0x0
+ self.unk_98 = 0x0
+ self.unk_9c = 8000.0
+ self.unk_a0 = 1400
+ self.unk_a4 = 0x0
+ self.unk_a8 = 72
+ self.unk_ac = 24
+ self.unk_b0 = 1728000
+ self.unk_b4 = 0x0
+ self.unk_b8 = 576000
+ self.unk_bc = 0x0
+ self.unk_c0 = 0x0
+ self.unk_c4 = 65536.0
+ self.unk_c8 = bytes(0x4c)
+ self.unk_114 = 65536.0
+ self.unk_118 = 0x0
+ self.unk_11c = 0x0
+ self.unk_120 = 0x0
+ self.unk_124 = 40
+ self.unk_128 = 600
+ self.unk_12c = bytes(0x8c)
+
+class AGXHWDataT81xx(ConstructClass):
+ subcon = Struct(
+ "unk_d8c" / Int32ul,
+ "unk_d90" / Int32ul,
+ "unk_d94" / Int32ul,
+ "unk_d98" / Int32ul,
+ "unk_d9c" / Float32l,
+ "unk_da0" / Int32ul,
+ "unk_da4" / Float32l,
+ "unk_da8" / Int32ul,
+ "unk_dac" / Float32l,
+ "unk_db0" / Int32ul,
+ "unk_db4" / Int32ul,
+ "unk_db8" / Float32l,
+ "unk_dbc" / Float32l,
+ "unk_dc0" / Int32ul,
+ "unk_dc4" / Int32ul,
+ "unk_dc8" / Int32ul,
+ "unk_dcc" / Int32ul,
+ )
+ def __init__(self, sgx, chip_info):
+ if chip_info.chip_id in (0x8103, 0x8112):
+ self.unk_d8c = 0x80000000
+ self.unk_d90 = 4
+ self.unk_d94 = 0
+ self.unk_d98 = 0
+ self.unk_d9c = 0.6
+ self.unk_da0 = 0
+ self.unk_da4 = 0.4
+ self.unk_da8 = 0
+ self.unk_dac = 0.38552
+ self.unk_db0 = 0
+ self.unk_db4 = 0
+ self.unk_db8 = 65536.0
+ self.unk_dbc = 13.56
+ self.unk_dc0 = 0
+ self.unk_dc4 = 0
+ self.unk_dc8 = 0
+ self.unk_dcc = 100 * sgx.gpu_num_perf_states
+ else:
+ self.unk_d8c = 0
+ self.unk_d90 = 0
+ self.unk_d94 = 0
+ self.unk_d98 = 0
+ self.unk_d9c = 0
+ self.unk_da0 = 0
+ self.unk_da4 = 0
+ self.unk_da8 = 0
+ self.unk_dac = 0
+ self.unk_db0 = 0
+ self.unk_db4 = 0
+ self.unk_db8 = 0
+ self.unk_dbc = 0
+ self.unk_dc0 = 0
+ self.unk_dc4 = 0
+ self.unk_dc8 = 0
+ self.unk_dcc = 0
+
+class PowerZone(ConstructClass):
+ subcon = Struct(
+ "val" / Float32l,
+ "target" / Dec(Int32ul),
+ "target_off" / Dec(Int32ul),
+ "filter_tc_x4" / Dec(Int32ul),
+ "filter_tc_xperiod" / Dec(Int32ul),
+ Ver("V >= V13_0B4", "unk_10" / Dec(Int32ul)),
+ Ver("V >= V13_0B4", "unk_14" / Dec(Int32ul)),
+ "filter_tc_neginv" / Float32l,
+ "filter_tc_inv" / Float32l,
+ "pad" / Int32ul,
+ )
+ def __init__(self, tc=None, target=None, off=None, period_ms=None):
+ self.val = 0.0
+ self.pad = 0
+ if tc is None:
+ self.target = 0
+ self.target_off = 0
+ self.filter_tc_x4 = 0
+ self.filter_tc_xperiod = 0
+ self.unk_10 = 0
+ self.unk_14 = 0
+ self.filter_tc_neginv = 0
+ self.filter_tc_inv = 0
+ else:
+ self.target = target
+ self.target_off = self.target - off
+ self.filter_tc_x4 = tc * 4
+ self.filter_tc_xperiod = tc * period_ms
+ self.unk_10 = 1320000000
+ self.unk_14 = 0
+ self.filter_tc_neginv = 1 / tc
+ self.filter_tc_inv = 1 - 1 / tc
+
+class AGXHWDataA(ConstructClass):
+ subcon = Struct(
+ "unk_0" / Int32ul,
+ "clocks_per_period" / Int32ul,
+ Ver("V >= V13_0B4", "clocks_per_period_2" / Int32ul),
+ "unk_8" / Int32ul,
+ "pwr_status" / Int32ul,
+ "unk_10" / Float32l,
+ "unk_14" / Int32ul,
+ "unk_18" / Int32ul,
+ "unk_1c" / Int32ul,
+ "unk_20" / Int32ul,
+ "unk_24" / Int32ul,
+ "actual_pstate" / Int32ul,
+ "tgt_pstate" / Int32ul,
+ "unk_30" / Int32ul,
+ "cur_pstate" / Int32ul,
+ "unk_38" / Int32ul,
+ Ver("V >= V13_0B4", "unk_3c_0" / Int32ul),
+ "base_pstate_scaled" / Int32ul,
+ "unk_40" / Int32ul,
+ "max_pstate_scaled" / Int32ul,
+ "unk_48" / Int32ul,
+ "min_pstate_scaled" / Int32ul,
+ "freq_mhz" / Float32l,
+ "unk_54" / HexDump(Bytes(0x20)),
+ Ver("V >= V13_0B4", "unk_74_0" / Int32ul),
+ "unk_74" / Array(16, Float32l),
+ "unk_b4" / HexDump(Bytes(0x100)),
+ "unk_1b4" / Int32ul,
+ "temp_c" / Int32ul,
+ "avg_power_mw" / Dec(Int32ul),
+ "update_ts" / Int64ul,
+ "unk_1c8" / Int32ul,
+ "unk_1cc" / HexDump(Bytes(0x644 - 0x1cc)),
+ "pad_644" / HexDump(Bytes(8)),
+
+ "unk_64c" / Int32ul,
+ "unk_650" / Int32ul,
+ "pad_654" / Int32ul,
+ "pwr_filter_a_neg" / Float32l,
+ "pad_65c" / Int32ul,
+ "pwr_filter_a" / Float32l,
+ "pad_664" / Int32ul,
+ "pwr_integral_gain" / Float32l,
+ "pad_66c" / Int32ul,
+ "pwr_integral_min_clamp" / Float32l,
+ "max_power_1" / Float32l,
+ "pwr_proportional_gain" / Float32l,
+ "pad_67c" / Int32ul,
+ "pwr_pstate_related_k" / Float32l,
+ "pwr_pstate_max_dc_offset" / Int32sl,
+ "unk_688" / Int32ul,
+ "max_pstate_scaled_2" / Int32ul,
+ "pad_690" / Int32ul,
+ "unk_694" / Int32ul,
+ "max_power_2" / Int32ul,
+
+ "pad_69c" / HexDump(Bytes(0x18)),
+
+ "unk_6b4" / Int32ul,
+ Ver("V >= V13_0B4", "unk_6b8_0" / HexDump(Bytes(0x10))),
+ "max_pstate_scaled_3" / Int32ul,
+ "unk_6bc" / Int32ul,
+
+ "pad_6c0" / HexDump(Bytes(0x14)),
+
+ "ppm_filter_tc_periods_x4" / Int32ul,
+ "unk_6d8" / Int32ul,
+
+ "pad_6dc" / Int32ul,
+
+ "ppm_filter_a_neg" / Float32l,
+ "pad_6e4" / Int32ul,
+ "ppm_filter_a" / Float32l,
+ "pad_6ec" / Int32ul,
+ "ppm_ki_dt" / Float32l,
+ "pad_6f4" / Int32ul,
+ "pwr_integral_min_clamp_2" / Int32ul,
+ "unk_6fc" / Float32l,
+ "ppm_kp" / Float32l,
+ "pad_704" / Int32ul,
+
+ "unk_708" / Int32ul,
+ "pwr_min_duty_cycle" / Int32ul,
+ "max_pstate_scaled_4" / Int32ul,
+ "unk_714" / Int32ul,
+
+ "pad_718" / Int32ul,
+
+ "unk_71c" / Float32l,
+ "max_power_3" / Int32ul,
+
+ "cur_power_mw_2" / Int32ul,
+
+ "ppm_filter_tc_ms" / Int32ul,
+ "unk_72c" / Int32ul,
+ Ver("V >= V13_0B4", "unk_730_0" / Int32ul),
+ Ver("V >= V13_0B4", "unk_730_4" / Int32ul),
+ Ver("V >= V13_0B4", "unk_730_8" / Int32ul),
+ Ver("V >= V13_0B4", "unk_730_c" / Int32ul),
+ "unk_730" / Float32l,
+ "unk_734" / Int32ul,
+
+ "unk_738" / Int32ul,
+ "unk_73c" / Int32ul,
+ "unk_740" / Int32ul,
+ "unk_744" / Int32ul,
+ "unk_748" / Array(4, Float32l),
+ "unk_758" / Int32ul,
+ "perf_tgt_utilization" / Int32ul,
+ "pad_760" / Int32ul,
+ "perf_boost_min_util" / Int32ul,
+ "perf_boost_ce_step" / Int32ul,
+ "perf_reset_iters" / Int32ul,
+ "pad_770" / Int32ul,
+ "unk_774" / Int32ul,
+ "unk_778" / Int32ul,
+ "perf_filter_drop_threshold" / Int32ul,
+
+ "perf_filter_a_neg" / Float32l,
+ "perf_filter_a2_neg" / Float32l,
+ "perf_filter_a" / Float32l,
+ "perf_filter_a2" / Float32l,
+ "perf_ki" / Float32l,
+ "perf_ki2" / Float32l,
+ "perf_integral_min_clamp" / Float32l,
+ "unk_79c" / Float32l,
+ "perf_kp" / Float32l,
+ "perf_kp2" / Float32l,
+ "boost_state_unk_k" / Float32l,
+
+ "base_pstate_scaled_2" / Dec(Int32ul),
+ "max_pstate_scaled_5" / Dec(Int32ul),
+ "base_pstate_scaled_3" / Dec(Int32ul),
+
+ "pad_7b8" / Int32ul,
+
+ "perf_cur_utilization" / Float32l,
+ "perf_tgt_utilization_2" / Int32ul,
+
+ "pad_7c4" / HexDump(Bytes(0x18)),
+
+ "unk_7dc" / Int32ul,
+ Ver("V >= V13_0B4", "unk_7e0_0" / HexDump(Bytes(0x10))),
+ "base_pstate_scaled_4" / Dec(Int32ul),
+ "pad_7e4" / Int32ul,
+
+ "unk_7e8" / HexDump(Bytes(0x14)),
+
+ "unk_7fc" / Float32l,
+ "pwr_min_duty_cycle_2" / Float32l,
+ "max_pstate_scaled_6" / Float32l,
+ "max_freq_mhz" / Int32ul,
+ "pad_80c" / Int32ul,
+ "unk_810" / Int32ul,
+ "pad_814" / Int32ul,
+ "pwr_min_duty_cycle_3" / Int32ul,
+ "unk_81c" / Int32ul,
+ "pad_820" / Int32ul,
+ "min_pstate_scaled_4" / Float32l,
+ "max_pstate_scaled_7" / Dec(Int32ul),
+ "unk_82c" / Int32ul,
+ "unk_alpha_neg" / Float32l,
+ "unk_alpha" / Float32l,
+ "unk_838" / Int32ul,
+ "unk_83c" / Int32ul,
+ "pad_840" / HexDump(Bytes(0x86c - 0x838 - 8)),
+
+ "unk_86c" / Int32ul,
+ "fast_die0_sensor_mask64" / Int64ul,
+ "fast_die0_release_temp_cc" / Int32ul,
+ "unk_87c" / Int32sl,
+ "unk_880" / Int32ul,
+ "unk_884" / Int32ul,
+ "pad_888" / Int32ul,
+ "unk_88c" / Int32ul,
+ "pad_890" / Int32ul,
+ "unk_894" / Float32l,
+ "pad_898" / Int32ul,
+ "fast_die0_ki_dt" / Float32l,
+ "pad_8a0" / Int32ul,
+ "unk_8a4" / Int32ul,
+ "unk_8a8" / Float32l,
+ "fast_die0_kp" / Float32l,
+ "pad_8b0" / Int32ul,
+ "unk_8b4" / Int32ul,
+ "pwr_min_duty_cycle_4" / Int32ul,
+ "max_pstate_scaled_8" / Dec(Int32ul),
+ "max_pstate_scaled_9" / Dec(Int32ul),
+ "fast_die0_prop_tgt_delta" / Int32ul,
+ "unk_8c8" / Int32ul,
+ "unk_8cc" / Int32ul,
+ "pad_8d0" / HexDump(Bytes(0x14)),
+ Ver("V >= V13_0B4", "unk_8e4_0" / HexDump(Bytes(0x10))),
+ "unk_8e4" / Int32ul,
+ "unk_8e8" / Int32ul,
+ "max_pstate_scaled_10" / Dec(Int32ul),
+ "unk_8f0" / Int32ul,
+ "unk_8f4" / Int32ul,
+ "pad_8f8" / Int32ul,
+ "pad_8fc" / Int32ul,
+ "unk_900" / HexDump(Bytes(0x24)),
+ "unk_924" / Array(8, Array(8, Float32l)),
+ "unk_a24" / Array(8, Array(8, Float32l)),
+ "unk_b24" / HexDump(Bytes(0x70)),
+ "max_pstate_scaled_11" / Dec(Int32ul),
+ "freq_with_off" / Int32ul,
+ "unk_b9c" / Int32ul,
+ "unk_ba0" / Int64ul,
+ "unk_ba8" / Int64ul,
+ "unk_bb0" / Int32ul,
+ "unk_bb4" / Int32ul,
+ "pad_bb8" / HexDump(Bytes(0xc2c - 0xbb8)),
+
+ "unk_c2c" / Int32ul,
+ "power_zone_count" / Int32ul,
+ "max_power_4" / Int32ul,
+ "max_power_5" / Int32ul,
+ "max_power_6" / Int32ul,
+ "unk_c40" / Int32ul,
+ "unk_c44" / Float32l,
+ "avg_power_target_filter_a_neg" / Float32l,
+ "avg_power_target_filter_a" / Float32l,
+ "avg_power_target_filter_tc_x4" / Dec(Int32ul),
+ "avg_power_target_filter_tc_xperiod" / Dec(Int32ul),
+ Ver("V >= V13_0B4", "base_clock_mhz" / Int32ul),
+ Ver("V >= V13_0B4", "unk_c58_4" / Int32ul),
+ "power_zones" / Array(5, PowerZone),
+ "avg_power_filter_tc_periods_x4" / Dec(Int32ul),
+ "unk_cfc" / Int32ul,
+ "unk_d00" / Int32ul,
+ "avg_power_filter_a_neg" / Float32l,
+ "unk_d08" / Int32ul,
+ "avg_power_filter_a" / Float32l,
+ "unk_d10" / Int32ul,
+ "avg_power_ki_dt" / Float32l,
+ "unk_d18" / Int32ul,
+ "unk_d1c" / Int32ul,
+ "unk_d20" / Float32l,
+ "avg_power_kp" / Float32l,
+ "unk_d28" / Int32ul,
+ "unk_d2c" / Int32ul,
+ "avg_power_min_duty_cycle" / Int32ul,
+ "max_pstate_scaled_12" / Int32ul,
+ "max_pstate_scaled_13" / Int32ul,
+ "unk_d3c" / Int32ul,
+ "max_power_7" / Float32l,
+ "max_power_8" / Int32ul,
+ "unk_d48" / Int32ul,
+ "unk_d4c" / Int32ul,
+ "unk_d50" / Int32ul,
+ Ver("V >= V13_0B4", "base_clock_mhz_2" / Int32ul),
+ Ver("V >= V13_0B4", "unk_d54_4" / HexDump(Bytes(0xc))),
+ "unk_d54" / HexDump(Bytes(0x10)),
+ "max_pstate_scaled_14" / Int32ul,
+ "unk_d68" / Bytes(0x24),
+
+ "t81xx_data" / AGXHWDataT81xx,
+
+ "unk_dd0" / HexDump(Bytes(0x40)),
+ Ver("V >= V13_0B4", "unk_e10_0" / AGXHWDataA130Extra),
+ "unk_e10" / HexDump(Bytes(0xc)),
+ "fast_die0_sensor_mask64_2" / Int64ul,
+ "unk_e24" / Int32ul,
+ "unk_e28" / Int32ul,
+ "unk_e2c" / HexDump(Bytes(0x1c)),
+ "unk_e48" / Array(8, Array(8, Float32l)),
+ "unk_f48" / Array(8, Array(8, Float32l)),
+ "pad_1048" / HexDump(Bytes(0x5e4)),
+ "fast_die0_sensor_mask64_alt" / Int64ul,
+ "fast_die0_sensor_present" / Int32ul,
+ Ver("V < V13_0B4", "unk_1638" / Array(2, Int32ul)),
+ "unk_1640" / HexDump(Bytes(0x2000)),
+ "unk_3640" / Int32ul,
+ "hws1" / AGXHWDataShared1,
+ Ver("V >= V13_0B4", "unk_pad1" / HexDump(Bytes(0x20))),
+ "hws2" / AGXHWDataShared2,
+ "unk_3c04" / Int32ul,
+ "hws3" / AGXHWDataShared3,
+ "unk_3c58" / HexDump(Bytes(0x3c)),
+ "unk_3c94" / Int32ul,
+ "unk_3c98" / Int64ul,
+ "unk_3ca0" / Int64ul,
+ "unk_3ca8" / Int64ul,
+ "unk_3cb0" / Int64ul,
+ "ts_last_idle" / Int64ul,
+ "ts_last_poweron" / Int64ul,
+ "ts_last_poweroff" / Int64ul,
+ "unk_3cd0" / Int64ul,
+ "unk_3cd8" / Int64ul,
+ Ver("V >= V13_0B4", "unk_3ce0_0" / Int32ul),
+ "unk_3ce0" / Int32ul,
+ "unk_3ce4" / Int32ul,
+ "unk_3ce8" / Int32ul,
+ "unk_3cec" / Int32ul,
+ "unk_3cf0" / Int32ul,
+ "unk_3cf4" / Array(8, Float32l),
+ "unk_3d14" / Array(8, Float32l),
+ "unk_3d34" / HexDump(Bytes(0x38)),
+ Ver("V >= V13_0B4", "unk_3d6c" / HexDump(Bytes(0x38))),
+ )
+
+ def __init__(self, sgx, chip_info):
+ super().__init__()
+
+ base_clock_khz = 24000
+ base_clock_mhz = base_clock_khz * 1000
+ period_ms = sgx.gpu_power_sample_period
+
+ self.unk_0 = 0
+ self.clocks_per_period = base_clock_khz * period_ms
+ self.clocks_per_period_2 = base_clock_khz * period_ms
+ self.unk_8 = 0
+ self.pwr_status = 4
+ self.unk_10 = 1.0
+ self.unk_14 = 0
+ self.unk_18 = 0
+ self.unk_1c = 0
+ self.unk_20 = 0
+ self.unk_24 = 0
+ self.actual_pstate = 1
+ self.tgt_pstate = 1
+ self.unk_30 = 0
+ self.cur_pstate = 0
+ self.unk_38 = 0
+ self.unk_3c_0 = 0
+ self.base_pstate_scaled = 100 * sgx.getprop("gpu-perf-base-pstate", 3)
+ self.unk_40 = 1
+ self.max_pstate_scaled = 100 * sgx.gpu_num_perf_states
+ self.unk_48 = 0
+ self.min_pstate_scaled = 100
+ self.freq_mhz = 0.0
+ self.unk_54 = bytes(0x20)
+ self.unk_74_0 = 0
+ # perf related
+ self.unk_74 = [0] * 16
+
+ self.unk_b4 = bytes(0x100)
+ self.unk_1b4 = 0
+ self.temp_c = 0
+ self.avg_power_mw = 0
+ self.update_ts = 0
+ self.unk_1c8 = 0
+ self.unk_1cc = bytes(0x644 - 0x1cc)
+
+ self.pad_644 = bytes(8)
+
+ self.unk_64c = 625
+ self.unk_650 = 0
+ self.pad_654 = 0
+ self.pwr_filter_a_neg = 1 - 1 / sgx.getprop("gpu-pwr-filter-time-constant", 313)
+ self.pad_65c = 0
+ self.pwr_filter_a = 1 - self.pwr_filter_a_neg
+ self.pad_664 = 0
+ self.pwr_integral_gain = sgx.getprop("gpu-pwr-integral-gain", 0.0202129)
+ self.pad_66c = 0
+ self.pwr_integral_min_clamp = sgx.getprop("gpu-pwr-integral-min-clamp", 0)
+ self.max_power_1 = chip_info.max_power
+ self.pwr_proportional_gain = sgx.getprop("gpu-pwr-proportional-gain", 5.2831855)
+ self.pad_67c = 0
+ self.pwr_pstate_related_k = -self.max_pstate_scaled / chip_info.max_power
+ self.pwr_pstate_max_dc_offset = sgx.gpu_pwr_min_duty_cycle - self.max_pstate_scaled
+ self.unk_688 = 0
+ self.max_pstate_scaled_2 = self.max_pstate_scaled
+ self.pad_690 = 0
+ self.unk_694 = 0
+ self.max_power_2 = chip_info.max_power
+ self.pad_69c = bytes(0x18)
+ self.unk_6b4 = 0
+ self.unk_6b8_0 = bytes(0x10)
+ self.max_pstate_scaled_3 = self.max_pstate_scaled
+ self.unk_6bc = 0
+ self.pad_6c0 = bytes(0x14)
+
+ # Note: integer rounding here
+ ppm_filter_tc_periods = sgx.gpu_ppm_filter_time_constant_ms // period_ms
+ self.ppm_filter_tc_periods_x4 = ppm_filter_tc_periods * 4
+ self.unk_6d8 = 0
+ self.pad_6dc = 0
+ self.ppm_filter_a_neg = 1 - 1 / ppm_filter_tc_periods
+ self.pad_6e4 = 0
+ self.ppm_filter_a = 1 - self.ppm_filter_a_neg
+ self.pad_6ec = 0
+ self.ppm_ki_dt = sgx.gpu_ppm_ki * (period_ms / 1000)
+ self.pad_6f4 = 0
+ self.pwr_integral_min_clamp_2 = self.pwr_integral_min_clamp
+ if Ver.check("V >= V13_0B4") or chip_info.chip_id != 0x8103:
+ self.unk_6fc = 65536.0
+ else:
+ self.unk_6fc = 0
+ self.ppm_kp = sgx.gpu_ppm_kp
+ self.pad_704 = 0
+ self.unk_708 = 0
+ self.pwr_min_duty_cycle = sgx.gpu_pwr_min_duty_cycle
+ self.max_pstate_scaled_4 = self.max_pstate_scaled
+ self.unk_714 = 0
+ self.pad_718 = 0
+ self.unk_71c = 0.0
+ self.max_power_3 = chip_info.max_power
+ self.cur_power_mw_2 = 0x0
+ self.ppm_filter_tc_ms = sgx.gpu_ppm_filter_time_constant_ms
+ self.unk_72c = 0
+ self.unk_730_0 = 0x232800
+ self.unk_730_4 = 0
+ self.unk_730_8 = 0
+ self.unk_730_c = 0
+ self.unk_730 = 0.0
+ self.unk_734 = 0
+ self.unk_738 = 0
+ self.unk_73c = 0
+ self.unk_740 = 0
+ self.unk_744 = 0
+ self.unk_748 = [0.0, 0.0, 0.0, 0.0]
+ self.unk_758 = 0
+ self.perf_tgt_utilization = sgx.gpu_perf_tgt_utilization
+ self.pad_760 = 0
+ self.perf_boost_min_util = sgx.getprop("gpu-perf-boost-min-util", 100)
+
+ self.perf_boost_ce_step = sgx.getprop("gpu-perf-boost-ce-step", 25)
+ self.perf_reset_iters = sgx.getprop("gpu-perf-reset-iters", 6)
+ self.pad_770 = 0x0
+ self.unk_774 = 6
+ self.unk_778 = 1
+ self.perf_filter_drop_threshold = sgx.gpu_perf_filter_drop_threshold
+ self.perf_filter_a_neg = 1 - 1 / sgx.gpu_perf_filter_time_constant
+ self.perf_filter_a2_neg = 1 - 1 / sgx.gpu_perf_filter_time_constant2
+ self.perf_filter_a = 1 - self.perf_filter_a_neg
+ self.perf_filter_a2 = 1 - self.perf_filter_a2_neg
+ self.perf_ki = sgx.getprop("gpu-perf-integral-gain", 7.895683288574219)
+ self.perf_ki2 = sgx.gpu_perf_integral_gain2
+ self.perf_integral_min_clamp = sgx.gpu_perf_integral_min_clamp
+ self.unk_79c = 95.0
+ self.perf_kp = sgx.getprop("gpu-perf-proportional-gain", 14.707962989807129)
+ self.perf_kp2 = sgx.gpu_perf_proportional_gain2
+ base_state = sgx.getprop("gpu-perf-base-pstate", 3)
+ max_state = sgx.gpu_num_perf_states
+ boost_states = max_state - base_state
+ self.boost_state_unk_k = boost_states / 0.95
+ self.base_pstate_scaled_2 = 100 * sgx.getprop("gpu-perf-base-pstate", 3)
+ self.max_pstate_scaled_5 = self.max_pstate_scaled
+ self.base_pstate_scaled_3 = 100 * sgx.getprop("gpu-perf-base-pstate", 3)
+ self.pad_7b8 = 0x0
+ self.perf_cur_utilization = 0.0
+ self.perf_tgt_utilization_2 = sgx.gpu_perf_tgt_utilization
+ self.pad_7c4 = bytes(0x18)
+ self.unk_7dc = 0x0
+ self.unk_7e0_0 = bytes(0x10)
+ self.base_pstate_scaled_4 = 100 * sgx.getprop("gpu-perf-base-pstate", 3)
+ self.pad_7e4 = 0x0
+ self.unk_7e8 = bytes(0x14)
+ self.unk_7fc = 65536.0
+ self.pwr_min_duty_cycle_2 = sgx.gpu_pwr_min_duty_cycle
+ self.max_pstate_scaled_6 = self.max_pstate_scaled
+ self.max_freq_mhz = sgx.perf_states[sgx.gpu_num_perf_states].freq // 1000000
+ self.pad_80c = 0x0
+ self.unk_810 = 0x0
+ self.pad_814 = 0x0
+ self.pwr_min_duty_cycle_3 = sgx.gpu_pwr_min_duty_cycle
+ self.unk_81c = 0x0
+ self.pad_820 = 0x0
+ self.min_pstate_scaled_4 = 100.0
+ self.max_pstate_scaled_7 = self.max_pstate_scaled
+ self.unk_82c = 0x0
+ self.unk_alpha_neg = 0.8
+ self.unk_alpha = 1 - self.unk_alpha_neg
+ self.unk_838 = 0x0
+ self.unk_83c = 0x0
+ self.pad_840 = bytes(0x2c)
+ self.unk_86c = 0x0
+ self.fast_die0_sensor_mask64 = chip_info.gpu_fast_die0_sensor_mask64
+ self.fast_die0_release_temp_cc = 100 * sgx.getprop("gpu-fast-die0-release-temp", 80)
+ self.unk_87c = chip_info.unk_87c
+ self.unk_880 = 0x4
+ self.unk_884 = 0x0
+ self.pad_888 = 0x0
+ self.unk_88c = 0x0
+ self.pad_890 = 0x0
+ self.unk_894 = 1.0
+ self.pad_898 = 0x0
+ self.fast_die0_ki_dt = sgx.gpu_fast_die0_integral_gain * (period_ms / 1000)
+ self.pad_8a0 = 0x0
+ self.unk_8a4 = 0x0
+ self.unk_8a8 = 65536.0
+ self.fast_die0_kp = sgx.gpu_fast_die0_proportional_gain
+ self.pad_8b0 = 0x0
+ self.unk_8b4 = 0x0
+ self.pwr_min_duty_cycle_4 = sgx.gpu_pwr_min_duty_cycle
+ self.max_pstate_scaled_8 = self.max_pstate_scaled
+ self.max_pstate_scaled_9 = self.max_pstate_scaled
+ self.fast_die0_prop_tgt_delta = 100 * sgx.getprop("gpu-fast-die0-prop-tgt-delta", 0)
+ self.unk_8c8 = 0
+ self.unk_8cc = chip_info.unk_8cc
+ self.pad_8d0 = bytes(0x14)
+ self.unk_8e4_0 = bytes(0x10)
+ self.unk_8e4 = 0
+ self.unk_8e8 = 0
+ self.max_pstate_scaled_10 = self.max_pstate_scaled
+ self.unk_8f0 = 0
+ self.unk_8f4 = 0
+ self.pad_8f8 = 0
+ self.pad_8fc = 0
+ self.unk_900 = bytes(0x24)
+ self.unk_924 = chip_info.unk_924
+ self.unk_a24 = chip_info.unk_924
+ self.unk_b24 = bytes(0x70)
+ self.max_pstate_scaled_11 = self.max_pstate_scaled
+ self.freq_with_off = 0x0
+ self.unk_b9c = 0
+ self.unk_ba0 = 0
+ self.unk_ba8 = 0
+ self.unk_bb0 = 0
+ self.unk_bb4 = 0
+ self.pad_bb8 = bytes(0x74)
+ self.unk_c2c = 1
+
+ self.power_zones = [PowerZone()] * 5
+ power_zone_count = 0
+ for i in range(5):
+ if sgx.getprop(f"gpu-power-zone-target-{i}", None) is None:
+ break
+ self.power_zones[i] = PowerZone(
+ sgx.getprop(f"gpu-power-zone-filter-tc-{i}", None),
+ sgx.getprop(f"gpu-power-zone-target-{i}", None),
+ sgx.getprop(f"gpu-power-zone-target-offset-{i}", None),
+ period_ms
+ )
+ power_zone_count += 1
+
+ self.power_zone_count = power_zone_count
+ self.max_power_4 = chip_info.max_power
+ self.max_power_5 = chip_info.max_power
+ self.max_power_6 = chip_info.max_power
+ self.unk_c40 = 0
+ self.unk_c44 = 0.0
+ self.avg_power_target_filter_a_neg = 1 - 1 / sgx.gpu_avg_power_target_filter_tc
+ self.avg_power_target_filter_a = 1 / sgx.gpu_avg_power_target_filter_tc
+ self.avg_power_target_filter_tc_x4 = 4 * sgx.gpu_avg_power_target_filter_tc
+ self.avg_power_target_filter_tc_xperiod = period_ms * sgx.gpu_avg_power_target_filter_tc
+ self.base_clock_mhz = base_clock_mhz
+ self.unk_c58_4 = 0
+
+ # Note: integer rounding
+ avg_power_filter_tc_periods = sgx.gpu_avg_power_filter_tc_ms // period_ms
+ self.avg_power_filter_tc_periods_x4 = avg_power_filter_tc_periods * 4
+ self.unk_cfc = 0
+ self.unk_d00 = 0
+ self.avg_power_filter_a_neg = 1 - 1 / avg_power_filter_tc_periods
+ self.unk_d08 = 0
+ self.avg_power_filter_a = 1 - self.avg_power_filter_a_neg
+ self.unk_d10 = 0
+ self.avg_power_ki_dt = sgx.gpu_avg_power_ki_only * (period_ms / 1000)
+ self.unk_d18 = 0
+ self.unk_d1c = 0
+ self.unk_d20 = 65536.0
+ self.avg_power_kp = sgx.gpu_avg_power_kp
+ self.unk_d28 = 0
+ self.unk_d2c = 0
+ self.avg_power_min_duty_cycle = sgx.gpu_avg_power_min_duty_cycle
+ self.max_pstate_scaled_12 = self.max_pstate_scaled
+ self.max_pstate_scaled_13 = self.max_pstate_scaled
+ self.unk_d3c = 0
+ self.max_power_7 = chip_info.max_power
+ self.max_power_8 = chip_info.max_power
+ self.unk_d48 = 0
+ self.unk_d4c = sgx.gpu_avg_power_filter_tc_ms
+ self.unk_d50 = 0
+ self.base_clock_mhz_2 = base_clock_mhz
+ self.unk_d54_4 = bytes(0xc)
+ self.unk_d54 = bytes(0x10)
+ self.max_pstate_scaled_14 = self.max_pstate_scaled
+ self.unk_d68 = bytes(0x24)
+
+ self.t81xx_data = AGXHWDataT81xx(sgx, chip_info)
+
+ self.unk_dd0 = bytes(0x40)
+
+ self.unk_e10_0 = AGXHWDataA130Extra()
+ self.unk_e10 = bytes(0xc)
+ self.fast_die0_sensor_mask64_2 = chip_info.gpu_fast_die0_sensor_mask64
+ self.unk_e24 = chip_info.unk_e24
+ self.unk_e28 = 1
+ self.unk_e2c = bytes(0x1c)
+ self.unk_e48 = chip_info.unk_e48
+ self.unk_f48 = chip_info.unk_e48
+ self.pad_1048 = bytes(0x5e4)
+ self.fast_die0_sensor_mask64_alt = chip_info.gpu_fast_die0_sensor_mask64_alt
+ self.fast_die0_sensor_present = chip_info.gpu_fast_die0_sensor_present
+ self.unk_1638 = [0, 1]
+ self.unk_1640 = bytes(0x2000)
+ self.unk_3640 = 0
+ self.hws1 = AGXHWDataShared1(chip_info)
+ self.unk_pad1 = bytes(0x20)
+ self.hws2 = AGXHWDataShared2(chip_info)
+ self.unk_3c04 = 0
+ self.hws3 = AGXHWDataShared3(chip_info)
+ self.unk_3c58 = bytes(0x3c)
+ self.unk_3c94 = 0 # flag
+ self.unk_3c98 = 0 # timestamp?
+ self.unk_3ca0 = 0 # timestamp?
+ self.unk_3ca8 = 0
+ self.unk_3cb0 = 0
+ self.ts_last_idle = 0
+ self.ts_last_poweron = 0
+ self.ts_last_poweroff = 0
+ self.unk_3cd0 = 0
+ self.unk_3cd8 = 0
+ self.unk_3ce0_0 = 0
+
+ self.unk_3ce0 = 0
+ self.unk_3ce4 = 0
+ self.unk_3ce8 = 1
+ self.unk_3cec = 0
+ self.unk_3cf0 = 0
+ self.unk_3cf4 = chip_info.unk_3cf4
+ self.unk_3d14 = chip_info.unk_3d14
+ self.unk_3d34 = bytes(0x38)
+ self.unk_3d6c = bytes(0x38)
+
+class IOMapping(ConstructClass):
+ _MAPTYPE = {
+ 0: "RO",
+ 1: "RW",
+ }
+
+ subcon = Struct(
+ "phys_addr" / Int64ul,
+ "virt_addr" / Int64ul,
+ "size" / Int32ul,
+ "range_size" / Int32ul, # Useally the same as size, but for MCC, this is the size of a single MMC register range.
+ "readwrite" / Int64ul
+ )
+
+ def __init__(self, phys=0, addr=0, size=0, range_size=0, readwrite=0):
+ self.phys_addr = phys
+ self.virt_addr = addr
+ self.size = size
+ self.range_size = range_size
+ self.readwrite = readwrite
+
+ def __str__(self):
+ if self.virt_addr == 0:
+ return "\n<IOMapping: Invalid>"
+
+ try:
+ hv = self._stream.uat.hv
+ except AttributeError:
+ hv = None
+
+ if hv:
+ dev, range = hv.device_addr_tbl.lookup(self.phys_addr)
+ offset = self.phys_addr - range.start
+ return f"\nIO Mapping: {self._MAPTYPE.get(self.readwrite, self.readwrite)} {self.virt_addr:#x} -> " \
+ f"{dev}+{offset:#x} = {self.phys_addr:#x} ({self.size:#x} / {self.range_size:#x})"
+ else:
+ return f"\nIO Mapping: {self._MAPTYPE.get(self.readwrite, self.readwrite)} {self.virt_addr:#x} -> " \
+ f"{self.phys_addr:#x} = {self.phys_addr:#x} ({self.size:#x} / {self.range_size:#x})"
+
+
+class AGXHWDataB(ConstructClass):
+ subcon = Struct(
+ Ver("V < V13_0B4", "unk_0" / Int64ul),
+ "unk_8" / Int64ul,
+ Ver("V < V13_0B4", "unk_10" / Int64ul),
+ "unk_18" / Int64ul,
+ "unk_20" / Int64ul,
+ "unk_28" / Int64ul,
+ "unk_30" / Int64ul,
+ "unkptr_38" / Int64ul,
+ "pad_40" / HexDump(Bytes(0x20)),
+ Ver("V < V13_0B4", "yuv_matrices" / Array(15, Array(3, Array(4, Int16sl)))),
+ Ver("V >= V13_0B4", "yuv_matrices" / Array(63, Array(3, Array(4, Int16sl)))),
+ "pad_1c8" / HexDump(Bytes(8)),
+ "io_mappings" / Array(0x14, IOMapping),
+ Ver("V >= V13_0B4", "unk_450_0" / HexDump(Bytes(0x68))),
+ "chip_id" / Int32ul,
+ "unk_454" / Int32ul,
+ "unk_458" / Int32ul,
+ "unk_45c" / Int32ul,
+ "unk_460" / Int32ul,
+ "unk_464" / Int32ul,
+ "unk_468" / Int32ul,
+ "unk_46c" / Int32ul,
+ "unk_470" / Int32ul,
+ "unk_474" / Int32ul,
+ "unk_478" / Int32ul,
+ "unk_47c" / Int32ul,
+ "unk_480" / Int32ul,
+ "unk_484" / Int32ul,
+ "unk_488" / Int32ul,
+ "unk_48c" / Int32ul,
+ "base_clock_khz" / Int32ul,
+ "power_sample_period" / Int32ul,
+ "pad_498" / ZPadding(4),
+
+ "unk_49c" / Int32ul,
+ "unk_4a0" / Int32ul,
+ "unk_4a4" / Int32ul,
+ "pad_4a8" / ZPadding(4),
+
+ "unk_4ac" / Int32ul,
+ "pad_4b0" / ZPadding(8),
+
+ "unk_4b8" / Int32ul,
+ "unk_4bc" / ZPadding(4),
+
+ "unk_4c0" / Int32ul,
+ "unk_4c4" / Int32ul,
+ "unk_4c8" / Int32ul,
+ "unk_4cc" / Int32ul,
+ "unk_4d0" / Int32ul,
+ "unk_4d4" / Int32ul,
+ "unk_4d8" / ZPadding(4),
+
+ "unk_4dc" / Int32ul,
+ "unk_4e0" / Int64ul,
+ "unk_4e8" / Int32ul,
+ "unk_4ec" / Int32ul,
+ "unk_4f0" / Int32ul,
+ "unk_4f4" / Int32ul,
+ "unk_4f8" / Int32ul,
+ "unk_4fc" / Int32ul,
+ "unk_500" / Int32ul,
+ Ver("V >= V13_0B4", "unk_504_0" / Int32ul),
+ "unk_504" / Int32ul,
+ "unk_508" / Int32ul,
+ "unk_50c" / Int32ul,
+ "unk_510" / Int32ul,
+ "unk_514" / Int32ul,
+ "unk_518" / Int32ul,
+ "unk_51c" / Int32ul,
+ "unk_520" / Int32ul,
+ "unk_524" / Int32ul,
+ "unk_528" / Int32ul,
+ "unk_52c" / Int32ul,
+ "unk_530" / Int32ul,
+ "unk_534" / Int32ul,
+ "unk_538" / Int32ul,
+ Ver("V >= V13_0B4", "unk_53c_0" / Int32ul),
+ "num_frags" / Int32ul,
+ "unk_540" / Int32ul,
+ "unk_544" / Int32ul,
+ "unk_548" / Int32ul,
+ "unk_54c" / Int32ul,
+ "unk_550" / Int32ul,
+ "unk_554" / Int32ul,
+ "gpu_region_base" / Int64ul,
+ "gpu_core" / Int32ul,
+ "gpu_rev" / Int32ul,
+ "num_cores" / Int32ul,
+ "max_pstate" / Int32ul,
+ Ver("V < V13_0B4", "num_pstates" / Int32ul),
+ "frequencies" / Array(16, Dec(Int32ul)),
+ "voltages" / Array(16, Array(8, Dec(Int32ul))),
+ "voltages_sram" / Array(16, Array(8, Dec(Int32ul))),
+ "unk_9b4" / Array(16, Float32l),
+ "unk_9f4" / Array(16, Int32ul),
+ "rel_max_powers" / Array(16, Dec(Int32ul)),
+ "rel_boost_freqs" / Array(16, Dec(Int32ul)),
+ Ver("V < V13_0B4", "min_sram_volt" / Dec(Int32ul)),
+ Ver("V < V13_0B4", "unk_ab8" / Int32ul),
+ Ver("V < V13_0B4", "unk_abc" / Int32ul),
+ Ver("V < V13_0B4", "unk_ac0" / Int32ul),
+
+ Ver("V >= V13_0B4", "unk_ac4_0" / HexDump(Bytes(0x1f0))),
+
+ "pad_ac4" / ZPadding(8),
+ "unk_acc" / Int32ul,
+ "unk_ad0" / Int32ul,
+ "pad_ad4" / ZPadding(16),
+ "unk_ae4" / Array(4, Int32ul),
+ "pad_af4" / ZPadding(4),
+ "unk_af8" / Int32ul,
+ "unk_afc" / Int32ul,
+ "unk_b00" / Int32ul,
+ "unk_b04" / Int32ul,
+ "unk_b08" / Int32ul,
+ "unk_b0c" / Int32ul,
+ "unk_b10" / Int32ul,
+ "pad_b14" / ZPadding(8),
+ "unk_b1c" / Int32ul,
+ "unk_b20" / Int32ul,
+ "unk_b24" / Int32ul,
+ "unk_b28" / Int32ul,
+ "unk_b2c" / Int32ul,
+ "unk_b30" / Int32ul,
+ "unk_b34" / Int32ul,
+ Ver("V >= V13_0B4", "unk_b38_0" / Int32ul),
+ Ver("V >= V13_0B4", "unk_b38_4" / Int32ul),
+ "unk_b38" / Array(6, Int64ul),
+ "unk_b68" / Int32ul,
+ Ver("V >= V13_0B4", "unk_b6c" / HexDump(Bytes(0xd0))),
+ Ver("V >= V13_0B4", "unk_c3c" / Int32ul),
+ )
+
+ def __init__(self, sgx, chip_info):
+ # Userspace VA map related
+ self.unk_0 = 0x13_00000000
+ self.unk_8 = 0x14_00000000
+ self.unk_10 = 0x1_00000000
+ self.unk_18 = 0xffc00000
+ self.unk_20 = 0x11_00000000
+ self.unk_28 = 0x11_00000000
+ # userspace address?
+ self.unk_30 = 0x6f_ffff8000
+ self.pad_40 = bytes(0x20)
+ # unmapped?
+ self.unkptr_38 = 0xffffffa0_11800000
+ self.pad_1c8 = bytes(8)
+
+ # Note: these are rounded poorly, need to recompute.
+ self.yuv_matrices = [
+ [ # BT.601 full range -> RGB
+ [ 0x2000, -0x8, 0x2cdb, -0x2cd3],
+ [ 0x2000, -0xb00, -0x16da, 0x21da],
+ [ 0x2000, 0x38b6, 0x8, -0x38be],
+ ],
+ [ # BT.709 full range -> RGB
+ [ 0x2000, -0x1, 0x3264, -0x3263],
+ [ 0x2000, -0x5fe, -0xefb, 0x14f9],
+ [ 0x2000, 0x3b61, 0x1, -0x3b62],
+ ],
+ [ # BT.2020 full range -> RGB
+ [ 0x2000, 0x0, 0x2f30, -0x2f30],
+ [ 0x2000, -0x544, -0x1248, 0x178c],
+ [ 0x2000, 0x3c34, -0x1, -0x3c33],
+ ],
+ [ # BT.601 limited range -> RGB
+ [ 0x2568, -0x9, 0x3343, -0x37e7],
+ [ 0x2568, -0xc92, -0x1a1e, 0x2203],
+ [ 0x2568, 0x40cf, 0x9, -0x4585],
+ ],
+ [ # BT.709 limited range -> RGB
+ [ 0x2568, -0x1, 0x3997, -0x3e43],
+ [ 0x2568, -0x6d9, -0x111f, 0x134b],
+ [ 0x2568, 0x43dd, 0x1, -0x488b],
+ ],
+ [ # BT.2020 limited range -> RGB
+ [ 0x2568, 0x0, 0x35ee, -0x3a9b],
+ [ 0x2568, -0x604, -0x14e5, 0x163c],
+ [ 0x2568, 0x44ce, -0x1, -0x497a],
+ ],
+ [ # Unknown YUV->RGB
+ [ 0x24cb, 0x0, 0x2cfa, -0x3676],
+ [ 0x24cb, -0xb0a, -0x16e9, 0x1877],
+ [ 0x24cb, 0x38d9, 0x0, -0x4255],
+ ],
+ [ # null
+ [ 0x0, 0x0, 0x0, 0x0],
+ [ 0x0, 0x0, 0x0, 0x0],
+ [ 0x0, 0x0, 0x0, 0x0],
+ ],
+ [ # RGB -> BT.601 full range
+ [ 0x2645, 0x4b23, 0xe98, 0x0],
+ [-0x15a1, -0x2a5e, 0x3fff, 0x4000],
+ [ 0x4000, -0x35a2, -0xa5e, 0x4000],
+ ],
+ [ # RGB -> BT.709 full range
+ [ 0x1b37, 0x5b8c, 0x93d, 0x0],
+ [ -0xeac, -0x3155, 0x4000, 0x4000],
+ [ 0x4000, -0x3a24, -0x5dd, 0x4000],
+ ],
+ [ # RGB -> BT.2020 full range
+ [ 0x21a0, 0x56c9, 0x797, 0x0],
+ [-0x11de, -0x2e22, 0x4000, 0x4000],
+ [ 0x4000, -0x3adb, -0x526, 0x4000],
+ ],
+ [ # RGB -> BT.601 limited range
+ [ 0x20bd, 0x4047, 0xc7c, 0x800],
+ [-0x12ed, -0x2513, 0x3800, 0x4000],
+ [ 0x3800, -0x2eee, -0x912, 0x4000],
+ ],
+ [ # RGB -> BT.709 limited range
+ [ 0x1748, 0x4e51, 0x7e7, 0x800],
+ [ -0xcd6, -0x2b2a, 0x3800, 0x4000],
+ [ 0x3800, -0x32df, -0x521, 0x4000],
+ ],
+ [ # RGB -> BT.2020 limited range
+ [ 0x1cc4, 0x4a3e, 0x67e, 0x800],
+ [ -0xfa3, -0x285e, 0x3800, 0x4000],
+ [ 0x3800, -0x337f, -0x481, 0x4000],
+ ],
+ [ # Unknown (identity?)
+ [-0x8000, 0x0, 0x0, 0x0],
+ [ 0x0, -0x8000, 0x0, 0x0],
+ [ 0x0, 0x0, -0x8000, 0x0],
+ ],
+ ]
+ if Ver.check("V >= V13_0B4"):
+ self.yuv_matrices = [
+ *self.yuv_matrices[:8],
+ *(24 * [[[0,0,0,0]]*3]),
+ *self.yuv_matrices[8:],
+ *(24 * [[[0,0,0,0]]*3]),
+ ]
+
+ self.unk_450_0 = bytes(0x68)
+
+ self.chip_id = chip_info.chip_id
+ self.unk_454 = 0x1
+ self.unk_458 = 0x1
+ self.unk_45c = 0x0
+ self.unk_460 = 0x1
+ self.unk_464 = 0x1
+ self.unk_468 = 0x1
+ self.unk_46c = 0x0
+ self.unk_470 = 0x0
+ self.unk_474 = 0x0
+ self.unk_478 = 0x0
+ self.unk_47c = 0x1
+ self.unk_480 = 0x0
+ self.unk_484 = 0x1
+ self.unk_488 = 0x0
+ self.unk_48c = 0x1
+ self.base_clock_khz = 24000
+ self.power_sample_period = sgx.gpu_power_sample_period
+ self.unk_49c = 0x1
+ self.unk_4a0 = 0x1
+ self.unk_4a4 = 0x1
+ self.unk_4ac = 0x0
+ self.unk_4b8 = 0x0
+ self.unk_4c0 = 0x1f
+ self.unk_4c4 = 0x0
+ self.unk_4c8 = 0x0
+ self.unk_4cc = 0x0
+ self.unk_4d0 = 0x0
+ self.unk_4d4 = 0x0
+ self.unk_4dc = 0x0
+ self.unk_4e0 = chip_info.hwdb_4e0
+ self.unk_4e8 = 0x0
+ self.unk_4ec = 0x0
+ self.unk_4f0 = 0x1
+ self.unk_4f4 = 0x1
+ self.unk_4f8 = 0x0
+ self.unk_4fc = 0x0
+ self.unk_500 = 0x0
+ self.unk_504_0 = 0
+ self.unk_504 = 0x31
+ self.unk_508 = 0x0
+ self.unk_50c = 0x0
+ self.unk_510 = 0x0
+ self.unk_514 = 0x0
+ self.unk_518 = 0x0
+ self.unk_51c = 0x0
+ self.unk_520 = 0x0
+ self.unk_524 = 0x1 # use_secure_cache_flush
+ self.unk_528 = 0x0
+ self.unk_52c = 0x0
+ self.unk_530 = 0x0
+ self.unk_534 = chip_info.hwdb_534
+ self.unk_538 = 0x0
+ self.unk_53c_0 = 0
+ self.num_frags = chip_info.num_cores
+ self.unk_540 = 0x0
+ self.unk_544 = 0x0
+ self.unk_548 = 0x0
+ self.unk_54c = 0x0
+ self.unk_550 = 0x0
+ self.unk_554 = 0x1
+ self.gpu_region_base = sgx.gpu_region_base
+ self.gpu_core = chip_info.gpu_core
+ self.gpu_rev = chip_info.gpu_rev
+ self.num_cores = chip_info.num_cores
+ self.max_pstate = sgx.gpu_num_perf_states
+ self.num_pstates = sgx.gpu_num_perf_states + 1
+
+ self.frequencies = [0] * 16
+ self.voltages = [[0] * 8 for i in range(16)]
+ self.voltages_sram = [[0] * 8 for i in range(16)]
+ self.unk_9b4 = [0.] * 16
+ self.unk_9f4 = [0] * 16
+ self.rel_max_powers = [0] * 16
+ self.rel_boost_freqs = [0] * 16
+ self.min_sram_volt = chip_info.min_sram_volt
+ self.unk_ab8 = chip_info.hwdb_ab8
+ self.unk_abc = chip_info.hwdb_abc
+ self.unk_ac0 = 0x1020
+ self.unk_ac4_0 = bytes(0x1f0)
+ self.unk_acc = 0x0
+ self.unk_ad0 = 0x0
+ if Ver.check("V >= V13_0B4"):
+ self.unk_ae4 = [0x0, 0x3, 0x7, 0x7]
+ else:
+ self.unk_ae4 = [0x0, 0xf, 0x3f, 0x3f]
+ self.unk_af8 = 0x0
+ self.unk_afc = 0x0
+ self.unk_b00 = 0x0
+ self.unk_b04 = 0x0
+ self.unk_b08 = 0x0
+ self.unk_b0c = 0x0
+ self.unk_b10 = 0x1
+ self.unk_b1c = 0x0
+ self.unk_b20 = 0x0
+ self.unk_b24 = 0x1
+ self.unk_b28 = 0x1
+ self.unk_b2c = 0x1
+ self.unk_b30 = chip_info.hwdb_b30
+ self.unk_b34 = 0x0
+ self.unk_b38_0 = 1
+ self.unk_b38_4 = 1
+ self.unk_b38 = [0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff]
+ self.unk_b68 = 0x0
+ self.unk_b6c = bytes(0xd0)
+ self.unk_c3c = 0x19
+
+class InitData_BufferMgrCtl(ConstructValueClass):
+ subcon = Array(126, Bytes(0x10))
+
+ def __init__(self):
+ self.value = [bytes(0x10)] * 126
+
+class InitData_GPUQueueStatsTA(ConstructClass):
+ subcon = Struct(
+ "busy" / Int32ul,
+ "unk_4" / Int32ul,
+ "cur_cmdqueue" / Int64ul,
+ "cur_count" / Int32ul,
+ "unk_14" / Int32ul,
+ )
+ def __init__(self):
+ self.busy = 0
+ self.unk_4 = 0
+ self.cur_cmdqueue = 0
+ self.cur_count = 0
+ self.unk_14 = 0
+
+class InitData_GPUStatsTA(ConstructClass):
+ subcon = Struct(
+ "unk_4" / Int32ul,
+ "queues" / Array(4, InitData_GPUQueueStatsTA),
+ "unk_68" / Bytes(0x8),
+ "unk_70" / Int32ul,
+ "unk_74" / Int32ul,
+ "unk_timestamp" / Int64ul,
+ "unk_80" / HexDump(Bytes(0x40)),
+ Ver("V >= V13_0B4", "unk_c0" / HexDump(Bytes(0x800))),
+ )
+
+ def __init__(self):
+ self.unk_4 = 0
+ self.queues = [InitData_GPUQueueStatsTA() for i in range(4)]
+ self.unk_68 = bytes(0x8)
+ self.unk_70 = 0
+ self.unk_74 = 0
+ self.unk_timestamp = 0
+ self.unk_80 = bytes(0x40)
+ self.unk_c0 = bytes(0x800)
+
+class InitData_GPUQueueStats3D(ConstructClass):
+ subcon = Struct(
+ "busy" / Int32ul,
+ "cur_cmdqueue" / Int64ul,
+ "unk_c" / Int32ul,
+ "unk_10" / Int32ul,
+ "unk_14" / HexDump(Bytes(0x28 - 0x14)),
+ )
+ def __init__(self):
+ self.busy = 0
+ self.cur_cmdqueue = 0
+ self.unk_c = 0
+ self.unk_10 = 0
+ self.unk_14 = bytes(0x14)
+
+class InitData_GPUStats3D(ConstructClass):
+ subcon = Struct(
+ "unk_0" / Bytes(0x18),
+ "queues" / Array(4, InitData_GPUQueueStats3D),
+ "unk_d0" / HexDump(Bytes(0x38)),
+ "tvb_overflows_1" / Int32ul,
+ "tvb_overflows_2" / Int32ul,
+ "unk_f8" / Int32ul,
+ "unk_fc" / Int32ul,
+ "cur_stamp_id" / Int32sl,
+ "unk_104" / Bytes(0x14),
+ "unk_118" / Int32sl,
+ "unk_11c" / Int32ul,
+ "unk_120" / Int32ul,
+ "unk_124" / Int32ul,
+ "unk_128" / Int32ul,
+ "unk_12c" / Int32ul,
+ "unk_timestamp" / Int64ul,
+ "unk_134" / Bytes(0x1c0 - 0x134),
+ Ver("V >= V13_0B4", "unk_1c0" / HexDump(Bytes(0x800))),
+ )
+
+ def __init__(self):
+ self.unk_0 = bytes(0x18)
+ self.queues = [InitData_GPUQueueStats3D() for i in range(4)]
+ self.unk_68 = 0
+ self.cur_cmdqueue = 0
+ self.unk_d0 = bytes(0x38)
+ self.tvb_overflows_1 = 0
+ self.tvb_overflows_2 = 0
+ self.unk_f8 = 0
+ self.unk_fc = 0
+ self.cur_stamp_id = -1
+ self.unk_104 = bytes(0x14)
+ self.unk_118 = -1
+ self.unk_11c = 0
+ self.unk_120 = 0
+ self.unk_124 = 0
+ self.unk_128 = 0
+ self.unk_12c = 0
+ self.unk_timestamp = 0
+ self.unk_134 = bytes(0x1c0 - 0x134)
+ self.unk_1c0 = bytes(0x800)
+
+class InitData_GPUGlobalStatsTA(ConstructClass):
+ subcon = Struct(
+ "total_cmds" / Int32ul,
+ "stats" / InitData_GPUStatsTA,
+ )
+
+ def __init__(self):
+ self.total_cmds = 0
+ self.stats = InitData_GPUStatsTA()
+
+class InitData_GPUGlobalStats3D(ConstructClass):
+ subcon = Struct(
+ "total_cmds" / Int32ul,
+ "unk_4" / Int32ul,
+ "stats" / InitData_GPUStats3D,
+ )
+
+ def __init__(self):
+ self.total_cmds = 0
+ self.unk_4 = 0
+ self.stats = InitData_GPUStats3D()
+
+class InitData_RegionB(ConstructClass):
+ subcon = Struct(
+ "channels" / ChannelInfoSet,
+ "pad_110" / ZPadding(0x50),
+ "unk_160" / Default(Int64ul, 0),
+ "unk_168" / Default(Int64ul, 0),
+ "stats_ta_addr" / Int64ul,
+ "stats_ta" / ROPointer(this.stats_ta_addr, InitData_GPUGlobalStatsTA),
+ "stats_3d_addr" / Int64ul,
+ "stats_3d" / ROPointer(this.stats_3d_addr, InitData_GPUGlobalStats3D),
+ "stats_cp_addr" / Int64ul,
+ "stats_cp" / ROPointer(this.stats_cp_addr, Bytes(0x140)),
+ "hwdata_a_addr" / Int64ul,
+ "hwdata_a" / ROPointer(this.hwdata_a_addr, AGXHWDataA),
+ "unkptr_190" / Int64ul, # size: 0x80, empty
+ "unk_190" / ROPointer(this.unkptr_190, Bytes(0x80)),
+ "unkptr_198" / Int64ul, # size: 0xc0, fw writes timestamps into this
+ "unk_198" / ROPointer(this.unkptr_198, Bytes(0xc0)),
+ "hwdata_b_addr" / Int64ul, # size: 0xb80, io stuff
+ "hwdata_b" / ROPointer(this.hwdata_b_addr, AGXHWDataB),
+ "hwdata_b_addr2" / Int64ul, # repeat of 1a0
+ "fwlog_ring2" / Int64ul, #
+ "unkptr_1b8" / Int64ul, # Unallocated, Size 0x1000
+ "unk_1b8" / Lazy(ROPointer(this.unkptr_1b8, Bytes(0x1000))),
+ "unkptr_1c0" / Int64ul, # Unallocated, size 0x300
+ "unk_1c0" / Lazy(ROPointer(this.unkptr_1c0, Bytes(0x300))),
+ "unkptr_1c8" / Int64ul, # Unallocated, unknown size
+ "unk_1c8" / Lazy(ROPointer(this.unkptr_1c8, Bytes(0x1000))),
+ "unk_1d0" / Int32ul,
+ "unk_1d4" / Int32ul,
+ "unk_1d8" / HexDump(Bytes(0x3c)),
+ "buffer_mgr_ctl_addr" / Int64ul, # Size: 0x4000
+ "buffer_mgr_ctl" / ROPointer(this.buffer_mgr_ctl_addr, InitData_BufferMgrCtl),
+ "buffer_mgr_ctl_addr2" / Int64ul, # Size: 0x4000
+ # Written to by DC_09
+ "unk_224" / HexDump(Bytes(0x685c)),
+ "unk_6a80" / Int32ul,
+ "gpu_idle" / Int32ul,
+ "unkpad_6a88" / HexDump(Bytes(0x14)),
+ "unk_6a9c" / Int32ul,
+ "unk_ctr0" / Int32ul,
+ "unk_ctr1" / Int32ul,
+ "unk_6aa8" / Int32ul,
+ "unk_6aac" / Int32ul,
+ "unk_ctr2" / Int32ul,
+ "unk_6ab4" / Int32ul,
+ "unk_6ab8" / Int32ul,
+ "unk_6abc" / Int32ul,
+ "unk_6ac0" / Int32ul,
+ "unk_6ac4" / Int32ul,
+ "unk_ctr3" / Int32ul,
+ "unk_6acc" / Int32ul,
+ "unk_6ad0" / Int32ul,
+ "unk_6ad4" / Int32ul,
+ "unk_6ad8" / Int32ul,
+ "unk_6adc" / Int32ul,
+ "unk_6ae0" / Int32ul,
+ "unk_6ae4" / Int32ul,
+ "unk_6ae8" / Int32ul,
+ "unk_6aec" / Int32ul,
+ "unk_6af0" / Int32ul,
+ "unk_ctr4" / Int32ul,
+ "unk_ctr5" / Int32ul,
+ "unk_6afc" / Int32ul,
+ "pad_6b00" / HexDump(Bytes(0x38)),
+ "unk_6b38" / Int32ul,
+ "pad_6b3c" / HexDump(Bytes(0x84)),
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.unk_1d0 = 0
+ self.unk_1d4 = 0
+ self.unk_1d8 = bytes(0x3c)
+ self.unk_224 = bytes(0x685c)
+ self.unkpad_6a88 = bytes(0x14)
+ self.pad_6b00 = bytes(0x38)
+ self.unk_6b38 = 0xff
+ self.pad_6b3c = bytes(0x84)
+
+ def mon(self, add_fn):
+ add_fn(self.unkptr_170, 0x140, "unkptr_170")
+ add_fn(self.unkptr_178, 0x1c0, "unkptr_178")
+ add_fn(self.unkptr_180, 0x140, "unkptr_180")
+ add_fn(self.unkptr_188_addr, 0x3b80, "unkptr_188")
+ add_fn(self.unkptr_190, 0x80, "unkptr_190")
+ add_fn(self.unkptr_198_addr, 0xc0, "unkptr_198")
+ add_fn(self.unkptr_1a0_addr, 0xb80, "unkptr_1a0")
+ add_fn(self.fwlog_ring2, 0x51000, "fwlog_ring2")
+ add_fn(self.unkptr_214, 0x4000, "unkptr_214")
+
+ # Unallocated during init
+ #add_fn(self.unkptr_1b8, 0x1000, "unkptr_1b8")
+ #add_fn(self.unkptr_1c0, 0x300, "unkptr_1c0")
+ #add_fn(self.unkptr_1c8, 0x1000, "unkptr_1c8")
+
+class InitData_PendingStamp(ConstructClass):
+ subcon = Struct(
+ "info" / Int32ul,
+ "wait_value" / Int32ul,
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.info = 0
+ self.wait_value = 0
+
+ def __bool__(self):
+ return bool(self.info or self.wait_value)
+
+class InitData_FaultInfo(ConstructClass):
+ subcon = Struct(
+ "unk_0" / Int32ul,
+ "unk_4" / Int32ul,
+ "queue_uuid" / Int32ul,
+ "unk_c" / Int32ul,
+ "unk_10" / Int32ul,
+ "unk_14" / Int32ul,
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.unk_0 = 0
+ self.unk_4 = 0
+ self.queue_uuid = 0
+ self.unk_c = 0
+ self.unk_10 = 0
+ self.unk_14 = 0
+
+class RCPowerZone(ConstructClass):
+ subcon = Struct(
+ "target" / Dec(Int32ul),
+ "target_off" / Dec(Int32ul),
+ "tc" / Dec(Int32ul),
+ )
+ def __init__(self, tc=None, target=None, off=None):
+ if tc is None:
+ self.target = 0
+ self.target_off = 0
+ self.tc = 0
+ else:
+ self.target = target
+ self.target_off = self.target - off
+ self.tc = tc
+
+
+class InitData_RegionC(ConstructClass):
+ subcon = Struct(
+ "ktrace_enable" / Int32ul,
+ "unk_4" / HexDump(Bytes(0x24)),
+ Ver("V >= V13_0B4", "unk_28_0" / Int32ul),
+ "unk_28" / Int32ul,
+ Ver("V >= V13_0B4", "unk_2c_0" / Int32ul),
+ "unk_2c" / Int32ul,
+ "unk_30" / Int32ul,
+ "unk_34" / Int32ul,
+ "unk_38" / HexDump(Bytes(0x1c)),
+ "unk_54" / Int16ul,
+ "unk_56" / Int16ul,
+ "unk_58" / Int16ul,
+ "unk_5a" / Int32ul,
+ "unk_5e" / Int32ul,
+ "unk_62" / Int32ul,
+ Ver("V >= V13_0B4", "unk_66_0" / HexDump(Bytes(0xc))),
+ "unk_66" / Int32ul,
+ "unk_6a" / HexDump(Bytes(0x16)),
+ "unk_80" / HexDump(Bytes(0xf80)),
+ "unk_1000" / HexDump(Bytes(0x7000)),
+ "unk_8000" / HexDump(Bytes(0x900)),
+ Ver("V >= V13_0B4", "unk_8900_0" / Int32ul),
+ "unk_8900" / Int32ul,
+ "unk_atomic" / Int32ul,
+ "max_power" / Int32ul,
+ "max_pstate_scaled" / Int32ul,
+ "max_pstate_scaled_2" / Int32ul,
+ "unk_8914" / Int32ul,
+ "unk_8918" / Int32ul,
+ "max_pstate_scaled_3" / Int32ul,
+ "unk_8920" / Int32ul,
+ "power_zone_count" / Int32ul,
+ "avg_power_filter_tc_periods" / Int32ul,
+ "avg_power_ki_dt" / Float32l,
+ "avg_power_kp" / Float32l,
+ "avg_power_min_duty_cycle" / Int32ul,
+ "avg_power_target_filter_tc" / Int32ul,
+ "power_zones" / Array(5, RCPowerZone),
+ "unk_8978" / HexDump(Bytes(0x44)),
+ Ver("V >= V13_0B4", "unk_89bc_0" / HexDump(Bytes(0x3c))),
+ "unk_89bc" / Int32ul,
+ "fast_die0_release_temp" / Int32ul,
+ "unk_89c4" / Int32sl,
+ "fast_die0_prop_tgt_delta" / Int32ul,
+ "fast_die0_kp" / Float32l,
+ "fast_die0_ki_dt" / Float32l,
+ "unk_89d4" / HexDump(Bytes(0xc)),
+ "unk_89e0" / Int32ul,
+ "max_power_2" / Int32ul,
+ "ppm_kp" / Float32l,
+ "ppm_ki_dt" / Float32l,
+ "unk_89f0" / Int32ul,
+ Ver("V >= V13_0B4", "unk_89f4_0" / HexDump(Bytes(0x8))),
+ Ver("V >= V13_0B4", "unk_89f4_8" / Int32ul),
+ Ver("V >= V13_0B4", "unk_89f4_c" / HexDump(Bytes(0x50))),
+ "hws1" / AGXHWDataShared1,
+ "hws2" / AGXHWDataShared2,
+ "hws3" / AGXHWDataShared3,
+ "unk_9004" / HexDump(Bytes(8)),
+ Ver("V >= V13_0B4", "unk_900c_0" / HexDump(Bytes(0x28))),
+ "unk_900c" / Int32ul,
+ Ver("V >= V13_0B4", "unk_9010_0" / Int32ul),
+ Ver("V >= V13_0B4", "unk_9010_4" / HexDump(Bytes(0x14))),
+ "unk_9010" / HexDump(Bytes(0x2c)),
+ "unk_903c" / Int32ul,
+ "unk_9040" / HexDump(Bytes(0xc0)),
+ "unk_9100" / HexDump(Bytes(0x6f00)),
+ "unk_10000" / HexDump(Bytes(0xe50)),
+ "unk_10e50" / Int32ul,
+ "unk_10e54" / HexDump(Bytes(0x2c)),
+ "fault_control" / Int32ul,
+ "do_init" / Int32ul,
+ "unk_10e88" / HexDump(Bytes(0x188)),
+ "idle_ts" / Int64ul,
+ "idle_unk" / Int64ul,
+ "unk_11020" / Int32ul,
+ "unk_11024" / Int32ul,
+ "unk_11028" / Int32ul,
+ Ver("V >= V13_0B4", "unk_1102c_0" / Int32ul),
+ Ver("V >= V13_0B4", "unk_1102c_4" / Int32ul),
+ Ver("V >= V13_0B4", "unk_1102c_8" / Dec(Int32ul)),
+ Ver("V >= V13_0B4", "unk_1102c_c" / Int32ul),
+ Ver("V >= V13_0B4", "unk_1102c_10" / Int32ul),
+ "unk_1102c" / Int32ul,
+ "idle_to_off_delay_ms" / Int32ul,
+ "fender_idle_to_off_delay_ms" / Int32ul,
+ "fw_early_wake_timeout_ms" / Int32ul,
+ "pending_stamps" / Array(0x110, InitData_PendingStamp),
+ "unk_117bc" / Int32ul,
+ "fault_info" / InitData_FaultInfo,
+ "counter" / Int32ul,
+ "unk_118dc" / Int32ul,
+ Ver("V >= V13_0B4", "unk_118e0_0" / HexDump(Bytes(0x9c))),
+ "unk_118e0" / Dec(Int32ul),
+ Ver("V >= V13_0B4", "unk_118e4_0" / Dec(Int32ul)),
+ "unk_118e4" / Int32ul,
+ "unk_118e8" / Int32ul,
+ "unk_118ec" / Array(0x15, Int8ul),
+ "unk_11901" / HexDump(Bytes(0x43f)),
+ Ver("V >= V13_0B4", "unk_11d40" / HexDump(Bytes(0x19c))),
+ Ver("V >= V13_0B4", "unk_11edc" / Int32ul),
+ Ver("V >= V13_0B4", "unk_11ee0" / HexDump(Bytes(0x1c))),
+ Ver("V >= V13_0B4", "unk_11efc" / Int32ul),
+ )
+
+ def __init__(self, sgx, chip_info):
+ period_ms = sgx.gpu_power_sample_period
+ avg_power_filter_tc_periods = sgx.gpu_avg_power_filter_tc_ms // period_ms
+
+ self.ktrace_enable = 0# 0xffffffff
+ self.unk_4 = bytes(0x24)
+ self.unk_28_0 = 1 # debug
+ self.unk_28 = 1
+ self.unk_2c_0 = 0
+ self.unk_2c = 1
+ self.unk_30 = 0
+ self.unk_34 = 120
+ self.unk_38 = bytes(0x1c)
+ self.unk_54 = 0xffff
+ self.unk_56 = 40
+ self.unk_58 = 0xffff
+ self.unk_5a = 0
+ self.unk_5e = 1
+ self.unk_62 = 0
+ self.unk_66_0 = bytes(0xc)
+ self.unk_66 = 1
+ self.unk_6a = bytes(0x16)
+ self.unk_80 = bytes(0xf80)
+ self.unk_1000 = bytes(0x7000)
+ self.unk_8000 = bytes(0x900)
+ self.unk_8900_0 = 0
+ self.unk_8900 = 1
+ # Accessed with OSIncrementAtomic/OSDecrementAtomic
+ self.unk_atomic = 0
+ self.max_power = chip_info.max_power
+ self.max_pstate_scaled = 100 * sgx.gpu_num_perf_states
+ self.max_pstate_scaled_2 = 100 * sgx.gpu_num_perf_states
+ self.unk_8914 = 0
+ self.unk_8918 = 0
+ self.max_pstate_scaled_3 = 100 * sgx.gpu_num_perf_states
+ self.unk_8920 = 0
+
+ self.power_zones = [RCPowerZone()] * 5
+ power_zone_count = 0
+ for i in range(5):
+ if sgx.getprop(f"gpu-power-zone-target-{i}", None) is None:
+ break
+ self.power_zones[i] = RCPowerZone(
+ sgx.getprop(f"gpu-power-zone-filter-tc-{i}", None),
+ sgx.getprop(f"gpu-power-zone-target-{i}", None),
+ sgx.getprop(f"gpu-power-zone-target-offset-{i}", None),
+ )
+ power_zone_count += 1
+
+ self.power_zone_count = power_zone_count
+ self.avg_power_filter_tc_periods = avg_power_filter_tc_periods
+ self.avg_power_ki_dt = sgx.gpu_avg_power_ki_only * (period_ms / 1000)
+ self.avg_power_kp = sgx.gpu_avg_power_kp
+ self.avg_power_min_duty_cycle = sgx.gpu_avg_power_min_duty_cycle
+ self.avg_power_target_filter_tc = sgx.gpu_avg_power_target_filter_tc
+ self.unk_8978 = bytes(0x44)
+ self.unk_89bc_0 = bytes(0x3c)
+ self.unk_89bc = chip_info.unk_8cc
+ self.fast_die0_release_temp = 100 * sgx.getprop("gpu-fast-die0-release-temp", 80)
+ self.unk_89c4 = chip_info.unk_87c
+ self.fast_die0_prop_tgt_delta = 100 * sgx.getprop("gpu-fast-die0-prop-tgt-delta", 0)
+ self.fast_die0_kp = sgx.gpu_fast_die0_proportional_gain
+ self.fast_die0_ki_dt = sgx.gpu_fast_die0_integral_gain * (period_ms / 1000)
+ self.unk_89d4 = bytes(0xc)
+ self.unk_89e0 = 1
+ self.max_power_2 = chip_info.max_power
+ self.ppm_kp = sgx.gpu_ppm_kp
+ self.ppm_ki_dt = sgx.gpu_ppm_ki * (period_ms / 1000)
+ self.unk_89f0 = 0
+ self.unk_89f4_0 = bytes(8)
+ self.unk_89f4_8 = 1
+ self.unk_89f4_c = bytes(0x50)
+ self.hws1 = AGXHWDataShared1(chip_info)
+ self.hws2 = AGXHWDataShared2(chip_info)
+ self.hws3 = AGXHWDataShared3(chip_info)
+ self.unk_9004 = bytes(8)
+ self.unk_900c_0 = bytes(0x28)
+ self.unk_900c = 1
+ self.unk_9010_0 = 1
+ self.unk_9010_4 = bytes(0x14)
+ self.unk_9010 = bytes(0x2c)
+ if Ver.check("V >= V13_0B4"):
+ self.unk_903c = 1
+ else:
+ self.unk_903c = 0
+ self.unk_9040 = bytes(0xc0)
+ self.unk_9100 = bytes(0x6f00)
+ self.unk_10000 = bytes(0xe50)
+ self.unk_10e50 = 0
+ self.unk_10e54 = bytes(0x2c)
+ self.unk_10e80_0 = bytes(0xed4)
+ self.unk_10e80_ed0 = 0
+ self.unk_10e80_ed4 = bytes(0x2c)
+ #self.fault_control = 0xb
+ self.fault_control = 0
+ self.do_init = 1
+ self.unk_10e88 = bytes(0x188)
+ self.idle_ts = 0
+ self.idle_unk = 0
+ self.unk_11020 = 40
+ self.unk_11024 = 10
+ self.unk_11028 = 250
+ self.unk_1102c_0 = 1
+ self.unk_1102c_4 = 1
+ self.unk_1102c_8 = 100
+ self.unk_1102c_c = 1
+ self.unk_1102c_10 = 0
+ self.unk_1102c = 0
+ self.idle_to_off_delay_ms = sgx.getprop("gpu-idle-off-delay-ms", 2)
+ self.fender_idle_to_off_delay_ms = sgx.getprop("gpu-fender-idle-off-delay-ms", 40)
+ self.fw_early_wake_timeout_ms = sgx.getprop("gpu-fw-early-wake-timeout-ms", 5)
+ self.pending_stamps = [InitData_PendingStamp() for i in range(0x110)]
+ self.unk_117bc = 0
+ self.fault_info = InitData_FaultInfo()
+ self.counter = 0
+ self.unk_118dc = 0
+ self.unk_118e0_0 = bytes(0x9c)
+ self.unk_118e0 = 40
+ self.unk_118e4_0 = 50
+ self.unk_118e4 = 0
+ self.unk_118e8 = 0 if chip_info.unk_118ec is None else 1
+ self.unk_118ec = chip_info.unk_118ec or [0] * 0x15
+ self.unk_11901 = bytes(0x43f)
+
+ self.unk_11d40 = bytes(0x19c)
+ self.unk_11edc = 8
+ self.unk_11ee0 = bytes(0x1c)
+ self.unk_11efc = 8
+
+class UatLevelInfo(ConstructClass):
+ subcon = Struct(
+ "unk_3" / Int8ul, # always 8
+ "unk_1" / Int8ul, # always 14, page bits?
+ "unk_2" / Int8ul, # always 14, also page bits?
+ "index_shift" / Int8ul,
+ "num_entries" / Int16ul,
+ "unk_4" / Int16ul, # 0x4000, Table size?
+ "unk_8" / Int64ul, # always 1
+ "phys_mask" / Int64ul,
+ "index_mask" / Int64ul,
+ )
+
+ def __init__(self, index_shift, num_entries, phys_mask):
+ self.index_shift = index_shift
+ self.unk_1 = 14
+ self.unk_2 = 14
+ self.unk_3 = 8
+ self.unk_4 = 0x4000 # I doubt anything other than 16k pages is supported
+ self.num_entries = num_entries
+ self.unk_8 = 1
+ self.phys_mask = phys_mask
+ self.index_mask = (num_entries - 1) << index_shift
+
+class InitData(ConstructClass):
+ subcon = Struct(
+ Ver("V >= V13_0B4", "ver_info" / Array(4, Int16ul)),
+ "regionA_addr" / Int64ul, # allocation size: 0x4000
+ "regionA" / ROPointer(this.regionA_addr, HexDump(Bytes(0x4000))),
+ "unk_8" / Default(Int32ul, 0),
+ "unk_c"/ Default(Int32ul, 0),
+ "regionB_addr" / Int64ul, # 0xfa00c338000 allocation size: 0x6bc0
+ "regionB" / ROPointer(this.regionB_addr, InitData_RegionB),
+ "regionC_addr" / Int64ul, # 0xfa000200000 allocation size: 0x11d40, heap?
+ "regionC" / ROPointer(this.regionC_addr, InitData_RegionC),
+ "fw_status_addr" / Int64ul, # allocation size: 0x4000, but probably only 0x80 bytes long
+ "fw_status" / ROPointer(this.fw_status_addr, InitData_FWStatus),
+ "uat_page_size" / Int16ul,
+ "uat_page_bits" / Int8ul,
+ "uat_num_levels" / Int8ul,
+ "uat_level_info" / Array(3, UatLevelInfo),
+ "pad_8c" / HexDump(Default(Bytes(0x14), bytes(0x14))),
+ "host_mapped_fw_allocations" / Int32ul, # must be 1
+ "unk_ac" / Int32ul,
+ "unk_b0" / Int32ul,
+ "unk_b4" / Int32ul,
+ "unk_b8" / Int32ul,
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.unk_ac = 0
+ self.unk_b0 = 0
+ self.unk_b4 = 0
+ self.unk_b8 = 0
+
+__all__.extend(k for k, v in globals().items()
+ if (callable(v) or isinstance(v, type)) and v.__module__ == __name__)
diff --git a/tools/proxyclient/m1n1/fw/agx/microsequence.py b/tools/proxyclient/m1n1/fw/agx/microsequence.py
new file mode 100644
index 0000000..475597f
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/agx/microsequence.py
@@ -0,0 +1,800 @@
+"""
+I think these are executed by a simple state machine on the firmware's arm core,
+and the typical result is a commandlist submitting to one of the gpu's hardware
+command processors.
+
+It seems like a common pattern is:
+ 1. Start (3D or Compute)
+ 2. Timestamp
+ 3. Wait For Interrupts
+ 4. Timestamp again
+ 5. Finish (3D or Compute)
+ 6. End
+
+Error messages call these as SKU commands
+
+"""
+from m1n1.constructutils import *
+
+from construct import *
+from construct.core import Int64ul, Int32ul, Int32sl
+import textwrap
+
+__all__ = []
+
+class TimeStamp(ConstructValueClass):
+ subcon = Int64ul
+
+ def __init__(self, value=0):
+ self.value = value
+
+class TsFlag(ConstructValueClass):
+ subcon = Int8ul
+
+ def __init__(self, value=0):
+ self.value = value
+
+class WrappedPointer(ConstructValueClass):
+ subcon = Int64ul
+
+ def __init__(self, value=0):
+ self.value = value
+
+class StampCounter(ConstructValueClass):
+ subcon = Hex(Int32ul)
+
+ def __init__(self):
+ self.value = 0
+
+class BufferManagerBlockControl(ConstructClass):
+ subcon = Struct(
+ "total" / Int32ul,
+ "wptr" / Int32ul,
+ "unk" / Int32ul,
+ "pad" / ZPadding(0x34)
+ )
+
+class BufferManagerCounter(ConstructClass):
+ subcon = Struct(
+ "count" / Int32ul,
+ "pad" / ZPadding(0x3c)
+ )
+
+class BufferManagerMisc(ConstructClass):
+ subcon = Struct(
+ "gpu_0" / Default(Int32ul, 0),
+ "gpu_4" / Default(Int32ul, 0),
+ "gpu_8" / Default(Int32ul, 0),
+ "gpu_c" / Default(Int32ul, 0),
+ "pad_10" / ZPadding(0x10),
+ "cpu_flag" / Int32ul,
+ "pad_24" / ZPadding(0x1c),
+ )
+
+class BufferManagerInfo(ConstructClass):
+ subcon = Struct(
+ "gpu_counter" / Int32ul,
+ "unk_4" / Int32ul,
+ "last_id" / Int32ul,
+ "cur_id" / Int32ul,
+ "unk_10" / Int32ul,
+ "gpu_counter2" / Int32ul,
+ "unk_18" / Int32ul,
+ Ver("V < V13_0B4", "unk_1c" / Int32ul),
+ "page_list_addr" / Int64ul,
+ "page_list_size" / Int32ul,
+ "page_count" / Int32ul,
+ "unk_30" / Int32ul,
+ "block_count" / Int32ul,
+ "unk_38" / Int32ul,
+ "block_list_addr" / Int64ul,
+ "block_ctl_addr" / Int64ul, # points to two u32s
+ "block_ctl" / ROPointer(this.block_ctl_addr, BufferManagerBlockControl),
+ "last_page" / Int32ul,
+ "gpu_page_ptr1" / Int32ul,
+ "gpu_page_ptr2" / Int32ul,
+ "unk_58" / Int32ul,
+ "block_size" / Int32ul,
+ "unk_60" / Int64ul,
+ "counter_addr" / Int64ul,
+ "counter" / ROPointer(this.counter_addr, BufferManagerCounter),
+ "unk_70" / Int32ul,
+ "unk_74" / Int32ul,
+ "unk_78" / Int32ul,
+ "unk_7c" / Int32ul,
+ "unk_80" / Int32ul,
+ "unk_84" / Int32ul,
+ "unk_88" / Int32ul,
+ "unk_8c" / Int32ul,
+ "unk_90" / HexDump(Bytes(0x30)),
+
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.gpu_counter = 0x0
+ self.unk_4 = 0
+ self.last_id = 0x0
+ self.cur_id = 0xffffffff
+ self.unk_10 = 0x0
+ self.gpu_counter2 = 0x0
+ self.unk_18 = 0x0
+ self.unk_1c = 0x0
+ self.unk_30 = 0xd1a
+ self.unk_38 = 0x0
+ self.gpu_page_ptr1 = 0x0
+ self.gpu_page_ptr2 = 0x0
+ self.unk_58 = 0x0
+ self.unk_60 = 0x0
+ self.unk_70 = 0x0
+ self.unk_74 = 0x0
+ self.unk_78 = 0x0
+ self.unk_7c = 0x0
+ self.unk_80 = 0x1
+ self.unk_84 = 0x66cc
+ self.unk_88 = 0x2244
+ self.unk_8c = 0x0
+ self.unk_90 = bytes(0x30)
+
+
+class Start3DClearPipelineBinding(ConstructClass):
+ subcon = Struct(
+ "pipeline_bind" / Int64ul,
+ "address" / Int64ul,
+ )
+
+ def __init__(self, pipeline_bind=None, address=None):
+ super().__init__()
+ self.pipeline_bind = pipeline_bind
+ self.address = address
+
+class Start3DStorePipelineBinding(ConstructClass):
+ subcon = Struct(
+ "unk_0" / Int64ul,
+ "unk_8" / Int32ul,
+ "pipeline_bind" / Int32ul,
+ "unk_10" / Int32ul,
+ "address" / Int32ul,
+ "unk_18" / Int32ul,
+ "unk_1c_padding" / Int32ul,
+ )
+
+ def __init__(self, pipeline_bind=None, address=None):
+ super().__init__()
+ self.unk_0 = 0
+ self.unk_8 = 0
+ self.pipeline_bind = pipeline_bind
+ self.unk_10 = 0
+ self.address = address
+ self.unk_18 = 0
+ self.unk_1c_padding = 0
+
+class Start3DArrayAddr(ConstructClass):
+ subcon = Struct(
+ "ptr" / Int64ul,
+ "unk_padding" / Int64ul,
+ )
+
+ def __init__(self, ptr=None):
+ super().__init__()
+ self.ptr = ptr
+ self.unk_padding = 0
+
+class AuxFBInfo(ConstructClass):
+ subcon = Struct(
+ "unk1" / Int32ul,
+ "unk2" / Int32ul,
+ "width" / Dec(Int32ul),
+ "height" / Dec(Int32ul),
+ Ver("V >= V13_0B4", "unk3" / Int64ul),
+ )
+
+ def __init__(self, unk1, unk2, width, height):
+ super().__init__()
+ self.unk1 = unk1
+ self.unk2 = unk2
+ self.width = width
+ self.height = height
+ self.unk3 = 0x100000
+
+class Start3DStruct1(ConstructClass):
+ subcon = Struct(
+ "store_pipeline_bind" / Int32ul, # 0x12, 0x34 seen
+ "store_pipeline_addr" / Int32ul,
+ "unk_8" / Int32ul,
+ "unk_c" / Int32ul,
+ "merge_upper_x" / Float32l,
+ "merge_upper_y" / Float32l,
+ "unk_18" / Int64ul,
+ "tile_blocks_y" / Int16ul, # * 4
+ "tile_blocks_x" / Int16ul, # * 4
+ "unk_24" / Int32ul,
+ "tile_counts" / Int32ul,
+ "unk_2c" / Int32ul,
+ "depth_clear_val1" / Float32l,
+ "stencil_clear_val1" / Int8ul,
+ "unk_35" / Int8ul,
+ "unk_36" / Int16ul,
+ "unk_38" / Int32ul,
+ "unk_3c" / Int32ul,
+ "unk_40" / Int32ul,
+ "unk_44_padding" / HexDump(Bytes(0xac)),
+ "depth_bias_array" / Start3DArrayAddr,
+ "scissor_array" / Start3DArrayAddr,
+ "visibility_result_buffer" / Int64ul,
+ "unk_118" / Int64ul,
+ "unk_120" / Array(37, Int64ul),
+ "unk_reload_pipeline" / Start3DClearPipelineBinding,
+ "unk_258" / Int64ul,
+ "unk_260" / Int64ul,
+ "unk_268" / Int64ul,
+ "unk_270" / Int64ul,
+ "reload_pipeline" / Start3DClearPipelineBinding,
+ "depth_flags" / Int64ul, # 0x40000 - has stencil 0x80000 - has depth
+ "unk_290" / Int64ul,
+ "depth_buffer_ptr1" / Int64ul,
+ "unk_2a0" / Int64ul,
+ "unk_2a8" / Int64ul,
+ "depth_buffer_ptr2" / Int64ul,
+ "depth_buffer_ptr3" / Int64ul,
+ "depth_aux_buffer_ptr" / Int64ul,
+ "stencil_buffer_ptr1" / Int64ul,
+ "unk_2d0" / Int64ul,
+ "unk_2d8" / Int64ul,
+ "stencil_buffer_ptr2" / Int64ul,
+ "stencil_buffer_ptr3" / Int64ul,
+ "stencil_aux_buffer_ptr" / Int64ul,
+ "unk_2f8" / Array(2, Int64ul),
+ "aux_fb_unk0" / Int32ul,
+ "unk_30c" / Int32ul,
+ "aux_fb" / AuxFBInfo,
+ "unk_320_padding" / HexDump(Bytes(0x10)),
+ "unk_partial_store_pipeline" / Start3DStorePipelineBinding,
+ "partial_store_pipeline" / Start3DStorePipelineBinding,
+ "depth_clear_val2" / Float32l,
+ "stencil_clear_val2" / Int8ul,
+ "unk_375" / Int8ul,
+ "unk_376" / Int16ul,
+ "unk_378" / Int32ul,
+ "unk_37c" / Int32ul,
+ "unk_380" / Int64ul,
+ "unk_388" / Int64ul,
+ Ver("V >= V13_0B4", "unk_390_0" / Int64ul),
+ "depth_dimensions" / Int64ul,
+ )
+
+class Start3DStruct2(ConstructClass):
+ subcon = Struct(
+ "unk_0" / Int64ul,
+ "clear_pipeline" / Start3DClearPipelineBinding,
+ "unk_18" / Int64ul,
+ "scissor_array" / Int64ul,
+ "depth_bias_array" / Int64ul,
+ "aux_fb" / AuxFBInfo,
+ "depth_dimensions" / Int64ul,
+ "visibility_result_buffer" / Int64ul,
+ "depth_flags" / Int64ul, # 0x40000 - has stencil 0x80000 - has depth
+ Ver("G >= G14", "unk_58_g14_0" / Int64ul),
+ Ver("G >= G14", "unk_58_g14_8" / Int64ul),
+ "depth_buffer_ptr1" / Int64ul,
+ "depth_buffer_ptr2" / Int64ul,
+ "stencil_buffer_ptr1" / Int64ul,
+ "stencil_buffer_ptr2" / Int64ul,
+ Ver("G >= G14", "unk_68_g14_0" / HexDump(Bytes(0x20))),
+ "unk_78" / Array(4, Int64ul),
+ "depth_aux_buffer_ptr1" / Int64ul,
+ "unk_a0" / Int64ul,
+ "depth_aux_buffer_ptr2" / Int64ul,
+ "unk_b0" / Int64ul,
+ "stencil_aux_buffer_ptr1" / Int64ul,
+ "unk_c0" / Int64ul,
+ "stencil_aux_buffer_ptr2" / Int64ul,
+ "unk_d0" / Int64ul,
+ "tvb_tilemap" / Int64ul,
+ "tvb_heapmeta_addr" / Int64ul,
+ "unk_e8" / Int64ul,
+ "tvb_heapmeta_addr2" / Int64ul,
+ "unk_f8" / Int64ul,
+ "aux_fb_ptr" / Int64ul,
+ "unk_108" / Array(6, Int64ul),
+ "pipeline_base" / Int64ul,
+ "unk_140" / Int64ul,
+ "unk_148" / Int64ul,
+ "unk_150" / Int64ul,
+ "unk_158" / Int64ul,
+ "unk_160" / Int64ul,
+ Ver("G < G14", "unk_168_padding" / HexDump(Bytes(0x1d8))),
+ Ver("G >= G14", "unk_198_padding" / HexDump(Bytes(0x1a8))),
+ Ver("V < V13_0B4", ZPadding(8)),
+ )
+
+class BufferThing(ConstructClass):
+ subcon = Struct(
+ "unk_0" / Int64ul,
+ "unk_8" / Int64ul,
+ "unk_10" / Int64ul,
+ "unkptr_18" / Int64ul,
+ "unk_20" / Int32ul,
+ "bm_misc_addr" / Int64ul,
+ "bm_misc" / ROPointer(this.bm_misc_addr, BufferManagerMisc),
+ "unk_2c" / Int32ul,
+ "unk_30" / Int64ul,
+ "unk_38" / Int64ul,
+ )
+
+class Start3DStruct6(ConstructClass):
+ subcon = Struct(
+ "tvb_overflow_count" / Int64ul,
+ "unk_8" / Int32ul,
+ "unk_c" / Int32ul,
+ "unk_10" / Int32ul,
+ "encoder_id" / Int64ul,
+ "unk_1c" / Int32ul,
+ "unknown_buffer" / Int64ul,
+ "unk_28" / Int64ul,
+ "unk_30" / Int32ul,
+ "unk_34" / Int64ul,
+ )
+
+class Start3DStruct7(ConstructClass):
+ subcon = Struct(
+ "unk_0" / Int64ul,
+ "stamp1_addr" / WrappedPointer, # same contents as below
+ "stamp1" / ROPointer(this.stamp1_addr.value, StampCounter),
+ "stamp2_addr" / WrappedPointer, # same as FinalizeComputeCmd.stamp - some kind of fence/token
+ "stamp2" / ROPointer(this.stamp2_addr.value, StampCounter),
+ "stamp_value" / Int32ul,
+ "ev_3d" / Int32ul,
+ "evctl_index" / Int32ul,
+ "unk_24" / Int32ul,
+ "uuid" / Int32ul,
+ "prev_stamp_value" / Int32ul,
+ "unk_30" / Int32ul,
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.stamp1_addr = StampCounter()
+ self.stamp2_addr = StampCounter()
+
+class Attachment(ConstructClass):
+ subcon = Struct(
+ "address" / Int64ul,
+ "size" / Int32ul,
+ "unk_c" / Int16ul,
+ "unk_e" / Int16ul,
+ )
+
+ def __init__(self, addr, size, unk_c, unk_e):
+ self.address = addr
+ self.size = size
+ self.unk_c = unk_c
+ self.unk_e = unk_e
+
+class Start3DCmd(ConstructClass):
+ subcon = Struct( # 0x194 bytes''''
+ "magic" / Const(0x24, Int32ul),
+ "struct1_addr" / Int64ul, # empty before run. Output? WorkCommand3D + 0x3c0
+ "struct1" / ROPointer(this.struct1_addr, Start3DStruct1),
+ "struct2_addr" / Int64ul, # ?? WorkCommand3D + 0x78
+ "struct2" / ROPointer(this.struct2_addr, Start3DStruct2),
+ "buf_thing_addr" / Int64ul,
+ "buf_thing" / ROPointer(this.buf_thing_addr, BufferThing),
+ "stats_ptr" / Int64ul,
+ "busy_flag_ptr" / Int64ul, # 4 bytes
+ "struct6_addr" / Int64ul, # 0x3c bytes
+ "struct6" / ROPointer(this.struct6_addr, Start3DStruct6),
+ "struct7_addr" / Int64ul, # 0x34 bytes
+ "struct7" / ROPointer(this.struct7_addr, Start3DStruct7),
+ "cmdqueue_ptr" / Int64ul, # points back to the CommandQueueInfo that this command came from
+ "workitem_ptr" / Int64ul, # points back at the WorkItem that this command came from
+ "context_id" / Int32ul,
+ "unk_50" / Int32ul,
+ "event_generation" / Int32ul,
+ "buffer_mgr_slot" / Int32ul,
+ "unk_5c" / Int32ul,
+ "prev_stamp_value" / Int64ul, # 0
+ "unk_68" / Int32ul, # 0
+ "unk_buf_ptr" / Int64ul,
+ "unk_buf2_ptr" / Int64ul, # 0x18 bytes
+ "unk_7c" / Int32ul,
+ "unk_80" / Int32ul,
+ "unk_84" / Int32ul,
+ "uuid" / Int32ul, # uuid for tracking
+ "attachments" / Array(16, Attachment),
+ "num_attachments" / Int32ul,
+ "unk_190" / Int32ul,
+ Ver("V >= V13_0B4", "unk_194" / Int64ul),
+ Ver("V >= V13_0B4", "unkptr_19c" / Int64ul),
+ )
+
+
+class Finalize3DCmd(ConstructClass):
+ subcon = Struct( # 0x9c bytes
+ "magic" / Const(0x25, Int32ul),
+ "uuid" / Int32ul, # uuid for tracking
+ "unk_8" / Int32ul, # 0
+ "stamp_addr" / Int64ul,
+ "stamp" / ROPointer(this.stamp_addr, StampCounter),
+ "stamp_value" / Int32ul,
+ "unk_18" / Int32ul,
+ "buf_thing_addr" / Int64ul,
+ "buf_thing" / ROPointer(this.buf_thing_addr, BufferThing),
+ "buffer_mgr_addr" / Int64ul,
+ "buffer_mgr" / ROPointer(this.buffer_mgr_addr, BufferManagerInfo),
+ "unk_2c" / Int64ul, # 1
+ "stats_ptr" / Int64ul,
+ "struct7_addr" / Int64ul,
+ "struct7" / ROPointer(this.struct7_addr, Start3DStruct7),
+ "busy_flag_ptr" / Int64ul,
+ "cmdqueue_ptr" / Int64ul,
+ "workitem_ptr" / Int64ul,
+ "unk_5c" / Int64ul,
+ "unk_buf_ptr" / Int64ul, # Same as Start3DCmd.unkptr_6c
+ "unk_6c" / Int64ul, # 0
+ "unk_74" / Int64ul, # 0
+ "unk_7c" / Int64ul, # 0
+ "unk_84" / Int64ul, # 0
+ "unk_8c" / Int64ul, # 0
+ Ver("G >= G14", "unk_8c_g14" / Int64ul),
+ "restart_branch_offset" / Int32sl,
+ "unk_98" / Int32ul, # 1
+ Ver("V >= V13_0B4", "unk_9c" / HexDump(Bytes(0x10))),
+ )
+
+class TilingParameters(ConstructClass):
+ subcon = Struct(
+ "size1" / Int32ul,
+ "unk_4" / Int32ul,
+ "unk_8" / Int32ul,
+ "x_max" / Dec(Int16ul),
+ "y_max" / Dec(Int16ul),
+ "tile_count" / Int32ul,
+ "x_blocks" / Int32ul,
+ "y_blocks" / Int32ul,
+ "size2" / Int32ul,
+ "size3" / Int32ul,
+ "unk_24" / Int32ul,
+ "unk_28" / Int32ul,
+ )
+
+class StartTACmdStruct2(ConstructClass):
+ subcon = Struct(
+ "unk_0" / Hex(Int64ul),
+ "unk_8" / Hex(Int32ul),
+ "unk_c" / Hex(Int32ul),
+ "tvb_tilemap" / Hex(Int64ul),
+ Ver("G < G14", "tvb_cluster_tilemaps" / Hex(Int64ul)),
+ "tpc" / Hex(Int64ul),
+ "tvb_heapmeta_addr" / Hex(Int64ul), # like Start3DStruct2.tvb_end_addr with bit 63 set?
+ "iogpu_unk_54" / Int32ul,
+ "iogpu_unk_55" / Int32ul,
+ "iogpu_unk_56" / Int64ul,
+ Ver("G < G14", "tvb_cluster_meta1" / Int64ul),
+ "unk_48" / Int64ul,
+ "unk_50" / Int64ul,
+ "tvb_heapmeta_addr2" / Int64ul,
+ Ver("G < G14", "unk_60" / Int64ul),
+ Ver("G < G14", "core_mask" / Int64ul),
+ "iogpu_deflake_1" / Int64ul,
+ "iogpu_deflake_2" / Int64ul,
+ "unk_80" / Int64ul,
+ "iogpu_deflake_3" / Int64ul, # bit 50 set
+ "encoder_addr" / Int64ul,
+ Ver("G < G14", "tvb_cluster_meta2" / Int64ul),
+ Ver("G < G14", "tvb_cluster_meta3" / Int64ul),
+ Ver("G < G14", "tiling_control" / Int64ul),
+ "unk_b0" / Array(6, Hex(Int64ul)),
+ "pipeline_base" / Int64ul,
+ Ver("G < G14", "tvb_cluster_meta4" / Int64ul),
+ Ver("G < G14", "unk_f0" / Int64ul),
+ "unk_f8" / Int64ul,
+ "unk_100" / Array(3, Hex(Int64ul)),
+ "unk_118" / Int32ul,
+ Ver("G >= G14", Padding(8 * 9)),
+ )
+
+class StartTACmdStruct3(ConstructClass):
+ subcon = Struct(
+ "unk_480" / Array(6, Int32ul),
+ "unk_498" / Int64ul,
+ "unk_4a0" / Int32ul,
+ "iogpu_deflake_1" / Int64ul,
+ "unk_4ac" / Int32ul,
+ "unk_4b0" / Int64ul,
+ "unk_4b8" / Int32ul,
+ "unk_4bc" / Int64ul,
+ "unk_4c4_padding" / HexDump(Bytes(0x48)),
+ "unk_50c" / Int32ul,
+ "unk_510" / Int64ul,
+ "unk_518" / Int64ul,
+ "unk_520" / Int64ul,
+ "unk_528" / Int32ul,
+ "unk_52c" / Int32ul,
+ "unk_530" / Int32ul,
+ "encoder_id" / Int32ul,
+ "unk_538" / Int32ul,
+ "unk_53c" / Int32ul,
+ "unknown_buffer" / Int64ul,
+ "unk_548" / Int64ul,
+ "unk_550" / Array(6, Int32ul),
+ "stamp1_addr" / WrappedPointer, # same contents as below
+ "stamp1" / ROPointer(this.stamp1_addr.value, StampCounter),
+ "stamp2_addr" / WrappedPointer, # same as FinalizeComputeCmd.stamp - some kind of fence/token
+ "stamp2" / ROPointer(this.stamp2_addr.value, StampCounter),
+ "stamp_value" / Int32ul,
+ "ev_ta" / Int32ul,
+ "evctl_index" / Int32ul, # 0-3
+ "unk_584" / Int32ul,
+ "uuid2" / Int32ul,
+ "prev_stamp_value" / Int32ul,
+ "unk_590" / Int32ul,
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.stamp1_addr = StampCounter()
+ self.stamp2_addr = StampCounter()
+
+class StartTACmd(ConstructClass):
+ subcon = Struct(
+ "magic" / Const(0x22, Int32ul),
+ "tiling_params_addr" / Int64ul,
+ "tiling_params" / ROPointer(this.tiling_params_addr, TilingParameters),
+ "struct2_addr" / Int64ul,
+ "struct2" / ROPointer(this.struct2_addr, StartTACmdStruct2),
+ "buffer_mgr_addr" / Int64ul,
+ "buffer_mgr" / ROPointer(this.buffer_mgr_addr, BufferManagerInfo),
+ "buf_thing_addr" / Int64ul,
+ "buf_thing" / ROPointer(this.buf_thing_addr, BufferThing),
+ "stats_ptr" / Int64ul,
+ "cmdqueue_ptr" / Int64ul,
+ "context_id" / Int32ul,
+ "unk_38" / Int32ul,
+ "event_generation" / Int32ul,
+ "buffer_mgr_slot" / Int64ul,
+ "unk_48" / Int64ul,
+ "unk_50" / Int32ul,
+ "struct3_addr" / Int64ul,
+ "struct3" / ROPointer(this.struct3_addr, StartTACmdStruct3),
+ "unkptr_5c" / Int64ul,
+ "unk_5c" / ROPointer(this.unkptr_5c, HexDump(Bytes(0x18))),
+ "unk_64" / Int32ul,
+ "unk_68" / Int32ul,
+ "uuid" / Int32ul,
+ "unk_70" / Int32ul,
+ "unk_74" / Array(29, Int64ul),
+ "unk_15c" / Int32ul,
+ "unk_160" / Int64ul,
+ "unk_168" / Int32ul,
+ "unk_16c" / Int32ul,
+ "unk_170" / Int64ul,
+ "unk_178" / Int32ul,
+ Ver("V >= V13_0B4", "unk_17c" / Int32ul),
+ Ver("V >= V13_0B4", "unkptr_180" / Int64ul),
+ Ver("V >= V13_0B4", "unk_188" / Int32ul),
+ )
+
+class FinalizeTACmd(ConstructClass):
+ subcon = Struct(
+ "magic" / Const(0x23, Int32ul),
+ "buf_thing_addr" / Int64ul,
+ "buf_thing" / ROPointer(this.buf_thing_addr, BufferThing),
+ "buffer_mgr_addr" / Int64ul,
+ "buffer_mgr" / ROPointer(this.buffer_mgr_addr, BufferManagerInfo),
+ "stats_ptr" / Int64ul,
+ "cmdqueue_ptr" / Int64ul, #
+ "context_id" / Int32ul,
+ "unk_28" / Int32ul,
+ "struct3_addr" / Int64ul,
+ "struct3" / ROPointer(this.struct3_addr, StartTACmdStruct3),
+ "unk_34" / Int32ul,
+ "uuid" / Int32ul,
+ "stamp_addr" / Int64ul,
+ "stamp" / ROPointer(this.stamp_addr, StampCounter),
+ "stamp_value" / Int32ul,
+ "unk_48" / Int64ul,
+ "unk_50" / Int32ul,
+ "unk_54" / Int32ul,
+ "unk_58" / Int64ul,
+ "unk_60" / Int32ul,
+ "unk_64" / Int32ul,
+ "unk_68" / Int32ul,
+ Ver("G >= G14", "unk_6c_g14" / Int64ul),
+ "restart_branch_offset" / Int32sl,
+ "unk_70" / Int32ul,
+ Ver("V >= V13_0B4", "unk_74" / HexDump(Bytes(0x10))),
+ )
+
+class ComputeArgs(ConstructClass):
+ subcon = Struct(
+ unk = Bytes(0x7fa0),
+ arg_buffers = Array(8, Int64ul),
+ threadgroups_per_grid_addr = Int64ul,
+ threads_per_threadgroup_addr = Int64ul,
+ )
+
+class ComputeInfo(ConstructClass):
+ # Only the cmdlist and pipelinebase and cmdlist fields are strictly needed to launch a basic
+ # compute shader.
+ subcon = Struct( # 0x1c bytes
+ "args" / Int64ul, # ComputeArgs
+ "cmdlist" / Int64ul, # CommandList from userspace
+ "unkptr_10" / Int64ul, # size 8, null
+ "unkptr_18" / Int64ul, # size 8, null
+ "unkptr_20" / Int64ul, # size 8, null
+ "unkptr_28" / Int64ul, #
+ "pipeline_base" / Int64ul, # 0x11_00000000: Used for certain "short" pointers like pipelines (and shaders?)
+ "unk_38" / Int64ul, # always 0x8c60.
+ "unk_40" / Int32ul, # 0x41
+ "unk_44" / Int32ul, # 0
+ "unkptr_48" / Int64ul, # related to threadgroups / thread layout
+ "unk_50" / Int32ul, # 0x40 - Size?
+ "unk_54" / Int32ul, # 0
+ "unk_58" / Int32ul, # 1
+ "unk_5c" / Int32ul, # 0
+ "unk_60" / Int32ul, # 0x1c
+ )
+
+# Related to "IOGPU Misc"
+class ComputeInfo2(ConstructClass):
+ subcon = Struct(
+ unk_0 = HexDump(Bytes(0x24)),
+ unkptr_24 = Int64ul, # equal to args
+ unkptr_2c = Int64ul, # points at end of cmdlist?
+ unk_34 = HexDump(Bytes(0x38)),
+ encoder_id = Int32ul,
+ unk_70 = Int32ul,
+ unk_74 = Int32ul,
+ unknown_buffer = Int64ul,
+ unk_80 = Int32ul,
+ unk_84 = Int32ul,
+ unk_88 = Int32ul,
+ stamp1_addr = Int64ul, # same contents as below
+ stamp1 = ROPointer(this.stamp1_addr, Hex(Int32ul)),
+ stamp2_addr = Int64ul, # same as FinalizeComputeCmd.stamp - some kind of fence/token
+ stamp2 = ROPointer(this.stamp2_addr, Hex(Int32ul)),
+ stamp_value = Int32ul,
+ unk_a0 = Int32ul,
+ unk_a4 = Int32ul,
+ unk_a8 = Int32ul,
+ uuid = Int32ul,
+ unk_b0 = Int32ul,
+ )
+
+class StartComputeCmd(ConstructClass):
+ subcon = Struct( # 0x154 bytes''''
+ "magic" / Const(0x29, Int32ul),
+ "unkptr_4" / Int64ul, # empty: WorkCommandCP + 0x14, size: 0x54
+ "computeinfo_addr" / Int64ul, # List of userspace VAs: WorkCommandCP + 0x68
+ "computeinfo" / ROPointer(this.computeinfo_addr, ComputeInfo),
+ "unkptr_14" / Int64ul, # In gpu-asc's heap? Did this pointer come from the gfx firmware?
+ "cmdqueue_ptr" / Int64ul, # points back to the submitinfo that this command came from
+ "context_id" / Int32ul, # 4
+ "unk_28" / Int32ul, # 1
+ "unk_2c" / Int32ul, # 0
+ "unk_30" / Int32ul,
+ "unk_34" / Int32ul,
+ "unk_38" / Int32ul,
+ "computeinfo2_addr" / Int64ul, # WorkCommandCP + 0x1f4
+ "computeinfo2" / ROPointer(this.computeinfo2_addr, ComputeInfo2),
+ "unk_44" / Int32ul,
+ "uuid" / Int32ul, # uuid for tracking?
+ "padding" / Bytes(0x154 - 0x4c),
+ )
+
+class FinalizeComputeCmd(ConstructClass):
+ subcon = Struct( # 0x64 bytes''''
+ "magic" / Const(0x2a, Int32ul),
+ "unkptr_4" / Int64ul, # same as ComputeStartCmd.unkptr_14
+ "cmdqueue_ptr" / Int64ul, # points back to the submitinfo
+ "unk_14" / Int32ul, # Context ID?
+ "unk_18" / Int32ul,
+ "unkptr_1c" / Int64ul, # same as ComputeStartCmd.unkptr_3c
+ "unk_24" / Int32ul,
+ "uuid" / Int32ul, # uuid for tracking?
+ "stamp" / Int64ul,
+ "stamp_value" / Int32ul, # Gets written to unkptr_2c (after stamp?)
+ "unk_38" / Int32ul,
+ "unk_3c" / Int32ul,
+ "unk_40" / Int32ul,
+ "unk_44" / Int32ul,
+ "unk_48" / Int32ul,
+ "unk_4c" / Int32ul,
+ "unk_50" / Int32ul,
+ "unk_54" / Int32ul,
+ "unk_58" / Int32ul,
+ Ver("G >= G14", "unk_5c_g14" / Int64ul),
+ "restart_branch_offset" / Int32sl, # realative offset from start of Finalize to StartComputeCmd
+ "unk_60" / Int32ul,
+ )
+
+class EndCmd(ConstructClass):
+ subcon = Struct(
+ "magic" / Const(0x18, Int8ul),
+ "unk_1" / Int8ul,
+ "unk_2" / Int8ul,
+ "flags" / Int8ul,
+ )
+
+ def __init__(self):
+ super().__init__()
+ self.unk_1 = 0
+ self.unk_2 = 0
+ self.flags = 0x40
+
+class TimestampCmd(ConstructClass):
+ subcon = Struct( # 0x34 bytes
+ "magic" / Const(0x19, Int8ul),
+ "unk_1" / Int8ul,
+ "unk_2" / Int8ul,
+ "unk_3" / Int8ul, # Sometimes 0x80
+ # all these pointers point to 0xfa0... addresses. Might be where the timestamp should be writen?
+ "ts0_addr" / Int64ul,
+ "ts0" / ROPointer(this.ts0_addr, TimeStamp),
+ "ts1_addr" / Int64ul,
+ "ts1" / ROPointer(this.ts1_addr, TimeStamp),
+ "ts2_addr" / Int64ul,
+ "ts2" / ROPointer(this.ts2_addr, TimeStamp),
+ "cmdqueue_ptr" / Int64ul,
+ "unk_24" / Int64ul,
+ Ver("V >= V13_0B4", "unkptr_2c_0" / Int64ul),
+ "uuid" / Int32ul,
+ "unk_30_padding" / Int32ul,
+ )
+
+class WaitForInterruptCmd(ConstructClass):
+ subcon = Struct(
+ "magic" / Const(0x01, Int8ul),
+ "unk_1" / Int8ul,
+ "unk_2" / Int8ul,
+ "unk_3" / Int8ul,
+ )
+
+ def __init__(self, unk_1, unk_2, unk_3):
+ super().__init__()
+ self.unk_1 = unk_1
+ self.unk_2 = unk_2
+ self.unk_3 = unk_3
+
+class NopCmd(ConstructClass):
+ # This doesn't exist
+ subcon = Struct(
+ "magic" / Const(0x00, Int32ul),
+ )
+
+ def __str__(self) -> str:
+ return "Nop"
+
+
+class MicroSequence(ConstructValueClass):
+ subcon = RepeatUntil(lambda obj, lst, ctx: lst[-1].cmdid == 0x18,
+ Struct(
+ "cmdid" / Peek(Int8ul),
+ "cmd" / Switch(this.cmdid, {
+ 0x01: WaitForInterruptCmd,
+ 0x18: EndCmd,
+ 0x19: TimestampCmd,
+ 0x22: StartTACmd,
+ 0x23: FinalizeTACmd,
+ 0x24: Start3DCmd,
+ 0x25: Finalize3DCmd,
+ 0x29: StartComputeCmd,
+ 0x2a: FinalizeComputeCmd,
+ }, default=Error)
+ )
+ )
+
+ def __str__(self):
+ s = "{\n"
+ for cmd in self.value:
+ s += str(cmd.cmd) + '\n'
+ if isinstance(cmd.cmd, EndCmd):
+ s += "}\n"
+ break
+ else:
+ s += "?\n"
+ return s
+
+__all__.extend(k for k, v in globals().items()
+ if (callable(v) or isinstance(v, type)) and v.__module__ == __name__)
diff --git a/tools/proxyclient/m1n1/fw/aop/__init__.py b/tools/proxyclient/m1n1/fw/aop/__init__.py
new file mode 100644
index 0000000..6828232
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/aop/__init__.py
@@ -0,0 +1,31 @@
+# SPDX-License-Identifier: MIT
+from .bootargs import ASCArgumentSection
+
+class AOPBase:
+ def __init__(self, u, adtnode):
+ self.fw_base, self.fw_len = adtnode.get_reg(2)
+ if u.adt["arm-io"].compatible[0] == "arm-io,t6000":
+ # argh
+ self.fw_base -= 0x2_0000_0000
+
+ @property
+ def _bootargs_span(self):
+ base = self.fw_base + self.u.proxy.read32(self.fw_base + 0x224)
+ length = self.u.proxy.read32(self.fw_base + 0x228)
+
+ return (base, length)
+
+ def read_bootargs(self):
+ blob = self.u.proxy.iface.readmem(*self._bootargs_span)
+ return ASCArgumentSection(blob)
+
+ def write_bootargs(self, args):
+ base, _ = self._bootargs_span
+ self.u.proxy.iface.writemem(base, args.to_bytes())
+
+ def update_bootargs(self, keyvals):
+ args = self.read_bootargs()
+ args.update(keyvals)
+ self.write_bootargs(args)
+
+__all__ = ["ASCArgumentSection", "AOPBase"]
diff --git a/tools/proxyclient/m1n1/fw/aop/bootargs.py b/tools/proxyclient/m1n1/fw/aop/bootargs.py
new file mode 100644
index 0000000..100e4f3
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/aop/bootargs.py
@@ -0,0 +1,64 @@
+# SPDX-License-Identifier: MIT
+
+class ASCArgumentSection:
+ def __init__(self, bytes_):
+ self.blob = bytearray(bytes_)
+ self.index = self.build_index()
+
+ def build_index(self):
+ off = 0
+ fields = []
+ while off < len(self.blob):
+ snip = self.blob[off:]
+ key = snip[0:4]
+ length = int.from_bytes(snip[4:8], byteorder='little')
+ fields.append((key.decode('ascii'), (off + 8, length)))
+ off += 8 + length
+
+ if off > len(self.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):
+ for key, val in self.items():
+ print(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)
diff --git a/tools/proxyclient/m1n1/fw/aop/ipc.py b/tools/proxyclient/m1n1/fw/aop/ipc.py
new file mode 100644
index 0000000..d26315d
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/aop/ipc.py
@@ -0,0 +1,365 @@
+from enum import IntEnum
+from construct import *
+from io import BytesIO
+
+from m1n1.utils import FourCC, chexdump
+from m1n1.constructutils import ZPadding
+from m1n1.fw.afk.epic import EPICCmd, EPICCategory
+
+
+EPICSubHeaderVer2 = Struct(
+ "length" / Int32ul,
+ "version" / Default(Int8ul, 2),
+ "category" / EPICCategory,
+ "type" / Hex(Int16ul),
+ "timestamp" / Default(Int64ul, 0),
+ "unk1" / Default(Hex(Int32ul), 0),
+ "unk2" / Default(Hex(Int32ul), 0),
+)
+
+class AOPAudioPropKey(IntEnum):
+ IS_READY = 0x01
+
+ UNK_11 = 0x11
+ PLACEMENT = 0x1e
+ UNK_21 = 0x21
+ ORIENTATION = 0x2e
+ LOCATION_ID = 0x30
+ SERIAL_NO = 0x3e
+ VENDOR_ID = 0x5a
+ PRODUCT_ID = 0x5b
+
+ SERVICE_CONTROLLER = 0x64
+ DEVICE_COUNT = 0x65
+
+ VERSION = 0x67
+
+class EPICCall:
+ @classmethod
+ def matches(cls, hdr, sub):
+ return int(sub.type) == cls.TYPE
+
+ def _args_fixup(self):
+ pass
+
+ def __init__(self, *args, **kwargs):
+ if args:
+ self.args = args[0]
+ else:
+ self.args = Container(**kwargs)
+ self._args_fixup()
+ self.rets = None
+
+ @classmethod
+ def from_stream(cls, f):
+ return cls(cls.ARGS.parse_stream(f))
+
+ def dump(self, logger=None):
+ if logger is None:
+ logger = print
+ args_fmt = [f"{k}={v}" for (k, v) in self.args.items() if k != "_io"]
+ rets_fmt = [f"{k}={v}" for (k, v) in self.rets.items() if k != "_io"]
+ logger(f"{type(self).__name__}({', '.join(args_fmt)}) -> ({', '.join(rets_fmt)})")
+
+ def read_resp(self, f):
+ self.rets = self.RETS.parse_stream(f)
+
+CALLTYPES = []
+def reg_calltype(calltype):
+ CALLTYPES.append(calltype)
+ return calltype
+
+@reg_calltype
+class GetHIDDescriptor(EPICCall):
+ TYPE = 0x1
+ ARGS = Struct(
+ "blank" / Const(0x0, Int32ul),
+ )
+ RETS = Struct(
+ "retcode" / Default(Hex(Int32ul), 0),
+ "descriptor" / HexDump(GreedyBytes),
+ )
+
+@reg_calltype
+class GetProperty(EPICCall):
+ TYPE = 0xa
+ ARGS = Struct(
+ "blank" / Const(0x0, Int32ul),
+ "key" / Enum(Int32ul, AOPAudioPropKey),
+ )
+ RETS = Struct(
+ #"blank" / Const(0x0, Int32ul),
+ "value" / GreedyBytes,
+ )
+
+@reg_calltype
+class WrappedCall(EPICCall):
+ SUBCLASSES = {}
+ TYPE = 0x20
+ HDR = Struct(
+ "blank" / Const(0x0, Int32ul),
+ "unk1" / Hex(Const(0xffffffff, Int32ul)),
+ "calltype" / Hex(Int32ul),
+ "blank2" / ZPadding(16),
+ "pad" / Hex(Int32ul),
+ "len" / Hex(Int64ul),
+ "residue" / HexDump(GreedyBytes),
+ )
+
+ @classmethod
+ def from_stream(cls, f):
+ payload = f.read()
+ subsub = cls.HDR.parse(payload)
+ calltype = int(subsub.calltype)
+ subcls = cls.SUBCLASSES.get(calltype, None)
+ if subcls is None:
+ raise ValueError(f"unknown calltype {calltype:#x}")
+ return subcls(subcls.ARGS.parse(payload))
+
+ @classmethod
+ def reg_subclass(cls, cls2):
+ cls.SUBCLASSES[int(cls2.CALLTYPE)] = cls2
+ return cls2
+
+ @classmethod
+ def matches(cls, hdr, sub):
+ return sub.category == EPICCategory.NOTIFY and sub.type == cls.TYPE
+
+ def check_retcode(self):
+ if self.rets.retcode:
+ self.dump()
+ raise ValueError(f"retcode {self.rets.retcode} in {str(type(self))} (call dumped, see above)")
+
+@WrappedCall.reg_subclass
+class AttachDevice(WrappedCall):
+ CALLTYPE = 0xc3_00_00_02
+ ARGS = Struct(
+ "blank" / Const(0x0, Int32ul),
+ "unk1" / Hex(Const(0xffffffff, Int32ul)),
+ "calltype" / Hex(Const(0xc3000002, Int32ul)),
+ "blank2" / ZPadding(16),
+ "pad" / Padding(4),
+ "len" / Hex(Const(0x2c, Int64ul)),
+ "devid" / FourCC,
+ "pad" / Padding(4),
+ )
+ RETS = Struct(
+ "retcode" / Default(Hex(Int32ul), 0),
+ "unk" / HexDump(GreedyBytes),
+ )
+
+@WrappedCall.reg_subclass
+class ProbeDevice(WrappedCall):
+ CALLTYPE = 0xc3_00_00_01
+ ARGS = Struct(
+ "blank" / Const(0x0, Int32ul),
+ "unk1" / Hex(Const(0xffffffff, Int32ul)),
+ "calltype" / Hex(Const(0xc3000001, Int32ul)),
+ "blank2" / ZPadding(16),
+ "pad" / Padding(4),
+ "len" / Hex(Const(0x28, Int64ul)),
+ "devno" / Int32ul,
+ )
+ RETS = Struct(
+ "retcode" / Default(Hex(Int32ul), 0),
+ "devid" / FourCC,
+ "blank2" / Const(0x0, Int32ul),
+ "unk1" / Const(8, Int32ul),
+ "blank3" / Const(0x0, Int32ul),
+ "unk2" / Hex(Const(0x01_0d_1c_20, Int32ul)),
+ "blank4" / Const(0x0, Int32ul),
+ "remainder" / HexDump(GreedyBytes),
+ )
+
+PDMConfig = Struct(
+ "unk1" / Int32ul,
+ "clockSource" / FourCC,
+ "pdmFrequency" / Int32ul,
+ "unk3_clk" / Int32ul,
+ "unk4_clk" / Int32ul,
+ "unk5_clk" / Int32ul,
+ "channelPolaritySelect" / Hex(Int32ul),
+ "unk7" / Hex(Int32ul),
+ "unk8" / Hex(Int32ul),
+ "unk9" / Hex(Int16ul),
+ "ratios" / Struct(
+ "r1" / Int8ul,
+ "r2" / Int8ul,
+ "r3" / Int8ul,
+ "pad" / Default(Int8ul, 0),
+ ),
+ "filterLengths" / Hex(Int32ul),
+ "coeff_bulk" / Int32ul,
+ #"coefficients" / Struct(
+ # "c1" / Int32sl[this._.ratios.r3 * 4 + 4],
+ # "c2" / Int32sl[this._.ratios.r2 * 4 + 4],
+ # "c3" / Int32sl[this._.ratios.r1 * 4 + 4],
+ #),
+ #"junk" / Padding(
+ # this.coeff_bulk * 4 - 48 \
+ # - (this.ratios.r1 + this.ratios.r2 + this.ratios.r3) * 16
+ #),
+ "coefficients" / Int32sl[
+ (this.ratios.r1 + this.ratios.r2 + this.ratios.r3) * 4 + 12
+ ],
+ "junk" / Padding(
+ lambda this: max(0,
+ this.coeff_bulk * 4 - 48 \
+ - (this.ratios.r1 + this.ratios.r2 + this.ratios.r3) * 16
+ )
+ ),
+ "unk10" / Int32ul, # maybe
+ "micTurnOnTimeMs" / Int32ul,
+ "blank" / ZPadding(16),
+ "unk11" / Int32ul,
+ "micSettleTimeMs" / Int32ul,
+ "blank2" / ZPadding(69),
+)
+
+DecimatorConfig = Struct(
+ "latency" / Int32ul,
+ "ratios" / Struct(
+ "r1" / Int8ul,
+ "r2" / Int8ul,
+ "r3" / Int8ul,
+ "pad" / Default(Int8ul, 0),
+ ),
+ "filterLengths" / Hex(Int32ul),
+ "coeff_bulk" / Int32ul,
+ "coefficients" / Int32sl[
+ (this.ratios.r1 + this.ratios.r2 + this.ratios.r3) * 4 + 12
+ ],
+ "junk" / Padding(
+ lambda this: max(0,
+ this.coeff_bulk * 4 - 48 \
+ - (this.ratios.r1 + this.ratios.r2 + this.ratios.r3) * 16
+ )
+ ),
+)
+
+PowerSetting = Struct(
+ "devid" / FourCC,
+ "cookie" / Int32ul,
+ "pad" / Padding(4),
+ "blank" / ZPadding(8),
+ "target_pstate" / FourCC,
+ "unk2" / Int32ul,
+ "blank2" / ZPadding(20),
+)
+
+DEVPROPS = {
+ ('hpai', 202): PowerSetting,
+ ('lpai', 202): PowerSetting,
+ ('hpai', 200): FourCC,
+ ('lpai', 200): FourCC,
+ ('pdm0', 200): PDMConfig,
+ ('pdm0', 210): DecimatorConfig,
+ ('lpai', 301): Struct(
+ "unk1" / Int32ul,
+ "unk2" / Int32ul,
+ "unk3" / Int32ul,
+ "unk4" / Int32ul,
+ ),
+}
+
+@WrappedCall.reg_subclass
+class GetDeviceProp(WrappedCall):
+ CALLTYPE = 0xc3_00_00_04
+ ARGS = Struct(
+ "blank" / Const(0x0, Int32ul),
+ "unk1" / Hex(Const(0xffffffff, Int32ul)),
+ "calltype" / Hex(Const(0xc3000004, Int32ul)),
+ "blank2" / ZPadding(16),
+ "pad" / Padding(4),
+ "len" / Hex(Const(0x30, Int64ul)),
+ "devid" / FourCC,
+ "modifier" / Int32ul,
+ "unk6" / Hex(Const(0x01, Int32ul)),
+ )
+ RETS = Struct(
+ "retcode" / Default(Hex(Int32ul), 0),
+ "len" / Optional(Int32ul),
+ "data" / Switch(lambda s: (s._params.devid, s._params.modifier),
+ DEVPROPS,
+ default=HexDump(GreedyBytes))
+ )
+
+ def read_resp(self, f):
+ self.rets = self.RETS.parse_stream(f,
+ devid=self.args.devid, modifier=self.args.modifier
+ )
+
+@WrappedCall.reg_subclass
+class SetDeviceProp(WrappedCall):
+ CALLTYPE = 0xc3_00_00_05
+ ARGS = Struct(
+ "blank" / Const(0x0, Int32ul),
+ "unk1" / Hex(Const(0xffffffff, Int32ul)),
+ "calltype" / Hex(Const(0xc3000005, Int32ul)),
+ "blank2" / ZPadding(16),
+ "pad" / Padding(4),
+ "len" / Hex(Int64ul), # len(this.data) + 0x30
+ "devid" / FourCC,
+ "modifier" / Int32ul,
+ "len2" / Hex(Int32ul), # len(this.data)
+ "data" / Switch(lambda s: (s.devid, s.modifier),
+ DEVPROPS,
+ default=HexDump(GreedyBytes))
+ )
+ RETS = Struct(
+ "retcode" / Default(Hex(Int32ul), 0),
+ "unk" / HexDump(GreedyBytes),
+ )
+
+ def _args_fixup(self):
+ data_len = len(self.ARGS.build(Container(len=0, len2=0, **self.args))) - 52
+ if 'len' not in self.args:
+ self.args.len = data_len + 0x30
+ if 'len2' not in self.args:
+ self.args.len2 = data_len
+
+@reg_calltype
+class IndirectCall(EPICCall):
+ ARGS = EPICCmd
+ RETS = EPICCmd
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.txbuf = None
+ self.rxbuf = None
+
+ @classmethod
+ def matches(cls, hdr, sub):
+ return sub.category == EPICCategory.COMMAND
+
+ def read_txbuf(self, ep):
+ cmd = self.args
+ ep.dart.invalidate_cache()
+ self.txbuf = ep.dart.ioread(0, cmd.txbuf, cmd.txlen)
+
+ # dump the command data for offline replays of traces
+ ep.log(f"===COMMAND TX DATA=== addr={cmd.txbuf:#x}")
+ chexdump(self.txbuf)
+ ep.log(f"===END DATA===")
+
+ def read_rxbuf(self, ep):
+ cmd = self.rets
+ ep.dart.invalidate_cache()
+ self.rxbuf = ep.dart.ioread(0, cmd.rxbuf, cmd.rxlen)
+
+ ep.log(f"===COMMAND RX DATA=== addr={cmd.rxbuf:#x}")
+ chexdump(self.rxbuf)
+ ep.log(f"===END DATA===")
+
+ def unwrap(self):
+ fd = BytesIO()
+ fd.write(b"\x00\x00\x00\x00")
+ fd.write(self.txbuf)
+ fd.seek(0)
+ wrapped = WrappedCall.from_stream(fd)
+ fd = BytesIO()
+ fd.write(b"\x00\x00\x00\x00")
+ fd.write(self.rxbuf)
+ fd.seek(0)
+ wrapped.read_resp(fd)
+ return wrapped
diff --git a/tools/proxyclient/m1n1/fw/asc/__init__.py b/tools/proxyclient/m1n1/fw/asc/__init__.py
new file mode 100644
index 0000000..4ffdb8b
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/asc/__init__.py
@@ -0,0 +1,125 @@
+# SPDX-License-Identifier: MIT
+from ...utils import *
+
+from .crash import ASCCrashLogEndpoint
+from .syslog import ASCSysLogEndpoint
+from .mgmt import ASCManagementEndpoint
+from .kdebug import ASCKDebugEndpoint
+from .ioreporting import ASCIOReportingEndpoint
+from .oslog import ASCOSLogEndpoint
+from .base import ASCBaseEndpoint, ASCTimeout
+from ...hw.asc import ASC
+
+__all__ = []
+
+class ASCDummyEndpoint(ASCBaseEndpoint):
+ SHORT = "dummy"
+
+class StandardASC(ASC):
+ ENDPOINTS = {
+ 0: ASCManagementEndpoint,
+ 1: ASCCrashLogEndpoint,
+ 2: ASCSysLogEndpoint,
+ 3: ASCKDebugEndpoint,
+ 4: ASCIOReportingEndpoint,
+ 8: ASCOSLogEndpoint,
+ 0xa: ASCDummyEndpoint, # tracekit
+ }
+
+ def __init__(self, u, asc_base, dart=None, stream=0):
+ super().__init__(u, asc_base)
+ self.remote_eps = set()
+ self.add_ep(0, ASCManagementEndpoint(self, 0))
+ self.dart = dart
+ self.stream = stream
+ self.eps = []
+ self.epcls = {}
+ self.dva_offset = 0
+ self.dva_size = 1 << 32
+ self.allow_phys = False
+
+ for cls in type(self).mro():
+ eps = getattr(cls, "ENDPOINTS", None)
+ if eps is None:
+ break
+ for k, v in eps.items():
+ if k not in self.epcls:
+ self.epcls[k] = v
+
+ def addr(self, addr):
+ return f"{addr:#x}"
+
+ def iomap(self, addr, size):
+ if self.dart is None:
+ return addr
+ dva = self.dva_offset | self.dart.iomap(self.stream, addr, size)
+
+ self.dart.invalidate_streams(1)
+ return dva
+
+ def ioalloc(self, size):
+ paddr = self.u.memalign(0x4000, size)
+ dva = self.iomap(paddr, size)
+ return paddr, dva
+
+ def ioread(self, dva, size):
+ if self.allow_phys and dva < self.dva_offset or dva >= (self.dva_offset + self.dva_size):
+ return self.iface.readmem(dva, size)
+
+ if self.dart:
+ return self.dart.ioread(self.stream, dva & 0xFFFFFFFFF, size)
+ else:
+ return self.iface.readmem(dva, size)
+
+ def iowrite(self, dva, data):
+ if self.allow_phys and dva < self.dva_offset or dva >= (self.dva_offset + self.dva_size):
+ return self.iface.writemem(dva, data)
+
+ if self.dart:
+ return self.dart.iowrite(self.stream, dva & 0xFFFFFFFFF, data)
+ else:
+ return self.iface.writemem(dva, data)
+
+ def iotranslate(self, dva, size):
+ if self.allow_phys and dva < self.dva_offset or dva >= (self.dva_offset + self.dva_size):
+ return [(dva, size)]
+
+ if self.dart:
+ return self.dart.iotranslate(self.stream, dva & 0xFFFFFFFFF, size)
+ else:
+ return [(dva, size)]
+
+ def start_ep(self, epno):
+ if epno not in self.epcls:
+ raise Exception(f"Unknown endpoint {epno:#x}")
+
+ epcls = self.epcls[epno]
+ ep = epcls(self, epno)
+ self.add_ep(epno, ep)
+ print(f"Starting endpoint #{epno:#x} ({ep.name})")
+ self.mgmt.start_ep(epno)
+ ep.start()
+
+ def start(self):
+ super().boot()
+ self.mgmt.start()
+ self.mgmt.wait_boot(3)
+
+ def stop(self, state=0x10):
+ for ep in list(self.epmap.values())[::-1]:
+ if ep.epnum < 0x10:
+ continue
+ ep.stop()
+ self.mgmt.stop(state=state)
+ self.epmap = {}
+ self.add_ep(0, ASCManagementEndpoint(self, 0))
+ if state < 0x10:
+ self.shutdown()
+
+ def boot(self):
+ print("Booting ASC...")
+ super().boot()
+ self.mgmt.wait_boot(1)
+
+__all__.extend(k for k, v in globals().items()
+ if (callable(v) or isinstance(v, type)) and v.__module__ == __name__)
diff --git a/tools/proxyclient/m1n1/fw/asc/base.py b/tools/proxyclient/m1n1/fw/asc/base.py
new file mode 100644
index 0000000..b10aaaf
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/asc/base.py
@@ -0,0 +1,59 @@
+# SPDX-License-Identifier: MIT
+from ...utils import *
+
+# System endpoints
+def msg_handler(message, regtype=None):
+ def f(x):
+ x.is_message = True
+ x.message = message
+ x.regtype = regtype
+ return x
+
+ return f
+
+class ASCMessage1(Register64):
+ EP = 7, 0
+
+class ASCTimeout(Exception):
+ pass
+
+class ASCBaseEndpoint:
+ BASE_MESSAGE = Register64
+ SHORT = None
+
+ def __init__(self, asc, epnum, name=None):
+ self.asc = asc
+ self.epnum = epnum
+ self.name = name or self.SHORT or f"{type(self).__name__}@{epnum:#x}"
+
+ self.msghandler = {}
+ self.msgtypes = {}
+ for name in dir(self):
+ i = getattr(self, name)
+ if not callable(i):
+ continue
+ if not getattr(i, "is_message", False):
+ continue
+ self.msghandler[i.message] = i
+ self.msgtypes[i.message] = i.regtype if i.regtype else self.BASE_MESSAGE
+
+ def handle_msg(self, msg0, msg1):
+ msg0 = self.BASE_MESSAGE(msg0)
+ handler = self.msghandler.get(msg0.TYPE, None)
+ regtype = self.msgtypes.get(msg0.TYPE, self.BASE_MESSAGE)
+
+ if handler is None:
+ return False
+ return handler(regtype(msg0.value))
+
+ def send(self, msg):
+ self.asc.send(msg, ASCMessage1(EP=self.epnum))
+
+ def start(self):
+ pass
+
+ def stop(self):
+ pass
+
+ def log(self, msg):
+ print(f"[{self.name}] {msg}")
diff --git a/tools/proxyclient/m1n1/fw/asc/crash.py b/tools/proxyclient/m1n1/fw/asc/crash.py
new file mode 100644
index 0000000..7f590c8
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/asc/crash.py
@@ -0,0 +1,248 @@
+# SPDX-License-Identifier: MIT
+from .base import *
+from ...utils import *
+from construct import *
+from ...sysreg import *
+
+class CrashLogMessage(Register64):
+ TYPE = 63, 52
+ SIZE = 51, 44
+ DVA = 43, 0
+
+CrashHeader = Struct(
+ "type" / Const("CLHE", FourCC),
+ "ver" / Int32ul,
+ "total_size" / Int32ul,
+ "flags" / Int32ul,
+ Padding(16)
+)
+
+CrashCver = Struct(
+ "uuid" / Bytes(16),
+ "version" / CString("utf8"),
+)
+
+CrashCstr = Struct(
+ "id" / Int32ul,
+ "string" / CString("utf8"),
+)
+
+CrashCtim = Struct(
+ "time" / Int64ul,
+)
+
+CrashCmbx = Struct(
+ "hdr" / Array(4, Hex(Int32ul)),
+ "type" / Int32ul,
+ "unk" / Int32ul,
+ "index" / Int32ul,
+ "messages" / GreedyRange(Struct(
+ "endpoint" / Hex(Int64ul),
+ "message" / Hex(Int64ul),
+ "timestamp" / Hex(Int32ul),
+ Padding(4),
+ )),
+)
+
+CrashCcst = Struct(
+ "task" / Int32ul,
+ "unk" / Int32ul,
+ "stack" / GreedyRange(Int64ul)
+)
+
+CrashCasC = Struct(
+ "l2c_err_sts" / Hex(Int64ul),
+ "l2c_err_adr" / Hex(Int64ul),
+ "l2c_err_inf" / Hex(Int64ul),
+ "lsu_err_sts" / Hex(Int64ul),
+ "fed_err_sts" / Hex(Int64ul),
+ "mmu_err_sts" / Hex(Int64ul)
+)
+
+CrashCrg8 = Struct(
+ "unk_0" / Int32ul,
+ "unk_4" / Int32ul,
+ "regs" / Array(31, Hex(Int64ul)),
+ "sp" / Int64ul,
+ "pc" / Int64ul,
+ "psr" / Int64ul,
+ "cpacr" / Int64ul,
+ "fpsr" / Int64ul,
+ "fpcr" / Int64ul,
+ "unk" / Array(64, Hex(Int64ul)),
+ "far" / Int64ul,
+ "unk_X" / Int64ul,
+ "esr" / Int64ul,
+ "unk_Z" / Int64ul,
+)
+
+CrashEntry = Struct(
+ "type" / FourCC,
+ Padding(4),
+ "flags" / Hex(Int32ul),
+ "len" / Int32ul,
+ "payload" / FixedSized(lambda ctx: ctx.len - 16 if ctx.type != "CLHE" else 16,
+ Switch(this.type, {
+ "Cver": CrashCver,
+ "Ctim": CrashCtim,
+ "Cmbx": CrashCmbx,
+ "Cstr": CrashCstr,
+ "Crg8": CrashCrg8,
+ "Ccst": CrashCcst,
+ "CasC": CrashCasC,
+ }, default=GreedyBytes)),
+)
+
+CrashLog = Struct(
+ "header" / CrashHeader,
+ "entries" / RepeatUntil(this.type == "CLHE", CrashEntry),
+)
+
+class CrashLogParser:
+ def __init__(self, data=None, asc=None):
+ self.asc = asc
+ if data is not None:
+ self.parse(data)
+
+ def parse(self, data):
+ self.data = CrashLog.parse(data)
+ pass
+
+ def default(self, entry):
+ print(f"# {entry.type} flags={entry.flags:#x}")
+ chexdump(entry.payload)
+ print()
+
+ def Ccst(self, entry):
+ print(f"Call stack (task {entry.payload.task}:")
+ for i in entry.payload.stack:
+ if not i:
+ break
+ print(f" - {i:#x}")
+ print()
+
+ def CasC(self, entry):
+ print(f"Async error info:")
+ print(entry.payload)
+ print()
+
+ def Cver(self, entry):
+ print(f"RTKit Version: {entry.payload.version}")
+ print()
+
+ def Crg8(self, entry):
+ print(f"Exception info:")
+
+ ctx = entry.payload
+
+ addr = self.asc.addr
+
+ spsr = SPSR(ctx.psr)
+ esr = ESR(ctx.esr)
+ elr = ctx.pc
+ far_phys = self.asc.iotranslate(ctx.far, 1)[0][0]
+ elr_phys = self.asc.iotranslate(ctx.pc, 1)[0][0]
+ sp_phys = self.asc.iotranslate(ctx.sp, 1)[0][0]
+
+ print(f" == Exception taken from {spsr.M.name} ==")
+ el = spsr.M >> 2
+ print(f" SPSR = {spsr}")
+ print(f" ELR = {addr(elr)}" + (f" (0x{elr_phys:x})" if elr_phys else ""))
+ print(f" ESR = {esr}")
+ print(f" FAR = {addr(ctx.far)}" + (f" (0x{far_phys:x})" if far_phys else ""))
+ print(f" SP = {ctx.sp:#x}" + (f" (0x{sp_phys:x})" if sp_phys else ""))
+
+ for i in range(0, 31, 4):
+ j = min(30, i + 3)
+ print(f" {f'x{i}-x{j}':>7} = {' '.join(f'{r:016x}' for r in ctx.regs[i:j + 1])}")
+
+ if elr_phys:
+ v = self.asc.p.read32(elr_phys)
+
+ print()
+ if v == 0xabad1dea:
+ print(" == Faulting code is not available ==")
+ else:
+ print(" == Faulting code ==")
+ dist = 16
+ self.asc.u.disassemble_at(elr_phys - dist * 4, (dist * 2 + 1) * 4, elr_phys)
+
+ print()
+
+ def Cstr(self, entry):
+ print(f"Message {entry.payload.id}: {entry.payload.string}")
+ print()
+
+ def Ctim(self, entry):
+ print(f"Crash time: {entry.payload.time:#x}")
+ print()
+
+ def Cmbx(self, entry):
+ print(f"Mailbox log (type {entry.payload.type}, index {entry.payload.index}):")
+ for i, msg in enumerate(entry.payload.messages):
+ print(f" #{i:3d} @{msg.timestamp:#10x} ep={msg.endpoint:#4x} {msg.message:#18x}")
+ print()
+
+ def CLHE(self, entry):
+ pass
+
+ def dump(self):
+ print("### Crash dump:")
+ print()
+ for entry in self.data.entries:
+ getattr(self, entry.type, self.default)(entry)
+
+class ASCCrashLogEndpoint(ASCBaseEndpoint):
+ SHORT = "crash"
+ BASE_MESSAGE = CrashLogMessage
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.iobuffer = None
+ self.iobuffer_dva = None
+ self.started = False
+
+ @msg_handler(0x1)
+ def Handle(self, msg):
+ if self.started:
+ return self.handle_crashed(msg)
+ else:
+ return self.handle_getbuf(msg)
+
+ def handle_getbuf(self, msg):
+ size = align(0x1000 * msg.SIZE, 0x4000)
+
+ if msg.DVA:
+ self.iobuffer_dva = msg.DVA
+ self.log(f"buf prealloc at dva {self.iobuffer_dva:#x}")
+ else:
+ self.iobuffer, self.iobuffer_dva = self.asc.ioalloc(size)
+ self.log(f"buf {self.iobuffer:#x} / {self.iobuffer_dva:#x}")
+ self.send(CrashLogMessage(TYPE=1, SIZE=size // 0x1000, DVA=self.iobuffer_dva))
+
+ self.started = True
+ return True
+
+ def crash_soft(self):
+ self.send(0x40)
+
+ def crash_hard(self):
+ self.send(0x22)
+
+ def handle_crashed(self, msg):
+ size = 0x1000 * msg.SIZE
+
+ self.log(f"Crashed!")
+ crashdata = self.asc.ioread(msg.DVA, size)
+ open("crash.bin", "wb").write(crashdata)
+ clog = CrashLogParser(crashdata, self.asc)
+ clog.dump()
+ raise Exception("ASC crashed!")
+
+ return True
+
+if __name__ == "__main__":
+ import sys
+ crashdata = open(sys.argv[1], "rb").read()
+ clog = CrashLogParser(crashdata)
+ clog.dump()
diff --git a/tools/proxyclient/m1n1/fw/asc/ioreporting.py b/tools/proxyclient/m1n1/fw/asc/ioreporting.py
new file mode 100644
index 0000000..f81b6c6
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/asc/ioreporting.py
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: MIT
+from .base import *
+from ...utils import *
+
+class IOReportingMessage(Register64):
+ TYPE = 63, 52
+
+class IOReporting_GetBuf(IOReportingMessage):
+ TYPE = 63, 52, Constant(1)
+ SIZE = 51, 44
+ DVA = 43, 0
+
+class IOReporting_Start(IOReportingMessage):
+ TYPE = 63, 52, Constant(0xc)
+
+class IOReporting_Report(IOReportingMessage):
+ TYPE = 63, 52, Constant(0x8)
+
+class ASCIOReportingEndpoint(ASCBaseEndpoint):
+ BASE_MESSAGE = IOReportingMessage
+ SHORT = "iorep"
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.iobuffer = None
+ self.iobuffer_dva = None
+
+ @msg_handler(1, IOReporting_GetBuf)
+ def GetBuf(self, msg):
+ if self.iobuffer:
+ self.log("WARNING: trying to reset iobuffer!")
+
+ self.bufsize = align(0x1000 * msg.SIZE, 0x4000)
+
+ if msg.DVA != 0:
+ self.iobuffer = self.iobuffer_dva = msg.DVA
+ self.log(f"buf {self.iobuffer:#x} / {self.iobuffer_dva:#x}")
+ else:
+ self.iobuffer, self.iobuffer_dva = self.asc.ioalloc(self.bufsize)
+ self.log(f"buf {self.iobuffer:#x} / {self.iobuffer_dva:#x}")
+ self.send(IOReporting_GetBuf(DVA=self.iobuffer_dva, SIZE=self.bufsize // 0x1000))
+
+ return True
+
+ @msg_handler(0xc, IOReporting_Start)
+ def Start(self, msg):
+ self.log("start")
+ return True
+
+ @msg_handler(8, IOReporting_Report)
+ def Init(self, msg):
+ self.log("report!")
+ buf = self.asc.iface.readmem(self.iobuffer, self.bufsize)
+ #chexdump(buf)
+ self.send(IOReporting_Report())
+ return True
diff --git a/tools/proxyclient/m1n1/fw/asc/kdebug.py b/tools/proxyclient/m1n1/fw/asc/kdebug.py
new file mode 100644
index 0000000..32a433f
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/asc/kdebug.py
@@ -0,0 +1,57 @@
+# SPDX-License-Identifier: MIT
+from .base import *
+from ...utils import *
+
+class KDebugMessage(Register64):
+ TYPE = 55, 48
+
+class KDebugGetBufMessage(KDebugMessage):
+ TYPE = 55, 48, Constant(1)
+ COUNT = 47, 0
+
+class KDebugPreallocBuf1Message(KDebugMessage):
+ TYPE = 55, 48, Constant(2)
+ DVA = 47, 12
+ FLAGS = 11, 0
+
+class KDebugPreallocBuf2Message(KDebugMessage):
+ TYPE = 55, 48, Constant(3)
+ DVA = 47, 0
+
+class KDebugSendBufMessage(KDebugMessage):
+ TYPE = 55, 48
+ DVA = 47, 0
+
+class KDebugStart(KDebugMessage):
+ TYPE = 55, 48, Constant(8)
+
+class ASCKDebugEndpoint(ASCBaseEndpoint):
+ SHORT = "kdebug"
+ BASE_MESSAGE = KDebugMessage
+
+ @msg_handler(1, KDebugGetBufMessage)
+ def GetBuf(self, msg):
+ size = align_up(msg.COUNT * 0x20, 0x4000)
+ self.iobuffer0, self.iobuffer0_iova = self.asc.ioalloc(size)
+ self.send(KDebugSendBufMessage(TYPE=1, DVA=self.iobuffer0_iova))
+
+ self.iobuffer1, self.iobuffer1_iova = self.asc.ioalloc(0x2000)
+ self.send(KDebugSendBufMessage(TYPE=2, DVA=self.iobuffer1_iova))
+ return True
+
+ @msg_handler(2, KDebugPreallocBuf1Message)
+ def SetBuf1(self, msg):
+ #self.send(KDebugSendBufMessage(TYPE=1, DVA=msg.DVA))
+ return True
+
+ @msg_handler(3, KDebugPreallocBuf2Message)
+ def SetBuf2(self, msg):
+ #self.send(KDebugSendBufMessage(TYPE=2, DVA=msg.DVA))
+ return True
+
+ def start(self):
+ self.iobuffer0 = None
+ self.iobuffer1 = None
+ self.iobuffer0_iova = None
+ self.iobuffer1_iova = None
+ self.send(KDebugStart())
diff --git a/tools/proxyclient/m1n1/fw/asc/mgmt.py b/tools/proxyclient/m1n1/fw/asc/mgmt.py
new file mode 100644
index 0000000..162fcd2
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/asc/mgmt.py
@@ -0,0 +1,144 @@
+# SPDX-License-Identifier: MIT
+import time
+
+from .base import *
+from ...utils import *
+
+## Management endpoint
+class ManagementMessage(Register64):
+ TYPE = 59, 52
+
+class Mgmt_Hello(ManagementMessage):
+ TYPE = 59, 52, Constant(1)
+ MAX_VER = 31, 16
+ MIN_VER = 15, 0
+
+class Mgmt_HelloAck(ManagementMessage):
+ TYPE = 59, 52, Constant(2)
+ MAX_VER = 31, 16
+ MIN_VER = 15, 0
+
+class Mgmt_Ping(ManagementMessage):
+ TYPE = 59, 52, Constant(3)
+
+class Mgmt_Pong(ManagementMessage):
+ TYPE = 59, 52, Constant(4)
+
+class Mgmt_StartEP(ManagementMessage):
+ TYPE = 59, 52, Constant(5)
+ EP = 39, 32
+ FLAG = 1, 0
+
+class Mgmt_SetIOPPower(ManagementMessage):
+ TYPE = 59, 52, Constant(6)
+ STATE = 15, 0
+
+class Mgmt_IOPPowerAck(ManagementMessage):
+ TYPE = 59, 52, Constant(7)
+ STATE = 15, 0
+
+class Mgmt_EPMap(ManagementMessage):
+ TYPE = 59, 52, Constant(8)
+ LAST = 51
+ BASE = 34, 32
+ BITMAP = 31, 0
+
+class Mgmt_EPMap_Ack(ManagementMessage):
+ TYPE = 59, 52, Constant(8)
+ LAST = 51
+ BASE = 34, 32
+ MORE = 0
+
+class Mgmt_SetAPPower(ManagementMessage):
+ TYPE = 59, 52, Constant(0xb)
+ STATE = 15, 0
+
+class ASCManagementEndpoint(ASCBaseEndpoint):
+ BASE_MESSAGE = ManagementMessage
+ SHORT = "mgmt"
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.syslog_started = False
+ self.iop_power_state = 0
+ self.ap_power_state = 0
+ self.verbose = 1
+
+ @msg_handler(1, Mgmt_Hello)
+ def Hello(self, msg):
+ self.log(f"Supported versions {msg.MIN_VER} .. {msg.MAX_VER}")
+ # FIXME: we pick the highest version, we should negotiate
+ self.send(Mgmt_HelloAck(MIN_VER=msg.MAX_VER, MAX_VER=msg.MAX_VER))
+ return True
+
+ @msg_handler(8, Mgmt_EPMap)
+ def EPMap(self, msg):
+ for i in range(32):
+ if msg.BITMAP & (1 << i):
+ epno = 32 * msg.BASE + i
+ self.asc.eps.append(epno)
+ if self.verbose > 0:
+ self.log(f"Adding endpoint {epno:#x}")
+
+ self.send(Mgmt_EPMap_Ack(BASE=msg.BASE, LAST=msg.LAST, MORE=0 if msg.LAST else 1))
+
+ if msg.LAST:
+ for ep in self.asc.eps:
+ if ep == 0: continue
+ if ep < 0x10:
+ self.asc.start_ep(ep)
+ self.boot_done()
+
+ return True
+
+ @msg_handler(0xb, Mgmt_SetAPPower)
+ def APPowerAck(self, msg):
+ if self.verbose > 0:
+ self.log(f"AP power state is now {msg.STATE:#x}")
+ self.ap_power_state = msg.STATE
+ return True
+
+ @msg_handler(7, Mgmt_IOPPowerAck)
+ def IOPPowerAck(self, msg):
+ if self.verbose > 0:
+ self.log(f"IOP power state is now {msg.STATE:#x}")
+ self.iop_power_state = msg.STATE
+ return True
+
+ @msg_handler(4, Mgmt_Pong)
+ def Pong(self, msg):
+ return True
+
+ def start(self):
+ self.log("Starting via message")
+ self.send(Mgmt_SetIOPPower(STATE=0x220))
+
+ def wait_boot(self, timeout=None):
+ if timeout is not None:
+ timeout += time.time()
+ while self.iop_power_state != 0x20 or self.ap_power_state != 0x20:
+ self.asc.work()
+ if timeout and time.time() > timeout:
+ raise ASCTimeout("Boot timed out")
+ self.log("Startup complete")
+
+ def start_ep(self, epno):
+ self.send(Mgmt_StartEP(EP=epno, FLAG=2))
+
+ def stop_ep(self, epno):
+ self.send(Mgmt_StartEP(EP=epno, FLAG=1))
+
+ def boot_done(self):
+ self.send(Mgmt_SetAPPower(STATE=0x20))
+
+ def ping(self):
+ self.send(Mgmt_Ping())
+
+ def stop(self, state=0x10):
+ self.log("Stopping via message")
+ self.send(Mgmt_SetAPPower(STATE=0x10))
+ while self.ap_power_state == 0x20:
+ self.asc.work()
+ self.send(Mgmt_SetIOPPower(STATE=state))
+ while self.iop_power_state != state:
+ self.asc.work()
diff --git a/tools/proxyclient/m1n1/fw/asc/oslog.py b/tools/proxyclient/m1n1/fw/asc/oslog.py
new file mode 100644
index 0000000..b1a360b
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/asc/oslog.py
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: MIT
+from .base import *
+from ...utils import *
+
+## OSLog endpoint
+
+class OSLogMessage(Register64):
+ TYPE = 63, 56
+
+class OSLog_Init(OSLogMessage):
+ TYPE = 63, 56, Constant(1)
+ UNK = 51, 0
+
+class OSLog_Ack(OSLogMessage):
+ TYPE = 63, 56, Constant(3)
+
+class ASCOSLogEndpoint(ASCBaseEndpoint):
+ BASE_MESSAGE = OSLogMessage
+ SHORT = "oslog"
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.started = False
+
+ @msg_handler(1, OSLog_Init)
+ def Init(self, msg):
+ self.log(f"oslog init: {msg.UNK:#x}")
+ self.send(OSLog_Ack())
+ self.started = True
+ return True
diff --git a/tools/proxyclient/m1n1/fw/asc/syslog.py b/tools/proxyclient/m1n1/fw/asc/syslog.py
new file mode 100644
index 0000000..3387c27
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/asc/syslog.py
@@ -0,0 +1,72 @@
+# SPDX-License-Identifier: MIT
+import struct
+
+from .base import *
+from ...utils import *
+
+## Syslog endpoint
+
+class SyslogMessage(Register64):
+ TYPE = 59, 52
+
+class Syslog_Init(SyslogMessage):
+ TYPE = 59, 52, Constant(8)
+ ENTRYSIZE = 39, 24
+ COUNT = 15, 0
+
+class Syslog_GetBuf(SyslogMessage):
+ TYPE = 59, 52, Constant(1)
+ SIZE = 51, 44
+ DVA = 43, 0
+
+class Syslog_Log(SyslogMessage):
+ TYPE = 59, 52, Constant(5)
+ INDEX = 7, 0
+
+class ASCSysLogEndpoint(ASCBaseEndpoint):
+ BASE_MESSAGE = SyslogMessage
+ SHORT = "syslog"
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.entrysize = None
+ self.count = None
+ self.iobuffer = None
+ self.iobuffer_dva = None
+ self.started = False
+
+ @msg_handler(8, Syslog_Init)
+ def Init(self, msg):
+ self.entrysize = msg.ENTRYSIZE
+ self.count = msg.COUNT
+ self.log(f"count {self.count}, entrysize {self.entrysize}")
+ return True
+
+ @msg_handler(1, Syslog_GetBuf)
+ def GetBuf(self, msg):
+ size = align(0x1000 * msg.SIZE, 0x4000)
+
+ if self.iobuffer:
+ print("WARNING: trying to reset iobuffer!")
+
+ if msg.DVA:
+ self.iobuffer_dva = msg.DVA
+ self.log(f"buf prealloc at dva {self.iobuffer_dva:#x}")
+ else:
+ self.iobuffer, self.iobuffer_dva = self.asc.ioalloc(size)
+ self.log(f"buf {self.iobuffer:#x} / {self.iobuffer_dva:#x}")
+ self.send(Syslog_GetBuf(SIZE=size // 0x1000, DVA=self.iobuffer_dva))
+
+ self.started = True
+ return True
+
+ @msg_handler(5, Syslog_Log)
+ def Log(self, msg):
+ stride = 0x20 + self.entrysize
+ log = self.asc.ioread(self.iobuffer_dva + msg.INDEX * stride, stride)
+ hdr, unk, context, logmsg = struct.unpack(f"<II24s{self.entrysize}s", log)
+ context = context.split(b"\x00")[0].decode("ascii")
+ logmsg = logmsg.split(b"\x00")[0].decode("ascii").rstrip("\n")
+ self.log(f"* [{context}]{logmsg}")
+ self.send(msg)
+ return True
diff --git a/tools/proxyclient/m1n1/fw/common.py b/tools/proxyclient/m1n1/fw/common.py
new file mode 100644
index 0000000..479e2df
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/common.py
@@ -0,0 +1,170 @@
+# SPDX-License-Identifier: MIT
+
+from dataclasses import dataclass
+from enum import IntEnum
+from m1n1.utils import *
+from construct import *
+
+uint8_t = Int8ul
+int16_t = Int16sl
+uint16_t = Int16ul
+int32_t = Int32sl
+uint32_t = Int32ul
+int64_t = Int64sl
+uint64_t = Int64ul
+
+uint = uint32_t
+int_ = int32_t
+ulong = uint64_t
+long_ = int64_t
+
+def Bool(c):
+ return ExprAdapter(c, lambda d, ctx: bool(d & 1), lambda d, ctx: int(d))
+
+def SizedArray(count, svar, subcon):
+ return Padded(subcon.sizeof() * count, Array(lambda ctx: min(count, ctx.get(svar, ctx._.get(svar))), subcon))
+
+def SizedBytes(count, svar):
+ return Lazy(Padded(count, Bytes(lambda ctx: ctx.get(svar) or ctx._.get(svar))))
+
+def UnkBytes(s):
+ return Default(HexDump(Bytes(s)), b"\x00" * s)
+
+bool_ = Bool(Int8ul)
+
+class OSObject(Construct):
+ TYPE = None
+
+ def _parse(self, stream, context, path, recurse=False):
+ tag = stream.read(1).decode("ascii")
+ if not recurse and self.TYPE is not None and self.TYPE != tag:
+ raise Exception("Object type mismatch")
+
+ if tag == "d":
+ count = Int32ul.parse_stream(stream)
+ d = {}
+ for i in range(count):
+ k = self._parse(stream, context, path, True)
+ v = self._parse(stream, context, path, True)
+ d[k] = v
+ return d
+ elif tag == "n":
+ return Int64ul.parse_stream(stream)
+ elif tag == "s":
+ length = Int32ul.parse_stream(stream)
+ s = stream.read(length).decode("utf-8")
+ assert stream.read(1) == b'\0'
+ return s
+ else:
+ raise Exception(f"Unknown object tag {tag!r}")
+
+ def _build(self, obj, stream, context, path):
+ assert False
+
+ def _sizeof(self, context, path):
+ return None
+
+class OSDictionary(OSObject):
+ TYPE = 'd'
+
+class OSSerialize(Construct):
+ def _parse(self, stream, context, path, recurse=False):
+ hdr = Int32ul.parse_stream(stream)
+ if hdr != 0xd3:
+ raise Exception("Bad header")
+
+ obj, last = self.parse_obj(stream)
+ assert last
+ return obj
+
+ def parse_obj(self, stream, level=0):
+ # align to 32 bits
+ pos = stream.tell()
+ if pos & 3:
+ stream.read(4 - (pos & 3))
+
+ tag = Int32ul.parse_stream(stream)
+
+ last = bool(tag & 0x80000000)
+ otype = (tag >> 24) & 0x1f
+ size = tag & 0xffffff
+
+ #print(f"{' '*level} @{stream.tell():#x} {otype} {last} {size}")
+
+ if otype == 1:
+ d = {}
+ for i in range(size):
+ k, l = self.parse_obj(stream, level + 1)
+ assert not l
+ v, l = self.parse_obj(stream, level + 1)
+ assert l == (i == size - 1)
+ d[k] = v
+ elif otype == 2:
+ d = []
+ for i in range(size):
+ v, l = self.parse_obj(stream, level + 1)
+ assert l == (i == size - 1)
+ d.append(v)
+ elif otype == 4:
+ d = Int64ul.parse_stream(stream)
+ elif otype == 9:
+ d = stream.read(size).decode("utf-8")
+ elif otype == 10:
+ d = stream.read(size)
+ elif otype == 11:
+ d = bool(size)
+ else:
+ raise Exception(f"Unknown tag {otype}")
+
+ #print(f"{' '*level} => {d}")
+ return d, last
+
+ def build_obj(self, obj, stream, last=True, level=0):
+ tag = 0
+ if last:
+ tag |= 0x80000000
+
+ if isinstance(obj, dict):
+ tag |= (1 << 24) | len(obj)
+ Int32ul.build_stream(tag, stream)
+ for i, (k, v) in enumerate(obj.items()):
+ self.build_obj(k, stream, False, level + 1)
+ self.build_obj(v, stream, i == len(obj) - 1, level + 1)
+ elif isinstance(obj, list):
+ tag |= (2 << 24) | len(obj)
+ Int32ul.build_stream(tag, stream)
+ for i, v in enumerate(obj):
+ self.build_obj(v, stream, i == len(obj) - 1, level + 1)
+ elif isinstance(obj, int):
+ tag |= (4 << 24) | 64
+ Int32ul.build_stream(tag, stream)
+ Int64ul.build_stream(obj, stream)
+ elif isinstance(obj, str):
+ obj = obj.encode("utf-8")
+ tag |= (9 << 24) | len(obj)
+ Int32ul.build_stream(tag, stream)
+ stream.write(obj)
+ elif isinstance(obj, bytes):
+ tag |= (10 << 24) | len(obj)
+ Int32ul.build_stream(tag, stream)
+ stream.write(obj)
+ elif isinstance(obj, bool):
+ tag |= (11 << 24) | int(obj)
+ Int32ul.build_stream(tag, stream)
+ else:
+ raise Exception(f"Cannot encode {obj!r}")
+
+ pos = stream.tell()
+ if pos & 3:
+ stream.write(bytes(4 - (pos & 3)))
+
+ def _build(self, obj, stream, context, path):
+ Int32ul.build_stream(0xd3, stream)
+ self.build_obj(obj, stream)
+
+ def _sizeof(self, context, path):
+ return None
+
+def string(size):
+ return Padded(size, CString("utf8"))
+
diff --git a/tools/proxyclient/m1n1/fw/dcp/__init__.py b/tools/proxyclient/m1n1/fw/dcp/__init__.py
new file mode 100644
index 0000000..e77f6cf
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/dcp/__init__.py
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: MIT
+
diff --git a/tools/proxyclient/m1n1/fw/dcp/client.py b/tools/proxyclient/m1n1/fw/dcp/client.py
new file mode 100644
index 0000000..941ab20
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/dcp/client.py
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: MIT
+from ...utils import *
+
+from ..asc import StandardASC
+from .dcpep import DCPEndpoint
+
+class DCPClient(StandardASC):
+ ENDPOINTS = {
+ 0x37: DCPEndpoint,
+ }
+
+ def __init__(self, u, asc_base, dart=None, disp_dart=None):
+ super().__init__(u, asc_base, dart)
+ self.disp_dart = disp_dart
diff --git a/tools/proxyclient/m1n1/fw/dcp/dcpav.py b/tools/proxyclient/m1n1/fw/dcp/dcpav.py
new file mode 100644
index 0000000..d063c6c
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/dcp/dcpav.py
@@ -0,0 +1,110 @@
+# SPDX-License-Identifier: MIT
+from construct import *
+
+from ...utils import *
+from ..asc import StandardASC
+from ..afk.epic import *
+
+class DCPAVControllerService(EPICStandardService):
+ NAME = "dcpav-controller-epic"
+ SHORT = "dcpav"
+
+ def setPower(self, power):
+ self.call(8, 0x8, struct.pack("<16xI12x", power))
+
+ def getPower(self, power):
+ return struct.unpack("<16xI12x", self.call(8, 0x9, bytes(32)))
+
+ def wakeDisplay(self):
+ self.call(8, 0xa, bytes(16))
+
+ def sleepDisplay(self):
+ self.call(8, 0xb, bytes(16))
+
+ def forceHotPlugDetect(self):
+ self.call(8, 0xc, bytes(16))
+
+ def setVirtualDeviceMode(self, mode):
+ self.call(8, 0xd, struct.pack("<16xI12x", mode))
+
+class DCPDPControllerService(EPICStandardService):
+ NAME = "dcpdp-controller-epic"
+ SHORT = "dcpdp"
+
+class DCPDPTXEndpoint(EPICEndpoint):
+ SHORT = "dptx"
+
+ SERVICES = [
+ DCPAVControllerService,
+ DCPDPControllerService,
+ ]
+
+ATC0 = 0
+ATC1 = 1
+ATC2 = 2
+ATC3 = 3
+LPDPTX = 4
+DPTX = 5
+
+DPPHY = 0
+DPIN0 = 1
+DPIN1 = 2
+
+class DCPDPTXRemotePortService(EPICStandardService):
+ NAME = "dcpdptx-port-epic"
+ SHORT = "port"
+
+ def displayRequest(self):
+ self.call(8, 8, bytes(16))
+
+ def displayRelease(self):
+ self.call(8, 9, bytes(16))
+
+ def connectTo(self, connected, unit, port, unk=0):
+ target = 0
+ if connected:
+ target |= (1 << 8)
+ target |= unit
+ target |= port << 4
+ self.call(8, 13, struct.pack("<16xII8x", unk, target))
+
+class DCPDPTXPortEndpoint(EPICEndpoint):
+ SHORT = "dpport"
+
+ SERVICES = [
+ DCPDPTXRemotePortService,
+ DCPDPControllerService,
+ ]
+
+class DCPDPDevice(EPICStandardService):
+ NAME = "dcpav-device-epic"
+ SHORT = "dpdev"
+
+class DCPAVDeviceEndpoint(EPICEndpoint):
+ SHORT = "avdev"
+
+ SERVICES = [
+ DCPDPDevice,
+ ]
+
+class DCPDPService(EPICStandardService):
+ NAME = "dcpav-service-epic"
+ SHORT = "dpserv"
+
+class DCPAVServiceEndpoint(EPICEndpoint):
+ SHORT = "avserv"
+
+ SERVICES = [
+ DCPDPService,
+ ]
+
+class DCPAVSimpleVideoInterface(EPICStandardService):
+ NAME = "dcpav-video-interface-epic"
+ SHORT = "video"
+
+class DCPAVVideoEndpoint(EPICEndpoint):
+ SHORT = "avserv"
+
+ SERVICES = [
+ DCPAVSimpleVideoInterface,
+ ]
diff --git a/tools/proxyclient/m1n1/fw/dcp/dcpep.py b/tools/proxyclient/m1n1/fw/dcp/dcpep.py
new file mode 100644
index 0000000..6e4f250
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/dcp/dcpep.py
@@ -0,0 +1,163 @@
+# SPDX-License-Identifier: MIT
+import struct
+from dataclasses import dataclass
+from enum import IntEnum
+
+from ..asc.base import *
+from ...utils import *
+
+## DCP main endpoint
+
+class DCPMessage(Register64):
+ TYPE = 3, 0
+
+class DCPEp_SetShmem(DCPMessage):
+ DVA = 63, 16
+ FLAG = 7, 4
+ TYPE = 3, 0, Constant(0)
+
+class DCPEp_InitComplete(DCPMessage):
+ TYPE = 3, 0, Constant(1)
+
+class CallContext(IntEnum):
+ CB = 0
+ CMD = 2
+ ASYNC = 3
+ OOBCB = 4
+ OOBCMD = 6
+
+class DCPEp_Msg(DCPMessage):
+ LEN = 63, 32
+ OFF = 31, 16
+ CTX = 11, 8, CallContext
+ ACK = 6
+ TYPE = 3, 0, Constant(2)
+
+@dataclass
+class DCPCallState:
+ tag: str
+ off: int
+ in_len: int
+ in_data: bytes
+ out_addr: int
+ out_len: int
+ complete: bool = False
+
+class DCPCallChannel(Reloadable):
+ def __init__(self, dcpep, name, buf, bufsize):
+ self.dcp = dcpep
+ self.name = name
+ self.buf = buf
+ self.bufsize = bufsize
+ self.off = 0
+ self.pending = []
+
+ def ack(self):
+ if not self.pending:
+ raise Exception("ACK with no calls pending")
+
+ self.pending[-1].complete = True
+
+ def call(self, ctx, tag, inbuf, out_len):
+ in_len = len(inbuf)
+ data = tag.encode("ascii")[::-1] + struct.pack("<II", in_len, out_len) + inbuf
+ data_size = len(data) + out_len
+ assert (self.off + data_size) <= self.bufsize
+
+ self.dcp.asc.iface.writemem(self.dcp.shmem + self.buf + self.off, data)
+
+ state = DCPCallState(off=self.off, tag=tag, in_len=in_len, in_data=data, out_len=out_len,
+ out_addr=self.buf + self.off + 12 + in_len)
+
+ self.off += align_up(data_size, 0x40)
+ self.pending.append(state)
+
+ print(f"len={data_size:#x} {in_len}")
+ self.dcp.send(DCPEp_Msg(LEN=data_size, OFF=state.off, CTX=ctx, ACK=0))
+
+ while not state.complete:
+ self.dcp.asc.work()
+
+ print(f"off={state.out_addr:#x} len={out_len}")
+ out_data = self.dcp.asc.iface.readmem(self.dcp.shmem + state.out_addr, out_len)
+
+ assert self.pending.pop() is state
+ self.off = state.off
+
+ return out_data
+
+class DCPCallbackChannel(Reloadable):
+ def __init__(self, dcpep, name, buf, bufsize):
+ self.dcp = dcpep
+ self.name = name
+ self.buf = buf
+ self.bufsize = bufsize
+ self.pending = []
+
+ def cb(self, msg):
+ data = self.dcp.asc.iface.readmem(self.dcp.shmem + self.buf + msg.OFF, msg.LEN)
+ tag = data[:4][::-1].decode("ascii")
+ in_len, out_len = struct.unpack("<II", data[4:12])
+ in_data = data[12:12 + in_len]
+
+ state = DCPCallState(off=msg.OFF, tag=tag, in_len=in_len, out_len=out_len,
+ in_data=in_data, out_addr=self.buf + msg.OFF + 12 + in_len)
+
+ self.pending.append(state)
+
+ out_data = self.dcp.mgr.handle_cb(state)
+ self.dcp.asc.iface.writemem(self.dcp.shmem + state.out_addr, out_data)
+ self.dcp.send(DCPEp_Msg(CTX=msg.CTX, ACK=1))
+
+ assert self.pending.pop() is state
+
+
+class DCPEndpoint(ASCBaseEndpoint):
+ BASE_MESSAGE = DCPMessage
+ SHORT = "dcpep"
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.shmem = self.shmem_dva = None
+ self.init_complete = False
+ self.mgr = None
+
+ self.ch_cb = DCPCallbackChannel(self, "CB", 0x60000, 0x8000)
+ self.ch_cmd = DCPCallChannel(self, "CMD", 0, 0x8000)
+ self.ch_async = DCPCallbackChannel(self, "ASYNC", 0x40000, 0x20000)
+ self.ch_oobcb = DCPCallbackChannel(self, "OOBCB", 0x68000, 0x8000)
+ self.ch_oobcmd = DCPCallChannel(self, "OOBCMD", 0x8000, 0x8000)
+
+ @msg_handler(2, DCPEp_Msg)
+ def Rx(self, msg):
+ if msg.ACK:
+ if msg.CTX in (CallContext.CMD, CallContext.CB):
+ self.ch_cmd.ack()
+ elif msg.CTX in (CallContext.OOBCMD, CallContext.OOBCB):
+ self.ch_oobcmd.ack()
+ else:
+ raise Exception(f"Unknown RX ack channel {msg.CTX}")
+ else:
+ if msg.CTX == CallContext.CB:
+ self.ch_cb.cb(msg)
+ elif msg.CTX == CallContext.OOBCMD:
+ self.ch_oobcb.cb(msg)
+ elif msg.CTX == CallContext.ASYNC:
+ self.ch_async.cb(msg)
+ else:
+ raise Exception(f"Unknown RX callback channel {msg.CTX}")
+ return True
+
+ @msg_handler(1, DCPEp_InitComplete)
+ def InitComplete(self, msg):
+ self.log("init complete")
+ self.init_complete = True
+ return True
+
+ def initialize(self):
+ self.shmem, self.shmem_dva = self.asc.ioalloc(0x100000)
+ self.asc.p.memset32(self.shmem, 0, 0x100000)
+ self.send(DCPEp_SetShmem(DVA=self.shmem_dva))
+ while not self.init_complete:
+ self.asc.work()
+
diff --git a/tools/proxyclient/m1n1/fw/dcp/iboot.py b/tools/proxyclient/m1n1/fw/dcp/iboot.py
new file mode 100644
index 0000000..8124ca9
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/dcp/iboot.py
@@ -0,0 +1,215 @@
+# SPDX-License-Identifier: MIT
+from construct import *
+
+from ...utils import *
+from ..asc import StandardASC
+from ..afk.epic import *
+from .dcpav import *
+
+EOTF = "EOTF" / Enum(Int32ul,
+ GAMMA_SDR = 1,
+ GAMMA_HDR = 2,
+)
+
+Encoding = "Encoding" / Enum(Int32ul,
+ RGB = 1,
+ YCBCR_444 = 3,
+ YCBCR_422 = 4,
+ YCBCR_420 = 5,
+)
+
+Colorimetry = "Colorimetry" / Enum(Int32ul,
+ BT601_709 = 1,
+ BT2020 = 2,
+ DCIP3 = 3,
+)
+
+Colorspace = "Colorspace" / Enum(Int32ul,
+ SRGB = 1,
+ Native = 2,
+ BT2020 = 3,
+)
+
+SurfaceFormat = "SurfaceFormat" / Enum(Int32ul,
+ BGRA = 1,
+ BGRA2 = 2,
+ RGBA = 3,
+ w18p = 4,
+ BGRA3 = 5,
+ _444v = 6,
+ _422v = 7,
+ _420v = 8,
+ w30r = 9,
+ w40a = 10,
+)
+
+Transform = "Transform" / Enum(Int8ul,
+ NONE = 0,
+ XFLIP = 1,
+ YFLIP = 2,
+ ROT_90 = 3,
+ ROT_180 = 4,
+ ROT_270 = 5,
+)
+
+AddrFormat = "AddrFormat" / Enum(Int32ul,
+ PLANAR = 1,
+ TILED = 2,
+ AGX = 3
+)
+
+TimingMode = Struct(
+ "valid" / Bool(Int32ul),
+ "width" / Int32ul,
+ "height" / Int32ul,
+ "fps_frac" / Int16ul,
+ "fps_int" / Int16ul,
+ Padding(8),
+)
+
+TimingModeList = Struct(
+ "count" / Int32ul,
+ "list" / GreedyRange(TimingMode),
+)
+
+ColorMode = Struct(
+ "valid" / Bool(Int32ul),
+ "colorimetry" / Colorimetry,
+ "eotf" / EOTF,
+ "encoding" / Encoding,
+ "bpp" / Int32ul,
+ "unk" / Int32ul,
+)
+
+ColorModeList = Struct(
+ "count" / Int32ul,
+ "list" / GreedyRange(ColorMode),
+)
+
+SwapInfo = Struct(
+ "unk1" / Int32ul,
+ "unk2" / Int32ul,
+ "unk3" / Int32ul,
+ "swap_id" / Int32ul,
+ "unk5" / Int32ul,
+)
+
+IBootPlaneInfo = Struct(
+ "unk1" / Default(Int32ul, 0),
+ "addr" / Default(Int64ul, 0),
+ "tile_size" / Default(Int32ul, 0),
+ "stride" / Default(Int32ul, 0),
+ "unk5" / Default(Int32ul, 0),
+ "unk6" / Default(Int32ul, 0),
+ "unk7" / Default(Int32ul, 0),
+ "unk8" / Default(Int32ul, 0),
+ "addr_format" / Default(AddrFormat, 0),
+ "unk9" / Default(Int32ul, 0),
+)
+
+IBootLayerInfo = Struct(
+ "planes" / Array(3, IBootPlaneInfo),
+ "unk" / Default(Int32ul, 0),
+ "plane_cnt" / Int32ul,
+ "width" / Int32ul,
+ "height" / Int32ul,
+ "surface_fmt" / SurfaceFormat,
+ "colorspace" / Colorspace,
+ "eotf" / EOTF,
+ "transform" / Transform,
+ Padding(3)
+)
+
+SwapSetLayer = Struct(
+ "unk" / Default(Int32ul, 0),
+ "layer_id" / Int32ul,
+ "layer_info" / IBootLayerInfo,
+ "src_w" / Int32ul,
+ "src_h" / Int32ul,
+ "src_x" / Int32ul,
+ "src_y" / Int32ul,
+ "dst_w" / Int32ul,
+ "dst_h" / Int32ul,
+ "dst_x" / Int32ul,
+ "dst_y" / Int32ul,
+ "unk2" / Default(Int32ul, 0),
+)
+
+class DCPIBootService(EPICService):
+ NAME = "disp0-service"
+ SHORT = "disp0"
+
+ def send_cmd(self, op, data=b'', replen=None):
+ msg = struct.pack("<IIII", op, 16 + len(data), 0, 0) + data
+ if replen is not None:
+ replen += 8
+ resp = super().send_cmd(0xc0, msg, replen)
+ if not resp:
+ return
+ rcmd, rlen = struct.unpack("<II", resp[:8])
+ return resp[8:rlen]
+
+ def setPower(self, power):
+ self.send_cmd(2, b"\x01" if power else b"\x00")
+
+ def getModeCount(self):
+ buf = self.send_cmd(3, b"", 12)
+ hpd, timing_cnt, color_cnt = struct.unpack("<B3xII", buf)
+ return bool(hpd), timing_cnt, color_cnt
+
+ def getTimingModes(self):
+ return TimingModeList.parse(self.send_cmd(4, replen=4096)).list
+
+ def getColorModes(self):
+ return ColorModeList.parse(self.send_cmd(5, replen=4096)).list
+
+ def setMode(self, timing_mode, color_mode):
+ data = TimingMode.build(timing_mode) + ColorMode.build(color_mode)
+ self.send_cmd(6, data)
+
+ def swapBegin(self):
+ return SwapInfo.parse(self.send_cmd(15, replen=128))
+
+ def swapSetLayer(self, layer_id, info, src_rect, dst_rect):
+ data = Container()
+ data.layer_id = layer_id
+ data.layer_info = info
+ data.src_w, data.src_h, data.src_x, data.src_y = src_rect
+ data.dst_w, data.dst_h, data.dst_x, data.dst_y = dst_rect
+ return self.send_cmd(16, SwapSetLayer.build(data), replen=128)
+
+ def swapSetTimestamp(self):
+ pass
+ # 17
+
+ def swapEnd(self):
+ return self.send_cmd(18, b"\x00" * 12, replen=128)
+
+ #def swapWait(self, swap_id):
+ #buf = struct.pack("<IIII", 1, swap_id, 0, swap_id)
+ #return self.send_cmd(19, buf, replen=128)
+
+class DCPIBootEndpoint(EPICEndpoint):
+ SHORT = "iboot"
+
+ SERVICES = [
+ DCPIBootService,
+ ]
+
+
+class DCPIBootClient(StandardASC):
+ DVA_OFFSET = 0xf00000000
+
+ ENDPOINTS = {
+ 0x20: AFKSystemEndpoint,
+ 0x23: DCPIBootEndpoint,
+ 0x24: DCPDPTXEndpoint,
+ 0x2a: DCPDPTXPortEndpoint,
+ 0x27: DCPAVDeviceEndpoint,
+ 0x28: DCPAVServiceEndpoint,
+ 0x29: DCPAVVideoEndpoint,
+ }
+
+ def __init__(self, u, asc_base, dart=None, disp_dart=None):
+ super().__init__(u, asc_base, dart)
+ self.disp_dart = disp_dart
diff --git a/tools/proxyclient/m1n1/fw/dcp/ipc.py b/tools/proxyclient/m1n1/fw/dcp/ipc.py
new file mode 100644
index 0000000..c961a8c
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/dcp/ipc.py
@@ -0,0 +1,789 @@
+# SPDX-License-Identifier: MIT
+
+from dataclasses import dataclass
+import pprint
+from enum import IntEnum
+
+from ..common import *
+from m1n1.utils import *
+from construct import *
+
+@dataclass
+class ByRef:
+ val: object
+
+class Pointer(Subconstruct):
+ pass
+
+class InPtr(Pointer):
+ pass
+
+class OutPtr(Pointer):
+ pass
+
+class InOutPtr(Pointer):
+ pass
+
+class InOut(Subconstruct):
+ pass
+
+Ptr = InOutPtr
+
+class NULL:
+ def __str__(self):
+ return "NULL"
+ def __repr__(self):
+ return "NULL"
+NULL = NULL()
+
+class Method:
+ def __init__(self, rtype, name, *args, **kwargs):
+ self.rtype = rtype
+ self.name = name
+
+ if args and kwargs:
+ raise Exception("Cannot specify args and kwargs")
+ elif args:
+ args = [(f"arg{i}", arg) for i, arg in enumerate(args)]
+ self.as_kwargs = False
+ elif kwargs:
+ args = list(kwargs.items())
+ self.as_kwargs = True
+ else:
+ args = []
+
+ self.args = args
+
+ in_size = 0
+ out_size = 0
+ self.in_fields = []
+ self.out_fields = []
+
+ if rtype is not None:
+ args.append(("ret", rtype))
+
+ self.dir = []
+ self.nullable = []
+ self.array_of_p = []
+
+ for i, (name, field) in enumerate(self.args):
+ align = 1
+
+ pfield = field
+ dir = "in"
+
+ if name == "ret":
+ dir = "out"
+
+ while isinstance(pfield, Subconstruct):
+ if isinstance(pfield, InPtr):
+ dir = "in"
+ elif isinstance(pfield, OutPtr):
+ dir = "out"
+ elif isinstance(pfield, (InOut, InOutPtr)):
+ dir = "inout"
+ pfield = pfield.subcon
+ if isinstance(pfield, FormatField):
+ align = min(4, pfield.length)
+
+ if dir in ("in", "inout"):
+ #if in_size % align:
+ #self.in_fields.append(Padding(align - (in_size % align)))
+ #in_size += align - (in_size % align)
+
+ self.in_fields.append(name / field)
+ in_size += field.sizeof()
+
+ if dir in ("out", "inout"):
+ #if out_size % align:
+ #self.out_fields.append(Padding(align - (out_size % align)))
+ #out_size += align - (out_size % align)
+
+ self.out_fields.append(name / field)
+ out_size += field.sizeof()
+
+ self.dir.append(dir)
+
+ for i, (name, field) in enumerate(self.args):
+ array_size = None
+ array_of_p = False
+ nullable = False
+ pfield = field
+
+ while isinstance(pfield, Subconstruct):
+ if isinstance(pfield, Array) and array_size is None:
+ array_size = pfield.count
+ if isinstance(pfield, Pointer):
+ nullable = True
+ array_of_p = array_size is not None
+ pfield = pfield.subcon
+
+ if nullable:
+ if array_of_p:
+ self.in_fields.append((name + "_null") / bool_[array_size])
+ in_size += array_size
+ else:
+ self.in_fields.append((name + "_null") / bool_)
+ in_size += 1
+
+ self.nullable.append(nullable)
+ self.array_of_p.append(array_of_p)
+
+ if in_size % 4:
+ self.in_fields.append(Padding(4 - (in_size % 4)))
+ if out_size % 4:
+ self.out_fields.append(Padding(4 - (out_size % 4)))
+
+ self.in_struct = Struct(*self.in_fields)
+ self.out_struct = Struct(*self.out_fields)
+
+ def get_field_val(self, i, in_vals, out_vals=None, nullobj=None):
+ name, field = self.args[i]
+
+ nullable = self.nullable[i]
+ array_of_p = self.array_of_p[i]
+
+ val = None
+
+ if out_vals:
+ val = out_vals.get(name, val)
+ if val is None and in_vals:
+ val = in_vals.get(name, val)
+
+ if nullable and val is not None:
+ null = in_vals.get(name + "_null", None)
+ if null is None:
+ return None
+ if not array_of_p:
+ val = nullobj if null else val
+ else:
+ val2 = [nullobj if n else val for val, n in zip(val, null)]
+ if isinstance(val, ListContainer):
+ val2 = ListContainer(val2)
+ val = val2
+
+ return val
+
+ def fmt_args(self, in_vals, out_vals=None):
+ s = []
+
+ for i, (name, field) in enumerate(self.args):
+ if name == "ret":
+ continue
+
+ dir = self.dir[i]
+ nullable = self.nullable[i]
+
+ val = self.get_field_val(i, in_vals, out_vals, nullobj=NULL)
+
+ if val is not None:
+ if self.is_long(val):
+ s.append(f"{name}=...")
+ elif isinstance(val, ListContainer):
+ s.append(f"{name}={list(val)!r}")
+ else:
+ s.append(f"{name}={val!r}")
+ elif dir == "out":
+ s.append(f"{name}=<out>")
+ else:
+ s.append(f"{name}=?")
+
+ return ", ".join(s)
+
+ def print_long_args(self, indent, in_vals, out_vals=None):
+ for i, (name, field) in enumerate(self.args):
+ if name == "ret":
+ continue
+
+ val = self.get_field_val(i, in_vals, out_vals, nullobj=NULL)
+
+ if name in in_vals and out_vals is not None and name not in out_vals:
+ continue
+
+ if self.is_long(val):
+ hdr = f"{indent} {name} = "
+ if isinstance(val, (ListContainer, Container)):
+ print(hdr + str(val).replace("\n", "\n" + indent))
+ elif isinstance(val, bytes):
+ print(hdr + f"({len(val):#x} bytes)")
+ chexdump(val, indent=indent + " ")
+ else:
+ dindent = " " * len(hdr)
+ if isinstance(val, dict) and "_io" in val:
+ del val["_io"]
+ print(hdr + pprint.pformat(val, sort_dicts=False).replace("\n", "\n" + dindent))
+
+ def is_long(self, arg):
+ if isinstance(arg, (list, bytes)):
+ return len(arg) > 4 or any(self.is_long(i) for i in arg)
+
+ return isinstance(arg, (dict, list, bytes))
+
+ def parse_input(self, data):
+ vals = self.in_struct.parse(data)
+
+ return Container({ k: v() if callable(v) else v for k,v in vals.items() })
+
+ def parse_output(self, data, in_vals):
+ context = dict(in_vals)
+
+ if "data" in context:
+ del context["data"]
+
+ vals = self.out_struct.parse(data, **context)
+
+ return Container({ k: v() if callable(v) else v for k,v in vals.items() })
+
+ def __str__(self):
+ if self.rtype is None:
+ rtype = "void"
+ else:
+ rtype = str(self.rtype)
+
+ args = []
+ for name, field in self.args:
+ if name == "ret":
+ continue
+ args.append(f"{field} {name}")
+
+ return f"{rtype} {self.name}({', '.join(args)})"
+
+ def callback(self, func, in_data):
+ in_vals = self.parse_input(in_data)
+
+ args = []
+ kwargs = {}
+
+ out_vals = {}
+
+ for i, (name, field) in enumerate(self.args):
+ if name == "ret":
+ continue
+
+ dir = self.dir[i]
+
+ val = self.get_field_val(i, in_vals, out_vals, nullobj=NULL)
+ is_null = val is NULL
+ if is_null:
+ val = None
+
+ if dir == "inout":
+ if val is not None and not isinstance(val, list):
+ val = ByRef(val)
+ out_vals[name] = val
+ elif dir == "out" and not is_null:
+ val = ByRef(None)
+ out_vals[name] = val
+
+ if self.as_kwargs:
+ kwargs[name] = val
+ else:
+ args.append(val)
+
+ retval = func(*args, **kwargs)
+
+ if self.rtype is None:
+ assert retval is None
+ else:
+ assert retval is not None
+ out_vals["ret"] = retval
+
+ out_vals = {k: v.val if isinstance(v, ByRef) else v for k, v in out_vals.items()}
+
+ context = dict(in_vals)
+
+ if "obj" in context:
+ del context["obj"]
+
+ out_data = self.out_struct.build(out_vals, **context)
+ return out_data
+
+
+ def call(self, call, *args, **kwargs):
+ if args and kwargs:
+ raise Exception("Cannot use both args and kwargs")
+
+ if args:
+ for arg, (name, field) in zip(args, self.args):
+ kwargs[name] = arg
+
+ in_vals = {}
+ out_refs = {}
+
+ for i, (name, field) in enumerate(self.args):
+ if name == "ret":
+ continue
+
+ val = kwargs[name]
+ dir = self.dir[i]
+ nullable = self.nullable[i]
+ array_of_p = self.array_of_p[i]
+
+ if nullable:
+ if not array_of_p:
+ in_vals[name + "_null"] = val is None
+ else:
+ defaults = field.parse(b"\x00" * field.sizeof())
+ in_vals[name + "_null"] = [i is None for i in val]
+ val = [v if v is not None else defaults[i] for i, v in enumerate(val)]
+ else:
+ assert val is not None
+
+ if val is None:
+ continue
+
+ if dir == "out":
+ assert isinstance(val, ByRef)
+ out_refs[name] = val
+ elif dir == "inout":
+ if isinstance(val, ByRef):
+ in_vals[name] = val.val
+ out_refs[name] = val
+ elif val is not None:
+ in_vals[name] = val
+ elif val is not None:
+ in_vals[name] = val
+
+ in_data = self.in_struct.build(in_vals)
+ print(f"{self.name}({self.fmt_args(in_vals)})")
+
+ out_data = call(in_data)
+ out_vals = self.parse_output(out_data, in_vals)
+
+ for k, v in out_refs.items():
+ v.val = out_vals[k]
+
+ if self.rtype is not None:
+ return out_vals["ret"]
+
+def dump_fields(fields):
+ off = 0
+ for f in fields:
+ sizeof = f.sizeof()
+ print(f"{off:#x}: {f} ({sizeof:#x})")
+ off += sizeof
+
+class Call(Method):
+ pass
+
+class Callback(Method):
+ pass
+
+int8_t = Int8sl
+uint8_t = Int8ul
+int16_t = Int16sl
+uint16_t = Int16ul
+int32_t = Int32sl
+uint32_t = Int32ul
+int64_t = Int64sl
+uint64_t = Int64ul
+
+uint = uint32_t
+int_ = int32_t
+ulong = uint64_t
+long_ = int64_t
+
+void = None
+
+class IPCObject:
+ @classmethod
+ def methods(cls):
+ ret = {}
+ for c in cls.mro():
+ ret.update({k: (cls, v) for k, v in cls.__dict__.items() if isinstance(v, Method)})
+
+ return ret
+
+rt_bw_config_t = Struct(
+ "unk1" / UnkBytes(8),
+ "reg1" / Int64ul,
+ "reg2" / Int64ul,
+ "unk2" / UnkBytes(4),
+ "bit" / Int32ul,
+ "padding" / UnkBytes(0x1c),
+)
+
+IOUserClient = Struct(
+ "addr" / Hex(Int64ul),
+ "unk" / Int32ul,
+ "flag1" / Int8ul,
+ "flag2" / Int8ul,
+ Padding(2)
+)
+
+IOMobileFramebufferUserClient = IOUserClient
+
+IOMFBStatus = Int32ul
+IOMFBParameterName = Int32ul
+
+BufferDescriptor = uint64_t
+
+SwapCompleteData = Bytes(0x12)
+SwapInfoBlob = Bytes(0x6c4)
+
+SWAP_SURFACES = 4
+
+Rect = NamedTuple("rect", "x y w h", Int32ul[4])
+
+IOMFBSwapRec = Struct(
+ "ts1" / Default(Int64ul, 0),
+ "ts2" / Default(Int64ul, 0),
+ "unk_10" / Default(Int64ul, 0),
+ "unk_18" / Default(Int64ul, 0),
+ "ts64_unk" / Default(Int64ul, 0),
+ "unk_28" / Default(Int64ul, 0),
+ "ts3" / Default(Int64ul, 0),
+ "unk_38" / Default(Int64ul, 0),
+ "flags1" / Hex(Int64ul),
+ "flags2" / Hex(Int64ul),
+ "swap_id" / Int32ul,
+ "surf_ids" / Int32ul[SWAP_SURFACES],
+ "src_rect" / Rect[SWAP_SURFACES],
+ "surf_flags" / Int32ul[SWAP_SURFACES],
+ "surf_unk" / Int32ul[SWAP_SURFACES],
+ "dst_rect" / Rect[SWAP_SURFACES],
+ "swap_enabled" / Hex(Int32ul),
+ "swap_completed" / Hex(Int32ul),
+ "unk_10c" / Hex(Default(Int32ul, 0)),
+ "unk_110" / UnkBytes(0x1b8),
+ "unk_2c8" / Hex(Default(Int32ul, 0)),
+ "unk_2cc" / UnkBytes(0x14),
+ "unk_2e0" / Hex(Default(Int32ul, 0)),
+ "unk_2e2" / UnkBytes(0x2),
+ "bl_unk" / Hex(Int64ul), # seen: 0x0, 0x1, 0x101, 0x1_0000, 0x101_010101
+ "bl_val" / Hex(Int32ul), # range 0x10000000 - approximately 0x7fe07fc0 for 4 - 510 nits
+ "bl_power" / Hex(Int8ul), # constant 0x40, 0x00: backlight off
+ "unk_2f3" / UnkBytes(0x2d),
+)
+
+assert IOMFBSwapRec.sizeof() == 0x320
+
+MAX_PLANES = 3
+
+ComponentTypes = Struct(
+ "count" / Int8ul,
+ "types" / SizedArray(7, "count", Int8ul),
+)
+
+#ComponentTypes = Bytes(8)
+
+PlaneInfo = Struct(
+ "width" / Int32ul,
+ "height" / Int32ul,
+ "base" / Hex(Int32ul),
+ "offset" / Hex(Int32ul),
+ "stride" / Hex(Int32ul),
+ "size" / Hex(Int32ul),
+ "tile_size" / Int16ul,
+ "tile_w" / Int8ul,
+ "tile_h" / Int8ul,
+ "unk1" / UnkBytes(0xd),
+ "unk2" / Hex(Int8ul),
+ "unk3" / UnkBytes(0x26),
+)
+
+assert PlaneInfo.sizeof() == 0x50
+
+IOSurface = Struct(
+ "is_tiled" / bool_,
+ "unk_1" / bool_,
+ "unk_2" / bool_,
+ "plane_cnt" / Int32ul,
+ "plane_cnt2" / Int32ul,
+ "format" / FourCC,
+ "unk_f" / Default(Hex(Int32ul), 0),
+ "xfer_func" / Int8ul,
+ "colorspace" / Int8ul,
+ "stride" / Int32ul,
+ "pix_size" / Int16ul,
+ "pel_w" / Int8ul,
+ "pel_h" / Int8ul,
+ "offset" / Default(Hex(Int32ul), 0),
+ "width" / Int32ul,
+ "height" / Int32ul,
+ "buf_size" / Hex(Int32ul),
+ "unk_2d" / Default(Int32ul, 0),
+ "unk_31" / Default(Int32ul, 0),
+ "surface_id" / Int32ul,
+ "comp_types" / Default(SizedArray(MAX_PLANES, "plane_cnt", ComponentTypes), []),
+ "has_comp" / Bool(Int64ul),
+ "planes" / Default(SizedArray(MAX_PLANES, "plane_cnt", PlaneInfo), []),
+ "has_planes" / Bool(Int64ul),
+ "compression_info" / Default(SizedArray(MAX_PLANES, "plane_cnt", UnkBytes(0x34)), []),
+ "has_compr_info" / Bool(Int64ul),
+ "unk_1f5" / Int32ul,
+ "unk_1f9" / Int32ul,
+ "padding" / UnkBytes(7),
+)
+
+assert IOSurface.sizeof() == 0x204
+
+IOMFBColorFixedMatrix = Array(5, Array(3, ulong))
+
+class PropID(IntEnum):
+ BrightnessCorrection = 14
+
+class UPPipeAP_H13P(IPCObject):
+ A000 = Call(bool_, "late_init_signal")
+ A029 = Call(void, "setup_video_limits")
+ A034 = Call(void, "update_notify_clients_dcp", Array(14, uint))
+ A035 = Call(bool_, "is_hilo")
+ A036 = Call(bool_, "apt_supported")
+ A037 = Call(uint, "get_dfb_info", InOutPtr(uint), InOutPtr(Array(4, ulong)), InOutPtr(uint))
+ A038 = Call(uint, "get_dfb_compression_info", InOutPtr(uint))
+
+ D000 = Callback(bool_, "did_boot_signal")
+ D001 = Callback(bool_, "did_power_on_signal")
+ D002 = Callback(void, "will_power_off_signal")
+ D003 = Callback(void, "rt_bandwidth_setup_ap", config=OutPtr(rt_bw_config_t))
+
+IdleCachingState = uint32_t
+
+class UnifiedPipeline2(IPCObject):
+ A352 = Call(bool_, "applyProperty", uint, uint)
+ A353 = Call(uint, "get_system_type")
+ A357 = Call(void, "set_create_DFB")
+ A358 = Call(IOMFBStatus, "vi_set_temperature_hint")
+
+ D100 = Callback(void, "match_pmu_service")
+ D101 = Callback(uint32_t, "UNK_get_some_field")
+ D102 = Callback(void, "set_number_property", key=string(0x40), value=uint)
+ D103 = Callback(void, "set_boolean_property", key=string(0x40), value=bool_)
+ D106 = Callback(void, "removeProperty", key=string(0x40))
+ D107 = Callback(bool_, "create_provider_service")
+ D108 = Callback(bool_, "create_product_service")
+ D109 = Callback(bool_, "create_PMU_service")
+ D110 = Callback(bool_, "create_iomfb_service")
+ D111 = Callback(bool_, "create_backlight_service")
+ D112 = Callback(void, "set_idle_caching_state_ap", IdleCachingState, uint)
+ D116 = Callback(bool_, "start_hardware_boot")
+ D117 = Callback(bool_, "is_dark_boot")
+ D118 = Callback(bool_, "is_waking_from_hibernate")
+ D120 = Callback(bool_, "read_edt_data", key=string(0x40), count=uint, value=InOut(Lazy(SizedArray(8, "count", uint32_t))))
+
+ D122 = Callback(bool_, "setDCPAVPropStart", length=uint)
+ D123 = Callback(bool_, "setDCPAVPropChunk", data=HexDump(SizedBytes(0x1000, "length")), offset=uint, length=uint)
+ D124 = Callback(bool_, "setDCPAVPropEnd", key=string(0x40))
+
+class UPPipe2(IPCObject):
+ A102 = Call(uint64_t, "test_control", cmd=uint64_t, arg=uint)
+ A103 = Call(void, "get_config_frame_size", width=InOutPtr(uint), height=InOutPtr(uint))
+ A104 = Call(void, "set_config_frame_size", width=uint, height=uint)
+ A105 = Call(void, "program_config_frame_size")
+ A130 = Call(bool_, "init_ca_pmu")
+ A131 = Call(bool_, "pmu_service_matched")
+ A132 = Call(bool_, "backlight_service_matched")
+
+ D201 = Callback(uint32_t, "map_buf", buf=InPtr(BufferDescriptor), vaddr=OutPtr(ulong), dva=OutPtr(ulong), unk=bool_)
+ D202 = Callback(void, "unmap_buf", buf=InPtr(BufferDescriptor), unk1=uint, unk2=ulong, unkB=uint)
+
+ D206 = Callback(bool_, "match_pmu_service_2")
+ D207 = Callback(bool_, "match_backlight_service")
+ D208 = Callback(uint64_t, "get_calendar_time_ms")
+ D211 = Callback(void, "update_backlight_factor_prop", int_)
+
+class PropRelay(IPCObject):
+ D300 = Callback(void, "pr_publish", prop_id=uint32_t, value=int_)
+
+class IOMobileFramebufferAP(IPCObject):
+ A401 = Call(uint32_t, "start_signal")
+
+ A407 = Call(uint32_t, "swap_start", swap_id=InOutPtr(uint), client=InOutPtr(IOUserClient))
+ A408 = Call(uint32_t, "swap_submit_dcp",
+ swap_rec=InPtr(IOMFBSwapRec),
+ surfaces=Array(4, InPtr(IOSurface)),
+ surfAddr=Array(4, Hex(ulong)),
+ unkBool=bool_,
+ unkFloat=Float64l,
+ unkInt=uint,
+ unkOutBool=OutPtr(bool_))
+
+ A410 = Call(uint32_t, "set_display_device", uint)
+ A411 = Call(bool_, "is_main_display")
+ A438 = Call(uint32_t, "swap_set_color_matrix", matrix=InOutPtr(IOMFBColorFixedMatrix), func=uint32_t, unk=uint)
+#"A438": "IOMobileFramebufferAP::swap_set_color_matrix(IOMFBColorFixedMatrix*, IOMFBColorMatrixFunction, unsigned int)",
+
+ A412 = Call(uint32_t, "set_digital_out_mode", uint, uint)
+ A413 = Call(uint32_t, "get_digital_out_state", InOutPtr(uint))
+ A414 = Call(uint32_t, "get_display_area", InOutPtr(ulong))
+ A419 = Call(uint32_t, "get_gamma_table", InOutPtr(Bytes(0xc0c)))
+ A422 = Call(uint32_t, "set_matrix", uint, InPtr(Array(3, Array(3, ulong))))
+ A423 = Call(uint32_t, "set_contrast", InOutPtr(Float32l))
+ A426 = Call(uint32_t, "get_color_remap_mode", InOutPtr(uint32_t))
+ A427 = Call(uint32_t, "setBrightnessCorrection", uint)
+
+ A435 = Call(uint32_t, "set_block_dcp", arg1=uint64_t, arg2=uint, arg3=uint, arg4=Array(8, ulong), arg5=uint, data=SizedBytes(0x1000, "length"), length=ulong)
+ A439 = Call(uint32_t, "set_parameter_dcp", param=IOMFBParameterName, value=Lazy(SizedArray(4, "count", ulong)), count=uint)
+
+ A440 = Call(uint, "display_width")
+ A441 = Call(uint, "display_height")
+ A442 = Call(void, "get_display_size", OutPtr(uint), OutPtr(uint))
+ A443 = Call(int_, "do_create_default_frame_buffer")
+ A444 = Call(void, "printRegs")
+ A447 = Call(int_, "enable_disable_video_power_savings", uint)
+ A454 = Call(void, "first_client_open")
+ A455 = Call(void, "last_client_close_dcp", OutPtr(uint))
+ A456 = Call(bool_, "writeDebugInfo", ulong)
+ A457 = Call(void, "flush_debug_flags", uint)
+ A458 = Call(bool_, "io_fence_notify", uint, uint, ulong, IOMFBStatus)
+ A460 = Call(bool_, "setDisplayRefreshProperties")
+ A463 = Call(void, "flush_supportsPower", bool_)
+ A464 = Call(uint, "abort_swaps_dcp", InOutPtr(IOMobileFramebufferUserClient))
+
+ A467 = Call(uint, "update_dfb", surf=InPtr(IOSurface))
+ A468 = Call(uint32_t, "setPowerState", ulong, bool_, OutPtr(uint))
+ A469 = Call(bool_, "isKeepOnScreen")
+
+ D552 = Callback(bool_, "setProperty_dict", key=string(0x40), value=InPtr(Padded(0x1000, OSDictionary())))
+ D561 = Callback(bool_, "setProperty_dict", key=string(0x40), value=InPtr(Padded(0x1000, OSDictionary())))
+ D563 = Callback(bool_, "setProperty_int", key=string(0x40), value=InPtr(uint64_t))
+ D565 = Callback(bool_, "setProperty_bool", key=string(0x40), value=InPtr(Bool(uint32_t)))
+ D567 = Callback(bool_, "setProperty_str", key=string(0x40), value=string(0x40))
+
+ D574 = Callback(IOMFBStatus, "powerUpDART", bool_)
+
+ D575 = Callback(bool_, "get_dot_pitch", OutPtr(uint))
+ D576 = Callback(void, "hotPlug_notify_gated", ulong)
+ D577 = Callback(void, "powerstate_notify", bool_, bool_)
+ D578 = Callback(bool_, "idle_fence_create", IdleCachingState)
+ D579 = Callback(void, "idle_fence_complete")
+
+ D581 = Callback(void, "swap_complete_head_of_line", uint, bool_, uint, bool_)
+ D582 = Callback(bool_, "create_default_fb_surface", uint, uint)
+ D583 = Callback(bool_, "serializeDebugInfoCb", ulong, InPtr(uint64_t), uint)
+ D584 = Callback(void, "clear_default_surface")
+
+ D588 = Callback(void, "resize_default_fb_surface_gated")
+ D589 = Callback(void, "swap_complete_ap_gated", swap_id=uint, unkBool=bool_, swap_data=InPtr(SwapCompleteData), swap_info=SwapInfoBlob, unkUint=uint)
+
+ D591 = Callback(void, "swap_complete_intent_gated", swap_id=uint, unkB=bool_, unkInt=uint32_t, width=uint, height=uint)
+ D593 = Callback(void, "enable_backlight_message_ap_gated", bool_)
+ D594 = Callback(void, "setSystemConsoleMode", bool_)
+
+ D596 = Callback(bool_, "isDFBAllocated")
+ D597 = Callback(bool_, "preserveContents")
+ D598 = Callback(void, "find_swap_function_gated")
+
+class ServiceRelay(IPCObject):
+ D400 = Callback(void, "get_property", obj=FourCC, key=string(0x40), value=OutPtr(Bytes(0x200)), lenght=InOutPtr(uint))
+ D401 = Callback(bool_, "sr_get_uint_prop", obj=FourCC, key=string(0x40), value=InOutPtr(ulong))
+ D404 = Callback(void, "sr_set_uint_prop", obj=FourCC, key=string(0x40), value=uint)
+ D406 = Callback(void, "set_fx_prop", obj=FourCC, key=string(0x40), value=uint)
+ D408 = Callback(uint64_t, "sr_getClockFrequency", obj=FourCC, arg=uint)
+ D411 = Callback(IOMFBStatus, "sr_mapDeviceMemoryWithIndex", obj=FourCC, index=uint, flags=uint, addr=OutPtr(ulong), length=OutPtr(ulong))
+ D413 = Callback(bool_, "sr_setProperty_dict", obj=FourCC, key=string(0x40), value=InPtr(Padded(0x1000, OSDictionary())))
+ D414 = Callback(bool_, "sr_setProperty_int", obj=FourCC, key=string(0x40), value=InPtr(uint64_t))
+ D415 = Callback(bool_, "sr_setProperty_bool", obj=FourCC, key=string(0x40), value=InPtr(Bool(uint32_t)))
+
+mem_desc_id = uint
+
+class MemDescRelay(IPCObject):
+ D451 = Callback(mem_desc_id, "allocate_buffer", uint, ulong, uint, OutPtr(ulong), OutPtr(ulong), OutPtr(ulong))
+ D452 = Callback(mem_desc_id, "map_physical", paddr=ulong, size=ulong, flags=uint, dva=OutPtr(ulong), dvasize=OutPtr(ulong))
+ D453 = Callback(mem_desc_id, "withAddressRange", ulong, ulong, uint, uint64_t, OutPtr(uint), OutPtr(ulong))
+ D454 = Callback(IOMFBStatus, "prepare", uint, uint)
+ D455 = Callback(IOMFBStatus, "complete", uint, uint)
+ D456 = Callback(bool_, "release_descriptor", uint)
+
+ALL_CLASSES = [
+ UPPipeAP_H13P,
+ UnifiedPipeline2,
+ IOMobileFramebufferAP,
+ ServiceRelay,
+ PropRelay,
+ UPPipe2,
+ MemDescRelay,
+]
+
+ALL_METHODS = {}
+
+for cls in ALL_CLASSES:
+ ALL_METHODS.update(cls.methods())
+
+SHORT_CHANNELS = {
+ "CB": "d",
+ "CMD": "C",
+ "ASYNC": "a",
+ "OOBCMD": "O",
+ "OOBCB": "o",
+}
+
+RDIR = { ">": "<", "<": ">" }
+
+class Call:
+ def __init__(self, dir, chan, off, msg, in_size, out_size, in_data=b''):
+ self.dir = dir
+ self.chan = chan
+ self.msg = msg
+ self.off = off
+ self.in_size = in_size
+ self.out_size = out_size
+ self.in_data = in_data
+ self.out_data = None
+ self.complete = False
+ self.ret = None
+
+ def ack(self, out_data):
+ self.out_data = out_data
+ self.complete = True
+
+ def print_req(self, indent=""):
+ log = f"{indent}{self.dir}{SHORT_CHANNELS[self.chan]}[{self.off:#x}] {self.msg} "
+
+ cls, method = ALL_METHODS.get(self.msg, (None, None))
+ if cls is None:
+ print(log + f"{self.in_size:#x}/{self.out_size:#x}")
+ return
+
+ log += f"{cls.__name__}::{method.name}("
+ in_size = method.in_struct.sizeof()
+
+ if in_size != len(self.in_data):
+ print(f"{log} !! Expected {in_size:#x} bytes, got {len(self.in_data):#x} bytes (in)")
+ dump_fields(method.in_fields)
+ chexdump(self.in_data)
+ self.in_vals = {}
+ return
+
+ self.in_vals = method.parse_input(self.in_data)
+
+ log += f"{method.fmt_args(self.in_vals)})"
+
+ print(log)
+
+ method.print_long_args(indent, self.in_vals)
+ #if method.in_fields:
+ #print(self.in_vals)
+
+ def print_reply(self, indent=""):
+ assert self.complete
+ log = f"{indent}{RDIR[self.dir]}{SHORT_CHANNELS[self.chan]}[{self.off:#x}] {self.msg} "
+
+ cls, method = ALL_METHODS.get(self.msg, (None, None))
+ if cls is None:
+ print(log + f"{self.in_size:#x}/{self.out_size:#x}")
+ return
+
+ log += f"{cls.__name__}::{method.name}("
+ out_size = method.out_struct.sizeof()
+
+ if out_size != len(self.out_data):
+ print(f"{log} !! Expected {out_size:#x} bytes, got {len(self.out_data):#x} bytes (out)")
+ dump_fields(method.out_fields)
+ chexdump(self.out_data)
+ return
+
+ self.out_vals = method.parse_output(self.out_data, self.in_vals)
+
+ log += f"{method.fmt_args(self.in_vals, self.out_vals)})"
+
+ if "ret" in self.out_vals:
+ self.ret = self.out_vals.ret
+ del self.out_vals["ret"]
+ log += f" = {self.ret!r}"
+
+ print(log)
+
+ method.print_long_args(indent, self.in_vals, self.out_vals)
+ #if len(method.out_fields) - (self.ret is not None):
+ #print(self.out_vals)
diff --git a/tools/proxyclient/m1n1/fw/dcp/manager.py b/tools/proxyclient/m1n1/fw/dcp/manager.py
new file mode 100644
index 0000000..7977c3a
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/dcp/manager.py
@@ -0,0 +1,319 @@
+# SPDX-License-Identifier: MIT
+import pprint
+import struct, functools, time
+from dataclasses import dataclass
+from enum import IntEnum
+
+from construct.lib import hexundump
+
+from ..asc.base import *
+from ...utils import *
+
+from . import ipc
+from .dcpep import CallContext
+
+## DCP API manager
+
+class DCPBaseManager:
+ def __init__(self, dcpep):
+ self.dcpep = dcpep
+ self.dcp = dcpep.asc
+ dcpep.mgr = self
+
+ self.name_map = {}
+ self.tag_map = {}
+
+ self.in_callback = 0
+
+ for k, (cls, v) in ipc.ALL_METHODS.items():
+ self.name_map[v.name] = k, v
+ self.tag_map[k] = v
+
+ def handle_cb(self, state):
+ method = self.tag_map.get(state.tag, None)
+ if method is None:
+ raise Exception(f"Unknown callback {state.tag}")
+
+ func = getattr(self, method.name, None)
+
+ if func is None:
+ raise Exception(f"Unimplemented callback {method!s} [{state.tag}]")
+
+ self.in_callback += 1
+ try:
+ retval = method.callback(func, state.in_data)
+ except Exception as e:
+ print(f"Exception in callback {method.name}")
+ raise
+ self.in_callback -= 1
+ return retval
+
+ def __getattr__(self, attr):
+ tag, method = self.name_map.get(attr, (None, None))
+ if method is None or tag.startswith("D"):
+ raise AttributeError(f"Unknown method {attr}")
+
+ out_len = method.out_struct.sizeof()
+ if self.in_callback:
+ ctx = CallContext.CB
+ else:
+ ctx = CallContext.CMD
+ rpc = functools.partial(self.dcpep.ch_cmd.call, ctx, tag, out_len=out_len)
+ return functools.partial(method.call, rpc)
+
+class DCPManager(DCPBaseManager):
+ def __init__(self, dcpep, compatible='t8103'):
+ super().__init__(dcpep)
+
+ self.iomfb_prop = {}
+ self.dcpav_prop = {}
+ self.service_prop = {}
+ self.pr_prop = {}
+
+ self.swaps = 0
+ self.frame = 0
+
+ self.mapid = 0
+ self.bufs = {}
+
+ self.compatible = compatible
+
+ ## IOMobileFramebufferAP methods
+
+ def find_swap_function_gated(self):
+ pass
+
+ def create_provider_service(self):
+ return True
+
+ def create_product_service(self):
+ return True
+
+ def create_PMU_service(self):
+ return True
+
+ def create_iomfb_service(self):
+ return True
+
+ def create_backlight_service(self):
+ return False
+
+ def setProperty(self, key, value):
+ self.iomfb_prop[key] = value
+ print(f"setProperty({key} = {value!r})")
+ return True
+
+ setProperty_dict = setProperty_int = setProperty_bool = setProperty_str = setProperty
+
+ def swap_complete_ap_gated(self, swap_id, unkBool, swap_data, swap_info, unkUint):
+ swap_data_ptr = "NULL" if swap_data is None else "..."
+ print(f"swap_complete_ap_gated({swap_id}, {unkBool}, {swap_data_ptr}, ..., {unkUint}")
+ if swap_data is not None:
+ chexdump(swap_data)
+ chexdump(swap_info)
+ self.swaps += 1
+ self.frame = swap_id
+
+ def swap_complete_intent_gated(self, swap_id, unkB, unkInt, width, height):
+ print(f"swap_complete_intent_gated({swap_id}, {unkB}, {unkInt}, {width}, {height}")
+ self.swaps += 1
+ self.frame = swap_id
+
+ def enable_backlight_message_ap_gated(self, unkB):
+ print(f"enable_backlight_message_ap_gated({unkB})")
+
+ # wrapper for set_digital_out_mode to print information on the setted modes
+ def SetDigitalOutMode(self, color_id, timing_id):
+ color_mode = [x for x in self.dcpav_prop['ColorElements'] if x['ID'] == color_id][0]
+ timing_mode = [x for x in self.dcpav_prop['TimingElements'] if x['ID'] == timing_id][0]
+ pprint.pprint(color_mode)
+ pprint.pprint(timing_mode)
+ self.set_digital_out_mode(color_id, timing_id)
+
+ ## UPPipeAP_H13P methods
+
+ def did_boot_signal(self):
+ return True
+
+ def did_power_on_signal(self):
+ return True
+
+ def will_power_off_signal(self):
+ return
+
+ def rt_bandwidth_setup_ap(self, config):
+ print("rt_bandwidth_setup_ap(...)")
+ if self.compatible == 't8103':
+ config.val = {
+ "reg1": 0x23b738014, # reg[5] in disp0/dispext0, plus 0x14 - part of pmgr
+ "reg2": 0x23bc3c000, # reg[6] in disp0/dispext0 - part of pmp/pmgr
+ "bit": 2,
+ }
+ elif self.compatible == 't600x':
+ config.val = {
+ "reg1": 0x28e3d0000 + 0x988, # reg[4] in disp0/dispext0, plus 0x988
+ "reg2": 0x0,
+ "bit": 0,
+ }
+ else:
+ raise ValueError(self.compatible)
+
+ ## UnifiedPipeline2 methods
+
+ def match_pmu_service(self):
+ pass
+
+ def set_number_property(self, key, value):
+ pass
+
+ def create_provider_service(self):
+ return True
+
+ def is_dark_boot(self):
+ return False
+
+ def read_edt_data(self, key, count, value):
+ return False
+
+ def UNK_get_some_field(self):
+ return 0
+
+ def start_hardware_boot(self):
+ self.set_create_DFB()
+ self.do_create_default_frame_buffer()
+ self.setup_video_limits()
+ self.flush_supportsPower(True)
+ self.late_init_signal()
+ self.setDisplayRefreshProperties()
+ return True
+
+ def setDCPAVPropStart(self, length):
+ print(f"setDCPAVPropStart({length:#x})")
+ self.dcpav_prop_len = length - 1 # off by one?
+ self.dcpav_prop_off = 0
+ self.dcpav_prop_data = []
+ return True
+
+ def setDCPAVPropChunk(self, data, offset, length):
+ print(f"setDCPAVPropChunk(..., {offset:#x}, {length:#x})")
+ assert offset == self.dcpav_prop_off
+ self.dcpav_prop_data.append(data)
+ self.dcpav_prop_off += len(data)
+ return True
+
+ def setDCPAVPropEnd(self, key):
+ print(f"setDCPAVPropEnd({key!r})")
+ blob = b"".join(self.dcpav_prop_data)
+ assert self.dcpav_prop_len == len(blob)
+ self.dcpav_prop[key] = ipc.OSSerialize().parse(blob)
+ self.dcpav_prop_data = self.dcpav_prop_len = self.dcpav_prop_off = None
+ #pprint.pprint(self.dcpav_prop[key])
+ return True
+
+ def set_boolean_property(self, key, value):
+ print(f"set {key!r} = {value}")
+
+ def removeProperty(self, key):
+ print(f"removeProperty({key!r})")
+
+ def powerstate_notify(self, unk1, unk2):
+ print(f"powerstate_notify({unk1}, {unk2})")
+
+ def create_default_fb_surface(self, width, height):
+ print(f"create_default_fb_surface({width}x{height})")
+ return True
+
+ def powerUpDART(self, unk):
+ print(f"powerUpDART({unk})")
+ return 0
+
+ def hotPlug_notify_gated(self, unk):
+ print(f"hotPlug_notify_gated({unk})")
+
+ def is_waking_from_hibernate(self):
+ return False
+
+ ## UPPipe2 methods
+
+ def match_pmu_service_2(self):
+ return True
+
+ def match_backlight_service(self):
+ return True
+
+ def get_calendar_time_ms(self):
+ return time.time_ns() // 1000_000
+
+ def update_backlight_factor_prop(self, value):
+ pass
+
+ def map_buf(self, buf, vaddr, dva, unk):
+ print(f"map buf {buf}, {unk}")
+ paddr, dcpdva, dvasize = self.bufs[buf]
+ vaddr.val = 0
+ dva.val = self.dcp.disp_dart.iomap(4, paddr, dvasize)
+ print(f"mapped to dva {dva}")
+ return 0
+
+ def update_backlight_factor_prop(self, unk):
+ print(f"update_backlight_factor_prop {unk}")
+
+ ## ServiceRelay methods
+
+ def sr_setProperty(self, obj, key, value):
+ self.service_prop.setdefault(obj, {})[key] = value
+ print(f"sr_setProperty({obj}/{key} = {value!r})")
+ return True
+
+ def sr_getClockFrequency(self, obj, arg):
+ print(f"sr_getClockFrequency({obj}, {arg})")
+ return 533333328
+
+ sr_setProperty_dict = sr_setProperty_int = sr_setProperty_bool = sr_setProperty_str = sr_setProperty
+
+ def sr_get_uint_prop(self, obj, key, value):
+ value.val = 0
+ return False
+
+ def sr_set_uint_prop(self, obj, key, value):
+ print(f"sr_set_uint_prop({obj}, {key} = {value})")
+
+ def set_fx_prop(self, obj, key, value):
+ print(f"set_fx_prop({obj}, {key} = {value})")
+
+ def sr_mapDeviceMemoryWithIndex(self, obj, index, flags, addr, length):
+ assert obj == "PROV"
+ addr.val, length.val = self.dcp.u.adt["/arm-io/disp0"].get_reg(index)
+ print(f"sr_mapDeviceMemoryWithIndex({obj}, {index}, {flags}, {addr.val:#x}, {length.val:#x})")
+ return 0
+
+ ## PropRelay methods
+
+ def pr_publish(self, prop_id, value):
+ self.pr_prop[prop_id] = value
+ print(f"pr_publish({prop_id}, {value!r})")
+
+ ## MemDescRelay methods:
+
+ def allocate_buffer(self, unk0, size, unk1, paddr, dva, dvasize):
+ print(f"allocate_buffer({unk0}, {size}, {unk1})")
+
+ dvasize.val = align_up(size, 4096)
+ paddr.val = self.dcp.u.memalign(0x4000, size)
+ dva.val = self.dcp.dart.iomap(0, paddr.val, size)
+
+ self.mapid += 1
+ print(f"Allocating {self.mapid} as {hex(paddr.val)} / {hex(dva.val)}")
+
+ self.bufs[self.mapid] = (paddr.val, dva.val, dvasize.val)
+
+ return self.mapid
+
+ def map_physical(self, paddr, size, flags, dva, dvasize):
+ dvasize.val = align_up(size, 4096)
+ dva.val = self.dcp.dart.iomap(0, paddr, size)
+ print(f"map_physical({paddr:#x}, {size:#x}, {flags}, {dva.val:#x}, {dvasize.val:#x})")
+
+ self.mapid += 1
+ return self.mapid
+
diff --git a/tools/proxyclient/m1n1/fw/dcp/parse_log.py b/tools/proxyclient/m1n1/fw/dcp/parse_log.py
new file mode 100644
index 0000000..e57f637
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/dcp/parse_log.py
@@ -0,0 +1,43 @@
+# SPDX-License-Identifier: MIT
+
+from m1n1.utils import *
+from m1n1.fw.dcp.ipc import *
+
+def parse_log(fd):
+ op_stack = {}
+ for line in fd:
+ optype, args = line.split(" ", 1)
+ if optype == "CALL":
+ d, msg, chan, off, msg, in_size, out_size, in_data = args.split(" ")
+ op = Call(d, chan, int(off, 0), msg, int(in_size, 0), int(out_size, 0),
+ bytes.fromhex(in_data))
+ op_stack.setdefault(chan, []).append(op)
+ elif optype == "ACK":
+ d, msg, chan, off, out_data = args.split(" ")
+ op = op_stack[chan].pop()
+ assert int(off, 0) == op.off
+ op.ack(bytes.fromhex(out_data))
+ else:
+ raise Exception(f"Unknown log cmd {optype}")
+
+ yield op
+
+def dump_log(fd):
+ nesting = {
+ "": 0,
+ "OOB": 0,
+ }
+ for op in parse_log(fd):
+ ctx = ""
+ if "OOB" in op.chan:
+ ctx = "[OOB] -----------> "
+ if not op.complete:
+ op.print_req(indent=ctx + " " * nesting.setdefault(ctx, 0))
+ nesting[ctx] += 1
+ else:
+ nesting[ctx] -= 1
+ op.print_reply(indent=ctx + " " * nesting.setdefault(ctx, 0))
+
+if __name__ == "__main__":
+ import sys
+ dump_log(open(sys.argv[1]))
diff --git a/tools/proxyclient/m1n1/fw/mtp.py b/tools/proxyclient/m1n1/fw/mtp.py
new file mode 100644
index 0000000..5b22b8a
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/mtp.py
@@ -0,0 +1,411 @@
+# SPDX-License-Identifier: MIT
+
+import struct
+from construct import *
+from ..constructutils import *
+from ..utils import *
+
+class HIDDescriptor(ConstructClass):
+ subcon = Struct(
+ "descriptor" / HexDump(GreedyBytes)
+ )
+
+class GPIOInit(ConstructClass):
+ subcon = Struct(
+ "unk1" / Int16ul,
+ "gpio_id"/ Int16ul,
+ "gpio_name" / PaddedString(32, "ascii")
+ )
+
+class InitBlock(ConstructClass):
+ subcon = Struct(
+ "type" / Int16ul,
+ "subtype" / Int16ul,
+ "length" / Int16ul,
+ "payload" / FixedSized(this.length,
+ Switch(this.type, {
+ 0: HIDDescriptor,
+ 1: GPIOInit,
+ 2: Bytes(0),
+ }, default=GreedyBytes))
+ )
+
+class InitMsg(ConstructClass):
+ subcon = Struct(
+ "msg_type" / Const(0xf0, Int8ul),
+ "msg_subtype" / Const(0x01, Int8ul),
+ "unk" / Const(0x00, Int8ul),
+ "device_id" / Int8ul,
+ "device_name" / PaddedString(16, "ascii"),
+ "msg" / RepeatUntil(lambda obj, lst, ctx: lst[-1].type == 2, InitBlock)
+ )
+
+class DeviceReadyMsg(ConstructClass):
+ subcon = Struct(
+ "msg_type" / Const(0xf1, Int8ul),
+ "device_id" / Int8ul,
+ "unk" / Int16ul
+ )
+
+class GPIORequestMsg(ConstructClass):
+ subcon = Struct(
+ "msg_type" / Const(0xa0, Int8ul),
+ "device_id" / Int8ul,
+ "gpio_num" / Int8ul,
+ "cmd" / Int16ul,
+ "args" / HexDump(GreedyBytes)
+ )
+
+NotificationMsg = Select(
+ DeviceReadyMsg,
+ InitMsg,
+ GPIORequestMsg,
+ HexDump(GreedyBytes),
+)
+
+class UnkDeviceControlMsg(ConstructClass):
+ subcon = Struct(
+ "command" / Int8ul,
+ "args" / HexDump(GreedyBytes),
+ )
+
+class DeviceEnableMsg(ConstructClass):
+ subcon = Struct(
+ "command" / Const(0xb4, Int8ul),
+ "device_id" / Int8ul,
+ )
+
+class DeviceResetMsg(ConstructClass):
+ subcon = Struct(
+ "command" / Const(0x40, Int8ul),
+ "unk1" / Int8ul,
+ "device_id" / Int8ul,
+ "state" / Int8ul,
+ )
+
+class InitBufMsg(ConstructClass):
+ subcon = Struct(
+ "command" / Const(0x91, Int8ul),
+ "unk1" / Int8ul,
+ "unk2" / Int8ul,
+ "buf_addr" / Int64ul,
+ "buf_size" / Int32ul,
+ )
+
+class InitAFEMsg(ConstructClass):
+ subcon = Struct(
+ "command" / Const(0x95, Int8ul),
+ "unk1" / Int8ul,
+ "unk2" / Int8ul,
+ "iface" / Int8ul,
+ "buf_addr" / Int64ul,
+ "buf_size" / Int32ul,
+ )
+
+class UnkMsgC1(ConstructClass):
+ subcon = Struct(
+ "command" / Const(0xc1, Int8ul),
+ "unk1" / Int8ul,
+ )
+
+class GPIOAckMsg(ConstructClass):
+ subcon = Struct(
+ "command" / Const(0xa1, Int8ul),
+ "unk" / Int32ul,
+ "msg" / GPIORequestMsg,
+ )
+
+DeviceControlMsg = Select(
+ DeviceEnableMsg,
+ DeviceResetMsg,
+ InitAFEMsg,
+ InitBufMsg,
+ UnkMsgC1,
+ UnkDeviceControlMsg
+)
+
+class DeviceControlAck(ConstructClass):
+ subcon = Struct(
+ "command" / Int8ul
+ )
+
+class MessageHeader(ConstructClass):
+ subcon = Struct(
+ "flags" / Int16ul,
+ "length" / Int16ul,
+ "retcode" / Int32ul,
+ )
+
+class TXMessage(ConstructClass):
+ subcon = Struct(
+ "hdr" / MessageHeader,
+ "msg" / FixedSized(this.hdr.length,
+ Switch(this.hdr.flags, {
+ 0x40: HexDump(GreedyBytes),
+ 0x80: DeviceControlMsg,
+ 0x81: Int8ul,
+ }))
+ )
+
+ def __init__(self):
+ self.hdr = MessageHeader()
+
+class RXMessage(ConstructClass):
+ subcon = Struct(
+ "hdr" / MessageHeader,
+ "msg" / FixedSized(this.hdr.length, HexDump(GreedyBytes)),
+ )
+
+class MTPInterface:
+ def __init__(self, proto, iface):
+ self.proto = proto
+ self.iface = iface
+ self.tx_seq = 0
+ self.initialized = False
+ self.gpios = {}
+
+ def send(self, msg):
+ self.proto.send(self.iface, self.tx_seq & 0xff, msg)
+ self.tx_seq += 1
+
+ def get_report(self, idx):
+ msg = TXMessage()
+ msg.hdr.flags = 0x81
+ msg.hdr.length = 1
+ msg.hdr.retcode = 0
+ msg.msg = idx
+ self.send(msg.build())
+
+ def packet(self, pkt):
+ self.log(f"RX: {pkt.hex()}")
+
+ def log(self, s):
+ self.proto.log(f"[{self.NAME}] " + s)
+
+ def initialize(self):
+ self.proto.comm.enable_device(self.iface)
+
+ def report(self, msg):
+ self.log(f"report: {msg.hex()}")
+
+ def ack(self, msg):
+ self.log(f"ack: {msg.hex()}")
+
+ def unk(self, msg):
+ self.log(f"unk: {msg.hex()}")
+
+ def packet(self, pkt):
+ msg = RXMessage.parse(pkt)
+ mtype = msg.hdr.flags
+ #self.log(f"FL:{msg.hdr.flag s:04x} unk:{msg.hdr.unk:08x}")
+ if mtype == 0x00:
+ self.report(msg.msg)
+ elif mtype == 0x80:
+ self.ack(msg.hdr.retcode, msg.msg)
+ elif mtype == 0x81:
+ self.log(f"REPORT")
+ chexdump(msg.msg, print_fn=self.log)
+ elif mtype == 0x40:
+ self.unk(msg.msg)
+
+ def __str__(self):
+ return f"{self.iface}/{self.NAME}"
+
+
+class MTPCommInterface(MTPInterface):
+ NAME = "comm"
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.last_cmd = None
+ self.gpios = {}
+
+
+ def device_control(self, dcmsg):
+ while self.last_cmd is not None:
+ self.proto.work()
+ msg = TXMessage()
+ msg.hdr.flags = 0x80
+ msg.hdr.length = len(dcmsg.build())
+ msg.hdr.retcode = 0
+ msg.msg = dcmsg
+ #self.log(f"Send device control {dcmsg}")
+ self.last_cmd = dcmsg.command
+ self.send(msg.build())
+ while self.last_cmd is not None:
+ self.proto.work()
+
+ def enable_device(self, iface):
+ msg = DeviceEnableMsg()
+ msg.device_id = iface
+ self.device_control(msg)
+
+ def report(self, msg):
+ msg = NotificationMsg.parse(msg)
+
+ if isinstance(msg, DeviceReadyMsg):
+ iface = self.proto.iface[msg.device_id]
+ iface.initialized = True
+ self.log(f"{iface}: init complete")
+ elif isinstance(msg, InitMsg):
+ iface = self.proto.get_interface(msg.device_id, msg.device_name)
+ for blk in msg.msg:
+ if isinstance(blk.payload, HIDDescriptor):
+ self.log(f"Got HID descriptor for {iface}:")
+ iface.descriptor = blk.payload.descriptor
+ self.log(hexdump(iface.descriptor))
+ iface.initialize()
+ elif isinstance(blk.payload, GPIOInit):
+ self.log(f"GPIO Init: {blk.payload}")
+ prop = getattr(self.proto.node[msg.device_name],
+ f"function-{blk.payload.gpio_name}".replace("-", "_"))
+ key = struct.pack(">I", prop.args[0]).decode("ascii")
+ val = prop.args[1]
+ self.log(f"GPIO key: {key}")
+ self.gpios[(msg.device_id, blk.payload.gpio_id)] = key, val
+ elif isinstance(msg, GPIORequestMsg):
+ self.log(f"GPIO request: {msg}")
+ smcep = self.proto.smc.epmap[0x20]
+ key, val = self.gpios[(msg.device_id, msg.gpio_num)]
+ if msg.cmd == 3:
+ smcep.write32(key, val | 1)
+ smcep.write32(key, val)
+
+ ackmsg = GPIOAckMsg()
+ ackmsg.unk = 0
+ ackmsg.msg = msg
+ self.device_control(ackmsg)
+
+ def ack(self, retcode, msg):
+ msg = DeviceControlAck.parse(msg)
+ self.log(f"Got ACK for {msg.command:#x}: {retcode:08x}")
+ assert msg.command == self.last_cmd
+ self.last_cmd = None
+
+ def init_afe(self, iface, data):
+ paddr, dva = self.proto.mtp.ioalloc(len(data))
+ self.proto.u.iface.writemem(paddr, data)
+
+ afemsg = InitAFEMsg()
+ afemsg.unk1 = 2
+ afemsg.unk2 = 0
+ afemsg.iface = iface
+ afemsg.buf_addr = dva
+ afemsg.buf_size = len(data)
+ self.device_control(afemsg)
+
+ def device_reset(self, iface, unk1, state):
+ self.log(f"device_reset({iface}, {unk1}, {state})")
+ rmsg = DeviceResetMsg()
+ rmsg.device_id = iface
+ rmsg.unk1 = unk1
+ rmsg.state = state
+ self.device_control(rmsg)
+
+class MTPHIDInterface(MTPInterface):
+ pass
+
+class MTPMultitouchInterface(MTPHIDInterface):
+ NAME = "multi-touch"
+
+ def initialize(self):
+ super().initialize()
+
+ #data = open("afe.bin", "rb").read()
+ #self.proto.comm.init_afe(self.iface, data)
+ #self.proto.comm.device_reset(self.iface, 1, 0)
+ #self.proto.comm.device_reset(self.iface, 1, 2)
+
+class MTPKeyboardInterface(MTPHIDInterface):
+ NAME = "keyboard"
+
+class MTPSTMInterface(MTPHIDInterface):
+ NAME = "stm"
+
+class MTPActuatorInterface(MTPHIDInterface):
+ NAME = "actuator"
+
+class MTPTPAccelInterface(MTPHIDInterface):
+ NAME = "tp_accel"
+
+class MTPProtocol:
+ INTERFACES = [
+ MTPCommInterface,
+ MTPMultitouchInterface,
+ MTPKeyboardInterface,
+ MTPSTMInterface,
+ MTPActuatorInterface,
+ MTPTPAccelInterface,
+ ]
+
+ def __init__(self, u, node, mtp, dockchannel, smc):
+ self.node = node
+ self.smc = smc
+ self.u = u
+ self.mtp = mtp
+ self.dockchannel = dockchannel
+ self.iface = {}
+
+ # Add initial comm interface
+ self.get_interface(0, "comm")
+
+ def get_interface(self, iface, name):
+ if iface in self.iface:
+ return self.iface[iface]
+
+ for cls in self.INTERFACES:
+ if cls.NAME == name:
+ break
+ else:
+ self.log(f"Unknown interface name {name}")
+ return None
+ obj = cls(self, iface)
+ self.iface[iface] = obj
+ setattr(self, name.replace("-", "_"), obj)
+ return obj
+
+ def checksum(self, d):
+ assert len(d) % 4 == 0
+ c = len(d) // 4
+ return 0xffffffff - sum(struct.unpack(f"<{c}I", d)) & 0xffffffff
+
+ def read_pkt(self):
+ self.mtp.work_pending()
+ hdr = self.dockchannel.read(8)
+ hlen, mtype, size, ctr, devid, pad = struct.unpack("<BBHBBH", hdr)
+ #self.log(f"<L:{hlen} T:{mtype:02x} S:{size:04x} D:{devid}")
+ assert hlen == 8
+ #assert mtype == 0x12
+ data = self.dockchannel.read(size)
+ checksum = struct.unpack("<I", self.dockchannel.read(4))[0]
+ expect = self.checksum(hdr + data)
+ if expect != checksum:
+ self.log(f"Checksum error: expected {expect:08x}, got {checksum:08x}")
+ return devid, data
+
+ def send(self, iface, seq, msg):
+ if len(msg) % 4:
+ msg += bytes(4 - len(msg) % 4)
+ hdr = struct.pack("<BBHBBH", 8, 0x11, len(msg), seq, iface, 0)
+ checksum = self.checksum(hdr + msg)
+ pkt = hdr + msg + struct.pack("<I", checksum)
+ self.dockchannel.write(pkt)
+ self.mtp.work_pending()
+
+ def work_pending(self):
+ self.mtp.work_pending()
+ while self.dockchannel.rx_count != 0:
+ self.work()
+ self.mtp.work_pending()
+
+ def work(self):
+ devid, pkt = self.read_pkt()
+ self.iface[devid].packet(pkt)
+
+ def wait_init(self, name):
+ self.log(f"Waiting for {name}...")
+ while not hasattr(self, name) or not getattr(self, name).initialized:
+ self.work()
+
+ def log(self, m):
+ print("[MTP]" + m)
diff --git a/tools/proxyclient/m1n1/fw/pmp.py b/tools/proxyclient/m1n1/fw/pmp.py
new file mode 100644
index 0000000..fbfef45
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/pmp.py
@@ -0,0 +1,168 @@
+# SPDX-License-Identifier: MIT
+import struct
+
+from ..utils import *
+
+from .asc import StandardASC
+from .asc.base import *
+
+class PMPMessage(Register64):
+ TYPE = 56, 44
+
+class PMP_Startup(PMPMessage):
+ TYPE = 56, 44, Constant(0x00)
+
+class PMP_Configure(PMPMessage):
+ TYPE = 56, 44, Constant(0x10)
+ DVA = 47, 0
+
+class PMP_Configure_Ack(PMPMessage):
+ TYPE = 56, 44, Constant(0x20)
+ UNK = 47, 0
+
+class PMP_Init1(PMPMessage):
+ TYPE = 56, 44, Constant(0x200)
+ UNK1 = 43, 16
+ UNK2 = 15, 0
+
+class PMP_Init1_Ack(PMPMessage):
+ TYPE = 56, 44, Constant(0x201)
+ UNK1 = 43, 16
+ UNK2 = 15, 0
+
+class PMP_Init2(PMPMessage):
+ TYPE = 56, 44, Constant(0x202)
+ UNK1 = 43, 16
+ UNK2 = 15, 0
+
+class PMP_Init2_Ack(PMPMessage):
+ TYPE = 56, 44, Constant(0x203)
+ UNK1 = 43, 16
+ UNK2 = 15, 0
+
+class PMP_Unk(PMPMessage):
+ TYPE = 56, 44, Constant(0x100)
+ UNK1 = 43, 16
+ UNK2 = 15, 0
+
+class PMP_Unk_Ack(PMPMessage):
+ TYPE = 56, 44, Constant(0x110)
+ UNK1 = 43, 16
+ UNK2 = 15, 0
+
+class PMP_DevPwr(PMPMessage):
+ TYPE = 56, 44, Constant(0x20e)
+ DEV = 31, 16
+ STATE = 15, 0
+
+class PMP_DevPwr_Sync(PMPMessage):
+ TYPE = 56, 44, Constant(0x208)
+ DEV = 31, 16
+ STATE = 15, 0
+
+class PMP_DevPwr_Ack(PMPMessage):
+ TYPE = 56, 44, Constant(0x209)
+ DEV = 31, 16
+ STATE = 15, 0
+
+class PMPEndpoint(ASCBaseEndpoint):
+ BASE_MESSAGE = PMPMessage
+ SHORT = "pmpep"
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.shmem = self.shmem_dva = None
+ self.init_complete = False
+ self.init1_acked = False
+ self.init2_acked = False
+ self.unk_acked = False
+
+ @msg_handler(0x00, PMP_Startup)
+ def Startup(self, msg):
+ self.log("Starting up")
+
+ self.shmem, self.shmem_dva = self.asc.ioalloc(0x10000)
+
+ self.send_init_config()
+ return True
+
+ def send_init_config(self):
+ self.asc.p.memset32(self.shmem, 0, 0x10000)
+ dram_config = self.asc.u.adt["arm-io/pmp/iop-pmp-nub"].energy_model_dram_configs
+ self.asc.iface.writemem(self.shmem + 0x2000, dram_config)
+
+ node = self.asc.u.adt["arm-io/pmp"]
+
+ maps = []
+ dva = 0xc0000000
+ for i in range(3, len(node.reg)):
+ addr, size = node.get_reg(i)
+ if size == 0:
+ maps.append(struct.pack("<QQ", 0, 0))
+ continue
+
+ self.asc.dart.iomap_at(0, dva, addr, size)
+ self.log(f"map {addr:#x} -> {dva:#x} [{size:#x}]")
+ maps.append(struct.pack("<QQ", dva, size))
+ dva += align(size, 0x4000)
+
+ chexdump(b"".join(maps))
+
+ self.asc.iface.writemem(self.shmem + 0xe000, b"".join(maps))
+ self.send(PMP_Configure(DVA=self.shmem_dva))
+
+ while not self.init_complete:
+ self.asc.work()
+ return True
+
+ @msg_handler(0x20, PMP_Configure_Ack)
+ def Configure_Ack(self, msg):
+ self.init_complete = True
+
+ props = self.asc.iface.readmem(self.shmem, 0x2000)
+ devinfo = self.asc.iface.readmem(self.shmem + 0x4000, 0x1000)
+ status = self.asc.iface.readmem(self.shmem + 0xc000, 0x100)
+
+ print("PMP Props:")
+ chexdump(props)
+ print("PMP Device Info:")
+ chexdump(devinfo)
+ print("PMP Status:")
+ chexdump(status)
+
+ self.send(PMP_Init1(UNK1=1, UNK2=3))
+ while not self.init1_acked:
+ self.asc.work()
+
+ self.send(PMP_Init2(UNK1=1, UNK2=0))
+ while not self.init2_acked:
+ self.asc.work()
+
+ self.send(PMP_Unk(UNK1=0x3bc, UNK2=2))
+ while not self.unk_acked:
+ self.asc.work()
+
+ return True
+
+ @msg_handler(0x201, PMP_Init1_Ack)
+ def Init1_Ack(self, msg):
+ self.init1_acked = True
+ return True
+
+ @msg_handler(0x203, PMP_Init2_Ack)
+ def Init2_Ack(self, msg):
+ self.init2_acked = True
+ return True
+
+ @msg_handler(0x110, PMP_Unk_Ack)
+ def Unk_Ack(self, msg):
+ self.unk_acked = True
+ return True
+
+
+class PMPClient(StandardASC):
+ pass
+
+ ENDPOINTS = {
+ 0x20: PMPEndpoint,
+ }
diff --git a/tools/proxyclient/m1n1/fw/smc.py b/tools/proxyclient/m1n1/fw/smc.py
new file mode 100644
index 0000000..204501c
--- /dev/null
+++ b/tools/proxyclient/m1n1/fw/smc.py
@@ -0,0 +1,202 @@
+# SPDX-License-Identifier: MIT
+import struct
+
+from ..utils import *
+
+from .asc import StandardASC
+from .asc.base import *
+
+SMC_READ_KEY = 0x10
+SMC_WRITE_KEY = 0x11
+SMC_GET_KEY_BY_INDEX = 0x12
+SMC_GET_KEY_INFO = 0x13
+SMC_INITIALIZE = 0x17
+SMC_NOTIFICATION = 0x18
+SMC_RW_KEY = 0x20
+
+class SMCMessage(Register64):
+ TYPE = 7, 0
+ UNK = 11, 8, Constant(0)
+ ID = 15, 12
+
+class SMCInitialize(SMCMessage):
+ TYPE = 7, 0, Constant(SMC_INITIALIZE)
+
+class SMCGetKeyInfo(SMCMessage):
+ TYPE = 7, 0, Constant(SMC_GET_KEY_INFO)
+ KEY = 63, 32
+
+class SMCGetKeyByIndex(SMCMessage):
+ TYPE = 7, 0, Constant(SMC_GET_KEY_BY_INDEX)
+ INDEX = 63, 32
+
+class SMCWriteKey(SMCMessage):
+ TYPE = 7, 0, Constant(SMC_WRITE_KEY)
+ SIZE = 23, 16
+ KEY = 63, 32
+
+class SMCReadKey(SMCMessage):
+ TYPE = 7, 0, Constant(SMC_READ_KEY)
+ SIZE = 23, 16
+ KEY = 63, 32
+
+class SMCReadWriteKey(SMCMessage):
+ TYPE = 7, 0, Constant(SMC_RW_KEY)
+ RSIZE = 23, 16
+ WSIZE = 31, 24
+ KEY = 63, 32
+
+class SMCResult(Register64):
+ RESULT = 7, 0
+ ID = 15, 12
+ SIZE = 31, 16
+ VALUE = 63, 32
+
+class SMCError(Exception):
+ pass
+
+class SMCEndpoint(ASCBaseEndpoint):
+ BASE_MESSAGE = SMCMessage
+ SHORT = "smcep"
+ TYPE_MAP = {
+ "ui64": ("<Q", None),
+ "ui32": ("<I", None),
+ "ui16": ("<H", None),
+ "ui8 ": ("<B", None),
+ "si64": ("<q", None),
+ "si32": ("<i", None),
+ "si16": ("<h", None),
+ "si8 ": ("<b", None),
+ "flag": ("<B", None),
+ "flt ": ("<f", None),
+ "hex_": (None, hexdump),
+ "ch8*": (None, lambda c: c.split(b"\x00")[0]),
+ "ioft": ("<Q", None),
+ }
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.shmem = None
+ self.msgid = 0
+ self.outstanding = set()
+ self.ret = {}
+
+ def start(self):
+ self.send(SMCInitialize(ID = 0))
+ self.msgid += 1 # important!
+ while self.shmem is None:
+ self.asc.work()
+
+ def new_msgid(self):
+ mid = (self.msgid & 0xF)
+ self.msgid += 1
+ assert(mid not in self.outstanding)
+ self.outstanding.add(mid)
+ return mid
+
+ def cmd(self, cmd):
+ cmd.ID = self.new_msgid()
+ self.send(cmd)
+ while cmd.ID in self.outstanding:
+ self.asc.work()
+ ret = self.ret[cmd.ID]
+ if ret.RESULT != 0:
+ raise SMCError(f"SMC error {ret}", ret)
+ return ret
+
+ def write(self, key, data):
+ key = int.from_bytes(key.encode("ascii"), byteorder="big")
+ self.asc.iface.writemem(self.shmem, data)
+ self.cmd(SMCWriteKey(KEY = key, SIZE = len(data)))
+
+ def read(self, key, size):
+ key = int.from_bytes(key.encode("ascii"), byteorder="big")
+ ret = self.cmd(SMCReadKey(KEY = key, SIZE = size))
+ if size <= 4:
+ return struct.pack("<I", ret.VALUE)[:size]
+ else:
+ return self.asc.iface.readmem(self.shmem, ret.SIZE)
+
+ def rw(self, key, data, outsize):
+ key = int.from_bytes(key.encode("ascii"), byteorder="big")
+ self.asc.iface.writemem(self.shmem, data)
+ ret = self.cmd(SMCReadWriteKey(KEY=key, RSIZE=outsize, WSIZE=len(data)))
+ if outsize <= 4:
+ return struct.pack("<I", ret.VALUE)[:outsize]
+ else:
+ return self.asc.iface.readmem(self.shmem, ret.SIZE)
+
+ def get_key_by_index(self, index):
+ ret = self.cmd(SMCGetKeyByIndex(INDEX = index))
+ key = ret.VALUE.to_bytes(4, byteorder="little").decode("ascii")
+ return key
+
+ def get_key_info(self, key):
+ key = int.from_bytes(key.encode("ascii"), byteorder="big")
+ ret = self.cmd(SMCGetKeyInfo(KEY = key))
+ info = self.asc.iface.readmem(self.shmem, 6)
+ length, type, flags = struct.unpack("B4sB", info)
+ return length, type.decode("ascii"), flags
+
+ def read64(self, key):
+ return struct.unpack("<Q", self.read(key, 8))[0]
+
+ def read32(self, key):
+ return struct.unpack("<I", self.read(key, 4))[0]
+
+ def read16(self, key):
+ return struct.unpack("<H", self.read(key, 2))[0]
+
+ def read8(self, key):
+ return struct.unpack("<B", self.read(key, 1))[0]
+
+ def read32b(self, key):
+ return struct.unpack(">I", self.read(key, 4))[0]
+
+ def write64(self, key, data):
+ self.write(key, struct.pack("<Q", data))
+
+ def write32(self, key, data):
+ self.write(key, struct.pack("<I", data))
+
+ def write16(self, key, data):
+ self.write(key, struct.pack("<H", data))
+
+ def write8(self, key, data):
+ self.write(key, struct.pack("<B", data))
+
+ def rw32(self, key, data):
+ return struct.unpack("<I", self.rw(key, struct.pack("<I", data), 4))[0]
+
+ def read_type(self, key, size, typecode):
+ fmt, func = self.TYPE_MAP.get(typecode, (None, None))
+
+ val = self.read(key, size)
+
+ if fmt:
+ val = struct.unpack(fmt, val)[0]
+ if func:
+ val = func(val)
+ return val
+
+ def handle_msg(self, msg0, msg1):
+ if self.shmem is None:
+ self.log("Starting up")
+ self.shmem = msg0
+ else:
+ msg = SMCResult(msg0)
+ ret = msg.RESULT
+ mid = msg.ID
+ if ret == SMC_NOTIFICATION:
+ self.log(f"Notification: {msg.VALUE:#x}")
+ return True
+ #print(f"msg {mid} return value {ret}")
+ self.outstanding.discard(mid)
+ self.ret[mid] = msg
+
+ return True
+
+class SMCClient(StandardASC):
+ ENDPOINTS = {
+ 0x20: SMCEndpoint,
+ }
diff --git a/tools/proxyclient/m1n1/gpiola.py b/tools/proxyclient/m1n1/gpiola.py
new file mode 100644
index 0000000..bbfd71e
--- /dev/null
+++ b/tools/proxyclient/m1n1/gpiola.py
@@ -0,0 +1,254 @@
+# SPDX-License-Identifier: MIT
+import os, sys, struct, time
+
+from .utils import *
+from . import asm
+from .proxy import REGION_RX_EL1
+from .sysreg import *
+
+class GPIOLogicAnalyzer(Reloadable):
+ def __init__(self, u, node=None, pins={}, regs={}, div=1, cpu=1, on_pin_change=True, on_reg_change=True):
+ self.u = u
+ self.p = u.proxy
+ self.iface = u.iface
+ self.cpu = cpu
+ self.base = 0
+ if node is not None:
+ self.base = u.adt[node].get_reg(0)[0]
+ else:
+ on_pin_change=False
+ self.node = node
+ self.pins = pins
+ self.regs = regs
+ assert len(pins) <= 32
+ assert div > 0
+ self.div = div
+ self.cbuf = self.u.malloc(0x1000)
+ self.dbuf = None
+ self.on_pin_change = on_pin_change
+ self.on_reg_change = on_reg_change
+ self.p.mmu_init_secondary(cpu)
+ self.tfreq = u.mrs(CNTFRQ_EL0)
+
+ def load_regmap(self, regmap, skip=set(), regs=set()):
+ base = regmap._base
+ for name, (addr, rcls) in regmap._namemap.items():
+ if name not in skip and (not regs or name in regs):
+ self.regs[name] = base + addr, rcls
+
+ def start(self, ticks, bufsize=0x10000):
+ self.bufsize = bufsize
+ if self.dbuf:
+ self.u.free(self.dbuf)
+ self.dbuf = self.u.malloc(bufsize)
+
+ text = f"""
+ trace:
+ mov x16, x2
+ add x3, x3, x2
+ add x2, x2, #4
+ mov x12, #-8
+ mov x10, x2
+ mov x6, #-1
+ mov x7, #0
+ ldr x8, ={self.base}
+ mrs x4, CNTPCT_EL0
+ isb
+ 1:
+ ldr w15, [x16]
+ cmp w15, #1
+ b.eq done
+ add x4, x4, x1
+ 2:
+ mrs x5, CNTPCT_EL0
+ isb
+ """
+ if self.div > 1:
+ text += f"""
+ cmp x5, x4
+ b.lo 2b
+ """
+
+ for idx, pin in enumerate(self.pins.values()):
+ text += f"""
+ ldr w9, [x8, #{pin * 4}]
+ bfi x7, x9, #{idx}, #1
+ """
+
+ if self.on_pin_change:
+ text += f"""
+ cmp x7, x6
+ b.eq 3f
+ mov x6, x7
+ """
+ if self.on_reg_change:
+ text += f"""
+ mov x11, x2
+ """
+
+ text += f"""
+ str w5, [x2], #4
+ str w7, [x2], #4
+ """
+ if self.on_reg_change:
+ text += f"""
+ mov x13, #0
+ add x14, x12, #8
+ """
+
+ for reg in self.regs.values():
+ if isinstance(reg, tuple):
+ reg = reg[0]
+ text += f"""
+ ldr x9, ={reg}
+ ldr w9, [x9]
+ str w9, [x2], #4
+ """
+ if self.on_reg_change:
+ text += f"""
+ eor w15, w9, #1
+ cmp x14, #0
+ b.eq 4f
+ ldr w15, [x14], #4
+ 4:
+ eor w15, w15, w9
+ orr w13, w13, w15
+ """
+
+ if self.on_reg_change:
+ text += f"""
+ cmp x13, #0
+ b.ne 4f
+ mov x2, x11
+ mov x11, x12
+ b 3f
+ 4:
+ """
+ text += f"""
+ mov x12, x11
+ cmp x2, x3
+ b.hs done
+ 3:
+ sub x0, x0, #1
+ cbnz x0, 1b
+ done:
+ sub x0, x2, x10
+ ret
+ """
+
+ code = asm.ARMAsm(text, self.cbuf)
+ self.iface.writemem(self.cbuf, code.data)
+ self.p.dc_cvau(self.cbuf, len(code.data))
+ self.p.ic_ivau(self.cbuf, len(code.data))
+
+ self.p.write32(self.dbuf, 0)
+
+ self.p.smp_call(self.cpu, code.trace | REGION_RX_EL1, ticks, self.div, self.dbuf, bufsize - (8 + 4 * len(self.regs)))
+
+ def complete(self):
+ self.p.write32(self.dbuf, 1)
+ wrote = self.p.smp_wait(self.cpu)
+ assert wrote <= self.bufsize
+ data = self.iface.readmem(self.dbuf + 4, wrote)
+ self.u.free(self.dbuf)
+ self.dbuf = None
+
+ stride = 2 + len(self.regs)
+
+ #chexdump(data)
+
+ self.data = [struct.unpack("<" + "I" * stride,
+ data[i:i + 4 * stride])
+ for i in range(0, len(data), 4 * stride)]
+
+ def vcd(self):
+ off = self.data[0][0]
+ if False: #len(self.data) > 1:
+ off2 = max(0, ((self.data[1][0] - off) & 0xffffffff) - 5000)
+ else:
+ off2 = 0
+
+ #print(off, off2)
+
+ vcd = []
+ vcd.append("""
+$timescale 1ns $end
+$scope module gpio $end
+""")
+ sym = 0
+ keys = []
+ rkeys = []
+
+ for name in self.pins:
+ keys.append(f"s{sym}")
+ vcd.append(f"$var wire 1 s{sym} {name} $end\n")
+ sym += 1
+ for name, reg in self.regs.items():
+ vcd.append(f"$var reg 32 s{sym} {name} [31:0] $end\n")
+ if isinstance(reg, tuple):
+ subkeys = {}
+ rcls = reg[1]
+ rkeys.append((f"s{sym}", rcls, subkeys))
+ sym += 1
+ for fname in rcls().fields.keys():
+ fdef = getattr(rcls, fname)
+ if isinstance(fdef, tuple):
+ width = fdef[0] - fdef[1] + 1
+ else:
+ width = 1
+ vcd.append(f"$var reg {width} s{sym} {name}.{fname} [{width-1}:0] $end\n")
+ subkeys[fname] = (width, f"s{sym}")
+ sym += 1
+ else:
+ rkeys.append((f"s{sym}", None, None))
+ sym += 1
+ vcd.append("""
+$enddefinitions $end
+$dumpvars
+""")
+
+ for v in self.data:
+ ts = v[0]
+ val = v[1]
+ regs = v[2:]
+ ts = ((ts - off) & 0xffffffff) - off2
+ ns = max(0, 1000000000 * ts // self.tfreq)
+ vcd.append(f"#{ns}\n")
+ vcd.append("\n".join(f"{(val>>i) & 1}{k}" for i, k in enumerate(keys)) + "\n")
+ for (key, rcls, subkeys), v in zip(rkeys, regs):
+ vcd.append(f"b{v:032b} {key}\n")
+ if rcls:
+ rval = rcls(v)
+ for field, (width, key) in subkeys.items():
+ v = getattr(rval, field)
+ vcd.append(f"b{v:0{width}b} {key}\n")
+
+
+ ns += ns//10
+ vcd.append(f"#{ns}\n" + "\n".join(f"{(val>>i) & 1}{k}" for i, k in enumerate(keys)) + "\n")
+
+ return "".join(vcd)
+
+ def show(self):
+ with open("/tmp/dump.vcd", "w") as fd:
+ fd.write(self.vcd())
+
+ gtkw = ("""
+[dumpfile] "/tmp/dump.vcd"
+[timestart] 0
+[size] 3063 1418
+[pos] -1 -1
+*-17.000000 2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
+[sst_width] 288
+[signals_width] 197
+[sst_expanded] 1
+[sst_vpaned_height] 421
+@23
+""" +
+ "\n".join("gpio." + k for k in self.pins) + "\n" +
+ "\n".join("gpio." + k + "[31:0]" for k in self.regs) + "\n")
+
+ with open("/tmp/dump.gtkw", "w") as fd:
+ fd.write(gtkw)
+
+ os.system("gtkwave /tmp/dump.gtkw&")
diff --git a/tools/proxyclient/m1n1/hostutils.py b/tools/proxyclient/m1n1/hostutils.py
new file mode 100644
index 0000000..535240b
--- /dev/null
+++ b/tools/proxyclient/m1n1/hostutils.py
@@ -0,0 +1,96 @@
+# SPDX-License-Identifier: MIT
+from pathlib import Path
+import os
+
+class KernelRegmapAccessor:
+ def __init__(self, name):
+ self.path = self._find_path(name)
+ self.read_ranges()
+ self.read_linelen()
+
+ @classmethod
+ def _find_path(cls, name):
+ basedir = Path("/sys/kernel/debug/regmap")
+
+ if (path := Path(name)).exists():
+ return path
+ elif (path := basedir.joinpath(name)).exists():
+ return path
+ elif name in (available := cls._list_regmaps(basedir)):
+ return available[name]
+ else:
+ raise ValueError(f"kernel regmap not found: {name}")
+
+ @classmethod
+ def _list_regmaps(cls, basedir):
+ return {
+ p.joinpath("name").open("rb").read().strip().decode(): p
+ for p in basedir.iterdir() if p.is_dir()
+ }
+
+ def open_node(self, name, mode="rb", **kwargs):
+ return self.path.joinpath(name).open(mode, **kwargs)
+
+ def read_ranges(self):
+ with self.open_node("range") as f:
+ self.ranges = [
+ range(int(a, 16), int(b, 16) + 1)
+ for a, b in (l.strip().split(b"-") for l in f)
+ ]
+
+ def read_linelen(self):
+ with self.open_node("registers", buffering=0) as f:
+ l = f.read(64).split(b"\n")[0]
+ valstr = l.split(b":")[1].strip()
+ self.linelen = len(l) + 1
+ self.working_width = len(valstr) * 4
+
+ def _find_off(self, reg):
+ off = 0
+ for r in self.ranges:
+ if reg >= r.stop:
+ off += r.stop - r.start
+ else:
+ off += reg - r.start
+ break
+ if reg not in r:
+ raise ValueError(f"register {reg:04x} out of range")
+ return off * self.linelen
+
+ def _read(self, reg, width=None):
+ assert width == self.working_width
+ with self.open_node("registers", buffering=0) as f:
+ f.seek(self._find_off(reg))
+ l = f.read(self.linelen)
+ regstr, valstr = l.split(b":")
+ assert int(regstr, 16) == reg
+ return int(valstr, 16)
+
+ def read(self, reg, width=None):
+ assert width % self.working_width == 0
+ ret = 0
+ for off in range(0, width // 8, self.working_width // 8):
+ ret |= self._read(reg + off, self.working_width) << (8 * off)
+ return ret
+
+ def _write(self, reg, val, width=None):
+ assert width == self.working_width
+ with self.open_node("registers", mode="wb") as f:
+ f.write(f"{reg:x} {val:x}".encode())
+
+ def write(self, reg, val, width=None):
+ assert width % self.working_width == 0
+ for off in range(0, width // 8, self.working_width // 8):
+ self._write(reg + off, val >> (8 * off), self.working_width)
+
+def require_debugfs():
+ if os.path.ismount("/sys/kernel/debug"):
+ return
+ os.system("mount -t debugfs none /sys/kernel/debug")
+
+if __name__ == "__main__":
+ require_debugfs()
+ from m1n1.hw.codecs import TAS5770Regs
+ tas = TAS5770Regs(KernelRegmapAccessor("tas2770"), 0)
+ import code
+ code.interact(local=locals())
diff --git a/tools/proxyclient/m1n1/hv/__init__.py b/tools/proxyclient/m1n1/hv/__init__.py
new file mode 100644
index 0000000..407b0d1
--- /dev/null
+++ b/tools/proxyclient/m1n1/hv/__init__.py
@@ -0,0 +1,1849 @@
+# SPDX-License-Identifier: MIT
+import io, sys, traceback, struct, array, bisect, os, plistlib, signal, runpy
+from construct import *
+
+from ..asm import ARMAsm
+from ..tgtypes import *
+from ..proxy import IODEV, START, EVENT, EXC, EXC_RET, ExcInfo
+from ..utils import *
+from ..sysreg import *
+from ..macho import MachO
+from ..adt import load_adt
+from .. import xnutools, shell
+
+from .gdbserver import *
+from .types import *
+from .virtutils import *
+from .virtio import *
+
+__all__ = ["HV"]
+
+class HV(Reloadable):
+ PAC_MASK = 0xfffff00000000000
+
+ PTE_VALID = 1 << 0
+
+ PTE_MEMATTR_UNCHANGED = 0b1111 << 2
+ PTE_S2AP_RW = 0b11 << 6
+ PTE_SH_NS = 0b11 << 8
+ PTE_ACCESS = 1 << 10
+ PTE_ATTRIBUTES = PTE_ACCESS | PTE_SH_NS | PTE_S2AP_RW | PTE_MEMATTR_UNCHANGED
+
+ SPTE_TRACE_READ = 1 << 63
+ SPTE_TRACE_WRITE = 1 << 62
+ SPTE_TRACE_UNBUF = 1 << 61
+ SPTE_MAP = 0 << 50
+ SPTE_HOOK = 1 << 50
+ SPTE_PROXY_HOOK_R = 2 << 50
+ SPTE_PROXY_HOOK_W = 3 << 50
+ SPTE_PROXY_HOOK_RW = 4 << 50
+
+ MSR_REDIRECTS = {
+ SCTLR_EL1: SCTLR_EL12,
+ TTBR0_EL1: TTBR0_EL12,
+ TTBR1_EL1: TTBR1_EL12,
+ TCR_EL1: TCR_EL12,
+ ESR_EL1: ESR_EL12,
+ FAR_EL1: FAR_EL12,
+ AFSR0_EL1: AFSR0_EL12,
+ AFSR1_EL1: AFSR1_EL12,
+ MAIR_EL1: MAIR_EL12,
+ AMAIR_EL1: AMAIR_EL12,
+ CONTEXTIDR_EL1: CONTEXTIDR_EL12,
+ ACTLR_EL1: ACTLR_EL12,
+ AMX_CTL_EL1: AMX_CTL_EL12,
+ SPRR_CONFIG_EL1: SPRR_CONFIG_EL12,
+ SPRR_PERM_EL1: SPRR_PERM_EL12,
+ SPRR_PERM_EL0: SPRR_PERM_EL02,
+ SPRR_UNK1_EL1: SPRR_UNK1_EL12,
+ SPRR_UMASK0_EL1: SPRR_UMASK0_EL12,
+ APCTL_EL1: APCTL_EL12,
+ APSTS_EL1: APSTS_EL12,
+ KERNELKEYLO_EL1: KERNELKEYLO_EL12,
+ KERNELKEYHI_EL1: KERNELKEYHI_EL12,
+ GXF_CONFIG_EL1: GXF_CONFIG_EL12,
+ GXF_ABORT_EL1: GXF_ABORT_EL12,
+ GXF_ENTER_EL1: GXF_ENTER_EL12,
+ VBAR_GL1: VBAR_GL12,
+ SPSR_GL1: SPSR_GL12,
+ ASPSR_GL1: ASPSR_GL12,
+ ESR_GL1: ESR_GL12,
+ ELR_GL1: ELR_GL12,
+ }
+
+ AIC_EVT_TYPE_HW = 1
+ IRQTRACE_IRQ = 1
+
+ def __init__(self, iface, proxy, utils):
+ self.iface = iface
+ self.p = proxy
+ self.u = utils
+ self.pac_mask = self.PAC_MASK
+ self.user_pac_mask = self.PAC_MASK
+ self.vbar_el1 = None
+ self.want_vbar = None
+ self.vectors = [None]
+ self._bps = [None, None, None, None, None]
+ self._bp_hooks = dict()
+ self._wps = [None, None, None, None]
+ self._wpcs = [0, 0, 0, 0]
+ self.sym_offset = 0
+ self.symbols = []
+ self.symbol_dict = {}
+ self.sysreg = {0: {}}
+ self.novm = False
+ self._in_handler = False
+ self._sigint_pending = False
+ self._in_shell = False
+ self._gdbserver = None
+ self.vm_hooks = [None]
+ self.interrupt_map = {}
+ self.mmio_maps = DictRangeMap()
+ self.dirty_maps = BoolRangeMap()
+ self.tracer_caches = {}
+ self.shell_locals = {}
+ self.xnu_mode = False
+ self._update_shell_locals()
+ self.wdt_cpu = None
+ self.smp = True
+ self.hook_exceptions = False
+ self.started_cpus = set()
+ self.started = False
+ self.ctx = None
+ self.hvcall_handlers = {}
+ self.switching_context = False
+ self.show_timestamps = False
+ self.virtio_devs = {}
+
+ def _reloadme(self):
+ super()._reloadme()
+ self._update_shell_locals()
+
+ def _update_shell_locals(self):
+ self.shell_locals.update({
+ "hv": self,
+ "iface": self.iface,
+ "p": self.p,
+ "u": self.u,
+ "trace": trace,
+ "TraceMode": TraceMode,
+ })
+
+ for attr in dir(self):
+ a = getattr(self, attr)
+ if callable(a):
+ self.shell_locals[attr] = getattr(self, attr)
+
+ self.shell_locals["ctx"] = self.context
+
+ def log(self, s, *args, show_cpu=True, **kwargs):
+ if self.ctx is not None and show_cpu:
+ ts=""
+ if self.show_timestamps:
+ ts = f"[{self.u.mrs(CNTPCT_EL0):#x}]"
+ print(ts+f"[cpu{self.ctx.cpu_id}] " + s, *args, **kwargs)
+ if self.print_tracer.log_file:
+ print(f"# {ts}[cpu{self.ctx.cpu_id}] " + s, *args, file=self.print_tracer.log_file, **kwargs)
+ else:
+ print(s, *args, **kwargs)
+ if self.print_tracer.log_file:
+ print("# " + s, *args, file=self.print_tracer.log_file, **kwargs)
+
+ def unmap(self, ipa, size):
+ assert self.p.hv_map(ipa, 0, size, 0) >= 0
+
+ def map_hw(self, ipa, pa, size):
+ '''map IPA (Intermediate Physical Address) to actual PA'''
+ #print(f"map_hw {ipa:#x} -> {pa:#x} [{size:#x}]")
+ if (ipa & 0x3fff) != (pa & 0x3fff):
+ self.map_sw(ipa, pa, size)
+ return
+
+ ipa_p = align_up(ipa)
+ if ipa_p != ipa:
+ self.map_sw(ipa, pa, min(ipa_p - ipa, size))
+ pa += ipa_p - ipa
+ size -= ipa_p - ipa
+
+ if size <= 0:
+ return
+
+ size_p = align_down(size)
+ if size_p > 0:
+ #print(f"map_hw real {ipa_p:#x} -> {pa:#x} [{size_p:#x}]")
+ assert self.p.hv_map(ipa_p, pa | self.PTE_ATTRIBUTES | self.PTE_VALID, size_p, 1) >= 0
+
+ if size_p != size:
+ self.map_sw(ipa_p + size_p, pa + size_p, size - size_p)
+
+ def map_sw(self, ipa, pa, size):
+ #print(f"map_sw {ipa:#x} -> {pa:#x} [{size:#x}]")
+ assert self.p.hv_map(ipa, pa | self.SPTE_MAP, size, 1) >= 0
+
+ def map_hook(self, ipa, size, read=None, write=None, **kwargs):
+ index = len(self.vm_hooks)
+ self.vm_hooks.append((read, write, ipa, kwargs))
+ self.map_hook_idx(ipa, size, index, read is not None, write is not None)
+
+ def map_hook_idx(self, ipa, size, index, read=False, write=False, flags=0):
+ if read:
+ if write:
+ t = self.SPTE_PROXY_HOOK_RW
+ else:
+ t = self.SPTE_PROXY_HOOK_R
+ elif write:
+ t = self.SPTE_PROXY_HOOK_W
+ else:
+ assert False
+
+ assert self.p.hv_map(ipa, (index << 2) | flags | t, size, 0) >= 0
+
+ def readmem(self, va, size):
+ '''read from virtual memory'''
+ with io.BytesIO() as buffer:
+ while size > 0:
+ pa = self.p.hv_translate(va, False, False)
+ if pa == 0:
+ break
+
+ size_in_page = 4096 - (va % 4096)
+ if size < size_in_page:
+ buffer.write(self.iface.readmem(pa, size))
+ break
+
+ buffer.write(self.iface.readmem(pa, size_in_page))
+ va += size_in_page
+ size -= size_in_page
+
+ return buffer.getvalue()
+
+ def writemem(self, va, data):
+ '''write to virtual memory'''
+ written = 0
+ while written < len(data):
+ pa = self.p.hv_translate(va, False, True)
+ if pa == 0:
+ break
+
+ size_in_page = 4096 - (va % 4096)
+ if len(data) - written < size_in_page:
+ self.iface.writemem(pa, data[written:])
+ written = len(data)
+ break
+
+ self.iface.writemem(pa, data[written:written + size_in_page])
+ va += size_in_page
+ written += size_in_page
+
+ return written
+
+ def trace_irq(self, device, num, count, flags):
+ for n in range(num, num + count):
+ if flags & self.IRQTRACE_IRQ:
+ self.interrupt_map[n] = device
+ else:
+ self.interrupt_map.pop(n, None)
+
+ start, size = self.adt["/arm-io/aic"].get_reg(0)
+ zone = irange(start, size)
+ if len(self.interrupt_map):
+ self.add_tracer(zone, "AIC_IRQ", TraceMode.RESERVED)
+ else:
+ self.del_tracer(zone, "AIC_IRQ")
+
+ assert self.p.hv_trace_irq(self.AIC_EVT_TYPE_HW, num, count, flags) > 0
+
+ def add_tracer(self, zone, ident, mode=TraceMode.ASYNC, read=None, write=None, **kwargs):
+ assert mode in (TraceMode.RESERVED, TraceMode.OFF, TraceMode.BYPASS) or read or write
+ self.mmio_maps[zone, ident] = (mode, ident, read, write, kwargs)
+ self.dirty_maps.set(zone)
+
+ def del_tracer(self, zone, ident):
+ del self.mmio_maps[zone, ident]
+ self.dirty_maps.set(zone)
+
+ def clear_tracers(self, ident):
+ for r, v in self.mmio_maps.items():
+ if ident in v:
+ v.pop(ident)
+ self.dirty_maps.set(r)
+
+ def trace_device(self, path, mode=TraceMode.ASYNC, ranges=None):
+ node = self.adt[path]
+ for index in range(len(node.reg)):
+ if ranges is not None and index not in ranges:
+ continue
+ addr, size = node.get_reg(index)
+ self.trace_range(irange(addr, size), mode)
+
+ def trace_range(self, zone, mode=TraceMode.ASYNC, read=True, write=True, name=None):
+ if mode is True:
+ mode = TraceMode.ASYNC
+ if mode and mode != TraceMode.OFF:
+ self.add_tracer(zone, "PrintTracer", mode,
+ self.print_tracer.event_mmio if read else None,
+ self.print_tracer.event_mmio if write else None,
+ start=zone.start,
+ name=name)
+ else:
+ self.del_tracer(zone, "PrintTracer")
+
+ def pt_update(self):
+ if not self.dirty_maps:
+ return
+
+ self.dirty_maps.compact()
+ self.mmio_maps.compact()
+
+ top = 0
+
+ for zone in self.dirty_maps:
+ if zone.stop <= top:
+ continue
+ top = max(top, zone.start)
+
+ for mzone, maps in self.mmio_maps.overlaps(zone):
+ if mzone.stop <= top:
+ continue
+ if top < mzone.start:
+ self.unmap(top, mzone.start - top)
+ self.log(f"PT[{top:09x}:{mzone.start:09x}] -> *UNMAPPED*")
+
+ top = mzone.stop
+ if not maps:
+ continue
+ maps = sorted(maps.values(), reverse=True)
+ mode, ident, read, write, kwargs = maps[0]
+
+ need_read = any(m[2] for m in maps)
+ need_write = any(m[3] for m in maps)
+
+ if mode == TraceMode.RESERVED:
+ self.log(f"PT[{mzone.start:09x}:{mzone.stop:09x}] -> RESERVED {ident}")
+ continue
+ elif mode in (TraceMode.HOOK, TraceMode.SYNC):
+ self.map_hook_idx(mzone.start, mzone.stop - mzone.start, 0,
+ need_read, need_write)
+ if mode == TraceMode.HOOK:
+ for m2, i2, r2, w2, k2 in maps[1:]:
+ if m2 == TraceMode.HOOK:
+ self.log(f"!! Conflict: HOOK {i2}")
+ elif mode == TraceMode.WSYNC:
+ flags = self.SPTE_TRACE_READ if need_read else 0
+ self.map_hook_idx(mzone.start, mzone.stop - mzone.start, 0,
+ False, need_write, flags=flags)
+ elif mode in (TraceMode.UNBUF, TraceMode.ASYNC, TraceMode.BYPASS):
+ pa = mzone.start
+ if mode == TraceMode.UNBUF:
+ pa |= self.SPTE_TRACE_UNBUF
+ if need_read:
+ pa |= self.SPTE_TRACE_READ
+ if need_write:
+ pa |= self.SPTE_TRACE_WRITE
+ self.map_sw(mzone.start, pa, mzone.stop - mzone.start)
+ elif mode == TraceMode.OFF:
+ self.map_hw(mzone.start, mzone.start, mzone.stop - mzone.start)
+ self.log(f"PT[{mzone.start:09x}:{mzone.stop:09x}] -> HW:{ident}")
+ continue
+
+ rest = [m[1] for m in maps[1:] if m[0] != TraceMode.OFF]
+ if rest:
+ rest = " (+ " + ", ".join(rest) + ")"
+ else:
+ rest = ""
+
+ self.log(f"PT[{mzone.start:09x}:{mzone.stop:09x}] -> {mode.name}.{'R' if read else ''}{'W' if read else ''} {ident}{rest}")
+
+ if top < zone.stop:
+ self.unmap(top, zone.stop - top)
+ self.log(f"PT[{top:09x}:{zone.stop:09x}] -> *UNMAPPED*")
+
+ self.u.inst(0xd50c83df) # tlbi vmalls12e1is
+ self.dirty_maps.clear()
+
+ def shellwrap(self, func, description, update=None, needs_ret=False):
+
+ while True:
+ try:
+ return func()
+ except Exception:
+ print(f"Exception in {description}")
+ traceback.print_exc()
+
+ if not self.ctx:
+ print("Running in asynchronous context. Target operations are not available.")
+
+ def do_exit(i):
+ raise shell.ExitConsole(i)
+
+ self.shell_locals["skip"] = lambda: do_exit(1)
+ self.shell_locals["cont"] = lambda: do_exit(0)
+ ret = self.run_shell("Entering debug shell", "Returning to tracer")
+ self.shell_locals["skip"] = self.skip
+ self.shell_locals["cont"] = self.cont
+
+ if self.ctx:
+ self.cpu() # Return to the original CPU to avoid confusing things
+
+ if ret == 1:
+ if needs_ret:
+ print("Cannot skip, return value required.")
+ else:
+ return
+
+ if update:
+ update()
+
+ def run_shell(self, entry_msg="Entering shell", exit_msg="Continuing"):
+ def handle_sigusr1(signal, stack):
+ raise shell.ExitConsole(EXC_RET.HANDLED)
+
+ def handle_sigusr2(signal, stack):
+ raise shell.ExitConsole(EXC_RET.EXIT_GUEST)
+
+ default_sigusr1 = signal.signal(signal.SIGUSR1, handle_sigusr1)
+ try:
+ default_sigusr2 = signal.signal(signal.SIGUSR2, handle_sigusr2)
+ try:
+ self._in_shell = True
+ try:
+ if not self._gdbserver is None:
+ self._gdbserver.notify_in_shell()
+ return shell.run_shell(self.shell_locals, entry_msg, exit_msg)
+ finally:
+ self._in_shell = False
+ finally:
+ signal.signal(signal.SIGUSR2, default_sigusr2)
+ finally:
+ signal.signal(signal.SIGUSR1, default_sigusr1)
+
+ @property
+ def in_shell(self):
+ return self._in_shell
+
+ def gdbserver(self, address="/tmp/.m1n1-unix", log=None):
+ '''activate gdbserver'''
+ if not self._gdbserver is None:
+ raise Exception("gdbserver is already running")
+
+ self._gdbserver = GDBServer(self, address, log)
+ self._gdbserver.activate()
+
+ def shutdown_gdbserver(self):
+ '''shutdown gdbserver'''
+ self._gdbserver.shutdown()
+ self._gdbserver = None
+
+ def handle_mmiotrace(self, data):
+ evt = EvtMMIOTrace.parse(data)
+
+ def do_update():
+ nonlocal mode, ident, read, write, kwargs
+ read = lambda *args, **kwargs: None
+ write = lambda *args, **kwargs: None
+
+ m = self.mmio_maps[evt.addr].get(ident, None)
+ if not m:
+ return
+
+ mode, ident, read_, write_, kwargs = m
+ read = read_ or read
+ write = write_ or write
+
+ maps = sorted(self.mmio_maps[evt.addr].values(), reverse=True)
+ for mode, ident, read, write, kwargs in maps:
+ if mode > TraceMode.WSYNC or (evt.flags.WRITE and mode > TraceMode.UNBUF):
+ print(f"ERROR: mmiotrace event but expected {mode.name} mapping")
+ continue
+ if mode == TraceMode.OFF:
+ continue
+ if evt.flags.WRITE:
+ if write:
+ self.shellwrap(lambda: write(evt, **kwargs),
+ f"Tracer {ident}:write ({mode.name})", update=do_update)
+ else:
+ if read:
+ self.shellwrap(lambda: read(evt, **kwargs),
+ f"Tracer {ident}:read ({mode.name})", update=do_update)
+
+ def handle_vm_hook_mapped(self, ctx, data):
+ maps = sorted(self.mmio_maps[data.addr].values(), reverse=True)
+
+ if not maps:
+ raise Exception(f"VM hook without a mapping at {data.addr:#x}")
+
+ def do_update():
+ nonlocal mode, ident, read, write, kwargs
+ read = lambda *args, **kwargs: None
+ write = lambda *args, **kwargs: None
+
+ m = self.mmio_maps[data.addr].get(ident, None)
+ if not m:
+ return
+
+ mode, ident, read_, write_, kwargs = m
+ read = read_ or read
+ write = write_ or write
+
+ mode, ident, read, write, kwargs = maps[0]
+
+ first = 0
+
+ val = data.data
+
+ if mode not in (TraceMode.HOOK, TraceMode.SYNC, TraceMode.WSYNC):
+ raise Exception(f"VM hook with unexpected mapping at {data.addr:#x}: {maps[0][0].name}")
+
+ if not data.flags.WRITE:
+ if mode == TraceMode.HOOK:
+ val = self.shellwrap(lambda: read(data.addr, 8 << data.flags.WIDTH, **kwargs),
+ f"Tracer {ident}:read (HOOK)", update=do_update, needs_ret=True)
+
+ if not isinstance(val, list) and not isinstance(val, tuple):
+ val = [val]
+ first += 1
+ elif mode == TraceMode.SYNC:
+ try:
+ val = self.u.read(data.addr, 8 << data.flags.WIDTH)
+ except:
+ self.log(f"MMIO read failed: {data.addr:#x} (w={data.flags.WIDTH})")
+ raise
+ if not isinstance(val, list) and not isinstance(val, tuple):
+ val = [val]
+ elif mode == TraceMode.WSYNC:
+ raise Exception(f"VM hook with unexpected mapping at {data.addr:#x}: {maps[0][0].name}")
+
+ for i in range(1 << max(0, data.flags.WIDTH - 3)):
+ self.p.write64(ctx.data + 16 + 8 * i, val[i])
+
+ elif mode == TraceMode.HOOK:
+ first += 1
+
+ flags = data.flags.copy()
+ flags.CPU = self.ctx.cpu_id
+ width = data.flags.WIDTH
+
+ if width > 3:
+ flags.WIDTH = 3
+ flags.MULTI = 1
+
+ for i in range(1 << max(0, width - 3)):
+ evt = Container(
+ flags = flags,
+ reserved = 0,
+ pc = ctx.elr,
+ addr = data.addr + 8 * i,
+ data = val[i]
+ )
+
+ for mode, ident, read, write, kwargs in maps[first:]:
+ if flags.WRITE:
+ if write:
+ self.shellwrap(lambda: write(evt, **kwargs),
+ f"Tracer {ident}:write ({mode.name})", update=do_update)
+ else:
+ if read:
+ self.shellwrap(lambda: read(evt, **kwargs),
+ f"Tracer {ident}:read ({mode.name})", update=do_update)
+
+ if data.flags.WRITE:
+ mode, ident, read, write, kwargs = maps[0]
+
+ if data.flags.WIDTH <= 3:
+ wval = val[0]
+ else:
+ wval = val
+
+ if mode == TraceMode.HOOK:
+ self.shellwrap(lambda: write(data.addr, wval, 8 << data.flags.WIDTH, **kwargs),
+ f"Tracer {ident}:write (HOOK)", update=do_update)
+ elif mode in (TraceMode.SYNC, TraceMode.WSYNC):
+ try:
+ self.u.write(data.addr, wval, 8 << data.flags.WIDTH)
+ except:
+ if data.flags.WIDTH > 3:
+ wval = wval[0]
+ self.log(f"MMIO write failed: {data.addr:#x} = {wval} (w={data.flags.WIDTH})")
+ raise
+
+ return True
+
+ def handle_vm_hook(self, ctx):
+ data = self.iface.readstruct(ctx.data, VMProxyHookData)
+
+ if data.id == 0:
+ return self.handle_vm_hook_mapped(ctx, data)
+
+ rfunc, wfunc, base, kwargs = self.vm_hooks[data.id]
+
+ d = data.data
+ if data.flags.WIDTH < 3:
+ d = d[0]
+
+ if data.flags.WRITE:
+ wfunc(base, data.addr - base, d, 8 << data.flags.WIDTH, **kwargs)
+ else:
+ val = rfunc(base, data.addr - base, 8 << data.flags.WIDTH, **kwargs)
+ if not isinstance(val, list) and not isinstance(val, tuple):
+ val = [val]
+ for i in range(1 << max(0, data.flags.WIDTH - 3)):
+ self.p.write64(ctx.data + 16 + 8 * i, val[i])
+
+ return True
+
+ def handle_irqtrace(self, data):
+ evt = EvtIRQTrace.parse(data)
+
+ if evt.type == self.AIC_EVT_TYPE_HW and evt.flags & self.IRQTRACE_IRQ:
+ dev = self.interrupt_map[int(evt.num)]
+ print(f"IRQ: {dev}: {evt.num}")
+
+ def addr(self, addr):
+ unslid_addr = addr + self.sym_offset
+ if self.xnu_mode and (addr < self.tba.virt_base or unslid_addr < self.macho.vmin):
+ return f"0x{addr:x}"
+
+ saddr, name = self.sym(addr)
+
+ if name is None:
+ return f"0x{addr:x} (0x{unslid_addr:x})"
+
+ return f"0x{addr:x} ({name}+0x{unslid_addr - saddr:x})"
+
+ def resolve_symbol(self, name):
+ return self.symbol_dict[name] - self.sym_offset
+
+ def sym(self, addr):
+ unslid_addr = addr + self.sym_offset
+
+ if self.xnu_mode and (addr < self.tba.virt_base or unslid_addr < self.macho.vmin):
+ return None, None
+
+ idx = bisect.bisect_left(self.symbols, (unslid_addr + 1, "")) - 1
+ if idx < 0 or idx >= len(self.symbols):
+ return None, None
+
+ return self.symbols[idx]
+
+ def get_sym(self, addr):
+ a, name = self.sym(addr)
+ if addr == a:
+ return name
+ else:
+ return None
+
+ def handle_msr(self, ctx, iss=None):
+ if iss is None:
+ iss = ctx.esr.ISS
+ iss = ESR_ISS_MSR(iss)
+ enc = iss.Op0, iss.Op1, iss.CRn, iss.CRm, iss.Op2
+
+ name = sysreg_name(enc)
+
+ skip = set()
+ shadow = {
+ #SPRR_CONFIG_EL1,
+ #SPRR_PERM_EL0,
+ #SPRR_PERM_EL1,
+ VMSA_LOCK_EL1,
+ #SPRR_UNK1_EL1,
+ #SPRR_UNK2_EL1,
+ MDSCR_EL1,
+ }
+ ro = {
+ ACC_CFG_EL1,
+ ACC_OVRD_EL1,
+ }
+ xlate = {
+ DC_CIVAC,
+ }
+ for i in range(len(self._bps)):
+ shadow.add(DBGBCRn_EL1(i))
+ shadow.add(DBGBVRn_EL1(i))
+ for i in range(len(self._wps)):
+ shadow.add(DBGWCRn_EL1(i))
+ shadow.add(DBGWVRn_EL1(i))
+
+ value = 0
+ if enc in shadow:
+ if iss.DIR == MSR_DIR.READ:
+ value = self.sysreg[self.ctx.cpu_id].setdefault(enc, 0)
+ self.log(f"Shadow: mrs x{iss.Rt}, {name} = {value:x}")
+ if iss.Rt != 31:
+ ctx.regs[iss.Rt] = value
+ else:
+ if iss.Rt != 31:
+ value = ctx.regs[iss.Rt]
+ self.log(f"Shadow: msr {name}, x{iss.Rt} = {value:x}")
+ self.sysreg[self.ctx.cpu_id][enc] = value
+ elif enc in skip or (enc in ro and iss.DIR == MSR_DIR.WRITE):
+ if iss.DIR == MSR_DIR.READ:
+ self.log(f"Skip: mrs x{iss.Rt}, {name} = 0")
+ if iss.Rt != 31:
+ ctx.regs[iss.Rt] = 0
+ else:
+ if iss.Rt != 31:
+ value = ctx.regs[iss.Rt]
+ self.log(f"Skip: msr {name}, x{iss.Rt} = {value:x}")
+ else:
+ if iss.DIR == MSR_DIR.READ:
+ enc2 = self.MSR_REDIRECTS.get(enc, enc)
+ value = self.u.mrs(enc2)
+ self.log(f"Pass: mrs x{iss.Rt}, {name} = {value:x} ({sysreg_name(enc2)})")
+ if iss.Rt != 31:
+ ctx.regs[iss.Rt] = value
+ else:
+ if iss.Rt != 31:
+ value = ctx.regs[iss.Rt]
+ enc2 = self.MSR_REDIRECTS.get(enc, enc)
+ sys.stdout.flush()
+ if enc in xlate:
+ value = self.p.hv_translate(value, True, False)
+ self.u.msr(enc2, value, call=self.p.gl2_call)
+ self.log(f"Pass: msr {name}, x{iss.Rt} = {value:x} (OK) ({sysreg_name(enc2)})")
+
+ ctx.elr += 4
+
+ if self.hook_exceptions:
+ self.patch_exception_handling()
+
+ return True
+
+ def handle_impdef(self, ctx):
+ if ctx.esr.ISS == 0x20:
+ return self.handle_msr(ctx, ctx.afsr1)
+
+ code = struct.unpack("<I", self.iface.readmem(ctx.elr_phys, 4))
+ c = ARMAsm(".inst " + ",".join(str(i) for i in code), ctx.elr_phys)
+ insn = "; ".join(c.disassemble())
+
+ self.log(f"IMPDEF exception on: {insn}")
+
+ return False
+
+ def handle_hvc(self, ctx):
+ idx = ctx.esr.ISS
+ if idx == 0:
+ return False
+
+ vector, target = self.vectors[idx]
+ if target is None:
+ self.log(f"EL1: Exception #{vector} with no target")
+ target = 0
+ ok = False
+ else:
+ ctx.elr = target
+ ctx.elr_phys = self.p.hv_translate(target, False, False)
+ ok = True
+
+ if (vector & 3) == EXC.SYNC:
+ spsr = SPSR(self.u.mrs(SPSR_EL12))
+ esr = ESR(self.u.mrs(ESR_EL12))
+ elr = self.u.mrs(ELR_EL12)
+ elr_phys = self.p.hv_translate(elr, False, False)
+ sp_el1 = self.u.mrs(SP_EL1)
+ sp_el0 = self.u.mrs(SP_EL0)
+ far = None
+ if esr.EC == ESR_EC.DABORT or esr.EC == ESR_EC.IABORT:
+ far = self.u.mrs(FAR_EL12)
+ if self.sym(elr)[1] != "com.apple.kernel:_panic_trap_to_debugger":
+ self.log("Page fault")
+ return ok
+
+ self.log(f"EL1: Exception #{vector} ({esr.EC!s}) to {self.addr(target)} from {spsr.M.name}")
+ self.log(f" ELR={self.addr(elr)} (0x{elr_phys:x})")
+ self.log(f" SP_EL1=0x{sp_el1:x} SP_EL0=0x{sp_el0:x}")
+ if far is not None:
+ self.log(f" FAR={self.addr(far)}")
+ if elr_phys:
+ self.u.disassemble_at(elr_phys - 4 * 4, 9 * 4, elr - 4 * 4, elr, sym=self.get_sym)
+ if self.sym(elr)[1] == "com.apple.kernel:_panic_trap_to_debugger":
+ self.log("Panic! Trying to decode panic...")
+ try:
+ self.decode_panic_call()
+ except:
+ self.log("Error decoding panic.")
+ try:
+ self.bt()
+ except:
+ pass
+ return False
+ if esr.EC == ESR_EC.UNKNOWN:
+ instr = self.p.read32(elr_phys)
+ if instr == 0xe7ffdeff:
+ self.log("Debugger break! Trying to decode panic...")
+ try:
+ self.decode_dbg_panic()
+ except:
+ self.log("Error decoding panic.")
+ try:
+ self.bt()
+ except:
+ pass
+ return False
+ return False
+ else:
+ elr = self.u.mrs(ELR_EL12)
+ self.log(f"Guest: {str(EXC(vector & 3))} at {self.addr(elr)}")
+
+ return ok
+
+ def handle_step(self, ctx):
+ # not sure why MDSCR_EL1.SS needs to be disabled here but otherwise
+ # if also SPSR.SS=0 no instruction will be executed after eret
+ # and instead a debug exception is generated again
+ self.u.msr(MDSCR_EL1, MDSCR(MDE=1).value)
+
+ # enable all breakpoints again
+ for i, vaddr in enumerate(self._bps):
+ if vaddr is None:
+ continue
+ self.u.msr(DBGBCRn_EL1(i), DBGBCR(E=1, PMC=0b11, BAS=0xf).value)
+
+ # enable all watchpoints again
+ for i, wpc in enumerate(self._wpcs):
+ self.u.msr(DBGWCRn_EL1(i), wpc)
+
+ return True
+
+ def handle_break(self, ctx):
+ # disable all breakpoints so that we don't get stuck
+ for i in range(5):
+ self.u.msr(DBGBCRn_EL1(i), 0)
+
+ # we'll need to single step to enable these breakpoints again
+ self.u.msr(MDSCR_EL1, MDSCR(SS=1, MDE=1).value)
+ self.ctx.spsr.SS = 1
+
+ if ctx.elr in self._bp_hooks:
+ if self._bp_hooks[ctx.elr](ctx):
+ return True
+
+ def handle_watch(self, ctx):
+ # disable all watchpoints so that we don't get stuck
+ for i in range(len(self._wps)):
+ self.u.msr(DBGWCRn_EL1(i), 0)
+
+ # we'll need to single step to enable these watchpoints again
+ self.u.msr(MDSCR_EL1, MDSCR(SS=1, MDE=1).value)
+ self.ctx.spsr.SS = 1
+
+ def add_hvcall(self, callid, handler):
+ self.hvcall_handlers[callid] = handler
+
+ def handle_brk(self, ctx):
+ iss = ctx.esr.ISS
+ if iss != 0x4242:
+ return self._lower()
+
+ # HV call from EL0/1
+ callid = ctx.regs[0]
+ handler = self.hvcall_handlers.get(callid, None)
+ if handler is None:
+ self.log(f"Undefined HV call #{callid}")
+ return False
+
+ ok = handler(ctx)
+ if ok:
+ ctx.elr += 4
+ return ok
+
+ def handle_dabort(self, ctx):
+ insn = self.p.read32(ctx.elr_phys)
+ far_phys = self.p.hv_translate(ctx.far, True, False)
+
+ if insn & 0x3b200c00 == 0x38200000:
+ page = far_phys & ~0x3fff
+
+ before = self.p.read32(far_phys)
+ self.map_hw(page, page, 0x4000)
+ r0b = self.ctx.regs[0]
+ self.log(f"-ELR={self.ctx.elr:#x} LR={self.ctx.regs[30]:#x}")
+ self.step()
+ self.log(f"+ELR={self.ctx.elr:#x}")
+ r0a = self.ctx.regs[0]
+ self.dirty_maps.set(irange(page, 0x4000))
+ self.pt_update()
+ after = self.p.read32(far_phys)
+ self.log(f"Unhandled atomic: @{far_phys:#x} {before:#x} -> {after:#x} | r0={r0b:#x} -> {r0a:#x}")
+ return True
+
+ if insn & 0x3f000000 == 0x08000000:
+ page = far_phys & ~0x3fff
+ before = self.p.read32(far_phys)
+ self.map_hw(page, page, 0x4000)
+ r0b = self.ctx.regs[0]
+ self.log(f"-ELR={self.ctx.elr:#x} LR={self.ctx.regs[30]:#x}")
+ self.step()
+ self.log(f"+ELR={self.ctx.elr:#x}")
+ r0a = self.ctx.regs[0]
+ self.dirty_maps.set(irange(page, 0x4000))
+ self.pt_update()
+ after = self.p.read32(far_phys)
+ self.log(f"Unhandled exclusive: @{far_phys:#x} {before:#x} -> {after:#x} | r0={r0b:#x} -> {r0a:#x}")
+ return True
+
+ def handle_sync(self, ctx):
+ if ctx.esr.EC == ESR_EC.MSR:
+ return self.handle_msr(ctx)
+
+ if ctx.esr.EC == ESR_EC.IMPDEF:
+ return self.handle_impdef(ctx)
+
+ if ctx.esr.EC == ESR_EC.HVC:
+ return self.handle_hvc(ctx)
+
+ if ctx.esr.EC == ESR_EC.SSTEP_LOWER:
+ return self.handle_step(ctx)
+
+ if ctx.esr.EC == ESR_EC.BKPT_LOWER:
+ return self.handle_break(ctx)
+
+ if ctx.esr.EC == ESR_EC.WATCH_LOWER:
+ return self.handle_watch(ctx)
+
+ if ctx.esr.EC == ESR_EC.BRK:
+ return self.handle_brk(ctx)
+
+ if ctx.esr.EC == ESR_EC.DABORT_LOWER:
+ return self.handle_dabort(ctx)
+
+ def _load_context(self):
+ self._info_data = self.iface.readmem(self.exc_info, ExcInfo.sizeof())
+ self.ctx = ExcInfo.parse(self._info_data)
+ return self.ctx
+
+ def _commit_context(self):
+ new_info = ExcInfo.build(self.ctx)
+ if new_info != self._info_data:
+ self.iface.writemem(self.exc_info, new_info)
+ self._info_data = new_info
+
+ def handle_exception(self, reason, code, info):
+ self.exc_info = info
+ self.exc_reason = reason
+ if reason in (START.EXCEPTION_LOWER, START.EXCEPTION):
+ code = EXC(code)
+ elif reason == START.HV:
+ code = HV_EVENT(code)
+ self.exc_code = code
+ self.is_fault = reason == START.EXCEPTION_LOWER and code in (EXC.SYNC, EXC.SERROR)
+
+ # Nested context switch is handled by the caller
+ if self.switching_context:
+ self.switching_context = False
+ return
+
+ self._in_handler = True
+
+ ctx = self._load_context()
+ self.exc_orig_cpu = self.ctx.cpu_id
+
+ handled = False
+ user_interrupt = False
+
+ try:
+ if reason == START.EXCEPTION_LOWER:
+ if code == EXC.SYNC:
+ handled = self.handle_sync(ctx)
+ elif code == EXC.FIQ:
+ self.u.msr(CNTV_CTL_EL0, 0)
+ self.u.print_context(ctx, False, sym=self.get_sym)
+ handled = True
+ elif reason == START.HV:
+ code = HV_EVENT(code)
+ if code == HV_EVENT.HOOK_VM:
+ handled = self.handle_vm_hook(ctx)
+ elif code == HV_EVENT.USER_INTERRUPT:
+ handled = True
+ user_interrupt = True
+ except Exception as e:
+ self.log(f"Python exception while handling guest exception:")
+ traceback.print_exc()
+
+ if handled:
+ ret = EXC_RET.HANDLED
+ if self._sigint_pending:
+ self.update_pac_mask()
+ self.log("User interrupt")
+ else:
+ self.log(f"Guest exception: {reason.name}/{code.name}")
+ self.update_pac_mask()
+ self.u.print_context(ctx, self.is_fault, sym=self.get_sym)
+
+ if self._sigint_pending or not handled or user_interrupt:
+ self._sigint_pending = False
+
+ signal.signal(signal.SIGINT, self.default_sigint)
+ ret = self.run_shell("Entering hypervisor shell", "Returning from exception")
+ signal.signal(signal.SIGINT, self._handle_sigint)
+
+ if ret is None:
+ ret = EXC_RET.HANDLED
+
+ self.pt_update()
+
+ self._commit_context()
+ self.ctx = None
+ self.exc_orig_cpu = None
+ self.p.exit(ret)
+
+ self._in_handler = False
+ if self._sigint_pending:
+ self._handle_sigint()
+
+ def handle_bark(self, reason, code, info):
+ self._in_handler = True
+ self._sigint_pending = False
+
+ signal.signal(signal.SIGINT, self.default_sigint)
+ ret = self.run_shell("Entering panic shell", "Exiting")
+ signal.signal(signal.SIGINT, self._handle_sigint)
+
+ self.p.exit(0)
+
+ def attach_virtio(self, dev, base=None, irq=None, verbose=False):
+ if base is None:
+ base = alloc_mmio_base(self.adt, 0x1000)
+ if irq is None:
+ irq = alloc_aic_irq(self.adt)
+
+ data = dev.config_data
+ data_base = self.u.heap.malloc(len(data))
+ self.iface.writemem(data_base, data)
+
+ config = VirtioConfig.build({
+ "irq": irq,
+ "devid": dev.devid,
+ "feats": dev.feats,
+ "num_qus": dev.num_qus,
+ "data": data_base,
+ "data_len": len(data),
+ "verbose": verbose,
+ })
+
+ config_base = self.u.heap.malloc(len(config))
+ self.iface.writemem(config_base, config)
+
+ name = None
+ for i in range(16):
+ n = "/arm-io/virtio%d" % i
+ if n not in self.adt:
+ name = n
+ break
+ if name is None:
+ raise ValueError("Too many virtios in ADT")
+
+ print(f"Adding {n} @ 0x{base:x}, irq {irq}")
+
+ node = self.adt.create_node(name)
+ node.reg = [Container(addr=node.to_bus_addr(base), size=0x1000)]
+ node.interrupt_parent = getattr(self.adt["/arm-io/aic"], "AAPL,phandle")
+ node.interrupts = (irq,)
+ node.compatible = ["virtio,mmio"]
+
+ self.p.hv_map_virtio(base, config_base)
+ self.add_tracer(irange(base, 0x1000), "VIRTIO", TraceMode.RESERVED)
+
+ dev.base = base
+ dev.hv = self
+ self.virtio_devs[base] = dev
+
+ def handle_virtio(self, reason, code, info):
+ ctx = self.iface.readstruct(info, ExcInfo)
+ self.virtio_ctx = info = self.iface.readstruct(ctx.data, VirtioExcInfo)
+
+ try:
+ handled = self.virtio_devs[info.devbase].handle_exc(info)
+ except:
+ self.log(f"Python exception from within virtio handler")
+ traceback.print_exc()
+ handled = False
+
+ if not handled:
+ signal.signal(signal.SIGINT, self.default_sigint)
+ self.run_shell("Entering hypervisor shell", "Returning")
+ signal.signal(signal.SIGINT, self._handle_sigint)
+
+ self.p.exit(EXC_RET.HANDLED)
+
+ def skip(self):
+ self.ctx.elr += 4
+ self.cont()
+
+ def cont(self):
+ os.kill(os.getpid(), signal.SIGUSR1)
+
+ def _lower(self):
+ if not self.is_fault:
+ print("Cannot lower non-fault exception")
+ return False
+
+ self.u.msr(ELR_EL12, self.ctx.elr)
+ self.u.msr(SPSR_EL12, self.ctx.spsr.value)
+ self.u.msr(ESR_EL12, self.ctx.esr.value)
+ self.u.msr(FAR_EL12, self.ctx.far)
+
+ exc_off = 0x80 * self.exc_code
+
+ if self.ctx.spsr.M == SPSR_M.EL0t:
+ exc_off += 0x400
+ elif self.ctx.spsr.M == SPSR_M.EL1t:
+ pass
+ elif self.ctx.spsr.M == SPSR_M.EL1h:
+ exc_off += 0x200
+ else:
+ print(f"Unknown exception level {self.ctx.spsr.M}")
+ return False
+
+ self.ctx.spsr.M = SPSR_M.EL1h
+ self.ctx.spsr.D = 1
+ self.ctx.spsr.A = 1
+ self.ctx.spsr.I = 1
+ self.ctx.spsr.F = 1
+ self.ctx.elr = self.u.mrs(VBAR_EL12) + exc_off
+
+ return True
+
+ def lower(self, step=False):
+ self.cpu() # Return to exception CPU
+
+ if not self._lower():
+ return
+ elif step:
+ self.step()
+ else:
+ self.cont()
+
+ def step(self):
+ self.u.msr(MDSCR_EL1, MDSCR(SS=1, MDE=1).value)
+ self.ctx.spsr.SS = 1
+ self.p.hv_pin_cpu(self.ctx.cpu_id)
+ self._switch_context()
+ self.p.hv_pin_cpu(0xffffffffffffffff)
+
+ def _switch_context(self, exit=EXC_RET.HANDLED):
+ # Flush current CPU context out to HV
+ self._commit_context()
+ self.exc_info = None
+ self.ctx = None
+
+ self.switching_context = True
+ # Exit out of the proxy
+ self.p.exit(exit)
+ # Wait for next proxy entry
+ self.iface.wait_and_handle_boot()
+ if self.switching_context:
+ raise Exception(f"Failed to switch context")
+
+ # Fetch new context
+ self._load_context()
+
+ def cpu(self, cpu=None):
+ if cpu is None:
+ cpu = self.exc_orig_cpu
+ if cpu == self.ctx.cpu_id:
+ return
+
+ if not self.p.hv_switch_cpu(cpu):
+ raise ValueError(f"Invalid or inactive CPU #{cpu}")
+
+ self._switch_context()
+ if self.ctx.cpu_id != cpu:
+ raise Exception(f"Switching to CPU #{cpu} but ended on #{self.ctx.cpu_id}")
+
+ def add_hw_bp(self, vaddr, hook=None):
+ if None not in self._bps:
+ raise ValueError("Cannot add more HW breakpoints")
+
+ i = self._bps.index(None)
+ cpu_id = self.ctx.cpu_id
+ try:
+ for cpu in self.cpus():
+ self.u.msr(DBGBCRn_EL1(i), DBGBCR(E=1, PMC=0b11, BAS=0xf).value)
+ self.u.msr(DBGBVRn_EL1(i), vaddr)
+ finally:
+ self.cpu(cpu_id)
+ self._bps[i] = vaddr
+ if hook is not None:
+ self._bp_hooks[vaddr] = hook
+
+ def remove_hw_bp(self, vaddr):
+ idx = self._bps.index(vaddr)
+ self._bps[idx] = None
+ cpu_id = self.ctx.cpu_id
+ try:
+ for cpu in self.cpus():
+ self.u.msr(DBGBCRn_EL1(idx), 0)
+ self.u.msr(DBGBVRn_EL1(idx), 0)
+ finally:
+ self.cpu(cpu_id)
+ if vaddr in self._bp_hooks:
+ del self._bp_hooks[vaddr]
+
+ def add_sym_bp(self, name, hook=None):
+ return self.add_hw_bp(self.resolve_symbol(name), hook=hook)
+
+ def remove_sym_bp(self, name):
+ return self.remove_hw_bp(self.resolve_symbol(name))
+
+ def clear_hw_bps(self):
+ for vaddr in self._bps:
+ self.remove_hw_bp(vaddr)
+
+ def add_hw_wp(self, vaddr, bas, lsc):
+ for i, i_vaddr in enumerate(self._wps):
+ if i_vaddr is None:
+ self._wps[i] = vaddr
+ self._wpcs[i] = DBGWCR(E=1, PAC=0b11, BAS=bas, LSC=lsc).value
+ cpu_id = self.ctx.cpu_id
+ try:
+ for cpu in self.cpus():
+ self.u.msr(DBGWCRn_EL1(i), self._wpcs[i])
+ self.u.msr(DBGWVRn_EL1(i), vaddr)
+ finally:
+ self.cpu(cpu_id)
+ return
+ raise ValueError("Cannot add more HW watchpoints")
+
+ def get_wp_bas(self, vaddr):
+ for i, i_vaddr in enumerate(self._wps):
+ if i_vaddr == vaddr:
+ return self._wpcs[i].BAS
+
+ def remove_hw_wp(self, vaddr):
+ idx = self._wps.index(vaddr)
+ self._wps[idx] = None
+ self._wpcs[idx] = 0
+ cpu_id = self.ctx.cpu_id
+ try:
+ for cpu in self.cpus():
+ self.u.msr(DBGWCRn_EL1(idx), 0)
+ self.u.msr(DBGWVRn_EL1(idx), 0)
+ finally:
+ self.cpu(cpu_id)
+
+ def exit(self):
+ os.kill(os.getpid(), signal.SIGUSR2)
+
+ def reboot(self):
+ print("Hard rebooting the system")
+ self.p.reboot()
+ sys.exit(0)
+
+ def hvc(self, arg):
+ assert 0 <= arg <= 0xffff
+ return 0xd4000002 | (arg << 5)
+
+ def decode_dbg_panic(self):
+ xnutools.decode_debugger_state(self.u, self.ctx)
+
+ def decode_panic_call(self):
+ xnutools.decode_panic_call(self.u, self.ctx)
+
+ def context(self):
+ f = f" (orig: #{self.exc_orig_cpu})" if self.ctx.cpu_id != self.exc_orig_cpu else ""
+ print(f" == On CPU #{self.ctx.cpu_id}{f} ==")
+ print(f" Reason: {self.exc_reason.name}/{self.exc_code.name}")
+ self.u.print_context(self.ctx, self.is_fault, sym=self.get_sym)
+
+ def bt(self, frame=None, lr=None):
+ if frame is None:
+ frame = self.ctx.regs[29]
+ if lr is None:
+ lr = self.unpac(self.ctx.elr) + 4
+
+ print("Stack trace:")
+ frames = set()
+ while frame:
+ if frame in frames:
+ print("Stack loop detected!")
+ break
+ frames.add(frame)
+ print(f" - {self.addr(lr - 4)}")
+ lrp = self.p.hv_translate(frame + 8)
+ fpp = self.p.hv_translate(frame)
+ if not fpp:
+ break
+ lr = self.unpac(self.p.read64(lrp))
+ frame = self.p.read64(fpp)
+
+ def cpus(self):
+ for i in sorted(self.started_cpus):
+ self.cpu(i)
+ yield i
+
+ def patch_exception_handling(self):
+ if self.ctx.cpu_id != 0:
+ return
+
+ if self.want_vbar is not None:
+ vbar = self.want_vbar
+ else:
+ vbar = self.u.mrs(VBAR_EL12)
+
+ if vbar == self.vbar_el1:
+ return
+
+ if vbar == 0:
+ return
+
+ if self.u.mrs(SCTLR_EL12) & 1:
+ vbar_phys = self.p.hv_translate(vbar, False, False)
+ if vbar_phys == 0:
+ self.log(f"VBAR vaddr 0x{vbar:x} translation failed!")
+ if self.vbar_el1 is not None:
+ self.want_vbar = vbar
+ self.u.msr(VBAR_EL12, self.vbar_el1)
+ return
+ else:
+ if vbar & (1 << 63):
+ self.log(f"VBAR vaddr 0x{vbar:x} without translation enabled")
+ if self.vbar_el1 is not None:
+ self.want_vbar = vbar
+ self.u.msr(VBAR_EL12, self.vbar_el1)
+ return
+
+ vbar_phys = vbar
+
+ if self.want_vbar is not None:
+ self.want_vbar = None
+ self.u.msr(VBAR_EL12, vbar)
+
+ self.log(f"New VBAR paddr: 0x{vbar_phys:x}")
+
+ #for i in range(16):
+ for i in [0, 3, 4, 7, 8, 11, 12, 15]:
+ idx = 0
+ addr = vbar_phys + 0x80 * i
+ orig = self.p.read32(addr)
+ if (orig & 0xfc000000) != 0x14000000:
+ self.log(f"Unknown vector #{i}:\n")
+ self.u.disassemble_at(addr, 16)
+ else:
+ idx = len(self.vectors)
+ delta = orig & 0x3ffffff
+ if delta == 0:
+ target = None
+ self.log(f"Vector #{i}: Loop\n")
+ else:
+ target = (delta << 2) + vbar + 0x80 * i
+ self.log(f"Vector #{i}: 0x{target:x}\n")
+ self.vectors.append((i, target))
+ self.u.disassemble_at(addr, 16)
+ self.p.write32(addr, self.hvc(idx))
+
+ self.p.dc_cvau(vbar_phys, 0x800)
+ self.p.ic_ivau(vbar_phys, 0x800)
+
+ self.vbar_el1 = vbar
+
+ def set_logfile(self, fd):
+ self.print_tracer.log_file = fd
+
+ def init(self):
+ self.adt = load_adt(self.u.get_adt())
+ self.iodev = self.p.iodev_whoami()
+ self.tba = self.u.ba.copy()
+ self.device_addr_tbl = self.adt.build_addr_lookup()
+ self.print_tracer = trace.PrintTracer(self, self.device_addr_tbl)
+
+ # disable unused USB iodev early so interrupts can be reenabled in hv_init()
+ for iodev in IODEV:
+ if iodev >= IODEV.USB0 and iodev != self.iodev:
+ print(f"Disable iodev {iodev!s}")
+ self.p.iodev_set_usage(iodev, 0)
+
+ print("Initializing hypervisor over iodev %s" % self.iodev)
+ self.p.hv_init()
+
+ self.iface.set_handler(START.EXCEPTION_LOWER, EXC.SYNC, self.handle_exception)
+ self.iface.set_handler(START.EXCEPTION_LOWER, EXC.IRQ, self.handle_exception)
+ self.iface.set_handler(START.EXCEPTION_LOWER, EXC.FIQ, self.handle_exception)
+ self.iface.set_handler(START.EXCEPTION_LOWER, EXC.SERROR, self.handle_exception)
+ self.iface.set_handler(START.EXCEPTION, EXC.FIQ, self.handle_exception)
+ self.iface.set_handler(START.HV, HV_EVENT.USER_INTERRUPT, self.handle_exception)
+ self.iface.set_handler(START.HV, HV_EVENT.HOOK_VM, self.handle_exception)
+ self.iface.set_handler(START.HV, HV_EVENT.VTIMER, self.handle_exception)
+ self.iface.set_handler(START.HV, HV_EVENT.WDT_BARK, self.handle_bark)
+ self.iface.set_handler(START.HV, HV_EVENT.CPU_SWITCH, self.handle_exception)
+ self.iface.set_handler(START.HV, HV_EVENT.VIRTIO, self.handle_virtio)
+ self.iface.set_event_handler(EVENT.MMIOTRACE, self.handle_mmiotrace)
+ self.iface.set_event_handler(EVENT.IRQTRACE, self.handle_irqtrace)
+
+ # Map MMIO ranges as HW by default
+ for r in self.adt["/arm-io"].ranges:
+ print(f"Mapping MMIO range: {r.parent_addr:#x} .. {r.parent_addr + r.size:#x}")
+ self.add_tracer(irange(r.parent_addr, r.size), "HW", TraceMode.OFF)
+
+ hcr = HCR(self.u.mrs(HCR_EL2))
+ if self.novm:
+ hcr.VM = 0
+ hcr.AMO = 0
+ else:
+ hcr.TACR = 1
+ hcr.TIDCP = 0
+ hcr.TVM = 0
+ hcr.FMO = 1
+ hcr.IMO = 0
+ hcr.TTLBOS = 1
+ self.u.msr(HCR_EL2, hcr.value)
+
+ # Trap dangerous things
+ hacr = HACR(0)
+ if not self.novm:
+ #hacr.TRAP_CPU_EXT = 1
+ #hacr.TRAP_SPRR = 1
+ #hacr.TRAP_GXF = 1
+ hacr.TRAP_CTRR = 1
+ hacr.TRAP_EHID = 1
+ hacr.TRAP_HID = 1
+ hacr.TRAP_ACC = 1
+ hacr.TRAP_IPI = 1
+ hacr.TRAP_SERROR_INFO = 1 # M1RACLES mitigation
+ hacr.TRAP_PM = 1
+ self.u.msr(HACR_EL2, hacr.value)
+
+ # enable and route debug exceptions to EL2
+ mdcr = MDCR(0)
+ mdcr.TDE = 1
+ mdcr.TDA = 1
+ mdcr.TDOSA = 1
+ mdcr.TDRA = 1
+ self.u.msr(MDCR_EL2, mdcr.value)
+ self.u.msr(MDSCR_EL1, MDSCR(MDE=1).value)
+
+ # Enable AMX
+ amx_ctl = AMX_CTL(self.u.mrs(AMX_CTL_EL1))
+ amx_ctl.EN_EL1 = 1
+ self.u.msr(AMX_CTL_EL1, amx_ctl.value)
+
+ # Set guest AP keys
+ self.u.msr(APVMKEYLO_EL2, 0x4E7672476F6E6147)
+ self.u.msr(APVMKEYHI_EL2, 0x697665596F755570)
+ self.u.msr(APSTS_EL12, 1)
+
+ self.map_vuart()
+
+ actlr = ACTLR(self.u.mrs(ACTLR_EL12))
+ actlr.EnMDSB = 1
+ self.u.msr(ACTLR_EL12, actlr.value)
+
+ self.setup_adt()
+
+ def map_vuart(self):
+ node = base = self.adt["/arm-io/uart0"]
+ base = node.get_reg(0)[0]
+
+ zone = irange(base, 0x4000)
+ irq = node.interrupts[0]
+ self.p.hv_map_vuart(base, irq, self.iodev)
+ self.add_tracer(zone, "VUART", TraceMode.RESERVED)
+
+ def map_essential(self):
+ # Things we always map/take over, for the hypervisor to work
+ _pmgr = {}
+
+ def wh(base, off, data, width):
+ self.log(f"PMGR W {base:x}+{off:x}:{width} = 0x{data:x}: Dangerous write")
+ self.p.mask32(base + off, 0x3ff, (data | 0xf) & ~(0x80000400))
+ _pmgr[base + off] = (data & 0xfffffc0f) | ((data & 0xf) << 4)
+
+ def rh(base, off, width):
+ data = self.p.read32(base + off)
+ ret = _pmgr.setdefault(base + off, data)
+ self.log(f"PMGR R {base:x}+{off:x}:{width} = 0x{data:x} -> 0x{ret:x}")
+ return ret
+
+ atc = f"ATC{self.iodev - IODEV.USB0}_USB"
+
+ hook_devs = ["UART0", atc]
+
+ pmgr = self.adt["/arm-io/pmgr"]
+ dev_by_name = {dev.name: dev for dev in pmgr.devices}
+ dev_by_id = {dev.id: dev for dev in pmgr.devices}
+
+ pmgr_hooks = []
+
+ def hook_pmgr_dev(dev):
+ ps = pmgr.ps_regs[dev.psreg]
+ if dev.psidx or dev.psreg:
+ addr = pmgr.get_reg(ps.reg)[0] + ps.offset + dev.psidx * 8
+ pmgr_hooks.append(addr)
+ for idx in dev.parents:
+ if idx in dev_by_id:
+ hook_pmgr_dev(dev_by_id[idx])
+
+ for name in hook_devs:
+ dev = dev_by_name[name]
+ hook_pmgr_dev(dev)
+
+ pmgr0_start = pmgr.get_reg(0)[0]
+
+ for addr in pmgr_hooks:
+ self.map_hook(addr, 4, write=wh, read=rh)
+ #TODO : turn into a real tracer
+ self.add_tracer(irange(addr, 4), "PMGR HACK", TraceMode.RESERVED)
+
+ pg_overrides = {
+ 0x23d29c05c: 0xc000000,
+ 0x23d29c044: 0xc000000,
+ }
+
+ for addr in pg_overrides:
+ self.map_hook(addr, 4, read=lambda base, off, width: pg_overrides[base + off])
+ self.add_tracer(irange(addr, 4), "PMGR HACK", TraceMode.RESERVED)
+
+ def cpustart_wh(base, off, data, width):
+ self.log(f"CPUSTART W {base:x}+{off:x}:{width} = 0x{data:x}")
+ if off >= 8:
+ assert width == 32
+ die = base // 0x20_0000_0000
+ cluster = (off - 8) // 4
+ for i in range(32):
+ if data & (1 << i):
+ self.start_secondary(die, cluster, i)
+
+ die_count = self.adt["/arm-io"].die_count if hasattr(self.adt["/arm-io"], "die-count") else 1
+
+ for die in range(0, die_count):
+ if self.u.adt["/chosen"].chip_id in (0x8103, 0x6000, 0x6001, 0x6002):
+ cpu_start = 0x54000 + die * 0x20_0000_0000
+ elif self.u.adt["/chosen"].chip_id in (0x8112,):
+ cpu_start = 0x34000 + die * 0x20_0000_0000
+ else:
+ self.log("CPUSTART unknown for this SoC!")
+ break
+
+ zone = irange(pmgr0_start + cpu_start, 0x20)
+ self.map_hook(pmgr0_start + cpu_start, 0x20, write=cpustart_wh)
+ self.add_tracer(zone, "CPU_START", TraceMode.RESERVED)
+
+ def start_secondary(self, die, cluster, cpu):
+ self.log(f"Starting guest secondary {die}:{cluster}:{cpu}")
+
+ for node in list(self.adt["cpus"]):
+ if ((die << 11) | (cluster << 8) | cpu) == node.reg:
+ break
+ else:
+ self.log("CPU not found!")
+ return
+
+ entry = self.p.read64(node.cpu_impl_reg[0]) & 0xfffffffffff
+ index = node.cpu_id
+ self.log(f" CPU #{index}: RVBAR = {entry:#x}")
+
+ self.sysreg[index] = {}
+ self.started_cpus.add(index)
+ self.p.hv_start_secondary(index, entry)
+
+ def setup_adt(self):
+ self.adt["product"].product_name += " on m1n1 hypervisor"
+ self.adt["product"].product_description += " on m1n1 hypervisor"
+ soc_name = "Virtual " + self.adt["product"].product_soc_name + " on m1n1 hypervisor"
+ self.adt["product"].product_soc_name = soc_name
+
+ if self.iodev >= IODEV.USB0:
+ idx = self.iodev - IODEV.USB0
+ for prefix in ("/arm-io/dart-usb%d",
+ "/arm-io/atc-phy%d",
+ "/arm-io/usb-drd%d",
+ "/arm-io/acio%d",
+ "/arm-io/acio-cpu%d",
+ "/arm-io/dart-acio%d",
+ "/arm-io/apciec%d",
+ "/arm-io/dart-apciec%d",
+ "/arm-io/apciec%d-piodma",
+ "/arm-io/i2c0/hpmBusManager/hpm%d",
+ "/arm-io/atc%d-dpxbar",
+ "/arm-io/atc%d-dpphy",
+ "/arm-io/atc%d-dpin0",
+ "/arm-io/atc%d-dpin1",
+ "/arm-io/atc-phy%d",
+ ):
+ name = prefix % idx
+ print(f"Removing ADT node {name}")
+ try:
+ del self.adt[name]
+ except KeyError:
+ pass
+
+ if self.wdt_cpu is not None:
+ name = f"/cpus/cpu{self.wdt_cpu}"
+ print(f"Removing ADT node {name}")
+ try:
+ del self.adt[name]
+ except KeyError:
+ pass
+
+ if not self.smp:
+ for cpu in list(self.adt["cpus"]):
+ if cpu.name != "cpu0":
+ print(f"Removing ADT node {cpu._path}")
+ try:
+ del self.adt["cpus"][cpu.name]
+ except KeyError:
+ pass
+
+ def set_bootargs(self, boot_args):
+ if "-v" in boot_args.split():
+ self.tba.video.display = 0
+ else:
+ self.tba.video.display = 1
+ print(f"Setting boot arguments to {boot_args!r}")
+ self.tba.cmdline = boot_args
+
+ def unmap_carveouts(self):
+ print(f"Unmapping TZ carveouts...")
+ carveout_p = self.p.mcc_get_carveouts()
+ while True:
+ base = self.p.read64(carveout_p)
+ size = self.p.read64(carveout_p + 8)
+ if not base:
+ break
+ print(f" Unmap [{base:#x}..{base + size - 1:#x}]")
+ self.del_tracer(irange(base, size), "RAM-LOW")
+ self.del_tracer(irange(base, size), "RAM-HIGH")
+ carveout_p += 16
+
+ def enable_time_stealing(self):
+ self.p.hv_set_time_stealing(True)
+
+ def disable_time_stealing(self):
+ self.p.hv_set_time_stealing(False)
+
+
+ def load_raw(self, image, entryoffset=0x800, use_xnu_symbols=False, vmin=0):
+ sepfw_start, sepfw_length = self.u.adt["chosen"]["memory-map"].SEPFW
+ tc_start, tc_size = self.u.adt["chosen"]["memory-map"].TrustCache
+ if hasattr(self.u.adt["chosen"]["memory-map"], "preoslog"):
+ preoslog_start, preoslog_size = self.u.adt["chosen"]["memory-map"].preoslog
+ else:
+ preoslog_size = 0
+
+ image_size = align(len(image))
+ sepfw_off = image_size
+ image_size += align(sepfw_length)
+ preoslog_off = image_size
+ image_size += preoslog_size
+ self.bootargs_off = image_size
+ bootargs_size = 0x4000
+ image_size += bootargs_size
+
+ print(f"Total region size: 0x{image_size:x} bytes")
+
+ self.phys_base = phys_base = guest_base = self.u.heap_top
+ self.ram_base = self.phys_base & ~0xffffffff
+ self.ram_size = self.u.ba.mem_size_actual
+ guest_base += 16 << 20 # ensure guest starts within a 16MB aligned region of mapped RAM
+ self.adt_base = guest_base
+ guest_base += align(self.u.ba.devtree_size)
+ tc_base = guest_base
+ guest_base += align(tc_size)
+ self.guest_base = guest_base
+ mem_top = self.u.ba.phys_base + self.u.ba.mem_size
+ mem_size = mem_top - phys_base
+
+ print(f"Physical memory: 0x{phys_base:x} .. 0x{mem_top:x}")
+ print(f"Guest region start: 0x{guest_base:x}")
+
+ self.entry = guest_base + entryoffset
+
+ print(f"Mapping guest physical memory...")
+ self.add_tracer(irange(self.ram_base, self.u.ba.phys_base - self.ram_base), "RAM-LOW", TraceMode.OFF)
+ self.add_tracer(irange(phys_base, self.u.ba.mem_size_actual - phys_base + self.ram_base), "RAM-HIGH", TraceMode.OFF)
+ self.unmap_carveouts()
+
+ print(f"Loading kernel image (0x{len(image):x} bytes)...")
+ self.u.compressed_writemem(guest_base, image, True)
+ self.p.dc_cvau(guest_base, len(image))
+ self.p.ic_ivau(guest_base, len(image))
+
+ print(f"Copying SEPFW (0x{sepfw_length:x} bytes)...")
+ self.p.memcpy8(guest_base + sepfw_off, sepfw_start, sepfw_length)
+
+ print(f"Copying TrustCache (0x{tc_size:x} bytes)...")
+ self.p.memcpy8(tc_base, tc_start, tc_size)
+
+ if hasattr(self.u.adt["chosen"]["memory-map"], "preoslog"):
+ print(f"Copying preoslog (0x{preoslog_size:x} bytes)...")
+ self.p.memcpy8(guest_base + preoslog_off, preoslog_start, preoslog_size)
+
+ print(f"Adjusting addresses in ADT...")
+ self.adt["chosen"]["memory-map"].SEPFW = (guest_base + sepfw_off, sepfw_length)
+ self.adt["chosen"]["memory-map"].TrustCache = (tc_base, tc_size)
+ self.adt["chosen"]["memory-map"].DeviceTree = (self.adt_base, align(self.u.ba.devtree_size))
+ self.adt["chosen"]["memory-map"].BootArgs = (guest_base + self.bootargs_off, bootargs_size)
+ if hasattr(self.u.adt["chosen"]["memory-map"], "preoslog"):
+ self.adt["chosen"]["memory-map"].preoslog = (guest_base + preoslog_off, preoslog_size)
+
+ print(f"Setting up bootargs at 0x{guest_base + self.bootargs_off:x}...")
+
+ self.tba.mem_size = mem_size
+ self.tba.phys_base = phys_base
+ self.tba.virt_base = 0xfffffe0010000000 + (phys_base & (32 * 1024 * 1024 - 1))
+ self.tba.devtree = self.adt_base - phys_base + self.tba.virt_base
+ self.tba.top_of_kernel_data = guest_base + image_size
+
+ if use_xnu_symbols == True:
+ self.sym_offset = vmin - guest_base + self.tba.phys_base - self.tba.virt_base
+
+ self.iface.writemem(guest_base + self.bootargs_off, BootArgs.build(self.tba))
+
+ print("Setting secondary CPU RVBARs...")
+ rvbar = self.entry & ~0xfff
+ for cpu in self.adt["cpus"][1:]:
+ addr, size = cpu.cpu_impl_reg
+ print(f" {cpu.name}: [0x{addr:x}] = 0x{rvbar:x}")
+ self.p.write64(addr, rvbar)
+
+ def _load_macho_symbols(self):
+ self.symbol_dict = self.macho.symbols
+ self.symbols = [(v, k) for k, v in self.macho.symbols.items()]
+ self.symbols.sort()
+
+ def load_macho(self, data, symfile=None):
+ if isinstance(data, str):
+ data = open(data, "rb")
+
+ self.macho = macho = MachO(data)
+ if symfile is not None:
+ if isinstance(symfile, str):
+ symfile = open(symfile, "rb")
+ syms = MachO(symfile)
+ macho.add_symbols("com.apple.kernel", syms)
+ self.xnu_mode = True
+
+ self._load_macho_symbols()
+
+ def load_hook(data, segname, size, fileoff, dest):
+ if segname != "__TEXT_EXEC":
+ return data
+
+ print(f"Patching segment {segname}...")
+
+ a = array.array("I", data)
+
+ output = []
+
+ p = 0
+ while (p := data.find(b"\x20\x00", p)) != -1:
+ if (p & 3) != 2:
+ p += 1
+ continue
+
+ opcode = a[p // 4]
+ inst = self.hvc((opcode & 0xffff))
+ off = fileoff + (p & ~3)
+ if off >= 0xbfcfc0:
+ print(f" 0x{off:x}: 0x{opcode:04x} -> hvc 0x{opcode:x} (0x{inst:x})")
+ a[p // 4] = inst
+ p += 4
+
+ print("Done.")
+ return a.tobytes()
+
+ #image = macho.prepare_image(load_hook)
+ image = macho.prepare_image()
+ self.load_raw(image, entryoffset=(macho.entry - macho.vmin), use_xnu_symbols=self.xnu_mode, vmin=macho.vmin)
+
+
+ def update_pac_mask(self):
+ tcr = TCR(self.u.mrs(TCR_EL12))
+ valid_bits = (1 << (64 - tcr.T1SZ)) - 1
+ self.pac_mask = 0xffffffffffffffff & ~valid_bits
+ valid_bits = (1 << (64 - tcr.T0SZ)) - 1
+ self.user_pac_mask = 0xffffffffffffffff & ~valid_bits
+
+ def unpac(self, v):
+ if v & (1 << 55):
+ return v | self.pac_mask
+ else:
+ return v & ~self.user_pac_mask
+
+ def load_system_map(self, path):
+ # Assume Linux
+ self.sym_offset = 0
+ self.xnu_mode = False
+ self.symbols = []
+ self.symbol_dict = {}
+ with open(path) as fd:
+ for line in fd.readlines():
+ addr, t, name = line.split()
+ addr = int(addr, 16)
+ self.symbols.append((addr, name))
+ self.symbol_dict[name] = addr
+ self.symbols.sort()
+
+ def add_kext_symbols(self, kext, demangle=False):
+ info_plist = plistlib.load(open(f"{kext}/Contents/Info.plist", "rb"))
+ identifier = info_plist["CFBundleIdentifier"]
+ name = info_plist["CFBundleName"]
+ macho = MachO(open(f"{kext}/Contents/MacOS/{name}", "rb"))
+ self.macho.add_symbols(identifier, macho, demangle=demangle)
+ self._load_macho_symbols()
+
+ def _handle_sigint(self, signal=None, stack=None):
+ self._sigint_pending = True
+ self.interrupt()
+
+ def interrupt(self):
+ if self._in_handler:
+ return
+
+ # Kick the proxy to break out of the hypervisor
+ self.iface.dev.write(b"!")
+
+ def run_script(self, path):
+ new_locals = runpy.run_path(path, init_globals=self.shell_locals, run_name="<hv_script>")
+ self.shell_locals.clear()
+ self.shell_locals.update(new_locals)
+
+ def run_code(self, code):
+ exec(code, self.shell_locals)
+
+ def start(self):
+ print("Disabling other iodevs...")
+ for iodev in IODEV:
+ if iodev != self.iodev:
+ print(f" - {iodev!s}")
+ self.p.iodev_set_usage(iodev, 0)
+
+ print("Doing essential MMIO remaps...")
+ self.map_essential()
+
+ print("Updating page tables...")
+ self.pt_update()
+
+ adt_blob = self.adt.build()
+ print(f"Uploading ADT (0x{len(adt_blob):x} bytes)...")
+ self.iface.writemem(self.adt_base, adt_blob)
+
+ print("Improving logo...")
+ self.p.fb_improve_logo()
+
+ print("Shutting down framebuffer...")
+ self.p.fb_shutdown(True)
+
+ print("Enabling SPRR...")
+ self.u.msr(SPRR_CONFIG_EL1, 1)
+
+ print("Enabling GXF...")
+ self.u.msr(GXF_CONFIG_EL1, 1)
+
+ print(f"Jumping to entrypoint at 0x{self.entry:x}")
+
+ self.iface.dev.timeout = None
+ self.default_sigint = signal.signal(signal.SIGINT, self._handle_sigint)
+
+ set_sigquit_stackdump_handler()
+
+ if self.wdt_cpu is not None:
+ self.p.hv_wdt_start(self.wdt_cpu)
+ # Does not return
+
+ self.started = True
+ self.started_cpus.add(0)
+ self.p.hv_start(self.entry, self.guest_base + self.bootargs_off)
+
+from .. import trace
diff --git a/tools/proxyclient/m1n1/hv/gdbserver/__init__.py b/tools/proxyclient/m1n1/hv/gdbserver/__init__.py
new file mode 100644
index 0000000..ade807f
--- /dev/null
+++ b/tools/proxyclient/m1n1/hv/gdbserver/__init__.py
@@ -0,0 +1,480 @@
+# SPDX-License-Identifier: MIT
+import errno, io, os, pkgutil, re, selectors, socketserver, threading, traceback
+from construct import Array, BytesInteger, Container, Int32ul, Int64ul, Struct
+
+from ...proxy import *
+from ...sysreg import *
+from ...utils import *
+
+from ..types import *
+
+__all__ = ["GDBServer"]
+
+class GDBServer:
+ __g = Struct(
+ "regs" / Array(32, Int64ul),
+ "pc" / Int64ul,
+ "spsr" / Int32ul,
+ "q" / Array(32, BytesInteger(16, swapped=True)),
+ "fpsr" / Int32ul,
+ "fpcr" / Int32ul,
+ )
+ __seperator = re.compile("[,;:]")
+
+ def __init__(self, hv, address, log):
+ self.__hc = None
+ self.__hg = None
+ self.__hv = hv
+ self.__interrupt_eventfd = os.eventfd(0, flags=os.EFD_CLOEXEC | os.EFD_NONBLOCK)
+ self.__interrupt_selector = selectors.DefaultSelector()
+ self.__request = None
+ self.log = log
+
+ self.__interrupt_selector.register(self.__interrupt_eventfd, selectors.EVENT_READ)
+
+ handle = self.__handle
+
+ class Handler(socketserver.BaseRequestHandler):
+ def handle(self):
+ handle(self.request)
+
+ self.__server = socketserver.UnixStreamServer(address, Handler, False)
+ self.__thread = threading.Thread(target=self.__server.serve_forever,)
+
+ def __add_wp(self, addr, kind, lsc):
+ start = addr & 7
+ if start + kind > 8:
+ return b"E01"
+
+ self.__hv.add_hw_wp(addr & ~7, ((1 << kind) - 1) << start, lsc)
+ return b"OK"
+
+ def __remove_wp(self, addr):
+ self.__hv.remove_hw_wp(addr & ~7)
+ return b"OK"
+
+ def __cpu(self, cpu):
+ if cpu is None:
+ return
+
+ self.__hv.cpu(cpu)
+
+ def __stop_reply(self):
+ self.__hc = None
+ self.__hg = None
+
+ prefix = b"T05thread:"
+
+ if self.__hv.exc_reason == START.EXCEPTION_LOWER:
+ if self.__hv.exc_code == EXC.SYNC:
+ if self.__hv.ctx.esr.EC == ESR_EC.BKPT_LOWER:
+ prefix = b"T05hwbreak:;thread:"
+ elif self.__hv.ctx.esr.EC == ESR_EC.WATCH_LOWER:
+ bas = self.__hv.get_wp_bas(self.__hv.ctx.far)
+ if not bas is None and bas != 0:
+ offset = 0
+ while (bas & (1 << offset)) == 0:
+ offset += 1
+ addr = self.__hv.ctx.far + offset
+ formatted_addr = bytes(format(addr, "x"), "utf-8")
+ prefix = b"T05watch:" + formatted_addr + b";thread:"
+ elif self.__hv.exc_reason == START.HV:
+ if self.__hv.exc_code == HV_EVENT.USER_INTERRUPT:
+ prefix = b"T02thread:"
+
+ return prefix + bytes(format(self.__hv.ctx.cpu_id, "x"), "utf-8") + b";"
+
+ def __wait_shell(self):
+ try:
+ os.eventfd_read(self.__interrupt_eventfd)
+ except BlockingIOError:
+ pass
+
+ while not self.__interrupt_eventfd in (key.fileobj for key, mask in self.__interrupt_selector.select()):
+ recv = self.__request.recv(1)
+ if not recv:
+ break
+
+ for byte in recv:
+ if byte in b"\1\3":
+ self.__hv.interrupt()
+ break
+
+ def __eval(self, data):
+ if self.log:
+ self.log(f"eval: {data}")
+
+ if len(data) < 1:
+ return b""
+
+ if data[0] in b"?":
+ return self.__stop_reply()
+
+ if data[0] in b"c":
+ if len(data) != 1:
+ self.__cpu(self.__hc)
+ self.__hv.ctx.elr = int(data[1:].decode(), 16)
+
+ self.__hv.cont()
+ self.__wait_shell()
+ return self.__stop_reply()
+
+ if data[0] in b"g":
+ self.__cpu(self.__hg)
+ g = Container()
+ g.regs = self.__hv.ctx.regs.copy()
+ g.regs[31] = self.__hv.ctx.sp[1]
+ g.pc = self.__hv.ctx.elr
+ g.spsr = self.__hv.ctx.spsr.value
+ g.q = self.__hv.u.q
+ g.fpsr = self.__hv.u.mrs(FPSR)
+ g.fpcr = self.__hv.u.mrs(FPCR)
+
+ return bytes(GDBServer.__g.build(g).hex(), "utf-8")
+
+ if data[0] in b"G":
+ g = GDBServer.__g.parse(bytes.fromhex(data[1:].decode()))
+ self.__cpu(self.__hg)
+
+ for index in range(31):
+ self.__hv.ctx.regs[index] = g.regs[index]
+
+ self.__hv.ctx.sp[1] = g.regs[31]
+ self.__hv.ctx.elr = g.pc
+ self.__hv.ctx.spsr = g.spsr.value
+
+ q = self.__hv.u.q
+ for index, value in enumerate(g.q):
+ q[index] = value
+ self.__hv.u.push_simd()
+
+ self.__hv.u.msr(FPSR, g.fpsr, silent=True)
+ self.__hv.u.msr(FPCR, g.fpsr, silent=True)
+
+ return b"OK"
+
+ if data[0] in b"H":
+ if len(data) > 1:
+ if data[1] in b"c":
+ cpu_id = int(data[2:].decode(), 16)
+ if cpu_id in self.__hv.started_cpus:
+ self.__hc = cpu_id
+ return b"OK"
+
+ return b"E01"
+
+ if data[1] in b"g":
+ cpu_id = int(data[2:].decode(), 16)
+ if cpu_id in self.__hv.started_cpus:
+ self.__hg = cpu_id
+ return b"OK"
+
+ return b"E01"
+
+ return b""
+
+ if data[0] in b"krR":
+ self.__hv.reboot()
+
+ if data[0] in b"m":
+ split = GDBServer.__seperator.split(data[1:].decode(), maxsplit=1)
+ fields = [int(field, 16) for field in split]
+ return bytes(self.__hv.readmem(fields[0], fields[1]).hex(), "utf-8")
+
+ if data[0] in b"M":
+ split = GDBServer.__seperator.split(data[1:].decode(), maxsplit=2)
+ mem = bytes.fromhex(split[2])[:int(split[1], 16)]
+ if self.__hv.writemem(int(split[0], 16), mem) < len(mem):
+ return "E22"
+
+ return b"OK"
+
+ if data[0] in b"p":
+ number = int(data[1:].decode(), 16)
+ self.__cpu(self.__hg)
+ if number < 31:
+ reg = GDBServer.__g.regs.subcon.subcon.build(self.__hv.ctx.regs[number])
+ elif number == 31:
+ reg = GDBServer.__g.regs.subcon.subcon.build(self.__hv.ctx.sp[1])
+ elif number == 32:
+ reg = GDBServer.__g.pc.build(self.__hv.ctx.elr)
+ elif number == 33:
+ reg = GDBServer.__g.spsr.build(self.__hv.ctx.spsr.value)
+ elif number < 66:
+ reg = GDBServer.__g.q.subcon.subcon.build(self.__hv.u.q[number - 34])
+ elif number == 66:
+ reg = GDBServer.__g.fpsr.build(self.__hv.u.mrs(FPSR))
+ elif number == 67:
+ reg = GDBServer.__g.fpcr.build(self.__hv.u.mrs(FPCR))
+ else:
+ return b"E01"
+
+ return bytes(reg.hex(), "utf-8")
+
+ if data[0] in b"P":
+ partition = data[1:].partition(b"=")
+ number = int(partition[0].decode(), 16)
+ reg = bytes.fromhex(partition[2].decode())
+ self.__cpu(self.__hg)
+ if number < 31:
+ self.__hv.ctx.regs[number] = GDBServer.__g.regs.subcon.subcon.unpack(reg)
+ elif number == 31:
+ self.__hv.ctx.regs[1] = GDBServer.__g.regs.subcon.subcon.unpack(reg)
+ elif number == 32:
+ self.__hv.ctx.elr = GDBServer.__g.pc.parse(reg)
+ elif number == 33:
+ self.__hv.ctx.spsr.value = GDBServer.__g.spsr.parse(reg)
+ elif number < 66:
+ self.__hv.u.q[number - 34] = GDBServer.__g.q.subcon.subcon.parse(reg)
+ self.__hv.u.push_simd()
+ elif number == 66:
+ self.__hv.u.msr(FPSR, GDBServer.__g.fpsr.parse(reg), silent=True)
+ elif number == 67:
+ self.__hv.u.msr(FPCR, GDBServer.__g.fpcr.parse(reg), silent=True)
+ else:
+ return b"E01"
+
+ return b"OK"
+
+ if data[0] in b"q":
+ split = GDBServer.__seperator.split(data[1:].decode(), maxsplit=1)
+ if split[0] == "C":
+ cpu_id = self.__hg or self.__hv.ctx.cpu_id
+ return b"QC" + bytes(format(cpu_id, "x"), "utf-8")
+
+ if split[0] == "fThreadInfo":
+ cpu_ids = b",".join(bytes(format(cpu.cpu_id, "x"), "utf-8") for cpu in self.__hv.adt["cpus"])
+ return b"m" + cpu_ids
+
+ if split[0] == "sThreadInfo":
+ return b"l"
+
+ if split[0] == "Rcmd":
+ self.__cpu(self.__hg)
+ self.__hv.run_code(split[1])
+ return b"OK"
+
+ if split[0] == "Supported":
+ return b"PacketSize=65536;qXfer:features:read+;hwbreak+"
+
+ if split[0] == "ThreadExtraInfo":
+ thread_id = int(split[1], 16)
+ for node in self.__hv.adt["cpus"]:
+ if node.cpu_id == thread_id:
+ return bytes(bytes(str(node), "utf-8").hex(), "utf-8")
+
+ return b""
+
+ if split[0] == "Xfer":
+ xfer = GDBServer.__seperator.split(split[1], maxsplit=4)
+ if xfer[0] == "features" and xfer[1] == "read":
+ resource = os.path.join("features", xfer[2])
+ annex = pkgutil.get_data(__name__, resource)
+ if annex is None:
+ return b"E00"
+
+ request_offset = int(xfer[3], 16)
+ request_len = int(xfer[4], 16)
+ read = annex[request_offset:request_offset + request_len]
+ return (b"l" if len(read) < request_len else b"m") + read
+
+ return b""
+
+ if split[0] == "HostInfo":
+ addressing_bits = bytes(str(64 - self.__hv.pac_mask.bit_count()), "utf-8")
+ return b"cputype:16777228;cpusubtype:2;endian:little;ptrsize:64;watchpoint_exceptions_received:before;addressing_bits:" + addressing_bits + b";"
+
+ return b""
+
+ if data[0] in b"s":
+ self.__cpu(self.__hc)
+
+ if len(data) != 1:
+ self.__hv.ctx.elr = int(data[1:].decode(), 16)
+
+ self.__hv.step()
+ return self.__stop_reply()
+
+ if data[0] in b"T":
+ if int(data[1:].decode(), 16) in self.__hv.started_cpus:
+ return b"OK"
+
+ return b"E01"
+
+ if data[0] in b"X":
+ partition = data[1:].partition(b":")
+ split = GDBServer.__seperator.split(partition[0].decode(), maxsplit=1)
+ mem = partition[2][:int(split[1], 16)]
+ if self.__hv.writemem(int(split[0], 16), mem) < len(mem):
+ return b"E22"
+
+ return b"OK"
+
+ if data[0] in b"z":
+ split = GDBServer.__seperator.split(data[1:].decode(), maxsplit=2)
+ if split[0] == "1":
+ self.__hv.remove_hw_bp(int(split[1], 16))
+ return b"OK"
+
+ if split[0] == "2":
+ return self.__remove_wp(int(split[1], 16))
+
+ if split[0] == "3":
+ return self.__remove_wp(int(split[1], 16))
+
+ if split[0] == "4":
+ return self.__remove_wp(int(split[1], 16))
+
+ return b""
+
+ if data[0] in b"Z":
+ split = GDBServer.__seperator.split(data[1:].decode(), maxsplit=2)
+ if split[0] == "1":
+ self.__hv.add_hw_bp(int(split[1], 16))
+ return b"OK"
+
+ if split[0] == "2":
+ addr = int(split[1], 16)
+ kind = int(split[2], 16)
+ return self.__add_wp(addr, kind, DBGWCR_LSC.S)
+
+ if split[0] == "3":
+ addr = int(split[1], 16)
+ kind = int(split[2], 16)
+ return self.__add_wp(addr, kind, DBGWCR_LSC.L)
+
+ if split[0] == "4":
+ addr = int(split[1], 16)
+ kind = int(split[2], 16)
+ return self.__add_wp(addr, kind, DBGWCR_LSC.S | DBGWCR_LSC.L)
+
+ return b""
+
+ return b""
+
+ def __send(self, prefix, data):
+ with io.BytesIO(prefix) as buffer:
+ buffer.write(prefix)
+
+ last = 0
+ for index, byte in enumerate(data):
+ if not byte in b"#$}*":
+ continue
+
+ buffer.write(data[last:index])
+ buffer.write(b"}")
+ buffer.write(bytes([byte ^ 0x20]))
+ last = index + 1
+
+ buffer.write(data[last:])
+ checksum = (sum(buffer.getvalue()) - sum(prefix)) % 256
+
+ buffer.write(b"#")
+ buffer.write(bytes(format(checksum, "02x"), "utf-8"))
+
+ value = buffer.getvalue()
+
+ if self.log:
+ self.log(f"send: {value}")
+
+ self.__request.send(value)
+
+ def __handle(self, request):
+ self.__request = request
+ input_buffer = b""
+
+ if not self.__hv.in_shell:
+ self.__hv.interrupt()
+ self.__wait_shell()
+
+ self.__interrupt_selector.register(self.__request, selectors.EVENT_READ)
+ try:
+ while True:
+ recv = self.__request.recv(65536)
+ if not recv:
+ break
+
+ input_buffer += recv
+
+ while True:
+ dollar = input_buffer.find(b"$")
+ if dollar < 0:
+ input_buffer = b""
+ break
+
+ sharp = input_buffer.find(b"#", dollar)
+ if sharp < 0 or len(input_buffer) < sharp + 3:
+ input_buffer = input_buffer[dollar:]
+ break
+
+ input_data = input_buffer[dollar + 1:sharp]
+ input_checksum = input_buffer[sharp + 1:sharp + 3]
+ input_buffer = input_buffer[sharp + 3:]
+
+ try:
+ parsed_input_checksum = int(input_checksum.decode(), 16)
+ except ValueError as error:
+ print(error)
+ continue
+
+ if (sum(input_data) % 256) != parsed_input_checksum:
+ self.__request.send(b"-")
+ continue
+
+ self.__request.send(b"+")
+
+ with io.BytesIO() as input_decoded:
+ input_index = 0
+ input_last = 0
+ while input_index < len(input_data):
+ if input_data[input_index] == b"*":
+ input_decoded.write(input_data[input_last:input_index])
+ instance = input_decoded.getvalue()[-1]
+ input_index += 1
+ input_run_len = input_data[input_index] - 29
+ input_run = bytes([instance]) * input_run_len
+ input_decoded.write(input_run)
+ input_index += 1
+ input_last = input_index
+ elif input_data[input_index] == b"}":
+ input_decoded.write(input_data[input_last:input_index])
+ input_index += 1
+ input_decoded.write(bytes([input_data[input_index] ^ 0x20]))
+ input_index += 1
+ input_last = input_index
+ else:
+ input_index += 1
+
+ input_decoded.write(input_data[input_last:])
+
+ try:
+ output_decoded = self.__eval(input_decoded.getvalue())
+ except Exception:
+ output_decoded = b"E." + bytes(traceback.format_exc(), "utf-8")
+
+ self.__send(b"$", output_decoded)
+ finally:
+ self.__interrupt_selector.unregister(self.__request)
+
+ def notify_in_shell(self):
+ os.eventfd_write(self.__interrupt_eventfd, 1)
+
+ def activate(self):
+ try:
+ self.__server.server_bind()
+ except OSError as error:
+ if error.errno != errno.EADDRINUSE:
+ raise
+
+ os.remove(self.__server.server_address)
+ self.__server.server_bind()
+
+ self.__server.server_activate()
+ self.__thread.start()
+
+ def shutdown(self):
+ os.close(self.__interrupt_eventfd)
+ self.__interrupt_selector.close()
+ self.__server.shutdown()
+ self.__server.server_close()
+ self.__thread.join()
diff --git a/tools/proxyclient/m1n1/hv/gdbserver/features/aarch64-core.xml b/tools/proxyclient/m1n1/hv/gdbserver/features/aarch64-core.xml
new file mode 100644
index 0000000..b6d344f
--- /dev/null
+++ b/tools/proxyclient/m1n1/hv/gdbserver/features/aarch64-core.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2009-2022 Free Software Foundation, Inc.
+ Contributed by ARM Ltd.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.aarch64.core">
+ <reg name="x0" bitsize="64"/>
+ <reg name="x1" bitsize="64"/>
+ <reg name="x2" bitsize="64"/>
+ <reg name="x3" bitsize="64"/>
+ <reg name="x4" bitsize="64"/>
+ <reg name="x5" bitsize="64"/>
+ <reg name="x6" bitsize="64"/>
+ <reg name="x7" bitsize="64"/>
+ <reg name="x8" bitsize="64"/>
+ <reg name="x9" bitsize="64"/>
+ <reg name="x10" bitsize="64"/>
+ <reg name="x11" bitsize="64"/>
+ <reg name="x12" bitsize="64"/>
+ <reg name="x13" bitsize="64"/>
+ <reg name="x14" bitsize="64"/>
+ <reg name="x15" bitsize="64"/>
+ <reg name="x16" bitsize="64"/>
+ <reg name="x17" bitsize="64"/>
+ <reg name="x18" bitsize="64"/>
+ <reg name="x19" bitsize="64"/>
+ <reg name="x20" bitsize="64"/>
+ <reg name="x21" bitsize="64"/>
+ <reg name="x22" bitsize="64"/>
+ <reg name="x23" bitsize="64"/>
+ <reg name="x24" bitsize="64"/>
+ <reg name="x25" bitsize="64"/>
+ <reg name="x26" bitsize="64"/>
+ <reg name="x27" bitsize="64"/>
+ <reg name="x28" bitsize="64"/>
+ <reg name="x29" bitsize="64"/>
+ <reg name="x30" bitsize="64"/>
+ <reg name="sp" bitsize="64" type="data_ptr"/>
+
+ <reg name="pc" bitsize="64" type="code_ptr"/>
+
+ <flags id="cpsr_flags" size="4">
+ <!-- Stack Pointer. -->
+ <field name="SP" start="0" end="0"/>
+
+ <!-- Exception Level. -->
+ <field name="EL" start="2" end="3"/>
+ <!-- Execution state. -->
+ <field name="nRW" start="4" end="4"/>
+
+ <!-- FIQ interrupt mask. -->
+ <field name="F" start="6" end="6"/>
+ <!-- IRQ interrupt mask. -->
+ <field name="I" start="7" end="7"/>
+ <!-- SError interrupt mask. -->
+ <field name="A" start="8" end="8"/>
+ <!-- Debug exception mask. -->
+ <field name="D" start="9" end="9"/>
+
+ <!-- ARMv8.0-A: Speculative Store Bypass. -->
+ <field name="SSBS" start="12" end="12"/>
+
+ <!-- Illegal Execution state. -->
+ <field name="IL" start="20" end="20"/>
+ <!-- Software Step. -->
+ <field name="SS" start="21" end="21"/>
+ <!-- ARMv8.1-A: Privileged Access Never. -->
+ <field name="PAN" start="22" end="22"/>
+ <!-- ARMv8.2-A: User Access Override. -->
+ <field name="UAO" start="23" end="23"/>
+ <!-- ARMv8.4-A: Data Independent Timing. -->
+ <field name="DIT" start="24" end="24"/>
+ <!-- ARMv8.5-A: Tag Check Override. -->
+ <field name="TCO" start="25" end="25"/>
+
+ <!-- Overflow Condition flag. -->
+ <field name="V" start="28" end="28"/>
+ <!-- Carry Condition flag. -->
+ <field name="C" start="29" end="29"/>
+ <!-- Zero Condition flag. -->
+ <field name="Z" start="30" end="30"/>
+ <!-- Negative Condition flag. -->
+ <field name="N" start="31" end="31"/>
+ </flags>
+ <reg name="cpsr" bitsize="32" type="cpsr_flags"/>
+
+</feature>
diff --git a/tools/proxyclient/m1n1/hv/gdbserver/features/aarch64-fpu.xml b/tools/proxyclient/m1n1/hv/gdbserver/features/aarch64-fpu.xml
new file mode 100644
index 0000000..4db5c50
--- /dev/null
+++ b/tools/proxyclient/m1n1/hv/gdbserver/features/aarch64-fpu.xml
@@ -0,0 +1,160 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2009-2022 Free Software Foundation, Inc.
+ Contributed by ARM Ltd.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.aarch64.fpu">
+ <vector id="v2d" type="ieee_double" count="2"/>
+ <vector id="v2u" type="uint64" count="2"/>
+ <vector id="v2i" type="int64" count="2"/>
+ <vector id="v4f" type="ieee_single" count="4"/>
+ <vector id="v4u" type="uint32" count="4"/>
+ <vector id="v4i" type="int32" count="4"/>
+ <vector id="v8f" type="ieee_half" count="8"/>
+ <vector id="v8u" type="uint16" count="8"/>
+ <vector id="v8i" type="int16" count="8"/>
+ <vector id="v8bf16" type="bfloat16" count="8"/>
+ <vector id="v16u" type="uint8" count="16"/>
+ <vector id="v16i" type="int8" count="16"/>
+ <vector id="v1u" type="uint128" count="1"/>
+ <vector id="v1i" type="int128" count="1"/>
+ <union id="vnd">
+ <field name="f" type="v2d"/>
+ <field name="u" type="v2u"/>
+ <field name="s" type="v2i"/>
+ </union>
+ <union id="vns">
+ <field name="f" type="v4f"/>
+ <field name="u" type="v4u"/>
+ <field name="s" type="v4i"/>
+ </union>
+ <union id="vnh">
+ <field name="bf" type="v8bf16"/>
+ <field name="f" type="v8f"/>
+ <field name="u" type="v8u"/>
+ <field name="s" type="v8i"/>
+ </union>
+ <union id="vnb">
+ <field name="u" type="v16u"/>
+ <field name="s" type="v16i"/>
+ </union>
+ <union id="vnq">
+ <field name="u" type="v1u"/>
+ <field name="s" type="v1i"/>
+ </union>
+ <union id="aarch64v">
+ <field name="d" type="vnd"/>
+ <field name="s" type="vns"/>
+ <field name="h" type="vnh"/>
+ <field name="b" type="vnb"/>
+ <field name="q" type="vnq"/>
+ </union>
+ <reg name="v0" bitsize="128" type="aarch64v" regnum="34"/>
+ <reg name="v1" bitsize="128" type="aarch64v" />
+ <reg name="v2" bitsize="128" type="aarch64v" />
+ <reg name="v3" bitsize="128" type="aarch64v" />
+ <reg name="v4" bitsize="128" type="aarch64v" />
+ <reg name="v5" bitsize="128" type="aarch64v" />
+ <reg name="v6" bitsize="128" type="aarch64v" />
+ <reg name="v7" bitsize="128" type="aarch64v" />
+ <reg name="v8" bitsize="128" type="aarch64v" />
+ <reg name="v9" bitsize="128" type="aarch64v" />
+ <reg name="v10" bitsize="128" type="aarch64v"/>
+ <reg name="v11" bitsize="128" type="aarch64v"/>
+ <reg name="v12" bitsize="128" type="aarch64v"/>
+ <reg name="v13" bitsize="128" type="aarch64v"/>
+ <reg name="v14" bitsize="128" type="aarch64v"/>
+ <reg name="v15" bitsize="128" type="aarch64v"/>
+ <reg name="v16" bitsize="128" type="aarch64v"/>
+ <reg name="v17" bitsize="128" type="aarch64v"/>
+ <reg name="v18" bitsize="128" type="aarch64v"/>
+ <reg name="v19" bitsize="128" type="aarch64v"/>
+ <reg name="v20" bitsize="128" type="aarch64v"/>
+ <reg name="v21" bitsize="128" type="aarch64v"/>
+ <reg name="v22" bitsize="128" type="aarch64v"/>
+ <reg name="v23" bitsize="128" type="aarch64v"/>
+ <reg name="v24" bitsize="128" type="aarch64v"/>
+ <reg name="v25" bitsize="128" type="aarch64v"/>
+ <reg name="v26" bitsize="128" type="aarch64v"/>
+ <reg name="v27" bitsize="128" type="aarch64v"/>
+ <reg name="v28" bitsize="128" type="aarch64v"/>
+ <reg name="v29" bitsize="128" type="aarch64v"/>
+ <reg name="v30" bitsize="128" type="aarch64v"/>
+ <reg name="v31" bitsize="128" type="aarch64v"/>
+
+ <flags id="fpsr_flags" size="4">
+ <!-- Invalid Operation cumulative floating-point exception bit. -->
+ <field name="IOC" start="0" end="0"/>
+ <!-- Divide by Zero cumulative floating-point exception bit. -->
+ <field name="DZC" start="1" end="1"/>
+ <!-- Overflow cumulative floating-point exception bit. -->
+ <field name="OFC" start="2" end="2"/>
+ <!-- Underflow cumulative floating-point exception bit. -->
+ <field name="UFC" start="3" end="3"/>
+ <!-- Inexact cumulative floating-point exception bit.. -->
+ <field name="IXC" start="4" end="4"/>
+ <!-- Input Denormal cumulative floating-point exception bit. -->
+ <field name="IDC" start="7" end="7"/>
+ <!-- Cumulative saturation bit, Advanced SIMD only. -->
+ <field name="QC" start="27" end="27"/>
+ <!-- When AArch32 is supported at any Exception level and AArch32
+ floating-point is implemented: Overflow condition flag for AArch32
+ floating-point comparison operations. -->
+ <field name="V" start="28" end="28"/>
+ <!-- When AArch32 is supported at any Exception level and AArch32
+ floating-point is implemented:
+ Carry condition flag for AArch32 floating-point comparison operations.
+ -->
+ <field name="C" start="29" end="29"/>
+ <!-- When AArch32 is supported at any Exception level and AArch32
+ floating-point is implemented:
+ Zero condition flag for AArch32 floating-point comparison operations.
+ -->
+ <field name="Z" start="30" end="30"/>
+ <!-- When AArch32 is supported at any Exception level and AArch32
+ floating-point is implemented:
+ Negative condition flag for AArch32 floating-point comparison
+ operations. -->
+ <field name="N" start="31" end="31"/>
+ </flags>
+ <reg name="fpsr" bitsize="32" type="fpsr_flags"/>
+
+ <flags id="fpcr_flags" size="4">
+ <!-- Flush Inputs to Zero (part of Armv8.7). -->
+ <field name="FIZ" start="0" end="0"/>
+ <!-- Alternate Handling (part of Armv8.7). -->
+ <field name="AH" start="1" end="1"/>
+ <!-- Controls how the output elements other than the lowest element of the
+ vector are determined for Advanced SIMD scalar instructions (part of
+ Armv8.7). -->
+ <field name="NEP" start="2" end="2"/>
+ <!-- Invalid Operation floating-point exception trap enable. -->
+ <field name="IOE" start="8" end="8"/>
+ <!-- Divide by Zero floating-point exception trap enable. -->
+ <field name="DZE" start="9" end="9"/>
+ <!-- Overflow floating-point exception trap enable. -->
+ <field name="OFE" start="10" end="10"/>
+ <!-- Underflow floating-point exception trap enable. -->
+ <field name="UFE" start="11" end="11"/>
+ <!-- Inexact floating-point exception trap enable. -->
+ <field name="IXE" start="12" end="12"/>
+ <!-- Input Denormal floating-point exception trap enable. -->
+ <field name="IDE" start="15" end="15"/>
+ <!-- Flush-to-zero mode control bit on half-precision data-processing
+ instructions. -->
+ <field name="FZ16" start="19" end="19"/>
+ <!-- Rounding Mode control field. -->
+ <field name="RMode" start="22" end="23"/>
+ <!-- Flush-to-zero mode control bit. -->
+ <field name="FZ" start="24" end="24"/>
+ <!-- Default NaN mode control bit. -->
+ <field name="DN" start="25" end="25"/>
+ <!-- Alternative half-precision control bit. -->
+ <field name="AHP" start="26" end="26"/>
+ </flags>
+ <reg name="fpcr" bitsize="32" type="fpcr_flags"/>
+</feature>
diff --git a/tools/proxyclient/m1n1/hv/gdbserver/features/target.xml b/tools/proxyclient/m1n1/hv/gdbserver/features/target.xml
new file mode 100644
index 0000000..ca0454a
--- /dev/null
+++ b/tools/proxyclient/m1n1/hv/gdbserver/features/target.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<!-- SPDX-License-Identifier: MIT -->
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target version="1.0">
+ <architecture>aarch64</architecture>
+ <xi:include href="aarch64-core.xml" />
+ <xi:include href="aarch64-fpu.xml" />
+</target>
diff --git a/tools/proxyclient/m1n1/hv/types.py b/tools/proxyclient/m1n1/hv/types.py
new file mode 100644
index 0000000..0c3142e
--- /dev/null
+++ b/tools/proxyclient/m1n1/hv/types.py
@@ -0,0 +1,60 @@
+# SPDX-License-Identifier: MIT
+from construct import *
+from enum import IntEnum
+
+from ..utils import *
+
+__all__ = [
+ "MMIOTraceFlags", "EvtMMIOTrace", "EvtIRQTrace", "HV_EVENT",
+ "VMProxyHookData", "TraceMode",
+]
+
+class MMIOTraceFlags(Register32):
+ ATTR = 31, 24
+ CPU = 23, 16
+ SH = 15, 14
+ WIDTH = 4, 0
+ WRITE = 5
+ MULTI = 6
+
+EvtMMIOTrace = Struct(
+ "flags" / RegAdapter(MMIOTraceFlags),
+ "reserved" / Int32ul,
+ "pc" / Hex(Int64ul),
+ "addr" / Hex(Int64ul),
+ "data" / Hex(Int64ul),
+)
+
+EvtIRQTrace = Struct(
+ "flags" / Int32ul,
+ "type" / Hex(Int16ul),
+ "num" / Int16ul,
+)
+
+class HV_EVENT(IntEnum):
+ HOOK_VM = 1
+ VTIMER = 2
+ USER_INTERRUPT = 3
+ WDT_BARK = 4
+ CPU_SWITCH = 5
+ VIRTIO = 6
+
+VMProxyHookData = Struct(
+ "flags" / RegAdapter(MMIOTraceFlags),
+ "id" / Int32ul,
+ "addr" / Hex(Int64ul),
+ "data" / Array(8, Hex(Int64ul)),
+)
+
+class TraceMode(IntEnum):
+ '''
+Different types of Tracing '''
+
+ OFF = 0
+ BYPASS = 1
+ ASYNC = 2
+ UNBUF = 3
+ WSYNC = 4
+ SYNC = 5
+ HOOK = 6
+ RESERVED = 7
diff --git a/tools/proxyclient/m1n1/hv/virtio.py b/tools/proxyclient/m1n1/hv/virtio.py
new file mode 100644
index 0000000..790bb26
--- /dev/null
+++ b/tools/proxyclient/m1n1/hv/virtio.py
@@ -0,0 +1,133 @@
+# SPDX-License-Identifier: MIT
+from construct import Struct, Int8ul, Int16ul, Int32sl, Int32ul, Int64ul
+from subprocess import Popen, PIPE
+import pathlib
+import struct
+import os
+import sys
+
+from ..utils import *
+
+VirtioConfig = Struct(
+ "irq" / Int32sl,
+ "devid" / Int32ul,
+ "feats" / Int64ul,
+ "num_qus" / Int32ul,
+ "data" / Int64ul,
+ "data_len" / Int64ul,
+ "verbose" / Int8ul,
+)
+
+class VirtioDescFlags(Register16):
+ WRITE = 1
+ NEXT = 0
+
+VirtioDesc = Struct(
+ "addr" / Int64ul,
+ "len" / Int32ul,
+ "flags" / RegAdapter(VirtioDescFlags),
+ "next" / Int16ul,
+)
+
+VirtioExcInfo = Struct(
+ "devbase" / Int64ul,
+ "qu" / Int16ul,
+ "idx" / Int16ul,
+ "pad" / Int32ul,
+ "descbase" / Int64ul,
+)
+
+class VirtioDev:
+ def __init__(self):
+ self.base, self.hv = None, None # assigned by HV object
+
+ def read_buf(self, desc):
+ return self.hv.iface.readmem(desc.addr, desc.len)
+
+ def read_desc(self, ctx, idx):
+ off = VirtioDesc.sizeof() * idx
+ return self.hv.iface.readstruct(ctx.descbase + off, VirtioDesc)
+
+ @property
+ def config_data(self):
+ return b""
+
+ @property
+ def devid(self):
+ return 0
+
+ @property
+ def num_qus(self):
+ return 1
+
+ @property
+ def feats(self):
+ return 0
+
+class Virtio9PTransport(VirtioDev):
+ def __init__(self, tag="m1n1", root=None):
+ p_stdin, self.fin = os.pipe()
+ self.fout, p_stdout = os.pipe()
+ if root is None:
+ root = str(pathlib.Path(__file__).resolve().parents[3])
+ if type(tag) is str:
+ self.tag = tag.encode("ascii")
+ else:
+ self.tag = tag
+ self.p = Popen([
+ "u9fs",
+ "-a", "none", # no auth
+ "-n", # not a network conn
+ "-u", os.getlogin(), # single user
+ root,
+ ], stdin=p_stdin, stdout=p_stdout, stderr=sys.stderr)
+
+ @property
+ def config_data(self):
+ return struct.pack("=H", len(self.tag)) + self.tag
+
+ @property
+ def devid(self):
+ return 9
+
+ @property
+ def num_qus(self):
+ return 1
+
+ @property
+ def feats(self):
+ return 1
+
+ def call(self, req):
+ os.write(self.fin, req)
+ resp = os.read(self.fout, 4)
+ length = int.from_bytes(resp, byteorder="little")
+ resp += os.read(self.fout, length - 4)
+ return resp
+
+ def handle_exc(self, ctx):
+ head = self.read_desc(ctx, ctx.idx)
+ assert not head.flags.WRITE
+
+ req = bytearray()
+
+ while not head.flags.WRITE:
+ req += self.read_buf(head)
+
+ if not head.flags.NEXT:
+ break
+ head = self.read_desc(ctx, head.next)
+
+ resp = self.call(bytes(req))
+ resplen = len(resp)
+
+ while len(resp):
+ self.hv.iface.writemem(head.addr, resp[:head.len])
+ resp = resp[head.len:]
+ if not head.flags.NEXT:
+ break
+ head = self.read_desc(ctx, head.next)
+
+ self.hv.p.virtio_put_buffer(ctx.devbase, ctx.qu, ctx.idx, resplen)
+
+ return True
diff --git a/tools/proxyclient/m1n1/hv/virtutils.py b/tools/proxyclient/m1n1/hv/virtutils.py
new file mode 100644
index 0000000..934abc0
--- /dev/null
+++ b/tools/proxyclient/m1n1/hv/virtutils.py
@@ -0,0 +1,43 @@
+# SPDX-License-Identifier: MIT
+from m1n1.utils import align_up
+
+def collect_aic_irqs_in_use(adt):
+ used = set()
+ aic_phandle = getattr(adt["/arm-io/aic"], "AAPL,phandle")
+ for node in adt.walk_tree():
+ if not hasattr(node, "interrupt_parent") or \
+ node.interrupt_parent != aic_phandle:
+ continue
+ for no in node.interrupts:
+ used.add(no)
+ return used
+
+def usable_aic_irq_range(adt):
+ # These are too optimistic but since we allocate
+ # from the bottom of the range it doesn't matter much.
+ return {
+ "aic,1": range(0, 0x400),
+ "aic,2": range(0, 0x1000),
+ }.get(adt["/arm-io/aic"].compatible[0])
+
+def alloc_aic_irq(adt):
+ used = collect_aic_irqs_in_use(adt)
+ for no in usable_aic_irq_range(adt):
+ if no not in used:
+ return no
+ return None
+
+def usable_mmio_range(adt):
+ arm_io_range = adt["arm-io"].ranges[0]
+ return range(arm_io_range.parent_addr, arm_io_range.parent_addr + arm_io_range.size)
+
+def alloc_mmio_base(adt, size, alignment=0x4000):
+ span = usable_mmio_range(adt)
+ la = adt.build_addr_lookup()
+ for zone, devs in la.populate(span):
+ if len(devs) != 0:
+ continue
+ base = align_up(zone.start, alignment)
+ if zone.stop > base + size:
+ return base
+ return None
diff --git a/tools/proxyclient/m1n1/hw/admac.py b/tools/proxyclient/m1n1/hw/admac.py
new file mode 100644
index 0000000..3fb8032
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/admac.py
@@ -0,0 +1,416 @@
+# SPDX-License-Identifier: MIT
+import sys, time
+from enum import IntEnum
+from ..utils import *
+
+__all__ = ["ADMACRegs", "ADMAC", "E_BUSWIDTH", "E_FRAME"]
+
+
+class R_RING(Register32):
+ # overflow/underflow counter
+ OF_UF = 31, 16
+
+ # goes through 0, 1, 2, 3 as the pieces of a report/descriptor
+ # are being read/written through REPORT_READ/DESC_WRITE
+ READOUT_PROGRESS = 13, 12
+
+ # when READ_SLOT==WRITE_SLOT one of the two is set
+ EMPTY = 8
+ FULL = 9
+
+ ERR = 10
+
+ # next slot to read
+ READ_SLOT = 5, 4
+
+ # next slot to be written to
+ WRITE_SLOT = 1, 0
+
+class R_CHAN_STATUS(Register32):
+ # only raised if the descriptor had NOTIFY set
+ DESC_DONE = 0
+
+ DESC_RING_EMPTY = 4
+ REPORT_RING_FULL = 5
+
+ # cleared by writing ERR=1 either to TX_DESC_RING or TX_REPORT_RING
+ RING_ERR = 6
+
+ UNK0 = 1
+ UNK3 = 8
+ UNK4 = 9
+ UNK5 = 10
+
+class R_CHAN_CONTROL(Register32):
+ RESET_RINGS = 0
+ CLEAR_OF_UF_COUNTERS = 1
+ UNK1 = 3
+
+class E_BUSWIDTH(IntEnum):
+ W_8BIT = 0
+ W_16BIT = 1
+ W_32BIT = 2
+
+class E_FRAME(IntEnum):
+ F_1_WORD = 0
+ F_2_WORDS = 1
+ F_4_WORDS = 2
+
+class R_BUSWIDTH(Register32):
+ WORD = 2, 0, E_BUSWIDTH
+ FRAME = 6, 4, E_FRAME
+
+class R_CARVEOUT(Register32):
+ SIZE = 31, 16
+ BASE = 15, 0
+
+class ADMACRegs(RegMap):
+ TX_EN = 0x0, Register32 # one bit per channel
+ TX_EN_CLR = 0x4, Register32
+
+ RX_EN = 0x8, Register32
+ RX_EN_CLR = 0xc, Register32
+
+ UNK_CTL = 0x10, Register32
+
+ # each of the four registers represents an internal interrupt line,
+ # bits represent DMA channels which at the moment raise that particular line
+ #
+ # the irq-destination-index prop in ADT maybe selects the line which
+ # is actually wired out
+ #
+ TX_INTSTATE = irange(0x30, 4, 0x4), Register32
+ RX_INTSTATE = irange(0x40, 4, 0x4), Register32
+
+ # a 24 MHz always-running counter, top bit is always set
+ COUNTER = 0x70, Register64
+
+ TX_SRAM_SIZE = 0x94, Register32
+ RX_SRAM_SIZE = 0x98, Register32
+
+ # -- per-channel registers --
+
+ CHAN_CTL = (irange(0x8000, 32, 0x200)), R_CHAN_CONTROL
+
+ CHAN_BUSWIDTH = (irange(0x8040, 32, 0x200)), R_BUSWIDTH
+ CHAN_SRAM_CARVEOUT = (irange(0x8050, 32, 0x200)), R_CARVEOUT
+ CHAN_BURSTSIZE = (irange(0x8054, 32, 0x200)), Register32
+
+ CHAN_RESIDUE = irange(0x8064, 32, 0x200), Register32
+
+ CHAN_DESC_RING = irange(0x8070, 32, 0x200), R_RING
+ CHAN_REPORT_RING = irange(0x8074, 32, 0x200), R_RING
+
+ TX_DESC_WRITE = irange(0x10000, 16, 4), Register32
+ TX_REPORT_READ = irange(0x10100, 16, 4), Register32
+
+ RX_DESC_WRITE = irange(0x14000, 16, 4), Register32
+ RX_REPORT_READ = irange(0x14100, 16, 4), Register32
+
+ # per-channel, per-internal-line
+ CHAN_STATUS = (irange(0x8010, 32, 0x200), irange(0x0, 4, 0x4)), R_CHAN_STATUS
+ CHAN_INTMASK = (irange(0x8020, 32, 0x200), irange(0x0, 4, 0x4)), R_CHAN_STATUS
+
+
+class ADMACDescriptorFlags(Register32):
+ # whether to raise DESC_DONE in CHAN_STATUS
+ NOTIFY = 16
+
+ # whether to repeat this descriptor ad infinitum
+ #
+ # once a descriptor with this flag is loaded, any descriptors loaded
+ # afterwards are also repeated and nothing short of full power domain reset
+ # seems to revoke that behaviour. this looks like a HW bug.
+ REPEAT = 17
+
+ # arbitrary ID propagated into reports
+ DESC_ID = 7, 0
+
+class ADMACDescriptor(Reloadable):
+ def __init__(self, addr, length, **flags):
+ self.addr = addr
+ self.length = length
+ self.flags = ADMACDescriptorFlags(**flags)
+
+ def __repr__(self):
+ return f"<descriptor: addr=0x{self.addr:x} len=0x{self.length:x} flags={self.flags}>"
+
+ def ser(self):
+ return [
+ self.addr & (1<<32)-1,
+ self.addr>>32 & (1<<32)-1,
+ self.length & (1<<32)-1,
+ int(self.flags)
+ ]
+
+ @classmethod
+ def deser(self, seq):
+ if not len(seq) == 4:
+ raise ValueError
+ return ADMACDescriptor(
+ seq[0] | seq[1] << 32, # addr
+ seq[2], # length (in bytes)
+ **ADMACDescriptorFlags(seq[3]).fields
+ )
+
+
+class ADMACReportFlags(Register32):
+ UNK1 = 24
+ UNK2 = 25
+ UNK4 = 26 # memory access fault?
+ UNK3 = 27
+ DESC_ID = 7, 0
+
+class ADMACReport(Reloadable):
+ def __init__(self, countval, unk1, flags):
+ self.countval, self.unk1, self.flags = countval, unk1, ADMACReportFlags(flags)
+
+ def __repr__(self):
+ return f"<report: countval=0x{self.countval:x} unk1=0x{self.unk1:x} flags={self.flags}>"
+
+ def ser(self):
+ return [
+ self.countval & (1<<32)-1,
+ self.countval>>32 & (1<<32)-1,
+ self.unk1 & (1<<32)-1,
+ int(self.flags)
+ ]
+
+ @classmethod
+ def deser(self, seq):
+ if not len(seq) == 4:
+ raise ValueError
+ return ADMACReport(
+ seq[0] | seq[1] << 32, # countval
+ seq[2], # unk1
+ seq[3] # flags
+ )
+
+
+class ADMACChannel(Reloadable):
+ def __init__(self, parent, channo):
+ self.p = parent
+ self.iface = parent.p.iface
+ self.dart = parent.dart
+ self.regs = parent.regs
+ self.tx = (channo % 2) == 0
+ self.rx = not self.tx
+ self.ch = channo
+
+ self._desc_id = 0
+ self._submitted = {}
+ self._last_report = None
+ self._est_byte_rate = None
+
+ def reset(self):
+ self.regs.CHAN_CTL[self.ch].set(RESET_RINGS=1, CLEAR_OF_UF_COUNTERS=1)
+ self.regs.CHAN_CTL[self.ch].set(RESET_RINGS=0, CLEAR_OF_UF_COUNTERS=0)
+
+ self.burstsize = 0xc0_0060
+ self.buswidth = E_BUSWIDTH.W_32BIT
+ self.framesize = E_FRAME.F_1_WORD
+
+ def enable(self):
+ self.regs.CHAN_INTMASK[self.ch, 0].reg = \
+ R_CHAN_STATUS(DESC_DONE=1, DESC_RING_EMPTY=1,
+ REPORT_RING_FULL=1, RING_ERR=1)
+
+ if self.tx:
+ self.regs.TX_EN.val = 1 << (self.ch//2)
+ else:
+ self.regs.RX_EN.val = 1 << (self.ch//2)
+
+ def disable(self):
+ if self.tx:
+ self.regs.TX_EN_CLR.val = 1 << (self.ch//2)
+ else:
+ self.regs.RX_EN_CLR.val = 1 << (self.ch//2)
+
+ @property
+ def buswidth(self):
+ self.regs.CHAN_BUSWIDTH[self.ch].reg.WORD
+
+ @buswidth.setter
+ def buswidth(self, wordsize):
+ return self.regs.CHAN_BUSWIDTH[self.ch].set(WORD=wordsize)
+
+ @property
+ def framesize(self):
+ self.regs.CHAN_BUSWIDTH[self.ch].reg.FRAME
+
+ @framesize.setter
+ def framesize(self, framesize):
+ return self.regs.CHAN_BUSWIDTH[self.ch].set(FRAME=framesize)
+
+ @property
+ def burstsize(self):
+ return self.regs.CHAN_BURSTSIZE[self.ch].val
+
+ @burstsize.setter
+ def burstsize(self, size):
+ self.regs.CHAN_BURSTSIZE[self.ch].val = size
+
+ @property
+ def sram_carveout(self):
+ reg = self.regs.CHAN_SRAM_CARVEOUT[self.ch].reg
+ return (reg.BASE, reg.SIZE)
+
+ @sram_carveout.setter
+ def sram_carveout(self, carveout):
+ base, size = carveout
+ self.regs.CHAN_SRAM_CARVEOUT[self.ch].reg = \
+ R_CARVEOUT(BASE=base, SIZE=size)
+
+ @property
+ def DESC_WRITE(self):
+ if self.tx:
+ return self.regs.TX_DESC_WRITE[self.ch//2]
+ else:
+ return self.regs.RX_DESC_WRITE[self.ch//2]
+
+ @property
+ def REPORT_READ(self):
+ if self.tx:
+ return self.regs.TX_REPORT_READ[self.ch//2]
+ else:
+ return self.regs.RX_REPORT_READ[self.ch//2]
+
+ def can_submit(self):
+ return not self.regs.CHAN_DESC_RING[self.ch].reg.FULL
+
+ def submit_desc(self, desc):
+ if self.regs.CHAN_DESC_RING[self.ch].reg.FULL:
+ raise Exception(f"ch{self.ch} descriptor ring full")
+
+ if self.p.debug:
+ print(f"admac: submitting (ch{self.ch}): {desc}", file=sys.stderr)
+
+ for piece in desc.ser():
+ self.DESC_WRITE.val = piece
+
+ self._submitted[desc.flags.DESC_ID] = desc
+
+ def submit(self, data=None, buflen=None, **kwargs):
+ if self.tx:
+ assert data is not None
+ buflen = len(data)
+ else:
+ assert buflen is not None
+
+ iova = self.p.get_buffer(buflen)
+ if self.tx:
+ self.p.iowrite(iova, data)
+ self.submit_desc(ADMACDescriptor(
+ iova, buflen, DESC_ID=self._desc_id, NOTIFY=1, **kwargs
+ ))
+ self._desc_id = (self._desc_id + 1) % 256
+
+ def read_reports(self):
+ data = bytearray()
+
+ while not self.regs.CHAN_REPORT_RING[self.ch].reg.EMPTY:
+ pieces = []
+ for _ in range(4):
+ pieces.append(self.REPORT_READ.val)
+ report = ADMACReport.deser(pieces)
+
+ if report.flags.DESC_ID in self._submitted:
+ desc = self._submitted[report.flags.DESC_ID]
+ else:
+ print(f"admac: stray report (ch{self.ch}): {report}", file=sys.stderr)
+ desc = None
+
+ if self.rx and desc and self.p.dart:
+ data.extend(self.p.ioread(desc.addr, desc.length))
+
+ if self.p.debug:
+ if self._last_report and desc:
+ countval_delta = report.countval - self._last_report.countval
+ est_rate = 24e6*desc.length/countval_delta/4
+ est = f"(estimated rate: {est_rate:.2f} dwords/s)"
+ else:
+ est = ""
+
+ print(f"admac: picked up (ch{self.ch}): {report} {est}", file=sys.stderr)
+
+ self._last_report = report
+
+ return data if self.rx else None
+
+ @property
+ def status(self):
+ return self.regs.CHAN_STATUS[self.ch, 0].reg
+
+ def poll(self, wait=True):
+ while not (self.status.DESC_DONE or self.status.RING_ERR):
+ time.sleep(0.001)
+
+ if not wait:
+ break
+
+ self.regs.CHAN_STATUS[self.ch,0].reg = R_CHAN_STATUS(DESC_DONE=1)
+
+ if self.status.RING_ERR:
+ if self.p.debug:
+ print(f"STATUS={self.regs.CHAN_STATUS[self.ch,1].reg} " + \
+ f"REPORT_RING={self.regs.CHAN_DESC_RING[self.ch]} " + \
+ f"DESC_RING={self.regs.CHAN_REPORT_RING[self.ch]}",
+ file=sys.stderr)
+ self.regs.CHAN_DESC_RING[self.ch].set(ERR=1)
+ self.regs.CHAN_REPORT_RING[self.ch].set(ERR=1)
+
+ return self.read_reports()
+
+
+class ADMAC(Reloadable):
+ def __init__(self, u, devpath, dart=None, dart_stream=2,
+ reserved_size=4*1024*1024, debug=False):
+ self.u = u
+ self.p = u.proxy
+ self.debug = debug
+
+ if type(devpath) is str:
+ adt_node = u.adt[devpath]
+ # ADT's #dma-channels counts pairs of RX/TX channel, so multiply by two
+ self.nchans = adt_node._properties["#dma-channels"] * 2
+ self.base, _ = adt_node.get_reg(0)
+ else:
+ self.base = devpath
+ self.nchans = 26
+
+ self.regs = ADMACRegs(u, self.base)
+ self.dart, self.dart_stream = dart, dart_stream
+
+ if dart is not None:
+ resmem_phys = u.heap.memalign(128*1024, reserved_size)
+ self.resmem_iova = self.dart.iomap(dart_stream, resmem_phys, reserved_size)
+ self.resmem_size = reserved_size
+ self.resmem_pos = 0
+ self.dart.invalidate_streams(1 << dart_stream)
+
+ self.chans = [ADMACChannel(self, no) for no in range(self.nchans)]
+
+ def ioread(self, base, size):
+ assert self.dart is not None
+ return self.dart.ioread(self.dart_stream, base, size)
+
+ def iowrite(self, base, data):
+ assert self.dart is not None
+ self.dart.iowrite(self.dart_stream, base, data)
+
+ def fill_canary(self):
+ ranges = self.dart.iotranslate(self.dart_stream,
+ self.resmem_iova, self.resmem_size)
+ assert len(ranges) == 1
+ start, size = ranges[0]
+ self.p.memset8(start, 0xba, size)
+
+ def get_buffer(self, size):
+ assert size < self.resmem_size
+
+ if self.resmem_pos + size > self.resmem_size:
+ self.resmem_pos = 0
+
+ bufptr = self.resmem_iova + self.resmem_pos
+ self.resmem_pos += size
+ return bufptr
diff --git a/tools/proxyclient/m1n1/hw/aes.py b/tools/proxyclient/m1n1/hw/aes.py
new file mode 100644
index 0000000..7e09335
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/aes.py
@@ -0,0 +1,110 @@
+# SPDX-License-Identifier: MIT
+from ..utils import *
+from enum import IntEnum
+from .dart import DART, DARTRegs
+import struct
+from enum import IntEnum
+
+
+class AES_OPCODE(IntEnum):
+ # 0 triggers an invalid command interrupt
+ SET_KEY = 1
+ SET_IV = 2
+ # 0x03 seems to take three additional argument, function unknown
+ # 0x04 seems to take one additional argument, function unknown
+ CRYPT = 5
+ GET_IV = 6
+ # 0x07 takes one additional argument, function unknown
+ BARRIER = 8 # can be used to trigger an IRQ but possibly also does more
+ # > 8 trigger an invalid command interrupt
+
+
+class AES_SET_KEY_LEN(IntEnum):
+ AES128 = 0
+ AES192 = 1
+ AES256 = 2
+
+
+class AES_SET_KEY_BLOCK_MODE(IntEnum):
+ ECB = 0
+ CBC = 1
+ CTR = 2
+
+
+class AESCommandBase(Register32):
+ OPCODE = 31, 28, AES_OPCODE
+
+
+class AESHwKey(IntEnum):
+ SOFTWARE = 0
+ UID = 1 # unique key for each chip
+ GID0 = 2 # (probably) globally unique key within a chip family
+ GID1 = 3 # globally unique key within a chip family
+ # 4-7 are probably empty / reserved for future use
+
+
+class AESSetKeyCommand(AESCommandBase):
+ OPCODE = 31, 28, Constant(AES_OPCODE.SET_KEY)
+ SLOT = 27, 27
+ KEY_SELECT = 26, 24
+ KEYLEN = 23, 22, AES_SET_KEY_LEN
+ # setting bit 21 breaks the engine and sets two bits in the IRQ status
+ ENCRYPT = 20, 20
+ KEYGEN = 19, 18
+ BLOCK_MODE = 17, 16, AES_SET_KEY_BLOCK_MODE
+ # 15, 0 doesn't seem to have any effect
+
+
+class AESCryptCommand(AESCommandBase):
+ OPCODE = 31, 28, Constant(AES_OPCODE.CRYPT)
+ KEY_SLOT = 27, 27
+ IV_SLOT = 26, 25
+ LEN = 24, 0
+
+
+class AESBarrierCommand(AESCommandBase):
+ OPCODE = 31, 28, Constant(AES_OPCODE.BARRIER)
+ IRQ = 27, 27
+
+
+class AESGetIVCommand(AESCommandBase):
+ OPCODE = 31, 28, Constant(AES_OPCODE.GET_IV)
+
+
+class AESSetIVCommand(AESCommandBase):
+ OPCODE = 31, 28, Constant(AES_OPCODE.SET_IV)
+ SLOT = 27, 26
+
+
+class AESIrqReg(Register32):
+ KEY1_EMPTY = 17, 17
+ KEY1_INVALID = 13, 13
+ KEY0_EMPTY = 11, 11
+ KEY0_INVALID = 7, 7
+ FLAG = 5, 5
+ UNKNOWN_COMMAND = 2, 2
+ FIFO_OVERFLOW = 1, 1
+
+
+class AESControlReg(Register32):
+ START = 0, 0
+ STOP = 1, 1
+ CLEAR_FIFO = 2, 2
+ # TOOD: not convinced about RESET anymore, I remember this un-broke the engine once but I can't reproduce that anymore
+ RESET = 3, 3
+
+
+class AESFifoStatusReg(Register32):
+ FIFO_WRITE_PTR = 31, 24
+ FIFO_READ_PTR = 23, 16
+ FIFO_LEVEL = 15, 8
+ FIFO_FULL = 2, 2
+ FIFO_EMPTY = 1, 1
+
+
+class AESRegs(RegMap):
+ R_CONTROL = 0x08, AESControlReg
+ R_IRQ_STATUS = 0x18, AESIrqReg
+ R_IRQ_ENABLE = 0x1C, AESIrqReg
+ R_FIFO_STATUS = 0x24, AESFifoStatusReg
+ R_CMD_FIFO = 0x200, Register32
diff --git a/tools/proxyclient/m1n1/hw/agx.py b/tools/proxyclient/m1n1/hw/agx.py
new file mode 100644
index 0000000..e5c2daa
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/agx.py
@@ -0,0 +1,111 @@
+# SPDX-License-Identifier: MIT
+from ..utils import *
+from enum import IntEnum
+
+__all__ = ["SGXRegs", "SGXInfoRegs", "agx_decode_unit", "R_FAULT_INFO"]
+
+class FAULT_REASON(IntEnum):
+ INVALID = 0
+ AF_FAULT = 1
+ WRITE_ONLY = 2
+ READ_ONLY = 3
+ NO_ACCESS = 4
+ UNK = 5
+
+class R_FAULT_INFO(Register64):
+ ADDR = 63, 24
+ WRITE = 23
+ CONTEXT = 22, 17
+ UNIT = 16, 9
+ UNK_8 = 8
+ REASON = 3, 1, FAULT_REASON
+ FAULTED = 0
+
+class SGXRegs(RegMap):
+ FAULT_INFO = 0x17030, R_FAULT_INFO
+
+class SGXInfoRegs(RegMap):
+ CORE_MASK_0 = 0x1500, Register32,
+ CORE_MASK_1 = 0x1514, Register32,
+
+ ID_00 = 0x4000, Register32,
+ ID_04 = 0x4004, Register32,
+ ID_08 = 0x4008, Register32,
+ ID_0c = 0x400c, Register32,
+ ID_10 = 0x4010, Register32,
+ ID_14 = 0x4014, Register32,
+ ID_18 = 0x4018, Register32,
+ ID_1c = 0x401c, Register32,
+
+ ID_8024 = 0x8024, Register32,
+
+class UNIT_00(IntEnum):
+ DCMPn = 0x00
+ UL1Cn = 0x01
+ CMPn = 0x02
+ GSL1_n = 0x03
+ IAPn = 0x04
+ VCEn = 0x05
+ TEn = 0x06
+ RASn = 0x07
+ VDMn = 0x08
+ PPPn = 0x09
+ IPFn = 0x0a
+ IPF_CPFn = 0x0b
+ VFn = 0x0c
+ VF_CPFn = 0x0d
+ ZLSn = 0x0e
+
+class UNIT_A0(IntEnum):
+ dPM = 0xa1
+ dCDM_KS0 = 0xa2
+ dCDM_KS1 = 0xa3
+ dCDM_KS2 = 0xa4
+ dIPP = 0xa5
+ dIPP_CS = 0xa6
+ dVDM_CSD = 0xa7
+ dVDM_SSD = 0xa8
+ dVDM_ILF = 0xa9
+ dVDM_ILD = 0xaa
+ dRDE0 = 0xab
+ dRDE1 = 0xac
+ FC = 0xad
+ GSL2 = 0xae
+
+ GL2CC_META0 = 0xb0
+ GL2CC_META1 = 0xb1
+ GL2CC_META2 = 0xb2
+ GL2CC_META3 = 0xb3
+ GL2CC_META4 = 0xb4
+ GL2CC_META5 = 0xb5
+ GL2CC_META6 = 0xb6
+ GL2CC_META7 = 0xb7
+ GL2CC_MB = 0xb8
+
+class UNIT_E0(IntEnum):
+ gPM_SPn = 0xe0
+ gVDM_CSD_SPn = 0xe1
+ gVDM_SSD_SPn = 0xe2
+ gVDM_ILF_SPn = 0xe3
+ gVDM_TFP_SPn = 0xe4
+ gVDM_MMB_SPn = 0xe5
+ gCDM_CS_SPn_KS0 = 0xe6
+ gCDM_CS_SPn_KS1 = 0xe7
+ gCDM_CS_SPn_KS2 = 0xe8
+ gCDM_SPn_KS0 = 0xe9
+ gCDM_SPn_KS1 = 0xea
+ gCDM_SPn_KS2 = 0xeb
+ gIPP_SPn = 0xec
+ gIPP_CS_SPn = 0xed
+ gRDE0_SPn = 0xee
+ gRDE1_SPn = 0xef
+
+def agx_decode_unit(v):
+ if v < 0xa0:
+ group = v >> 4
+ return UNIT_00(v & 0x0f).name.replace("n", str(group))
+ elif v < 0xe0:
+ return UNIT_A0(v).name
+ else:
+ group = (v >> 4) & 1
+ return UNIT_E0(v & 0xef).name.replace("n", str(group))
diff --git a/tools/proxyclient/m1n1/hw/asc.py b/tools/proxyclient/m1n1/hw/asc.py
new file mode 100644
index 0000000..f0923f8
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/asc.py
@@ -0,0 +1,121 @@
+# SPDX-License-Identifier: MIT
+from ..utils import *
+import time
+
+class R_MBOX_CTRL(Register32):
+ FIFOCNT = 23, 20
+ OVERFLOW = 18
+ EMPTY = 17
+ FULL = 16
+ RPTR = 15, 12
+ WPTR = 11, 8
+ ENABLE = 0
+
+class R_CPU_CONTROL(Register32):
+ RUN = 4
+
+class R_CPU_STATUS(Register32):
+ IDLE = 5
+ FIQ_NOT_PEND = 3 # guess
+ IRQ_NOT_PEND = 2 # guess
+ STOPPED = 1
+ RUNNING = 0
+
+class R_INBOX1(Register64):
+ EP = 7, 0
+
+class R_OUTBOX1(Register64):
+ OUTCNT = 56, 52
+ INCNT = 51, 48
+ OUTPTR = 47, 44
+ INPTR = 43, 40
+ EP = 7, 0
+
+class ASCRegs(RegMap):
+ CPU_CONTROL = 0x0044, R_CPU_CONTROL
+ CPU_STATUS = 0x0048, R_CPU_STATUS
+
+ INBOX_CTRL = 0x8110, R_MBOX_CTRL
+ OUTBOX_CTRL = 0x8114, R_MBOX_CTRL
+ INBOX0 = 0x8800, Register64
+ INBOX1 = 0x8808, R_INBOX1
+ OUTBOX0 = 0x8830, Register64
+ OUTBOX1 = 0x8838, R_OUTBOX1
+
+class ASC:
+ def __init__(self, u, asc_base):
+ self.u = u
+ self.p = u.proxy
+ self.iface = u.iface
+ self.asc = ASCRegs(u, asc_base)
+ self.verbose = 0
+ self.epmap = {}
+
+ def recv(self):
+ if self.asc.OUTBOX_CTRL.reg.EMPTY:
+ return None, None
+
+ msg0 = self.asc.OUTBOX0.val
+ msg1 = R_INBOX1(self.asc.OUTBOX1.val)
+ if self.verbose >= 3:
+ print(f"< {msg1.EP:02x}:{msg0:#x}")
+ return msg0, msg1
+
+ def send(self, msg0, msg1):
+ self.asc.INBOX0.val = msg0
+ self.asc.INBOX1.val = msg1
+
+ if self.verbose >= 3:
+ if isinstance(msg0, Register):
+ print(f"> {msg1.EP:02x}:{msg0}")
+ else:
+ print(f"> {msg1.EP:02x}:{msg0:#x}")
+
+ while self.asc.INBOX_CTRL.reg.FULL:
+ pass
+
+ def is_running(self):
+ return not self.asc.CPU_STATUS.reg.STOPPED
+
+ def boot(self):
+ self.asc.CPU_CONTROL.set(RUN=1)
+
+ def shutdown(self):
+ self.asc.CPU_CONTROL.set(RUN=0)
+
+ def add_ep(self, idx, ep):
+ self.epmap[idx] = ep
+ setattr(self, ep.SHORT, ep)
+
+ def has_messages(self):
+ return not self.asc.OUTBOX_CTRL.reg.EMPTY
+
+ def work_pending(self):
+ while self.has_messages():
+ self.work()
+
+ def work(self):
+ if self.asc.OUTBOX_CTRL.reg.EMPTY:
+ return True
+
+ msg0, msg1 = self.recv()
+
+ handled = False
+
+ ep = self.epmap.get(msg1.EP, None)
+ if ep:
+ handled = ep.handle_msg(msg0, msg1)
+
+ if not handled:
+ print(f"unknown message: {msg0:#16x} / {msg1}")
+
+ return handled
+
+ def work_forever(self):
+ while self.work():
+ pass
+
+ def work_for(self, timeout):
+ deadline = time.time() + timeout
+ while time.time() < deadline:
+ self.work()
diff --git a/tools/proxyclient/m1n1/hw/atc.py b/tools/proxyclient/m1n1/hw/atc.py
new file mode 100644
index 0000000..7ae92c0
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/atc.py
@@ -0,0 +1,455 @@
+# SPDX-License-Identifier: MIT
+
+from enum import IntEnum
+from m1n1.utils import *
+
+
+class R_USB2PHY_USBCTL(Register32):
+ MODE_HOST = 1
+ MODE_ISOLATION = 2
+
+
+class R_USB2PHY_CTL(Register32):
+ RESET = 0
+ PORT_RESET = 1
+ APB_RESETN = 2
+ SIDDQ = 3
+
+
+class R_USB2PHY_SIG(Register32):
+ VBUSDET_FORCE_VAL = 0
+ VBUSDET_FORCE_EN = 1
+ VBUSVLDEXT_FORCE_VAL = 2
+ VBUSVLDEXT_FORCE_EN = 3
+ MODE_HOST = 19, 12
+
+
+class R_USB2PHY_MISCTUNE(Register32):
+ APBCLK_GATE_OFF = 29
+ REFCLK_GATE_OFF = 30
+
+
+class Usb2PhyRegs(RegMap):
+ USB2PHY_USBCTL = 0x00, R_USB2PHY_USBCTL
+ USB2PHY_CTL = 0x04, R_USB2PHY_CTL
+ USB2PHY_SIG = 0x08, R_USB2PHY_SIG
+ USB2PHY_MISCTUNE = 0x1C, R_USB2PHY_MISCTUNE
+
+
+class R_AUSPLL_DCO_EFUSE_SPARE(Register32):
+ RODCO_ENCAP_EFUSE = 10, 9
+ RODCO_BIAS_ADJUST_EFUSE = 14, 12
+
+
+class R_AUSPLL_FRACN_CAN(Register32):
+ DLL_START_CAPCODE = 18, 17
+
+
+class R_AUSPLL_FSM_CTRL(Register32):
+ APBREQ_OVSEL = 21, 13
+
+
+class R_AUSPLL_CMD_OVERRIDE(Register32):
+ APB_OVERRIDE = 28
+
+
+class R_AUSPLL_CLKOUT_DTC_VREG(Register32):
+ DTC_VREG_ADJUST = 16, 14
+
+
+class R_AUS_COMMON_SHIM_BLK_VREG(Register32):
+ VREG_TRIM = 6, 2
+
+
+class R_CIO3PLL_CLK_CTRL(Register32):
+ PCLK_EN = 1
+ REFCLK_EN = 5
+
+
+class R_CIO3PLL_DCO_NCTRL(Register32):
+ DCO_COARSEBIN_EFUSE0 = 6, 0
+ DCO_COARSEBIN_EFUSE1 = 23, 17
+
+
+class R_CIO3PLL_FRACN_CAN(Register32):
+ DLL_CAL_START_CAPCODE = 18, 17
+
+
+class R_CIO3PLL_DTC_VREG(Register32):
+ DTC_VREG_ADJUST = 16, 14
+
+
+class E_ACIOPHY_CROSSBAR_PROTOCOL(IntEnum):
+ USB4 = 0
+ USB3 = 5
+ USB3_DP = 8
+ DP = 10
+
+
+class R_ACIOPHY_CROSSBAR(Register32):
+ PROTOCOL_SWAPPED = 0
+ PROTOCOL = 4, 1, E_ACIOPHY_CROSSBAR_PROTOCOL
+ DPMODE = 17, 5
+
+
+class E_ACIOPHY_LANE_MODE(IntEnum):
+ USB4 = 0
+ USB3 = 1
+ DP = 2
+ OFF = 3
+
+
+class R_ACIOPHY_LANE_MODE(Register32):
+ RX0 = 2, 0, E_ACIOPHY_LANE_MODE
+ TX0 = 5, 3, E_ACIOPHY_LANE_MODE
+ RX1 = 8, 6, E_ACIOPHY_LANE_MODE
+ TX1 = 11, 9, E_ACIOPHY_LANE_MODE
+
+
+class R_ATCPHY_POWER(Register32):
+ SLEEP_SMALL = 0
+ SLEEP_BIG = 1
+ CLAMP_EN = 2
+ APB_RESET_N = 3
+ PHY_RESET_N = 4
+
+
+class R_ATCPHY_MISC(Register32):
+ RESET_N = 0
+ LANE_SWAP = 2
+
+
+class R_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG0(Register32):
+ PMA_TXA_BYTECLK_RESET_SYNC_EN_OV = 2
+ PMA_TXA_BYTECLK_RESET_SYNC_EN = 3
+ PMA_TXA_BYTECLK_RESET_SYNC_CLR_OV = 4
+ PMA_TXA_BYTECLK_RESET_SYNC_CLR = 5
+ PMA_TXA_BYTECLK_RESET_SYNC_SEL_OV = 6
+
+
+class R_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1(Register32):
+ PMA_TXA_DIV2_EN_OV = 8
+ PMA_TXA_DIV2_EN = 9
+ PMA_TXA_DIV2_RESET_OV = 10
+ PMA_TXA_DIV2_RESET = 11
+ PMA_TXA_CLK_EN_OV = 22
+ PMA_TXA_CLK_EN = 23
+
+
+class R_AUSPMA_TX_SHM_TXA_IMP_REG0(Register32):
+ PMA_TXA_CAL_CTRL_OV = 0
+ PMA_TXA_CAL_CTRL = 18, 1
+ PMA_TXA_CAL_CTRL_BASE_OV = 19
+ PMA_TXA_CAL_CTRL_BASE = 23, 20
+ PMA_TXA_HIZ_OV = 29
+ PMA_TXA_HIZ = 30
+
+
+class R_AUSPMA_TX_SHM_TXA_IMP_REG2(Register32):
+ PMA_TXA_MARGIN_OV = 0
+ PMA_TXA_MARGIN = 18, 1
+ PMA_TXA_MARGIN_2R_OV = 19
+ PMA_TXA_MARGIN_2R = 20
+
+
+class R_AUSPMA_TX_SHM_TXA_IMP_REG3(Register32):
+ PMA_TXA_MARGIN_POST_OV = 0
+ PMA_TXA_MARGIN_POST = 10, 1
+ PMA_TXA_MARGIN_POST_2R_OV = 11
+ PMA_TXA_MARGIN_POST_2R = 12
+ PMA_TXA_MARGIN_POST_4R_OV = 13
+ PMA_TXA_MARGIN_POST_4R = 14
+ PMA_TXA_MARGIN_PRE_OV = 15
+ PMA_TXA_MARGIN_PRE = 21, 16
+ PMA_TXA_MARGIN_PRE_2R_OV = 22
+ PMA_TXA_MARGIN_PRE_2R = 23
+ PMA_TXA_MARGIN_PRE_4R_OV = 24
+ PMA_TXA_MARGIN_PRE_4R = 25
+
+
+class R_AUSPMA_TX_SHM_TXA_LDOCLK(Register32):
+ PMA_TXA_LDOCLK_BYPASS_SML_OV = 8
+ PMA_TXA_LDOCLK_BYPASS_SML = 9
+ PMA_TXA_LDOCLK_BYPASS_BIG_OV = 10
+ PMA_TXA_LDOCLK_BYPASS_BIG = 11
+ PMA_TXA_LDOCLK_EN_SML_OV = 12
+ PMA_TXA_LDOCLK_EN_SML = 13
+ PMA_TXA_LDOCLK_EN_BIG_OV = 14
+ PMA_TXA_LDOCLK_EN_BIG = 15
+
+
+class R_AUSPMA_RX_SHM_TJ_RXA_CTLE_CTRL0(Register32):
+ PMA_RXA_TX_CLK_EN = 20
+ PMA_RXA_TX_CLK_EN_OV = 21
+
+
+class R_AUSPMA_RX_SHM_TJ_RXA_AFE_CTRL1(Register32):
+ CLK_LANE_RX_DIV20_SYNC_RESET_N_OV = 29
+ CLK_LANE_RX_DIV20_SYNC_RESET_N_VAL = 30
+
+
+class R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL10(Register32):
+ PMA_RXA_DTVREG_ADJUST = 31, 27
+
+
+class R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL11(Register32):
+ PMA_RXA_DTVREG_BIG_EN = 23
+ PMA_RXA_DTVREG_BIG_EN_OV = 24
+ PMA_RXA_DTVREG_SML_EN = 25
+ PMA_RXA_DTVREG_SML_EN_OV = 26
+
+
+class R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12(Register32):
+ PMA_RXA_TX_BYTECLK_RESET_SYNC_CLR = 22
+ PMA_RXA_TX_BYTECLK_RESET_SYNC_CLR_OV = 23
+ PMA_RXA_TX_BYTECLK_RESET_SYNC_EN = 24
+ PMA_RXA_TX_BYTECLK_RESET_SYNC_EN_OV = 25
+ PMA_RXA_TX_HRCLK_SEL = 28
+ PMA_RXA_TX_HRCLK_SEL_OV = 29
+ PMA_RXA_TX_PBIAS_EN = 30
+ PMA_RXA_TX_PBIAS_EN_OV = 31
+
+
+class R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13(Register32):
+ PMA_RXA_TX_PRE_EN = 0
+ PMA_RXA_TX_PRE_EN_OV = 1
+ PMA_RXA_TX_PST1_EN = 2
+ PMA_RXA_TX_PST1_EN_OV = 3
+ PMA_RXA_DTVREG_ADJUST_OV = 15
+
+
+class R_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16(Register32):
+ PMA_RXA_RXTERM_EN = 21
+ PMA_RXA_RXTERM_EN_OV = 22
+ PMA_RXA_RXTERM_PULLUP_LEAK_EN = 23
+ PMA_RXA_RXTERM_PULLUP_LEAK_EN_OV = 24
+ PMA_RXA_TX_CAL_CODE = 29, 25
+ PMA_RXA_TX_CAL_CODE_OV = 30
+
+
+class R_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17(Register32):
+ PMA_RXA_TX_MARGIN = 19, 15
+ PMA_RXA_TX_MARGIN_OV = 20
+ PMA_RXA_TX_MARGIN_LSB = 21
+ PMA_RXA_TX_MARGIN_LSB_OV = 22
+ PMA_RXA_TX_MARGIN_P1 = 26, 23
+ PMA_RXA_TX_MARGIN_P1_OV = 27
+ PMA_RXA_TX_MARGIN_P1_LSB = 29, 28
+ PMA_RXA_TX_MARGIN_P1_LSB_OV = 30
+
+
+class R_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18(Register32):
+ PMA_RXA_TX_P1_CODE = 3, 0
+ PMA_RXA_TX_P1_CODE_OV = 4
+ PMA_RXA_TX_P1_LSB_CODE = 6, 5
+ PMA_RXA_TX_P1_LSB_CODE_OV = 7
+ PMA_RXA_TX_MARGIN_PRE = 10, 8
+ PMA_RXA_TX_MARGIN_PRE_OV = 11
+ PMA_RXA_TX_MARGIN_PRE_LSB = 13, 12
+ PMA_RXA_TX_MARGIN_PRE_LSB_OV = 14
+ PMA_RXA_TX_PRE_LSB_CODE = 16, 15
+ PMA_RXA_TX_PRE_LSB_CODE_OV = 17
+ PMA_RXA_TX_PRE_CODE = 21, 18
+ PMA_RXA_TX_PRE_CODE_OV = 22
+
+
+class R_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19(Register32):
+ PMA_RXA_TX_TEST_EN = 21
+ PMA_RXA_TX_TEST_EN_OV = 22
+ PMA_RXA_TX_EN = 23
+ PMA_RXA_TX_EN_OV = 24
+ PMA_RXA_TX_CLK_DLY_CTRL_TAPGEN = 27, 25
+ PMA_RXA_TX_CLK_DIV2_EN = 28
+ PMA_RXA_TX_CLK_DIV2_EN_OV = 29
+ PMA_RXA_TX_CLK_DIV2_RST = 30
+ PMA_RXA_TX_CLK_DIV2_RST_OV = 31
+
+
+class R_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22(Register32):
+ PMA_RXA_VREF_ADJUST_GRAY = 11, 7
+ PMA_RXA_VREF_ADJUST_GRAY_OV = 12
+ PMA_RXA_VREF_BIAS_SEL = 14, 13
+ PMA_RXA_VREF_BIAS_SEL_OV = 15
+ PMA_RXA_VREF_BOOST_EN = 16
+ PMA_RXA_VREF_BOOST_EN_OV = 17
+ PMA_RXA_VREF_EN = 18
+ PMA_RXA_VREF_EN_OV = 19
+ LPBKIN_RECOVERED_DATA = 29, 28
+ PMA_RXA_TEST_RXLPBKDT_EN = 30
+ PMA_RXA_TEST_RXLPBKDT_EN_OV = 31
+
+
+class R_AUSPMA_RX_TOP_TJ_CFG_RX_TXMODE(Register32):
+ RX_TXMODE = 0
+
+class R_ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0(Register32):
+ DP_PMA_BYTECLK_RESET = 0
+ DP_MAC_DIV20_CLK_SEL = 1
+ DPTXPHY_PMA_LANE_RESET_N = 2
+ DPTXPHY_PMA_LANE_RESET_N_OV = 3
+ DPTX_PCLK1_SELECT = 6, 4
+ DPTX_PCLK2_SELECT = 9, 7
+ DPRX_PCLK_SELECT = 12, 10
+ DPTX_PCLK1_ENABLE = 13
+ DPTX_PCLK2_ENABLE = 14
+ DPRX_PCLK_ENABLE = 15
+
+
+class AtcPhyRegs(RegMap):
+ ACIOPHY_CFG0 = 0x08, Register32
+ ACIOPHY_LANE_MODE = 0x48, R_ACIOPHY_LANE_MODE
+ ACIOPHY_CROSSBAR = 0x4C, R_ACIOPHY_CROSSBAR
+ ACIOPHY_BIST_EN = 0x84, Register32
+ ACIOPHY_BIST_OV = 0x8C, Register32
+ ACIOPHY_BIST_CFG0 = 0x90, Register32
+ ACIOPHY_BIST_STAT = 0x9C, Register32
+ ACIOPHY_BIST_RESET = 0xA8, Register32
+ ACIOPHY_BIST_CFG1 = 0xAC, Register32
+ ACIOPHY_SLEEP_CTRL = 0x1B0, Register32
+
+ AUS_COMMON_SHIM_BLK_VREG = 0x0A04, R_AUS_COMMON_SHIM_BLK_VREG
+
+ AUSPLL_FSM_CTRL = 0x1014, R_AUSPLL_FSM_CTRL
+ AUSPLL_CMD_OVERRIDE = 0x2000, R_AUSPLL_CMD_OVERRIDE
+ AUSPLL_CLKOUT_DTC_VREG = 0x2220, R_AUSPLL_CLKOUT_DTC_VREG
+ AUSPLL_DCO_EFUSE_SPARE = 0x222C, R_AUSPLL_DCO_EFUSE_SPARE
+ AUSPLL_FRACN_CAN = 0x22A4, R_AUSPLL_FRACN_CAN
+
+ CIO3PLL_CLK_CTRL = 0x2A00, R_CIO3PLL_CLK_CTRL
+ CIO3PLL_DTC_VREG = 0x2A20, R_CIO3PLL_DTC_VREG
+ CIO3PLL_DCO_NCTRL = 0x2A38, R_CIO3PLL_DCO_NCTRL
+ CIO3PLL_FRACN_CAN = 0x2AA4, R_CIO3PLL_FRACN_CAN
+
+ ATCPHY_POWER_CTRL = 0x20000, R_ATCPHY_POWER
+ ATCPHY_POWER_STAT = 0x20004, R_ATCPHY_POWER
+ ATCPHY_MISC = 0x20008, R_ATCPHY_MISC
+
+ ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0 = 0x7000, R_ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0
+ DPPHY_UNK_1028 = 0x1028, Register32
+ USB2PHY_AUX_CFG_BLK_AUX_POWER_DOWN_CONTROL_0 = 0x54000, Register32
+
+ FABRIC_TUNABLES = irange(0x45000, 0x1000 // 4, 4), Register32
+
+ LPDPTX_AUX_CFG_BLK = irange(0x50000, 0x1000 // 4, 4), Register32
+ LPDPTX_AUX_CFG_BLK_AUX_CTRL = 0x50000, Register32
+ LPDPTX_AUX_CFG_BLK_AUX_LDO_CTRL = 0x50008, Register32
+ LPDPTX_AUX_CFG_BLK_AUX_MARGIN = 0x5000c, Register32
+ LPDPTX_AUX_SHM_CFG_BLK_AUX_CTRL_REG0 = 0x50204, Register32
+ LPDPTX_AUX_SHM_CFG_BLK_AUX_CTRL_REG1 = 0x50208, Register32
+
+ LN0_AUSPMA_RX_TOP = irange(0x9000, 0x1000 // 4, 4), Register32
+ LN0_AUSPMA_RX_TOP_TJ_CFG_RX_TXMODE = 0x90F0, R_AUSPMA_RX_TOP_TJ_CFG_RX_TXMODE
+
+ LN0_AUSPMA_RX_EQ = irange(0xA000, 0x1000 // 4, 4), Register32
+
+ LN0_AUSPMA_RX_SHM = irange(0xB000, 0x1000 // 4, 4), Register32
+ LN0_AUSPMA_RX_SHM_TJ_RXA_CTLE_CTRL0 = 0xB000, R_AUSPMA_RX_SHM_TJ_RXA_CTLE_CTRL0
+ LN0_AUSPMA_RX_SHM_TJ_RXA_AFE_CTRL1 = 0xB004, R_AUSPMA_RX_SHM_TJ_RXA_AFE_CTRL1
+ LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL2 = 0xB008, Register32
+ LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL3 = 0xB00C, Register32
+ LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL4 = 0xB010, Register32
+ LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL5 = 0xB014, Register32
+ LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL6 = 0xB018, Register32
+ LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL7 = 0xB01C, Register32
+ LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL8 = 0xB020, Register32
+ LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL9 = 0xB024, Register32
+ LN0_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL10 = 0xB028, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL10
+ LN0_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL11 = 0xB02C, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL11
+ LN0_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12 = 0xB030, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12
+ LN0_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13 = 0xB034, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13
+ LN0_AUSPMA_RX_SHM_TJ_UNK_CTRL14A = 0xB038, Register32
+ LN0_AUSPMA_RX_SHM_TJ_UNK_CTRL14B = 0xB03C, Register32
+ LN0_AUSPMA_RX_SHM_TJ_UNK_CTRL15A = 0xB040, Register32
+ LN0_AUSPMA_RX_SHM_TJ_UNK_CTRL15B = 0xB044, Register32
+ LN0_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16 = 0xB048, R_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16
+ LN0_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17 = 0xB04C, R_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17
+ LN0_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18 = 0xB050, R_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18
+ LN0_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19 = 0xB054, R_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19
+ LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL20 = 0xB058, Register32
+ LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL21 = 0xB05C, Register32
+ LN0_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22 = 0xB060, R_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22
+
+ LN0_AUSPMA_TX_TOP = irange(0xC000, 0x1000 // 4, 4), Register32
+
+ LN0_AUSPMA_TX_SHM = irange(0xD000, 0x1000 // 4, 4), Register32
+ LN0_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG0 = 0xD000, R_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG0
+ LN0_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1 = 0xD004, R_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1
+ LN0_AUSPMA_TX_SHM_TXA_IMP_REG0 = 0xD008, R_AUSPMA_TX_SHM_TXA_IMP_REG0
+ LN0_AUSPMA_TX_SHM_TXA_IMP_REG1 = 0xD00C, Register32
+ LN0_AUSPMA_TX_SHM_TXA_IMP_REG2 = 0xD010, R_AUSPMA_TX_SHM_TXA_IMP_REG2
+ LN0_AUSPMA_TX_SHM_TXA_IMP_REG3 = 0xD014, R_AUSPMA_TX_SHM_TXA_IMP_REG3
+ LN0_AUSPMA_TX_SHM_TXA_UNK_REG0 = 0xD018, Register32
+ LN0_AUSPMA_TX_SHM_TXA_UNK_REG1 = 0xD01C, Register32
+ LN0_AUSPMA_TX_SHM_TXA_UNK_REG2 = 0xD020, Register32
+ LN0_AUSPMA_TX_SHM_TXA_LDOCLK = 0xD024, R_AUSPMA_TX_SHM_TXA_LDOCLK
+
+ LN1_AUSPMA_RX_TOP = irange(0x10000, 0x1000 // 4, 4), Register32
+ LN1_AUSPMA_RX_TOP_TJ_CFG_RX_TXMODE = 0x100F0, R_AUSPMA_RX_TOP_TJ_CFG_RX_TXMODE
+
+ LN1_AUSPMA_RX_EQ = irange(0x11000, 0x1000 // 4, 4), Register32
+
+ LN1_AUSPMA_RX_SHM = irange(0x12000, 0x1000 // 4, 4), Register32
+ LN1_AUSPMA_RX_SHM_TJ_RXA_CTLE_CTRL0 = 0x12000, R_AUSPMA_RX_SHM_TJ_RXA_CTLE_CTRL0
+ LN1_AUSPMA_RX_SHM_TJ_RXA_AFE_CTRL1 = 0x12004, R_AUSPMA_RX_SHM_TJ_RXA_AFE_CTRL1
+ LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL2 = 0x12008, Register32
+ LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL3 = 0x1200C, Register32
+ LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL4 = 0x12010, Register32
+ LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL5 = 0x12014, Register32
+ LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL6 = 0x12018, Register32
+ LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL7 = 0x1201C, Register32
+ LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL8 = 0x12020, Register32
+ LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL9 = 0x12024, Register32
+ LN1_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL10 = 0x12028, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL10
+ LN1_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL11 = 0x1202C, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL11
+ LN1_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12 = 0x12030, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12
+ LN1_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13 = 0x12034, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13
+ LN1_AUSPMA_RX_SHM_TJ_UNK_CTRL14A = 0x12038, Register32
+ LN1_AUSPMA_RX_SHM_TJ_UNK_CTRL14B = 0x1203C, Register32
+ LN1_AUSPMA_RX_SHM_TJ_UNK_CTRL15A = 0x12040, Register32
+ LN1_AUSPMA_RX_SHM_TJ_UNK_CTRL15B = 0x12044, Register32
+ LN1_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16 = 0x12048, R_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16
+ LN1_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17 = 0x1204C, R_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17
+ LN1_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18 = 0x12050, R_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18
+ LN1_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19 = 0x12054, R_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19
+ LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL20 = 0x12058, Register32
+ LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL21 = 0x1205C, Register32
+ LN1_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22 = 0x12060, R_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22
+
+ LN1_AUSPMA_TX_TOP = irange(0x13000, 0x1000 // 4, 4), Register32
+
+ LN1_AUSPMA_TX_SHM = irange(0x14000, 0x1000 // 4, 4), Register32
+ LN1_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG0 = 0x14000, R_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG0
+ LN1_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1 = 0x14004, R_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1
+ LN1_AUSPMA_TX_SHM_TXA_IMP_REG0 = 0x14008, R_AUSPMA_TX_SHM_TXA_IMP_REG0
+ LN1_AUSPMA_TX_SHM_TXA_IMP_REG1 = 0x1400C, Register32
+ LN1_AUSPMA_TX_SHM_TXA_IMP_REG2 = 0x14010, R_AUSPMA_TX_SHM_TXA_IMP_REG2
+ LN1_AUSPMA_TX_SHM_TXA_IMP_REG3 = 0x14014, R_AUSPMA_TX_SHM_TXA_IMP_REG3
+ LN1_AUSPMA_TX_SHM_TXA_UNK_REG0 = 0x14018, Register32
+ LN1_AUSPMA_TX_SHM_TXA_UNK_REG1 = 0x1401C, Register32
+ LN1_AUSPMA_TX_SHM_TXA_UNK_REG2 = 0x14020, Register32
+ LN1_AUSPMA_TX_SHM_TXA_LDOCLK = 0x14024, R_AUSPMA_TX_SHM_TXA_LDOCLK
+
+ ACIOPHY_TOP_TUNABLE_118 = 0x118, Register32
+ ACIOPHY_TOP_TUNABLE_11c = 0x11C, Register32
+ ACIOPHY_TOP_TUNABLE_124 = 0x124, Register32
+ ACIOPHY_TOP_TUNABLE_a00 = 0xA00, Register32
+ ACIOPHY_TOP_TUNABLE_808 = 0x808, Register32
+
+ AUSPLL_TOP_FREQ_DESC_0A = 0x2080, Register32
+ AUSPLL_TOP_FREQ_DESC_0B = 0x2084, Register32
+ AUSPLL_TOP_FREQ_DESC_0C = 0x2088, Register32
+
+ AUSPLL_TOP_TUNABLE_2094 = 0x2094, Register32
+ AUSPLL_TOP_TUNABLE_20a0 = 0x20A0, Register32
+ AUSPLL_TOP_TUNABLE_20ac = 0x20AC, Register32
+ AUSPLL_TOP_TUNABLE_20b8 = 0x20B8, Register32
+
+ CIO3PLL_TOP_TUNABLE_10 = 0x2810, Register32
+ CIO3PLL_TOP_TUNABLE_88 = 0x2888, Register32
+ CIO3PLL_TOP_TUNABLE_94 = 0x2894, Register32
+
+ CIO3PLL_CORE_TUNABLE_1c = 0x2A1C, Register32
+ CIO3PLL_CORE_TUNABLE_28 = 0x2A28, Register32
+ CIO3PLL_CORE_TUNABLE_9c = 0x2A9C, Register32
+
+ AUSPLL_CORE_TUNABLE_78 = 0x2278, Register32
diff --git a/tools/proxyclient/m1n1/hw/codecs/__init__.py b/tools/proxyclient/m1n1/hw/codecs/__init__.py
new file mode 100644
index 0000000..46bb5ee
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/codecs/__init__.py
@@ -0,0 +1,109 @@
+from m1n1.utils import RegMap, Register8
+from enum import IntEnum
+from .cs42l84 import *
+
+class E_PWR_MODE(IntEnum):
+ ACTIVE = 0
+ MUTE = 1
+ SHUTDOWN = 2
+
+class R_PWR_CTL(Register8):
+ ISNS_PD = 3
+ VSNS_PD = 2
+ MODE = 1, 0, E_PWR_MODE
+
+class R_PB_CFG0(Register8):
+ PDM_MAP = 7
+ PB_PDM_SRC = 6
+ PB_SRC = 5
+ AMP_LEVEL = 4, 0
+
+class R_PB_CFG2(Register8):
+ DVC_PCM = 7, 0
+
+class R_PB_CFG3(Register8):
+ DVC_PDM = 7, 0
+
+class E_RX_SCFG(IntEnum):
+ I2C_OFFSET = 0b00
+ LEFT = 0b01
+ RIGHT = 0b10
+ DOWNMIX = 0b11
+
+class E_RX_WLEN(IntEnum):
+ W_16BIT = 0b00
+ W_20BIT = 0b01
+ W_24BIT = 0b10
+ W_32BIT = 0b11
+
+class E_RX_SLEN(IntEnum):
+ W_16BIT = 0b00
+ W_24BIT = 0b01
+ W_32BIT = 0b10
+
+class R_TDM_CFG2(Register8):
+ RX_SCFG = 5, 4, E_RX_SCFG
+ RX_WLEN = 3, 2, E_RX_WLEN
+ RX_SLEN = 1, 0, E_RX_SLEN
+
+class R_TDM_CFG3(Register8):
+ RX_SLOT_R = 7, 4
+ RX_SLOT_L = 3, 0
+
+class TAS5770Regs(RegMap):
+ PWR_CTL = 0x002, R_PWR_CTL
+ PB_CFG0 = 0x003, R_PB_CFG0
+ PB_CFG2 = 0x005, R_PB_CFG2
+ PB_CFG3 = 0x006, R_PB_CFG3
+ TDM_CFG2 = 0x00c, R_TDM_CFG2
+ TDM_CFG3 = 0x00d, R_TDM_CFG3
+
+class R_MODE_CTRL(Register8):
+ BOP_SRC = 7
+ ISNS_PD = 3
+ VSNS_PD = 2
+ MODE = 1, 0, E_PWR_MODE
+
+class R_CHNL_0(Register8):
+ CDS_MODE = 7, 6
+ AMP_LEVEL = 5, 1
+
+class R_DVC(Register8):
+ DVC_LVL = 7, 0
+
+class R_INT_MASK0(Register8):
+ BOPM = 7
+ BOPIH = 6
+ LIMMA = 5
+ PBIP = 4
+ LIMA = 3
+ TDMCE = 2
+ OC = 1
+ OT = 0
+
+class R_INT_CLK_CFG(Register8):
+ CLK_ERR_PWR_EN = 7
+ DIS_CLK_HAT = 6
+ CLK_HALT_TIMER = 5, 3
+ IRQZ_CLR = 2
+ IRQZ_PIN_CFG = 1, 0
+
+class SN012776Regs(RegMap):
+ MODE_CTRL = 0x002, R_MODE_CTRL
+ CHNL_0 = 0x003, R_CHNL_0
+ DVC = 0x01a, R_DVC
+
+ INT_MASK0 = 0x03b, R_INT_MASK0
+ INT_MASK1 = 0x03c, Register8
+ INT_MASK2 = 0x040, Register8
+ INT_MASK3 = 0x041, Register8
+ INT_MASK4 = 0x03d, Register8
+
+ INT_LTCH0 = 0x049, R_INT_MASK0
+ INT_LTCH1 = 0x04a, Register8
+ INT_LTCH1_0 = 0x04b, Register8
+ INT_LTCH2 = 0x04f, Register8
+ INT_LTCH3 = 0x050, Register8
+ INT_LTCH4 = 0x051, Register8
+
+ INT_CLK_CFG = 0x05c, R_INT_CLK_CFG
diff --git a/tools/proxyclient/m1n1/hw/codecs/cs42l84.py b/tools/proxyclient/m1n1/hw/codecs/cs42l84.py
new file mode 100644
index 0000000..095b421
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/codecs/cs42l84.py
@@ -0,0 +1,365 @@
+from m1n1.utils import Register8, Register16, Register32, RegMap, irange
+from enum import IntEnum
+
+class R_IRQ_MASK1(Register8):
+ RING_PLUG = 0
+ RING_UNPLUG = 1
+ TIP_PLUG = 2
+ TIP_UNPLUG = 3
+
+class R_IRQ_MASK3(Register8):
+ HSDET_AUTO_DONE = 7
+
+class E_DCID_GND_SEL(IntEnum):
+ NONE = 0
+ HS3 = 1
+ HS4 = 2
+
+class E_DCID_Z_RANGE(IntEnum):
+ NONE = 0
+ UNK2 = 2
+ UNK3 = 3
+
+class R_DCID_CTRL1(Register8):
+ Z_RANGE = 2, 0, E_DCID_Z_RANGE
+
+class R_DCID_CTRL2(Register8):
+ GND_SEL = 6, 4, E_DCID_GND_SEL
+
+class R_DCID_CTRL3(Register8):
+ START = 0
+
+class R_DCID_STATUS(Register32):
+ OVERALL = 9, 0
+ DONE = 10
+ U = 20, 11
+ D = 30, 21
+
+class E_DEBOUNCE_TIME(IntEnum):
+ T_0MS = 0b000
+ T_125MS = 0b001
+ T_250MS = 0b010
+ T_500MS = 0b011
+ T_750MS = 0b100
+ T_1S = 0b101
+
+class R_TR_SENSE_CTRL(Register8):
+ INV = 7
+ UNK1 = 6
+ FALLTIME = 5, 3, E_DEBOUNCE_TIME
+ RISETIME = 2, 0, E_DEBOUNCE_TIME
+
+class R_TR_SENSE_STATUS(Register8):
+ RING_PLUG = 0
+ RING_UNPLUG = 1
+ TIP_PLUG = 2
+ TIP_UNPLUG = 3
+
+class R_HS_DET_STATUS2(Register8):
+ HS_TRUE = 1
+ SHORT_TRUE = 0
+
+class R_MSM_BLOCK_EN1(Register8):
+ pass
+
+class R_MSM_BLOCK_EN2(Register8):
+ ASP_EN = 6
+ BUS_EN = 5
+ DAC_EN = 4
+ ADC_EN = 3
+
+class R_MSM_BLOCK_EN3(Register8):
+ TR_SENSE_EN = 3
+ DCID_EN = 4
+
+class R_HS_CLAMP_DISABLE(Register8):
+ HS_CLAMP_DISABLE = 0
+
+class E_SAMP_RATE(IntEnum):
+ S_16KHZ = 1
+ S_24KHZ = 2
+ S_32KHZ = 3
+ S_48KHZ = 4
+ S_96KHZ = 5
+ S_192KHZ = 6
+ S_22K05HZ = 10
+ S_44K1HZ = 12
+ S_88K2HZ = 13
+ S_176K4HZ = 14
+
+class E_MCLK_SRC(IntEnum):
+ RCO = 0b00
+ MCLK_PIN = 0b01
+ BCLK = 0b10
+ PLL = 0b11
+
+class E_MCLK_FREQ(IntEnum):
+ F_12MHZ = 0b00
+ F_24MHZ = 0b01
+ F_12_288KHZ = 0b10
+ F_24_576KHZ = 0b11
+
+class R_CCM_CTRL1(Register8):
+ MCLK_SRC = 1, 0, E_MCLK_SRC
+ MCLK_FREQ = 3, 2, E_MCLK_FREQ
+
+class E_REFCLK_DIV(IntEnum):
+ DIV1 = 0b00
+ DIV2 = 0b01
+ DIV4 = 0b10
+ DIV8 = 0b11
+
+class R_CCM_CTRL3(Register8):
+ REFCLK_DIV = 2, 1, E_REFCLK_DIV
+ REFCLK_IS_MCLK = 0 # BLCK otherwise
+
+class R_CCM_CTRL4(Register8):
+ REFCLK_EN = 0
+
+class R_CCM_SAMP_RATE(Register8):
+ RATE = 7, 0, E_SAMP_RATE
+
+class E_PLL_MODE(IntEnum):
+ UNSUPP = 0b00
+ BYPASS_512 = 0b01
+ BYPASS_1024 = 0b10
+ BYPASS_BOTH = 0b11
+
+class R_PLL_CTRL(Register8):
+ MODE = 2, 1, E_PLL_MODE
+ EN = 0
+
+class E_WNF_CF(IntEnum):
+ F_UNK = 0b00
+ F_300HZ = 0b11
+
+class R_ADC_CTRL1(Register8):
+ PREAMP_GAIN = 7, 6
+ PGA_GAIN = 5, 0
+
+class R_ADC_CTRL4(Register8): # maybe
+ WNF_CF = 5, 4, E_WNF_CF
+ WNF_EN = 3
+
+class R_DAC_CTRL1(Register8):
+ UNMUTE = 0
+
+ HP_LOAD = 2 # maybe
+ UNK1 = 3
+ UNK2 = 4
+ UNK3 = 5
+ HIGH_V = 6
+
+class E_PULLDOWN_R(IntEnum):
+ NONE = 0x0
+ R_UNK8 = 0x8
+ R_1K1OHMS = 0xc
+
+class R_DAC_CTRL2(Register8):
+ PULLDOWN_R = 3, 0, E_PULLDOWN_R
+
+class R_HP_VOL_CTRL(Register8):
+ ZERO_CROSS = 1
+ SOFT = 0
+
+class E_BUS_SOURCE(IntEnum):
+ EMPTY = 0b0000
+ ADC = 0b0111
+ ASP_RX_CH1 = 0b1101
+ ASP_RX_CH2 = 0b1110
+
+class R_BUS_DAC_SRC(Register8):
+ CHB = 7, 4, E_BUS_SOURCE
+ CHA = 3, 0, E_BUS_SOURCE
+
+class R_BUS_ASP_TX_SRC(Register8):
+ CH2 = 7, 4, E_BUS_SOURCE
+ CH1 = 3, 0, E_BUS_SOURCE
+
+class E_HSBIAS_SENSE_TRIP(IntEnum):
+ C_12UA = 0b000
+ C_23UA = 0b001
+ C_41UA = 0b010
+ C_52UA = 0b011
+ C_64UA = 0b100
+ C_75UA = 0b101
+ C_93UA = 0b110
+ C_104UA = 0b111
+
+class R_HSBIAS_SC_AUTOCTL(Register8):
+ HSBIAS_SENSE_EN = 7
+ AUTO_HSBIAS_HIZ = 6
+ TIP_SENSE_EN = 5
+ SENSE_TRIP = 2, 0, E_HSBIAS_SENSE_TRIP
+
+class E_TIP_SENSE_CTRL(IntEnum):
+ DISABLED = 0b00
+ DIG_INPUT = 0b01
+ SHORT_DET = 0b11
+
+class R_TIP_SENSE_CTRL2(Register8):
+ CTRL = 7, 6, E_TIP_SENSE_CTRL
+ INV = 5
+
+class E_HSBIAS_DET_MODE(IntEnum):
+ DISABLED = 0b00
+ SHORT_DET = 0b01
+ NORMAL = 0b11
+
+class E_HSBIAS_CTRL(IntEnum):
+ HI_Z = 0b00
+ U_0V0 = 0b01
+ U_2V0 = 0b10
+ U_2V7 = 0b11
+
+class R_MISC_DET_CTRL(Register8):
+ UNK1 = 7
+ DETECT_MODE = 4, 3, E_HSBIAS_DET_MODE
+ HSBIAS_CTRL = 2, 1, E_HSBIAS_CTRL
+ PDN_MIC_LVL_DET = 0
+
+class E_S0_DEBOUNCE_TIME(IntEnum):
+ T_10MS = 0b000
+ T_20MS = 0b001
+ T_30MS = 0b010
+ T_40MS = 0b011
+ T_50MS = 0b100
+ T_60MS = 0b101
+ T_70MS = 0b110
+ T_80MS = 0b111
+
+class R_MIC_DET_CTRL2(Register8):
+ DEBOUNCE_TIME = 7, 5, E_S0_DEBOUNCE_TIME
+
+class R_MIC_DET_CTRL4(Register8):
+ LATCH_TO_VP = 1
+
+class R_HS_DET_CTRL2(Register8):
+ CTRL = 7, 6
+ SET = 5, 4
+ REF = 3
+ AUTO_TIME = 1, 0
+
+class R_HS_SWITCH_CTRL(Register8):
+ REF_HS3 = 7
+ REF_HS4 = 6
+ HSB_FILT_HS3 = 5
+ HSB_FILT_HS4 = 4
+ HSB_HS3 = 3
+ HSB_HS4 = 2
+ GNDHS_HS3 = 1
+ GNDHS_HS4 = 0
+
+class R_ASP_CTRL(Register8):
+ TDM_MODE = 2
+ BCLK_EN = 1
+
+class R_ASP_FSYNC_CTRL23(Register16):
+ BCLK_PERIOD = 12, 1
+
+class R_ASP_TX_HIZ_DLY_CTRL(Register8):
+ DRV_Z = 5, 4
+ HIZ_DELAY = 3, 2
+ FS = 1
+ UNK1 = 0
+
+class R_ASP_RX_EN(Register8):
+ CH2_EN = 1
+ CH1_EN = 0
+
+class R_ASP_CH_CTRL(Register32):
+ WIDTH = 23, 16
+ SLOT_START = 10, 1
+ EDGE = 0 # set for rising edge
+
+class CS42L84Regs(RegMap):
+ DEVID = irange(0x0, 5), Register8
+ FREEZE = 0x6, Register8
+
+ SW_RESET = 0x203, Register8
+
+ IRQ_STATUS1 = 0x400, R_IRQ_MASK1
+ IRQ_STATUS2 = 0x401, Register8
+ IRQ_STATUS3 = 0x402, R_IRQ_MASK3
+ PLL_LOCK_STATUS = 0x40e, Register8 # bit 0x10
+
+ IRQ_MASK1 = 0x418, R_IRQ_MASK1
+ IRQ_MASK2 = 0x419, Register8
+ IRQ_MASK3 = 0x41a, R_IRQ_MASK3
+
+ CCM_CTRL1 = 0x600, R_CCM_CTRL1
+ CCM_SAMP_RATE = 0x601, R_CCM_SAMP_RATE
+ CCM_CTRL3 = 0x602, R_CCM_CTRL3
+ CCM_CTRL4 = 0x603, R_CCM_CTRL4
+ CCM_ASP_CLK_CTRL = 0x608, Register8
+
+ PLL_CTRL = 0x800, R_PLL_CTRL
+ PLL_DIV_FRAC = irange(0x804, 3), Register8
+ PLL_DIV_INT = 0x807, Register8
+ PLL_DIVOUT = 0x808, Register8
+
+ DCID_CTRL1 = 0x1200, R_DCID_CTRL1
+ DCID_CTRL2 = 0x1201, R_DCID_CTRL2
+ DCID_CTRL3 = 0x1202, R_DCID_CTRL3
+ DCID_TRIM_OFFSET = 0x1207, Register8
+ DCID_TRIM_SLOPE = 0x120a, Register8
+
+ # R_pull = 1100 - (regval - 128)*2
+ DCID_PULLDOWN_TRIM = 0x120b, Register8
+ DCID_STATUS = 0x120c, R_DCID_STATUS
+
+ # tip/ring sense
+ TR_SENSE_CTRL1 = 0x1280, Register8
+ TR_SENSE_CTRL2 = 0x1281, Register8
+ RING_SENSE_CTRL = 0x1282, R_TR_SENSE_CTRL
+ TIP_SENSE_CTRL = 0x1283, R_TR_SENSE_CTRL
+ TR_SENSE_STATUS = 0x1288, R_TR_SENSE_STATUS
+
+ HSBIAS_SC_AUTOCTL = 0x1470, R_HSBIAS_SC_AUTOCTL
+ WAKE_CTRL = 0x1471, Register8
+ TIP_SENSE_CTRL2 = 0x1473, R_TIP_SENSE_CTRL2
+ MISC_DET_CTRL = 0x1474, R_MISC_DET_CTRL
+ MIC_DET_CTRL2 = 0x1478, R_MIC_DET_CTRL2
+ MIC_DET_CTRL4 = 0x1477, R_MIC_DET_CTRL4
+
+ HS_DET_STATUS1 = 0x147c, Register8
+ HS_DET_STATUS2 = 0x147d, R_HS_DET_STATUS2
+ HS_DET_IRQ_MASK = irange(0x1480, 2), Register8
+ HS_DET_IRQ_STATUS = irange(0x1484, 2), Register8
+
+ MSM_BLOCK_EN1 = 0x1800, R_MSM_BLOCK_EN1
+ MSM_BLOCK_EN2 = 0x1801, R_MSM_BLOCK_EN2
+ MSM_BLOCK_EN3 = 0x1802, R_MSM_BLOCK_EN3
+
+ HS_DET_CTRL1 = 0x1810, Register8
+ HS_DET_CTRL2 = 0x1811, R_HS_DET_CTRL2
+ HS_SWITCH_CTRL = 0x1812, R_HS_SWITCH_CTRL
+ HS_CLAMP_DISABLE = 0x1813, R_HS_CLAMP_DISABLE
+ ADC_CTRL1 = 0x2000, R_ADC_CTRL1
+ ADC_CTRL2 = 0x2001, Register8 # volume
+ ADC_CTRL3 = 0x2002, Register8
+ ADC_CTRL4 = 0x2003, R_ADC_CTRL4
+
+ DAC_CTRL1 = 0x3000, R_DAC_CTRL1
+ DAC_CTRL2 = 0x3001, R_DAC_CTRL2
+ DACA_VOL_LSB = 0x3004, Register8
+ DACA_VOL_MSB = 0x3005, Register8 # sign bit
+ DACB_VOL_LSB = 0x3006, Register8
+ DACB_VOL_MSB = 0x3007, Register8 # sign bit
+ HP_VOL_CTRL = 0x3020, R_HP_VOL_CTRL
+ HP_CLAMP_CTRL = 0x3123, Register8
+
+ BUS_ASP_TX_SRC = 0x4000, R_BUS_ASP_TX_SRC
+ BUS_DAC_SRC = 0x4001, R_BUS_DAC_SRC
+
+ ASP_CTRL = 0x5000, R_ASP_CTRL
+ ASP_FSYNC_CTRL23 = 0x5010, R_ASP_FSYNC_CTRL23
+ ASP_DATA_CTRL = 0x5018, R_ASP_TX_HIZ_DLY_CTRL
+
+ ASP_RX_EN = 0x5020, R_ASP_RX_EN
+ ASP_TX_EN = 0x5024, Register8
+
+ ASP_RX1_CTRL = 0x5028, R_ASP_CH_CTRL # 32bit
+ ASP_RX2_CTRL = 0x502c, R_ASP_CH_CTRL # 32bit
+ ASP_TX1_CTRL = 0x5068, R_ASP_CH_CTRL
+ ASP_TX2_CTRL = 0x506c, R_ASP_CH_CTRL
diff --git a/tools/proxyclient/m1n1/hw/dart.py b/tools/proxyclient/m1n1/hw/dart.py
new file mode 100644
index 0000000..cd3d8d7
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/dart.py
@@ -0,0 +1,102 @@
+# SPDX-License-Identifier: MIT
+
+import struct
+
+from enum import IntEnum
+from ..utils import *
+from ..malloc import Heap
+
+from .dart8020 import DART8020, DART8020Regs
+from .dart8110 import DART8110, DART8110Regs
+
+__all__ = ["DART"]
+
+class DART(Reloadable):
+ PAGE_BITS = 14
+ PAGE_SIZE = 1 << PAGE_BITS
+
+ def __init__(self, iface, regs, util=None, compat="dart,t8020", iova_range=(0x80000000, 0x90000000)):
+ self.iface = iface
+ self.iova_allocator = [Heap(iova_range[0], iova_range[1], self.PAGE_SIZE)
+ for i in range(16)]
+ if compat in ["dart,t8020", "dart,t6000"]:
+ self.dart = DART8020(iface, regs, util, compat)
+ elif compat in ["dart,t8110"]:
+ self.dart = DART8110(iface, regs, util)
+ else:
+ raise TypeError(compat)
+
+ @classmethod
+ def from_adt(cls, u, path, instance=0, **kwargs):
+ dart_addr = u.adt[path].get_reg(instance)[0]
+ compat = u.adt[path].compatible[0]
+ if compat in ["dart,t8020", "dart,t6000"]:
+ regs = DART8020Regs(u, dart_addr)
+ elif compat in ["dart,t8110"]:
+ regs = DART8110Regs(u, dart_addr)
+ return cls(u.iface, regs, u, compat, **kwargs)
+
+ def ioread(self, stream, base, size):
+ if size == 0:
+ return b""
+
+ ranges = self.iotranslate(stream, base, size)
+
+ iova = base
+ data = []
+ for addr, size in ranges:
+ if addr is None:
+ raise Exception(f"Unmapped page at iova {iova:#x}")
+ data.append(self.iface.readmem(addr, size))
+ iova += size
+
+ return b"".join(data)
+
+ def iowrite(self, stream, base, data):
+ if len(data) == 0:
+ return
+
+ ranges = self.iotranslate(stream, base, len(data))
+
+ iova = base
+ p = 0
+ for addr, size in ranges:
+ if addr is None:
+ raise Exception(f"Unmapped page at iova {iova:#x}")
+ self.iface.writemem(addr, data[p:p + size])
+ p += size
+ iova += size
+
+ def iomap(self, stream, addr, size):
+ iova = self.iova_allocator[stream].malloc(size)
+
+ self.iomap_at(stream, iova, addr, size)
+ return iova
+
+ def iomap_at(self, stream, iova, addr, size):
+ self.dart.iomap_at(stream, iova, addr, size)
+
+ def iotranslate(self, stream, start, size):
+ return self.dart.iotranslate(stream, start, size)
+
+ def initialize(self):
+ self.dart.initialize()
+
+ def show_error(self):
+ self.dart.show_error()
+
+ def invalidate_streams(self, streams=0xffffffff):
+ self.dart.invalidate_streams(streams)
+
+ def invalidate_cache(self):
+ self.dart.invalidate_cache()
+
+ def dump_device(self, idx):
+ self.dart.dump_device(idx)
+
+ def dump_all(self):
+ for i in range(16):
+ self.dump_device(i)
+
+ def dump_params(self):
+ self.dart.dump_params()
diff --git a/tools/proxyclient/m1n1/hw/dart8020.py b/tools/proxyclient/m1n1/hw/dart8020.py
new file mode 100644
index 0000000..9954d3c
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/dart8020.py
@@ -0,0 +1,381 @@
+# SPDX-License-Identifier: MIT
+
+import struct
+
+from enum import IntEnum
+from ..utils import *
+from ..malloc import Heap
+
+__all__ = ["DART8020Regs", "DART8020"]
+
+class R_ERROR(Register32):
+ FLAG = 31
+ STREAM = 27, 24
+ CODE = 23, 0
+ NO_DAPF_MATCH = 11
+ WRITE = 10
+ SUBPAGE_PROT = 7
+ PTE_READ_FAULT = 6
+ READ_FAULT = 4
+ WRITE_FAULT = 3
+ NO_PTE = 2
+ NO_PMD = 1
+ NO_TTBR = 0
+
+class R_STREAM_COMMAND(Register32):
+ INVALIDATE = 20
+ BUSY = 2
+
+class R_TCR(Register32):
+ BYPASS_DAPF = 12
+ BYPASS_DART = 8
+ TRANSLATE_ENABLE = 7
+
+class R_TTBR(Register32):
+ VALID = 31
+ ADDR = 30, 0
+
+class R_REMAP(Register32):
+ MAP3 = 31, 24
+ MAP2 = 23, 16
+ MAP1 = 15, 8
+ MAP0 = 7, 0
+
+class PTE_T8020(Register64):
+ SP_START = 63, 52
+ SP_END = 51, 40
+ OFFSET = 39, 14
+ SP_PROT_DIS = 1
+ VALID = 0
+
+class PTE_T6000(Register64):
+ SP_START = 63, 52
+ SP_END = 51, 40
+ OFFSET = 39, 10
+ SP_PROT_DIS = 1
+ VALID = 0
+
+class R_CONFIG(Register32):
+ LOCK = 15
+
+class R_DAPF_LOCK(Register32):
+ LOCK = 0
+
+class DART8020Regs(RegMap):
+ STREAM_COMMAND = 0x20, R_STREAM_COMMAND
+ STREAM_SELECT = 0x34, Register32
+ ERROR = 0x40, R_ERROR
+ ERROR_ADDR_LO = 0x50, Register32
+ ERROR_ADDR_HI = 0x54, Register32
+ CONFIG = 0x60, R_CONFIG
+ REMAP = irange(0x80, 4, 4), R_REMAP
+
+ DAPF_LOCK = 0xf0, R_DAPF_LOCK
+ UNK1 = 0xf8, Register32
+ ENABLED_STREAMS = 0xfc, Register32
+
+ TCR = irange(0x100, 16, 4), R_TCR
+ TTBR = (irange(0x200, 16, 16), range(0, 16, 4)), R_TTBR
+
+PTE_TYPES = {
+ "dart,t8020": PTE_T8020,
+ "dart,t6000": PTE_T6000,
+}
+
+class DART8020(Reloadable):
+ PAGE_BITS = 14
+ PAGE_SIZE = 1 << PAGE_BITS
+
+ L0_SIZE = 4 # TTBR count
+ L0_OFF = 36
+ L1_OFF = 25
+ L2_OFF = 14
+
+ IDX_BITS = 11
+ Lx_SIZE = (1 << IDX_BITS)
+ IDX_MASK = Lx_SIZE - 1
+
+ def __init__(self, iface, regs, util=None, compat="dart,t8020"):
+ self.iface = iface
+ self.regs = regs
+ self.u = util
+ self.pt_cache = {}
+ self.enabled_streams = regs.ENABLED_STREAMS.val
+ self.ptecls = PTE_TYPES[compat]
+
+ @classmethod
+ def from_adt(cls, u, path, instance=0, **kwargs):
+ dart_addr = u.adt[path].get_reg(instance)[0]
+ dart = cls(u.iface, dart_addr, u)
+ dart.ptecls = PTE_TYPES[u.adt[path].compatible[0]]
+ return dart
+
+ def iomap_at(self, stream, iova, addr, size):
+ if size == 0:
+ return
+
+ if not (self.enabled_streams & (1 << stream)):
+ self.enabled_streams |= (1 << stream)
+ self.regs.ENABLED_STREAMS.val |= self.enabled_streams
+
+ tcr = self.regs.TCR[stream].reg
+
+ if tcr.BYPASS_DART and not tcr.TRANSLATE_ENABLE:
+ raise Exception("Stream is bypassed in DART")
+
+ if tcr.BYPASS_DART or not tcr.TRANSLATE_ENABLE:
+ raise Exception(f"Unknown DART mode {tcr}")
+
+ if addr & (self.PAGE_SIZE - 1):
+ raise Exception(f"Unaligned PA {addr:#x}")
+
+ if iova & (self.PAGE_SIZE - 1):
+ raise Exception(f"Unaligned IOVA {iova:#x}")
+
+ start_page = align_down(iova, self.PAGE_SIZE)
+ end = iova + size
+ end_page = align_up(end, self.PAGE_SIZE)
+
+ dirty = set()
+
+ for page in range(start_page, end_page, self.PAGE_SIZE):
+ paddr = addr + page - start_page
+
+ l0 = page >> self.L0_OFF
+ assert l0 < self.L0_SIZE
+ ttbr = self.regs.TTBR[stream, l0].reg
+ if not ttbr.VALID:
+ l1addr = self.u.memalign(self.PAGE_SIZE, self.PAGE_SIZE)
+ self.pt_cache[l1addr] = [0] * self.Lx_SIZE
+ ttbr.VALID = 1
+ ttbr.ADDR = l1addr >> 12
+ self.regs.TTBR[stream, l0].reg = ttbr
+
+ cached, l1 = self.get_pt(ttbr.ADDR << 12)
+ l1idx = (page >> self.L1_OFF) & self.IDX_MASK
+ l1pte = self.ptecls(l1[l1idx])
+ if not l1pte.VALID:
+ l2addr = self.u.memalign(self.PAGE_SIZE, self.PAGE_SIZE)
+ self.pt_cache[l2addr] = [0] * self.Lx_SIZE
+ l1pte = self.ptecls(
+ OFFSET=l2addr >> self.PAGE_BITS, VALID=1, SP_PROT_DIS=1)
+ l1[l1idx] = l1pte.value
+ dirty.add(ttbr.ADDR << 12)
+ else:
+ l2addr = l1pte.OFFSET << self.PAGE_BITS
+
+ dirty.add(l1pte.OFFSET << self.PAGE_BITS)
+ cached, l2 = self.get_pt(l2addr)
+ l2idx = (page >> self.L2_OFF) & self.IDX_MASK
+ self.pt_cache[l2addr][l2idx] = self.ptecls(
+ SP_START=0, SP_END=0xfff,
+ OFFSET=paddr >> self.PAGE_BITS, VALID=1, SP_PROT_DIS=1).value
+
+ for page in dirty:
+ self.flush_pt(page)
+
+ def iotranslate(self, stream, start, size):
+ if size == 0:
+ return []
+
+ tcr = self.regs.TCR[stream].reg
+
+ if tcr.BYPASS_DART and not tcr.TRANSLATE_ENABLE:
+ return [(start, size)]
+
+ if tcr.BYPASS_DART or not tcr.TRANSLATE_ENABLE:
+ raise Exception(f"Unknown DART mode {tcr}")
+
+ start = start & 0xffffffff
+
+ start_page = align_down(start, self.PAGE_SIZE)
+ start_off = start - start_page
+ end = start + size
+ end_page = align_up(end, self.PAGE_SIZE)
+ end_size = end - (end_page - self.PAGE_SIZE)
+
+ pages = []
+
+ for page in range(start_page, end_page, self.PAGE_SIZE):
+ l0 = page >> self.L0_OFF
+ assert l0 < self.L0_SIZE
+ ttbr = self.regs.TTBR[stream, l0].reg
+ if not ttbr.VALID:
+ pages.append(None)
+ continue
+
+ cached, l1 = self.get_pt(ttbr.ADDR << 12)
+ l1pte = self.ptecls(l1[(page >> self.L1_OFF) & self.IDX_MASK])
+ if not l1pte.VALID and cached:
+ cached, l1 = self.get_pt(ttbr.ADDR << 12, uncached=True)
+ l1pte = self.ptecls(l1[(page >> self.L1_OFF) & self.IDX_MASK])
+ if not l1pte.VALID:
+ pages.append(None)
+ continue
+
+ cached, l2 = self.get_pt(l1pte.OFFSET << self.PAGE_BITS)
+ l2pte = self.ptecls(l2[(page >> self.L2_OFF) & self.IDX_MASK])
+ if not l2pte.VALID and cached:
+ cached, l2 = self.get_pt(l1pte.OFFSET << self.PAGE_BITS, uncached=True)
+ l2pte = self.ptecls(l2[(page >> self.L2_OFF) & self.IDX_MASK])
+ if not l2pte.VALID:
+ pages.append(None)
+ continue
+
+ pages.append(l2pte.OFFSET << self.PAGE_BITS)
+
+ ranges = []
+
+ for page in pages:
+ if not ranges:
+ ranges.append((page, self.PAGE_SIZE))
+ continue
+ laddr, lsize = ranges[-1]
+ if ((page is None and laddr is None) or
+ (page is not None and laddr == (page - lsize))):
+ ranges[-1] = laddr, lsize + self.PAGE_SIZE
+ else:
+ ranges.append((page, self.PAGE_SIZE))
+
+ ranges[-1] = (ranges[-1][0], ranges[-1][1] - self.PAGE_SIZE + end_size)
+
+ if start_off:
+ ranges[0] = (ranges[0][0] + start_off if ranges[0][0] else None,
+ ranges[0][1] - start_off)
+
+ return ranges
+
+ def get_pt(self, addr, uncached=False):
+ cached = True
+ if addr not in self.pt_cache or uncached:
+ cached = False
+ self.pt_cache[addr] = list(
+ struct.unpack(f"<{self.Lx_SIZE}Q", self.iface.readmem(addr, self.PAGE_SIZE)))
+
+ return cached, self.pt_cache[addr]
+
+ def flush_pt(self, addr):
+ assert addr in self.pt_cache
+ self.iface.writemem(addr, struct.pack(f"<{self.Lx_SIZE}Q", *self.pt_cache[addr]))
+
+ def initialize(self):
+ for i in range(15):
+ self.regs.TCR[i].reg = R_TCR(TRANSLATE_ENABLE=1)
+ self.regs.TCR[15].reg = R_TCR(BYPASS_DART=1)
+
+ for i in range(16):
+ for j in range(4):
+ self.regs.TTBR[i, j].reg = R_TTBR(VALID = 0)
+
+ self.regs.ERROR.val = 0xffffffff
+ self.regs.UNK1.val = 0
+ self.regs.ENABLED_STREAMS.val = 0
+ self.enabled_streams = 0
+
+ self.invalidate_streams()
+
+ def show_error(self):
+ if self.regs.ERROR.reg.FLAG:
+ print(f"ERROR: {self.regs.ERROR.reg!s}")
+ print(f"ADDR: {self.regs.ERROR_ADDR_HI.val:#x}:{self.regs.ERROR_ADDR_LO.val:#x}")
+ self.regs.ERROR.val = 0xffffffff
+
+ def invalidate_streams(self, streams=0xffffffff):
+ self.regs.STREAM_SELECT.val = streams
+ self.regs.STREAM_COMMAND.val = R_STREAM_COMMAND(INVALIDATE=1)
+ while self.regs.STREAM_COMMAND.reg.BUSY:
+ pass
+
+ def invalidate_cache(self):
+ self.pt_cache = {}
+
+ def dump_table2(self, base, l1_addr):
+
+ def print_block(base, pte, start, last):
+ pgcount = last - start
+ pte.OFFSET -= pgcount
+ print(" page (%4d): %08x ... %08x -> %016x [%d%d]" % (
+ start, base + start*0x4000, base + (start+1)*0x4000,
+ pte.OFFSET << self.PAGE_BITS, pte.SP_PROT_DIS, pte.VALID))
+ if start < last:
+ print(" ==> (%4d): ... %08x -> %016x size: %08x" % (
+ last, base + (last+1)*0x4000,
+ (pte.OFFSET + pgcount - 1) << self.PAGE_BITS, pgcount << self.PAGE_BITS))
+
+ cached, tbl = self.get_pt(l1_addr)
+
+ unmapped = False
+ start = 0
+ next_pte = self.ptecls(VALID=0)
+
+ for i, pte in enumerate(tbl):
+ pte = self.ptecls(pte)
+ if not pte.VALID:
+ if not unmapped:
+ if next_pte.VALID:
+ print_block(base, next_pte, start, i)
+ print(" ...")
+ unmapped = True
+ next_pte = pte
+ continue
+
+ unmapped = False
+
+ if int(pte) != int(next_pte):
+ if next_pte.VALID:
+ print_block(base, next_pte, start, i)
+ start = i
+
+ next_pte = pte
+ next_pte.OFFSET += 1
+
+ if next_pte.VALID:
+ print_block(base, next_pte, start, 2048)
+
+ def dump_table(self, base, l1_addr):
+ cached, tbl = self.get_pt(l1_addr)
+
+ unmapped = False
+ for i, pte in enumerate(tbl):
+ pte = self.ptecls(pte)
+ if not pte.VALID:
+ if not unmapped:
+ print(" ...")
+ unmapped = True
+ continue
+
+ unmapped = False
+
+ print(" table (%d): %08x ... %08x -> %016x [%d%d]" % (
+ i, base + i*0x2000000, base + (i+1)*0x2000000,
+ pte.OFFSET << self.PAGE_BITS, pte.SP_PROT_DIS, pte.VALID))
+ self.dump_table2(base + i*0x2000000, pte.OFFSET << self.PAGE_BITS)
+
+ def dump_ttbr(self, idx, ttbr):
+ if not ttbr.VALID:
+ return
+
+ l1_addr = (ttbr.ADDR) << 12
+ print(" TTBR%d: %09x" % (idx, l1_addr))
+
+ self.dump_table(0, l1_addr)
+
+ def dump_device(self, idx):
+ tcr = self.regs.TCR[idx].reg
+ ttbrs = self.regs.TTBR[idx, :]
+ print(f"dev {idx:02x}: TCR={tcr!s} TTBRs = [{', '.join(map(str, ttbrs))}]")
+
+ if tcr.TRANSLATE_ENABLE and tcr.BYPASS_DART:
+ print(" mode: INVALID")
+ elif tcr.TRANSLATE_ENABLE:
+ print(" mode: TRANSLATE")
+
+ for idx, ttbr in enumerate(ttbrs):
+ self.dump_ttbr(idx, ttbr.reg)
+ elif tcr.BYPASS_DART:
+ print(" mode: BYPASS")
+ else:
+ print(" mode: UNKNOWN")
+
+ def dump_params(self):
+ pass
diff --git a/tools/proxyclient/m1n1/hw/dart8110.py b/tools/proxyclient/m1n1/hw/dart8110.py
new file mode 100644
index 0000000..1655182
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/dart8110.py
@@ -0,0 +1,541 @@
+# SPDX-License-Identifier: MIT
+
+import struct
+
+from enum import IntEnum
+from ..utils import *
+from ..malloc import Heap
+
+__all__ = ["DART8110Regs", "DART8110"]
+
+class R_PARAMS_0(Register32):
+ CLIENT_PARTITIONS_SUPPORTED = 29
+ LOG2_PGSZ = 27, 24
+ LOG2_TE_COUNT = 22, 20
+ TLB_SET_COUNT = 11, 0
+
+class R_PARAMS_4(Register32):
+ LOG2_NUM_WAYS = 30, 28
+ NUM_ASCS = 25, 24
+ NUM_W_PORTS = 22, 20
+ NUM_R_PORTS = 18, 16
+ NUM_APFS = 15, 8
+ SUPPORT_STT_PREFETCH = 6
+ SUPPORT_TLB_PREFETCH = 5
+ SUPPORT_CTC_PREFETCH = 4
+ SUPPORT_HW_FLUSH = 3
+ SUPPORT_TZ_TAGGER = 2
+ SUPPORT_REG_LOCK = 1
+ SUPPORT_FULL_BYPASS = 0
+
+class R_PARAMS_8(Register32):
+ PA_WIDTH = 29, 24
+ VA_WIDTH = 21, 16
+ VERS_MAJ = 15, 8
+ VERS_MIN = 7, 0
+
+class R_PARAMS_C(Register32):
+ NUM_CLIENTS = 24, 16
+ NUM_SIDS = 8, 0
+
+class R_ERROR(Register32):
+ FLAG = 31
+ SMMU = 30
+ REGION_PROTECT = 29
+ WRITE_nREAD = 28
+ SID = 27, 20
+ SECONDARY = 19
+ FILL_REGION = 18
+ BPF_REJECT = 14
+ EXTERNAL = 13
+ STT_FLUSH = 12
+ STT_MISMATCH = 11
+ APF_REJECT = 10
+ DROP_PROTECT = 9
+ CTRR_WRITE_PROTECT = 8
+ AXI_ERROR = 7
+ AXI_DECODE = 6
+ READ_FAULT = 5
+ WRITE_FAULT = 4
+ NO_PTE = 3
+ NO_PMD = 2 # "STE"
+ NO_PGD = 1 # "CTE"
+ NO_TTBR = 0
+
+class R_TLB_OP(Register32):
+ BUSY = 31
+
+ # None of these bits are supported on hwrev 1
+ HARDWARE_FLUSH = 30
+ FLUSH_VA_RANGE = 14
+ ENABLE_STT_FLUSH = 13
+ DISABLE_STC_FLUSH = 12
+
+ # 0 = flush all
+ # 1 = flush SID
+ # 2 = TLB read
+ # 3 = TLB write????
+ # 4 = flush unlock, definitely not supported on hwrev 1
+ OP = 10, 8
+ STREAM = 7, 0
+
+class R_TLB_OP_IDX(Register32):
+ SET = 13, 8
+ WAY = 6, 4
+ TE = 2, 0
+
+class R_PROTECT(Register32):
+ LOCK_TZ_SELECT = 4
+ LOCK_TZ_CONFIG = 3
+ # This bit can be set, but unknown what it protects
+ _BIT2 = 2
+ LOCK_REG_4xx = 1
+ LOCK_TCR_TTBR = 0
+
+class R_DIAG_LOCK(Register32):
+ # FIXME: how does this work exactly?
+ LOCK_ON_ERR = 1
+ LOCK = 0
+
+class R_TCR(Register32):
+ REMAP = 11, 8
+ REMAP_EN = 7
+ FOUR_LEVELS = 3 # not supported on hwrev 1
+ BYPASS_DAPF = 2
+ BYPASS_DART = 1
+ TRANSLATE_ENABLE = 0
+
+class R_TTBR(Register32):
+ ADDR = 29, 2
+ VALID = 0
+
+class PTE(Register64):
+ SP_START = 63, 52
+ SP_END = 51, 40
+ OFFSET = 37, 10
+ RDPROT = 3
+ WRPROT = 2
+ UNCACHABLE = 1
+ VALID = 0
+
+class DART8110Regs(RegMap):
+ PARAMS_0 = 0x000, R_PARAMS_0
+ PARAMS_4 = 0x004, R_PARAMS_4
+ PARAMS_8 = 0x008, R_PARAMS_8
+ PARAMS_C = 0x00C, R_PARAMS_C
+ # Unknown RO
+ REG_0x10 = 0x010, Register32
+ REG_0x14 = 0x014, Register32 # hwrev 2 only
+
+ TLB_OP = 0x080, R_TLB_OP
+ TLP_OP_IDX = 0x084, R_TLB_OP_IDX
+ TLB_TAG_LO = 0x088, Register32
+ TLB_TAG_HI = 0x08c, Register32 # hwrev 2 only
+ TLB_PA_LO = 0x090, Register32
+ TLB_PA_HI = 0x094, Register32
+ TLB_START_DVA_PAGE = 0x098, Register32 # hwrev 2 only
+ TLB_END_DVA_PAGE = 0x0a0, Register32 # hwrev 2 only
+
+ ERROR = 0x100, R_ERROR
+ ERROR_DISABLE = 0x104, R_ERROR
+
+ # Found via register bruteforcing
+ STREAM_UNK_SET = irange(0x120, 8, 4), Register32
+ STREAM_UNK_CLR = irange(0x140, 8, 4), Register32
+
+ # these are all accessed by error interrupt handler
+ REG_0x160 = 0x160, Register32
+ REG_0x164 = 0x164, Register32
+ ERROR_ADDR_LO = 0x170, Register32
+ ERROR_ADDR_HI = 0x174, Register32
+ REG_0x178 = 0x178, Register32 # hwrev 2 only
+ REG_0x180 = irange(0x180, 4, 4), Register32
+ REG_0x1a0 = irange(0x1a0, 8, 4), Register32
+ ERR_SECONDARY = irange(0x1c0, 8, 4), Register32
+
+ # Write bits to _PROTECT to protect them.
+ # They can be unprotected by writing to _UNPROTECT unless _LOCK is written.
+ # If _LOCK is written, protection can be enabled but not disabled.
+ REG_PROTECT = 0x200, R_PROTECT
+ REG_UNPROTECT = 0x204, R_PROTECT
+ REG_PROTECT_LOCK = 0x208, R_PROTECT
+
+ # Tunables touch this, can set bits FF00001F, RW
+ REG_0x20c = 0x20c, Register32
+
+ DIAG_LOCK = 0x210, R_DIAG_LOCK
+
+ # All unknown, related to transaction queueing???
+
+ # can set bits 3FFFFFFC, RW
+ REG_0x218 = 0x218, Register32
+ # Tunables touch this, can set bits 000F0F0F, RW
+ REG_0x220 = 0x220, Register32
+ # Tunables touch this, can set bits 00FFFFFF, RW
+ REG_0x224 = 0x224, Register32
+ # can set bits 3F3F3F3F
+ TLIMIT = 0x228, Register32
+ # can set bits 07070707
+ TEQRESERVE = 0x22c, Register32
+ # RO, outstanding transaction count???
+ TRANS = irange(0x230, 4, 4), Register32
+
+ # hwrev 2 only for all of these
+ REG_0x300 = 0x300, Register32
+ REG_0x308 = 0x308, Register32
+ REG_0x310 = 0x310, Register32
+ REG_0x318 = 0x318, Register32
+ REG_0x320 = 0x320, Register32
+ REG_0x328 = 0x328, Register32
+ REG_0x330 = 0x330, Register32
+ REG_0x338 = 0x338, Register32
+ REG_0x340 = 0x340, Register32
+ REG_0x348 = 0x348, Register32
+ REG_0x350 = 0x350, Register32
+ REG_0x358 = 0x358, Register32
+
+ # Unknown
+ REG_0x400 = 0x400, Register32 # can set 00000003
+ REG_0x404 = 0x404, Register32 # can set 001FFFFF
+ REG_0x408 = 0x408, Register32 # can set 00FFFFFC
+ REG_0x410 = 0x410, Register32 # can set 3FFFFFFC
+
+ # These registers exist even though it's "not supported"
+ TZ_CONFIG = 0x500, Register32 # 3 bits
+ TZ_SELECT = 0x504, Register32 # 1 bit
+ TZ_REGION0_START = 0x508, Register32
+ TZ_REGION0_END = 0x510, Register32
+ TZ_REGION0_OFFSET = 0x518, Register32
+ TZ_REGION1_START = 0x520, Register32
+ TZ_REGION1_END = 0x528, Register32
+ TZ_REGION1_OFFSET = 0x530, Register32
+ TZ_REGION2_START = 0x538, Register32
+ TZ_REGION2_END = 0x540, Register32
+ TZ_REGION2_OFFSET = 0x548, Register32
+
+ # completely guessed, unverified, can set bits 0F077077
+ PERF_INTR_ENABLE = 0x700, Register32
+ PERF_INTR_STATUS = 0x704, Register32
+
+ PERF_UNK1 = irange(0x720, 8, 4), Register32
+ PERF_UNK2 = irange(0x740, 8, 4), Register32
+
+ PERF_TLB_MISS = 0x760, Register32
+ PERF_TLB_FILL = 0x764, Register32
+ PERF_TLB_HIT = 0x768, Register32
+ PERF_ST_MISS = 0x770, Register32
+ PERF_ST_FILL = 0x774, Register32
+ PERF_ST_HIT = 0x778, Register32
+ # hwrev 1 doesn't have these
+ PERF_CTC_MISS = 0x780, Register32
+ PERF_CTC_FILL = 0x784, Register32
+ PERF_CTC_HIT = 0x788, Register32
+
+ UNK_TUNABLES = irange(0x800, 256, 4), Register32
+
+ ENABLE_STREAMS = irange(0xc00, 8, 4), Register32
+ DISABLE_STREAMS = irange(0xc20, 8, 4), Register32
+
+ TCR = irange(0x1000, 256, 4), R_TCR
+ TTBR = irange(0x1400, 256, 4), R_TTBR
+
+
+class DART8110(Reloadable):
+ PAGE_BITS = 14
+ PAGE_SIZE = 1 << PAGE_BITS
+
+ L1_OFF = 25
+ L2_OFF = 14
+
+ IDX_BITS = 11
+ Lx_SIZE = (1 << IDX_BITS)
+ IDX_MASK = Lx_SIZE - 1
+
+ def __init__(self, iface, regs, util=None):
+ self.iface = iface
+ self.regs = regs
+ self.u = util
+ self.pt_cache = {}
+
+ enabled_streams = 0
+ for i in range(8):
+ enabled_streams |= regs.ENABLE_STREAMS[i].val << 32*i
+ self.enabled_streams = enabled_streams
+
+ @classmethod
+ def from_adt(cls, u, path, instance=0, **kwargs):
+ dart_addr = u.adt[path].get_reg(instance)[0]
+ regs = DART8110Regs(u, dart_addr)
+ dart = cls(u.iface, regs, u, **kwargs)
+ return dart
+
+ def iomap_at(self, stream, iova, addr, size):
+ if size == 0:
+ return
+
+ if not (self.enabled_streams & (1 << stream)):
+ self.enabled_streams |= (1 << stream)
+ self.regs.ENABLE_STREAMS[stream // 32].val |= (1 << (stream % 32))
+
+ tcr = self.regs.TCR[stream].reg
+
+ if tcr.BYPASS_DART and not tcr.TRANSLATE_ENABLE:
+ raise Exception("Stream is bypassed in DART")
+
+ if tcr.BYPASS_DART or not tcr.TRANSLATE_ENABLE:
+ raise Exception(f"Unknown DART mode {tcr}")
+
+ if addr & (self.PAGE_SIZE - 1):
+ raise Exception(f"Unaligned PA {addr:#x}")
+
+ if iova & (self.PAGE_SIZE - 1):
+ raise Exception(f"Unaligned IOVA {iova:#x}")
+
+ start_page = align_down(iova, self.PAGE_SIZE)
+ end = iova + size
+ end_page = align_up(end, self.PAGE_SIZE)
+
+ dirty = set()
+
+ for page in range(start_page, end_page, self.PAGE_SIZE):
+ paddr = addr + page - start_page
+
+ ttbr = self.regs.TTBR[stream].reg
+ if not ttbr.VALID:
+ l1addr = self.u.memalign(self.PAGE_SIZE, self.PAGE_SIZE)
+ self.pt_cache[l1addr] = [0] * self.Lx_SIZE
+ ttbr.VALID = 1
+ ttbr.ADDR = l1addr >> self.PAGE_BITS
+ self.regs.TTBR[stream].reg = ttbr
+
+ cached, l1 = self.get_pt(ttbr.ADDR << self.PAGE_BITS)
+ l1idx = (page >> self.L1_OFF) & self.IDX_MASK
+ l1pte = PTE(l1[l1idx])
+ if not l1pte.VALID:
+ l2addr = self.u.memalign(self.PAGE_SIZE, self.PAGE_SIZE)
+ self.pt_cache[l2addr] = [0] * self.Lx_SIZE
+ l1pte = PTE(
+ OFFSET=l2addr >> self.PAGE_BITS, VALID=1)
+ l1[l1idx] = l1pte.value
+ dirty.add(ttbr.ADDR << self.PAGE_BITS)
+ else:
+ l2addr = l1pte.OFFSET << self.PAGE_BITS
+
+ dirty.add(l1pte.OFFSET << self.PAGE_BITS)
+ cached, l2 = self.get_pt(l2addr)
+ l2idx = (page >> self.L2_OFF) & self.IDX_MASK
+ self.pt_cache[l2addr][l2idx] = PTE(
+ SP_START=0, SP_END=0xfff,
+ OFFSET=paddr >> self.PAGE_BITS, VALID=1).value
+
+ for page in dirty:
+ self.flush_pt(page)
+
+ def iotranslate(self, stream, start, size):
+ if size == 0:
+ return []
+
+ tcr = self.regs.TCR[stream].reg
+
+ if tcr.BYPASS_DART and not tcr.TRANSLATE_ENABLE:
+ # FIXME this may not be correct
+ return [(start, size)]
+
+ if tcr.BYPASS_DART or not tcr.TRANSLATE_ENABLE:
+ raise Exception(f"Unknown DART mode {tcr}")
+
+ start = start & 0xfffffffff
+
+ start_page = align_down(start, self.PAGE_SIZE)
+ start_off = start - start_page
+ end = start + size
+ end_page = align_up(end, self.PAGE_SIZE)
+ end_size = end - (end_page - self.PAGE_SIZE)
+
+ pages = []
+
+ for page in range(start_page, end_page, self.PAGE_SIZE):
+ ttbr = self.regs.TTBR[stream].reg
+ if not ttbr.VALID:
+ pages.append(None)
+ continue
+
+ cached, l1 = self.get_pt(ttbr.ADDR << self.PAGE_BITS)
+ l1pte = PTE(l1[(page >> self.L1_OFF) & self.IDX_MASK])
+ if not l1pte.VALID and cached:
+ cached, l1 = self.get_pt(ttbr.ADDR << self.PAGE_BITS, uncached=True)
+ l1pte = PTE(l1[(page >> self.L1_OFF) & self.IDX_MASK])
+ if not l1pte.VALID:
+ pages.append(None)
+ continue
+
+ cached, l2 = self.get_pt(l1pte.OFFSET << self.PAGE_BITS)
+ l2pte = PTE(l2[(page >> self.L2_OFF) & self.IDX_MASK])
+ if not l2pte.VALID and cached:
+ cached, l2 = self.get_pt(l1pte.OFFSET << self.PAGE_BITS, uncached=True)
+ l2pte = PTE(l2[(page >> self.L2_OFF) & self.IDX_MASK])
+ if not l2pte.VALID:
+ pages.append(None)
+ continue
+
+ pages.append(l2pte.OFFSET << self.PAGE_BITS)
+
+ ranges = []
+
+ for page in pages:
+ if not ranges:
+ ranges.append((page, self.PAGE_SIZE))
+ continue
+ laddr, lsize = ranges[-1]
+ if ((page is None and laddr is None) or
+ (page is not None and laddr == (page - lsize))):
+ ranges[-1] = laddr, lsize + self.PAGE_SIZE
+ else:
+ ranges.append((page, self.PAGE_SIZE))
+
+ ranges[-1] = (ranges[-1][0], ranges[-1][1] - self.PAGE_SIZE + end_size)
+
+ if start_off:
+ ranges[0] = (ranges[0][0] + start_off if ranges[0][0] else None,
+ ranges[0][1] - start_off)
+
+ return ranges
+
+ def get_pt(self, addr, uncached=False):
+ cached = True
+ if addr not in self.pt_cache or uncached:
+ cached = False
+ self.pt_cache[addr] = list(
+ struct.unpack(f"<{self.Lx_SIZE}Q", self.iface.readmem(addr, self.PAGE_SIZE)))
+
+ return cached, self.pt_cache[addr]
+
+ def flush_pt(self, addr):
+ assert addr in self.pt_cache
+ self.iface.writemem(addr, struct.pack(f"<{self.Lx_SIZE}Q", *self.pt_cache[addr]))
+
+ def initialize(self):
+ for i in range(15):
+ self.regs.TCR[i].reg = R_TCR(TRANSLATE_ENABLE=1)
+ self.regs.TCR[15].reg = R_TCR(BYPASS_DART=1)
+
+ for i in range(16):
+ self.regs.TTBR[i].reg = R_TTBR(VALID = 0)
+
+ # self.regs.ERROR.val = 0xffffffff
+ # self.regs.UNK1.val = 0
+ self.regs.DISABLE_STREAMS[0].val = 0xffff
+ self.enabled_streams = 0
+
+ self.invalidate_streams()
+
+ def show_error(self):
+ if self.regs.ERROR.reg.FLAG:
+ print(f"ERROR: {self.regs.ERROR.reg!s}")
+ print(f"ADDR: {self.regs.ERROR_ADDR_HI.val:#x}:{self.regs.ERROR_ADDR_LO.val:#x}")
+ self.regs.ERROR.val = 0x80000004
+
+ def invalidate_streams(self, streams=0xffff):
+ for sid in range(256):
+ if streams & (1 << sid):
+ self.regs.TLB_OP.val = R_TLB_OP(STREAM=sid, OP=1)
+ while self.regs.TLB_OP.reg.BUSY:
+ pass
+
+ def invalidate_cache(self):
+ self.pt_cache = {}
+
+ def dump_table2(self, base, l1_addr):
+
+ def print_block(base, pte, start, last):
+ pgcount = last - start
+ pte.OFFSET -= pgcount
+ print(" page (%4d): %09x ... %09x -> %016x [%d%d%d%d]" % (
+ start, base + start*0x4000, base + (start+1)*0x4000,
+ pte.OFFSET << self.PAGE_BITS,
+ pte.RDPROT, pte.WRPROT, pte.UNCACHABLE, pte.VALID))
+ if start < last:
+ print(" ==> (%4d): ... %09x -> %016x size: %08x" % (
+ last, base + (last+1)*0x4000,
+ (pte.OFFSET + pgcount - 1) << self.PAGE_BITS, pgcount << self.PAGE_BITS))
+
+ cached, tbl = self.get_pt(l1_addr)
+
+ unmapped = False
+ start = 0
+ next_pte = PTE(VALID=0)
+
+ for i, pte in enumerate(tbl):
+ pte = PTE(pte)
+ if not pte.VALID:
+ if not unmapped:
+ if next_pte.VALID:
+ print_block(base, next_pte, start, i)
+ print(" ...")
+ unmapped = True
+ next_pte = pte
+ continue
+
+ unmapped = False
+
+ if int(pte) != int(next_pte):
+ if next_pte.VALID:
+ print_block(base, next_pte, start, i)
+ start = i
+
+ next_pte = pte
+ next_pte.OFFSET += 1
+
+ if next_pte.VALID:
+ print_block(base, next_pte, start, 2048)
+
+ def dump_table(self, base, l1_addr):
+ cached, tbl = self.get_pt(l1_addr)
+
+ unmapped = False
+ for i, pte in enumerate(tbl):
+ pte = PTE(pte)
+ if not pte.VALID:
+ if not unmapped:
+ print(" ...")
+ unmapped = True
+ continue
+
+ unmapped = False
+
+ print(" table (%d): %09x ... %09x -> %016x [%d%d%d%d]" % (
+ i, base + i*0x2000000, base + (i+1)*0x2000000,
+ pte.OFFSET << self.PAGE_BITS,
+ pte.RDPROT, pte.WRPROT, pte.UNCACHABLE, pte.VALID))
+ self.dump_table2(base + i*0x2000000, pte.OFFSET << self.PAGE_BITS)
+
+ def dump_ttbr(self, ttbr):
+ if not ttbr.VALID:
+ return
+
+ l1_addr = (ttbr.ADDR) << self.PAGE_BITS
+ print(" TTBR: %011x" % (l1_addr))
+
+ self.dump_table(0, l1_addr)
+
+ def dump_device(self, idx):
+ tcr = self.regs.TCR[idx].reg
+ ttbr = self.regs.TTBR[idx]
+ print(f"dev {idx:02x}: TCR={tcr!s} TTBR = {ttbr!s}")
+
+ if tcr.TRANSLATE_ENABLE and tcr.BYPASS_DART:
+ print(" mode: INVALID")
+ elif tcr.TRANSLATE_ENABLE:
+ print(" mode: TRANSLATE")
+
+ self.dump_ttbr(ttbr.reg)
+ elif tcr.BYPASS_DART:
+ print(" mode: BYPASS")
+ else:
+ print(" mode: UNKNOWN")
+
+ def dump_params(self):
+ print(self.regs.PARAMS_0.reg)
+ print(self.regs.PARAMS_4.reg)
+ print(self.regs.PARAMS_8.reg)
+ print(self.regs.PARAMS_C.reg)
diff --git a/tools/proxyclient/m1n1/hw/dockchannel.py b/tools/proxyclient/m1n1/hw/dockchannel.py
new file mode 100644
index 0000000..1a9443e
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/dockchannel.py
@@ -0,0 +1,120 @@
+# SPDX-License-Identifier: MIT
+import struct
+
+from ..utils import *
+
+__all__ = ["DockChannel"]
+
+# DockChannel layout:
+# 00000 : Global regs
+
+# 08000 : IRQ regs (0)
+# 0c000 : IRQ regs (1)
+# 10000 : IRQ regs (2)
+# 14000 : IRQ regs (3) -> AIC #0
+# 18000 : IRQ regs (4) -> AIC #1
+# 1c000 : IRQ regs (5) (not always present)
+
+# 28000 : FIFO regs (1A)
+# 2c000 : Data regs (1A)
+# 30000 : FIFO regs (1B)
+# 34000 : Data regs (1B)
+# 38000 : FIFO regs (2A)
+# 3c000 : Data regs (2A)
+# 40000 : FIFO regs (2B)
+# 44000 : Data regs (2B)
+# (possibly more)
+
+class R_RX_DATA(Register32):
+ DATA = 31, 8
+ COUNT = 7, 0
+
+class DockChannelIRQRegs(RegMap):
+ IRQ_MASK = 0x0, Register32
+ IRQ_FLAG = 0x4, Register32
+
+class DockChannelConfigRegs(RegMap):
+ TX_THRESH = 0x0, Register32
+ RX_THRESH = 0x4, Register32
+
+class DockChannelDataRegs(RegMap):
+ TX_8 = 0x4, Register32
+ TX_16 = 0x8, Register32
+ TX_24 = 0xc, Register32
+ TX_32 = 0x10, Register32
+ TX_FREE = 0x14, Register32
+ RX_8 = 0x1c, R_RX_DATA
+ RX_16 = 0x20, R_RX_DATA
+ RX_24 = 0x24, R_RX_DATA
+ RX_32 = 0x28, Register32
+ RX_COUNT = 0x2c, Register32
+
+class DockChannel:
+ def __init__(self, u, irq_base, fifo_base, irq_idx):
+ self.u = u
+ self.p = u.proxy
+ self.iface = u.iface
+ self.config = DockChannelConfigRegs(u, fifo_base)
+ self.data = DockChannelDataRegs(u, fifo_base + 0x4000)
+ self.irq = DockChannelIRQRegs(u, irq_base)
+ self.irq_idx = irq_idx
+ self.irq.IRQ_MASK.val = 3 << (irq_idx * 2)
+
+ @property
+ def tx_irq(self):
+ self.irq.IRQ_FLAG.val = 1 << (self.irq_idx * 2)
+ return self.irq.IRQ_FLAG.val & (1 << (self.irq_idx * 2))
+
+ @property
+ def rx_irq(self):
+ self.irq.IRQ_FLAG.val = 2 << (self.irq_idx * 2)
+ return self.irq.IRQ_FLAG.val & (2 << (self.irq_idx * 2))
+
+ @property
+ def rx_count(self):
+ return self.data.RX_COUNT.val
+
+ @property
+ def tx_free(self):
+ return self.data.TX_FREE.val
+
+ def set_tx_thresh(self, v):
+ self.config.TX_THRESH.val = v
+
+ def set_rx_thresh(self, v):
+ self.config.RX_THRESH.val = v
+
+ def write(self, data):
+ p = 0
+ left = len(data)
+ while left >= 4:
+ while self.tx_free < 4:
+ pass
+ d = struct.unpack("<I", data[p:p+4])[0]
+ self.data.TX_32.val = d
+ p += 4
+ left -= 4
+ while left >= 1:
+ while self.tx_free < 1:
+ pass
+ self.data.TX_8.val = data[p]
+ p += 1
+ left -= 1
+
+ def read(self, count):
+ data = []
+ left = count
+ while left >= 4:
+ while self.rx_count < 4:
+ pass
+ data.append(struct.pack("<I", self.data.RX_32.val))
+ left -= 4
+ while left >= 1:
+ while self.rx_count < 1:
+ pass
+ data.append(bytes([self.data.RX_8.DATA]))
+ left -= 1
+ return b"".join(data)
+
+ def read_all(self):
+ return self.read(self.rx_count)
diff --git a/tools/proxyclient/m1n1/hw/dwc3.py b/tools/proxyclient/m1n1/hw/dwc3.py
new file mode 100644
index 0000000..41948f4
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/dwc3.py
@@ -0,0 +1,268 @@
+# SPDX-License-Identifier: MIT
+
+from enum import IntEnum
+from m1n1.utils import *
+
+
+class R_XHCI_USBCMD(Register32):
+ EU3S = 11
+ EWE = 10
+ CRS = 9
+ CSS = 8
+ LHCRST = 7
+ HSEE = 3
+ INTE = 2
+ HCRST = 1
+ RS = 0
+
+
+class R_XHCI_USBSTS(Register32):
+ HCE = 12
+ CNR = 11
+ SRE = 10
+ RSS = 9
+ SSS = 8
+ PCD = 4
+ EINT = 3
+ HSE = 2
+ HCH = 0
+
+
+class R_XHCI_CRCR_LO(Register32):
+ CRP = 31, 6
+ CRR = 3
+ CA = 2
+ CS = 1
+ RCS = 0
+
+
+class R_XHCI_DNCTRL(Register32):
+ N0_N15 = 15, 0
+
+
+class R_XHCI_DOORBELL(Register32):
+ TASK_ID = 31, 16
+ RSVD = 15, 8
+ TARGET = 7, 0
+
+
+class R_XHCI_PORTSC(Register32):
+ CCS = 0
+ PED = 1
+ OCA = 3
+ RESET = 4
+ PLS = 8, 5
+ PP = 9
+ SPEED = 13, 10
+ PIC = 15, 14
+ LWS = 16
+ CSC = 17
+ PEC = 18
+ WRC = 19
+ OCC = 20
+ PRC = 21
+ PLC = 22
+ CEC = 23
+ CAS = 24
+ WCE = 25
+ WDE = 26
+ WOE = 27
+ DR = 30
+ WPR = 31
+
+
+class R_XHCI_PORTLI(Register32):
+ ERROR_CNT = 15, 0
+ RLC = 19, 16
+ TLC = 23, 20
+ RSV = 31, 24
+
+
+class R_XHCI_IMAN(Register32):
+ IP = 0
+ IE = 1
+
+
+class XhciRegs(RegMap):
+ HCSPARAMS1 = 0x04, Register32
+ HCSPARAMS2 = 0x08, Register32
+ HCSPARAMS3 = 0x0C, Register32
+ HCCPARAMS1 = 0x10, Register32
+ DBOFF = 0x14, Register32
+ RTSOFF = 0x18, Register32
+ HCCPARAMS2 = 0x1C, Register32
+ USBCMD = 0x20, R_XHCI_USBCMD
+ USBSTS = 0x24, R_XHCI_USBSTS
+ DNCTRL = 0x34, R_XHCI_DNCTRL
+ CRCR_LO = 0x38, R_XHCI_CRCR_LO
+ CRCR_HI = 0x3C, Register32
+ DCBAAP_LO = 0x50, Register32
+ DCBAAP_HI = 0x54, Register32
+
+ PORTSC0 = 0x420, R_XHCI_PORTSC
+ PORTPMSC0 = 0x424, Register32
+ PORTLI0 = 0x428, R_XHCI_PORTLI
+ PORTHLPMC0 = 0x42C, Register32
+
+ PORTSC1 = 0x430, R_XHCI_PORTSC
+ PORTPMSC1 = 0x434, Register32
+ PORTLI1 = 0x438, R_XHCI_PORTLI
+ PORTHLPMC1 = 0x43C, Register32
+
+ MFINDEX = 0x440, Register32
+ IMAN0 = 0x460 + 0x00, R_XHCI_IMAN
+ IMOD0 = 0x460 + 0x04, Register32
+ ERSTSZ0 = 0x460 + 0x08, Register32
+ RSVD0 = 0x460 + 0x0C, Register32
+ ERSTBA0 = 0x460 + 0x10, Register64
+ ERDP0 = 0x460 + 0x18, Register64
+
+ IMAN1 = 0x480 + 0x00, R_XHCI_IMAN
+ IMOD1 = 0x480 + 0x04, Register32
+ ERSTSZ1 = 0x480 + 0x08, Register32
+ RSVD1 = 0x480 + 0x0C, Register32
+ ERSTBA1 = 0x480 + 0x10, Register64
+ ERDP1 = 0x480 + 0x18, Register64
+
+ IMAN2 = 0x4A0 + 0x00, R_XHCI_IMAN
+ IMOD2 = 0x4A0 + 0x04, Register32
+ ERSTSZ2 = 0x4A0 + 0x08, Register32
+ RSVD0 = 0x4A0 + 0x0C, Register32
+ ERSTBA2 = 0x4A0 + 0x10, Register64
+ ERDP2 = 0x4A0 + 0x18, Register64
+
+ IMAN3 = 0x4C0 + 0x00, R_XHCI_IMAN
+ IMOD3 = 0x4C0 + 0x04, Register32
+ ERSTSZ3 = 0x4C0 + 0x08, Register32
+ RSVD0 = 0x4C0 + 0x0C, Register32
+ ERSTBA3 = 0x4C0 + 0x10, Register64
+ ERDP3 = 0x4C0 + 0x18, Register64
+
+ DOORBELL = irange(0x4E0, 256, 4), R_XHCI_DOORBELL
+
+
+class R_GUSB3PIPECTL(Register32):
+ PHYSOFTRST = 31
+ U2SSINP3OK = 29
+ DISRXDETINP3 = 28
+ UX_EXIT_PX = 27
+ REQP1P2P3 = 24
+ DEPOCHANGE = 18
+ SUSPHY = 17
+ LFPSFILT = 9
+ RX_DETOPOLL = 8
+
+
+class R_GUSB2PHYCFG(Register32):
+ PHYSOFTRST = 31
+ U2_FREECLK_EXISTS = 30
+ SUSPHY = 6
+ ULPI_UTMI = 4
+ ENBLSLPM = 8
+
+
+class R_GCTL(Register32):
+ U2RSTECN = 16
+ PRTCAP = 14, 12
+ CORESOFTRESET = 11
+ SOFITPSYNC = 10
+ SCALEDOWN = 6, 4
+ DISSCRAMBLE = 3
+ U2EXIT_LFPS = 2
+ GBLHIBERNATIONEN = 1
+ DSBLCLKGTNG = 0
+
+
+class Dwc3CoreRegs(RegMap):
+ GSBUSCFG0 = 0x100, Register32
+ GSBUSCFG1 = 0x104, Register32
+ GTXTHRCFG = 0x108, Register32
+ GRXTHRCFG = 0x10C, Register32
+ GCTL = 0x110, R_GCTL
+ GEVTEN = 0x114, Register32
+ GSTS = 0x118, Register32
+ GUCTL1 = 0x11C, Register32
+ GSNPSID = 0x120, Register32
+ GGPIO = 0x124, Register32
+ GUID = 0x128, Register32
+ GUCTL = 0x12C, Register32
+ GBUSERRADDR0 = 0x130, Register32
+ GBUSERRADDR1 = 0x134, Register32
+ GPRTBIMAP0 = 0x138, Register32
+ GPRTBIMAP1 = 0x13C, Register32
+ GHWPARAMS0 = 0x140, Register32
+ GHWPARAMS1 = 0x144, Register32
+ GHWPARAMS2 = 0x148, Register32
+ GHWPARAMS3 = 0x14C, Register32
+ GHWPARAMS4 = 0x150, Register32
+ GHWPARAMS5 = 0x154, Register32
+ GHWPARAMS6 = 0x158, Register32
+ GHWPARAMS7 = 0x15C, Register32
+ GDBGFIFOSPACE = 0x160, Register32
+ GDBGLTSSM = 0x164, Register32
+ GDBGBMU = 0x16C, Register32
+ GDBGLSPMUX = 0x170, Register32
+ GDBGLSP = 0x174, Register32
+ GDBGEPINFO0 = 0x178, Register32
+ GDBGEPINFO1 = 0x17C, Register32
+ GPRTBIMAP_HS0 = 0x180, Register32
+ GPRTBIMAP_HS1 = 0x184, Register32
+ GPRTBIMAP_FS0 = 0x188, Register32
+ GPRTBIMAP_FS1 = 0x18C, Register32
+ GUCTL2 = 0x19C, Register32
+ GUSB2PHYCFG = 0x200, R_GUSB2PHYCFG
+ GUSB2I2CCTL = 0x240, Register32
+ GUSB2PHYACC = 0x280, Register32
+ GUSB3PIPECTL = 0x2C0, R_GUSB3PIPECTL
+ DWC3_GHWPARAMS8 = 0x600, Register32
+ DWC3_GUCTL3 = 0x60C, Register32
+ DWC3_GFLADJ = 0x630, Register32
+ DWC3_GHWPARAMS9 = 0x680, Register32
+
+
+class R_PIPEHANDLER_OVERRIDE(Register32):
+ RXVALID = 0
+ RXDETECT = 2
+
+
+class E_PIPEHANDLER_MUX_MODE(IntEnum):
+ USB3_PHY = 0
+ DUMMY_PHY = 1
+ UNK2 = 2
+
+
+class E_PIPEHANDLER_CLK_SELECT(IntEnum):
+ UNK0 = 0
+ USB3_PHY = 1
+ DUMMY_PHY = 2
+ UNK4 = 4
+
+
+class R_PIPEHANDLER_MUX_CTRL(Register32):
+ MUX_MODE = 1, 0, E_PIPEHANDLER_MUX_MODE
+ CLK_SELECT = 5, 3, E_PIPEHANDLER_CLK_SELECT
+
+
+class R_PIPEHANDLER_LOCK(Register32):
+ LOCK_EN = 0
+
+
+class R_PIPEHANDLER_AON_GEN(Register32):
+ DWC3_FORCE_CLAMP_EN = 4
+ DWC3_RESET_N = 0
+
+
+class R_PIPEHANDLER_NONSELECTED_OVERRIDE(Register32):
+ NATIVE_POWER_DOWN = 3, 0
+ NATIVE_RESET = 12
+ DUMMY_PHY_EN = 15
+
+
+class PipehandlerRegs(RegMap):
+ PIPEHANDLER_OVERRIDE = 0x00, R_PIPEHANDLER_OVERRIDE
+ PIPEHANDLER_OVERRIDE_VALUES = 0x04, R_PIPEHANDLER_OVERRIDE
+ PIPEHANDLER_MUX_CTRL = 0x0C, R_PIPEHANDLER_MUX_CTRL
+ PIPEHANDLER_LOCK_REQ = 0x10, R_PIPEHANDLER_LOCK
+ PIPEHANDLER_LOCK_ACK = 0x14, R_PIPEHANDLER_LOCK
+ PIPEHANDLER_AON_GEN = 0x1C, R_PIPEHANDLER_AON_GEN
+ PIPEHANDLER_NONSELECTED_OVERRIDE = 0x20, R_PIPEHANDLER_NONSELECTED_OVERRIDE
diff --git a/tools/proxyclient/m1n1/hw/i2c.py b/tools/proxyclient/m1n1/hw/i2c.py
new file mode 100644
index 0000000..e2bda7a
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/i2c.py
@@ -0,0 +1,251 @@
+# SPDX-License-Identifier: MIT
+from ..utils import *
+from enum import IntEnum
+
+__all__ = ["I2C", "I2CRegs"]
+
+
+class R_MTXFIFO(Register32):
+ READ = 10 # Read (DATA=count)
+ STOP = 9 # Issue START before
+ START = 8 # Issue STOP after
+ DATA = 7, 0 # Byte to send or count
+
+class R_MRXFIFO(Register32):
+ EMPTY = 8 # FIFO empty
+ DATA = 7, 0 # FIFO data
+
+class R_MCNT(Register32):
+ S_RXCNT = 31, 24 # Slave RX count
+ S_TXCNT = 23, 16 # Slave TX count
+ M_RXCNT = 15, 8 # Master RX count
+ M_TXCNT = 7, 0 # Master TX count
+
+class E_MST(IntEnum):
+ IDLE = 0
+ FRD1 = 1
+ FRD2 = 2
+ COMMAND = 3
+ START = 4
+ WRITE = 5
+ READ = 6
+ ACK = 7
+ STOP = 8
+ BAD = 15
+
+class E_SST(IntEnum):
+ IDLE = 0
+ START = 1
+ ST_ACK = 2
+ DATA = 3
+ ACK = 4
+
+class R_XFSTA(Register32):
+ MST = 27, 24, E_MST # Master controller state
+ SRD = 20 # Slave read in progress
+ SWR = 19 # Slave write in progress
+ SST = 18, 16, E_SST # Slave controller state
+ XFIFO = 9, 8 # FIFO number for error
+ XFCNT = 7, 0 # Number of bytes in current xfer
+
+class R_SADDR(Register32):
+ DEB = 31 # Enable SDA/SCL read debug
+ DIR = 30 # Direct (bitbang) mode
+ ENS = 29 # Enable slave interface
+ RST_STX = 28 # Reset slave TX FIFO
+ RST_SRX = 27 # Reset master RX fifo (if ^ both, controller too)
+ PEN = 26 # Promiscuous mode (slave)
+ AAE = 25 # SALT/ALTMASK enable
+ SAE = 24 # SADDR enable
+ ALTMASK = 23, 16 # MASK for SALT bits
+ SALT = 15, 8 # Alt slave address
+ SADDR = 7, 0 # Slave address
+
+class R_SMSTA(Register32):
+ XIP = 28 # Xaction in progress
+ XEN = 27 # Xaction ended
+ UJF = 26 # UnJam failure
+ JMD = 25 # Jam ocurred
+ JAM = 24 # Currently jammed
+ MTO = 23 # Master timeout
+ MTA = 22 # Master arb lost
+ MTN = 21 # Master received NACK
+ MRF = 20 # Master RX fifo full
+ MRNE = 19 # Master RX fifo not empty
+ MTF = 17 # Master TX fifo full
+ MTE = 16 # Master RX fifo empty
+ STO = 15 # Slave timeout
+ STA = 14 # Slave arb lost
+ STN = 13 # Slave received NACK
+ SRF = 12 # Slave RX fifo full
+ SRNE = 11 # Slave RX fifo not empty
+ STR = 10 # Slave transmit required
+ STF = 9 # Slave TX fifo full
+ STE = 8 # Slave TX fifo empty
+ TOS = 7 # Timeout due to slave FIFO
+ TOM = 6 # Timeout due to master FIFO
+ TOE = 5 # Slave timeout due to ext clock stretch
+ DCI = 4 # Direct clock in
+ DDI = 3 # Direct data in
+ DCO = 2 # Direct clock out
+ DDO = 1 # Direct data out
+ NN = 0 # NACK next (slave)
+
+class R_CTL(Register32):
+ MSW = 26, 16 # Maximum slave write size
+ ENABLE = 11 # Unknown enable bit (clock sel? Apple thing)
+ MRR = 10 # Master receive FIFO reset
+ MTR = 9 # Master transmit FIFO reset
+ UJM = 8 # Enable auto unjam machine
+ CLK = 7, 0 # Clock divider
+
+class R_STXFIFO(Register32):
+ DATA = 7, 0 # Data
+
+class R_SRXFIFO(Register32):
+ N = 12 # NACK received after this byte
+ P = 11 # Stop received, data not valid
+ S = 10 # Start received before
+ O = 9 # Overflow (promisc only)
+ E = 8 # Empty (data not valid)
+ DATA = 7, 0 # Data
+
+# Apple reg
+class R_FIFOCTL(Register32):
+ HALT = 0 # Halt machinery
+
+class I2CRegs(RegMap):
+ MTXFIFO = 0x00, R_MTXFIFO
+ MRXFIFO = 0x04, R_MRXFIFO
+ MCNT = 0x08, R_MCNT
+ XFSTA = 0x0c, R_XFSTA
+ SADDR = 0x10, R_SADDR
+ SMSTA = 0x14, R_SMSTA
+ IMASK = 0x18, R_SMSTA
+ CTL = 0x1c, R_CTL
+ STXFIFO = 0x20, R_STXFIFO
+ SRXFIFO = 0x20, R_SRXFIFO
+ FIFOCTL = 0x44, R_FIFOCTL
+
+
+class I2C:
+ def __init__(self, u, adt_path):
+ self.u = u
+ self.p = u.proxy
+ self.iface = u.iface
+ self.base = u.adt[adt_path].get_reg(0)[0]
+ self.regs = I2CRegs(u, self.base)
+ self.devs = []
+
+ def clear_fifos(self):
+ self.regs.CTL.set(MTR=1, MRR=1)
+
+ def clear_status(self):
+ self.regs.SMSTA.val = 0xffffffff
+
+ def _fifo_read(self, nbytes):
+ read = []
+ for _ in range(nbytes):
+ val = self.regs.MRXFIFO.reg
+ timeout = 10000
+ while val.EMPTY and timeout > 0:
+ val = self.regs.MRXFIFO.reg
+ timeout -= 1
+ if timeout == 0:
+ raise Exception("timeout")
+ read.append(int(val) & 0xff)
+ return bytes(read)
+
+ def _fifo_write(self, buf, stop=False):
+ for no, byte in enumerate(buf):
+ sending_stop = stop and no == len(buf) - 1
+ self.regs.MTXFIFO.set(DATA=byte, STOP=int(sending_stop))
+
+ if not stop:
+ return
+
+ timeout = 10000
+ while not self.regs.SMSTA.reg.XEN and timeout > 0:
+ timeout -= 1
+ if timeout == 0:
+ raise Exception("timeout")
+
+ def write_reg(self, addr, reg, data, regaddrlen=1):
+ self.clear_fifos()
+ self.clear_status()
+
+ self.regs.CTL.set(ENABLE=1, CLK=0x4)
+ self.regs.MTXFIFO.set(DATA=addr << 1, START=1)
+ regbytes = int.to_bytes(reg, regaddrlen, byteorder="big")
+ self._fifo_write(regbytes + bytes(data), stop=True)
+ self.regs.CTL.set(ENABLE=0, CLK=0x4)
+
+ def read_reg(self, addr, reg, nbytes, regaddrlen=1):
+ self.clear_fifos()
+ self.clear_status()
+
+ self.regs.CTL.set(ENABLE=1, CLK=0x4)
+ self.regs.MTXFIFO.set(DATA=addr << 1, START=1)
+ regbytes = int.to_bytes(reg, regaddrlen, byteorder="big")
+ self._fifo_write(regbytes, stop=False)
+ self.regs.MTXFIFO.set(DATA=(addr << 1) | 1, START=1)
+ self.regs.MTXFIFO.set(DATA=nbytes, STOP=1, READ=1)
+ data = self._fifo_read(nbytes)
+ self.regs.CTL.set(ENABLE=0, CLK=0x4)
+ return data
+
+class I2CRegMapDev:
+ REGMAP = None
+ ADDRESSING = (0, 1)
+
+ def __init__(self, bus, addr, name=None):
+ self.bus = bus
+ self.addr = addr
+ self.curr_page = None
+ self.name = name
+
+ self.paged, self.regimmbytes = self.ADDRESSING
+ if self.REGMAP is not None:
+ self.regs = self.REGMAP(self, 0)
+
+ @classmethod
+ def from_adt(cls, bus, path):
+ node = bus.u.adt[path]
+ addr = node.reg[0] & 0xff
+ return cls(bus, addr, node.name)
+
+ def _switch_page(self, page):
+ assert self.paged
+ self.bus.write_reg(self.addr, 0, bytes([page]),
+ regaddrlen=self.regimmbytes)
+ self.curr_page = page
+
+ def _snip_regaddr(self, addr):
+ pageshift = self.regimmbytes * 8
+ page = addr >> pageshift
+ immediate = addr & ~(~0 << pageshift)
+ return (page, immediate)
+
+ def write(self, reg, val, width=8):
+ page, imm = self._snip_regaddr(reg)
+
+ if self.paged and page != self.curr_page:
+ self._switch_page(page)
+
+ valbytes = val.to_bytes(width//8, byteorder="little")
+ self.bus.write_reg(self.addr, imm, valbytes,
+ regaddrlen=self.regimmbytes)
+
+ def read(self, reg, width=8):
+ page, imm = self._snip_regaddr(reg)
+
+ if self.paged and page != self.curr_page:
+ self._switch_page(page)
+
+ data = self.bus.read_reg(self.addr, imm, width//8,
+ regaddrlen=self.regimmbytes)
+ return int.from_bytes(data, byteorder='little')
+
+ def __repr__(self):
+ label = self.name or f"@ {self.addr:02x}"
+ return f"<{type(self).__name__} {label}>"
diff --git a/tools/proxyclient/m1n1/hw/isp.py b/tools/proxyclient/m1n1/hw/isp.py
new file mode 100644
index 0000000..4d8d0fd
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/isp.py
@@ -0,0 +1,507 @@
+import struct
+from enum import IntEnum
+from ..utils import *
+
+class ISPCommandDirection(IntEnum):
+ RX = 0
+ TX = 1
+
+class ISPCommand:
+ """ Represents a command in any IPC channel """
+
+ def __init__(self, channel, message, direction):
+ value, u0, u1 = struct.unpack('<3q40x', message.data)
+ self.message = message
+ self.channel = channel
+ self.direction = direction
+ self.tracer = channel.tracer
+ self.raw_value = value
+ self.value = value & 0xFFFFFFFFFFFFFFFC
+ self.arg0 = u0
+ self.arg1 = u1
+
+ def dump(self):
+ self.log(f"[CMD Value: {hex(self.value)}, U0: {hex(self.arg0)}, U1: {hex(self.arg1)}]")
+
+ def read_iova(self, address, length):
+ return self.tracer.dart.ioread(0, address, length)
+
+ def valid(self):
+ return True
+
+ def log(self, message):
+ if self.direction is ISPCommandDirection.RX:
+ self.tracer.log(f"<== [{self.channel.name}]({self.message.index}): {message}")
+ else:
+ self.tracer.log(f"==> [{self.channel.name}]({self.message.index}): {message}")
+
+class ISPTerminalCommand(ISPCommand):
+ """ Represents a command in TERMINAL channel
+
+ A command arguments include a pointer to a buffer that contains log line
+ and the length of the buffer. Buffers are 0x80 bytes wide.
+ """
+ # ISP sends buffer address at beginning
+ BUFFER_ADDRESS = None
+ # It seems messages are capped to 100 bytes
+ MAX_BUFFER_SIZE = 0x80
+
+ @staticmethod
+ def set_address(address):
+ if address != 0:
+ ISPTerminalCommand.BUFFER_ADDRESS = address
+
+ @staticmethod
+ def move_cursor():
+ if ISPTerminalCommand.BUFFER_ADDRESS:
+ ISPTerminalCommand.BUFFER_ADDRESS += ISPTerminalCommand.MAX_BUFFER_SIZE
+ else:
+ return None
+
+ def __init__(self, channel, message, direction):
+ super().__init__(channel, message, direction)
+
+ ## Set buffer address
+ ISPTerminalCommand.set_address(self.value)
+
+ ## Read contents
+ self.buffer_message = self.read_iova(ISPTerminalCommand.BUFFER_ADDRESS, self.arg0)
+
+ ## Move cursor
+ ISPTerminalCommand.move_cursor()
+
+ def dump(self):
+ self.log(f"ISPCPU: {self.buffer_message}]")
+
+ def log(self, message):
+ self.tracer.log(f"[{self.channel.name}]({str(self.message.index).ljust(3)}): {message}")
+
+class ISPIOCommand(ISPCommand):
+ """ Represents a command in IO channel
+
+ An IO command is used to request ISP to perform some operations. The command
+ contains a pointer to a command struct which contains a OPCODE. The OPCODE
+ is used to differentate commands.
+ """
+
+ def __init__(self, channel, message, direction):
+ super().__init__(channel, message, direction)
+ self.iova = self.value
+ if self.iova != 0:
+ contents = self.read_iova(self.iova, 0x8)
+ self.contents = int.from_bytes(contents, byteorder="little")
+ else:
+ self.contents = None
+
+ def dump(self):
+ if self.iova != 0:
+ self.log(f"[IO Addr: {hex(self.iova)}, Size: {hex(self.arg0)}, U1: {hex(self.arg1)} -> Opcode: {hex(self.contents >> 32)}]")
+
+class ISPT2HBufferCommand(ISPCommand):
+ """ Represents a command in BUF_T2H channel """
+ def __init__(self, channel, message, direction):
+ super().__init__(channel, message, direction)
+ self.iova = self.value
+ if self.iova != 0:
+ self.contents = self.read_iova(self.iova, 0x280)
+
+ def dump(self):
+ super().dump()
+ if self.iova != 0:
+ chexdump(self.contents)
+
+class ISPH2TBufferCommand(ISPCommand):
+ """ Represents a command in BUF_H2T channel """
+ def __init__(self, channel, message, direction):
+ super().__init__(channel, message, direction)
+ self.iova = self.value
+ if self.iova != 0:
+ # Dumping first 0x20 bytes after iova translation, but no idea how internal struct
+ self.contents = self.read_iova(self.iova, 0x20)
+
+ def dump(self):
+ super().dump()
+ if self.iova != 0:
+ chexdump(self.contents)
+
+class ISPT2HIOCommand(ISPCommand):
+ """ Represents a command in IO_T2H channel """
+ def __init__(self, channel, message, direction):
+ super().__init__(channel, message, direction)
+ self.iova = self.value
+ if self.iova != 0:
+ # Dumping first 0x20 bytes after iova translation, but no idea how internal struct
+ self.contents = self.read_iova(self.iova, 0x20)
+
+ def dump(self):
+ super().dump()
+ if self.iova != 0:
+ chexdump(self.contents)
+
+class ISPSharedMallocCommand(ISPCommand):
+ """ Represents a command in SHAREDMALLOC channel
+
+ A command of this type can either request memory allocation or memory free
+ depending the arguments. When ISP needs to allocate memory, it puts a
+ message in the SHAREDMALLOC channel, message arguments are length of buffer
+ and type of allocation.
+
+ CPU detects the new message, perform memory allocation and mutate the
+ original message to indicate the address of the allocated memory block.
+ """
+
+ def __init__(self, channel, message, direction):
+ super().__init__(channel, message, direction)
+ self.address = self.value
+ self.size = self.arg0
+ self.type = self.arg1 #.to_bytes(8, byteorder="little")
+
+ def dump(self):
+ if self.direction == ISPCommandDirection.RX:
+ if self.address is 0:
+ self.log(f"[FW Malloc, Length: {hex(self.size)}, Type: {hex(self.type)}]")
+ else:
+ self.log(f"[FW Free, Address: {hex(self.value)}, Length: {hex(self.size)}, Type: {hex(self.type)})]")
+ else:
+ if self.address is 0:
+ self.log(f"[FW Free]")
+ else:
+ self.log(f"[FW Malloc, Address: {hex(self.value)}, Type: {hex(self.type)})]")
+
+class ISPChannelTable:
+ """ A class used to present IPC table.
+
+ The Channel Table describes the IPC channels available to communicate with
+ the ISP.
+
+ In the M1 processor (tonga), the list of channels exposed by ISP are:
+ [CH - TERMINAL] (src = 0, type = 2, entries = 768, iova = 0x1804700)
+ [CH - IO] (src = 1, type = 0, entries = 8, iova = 0x1810700)
+ [CH - BUF_H2T] (src = 2, type = 0, entries = 64, iova = 0x1810b00)
+ [CH - BUF_T2H] (src = 3, type = 1, entries = 64, iova = 0x1811b00)
+ [CH - SHAREDMALLOC] (src = 3, type = 1, entries = 8, iova = 0x1812b00)
+ [CH - IO_T2H] (src = 3, type = 1, entries = 8, iova = 0x1812d00)
+
+ Each entry in the table is 256 bytes wide. Here is the layout of each entry:
+ 0x00 - 0x1F = Name (NULL terminated string)
+ 0x20 - 0x3F = Padding
+ 0x40 - 0x43 = Type (DWORD)
+ 0x44 - 0x47 = Source (DWORD)
+ 0x48 - 0x4F = Entries (QWORD)
+ 0x50 - 0x58 = Address (QWORD)
+ """
+
+ ENTRY_LENGTH = 256
+
+ def __init__(self, tracer, number_of_channels, table_address):
+ self.tracer = tracer
+ self.address = table_address
+ self.count = number_of_channels
+ self.size = number_of_channels * self.ENTRY_LENGTH
+ self.channels = []
+
+ _table = self.ioread(self.address & 0xFFFFFFFF, self.size)
+ for offset in range(0, self.size, self.ENTRY_LENGTH):
+ _entry = _table[offset: offset + self.ENTRY_LENGTH]
+ _name, _type, _source, _entries, _address = struct.unpack('<32s32x2I2q168x', _entry)
+ _channel = ISPChannel(self, _name, _type, _source, _entries, _address)
+ # We want to process terminal logs as fast as possible before they are processed by CPU
+ # So we use a special implementation for TERMINAL channel that fetches all logs
+ if _channel.name == "TERMINAL":
+ _channel = ISPTerminalChannel(self, _name, _type, _source, _entries, _address)
+ self.channels.append(_channel)
+
+ def get_last_write_command(self, doorbell_value):
+ """ Gets last written message given a Doorbell value """
+ if self.channels and len(self.channels) > 0:
+ names = []
+ channel_cmds = []
+ for channel in self.channels:
+ # We want to process terminal logs as fast as possible before they are processed by CPU
+ if (channel.doorbell == doorbell_value) or channel.name == "TERMINAL":
+ names.append(channel.name)
+ for cmd in channel.get_commands(ISPCommandDirection.TX):
+ channel_cmds.append(cmd)
+
+ self.log(f"CHs: [{(','.join(names))}]")
+ for cmd in channel_cmds:
+ cmd.dump()
+
+ def get_last_read_command(self, pending_irq):
+ """ Gets last read message given a IRQ value """
+ cmds = []
+ scanned_channels = []
+ if self.channels and len(self.channels) > 0:
+ cidx = 0
+ for channel in self.channels:
+ if (pending_irq >> channel.source & 1) != 0:
+ scanned_channels.append(channel.name)
+ for cmd in channel.get_commands(ISPCommandDirection.RX):
+ cmds.append(cmd)
+ cidx = cidx + 1
+
+ if len(scanned_channels) > 0:
+ self.log(f"CHs: [{(','.join(scanned_channels))}]")
+ for cmd in cmds:
+ cmd.dump()
+
+ def dump(self):
+ """ Dumps the content of each channel """
+ if self.channels and len(self.channels) > 0:
+ for channel in self.channels:
+ channel.dump()
+
+ def ioread(self, address, size):
+ return self.tracer.ioread(address, size)
+
+ def log(self, message):
+ self.tracer.log(message)
+
+ def __str__(self):
+ s = "======== CHANNEL TABLE ========\n"
+ for channel in self.channels:
+ s += f"\t{str(channel)}\n"
+ return s
+
+class ISPChannel:
+ """ A class used to represent IPC channel
+
+ ISP channels are ring buffers used by communication between CPU and ISP.
+ channel length is measured in number of entries, each entry is 64 bytes,
+ so channel size is '(entries * 64)' bytes.
+
+ Channel Source is used to filter out channels when processing interrupts
+ and doorbell. Each time CPU wants to notify ISP about a new message it
+ writes doorbell register. In the other hand, when ISP wants to notify CPU
+ about a new message it triggers a hardware interrupt.
+
+ Channel Type is a mistery, but it seems to have a connection with cmd bit
+ mask.
+ """
+
+ ENTRY_LENGTH = 64
+
+ def __init__(self, table, name, _type, source, number_of_entries, address):
+ self.table = table
+ self.tracer = table.tracer
+ self.name = str(name, "ascii").rstrip('\x00')
+ self.source = source
+ self.type = _type
+ self.number_of_entries = number_of_entries
+ self.entry_size = self.ENTRY_LENGTH
+ self.size = self.number_of_entries * self.entry_size
+ self.address = address
+ self.doorbell = 1 << source
+ self.last_message_sent = None
+ self.last_message_received = None
+
+ def get_commands(self, direction):
+ """ Gets a command from the channel"""
+ commands = []
+ message = self.get_message(direction)
+ if message:
+ command = self.__convert2command__(message, direction)
+ if command:
+ commands.append(command)
+ return commands
+
+ def get_message(self, direction):
+ """ Gets a message from the channel and increase the associated index """
+ last_message = self.last_message_sent if direction is ISPCommandDirection.TX else self.last_message_received
+ index = (last_message.index + 1) if last_message else 0
+ new_index, message = self.__read_message__(index)
+ if message:
+ if last_message and last_message == message:
+ return
+
+ last_message = message
+ if direction is ISPCommandDirection.TX:
+ self.last_message_sent = last_message
+ else:
+ self.last_message_received = last_message
+ return message
+
+ def dump(self):
+ """ Dumps the content of the channel """
+ s = f"[{self.name}] Channel messages: \n"
+ for index in range(self.number_of_entries):
+ _, message = self.__read_message__(index)
+ s = s + "\t" + str(message) + "\n"
+ self.table.log(s)
+
+ def __convert2command__(self, message, direction):
+ """ Converts a channel message into a command """
+ if self.name == "TERMINAL":
+ return ISPTerminalCommand(self, message, direction)
+ elif self.name == "IO" or self.name == "DEBUG":
+ return ISPIOCommand(self, message, direction)
+ elif self.name == "SHAREDMALLOC":
+ return ISPSharedMallocCommand(self, message, direction)
+ elif self.name == "BUF_T2H":
+ return ISPT2HBufferCommand(self, message, direction)
+ elif self.name == "BUF_H2T":
+ return ISPH2TBufferCommand(self, message, direction)
+ elif self.name == "IO_T2H":
+ return ISPT2HIOCommand(self, message, direction)
+ else:
+ return ISPCommand(self, message, direction)
+
+ def __read_message__(self, index):
+ message_data = self.__read_by_index__(index)
+ message = ISPChannelMessage(index, message_data)
+ if message.valid():
+ index += 1
+ if index >= self.number_of_entries:
+ index = 0
+ return index, message
+ return 0, None
+
+ def __read_by_index__(self, index):
+ return self.table.ioread(self.address + (self.entry_size * index), self.entry_size)
+
+ def __str__(self):
+ return f"[CH - {str(self.name)}] (src = {self.source!s}, type = {self.type!s}, size = {self.number_of_entries!s}, iova = {hex(self.address)!s})"
+
+class ISPTerminalChannel(ISPChannel):
+ """ Special channel implementation for TERMINAL channel
+ Addresses of log buffers are removed from memory after MacOS processes them,
+ hence we want to be a little bit ahead of MacOS and fetch all entries if
+ possible.
+ """
+
+ def __init__(self, table, name, _type, source, number_of_entries, address):
+ super().__init__(table, name, _type, source, number_of_entries, address)
+ self.last_index = 0
+
+ def get_commands(self, direction):
+ """ Gets a command from the channel"""
+ commands = []
+ for i in range(self.number_of_entries):
+ index = (self.last_index + i) % self.number_of_entries
+ _, message = self.__read_message__(index)
+ if message and message.valid():
+ command = self.__convert2command__(message, ISPCommandDirection.RX)
+ if command:
+ commands.append(command)
+ else:
+ self.last_index = index
+ break
+ return commands
+
+class ISPChannelMessage:
+ """ A class used to represent IPC channel message or entry
+
+ Each entry is 64 bytes, however only 24 bytes seems to be used. These 24
+ bytes are divided in three qwords (8-bytes).
+ """
+
+ def __init__(self, index, data):
+ self.index = index
+ self.data = data
+ idx = 0
+ for arg in struct.unpack('<8q', self.data):
+ setattr(self, f"arg{idx}", arg)
+ idx += 1
+
+ def valid(self):
+ """ Checks if a message seems to be valid
+
+ So far I have observed that invalid messages or empty slots
+ are usually marked as 0x1 (or 0x3 in case of TERMINAL msgs)
+ """
+ return (self.arg0 is not 0x1) and (self.arg0 is not 0x3)
+
+ def __str__(self):
+ s = "ISP Message: {"
+ idx = 0
+ for arg in struct.unpack('<8q', self.data):
+ s = s + f"Arg{idx}: {hex(arg)}, "
+ idx = idx + 1
+ s = s + "}"
+ return s
+
+ def __eq__(self, other):
+ return self.data == other.data
+
+class ISP_REVISION(Register32):
+ REVISION = 15, 0
+
+class ISP_PMU(Register32):
+ STATUS = 7, 0
+ OTHER = 63, 8
+
+class ISP_PMU_SPECIAL_STATUS(Register32):
+ STATUS = 7, 0
+ OTHER = 63, 8
+
+class ISPRegs(RegMap):
+ ISP_CPU_CONTROL = 0x0000, Register32
+ ISP_CPU_STATUS = 0x0004, Register32
+ ISP_REVISION = 0x1800000, ISP_REVISION
+ ISP_POWER_UNKNOWN = 0x20e0080, Register32
+ ISP_IRQ_INTERRUPT = 0x2104000, Register32
+ ISP_IRQ_INTERRUPT_2 = 0x2104004, Register32
+ ISP_SENSOR_REF_CLOCK = irange(0x2104190, 3, 4), Register32
+ ISP_GPR0 = 0x2104170, Register32
+ ISP_GPR1 = 0x2104174, Register32
+ ISP_GPR2 = 0x2104178, Register32
+ ISP_GPR3 = 0x210417c, Register32
+ ISP_GPR4 = 0x2104180, Register32
+ ISP_GPR5 = 0x2104184, Register32
+ ISP_GPR6 = 0x2104188, Register32
+ ISP_GPR7 = 0x210418c, Register32
+
+ ISP_DOORBELL_RING0 = 0x21043f0, Register32
+ ISP_IRQ_INTERRUPT_ACK = 0x21043fc, Register32
+
+ ISP_SMBUS_REG_MTXFIFO = irange(0x2110000, 4, 0x1000), Register32
+ ISP_SMBUS_REG_MRXFIFO = irange(0x2110004, 4, 0x1000), Register32
+ ISP_SMBUS_REG_UNK_1 = irange(0x2110008, 4, 0x1000), Register32
+ ISP_SMBUS_REG_UNK_2 = irange(0x211000c, 4, 0x1000), Register32
+ ISP_SMBUS_REG_UNK_3 = irange(0x2110010, 4, 0x1000), Register32
+ ISP_SMBUS_REG_SMSTA = irange(0x2110014, 4, 0x1000), Register32
+ ISP_SMBUS_REG_UNK_4 = irange(0x2110018, 4, 0x1000), Register32
+ ISP_SMBUS_REG_CTL = irange(0x211001c, 4, 0x1000), Register32
+ ISP_SMBUS_REG_UNK_5 = irange(0x2110020, 4, 0x1000), Register32
+ ISP_SMBUS_REG_UNK_6 = irange(0x2110024, 4, 0x1000), Register32
+ ISP_SMBUS_REG_REV = irange(0x2110028, 4, 0x1000), Register32
+ ISP_SMBUS_REG_UNK_7 = irange(0x211002c, 4, 0x1000), Register32
+ ISP_SMBUS_REG_UNK_8 = irange(0x2110030, 4, 0x1000), Register32
+ ISP_SMBUS_REG_UNK_9 = irange(0x2110034, 4, 0x1000), Register32
+ ISP_SMBUS_REG_UNK_A = irange(0x2110038, 4, 0x1000), Register32
+ ISP_SMBUS_REG_UNK_B = irange(0x211003c, 4, 0x1000), Register32
+
+ ISP_DPE_REG_UNK1 = 0x2504000, Register32
+ ISP_DPE_REG_UNK2 = 0x2508000, Register32
+
+ ISP_CPU_BUFFER = 0x1050000, Register32
+
+ ISP_SPMI0_REGISTER_BASE = 0x2900000, Register32
+ ISP_SPMI1_REGISTER_BASE = 0x2920000, Register32
+ ISP_SPMI2_REGISTER_BASE = 0x2940000, Register32
+
+class PSReg(RegMap):
+ PMU_UNKNOWN0 = 0x4000, ISP_PMU
+ PMU_UNKNOWN1 = 0x4008, ISP_PMU
+ PMU_UNKNOWN2 = 0x4010, ISP_PMU
+ PMU_UNKNOWN3 = 0x4018, ISP_PMU
+ PMU_UNKNOWN4 = 0x4020, ISP_PMU
+ PMU_UNKNOWN5 = 0x4028, ISP_PMU
+ PMU_UNKNOWN6 = 0x4030, ISP_PMU
+ PMU_UNKNOWN7 = 0x4038, ISP_PMU
+ PMU_UNKNOWN8 = 0x4040, ISP_PMU
+ PMU_UNKNOWN9 = 0x4048, ISP_PMU
+ PMU_UNKNOWNA = 0x4050, ISP_PMU
+ PMU_UNKNOWNB = 0x4058, ISP_PMU
+ PMU_SPECIAL_STATUS = 0x4060, ISP_PMU_SPECIAL_STATUS
+ CLOCK_TICK_LOW = 0x34004, Register32
+ CLOCK_TICK_HIGH = 0x34008, Register32
+ RT_BANDWIDTH_SCRATCH1 = 0x38014, Register32
+ RT_BANDWIDTH_SCRATCH2 = 0x38018, Register32
+
+class SPMIReg(RegMap):
+ SPMI_UNKNOWN0 = 0x28, Register32
+ SPMI_UNKNOWN1 = 0x40, Register32
+ SPMI_UNKNOWN2 = 0x90, Register32
+ SPMI_UNKNOWN3 = 0x80a0, Register32
+ SPMI_UNKNOWN4 = 0x80a4, Register32 \ No newline at end of file
diff --git a/tools/proxyclient/m1n1/hw/jpeg.py b/tools/proxyclient/m1n1/hw/jpeg.py
new file mode 100644
index 0000000..c92bfe0
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/jpeg.py
@@ -0,0 +1,334 @@
+# SPDX-License-Identifier: MIT
+from ..utils import *
+from enum import IntEnum
+
+
+class R_STATUS(Register32):
+ DONE = 0
+ TIMEOUT = 1
+ RD_BUF_OVERFLOW = 2
+ WR_BUF_OVERFLOW = 3
+ CODEC_BUF_OVERFLOW = 4
+ SOME_KIND_OF_MACROBLOCK_SIZE_ERROR = 5
+ AXI_ERROR = 6
+ UNKNOWN_FLAG = 7
+
+
+class E_CODEC(IntEnum):
+ _444 = 0
+ _422 = 1
+ _411 = 2
+ _420 = 3
+ _400 = 4
+
+
+class R_CODEC(Register32):
+ CODEC = 2, 0, E_CODEC
+
+
+class E_ENCODE_PIXEL_FORMAT(IntEnum):
+ RGB101010 = 0
+ YUV10_linear = 1
+ RGB888 = 2
+ RGB565 = 3
+ YUV_planar = 4 # partially tested, details not understood
+ YUV_linear = 5 # partially tested, details not understood
+
+
+class R_ENCODE_PIXEL_FORMAT(Register32):
+ FORMAT = 4, 0, E_ENCODE_PIXEL_FORMAT
+
+
+class E_SCALE(IntEnum):
+ DIV1 = 0
+ DIV2 = 1
+ DIV4 = 2
+ DIV8 = 3
+
+
+class R_SCALE_FACTOR(Register32):
+ SCALE = 1, 0, E_SCALE
+
+
+class E_DECODE_PIXEL_FORMAT(IntEnum):
+ YUV444_planar = 0
+ YUV422_planar = 1
+ YUV420_planar = 2
+ YUV422_linear = 3
+ _YUV10_broken_doesnt_work = 4
+ RGBA8888 = 5
+ RGB565 = 6
+ _RGB101010_broken_doesnt_work = 7
+
+
+class R_DECODE_PIXEL_FORMAT(Register32):
+ FORMAT = 3, 0, E_DECODE_PIXEL_FORMAT
+
+
+class E_JPEG_IO_FLAGS_SUBSAMPLING(IntEnum):
+ _444 = 0
+ _422 = 1
+ _420 = 2
+ _400 = 3
+ FOUR_COMPONENTS_MODE = 4
+ _411_BROKEN = 6
+
+
+class R_JPEG_IO_FLAGS(Register32):
+ SUBSAMPLING_MODE = 2, 0, E_JPEG_IO_FLAGS_SUBSAMPLING
+ # not sure what this is supposed to do
+ MAKE_DECODE_WORK_BREAK_ENCODE = 3
+ OUTPUT_MACROBLOCKS_UNFLIPPED_H = 4
+ OUTPUT_8BYTE_CHUNKS_CORRECTLY = 5
+
+
+class R_JPEG_OUTPUT_FLAGS(Register32):
+ # bit0 doesn't seem to do anything
+ SKIP_HEADERS = 1 # output only SOS/EOI, no SOI/DQT/SOF0/DHT
+ OUTPUT_SOF0_AFTER_DHT = 2 # output SOF0 after DHT instead of before it
+ # bit3 doesn't seem to do anything
+ COMPRESS_WORSE = 4 # not sure exactly what this does
+
+
+class R_QTBL_SEL(Register32):
+ COMPONENT0 = 1, 0
+ COMPONENT1 = 3, 2
+ COMPONENT2 = 5, 4
+ COMPONENT3 = 7, 6 # guessed
+
+
+class JPEGRegs(RegMap):
+ REG_0x0 = 0x0, Register32
+ REG_0x4 = 0x4, Register32
+ MODE = 0x8, Register32
+ REG_0xc = 0xc, Register32
+
+ REG_0x10 = 0x10, Register32
+ REG_0x14 = 0x14, Register32
+ REG_0x18 = 0x18, Register32
+ # REG_0x1c = 0x1c, Register32
+
+ REG_0x20 = 0x20, Register32
+ STATUS = 0x24, R_STATUS
+
+ CODEC = 0x28, R_CODEC
+
+ REG_0x2c = 0x2c, Register32
+ REG_0x30 = 0x30, Register32
+ REG_0x34 = 0x34, Register32
+ # this changes the output drastically if set to 1 for decode
+ # breaks encode if not set to 1
+ REG_0x38 = 0x38, Register32
+
+ # not sure what the difference is. siting? type2 seems to win over type1
+ CHROMA_HALVE_H_TYPE1 = 0x3c, Register32
+ CHROMA_HALVE_H_TYPE2 = 0x40, Register32
+ CHROMA_HALVE_V_TYPE1 = 0x44, Register32
+ CHROMA_HALVE_V_TYPE2 = 0x48, Register32
+
+ # if double and quadruple both set --> double
+ CHROMA_DOUBLE_H = 0x4c, Register32
+ CHROMA_QUADRUPLE_H = 0x50, Register32
+ CHROMA_DOUBLE_V = 0x54, Register32
+
+ # details not fully understood yet
+ PX_USE_PLANE1 = 0x58, Register32
+ PX_TILES_W = 0x5c, Register32
+ PX_TILES_H = 0x60, Register32
+ PX_PLANE0_WIDTH = 0x64, Register32
+ PX_PLANE0_HEIGHT = 0x68, Register32
+ PX_PLANE0_TILING_H = 0x6c, Register32
+ PX_PLANE0_TILING_V = 0x70, Register32
+ PX_PLANE0_STRIDE = 0x74, Register32
+ PX_PLANE1_WIDTH = 0x78, Register32
+ PX_PLANE1_HEIGHT = 0x7c, Register32
+ PX_PLANE1_TILING_H = 0x80, Register32
+ PX_PLANE1_TILING_V = 0x84, Register32
+ PX_PLANE1_STRIDE = 0x88, Register32
+
+ INPUT_START1 = 0x8c, Register32
+ INPUT_START2 = 0x90, Register32
+ REG_0x94 = 0x94, Register32
+ REG_0x98 = 0x98, Register32
+ INPUT_END = 0x9c, Register32
+
+ OUTPUT_START1 = 0xa0, Register32
+ OUTPUT_START2 = 0xa4, Register32
+ OUTPUT_END = 0xa8, Register32
+
+ MATRIX_MULT = irange(0xAC, 11, 4), Register32
+ DITHER = irange(0xD8, 10, 4), Register32
+
+ ENCODE_PIXEL_FORMAT = 0x100, R_ENCODE_PIXEL_FORMAT
+ # RGB888: R, G, B = byte pos
+ # RGB101010: R, G, B = 0/1/2 = low/mid/high bits
+ # RGB565: R, G, B = 0/1/2 = low/mid/high bits
+ # YUV10: Y, U, V = 0/1/2 = low/mid/high bits
+ # YUV linear: Y0 Cb Cr Y1 = byte pos
+ # YUV planar: Y U V = 0 for Y, 0/1 for U/V indicating position somehow
+ ENCODE_COMPONENT0_POS = 0x104, Register32
+ ENCODE_COMPONENT1_POS = 0x108, Register32
+ ENCODE_COMPONENT2_POS = 0x10c, Register32
+ ENCODE_COMPONENT3_POS = 0x110, Register32
+
+ CONVERT_COLOR_SPACE = 0x114, Register32
+
+ REG_0x118 = 0x118, Register32
+ REG_0x11c = 0x11c, Register32
+
+ REG_0x120 = 0x120, Register32
+
+ # details not understood yet
+ TILING_ENABLE = 0x124, Register32
+ TILING_PLANE0 = 0x128, Register32
+ TILING_PLANE1 = 0x12c, Register32
+
+ DECODE_MACROBLOCKS_W = 0x130, Register32
+ DECODE_MACROBLOCKS_H = 0x134, Register32
+ RIGHT_EDGE_PIXELS = 0x138, Register32
+ BOTTOM_EDGE_PIXELS = 0x13c, Register32
+ RIGHT_EDGE_SAMPLES = 0x140, Register32
+ BOTTOM_EDGE_SAMPLES = 0x144, Register32
+
+ SCALE_FACTOR = 0x148, R_SCALE_FACTOR
+
+ DECODE_PIXEL_FORMAT = 0x14c, R_DECODE_PIXEL_FORMAT
+ # 0 = Cb Y'0 Cr Y'1 1 = Y'0 Cb Y'1 Cr
+ YUV422_ORDER = 0x150, Register32
+ # 0 = BGRA 1 = RGBA
+ RGBA_ORDER = 0x154, Register32
+ RGBA_ALPHA = 0x158, Register32
+
+ PLANAR_CHROMA_HALVING = 0x15c, Register32
+
+ REG_0x160 = 0x160, Register32
+ REG_0x164 = 0x164, Register32
+ # REG_0x168 = 0x168, Register32
+ REG_0x16c = 0x16c, Register32
+
+ REG_0x170 = 0x170, Register32
+ # REG_0x174 = 0x174, Register32
+ PERFCOUNTER = 0x178, Register32
+ # REG_0x17c = 0x17c, Register32
+
+ # REG_0x180 = 0x180, Register32
+ TIMEOUT = 0x184, Register32
+ HWREV = 0x188, Register32
+
+ REG_0x18c = 0x18c, Register32
+ REG_0x190 = 0x190, Register32
+ REG_0x194 = 0x194, Register32
+ REG_0x198 = 0x198, Register32
+ REG_0x19c = 0x19c, Register32
+
+ ENABLE_RST_LOGGING = 0x1a0, Register32
+ RST_LOG_ENTRIES = 0x1a4, Register32
+
+ REG_0x1a8 = 0x1a8, Register32
+ REG_0x1ac = 0x1ac, Register32
+ REG_0x1b0 = 0x1b0, Register32
+
+ REG_0x1b4 = 0x1b4, Register32
+ REG_0x1b8 = 0x1b8, Register32
+ REG_0x1bc = 0x1bc, Register32
+
+ REG_0x1c0 = 0x1c0, Register32
+ REG_0x1c4 = 0x1c4, Register32
+
+ REG_0x1c8 = 0x1c8, Register32
+
+ REG_0x1cc = 0x1cc, Register32
+ REG_0x1d0 = 0x1d0, Register32
+ REG_0x1d4 = 0x1d4, Register32
+ REG_0x1d8 = 0x1d8, Register32
+
+ REG_0x1dc = 0x1dc, Register32
+ REG_0x1e0 = 0x1e0, Register32
+ REG_0x1e4 = 0x1e4, Register32
+ REG_0x1e8 = 0x1e8, Register32
+
+ REG_0x1ec = 0x1ec, Register32
+ REG_0x1f0 = 0x1f0, Register32
+ REG_0x1f4 = 0x1f4, Register32
+ REG_0x1f8 = 0x1f8, Register32
+
+ REG_0x1fc = 0x1fc, Register32
+ REG_0x200 = 0x200, Register32
+
+ REG_0x204 = 0x204, Register32
+ REG_0x208 = 0x208, Register32
+
+ REG_0x20c = 0x20c, Register32
+ REG_0x210 = 0x210, Register32
+ REG_0x214 = 0x214, Register32
+ REG_0x218 = 0x218, Register32
+
+ REG_0x21c = 0x21c, Register32
+ REG_0x220 = 0x220, Register32
+
+ REG_0x224 = 0x224, Register32
+ REG_0x228 = 0x228, Register32
+
+ REG_0x22c = 0x22c, Register32
+ REG_0x230 = 0x230, Register32
+ REG_0x234 = 0x234, Register32
+
+ REG_0x238 = 0x238, Register32
+ REG_0x23c = 0x23c, Register32
+ REG_0x240 = 0x240, Register32
+ REG_0x244 = 0x244, Register32
+ REG_0x248 = 0x248, Register32
+
+ REG_0x24c = 0x24c, Register32
+ REG_0x250 = 0x250, Register32
+ REG_0x254 = 0x254, Register32
+ REG_0x258 = 0x258, Register32
+ REG_0x25c = 0x25c, Register32
+
+ REG_0x260 = 0x260, Register32
+ REG_0x264 = 0x264, Register32
+ REG_0x268 = 0x268, Register32
+ REG_0x26c = 0x26c, Register32
+
+ REG_0x280 = 0x280, Register32
+
+ JPEG_IO_FLAGS = 0x1000, R_JPEG_IO_FLAGS
+ REG_0x1004 = 0x1004, Register32
+ REG_0x1008 = 0x1008, Register32
+ QTBL_SEL = 0x100c, R_QTBL_SEL
+
+ # fixme what _exactly_ does this control
+ HUFFMAN_TABLE = 0x1010, Register32
+ RST_INTERVAL = 0x1014, Register32 # 16 bits effective
+ JPEG_HEIGHT = 0x1018, Register32
+ JPEG_WIDTH = 0x101c, Register32
+
+ COMPRESSED_BYTES = 0x1020, Register32
+ JPEG_OUTPUT_FLAGS = 0x1024, R_JPEG_OUTPUT_FLAGS
+ REG_0x1028 = 0x1028, Register32
+ REG_0x102c = 0x102c, Register32
+
+ BITSTREAM_CORRUPTION = 0x1030, Register32
+ # REG_0x1034 = 0x1034, Register32
+ # REG_0x1038 = 0x1038, Register32
+ # REG_0x103c = 0x103c, Register32
+
+ REG_0x1080 = 0x1080, Register32
+ REG_0x1084 = 0x1084, Register32
+ # REG_0x1088 = 0x1088, Register32
+ REG_0x108c = 0x108c, Register32
+ REG_0x1090 = 0x1090, Register32
+
+ SHIKINO_VERSION_MAGIC0 = 0x10e0, Register32
+ SHIKINO_VERSION_MAGIC1 = 0x10e4, Register32
+ SHIKINO_VERSION_MAGIC2 = 0x10e8, Register32
+ SHIKINO_VERSION_MAGIC3 = 0x10ec, Register32
+ SHIKINO_VERSION_MAGIC4 = 0x10f0, Register32
+ # REG_0x10f4 = 0x10f4, Register32
+ # REG_0x10f8 = 0x10f8, Register32
+ # REG_0x10fc = 0x10fc, Register32
+
+ QTBL = irange(0x1100, 64, 4), Register32
+
+ # todo what's the format?
+ RSTLOG = irange(0x2000, 1024, 4), Register32
diff --git a/tools/proxyclient/m1n1/hw/mca.py b/tools/proxyclient/m1n1/hw/mca.py
new file mode 100644
index 0000000..4c68a39
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/mca.py
@@ -0,0 +1,110 @@
+# SPDX-License-Identifier: MIT
+from ..utils import *
+from enum import IntEnum
+
+class R_STATUS(Register32):
+ EN = 0
+ RST = 1
+
+class R_MCLK_CONF(Register32):
+ SEL = 3, 0
+
+class R_PORT_ENABLES(Register32):
+ CLOCK1 = 1
+ CLOCK2 = 2
+ DATA = 3
+
+class R_PORT_CLKSEL(Register32):
+ SEL = 11, 8
+
+class R_PORT_DATASEL(Register32):
+ TXA0 = 0
+ TXA1 = 2
+ TXA2 = 4
+ TXA3 = 6
+ TXA4 = 8
+ TXA5 = 10
+
+ TXB0 = 1
+ TXB1 = 3
+ TXB2 = 5
+ TXB3 = 7
+ TXB4 = 9
+ TXB5 = 11
+
+class E_SLOT_WIDTH(IntEnum):
+ NONE = 0
+
+ W_16BIT = 0x4
+ W_20BIT = 0x8
+ W_24BIT = 0xc
+ W_32BIT = 0x10
+
+class R_SERDES_CONF(Register32):
+ NSLOTS = 3, 0
+ SLOT_WIDTH = 8, 4, E_SLOT_WIDTH
+
+ BCLK_POL = 10
+ LSB_FIRST = 11
+
+ UNK1 = 12
+ UNK2 = 13
+ IDLE_UNDRIVEN = 14 # TX only
+ NO_DATA_FEEDBACK = 15 # RX only
+
+ SYNC_SEL = 18, 16
+
+class R_INTMASK(Register32):
+ # macOS interested in 0x823c
+ UNK1 = 2 # m
+ UNK2 = 3 # m
+ UNK3 = 4 # m
+ TX_UNDERFLOW = 5 # m
+
+ UNK4 = 9 # m
+ READ_SENSITIVE_UNK1 = 11
+ READ_SENSITIVE_UNK2 = 15 # m
+
+class MCAClusterRegs(RegMap):
+ MCLK_STATUS = 0x0, R_STATUS
+ MCLK_CONF = 0x4, R_MCLK_CONF
+
+ SYNCGEN_STATUS = 0x100, R_STATUS
+ SYNCGEN_MCLK_SEL = 0x104, Register32
+ SYNCGEN_HI_PERIOD = 0x108, Register32
+ SYNCGEN_LO_PERIOD = 0x10c, Register32
+
+ PORT_ENABLES = 0x600, R_PORT_ENABLES
+ PORT_CLK_SEL = 0x604, R_PORT_CLKSEL
+ PORT_DATA_SEL = 0x608, R_PORT_DATASEL
+
+ INTSTATE = 0x700, R_INTMASK
+ INTMASK = 0x704, R_INTMASK
+
+class MCATXSerdesRegs(RegMap):
+ STATUS = 0x0, R_STATUS
+ CONF = 0x4, R_SERDES_CONF
+ BITDELAY = 0x8, Register32
+ CHANMASK = irange(0xc, 4, 4), Register32
+
+class MCARXSerdesRegs(RegMap):
+ STATUS = 0x0, R_STATUS
+ UNK1 = 0x4, Register32
+ CONF = 0x8, R_SERDES_CONF
+ BITDELAY = 0xc, Register32
+ CHANMASK = irange(0x10, 4, 4), Register32
+
+
+class MCACluster:
+ def __init__(self, u, base):
+ self.regs = MCAClusterRegs(u, base)
+ self.txa = MCATXSerdesRegs(u, base + 0x300)
+ self.txb = MCATXSerdesRegs(u, base + 0x500)
+ self.rxa = MCARXSerdesRegs(u, base + 0x200)
+ self.rxb = MCARXSerdesRegs(u, base + 0x400)
+ self.all_regs = [
+ self.regs,
+ self.txa, self.txb,
+ self.rxa, self.rxb
+ ]
+
diff --git a/tools/proxyclient/m1n1/hw/nco.py b/tools/proxyclient/m1n1/hw/nco.py
new file mode 100644
index 0000000..3d1840d
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/nco.py
@@ -0,0 +1,89 @@
+# SPDX-License-Identifier: MIT
+
+__all__ = ["NCO"]
+
+def galois_lfsr(init, poly):
+ state = init
+ for i in range((1 << poly.bit_length() - 1) - 1):
+ if state & 1:
+ state = (state >> 1) ^ (poly >> 1)
+ else:
+ state = (state >> 1)
+ yield state
+
+def gen_lookup_tables():
+ fwd, inv = dict(), dict()
+ lfsr_states = [0] + list(reversed(list(galois_lfsr(0x7ff, 0xa01))))
+ for cycle, sr_state in enumerate(lfsr_states):
+ fwd[cycle + 2] = sr_state
+ inv[sr_state] = cycle + 2
+ return fwd, inv
+
+
+class NCOChannel:
+ def __init__(self, parent, base):
+ self.parent = parent
+ self.base = base
+ self.p = parent.u.proxy
+
+ def enabled(self):
+ return bool(self.p.read32(self.base) & (1<<31))
+
+ def enable(self):
+ self.p.set32(self.base, 1<<31)
+
+ def disable(self):
+ self.p.clear32(self.base, 1<<31)
+
+ def set_rate(self, target):
+ was_enabled = self.enabled()
+ for off, val in enumerate(NCO.calc_regvals(self.parent.fin, target)):
+ self.p.write32(self.base + off*4, val)
+ if was_enabled:
+ self.enable()
+
+ def get_rate(self):
+ return NCO.calc_rate(self.parent.fin,
+ [self.p.read32(self.base + off*4) for off in range(4)]
+ )
+
+ def __repr__(self):
+ return f"<NCO channel @ 0x{self.base:x}>"
+
+
+class NCO:
+ TBL, TBL_INV = gen_lookup_tables()
+
+ @classmethod
+ def calc_rate(self, fin, regvals):
+ try:
+ div = self.TBL_INV[regvals[1] >> 2] << 2 | regvals[1] & 3
+ except KeyError:
+ raise ValueError("bad configuration")
+ inc1 = regvals[2]
+ inc2 = regvals[3] - 0x1_0000_0000
+ return 2 * fin * (inc1 - inc2) // (div * (inc1 - inc2) + inc1)
+
+ @classmethod
+ def calc_regvals(self, fin, fout):
+ div = 2 * fin // fout
+ inc1 = (2 * fin - div * fout)
+ inc2 = inc1 - fout
+ try:
+ return [0, self.TBL[div >> 2] << 2 | div & 3, inc1, inc2 + 0x1_0000_0000]
+ except KeyError:
+ raise ValueError("target rate out of range")
+
+ def __init__(self, u, devpath, stride=0x4000):
+ self.u = u
+ node = u.adt[devpath]
+ self.fin = u.adt["/arm-io"].clock_frequencies[node.clock_ids[0] - 256]
+
+ reg = node.get_reg(0)
+ self.chans = [
+ NCOChannel(self, base)
+ for base in range(reg[0], reg[0] + reg[1], stride)
+ ]
+
+ def __getitem__(self, idx):
+ return self.chans[idx]
diff --git a/tools/proxyclient/m1n1/hw/pmgr.py b/tools/proxyclient/m1n1/hw/pmgr.py
new file mode 100644
index 0000000..85ba247
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/pmgr.py
@@ -0,0 +1,71 @@
+# SPDX-License-Identifier: MIT
+from ..utils import *
+
+class R_PSTATE(Register32):
+ RESET = 31
+ AUTO_ENABLE = 28
+ AUTO_STATE = 27, 24
+ PARENT_MISSING = 11
+ DEV_DISABLE = 10
+ WAS_CLKGATED = 9
+ WAS_PWRGATED = 8
+ ACTUAL = 7, 4
+ DESIRED = 3, 0
+
+class R_PWRGATE(Register32):
+ GATE = 31
+
+class R_CLK_CFG(Register32):
+ UNK31 = 31
+ SRC = 30, 24
+ UNK20 = 20
+ UNK8 = 8
+ UNK0 = 7, 0
+
+class PMGRRegs0(RegMap):
+ PS3 = irange(0x0000, 10, 8), R_PSTATE
+ PS4 = irange(0x0200, 32, 8), R_PSTATE
+ PS5 = irange(0x0300, 32, 8), R_PSTATE
+ PS6 = irange(0x0c00, 2, 8), R_PSTATE
+ PS7 = irange(0x4000, 13, 8), R_PSTATE
+ PS8 = irange(0x8000, 5, 8), R_PSTATE
+ PS9 = irange(0xc000, 7, 8), R_PSTATE
+ PS10 = irange(0x10000, 10, 8), R_PSTATE
+ PS11 = irange(0x100, 32, 8), R_PSTATE
+ PS12 = irange(0x400, 15, 8), R_PSTATE
+
+ PG1 = irange(0x1c010, 16, 8), R_PWRGATE
+ PG1CFG = (irange(0x1c090, 69, 24), irange(0, 6, 4)), Register32
+
+ CPUTVM0 = 0x48000, Register32
+ CPUTVM1 = 0x48c00, Register32
+ CPUTVM2 = 0x48800, Register32
+ CPUTVM3 = 0x48400, Register32
+
+class PMGRRegs1(RegMap):
+ PS0 = irange(0x58, 32, 8), R_PSTATE
+ PS1 = irange(0x4000, 32, 8), R_PSTATE
+ PS2 = irange(0x8000, 32, 8), R_PSTATE
+
+ PG0 = irange(0x1c010, 32, 8), R_PWRGATE
+
+class PMGRRegs2(RegMap):
+ CLK_CFG0 = irange(0x40000, 86, 4), R_CLK_CFG
+ CLK_CFG1 = irange(0x40200, 8, 4), R_CLK_CFG
+ CLK_CFG2 = irange(0x40280, 2, 4), R_CLK_CFG
+
+class PMGR:
+ def __init__(self, u):
+ self.u = u
+ self.p = u.proxy
+ self.iface = u.iface
+ self.node = u.adt["/arm-io/pmgr"]
+ self.regs = [
+ PMGRRegs0(u, self.node.get_reg(0)[0]),
+ PMGRRegs1(u, self.node.get_reg(1)[0]),
+ PMGRRegs2(u, self.node.get_reg(2)[0]),
+ ]
+
+ def dump_all(self):
+ for i in self.regs:
+ i.dump_regs()
diff --git a/tools/proxyclient/m1n1/hw/pmu.py b/tools/proxyclient/m1n1/hw/pmu.py
new file mode 100644
index 0000000..b545df4
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/pmu.py
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: MIT
+import struct
+
+from ..utils import *
+from .spmi import SPMI
+
+__all__ = ["PMU"]
+
+class PMU:
+
+ def __init__(self, u, adt_path=None):
+ self.u = u
+ if adt_path is None:
+ adt_path = PMU.find_primary_pmu(u.adt)
+
+ self.node = u.adt[adt_path]
+ self.spmi = SPMI(u, adt_path.rpartition('/')[0])
+ self.adt_path = adt_path
+ self.primary = u.adt[adt_path].is_primary == 1
+ self.reg = u.adt[adt_path].reg[0]
+
+ def reset_panic_counter(self):
+ if self.primary:
+ leg_scrpad = self.node.info_leg__scrpad[0]
+ self.spmi.write8(self.reg, leg_scrpad + 2, 0) # error counts
+
+ @staticmethod
+ def find_primary_pmu(adt):
+ for child in adt["/arm-io"]:
+ if child.name.startswith("nub-spmi"):
+ for pmu in child:
+ compat = getattr(pmu, "compatible")[0] if hasattr(pmu, "compatible") else "unset"
+ primary = (getattr(pmu, "is-primary") == 1) if hasattr(pmu, "is-primary") else False
+ if compat == "pmu,spmi" and primary:
+ return pmu._path.removeprefix('/device-tree')
+ raise KeyError(f"primary 'pmu,spmi' node not found")
diff --git a/tools/proxyclient/m1n1/hw/prores.py b/tools/proxyclient/m1n1/hw/prores.py
new file mode 100644
index 0000000..5985e72
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/prores.py
@@ -0,0 +1,250 @@
+# SPDX-License-Identifier: MIT
+from ..utils import *
+from collections import namedtuple
+from enum import IntEnum
+
+
+EncodeNotRawDescriptor = namedtuple('EncodeNotRawDescriptor', [
+ 'flags', # +0x000
+ # [31:16] ?????
+ # 13 ?????
+ # 12 ?????
+ # [11:9] ?????
+ # 8 - enable alpha
+ # 7 - alpha channel bpp
+ # 0 -> 8bpp
+ # 1 -> 16bpp
+ # 6 - something unknown about tiling
+ # [5:4] ?????
+ # [3:2] - chroma subsampling
+ # 00 -> broken?
+ # 01 -> broken?
+ # 10 -> 4:2:2
+ # 11 -> 4:4:4
+ # [1:0] - input bpp
+ # 00 -> 8bpp
+ # 01 -> 16bpp?
+ # 10 -> 16bpp?
+ # 11 -> 16bpp?
+ # the last three all produce slightly differnet outputs
+ # so might be 10/12/14/16?????
+ 'flags2', # +0x004
+ 'output_iova', # +0x008
+ 'max_out_sz', # +0x010
+ 'offset_x', # +0x014
+ 'offset_y', # +0x016
+ 'pix_surface_w_2_', # +0x018
+ 'pix_surface_h_2_', # +0x01a
+ 'pix_surface_w', # +0x01c
+ 'pix_surface_h', # +0x01e
+ 'luma_stride', # +0x020
+ 'chroma_stride', # +0x022
+ 'alpha_stride', # +0x024
+ 'unk_pad_0x26_', # +0x026
+ 'luma_iova', # +0x028
+ 'pix_plane0_tileheader_thing_', # +0x030
+ 'chroma_iova', # +0x038
+ 'pix_plane1_tileheader_thing_', # +0x040
+ 'alpha_iova', # +0x048
+ 'pix_plane2_tileheader_thing_', # +0x050
+ 'frame_header_sz', # +0x058
+ 'unk_pad_0x5a_', # +0x05a
+ 'bitstream_version', # +0x05b
+ 'encoder_identifier', # +0x05c
+ 'pix_surface_w_byteswap_', # +0x060
+ 'pix_surface_h_byteswap_', # +0x062
+ 'chroma_format_interlace_mode', # +0x064
+ 'aspect_ratio_frame_rate', # +0x065
+ 'color_primaries', # +0x066
+ 'transfer_characteristic', # +0x067
+ 'matrix_coefficients', # +0x068
+ 'alpha_channel_type', # +0x069
+ 'frame_hdr_reserved14', # +0x06a
+ 'unk_pad_0x6c_', # +0x06c
+ 'deprecated_number_of_slices', # +0x0ec
+ 'log2_desired_slice_size_in_mb', # +0x0ee
+ 'quantization_index', # +0x0ef
+ 'unk_0xf0_', # +0x0f0
+ 'unk_0xf2_', # +0x0f2
+ 'unk_0xf4_', # +0x0f4
+ 'unk_0xfc_', # +0x0fc
+ 'unk_0x100_0_', # +0x100
+ 'unk_0x100_1_', # +0x104
+ 'unk_0x100_2_', # +0x108
+ 'unk_0x100_3_', # +0x10c
+ 'unk_0x110_0_', # +0x110
+ 'unk_0x110_1_', # +0x114
+ 'unk_0x110_2_', # +0x118
+ 'unk_0x110_3_', # +0x11c
+ 'unk_0x110_4_', # +0x120
+ 'unk_0x110_5_', # +0x124
+ 'unk_0x110_6_', # +0x128
+ 'unk_0x110_7_', # +0x12c
+ 'unk_0x110_8_', # +0x130
+ 'unk_0x110_9_', # +0x134
+ 'unk_0x110_10_', # +0x138
+ 'unk_0x110_11_', # +0x13c
+ 'unk_0x110_12_', # +0x140
+ 'unk_0x110_13_', # +0x144
+ 'unk_0x110_14_', # +0x148
+ 'unk_0x110_15_', # +0x14c
+ 'quant_table_sel', # +0x150
+ # upper nibble: quality / table index
+ # lower nibble UNKNOWN!
+ 'unk_pad_0x154_', # +0x154
+])
+ENCODE_NOT_RAW_STRUCT = "<IIQIHHHHHHHHH2sQQQQQQH1sBIHHBBBBBB2s128sHBBHHQIIIIIIIIIIIIIIIIIIIIII44s"
+
+
+class ProResRegs(RegMap):
+ # something reads
+ REG_0x0 = 0x000, Register32
+ MODE = 0x008, Register32 # 4 bits
+ IRQ_ENABLE = 0x00c, Register32 # 2 bits
+ IRQ_STATUS = 0x010, Register32
+
+ ST0 = 0x014, Register32 # interrupt handler reads
+ ST1 = 0x018, Register32 # interrupt handler reads
+ REG_0x1c = 0x01c, Register32 # interrupt handler reads
+ REG_0x38 = 0x038, Register32 # exists, maybe RO
+ REG_0x3c = 0x03c, Register32 # interrupt handler reads
+ REG_0x40 = 0x040, Register32 # exists, maybe RO, looks like 0x44
+ REG_0x44 = 0x044, Register32 # interrupt handler reads
+ REG_0x48 = 0x048, Register32 # exists, maybe RO, looks like 0x44
+ REG_0x4c = 0x04c, Register32 # exists, maybe RO, looks like 0x44
+ REG_0x50 = 0x050, Register32 # exists, maybe RO, looks like 0x44
+ REG_0x54 = 0x054, Register32 # exists, maybe RO, looks like 0x44
+
+ DR_SIZE = 0x100, Register32
+ DR_ADDR_LO = 0x104, Register32
+ DR_ADDR_HI = 0x108, Register32
+ DR_HEAD = 0x10c, Register32 # bit24 is special, something about wrapping around?
+ DR_TAIL = 0x110, Register32
+
+ # This giant block may or may not be touched by tunables
+ # Function is all unknown
+ REG_0x114 = 0x114, Register32 # can set bits 0000FFFF
+ REG_0x118 = 0x118, Register32 # can set bits 07FF07FF
+
+ REG_0x134 = 0x134, Register32 # can set bits 00000003
+
+ REG_0x144 = 0x144, Register32 # can set bits 00000001
+ REG_0x148 = 0x148, Register32 # can set bits 00000001
+
+ REG_0x160 = 0x160, Register32 # can set bits BFFF3FFF
+ REG_0x164 = 0x164, Register32 # can set bits 07FF07FF
+
+ REG_0x170 = 0x170, Register32 # can set bits BFFF3FFF
+ REG_0x174 = 0x174, Register32 # can set bits 07FF07FF
+
+ REG_0x180 = 0x180, Register32 # can set bits BFFF3FFF
+ REG_0x184 = 0x184, Register32 # can set bits 07FF07FF
+
+ REG_0x190 = 0x190, Register32 # can set bits BFFF3FFF
+ REG_0x194 = 0x194, Register32 # can set bits 000000FF
+ REG_0x198 = 0x198, Register32 # RO? init value 07FB066F
+
+ REG_0x1a0 = 0x1a0, Register32 # can set bits BFFF3FFF
+ REG_0x1a4 = 0x1a4, Register32 # can set bits 000000FF
+ REG_0x1a8 = 0x1a8, Register32 # RO? init value 037C03EE
+
+ REG_0x1b0 = 0x1b0, Register32 # can set bits BFFF3FFF
+ REG_0x1b4 = 0x1b4, Register32 # can set bits 000000FF
+ REG_0x1b8 = 0x1b8, Register32 # RO? init value 04E00377
+
+ REG_0x1c0 = 0x1c0, Register32 # can set bits BFFF3FFF
+ REG_0x1c4 = 0x1c4, Register32 # can set bits 000000FF
+ REG_0x1c8 = 0x1c8, Register32 # RO? init value 051C00DA
+
+ REG_0x1d0 = 0x1d0, Register32 # can set bits BFFF3FFF
+ REG_0x1d4 = 0x1d4, Register32 # can set bits 000000FF
+ REG_0x1d8 = 0x1d8, Register32 # can set bits 000000FF
+ REG_0x1dc = 0x1dc, Register32 # can set bits 00FFFFFF
+
+ REG_0x1ec = 0x1ec, Register32 # can set bits FFFFFFFF
+
+ REG_0x270 = 0x270, Register32 # can set bits BFFF3FFF
+ REG_0x274 = 0x274, Register32 # can set bits 07FF07FF
+ REG_0x278 = 0x278, Register32 # can set bits FFFFFFC0
+ REG_0x27c = 0x27c, Register32 # can set bits 000003FF
+ REG_0x280 = 0x280, Register32 # can set bits FFFFFFC0
+ REG_0x284 = 0x284, Register32 # can set bits FFFFFFC0
+ REG_0x28c = 0x28c, Register32 # can set bits FFFFFFC0
+
+ REG_0x290 = 0x290, Register32 # can set bits BFFF3FFF
+ REG_0x294 = 0x294, Register32 # can set bits 000000FF
+ REG_0x298 = 0x298, Register32 # RO? init value 07FB066F
+
+ REG_0x2a0 = 0x2a0, Register32 # can set bits BFFF3FFF
+ REG_0x2a4 = 0x2a4, Register32 # can set bits 000000FF
+ REG_0x2a8 = 0x2a8, Register32 # RO? init value 037C03EE
+
+ REG_0x2b0 = 0x2b0, Register32 # can set bits BFFF3FFF
+ REG_0x2b4 = 0x2b4, Register32 # can set bits 000000FF
+ REG_0x2b8 = 0x2b8, Register32 # RO? init value 04E00377
+
+ REG_0x2c0 = 0x2c0, Register32 # can set bits BFFF3FFF
+ REG_0x2c4 = 0x2c4, Register32 # can set bits 000000FF
+ REG_0x2c8 = 0x2c8, Register32 # RO? init value 051C00DA
+
+ REG_0x2d0 = 0x2d0, Register32 # can set bits FFFFFFFD, CANNOT clear 00000011
+ REG_0x2d4 = 0x2d4, Register32 # can set bits 00000001
+ REG_0x2d8 = 0x2d8, Register32 # can set bits FFFF0007
+ REG_0x2dc = 0x2dc, Register32 # RO? init value 07FB066F
+ REG_0x2e0 = 0x2e0, Register32 # can set bits 07FF07FF
+
+ REG_0x2f0 = 0x2f0, Register32 # can set bits FFFFFFFF
+
+ REG_0x2f8 = 0x2f8, Register32 # can set bits FFFFFFFD, CANNOT clear 00000011
+ REG_0x2fc = 0x2fc, Register32 # can set bits 00000001
+ REG_0x300 = 0x300, Register32 # can set bits FFFF0007
+ REG_0x304 = 0x304, Register32 # RO? init value 037C03EE
+ REG_0x308 = 0x308, Register32 # can set bits 07FF07FF
+
+ REG_0x318 = 0x318, Register32 # can set bits FFFFFFFF
+
+ REG_0x320 = 0x320, Register32 # can set bits FFFFFFFD, CANNOT clear 00000011
+ REG_0x324 = 0x324, Register32 # can set bits 00000001
+ REG_0x328 = 0x328, Register32 # can set bits FFFF0007
+ REG_0x32c = 0x32c, Register32 # RO? init value 04E00377
+ REG_0x330 = 0x330, Register32 # can set bits 07FF07FF
+
+ REG_0x340 = 0x340, Register32 # can set bits FFFFFFFF
+
+ REG_0x350 = 0x350, Register32 # can set bits BFFF3FFF
+ REG_0x354 = 0x354, Register32 # can set bits 07FF07FF
+ REG_0x358 = 0x358, Register32 # can set bits FFFFFFC0
+ REG_0x35c = 0x35c, Register32 # can set bits 000003FF
+ REG_0x360 = 0x360, Register32 # can set bits FFFFFFC0
+ REG_0x364 = 0x364, Register32 # can set bits FFFFFFC0
+ REG_0x368 = 0x368, Register32 # can set bits FFFFFFC0
+
+ REG_0x370 = 0x370, Register32 # can set bits BFFF3FFF
+ REG_0x374 = 0x374, Register32 # can set bits 07FF07FF
+
+
+ QUANT_LUMA_EHQ = irange(0x0800, 32, 4), Register32
+ QUANT_LUMA_HQ = irange(0x0880, 32, 4), Register32
+ QUANT_LUMA_NQ = irange(0x0900, 32, 4), Register32
+ QUANT_LUMA_LT = irange(0x0980, 32, 4), Register32
+ QUANT_LUMA_PROXY = irange(0x0A00, 32, 4), Register32
+ QUANT_CHROMA_EHQ = irange(0x1000, 32, 4), Register32
+ QUANT_CHROMA_HQ = irange(0x1080, 32, 4), Register32
+ QUANT_CHROMA_NQ = irange(0x1100, 32, 4), Register32
+ QUANT_CHROMA_LT = irange(0x1180, 32, 4), Register32
+ QUANT_CHROMA_PROXY = irange(0x1200, 32, 4), Register32
+
+ # wtf, writing to this doesn't actually work? do we have to enable it?
+ DC_QUANT_SCALE = irange(0x1800, 112, 4), Register32
+
+ REG_0x19c0 = 0x19c0, Register32 # unknown, all 1s, RO?
+ REG_0x19c4 = 0x19c4, Register32 # unknown, all 1s, RO?
+ REG_0x19c8 = 0x19c8, Register32 # unknown, all 1s, RO?
+ REG_0x19cc = 0x19cc, Register32 # unknown, all 1s, RO?
+ REG_0x19d0 = 0x19d0, Register32 # unknown, all 1s, RO?
+ REG_0x19d4 = 0x19d4, Register32 # unknown, all 1s, RO?
+ REG_0x19d8 = 0x19d8, Register32 # unknown, all 1s, RO?
+ REG_0x19dc = 0x19dc, Register32 # unknown, can set bits 00000001
+
+ # Unknown, inits to 0x12345678, can R/W
+ REG_0x1A00 = 0x1a00, Register32
diff --git a/tools/proxyclient/m1n1/hw/scaler.py b/tools/proxyclient/m1n1/hw/scaler.py
new file mode 100644
index 0000000..714f366
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/scaler.py
@@ -0,0 +1,212 @@
+# SPDX-License-Identifier: MIT
+from ..utils import *
+from enum import IntEnum
+
+
+### NOTE: This is "MSR10j" (M1 Max), and there definitely *ARE* differences from M1
+
+class R_IRQS(Register32):
+ DONE = 0
+ DBGSTS_ERROR = 1
+ READ_ERROR = 3
+ # This doesn't actually trigger on bad IOVAs?
+ WRITE_ERROR_MAYBE = 4
+ DECOMPRESSION_ERROR = 9
+ CONTEXT_SWITCH = 10
+ _BIT11 = 11
+ AXI_ERROR = 12
+ _BIT13 = 13
+
+
+class E_ROTATE(IntEnum):
+ # clockwise rotations
+ ROT_0 = 0
+ ROT_90 = 1
+ ROT_180 = 2
+ ROT_270 = 3
+
+
+class R_FLIP_ROTATE(Register32):
+ ROTATE = 1, 0, E_ROTATE
+ FLIP_UPDOWN = 2
+ FLIP_LEFTRIGHT = 3
+
+
+class R_SCALE_FLAGS(Register32):
+ EN = 0
+ MAKE_THE_OUTPUT_YELLOW = 1
+ # only when bit1 is set, only on H scaling
+ MAKE_A_BLUE_LINE_APPEAR = 4
+
+
+class ScalerMainRegs(RegMap):
+ # on startup 1 will be written followed by 0
+ # but it's not clear if that actually does anything
+ HW_VERSION = 0x00000, Register32
+ # bit0 = normal, bit1 = apiodma related
+ # if things are reset here, reading all other regs will *HANG*
+ RESET = 0x00004, Register32
+ # can set 0x1f00
+ RESET_APIODMA_RELATED = 0x00008, Register32
+ IS_RUNNING = 0x0000c, Register32
+ # writable, can set to 0xfff
+ REG_0x10 = 0x00010, Register32
+ REGISTER_FIFO_AVAILABILITY = 0x00014, Register32
+ # XNU sets 0x121b, we can at most set 0x3e1b
+ IRQ_ENABLE = 0x00018, R_IRQS
+ MSR_GLBL_IRQSTS = 0x0001c, R_IRQS
+ FRAME_COUNT = 0x00020, Register32
+
+ # can set 7
+ REG_0x58 = 0x00058, Register32
+
+ # can set 0xffff
+ REG_0x74 = 0x00074, Register32
+
+ # 1, or 3 if readonly??
+ START = 0x00080, Register32
+ # can set all bits
+ REG_0x84 = 0x00084, Register32
+ # can set all bits
+ REG_0x88 = 0x00088, Register32
+ # can set 0x8000ffff
+ REG_0x8c = 0x0008c, Register32
+
+ # can set all bits
+ REG_0x98 = 0x00098, Register32
+ # 0x3f3d?
+ MSR_CTRL_DBGSTS = 0x0009c, Register32
+ # can set 3
+ REG_0xa0 = 0x000a0, Register32
+ PROFILING_RELATED = 0x000a4, Register32
+
+ # Can set bits 0/1/2
+ # Does something breaking horizontal scaling
+ # bit2 seems to affect alpha output
+ PIXEL_AVERAGING = 0x000e4, Register32
+
+ TRANSFORM_ID = 0x00110, Register32
+
+ RDMA_THING0 = 0x00180, Register32
+ RDMA_THING1 = 0x00184, Register32
+ RDMA_THING2 = 0x00188, Register32
+ RDMA_THING3 = 0x0018c, Register32
+ RDMA_THING4 = 0x00190, Register32
+
+ # there's probably another source plane existing?
+ SRC_PLANE0_LO = 0x00198, Register32
+ SRC_PLANE0_HI = 0x0019c, Register32
+ SRC_PLANE1_LO = 0x001a0, Register32
+ SRC_PLANE1_HI = 0x001a4, Register32
+ SRC_PLANE2_LO = 0x001a8, Register32
+ SRC_PLANE2_HI = 0x001ac, Register32
+
+ SRC_PLANE0_COMPRESSEDTHING_LO = 0x001b8, Register32
+ SRC_PLANE0_COMPRESSEDTHING_HI = 0x001bc, Register32
+ SRC_PLANE1_COMPRESSEDTHING_LO = 0x001c0, Register32
+ SRC_PLANE1_COMPRESSEDTHING_HI = 0x001c4, Register32
+ SRC_PLANE2_COMPRESSEDTHING_LO = 0x001c8, Register32
+ SRC_PLANE2_COMPRESSEDTHING_HI = 0x001cc, Register32
+
+ SRC_PLANE0_STRIDE = 0x001d8, Register32
+ SRC_PLANE1_STRIDE = 0x001dc, Register32
+ SRC_PLANE2_STRIDE = 0x001e0, Register32
+
+ # seems to be in "pixels"
+ SRC_PLANE0_OFFSET = 0x001e8, Register32
+ SRC_PLANE1_OFFSET = 0x001ec, Register32
+ SRC_PLANE2_OFFSET = 0x001f0, Register32
+
+ SRC_SWIZZLE = 0x001f8, Register32
+ SRC_W = 0x001fc, Register32
+ SRC_H = 0x00200, Register32
+ CACHE_HINTS_THING0 = irange(0x00204, 4, 4), Register32
+ CACHE_HINTS_THING1 = irange(0x00214, 4, 4), Register32
+ TUNABLES_THING0 = irange(0x00224, 4, 4), Register32
+ SRC_SIZE_THING2 = 0x00234, Register32
+ SRC_SIZE_THING3 = 0x00238, Register32
+ SRC_SIZE_THING4 = 0x0023c, Register32
+
+ SRC_SIZE_THING5 = 0x00244, Register32
+ SRC_SIZE_THING6 = 0x00248, Register32
+ SRC_SIZE_THING7 = 0x0024c, Register32
+
+ WDMA_THING0 = 0x00280, Register32
+ WDMA_THING1 = 0x00284, Register32
+ WDMA_THING2 = 0x00288, Register32
+ WDMA_THING3 = 0x0028c, Register32
+ DST_PLANE0_LO = 0x00290, Register32
+ DST_PLANE0_HI = 0x00294, Register32
+ DST_PLANE1_LO = 0x00298, Register32
+ DST_PLANE1_HI = 0x0029c, Register32
+ DST_PLANE2_LO = 0x002a0, Register32
+ DST_PLANE2_HI = 0x002a4, Register32
+ DST_PLANE0_COMPRESSEDTHING_LO = 0x002a8, Register32
+ DST_PLANE0_COMPRESSEDTHING_HI = 0x002ac, Register32
+ DST_PLANE1_COMPRESSEDTHING_LO = 0x002b0, Register32
+ DST_PLANE1_COMPRESSEDTHING_HI = 0x002b4, Register32
+ DST_PLANE2_COMPRESSEDTHING_LO = 0x002b8, Register32
+ DST_PLANE2_COMPRESSEDTHING_HI = 0x002bc, Register32
+ DST_PLANE0_STRIDE = 0x002c0, Register32
+ DST_PLANE1_STRIDE = 0x002c4, Register32
+ DST_PLANE2_STRIDE = 0x002c8, Register32
+ DST_PLANE0_OFFSET = 0x002cc, Register32
+ DST_PLANE1_OFFSET = 0x002d0, Register32
+ DST_PLANE2_OFFSET = 0x002d4, Register32
+ DST_SWIZZLE = 0x002d8, Register32
+ DST_W = 0x002dc, Register32
+ DST_H = 0x002e0, Register32
+ # uhh is there a macos bug with these? last val always overwritten
+ CACHE_HINTS_THING2 = irange(0x002e4, 3, 4), Register32
+ CACHE_HINTS_THING3 = irange(0x002f0, 3, 4), Register32
+ TUNABLES_THING1 = irange(0x002fc, 3, 4), Register32
+ DST_SIZE_THING2 = 0x00308, Register32
+ DST_SIZE_THING3 = 0x0030c, Register32
+ DST_SIZE_THING4 = 0x00310, Register32
+ DST_SIZE_THING5 = 0x00314, Register32
+ DST_SIZE_THING6 = 0x00318, Register32
+ DST_SIZE_THING7 = 0x0031c, Register32
+
+ FLIP_ROTATE = 0x00380, R_FLIP_ROTATE
+
+ # can set bit 0/1
+ # the output obviously changes when this is set
+ PSEUDO_LINEAR_SCALING = 0x00480, Register32
+
+ SCALE_V_FLAGS = 0x01000, R_SCALE_FLAGS
+ # No idea what a DDA is? Q1.22 or U1.22 (23 bits total)
+ # Also macOS doesn't touch a bunch of the V ones (uses H instead???)
+ SCALE_V_DDA_THING0 = 0x01004, Register32
+ SCALE_V_DDA_THING1 = 0x01008, Register32
+ # Q4.22 or U4.22 (26 bits total)
+ SCALE_V_RATIO_0 = 0x0100c, Register32
+ SCALE_V_RATIO_1 = 0x01010, Register32
+ SCALE_V_RATIO_2 = 0x01014, Register32
+ SCALE_V_RATIO_3 = 0x01018, Register32
+ SCALE_V_DDA_THING2 = 0x0101c, Register32
+ SCALE_V_RATIO_4 = 0x01020, Register32
+ SCALE_V_RATIO_5 = 0x01024, Register32
+
+ # 9 taps, 32 phases polyphase resampling filter
+ # Q4.12 (16-bit total) fixed point filter coeffs
+ # packed into 32-bit registers, 3 sets of filters total (chroma/luma/alpha??)
+ # exact ordering and arithmetic performed not yet clear
+ SCALE_FILTER_V_BLOCK0 = irange(0x01400, 9 * 32, 4), Register32
+ SCALE_FILTER_V_BLOCK1 = irange(0x01c00, 9 * 32 // 2, 4), Register32
+
+ SCALE_H_FLAGS = 0x02000, R_SCALE_FLAGS
+ # No idea what a DDA is? Q1.22 or U1.22 (23 bits total)
+ SCALE_H_DDA_THING0 = 0x02004, Register32
+ SCALE_H_DDA_THING1 = 0x02008, Register32
+ # Q4.22 or U4.22 (26 bits total)
+ SCALE_H_RATIO_0 = 0x0200c, Register32
+ SCALE_H_RATIO_1 = 0x02010, Register32
+ SCALE_H_RATIO_2 = 0x02014, Register32
+ SCALE_H_RATIO_3 = 0x02018, Register32
+ SCALE_H_DDA_THING2 = 0x0201c, Register32
+ SCALE_H_RATIO_4 = 0x02020, Register32
+ SCALE_H_RATIO_5 = 0x02024, Register32
+
+ # 15 taps, 32 phases polyphase resampling filter
+ SCALE_FILTER_H_BLOCK0 = irange(0x02400, 15 * 32, 4), Register32
+ SCALE_FILTER_H_BLOCK1 = irange(0x02c00, 15 * 32 // 2, 4), Register32
diff --git a/tools/proxyclient/m1n1/hw/sep.py b/tools/proxyclient/m1n1/hw/sep.py
new file mode 100644
index 0000000..f206a71
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/sep.py
@@ -0,0 +1,169 @@
+# SPDX-License-Identifier: MIT
+import struct
+from collections import defaultdict, deque
+from enum import IntEnum
+
+from ..trace.asc import ASCRegs
+from ..utils import *
+
+
+class BootRomMsg(IntEnum):
+ GET_STATUS = 2
+ BOOT_TZ0 = 5
+ BOOT_IMG4 = 6
+ SET_SHMEM = 0x18
+
+
+class BootRomStatus(IntEnum):
+ STATUS_OK = 0x66
+ STATUS_BOOT_TZ0_DONE = 0x69
+ STATUS_BOOT_IMG4_DONE = 0x6A
+ STATUS_BOOT_UNK_DONE = 0xD2
+
+
+class SEPMessage(Register64):
+ EP = 7, 0
+ TAG = 15, 8
+ TYPE = 23, 16
+ PARAM = 31, 24
+ DATA = 63, 32
+
+
+# TODO: make this class actually own the shared memory instead of just
+# generating a static buffer if we actually need to read/write to
+# individual items inside the shmem buffer
+class SEPShMem:
+ def __init__(self):
+ self.items = []
+ self.offset = 0x4000
+
+ def add_item(self, name, data, min_size=0):
+ sz = align_up(len(data) + 4, 0x4000)
+ sz = max(sz, min_size)
+ self.items.append((name, self.offset, sz, struct.pack("<I", len(data)) + data))
+ self.offset += sz
+
+ def finalize(self):
+ bfr = bytearray(b"\x00" * self.offset)
+ for i, (name, offset, sz, data) in enumerate(self.items):
+ bfr[i * 16 : i * 16 + 12] = struct.pack("<4sII", name, sz, offset)
+ bfr[offset : offset + len(data)] = data
+
+ cnt = len(self.items)
+ bfr[cnt * 16 : cnt * 16 + 4] = b"llun" # null
+
+ return bfr
+
+
+class SEP:
+ SHMEM_IOVA = 0xBEEF0000
+ FW_IOVA = 0xDEAD0000
+
+ def __init__(self, proxy, iface, utils):
+ self.i = iface
+ self.p = proxy
+ self.u = utils
+
+ self.sep_base = self.u.adt["/arm-io/sep"].get_reg(0)[0]
+ self.dart_base = self.u.adt["/arm-io/dart-sep"].get_reg(0)[0]
+
+ self.asc = ASCRegs(self.u, self.sep_base)
+
+ self.dart_handle = self.p.dart_init(self.dart_base, 0)
+
+ self.epnum2name = {}
+ self.epname2num = {}
+ self.msgs = defaultdict(deque)
+
+ def map_sepfw(self):
+ sepfw_addr, sepfw_size = self.u.adt["/chosen/memory-map"].SEPFW
+ self.p.dart_map(self.dart_handle, self.FW_IOVA, sepfw_addr, sepfw_size)
+
+ def unmap_sepfw(self):
+ _, sepfw_size = self.u.adt["/chosen/memory-map"].SEPFW
+ self.p.dart_unmap(self.dart_handle, self.FW_IOVA, sepfw_size)
+
+ def create_shmem(self):
+ shmem = SEPShMem()
+
+ # PNIC - panic buffer
+ shmem.add_item(b"CINP", b"\x00", 0x8000)
+
+ # ALPO / SIPS - unknown img4-like blobs from the ADT
+ addr, sz = self.u.adt["/chosen/boot-object-manifests"].lpol
+ shmem.add_item(b"OPLA", self.i.readmem(addr, sz))
+ addr, sz = self.u.adt["/chosen/boot-object-manifests"].ibot
+ shmem.add_item(b"IPIS", self.i.readmem(addr, sz))
+
+ bfr = shmem.finalize()
+ sz = align_up(len(bfr), 0x4000)
+ self.shmem = self.u.heap.memalign(0x4000, 0x30000)
+ self.i.writemem(self.shmem, bfr)
+ self.p.dart_map(self.dart_handle, self.SHMEM_IOVA, self.shmem, 0x30000)
+
+ def boot(self):
+ self.create_shmem()
+ self.map_sepfw()
+
+ self.send_msg(SEPMessage(EP=0xFF, TYPE=BootRomMsg.GET_STATUS))
+ self.expect_msg(0xFF, BootRomStatus.STATUS_OK)
+
+ self.send_msg(SEPMessage(EP=0xFF, TYPE=BootRomMsg.BOOT_TZ0))
+ self.expect_msg(0xFF, BootRomStatus.STATUS_BOOT_TZ0_DONE)
+ self.expect_msg(0xFF, BootRomStatus.STATUS_BOOT_UNK_DONE)
+
+ self.send_msg(SEPMessage(EP=0xFF, TYPE=BootRomMsg.GET_STATUS))
+ self.expect_msg(0xFF, BootRomStatus.STATUS_OK)
+
+ self.send_msg(
+ SEPMessage(EP=0xFF, TYPE=BootRomMsg.BOOT_IMG4, DATA=self.FW_IOVA >> 0xC)
+ )
+ self.send_msg(
+ SEPMessage(EP=0xFE, TYPE=BootRomMsg.SET_SHMEM, DATA=self.SHMEM_IOVA >> 0xC)
+ )
+
+ self.expect_msg(0xFF, BootRomStatus.STATUS_BOOT_IMG4_DONE)
+
+ self.unmap_sepfw()
+
+ def expect_msg(self, ep, type):
+ msg = self.recv_msg(ep, block=True)
+ if msg.TYPE != type:
+ raise ValueError(
+ f"Expected type 0x{type:x} but got message with type 0x{msg.TYPE:x}"
+ )
+
+ def send_msg(self, msg):
+ self.asc.INBOX0 = msg.value
+ self.asc.INBOX1 = 0
+
+ def _recv_single_msg(self):
+ msg = SEPMessage(self.asc.OUTBOX0.val)
+ _ = self.asc.OUTBOX1.val
+ return msg
+
+ def _try_recv_msgs(self):
+ while not self.asc.OUTBOX_CTRL.reg.EMPTY:
+ msg = self._recv_single_msg()
+ self.msgs[msg.EP].append(msg)
+ self._handle_ep_discovery()
+
+ def _handle_ep_discovery(self):
+ while len(self.msgs[0xFD]):
+ msg = self.msgs[0xFD].popleft()
+ if msg.TYPE == 0:
+ cs = "".join(
+ [chr((msg.DATA >> (i * 8)) & 0xFF) for i in range(3, -1, -1)]
+ )
+ self.epnum2name[msg.PARAM] = cs
+ self.epname2num[cs] = msg.PARAM
+
+ def recv_msg(self, ep, block=False):
+ self._try_recv_msgs()
+ while block and len(self.msgs[ep]) < 1:
+ self._try_recv_msgs()
+
+ if len(self.msgs[ep]):
+ return self.msgs[ep].popleft()
+ else:
+ return None
diff --git a/tools/proxyclient/m1n1/hw/spi.py b/tools/proxyclient/m1n1/hw/spi.py
new file mode 100644
index 0000000..1fa9be5
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/spi.py
@@ -0,0 +1,147 @@
+# SPDX-License-Identifier: MIT
+from ..utils import *
+
+__all__ = ["SPIRegs"]
+
+class R_CTRL(Register32):
+ RX_FIFO_RESET = 3
+ TX_FIFO_RESET = 2
+ RUN = 0
+
+class R_CFG(Register32):
+ # impl: 002fb1e6
+ IE_TX_COMPLETE = 21
+ b19 = 19
+ FIFO_THRESH = 18, 17
+ # 0 = 8 bytes
+ # 1 = 4 bytes
+ # 2 = 1 byte
+ # 3 = disabled
+ WORD_SIZE = 16, 15
+ # 0 = 8bit
+ # 1 = 16bit
+ # 2 = 32bit
+ LSB_FIRST = 13
+ b12 = 12
+ IE_RX_THRESH = 8
+ IE_RX_COMPLETE = 7
+ MODE = 6, 5
+ # 0 = polled
+ # 1 = irq
+ CPOL = 2
+ CPHA = 1
+
+class R_STATUS(Register32):
+ TX_COMPLETE = 22
+ TXRX_THRESH = 1 # updated if MODE == 1
+ RX_COMPLETE = 0
+
+class R_PIN(Register32):
+ CS = 1
+ KEEP_MOSI = 0
+
+class R_CLKDIV(Register32):
+ DIVIDER = 10, 0 # SPI freq = CLK / (DIVIDER + 1)
+
+class R_INTER_DELAY(Register32):
+ DELAY = 15, 0
+
+class R_FIFOSTAT(Register32):
+ LEVEL_RX = 31, 24
+ RX_EMPTY = 20
+ LEVEL_TX = 15, 8
+ TX_FULL = 4
+
+class R_IRQ_XFER(Register32):
+ TX_XFER_DONE = 1
+ RX_XFER_DONE = 0
+
+class R_IRQ_FIFO(Register32):
+ TX_OVERFLOW = 17
+ RX_UNDERRUN = 16
+ TX_EMPTY = 9
+ RX_FULL = 8
+ TX_THRESH = 5
+ RX_THRESH = 4
+
+class R_XFSTATUS(Register32):
+ SR_FULL = 26
+ SHIFTING = 20
+ STATE = 17, 16
+ UNK = 0
+
+class R_DIVSTATUS(Register32):
+ COUNT2 = 31, 16
+ COUNT1 = 15, 0
+
+class R_SHIFTCFG(Register32):
+ OVERRIDE_CS = 24
+ BITS = 21, 16
+ RX_ENABLE = 11
+ TX_ENABLE = 10
+ CS_AS_DATA = 9
+ AND_CLK_DATA = 8
+ #? = 2 # needs to be 1 for RX to not break
+ CS_ENABLE = 1
+ CLK_ENABLE = 0
+
+class R_PINCFG(Register32):
+ MOSI_INIT_VAL = 10
+ CS_INIT_VAL = 9
+ CLK_INIT_VAL = 8
+ KEEP_MOSI = 2
+ KEEP_CS = 1
+ KEEP_CLK = 0
+
+class R_DELAY(Register32):
+ DELAY = 31, 16
+ MOSI_VAL = 12
+ CS_VAL = 10
+ SCK_VAL = 8
+ SET_MOSI = 6
+ SET_CS = 5
+ SET_SCK = 4
+ NO_INTERBYTE = 1
+ ENABLE = 0
+
+class R_SCKCFG(Register32):
+ PERIOD = 31, 16
+ PHASE1 = 9
+ PHASE0 = 8
+ RESET_TO_IDLE = 4
+
+class R_SCKPHASES(Register32):
+ PHASE1_START = 31, 16
+ PHASE0_START = 15, 0
+
+class SPIRegs(RegMap):
+ CTRL = 0x00, R_CTRL
+ CFG = 0x04, R_CFG
+ STATUS = 0x08, R_STATUS
+ PIN = 0x0C, R_PIN
+ TXDATA = 0x10, Register32
+ RXDATA = 0x20, Register32
+ CLKDIV = 0x30, R_CLKDIV
+ RXCNT = 0x34, Register32
+ INTER_DELAY = 0x38, R_INTER_DELAY
+ TXCNT = 0x4C, Register32
+ FIFOSTAT = 0x10C, R_FIFOSTAT
+
+ IE_XFER = 0x130, R_IRQ_XFER
+ IF_XFER = 0x134, R_IRQ_XFER
+ IE_FIFO = 0x138, R_IRQ_FIFO
+ IF_FIFO = 0x13c, R_IRQ_FIFO
+
+ SHIFTCFG = 0x150, R_SHIFTCFG
+ PINCFG = 0x154, R_PINCFG
+
+ DELAY_PRE = 0x160, R_DELAY
+ SCKCFG = 0x164, R_SCKCFG
+ DELAY_POST = 0x168, R_DELAY
+
+ SCKPHASES = 0x180, R_SCKPHASES
+
+ UNK_PHASE = 0x18c, Register32 # probably MISO sample point
+
+ XFSTATUS = 0x1c0, R_XFSTATUS
+ DIVSTATUS = 0x1e0, R_DIVSTATUS
diff --git a/tools/proxyclient/m1n1/hw/spmi.py b/tools/proxyclient/m1n1/hw/spmi.py
new file mode 100644
index 0000000..a312f61
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/spmi.py
@@ -0,0 +1,98 @@
+# SPDX-License-Identifier: MIT
+import struct
+
+from ..utils import *
+
+__all__ = ["SPMI"]
+
+CMD_EXT_WRITE = 0x00
+CMD_EXT_READ = 0x20
+CMD_EXT_WRITEL = 0x30
+CMD_EXT_READL = 0x38
+CMD_WRITE = 0x40
+CMD_READ = 0x60
+CMD_ZERO_WRITE = 0x80
+
+class R_CMD(Register32):
+ REG = 31, 16
+ ACTIVE = 15
+ SLAVE_ID = 14, 8
+ CMD = 7, 0
+
+class R_STATUS(Register32):
+ RX_EMPTY = 24
+ RX_COUNT = 23, 16
+ TX_EMPTY = 8
+ TX_COUNT = 7, 0
+
+class SPMIRegs(RegMap):
+ STATUS = 0x00, R_STATUS
+ CMD = 0x04, R_CMD
+ REPLY = 0x08, Register32
+ IRQ_FLAG = 0x80, Register32
+
+class SPMI:
+ def __init__(self, u, adt_path):
+ self.u = u
+ self.p = u.proxy
+ self.iface = u.iface
+ self.base = u.adt[adt_path].get_reg(0)[0]
+ self.regs = SPMIRegs(u, self.base)
+
+ def read(self, slave, reg, size):
+ while not self.regs.STATUS.reg.RX_EMPTY:
+ print(">", self.regs.REPLY.val)
+
+ self.regs.CMD.reg = R_CMD(REG = reg, ACTIVE=1, SLAVE_ID = slave, CMD = CMD_EXT_READL | (size - 1))
+
+ buf = b""
+
+ left = size + 4
+ while left > 0:
+ while self.regs.STATUS.reg.RX_EMPTY:
+ pass
+ v = self.regs.REPLY.val
+ buf += struct.pack("<I", v)
+ left -= 4
+
+ return buf[4:4+size]
+
+ def write(self, slave, reg, data):
+ while not self.regs.STATUS.reg.RX_EMPTY:
+ self.regs.REPLY.val
+
+ size = len(data)
+ self.regs.CMD.reg = R_CMD(REG = reg, ACTIVE=1, SLAVE_ID = slave, CMD = CMD_EXT_WRITEL | (size - 1))
+
+ while data:
+ blk = (data[:4] + b"\0\0\0")[:4]
+ self.regs.CMD.val = struct.unpack("<I", blk)[0]
+ data = data[4:]
+
+ while self.regs.STATUS.reg.RX_EMPTY:
+ pass
+ return self.regs.REPLY.val
+
+ def read8(self, slave, reg):
+ return struct.unpack("<B", self.read(slave, reg, 1))[0]
+
+ def read16(self, slave, reg):
+ return struct.unpack("<H", self.read(slave, reg, 2))[0]
+
+ def read32(self, slave, reg):
+ return struct.unpack("<I", self.read(slave, reg, 4))[0]
+
+ def read64(self, slave, reg):
+ return struct.unpack("<Q", self.read(slave, reg, 8))[0]
+
+ def write8(self, slave, reg, val):
+ return self.write(slave, reg, struct.pack("<B", val))
+
+ def write16(self, slave, reg, val):
+ return self.write(slave, reg, struct.pack("<H", val))
+
+ def write32(self, slave, reg, val):
+ return self.write(slave, reg, struct.pack("<I", val))
+
+ def write64(self, slave, reg, val):
+ return self.write(slave, reg, struct.pack("<Q", val))
diff --git a/tools/proxyclient/m1n1/hw/uat.py b/tools/proxyclient/m1n1/hw/uat.py
new file mode 100644
index 0000000..f5dce9f
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/uat.py
@@ -0,0 +1,571 @@
+"""
+ UAT is just regular ARMv8 pagetables, shared between the gfx-asc firmware
+ and the actual AGX hardware.
+
+ The OS doesn't have direct control over it, TTBR0 and TTBR1 entries are placed at
+ gpu-region-base, one pair for each context. The firmware automatically loads TTBR0/TTBR1
+ on boot and whenever the context changes.
+"""
+
+
+import struct
+from ..fw.agx.handoff import GFXHandoff
+from ..utils import *
+from ..malloc import Heap
+from enum import IntEnum
+import traceback
+
+__all__ = []
+
+class MemoryAttr(IntEnum):
+ # ff = Normal, Outer Writeback RW, Inner Writeback RW
+ Normal = 0 # Only accessed by the gfx-asc coprocessor
+ # 00 = Device nGnRnE
+ Device = 1
+ # f4 = Normal, Outer Writeback RW, Inner NC
+ Shared = 2 # Probally Outer-shareable. Shared with either the main cpu or AGX hardware
+ # 4f = Normal, Outer NC, Inner Writeback RW
+ UNK3 = 3
+ # 00 = Device nGnRnE
+ UNK4 = 4
+ # ff = Normal, Outer Writeback RW, Inner Writeback RW
+ UNK5 = 5
+ # 00 = Device nGnRnE
+ UNK6 = 6
+ # 00 = Device nGnRnE
+ UNK7 = 7
+
+
+class TTBR(Register64):
+ ASID = 63, 48
+ BADDR = 47, 1
+ VALID = 0
+
+ def valid(self):
+ return self.VALID == 1
+
+ def offset(self):
+ return self.BADDR << 1
+
+ def set_offset(self, offset):
+ self.BADDR = offset >> 1
+
+ def describe(self):
+ return f"{self.offset():x} [ASID={self.ASID}, VALID={self.VALID}]"
+
+class PTE(Register64):
+ OFFSET = 47, 14
+ UNK0 = 10 # probally an ownership flag, seems to be 1 for FW created PTEs and 0 for OS PTEs
+ TYPE = 1
+ VALID = 0
+
+ def valid(self):
+ return self.VALID == 1 and self.TYPE == 1
+
+ def offset(self):
+ return self.OFFSET << 14
+
+ def set_offset(self, offset):
+ self.OFFSET = offset >> 14
+
+ def describe(self):
+ if not self.valid():
+ return f"<invalid> [{int(self)}:x]"
+ return f"{self.offset():x}, UNK={self.UNK0}"
+
+class Page_PTE(Register64):
+ OS = 55 # Owned by host os or firmware
+ UXN = 54
+ PXN = 53
+ OFFSET = 47, 14
+ nG = 11 # global or local TLB caching
+ AF = 10
+ SH = 9, 8
+ AP = 7, 6
+ AttrIndex = 4, 2
+ TYPE = 1
+ VALID = 0
+
+ def valid(self):
+ return self.VALID == 1 and self.TYPE == 1
+
+ def offset(self):
+ return self.OFFSET << 14
+
+ def set_offset(self, offset):
+ self.OFFSET = offset >> 14
+
+ def access_fw(self, gl=False):
+ if not self.OS:
+ return [[
+ ["--", "--", "--", "--"],
+ ["--", "RW", "--", "RW"],
+ ["--", "RX", "--", "--"],
+ ["RX", "R-", "--", "R-"],
+ ], [
+ ["--", "--", "--", "RW"],
+ ["--", "--", "--", "RW"],
+ ["RX", "--", "--", "R-"],
+ ["RX", "RW", "--", "R-"],
+ ]][gl][self.AP][(self.UXN << 1) | self.PXN]
+ else:
+ return [
+ ["--", "R-", "-?", "RW"],
+ ["R-", "--", "RW", "RW"],
+ ["--", "--", "--", "--"],
+ ["--", "--", "--", "--"],
+ ][self.AP][(self.UXN << 1) | self.PXN]
+
+ def access_gpu(self):
+ if not self.OS:
+ return "--"
+
+ return [
+ ["--", "R-", "-W", "RW"],
+ ["--", "--", "--", "R-"],
+ ["R-", "-W", "RW", "--"],
+ ["--", "--", "--", "--"],
+ ][self.AP][(self.UXN << 1) | self.PXN]
+
+ def describe(self):
+ if not self.valid():
+ return f"<invalid> [{int(self)}:x]"
+
+ return (
+ f"{self.offset():x} [GPU={self.access_gpu()}, EL1={self.access_fw(0)}, GL1={self.access_fw(1)}, " +
+ f"perm={self.OS}{self.AP:02b}{self.UXN}{self.PXN}, " +
+ f"{MemoryAttr(self.AttrIndex).name}, {['Global', 'Local'][self.nG]}, " +
+ f"Owner={['FW', 'OS'][self.OS]}, AF={self.AF}, SH={self.SH}] ({self.value:#x})"
+ )
+
+class UatAccessor(Reloadable):
+ def __init__(self, uat, ctx=0):
+ self.uat = uat
+ self.ctx = ctx
+
+ def translate(self, addr, width):
+ paddr, _ = self.uat.iotranslate(self.ctx, addr, width)[0]
+ if paddr is None:
+ raise Exception(f"UAT Failed to translate {addr:#x}")
+ return paddr
+
+ def read(self, addr, width):
+ return self.uat.u.read(self.translate(addr, width), width)
+ def read8(self, addr):
+ return self.uat.p.read8(self.translate(addr, 1))
+ def read16(self, addr):
+ return self.uat.p.read16(self.translate(addr, 2))
+ def read32(self, addr):
+ return self.uat.p.read32(self.translate(addr, 4))
+ def read64(self, addr):
+ return self.uat.p.read64(self.translate(addr, 8))
+
+ def write(self, addr, data, width):
+ self.uat.u.write(self.translate(addr, width), data, width)
+ def write8(self, addr, data):
+ self.uat.p.write8(self.translate(addr, 1), daat)
+ def write16(self, addr, data):
+ self.uat.p.write6(self.translate(addr, 2), data)
+ def write32(self, addr, data):
+ self.uat.p.write32(self.translate(addr, 4), data)
+ def write64(self, addr, data):
+ self.uat.p.write64(self.translate(addr, 8), data)
+
+class UatStream(Reloadable):
+ CACHE_SIZE = 0x1000
+
+ def __init__(self, uat, ctx, addr, recurse=True):
+ self.uat = uat
+ self.ctx = ctx
+ self.pos = addr
+ self.cache = None
+ self.meta_fn = None
+ self.recurse = recurse
+
+ def to_accessor(self):
+ return UatAccessor(self.uat, self.ctx)
+
+ def read(self, size):
+ assert size >= 0
+
+ data = b""
+ if self.cache:
+ data = self.cache[:size]
+ cached = len(self.cache)
+ self.pos += min(cached, size)
+ if cached > size:
+ self.cache = self.cache[size:]
+ return data
+ self.cache = None
+ if cached == size:
+ return data
+
+ size -= cached
+
+ # align any cache overreads to the next page boundary
+ remaining_in_page = self.uat.PAGE_SIZE - (self.pos % self.uat.PAGE_SIZE)
+ to_cache = min(remaining_in_page, self.CACHE_SIZE)
+
+ try:
+ self.cache = self.uat.ioread(self.ctx, self.pos, max(size, to_cache))
+ except:
+ traceback.print_exc()
+ raise
+ return data + self.read(size)
+
+ def readable(self):
+ return True
+
+ def write(self, bytes):
+ self.uat.iowrite(self.ctx, self.pos, bytes)
+ self.pos += len(bytes)
+ self.cache = None
+ return len(bytes)
+
+ def writable(self):
+ return True
+
+ def flush(self):
+ self.cache = None
+
+ def seek(self, n, wherenc=0):
+ self.cache = None
+ if wherenc == 0:
+ self.pos = n
+ elif wherenc == 2:
+ self.pos += n
+
+ def seekable(self):
+ return True
+
+ def tell(self):
+ return self.pos
+
+ def closed(self):
+ return False
+
+
+class UAT(Reloadable):
+ NUM_CONTEXTS = 64
+
+ PAGE_BITS = 14
+ PAGE_SIZE = 1 << PAGE_BITS
+
+ L0_SIZE = 2
+ L0_OFF = 39
+ L1_SIZE = 8
+ L1_OFF = 36
+ L2_OFF = 25
+ L3_OFF = 14
+
+ IDX_BITS = 11
+ Lx_SIZE = (1 << IDX_BITS)
+
+ LEVELS = [
+ (L0_OFF, L0_SIZE, TTBR),
+ (L1_OFF, L1_SIZE, PTE),
+ (L2_OFF, Lx_SIZE, PTE),
+ (L3_OFF, Lx_SIZE, Page_PTE),
+ ]
+
+ def __init__(self, iface, util=None, hv=None):
+ self.iface = iface
+ self.u = util
+ self.p = util.proxy
+ self.hv = hv
+ self.pt_cache = {}
+ self.dirty = set()
+ self.dirty_ranges = {}
+ self.allocator = None
+ self.ttbr = None
+ self.initialized = False
+ self.sgx_dev = self.u.adt["/arm-io/sgx"]
+ self.shared_region = self.sgx_dev.gfx_shared_region_base
+ self.gpu_region = self.sgx_dev.gpu_region_base
+ self.ttbr0_base = self.u.memalign(self.PAGE_SIZE, self.PAGE_SIZE)
+ self.ttbr1_base = self.sgx_dev.gfx_shared_region_base
+ self.handoff = GFXHandoff(self.u)
+
+ self.VA_MASK = 0
+ for (off, size, _) in self.LEVELS:
+ self.VA_MASK |= (size - 1) << off
+ self.VA_MASK |= self.PAGE_SIZE - 1
+
+
+ def set_l0(self, ctx, off, base, asid=0):
+ ttbr = TTBR(BADDR = base >> 1, ASID = asid, VALID=(base != 0))
+ print(f"[UAT] Set L0 ctx={ctx} off={off:#x} base={base:#x} asid={asid} ({ttbr})")
+ self.write_pte(self.gpu_region + ctx * 16, off, 2, ttbr)
+
+ def ioread(self, ctx, base, size):
+ if size == 0:
+ return b""
+
+ ranges = self.iotranslate(ctx, base, size)
+
+ iova = base
+ data = []
+ for addr, size in ranges:
+ if addr is None:
+ raise Exception(f"Unmapped page at iova {ctx}:{iova:#x}")
+ data.append(self.iface.readmem(addr, size))
+ iova += size
+
+ return b"".join(data)
+
+ def iowrite(self, ctx, base, data):
+ if len(data) == 0:
+ return
+
+ ranges = self.iotranslate(ctx, base, len(data))
+
+ iova = base
+ p = 0
+ for addr, size in ranges:
+ if addr is None:
+ raise Exception(f"Unmapped page at iova {ctx}:{iova:#x}")
+ self.iface.writemem(addr, data[p:p + size])
+ p += size
+ iova += size
+
+ # A stream interface that can be used for random access by Construct
+ def iostream(self, ctx, base, recurse=True):
+ return UatStream(self, ctx, base, recurse)
+
+ # A read/write register interface like proxy/utils objects that can be used by RegMap
+ def ioaccessor(self, ctx):
+ return UatAccessor(self, ctx)
+
+ def iomap(self, ctx, addr, size, **flags):
+ iova = self.allocator.malloc(size)
+
+ self.iomap_at(ctx, iova, addr, size, **flags)
+ self.flush_dirty()
+ return iova
+
+ def iomap_at(self, ctx, iova, addr, size, **flags):
+ if size == 0:
+ return
+
+ if addr & (self.PAGE_SIZE - 1):
+ raise Exception(f"Unaligned PA {addr:#x}")
+
+ if iova & (self.PAGE_SIZE - 1):
+ raise Exception(f"Unaligned IOVA {iova:#x}")
+
+ self.init()
+
+ map_flags = {'OS': 1, 'AttrIndex': MemoryAttr.Normal, 'VALID': 1, 'TYPE': 1, 'AP': 1, 'AF': 1, 'UXN': 1}
+ map_flags.update(flags)
+
+ start_page = align_down(iova, self.PAGE_SIZE)
+ end = iova + size
+ end_page = align_up(end, self.PAGE_SIZE)
+
+ for page in range(start_page, end_page, self.PAGE_SIZE):
+ table_addr = self.gpu_region + ctx * 16
+ for (offset, size, ptecls) in self.LEVELS:
+ if ptecls is Page_PTE:
+ pte = Page_PTE(**map_flags)
+ pte.set_offset(addr)
+ self.write_pte(table_addr, page >> offset, size, pte)
+ addr += self.PAGE_SIZE
+ else:
+ pte = self.fetch_pte(table_addr, page >> offset, size, ptecls)
+ if not pte.valid():
+ table = self.u.memalign(self.PAGE_SIZE, self.PAGE_SIZE)
+ self.p.memset32(table, 0, self.PAGE_SIZE)
+ pte.set_offset(table)
+ if ptecls is not TTBR:
+ pte.VALID = 1
+ pte.TYPE = 1
+ #pte.UNK0 = 1
+ self.write_pte(table_addr, page >> offset, size, pte)
+ table_addr = pte.offset()
+
+ self.dirty_ranges.setdefault(ctx, []).append((start_page, end_page - start_page))
+ #self.flush_dirty()
+
+
+ def fetch_pte(self, offset, idx, size, ptecls):
+ idx = idx & (size - 1)
+
+ cached, table = self.get_pt(offset, size=size)
+ pte = ptecls(table[idx])
+ if not pte.valid() and cached:
+ self.flush_dirty()
+ cached, table = self.get_pt(offset, size=size, uncached=True)
+ pte = ptecls(table[idx])
+
+ return pte
+
+ def write_pte(self, offset, idx, size, pte):
+ idx = idx & (size - 1)
+
+ cached, table = self.get_pt(offset, size=size)
+
+ table[idx] = pte.value
+ self.dirty.add(offset)
+
+ def iotranslate(self, ctx, start, size):
+ if size == 0:
+ return []
+
+ start = start & self.VA_MASK
+
+ start_page = align_down(start, self.PAGE_SIZE)
+ start_off = start - start_page
+ end = start + size
+ end_page = align_up(end, self.PAGE_SIZE)
+ end_size = end - (end_page - self.PAGE_SIZE)
+
+ pages = []
+
+ for page in range(start_page, end_page, self.PAGE_SIZE):
+ table_addr = self.gpu_region + ctx * 16
+ for (offset, size, ptecls) in self.LEVELS:
+ pte = self.fetch_pte(table_addr, page >> offset, size, ptecls)
+ if not pte.valid():
+ break
+ table_addr = pte.offset()
+
+ if pte.valid():
+ pages.append(pte.offset())
+ else:
+ pages.append(None)
+
+ ranges = []
+
+ for page in pages:
+ if not ranges:
+ ranges.append((page, self.PAGE_SIZE))
+ continue
+ laddr, lsize = ranges[-1]
+ if ((page is None and laddr is None) or
+ (page is not None and laddr == (page - lsize))):
+ ranges[-1] = laddr, lsize + self.PAGE_SIZE
+ else:
+ ranges.append((page, self.PAGE_SIZE))
+
+ ranges[-1] = (ranges[-1][0], ranges[-1][1] - self.PAGE_SIZE + end_size)
+
+ if start_off:
+ ranges[0] = (ranges[0][0] + start_off if ranges[0][0] else None,
+ ranges[0][1] - start_off)
+
+ return ranges
+
+ def get_pt(self, addr, size=None, uncached=False):
+ if size is None:
+ size = self.Lx_SIZE
+ cached = True
+ if addr not in self.pt_cache or uncached:
+ cached = False
+ self.pt_cache[addr] = list(
+ struct.unpack(f"<{size}Q", self.iface.readmem(addr, size * 8)))
+
+ return cached, self.pt_cache[addr]
+
+ def flush_pt(self, addr):
+ assert addr in self.pt_cache
+ table = self.pt_cache[addr]
+ self.iface.writemem(addr, struct.pack(f"<{len(table)}Q", *table))
+ #self.p.dc_civac(addr, 0x4000)
+
+ def flush_dirty(self):
+ inval = False
+
+ for page in self.dirty:
+ self.flush_pt(page)
+ inval = True
+
+ self.dirty.clear()
+
+ for ctx, ranges in self.dirty_ranges.items():
+ asid = ctx << 48
+ self.u.inst("tlbi aside1os, x0", asid)
+
+ def invalidate_cache(self):
+ self.pt_cache = {}
+
+ def recurse_level(self, level, base, table, page_fn=None, table_fn=None):
+ def extend(addr):
+ if addr >= 0x80_00000000:
+ addr |= 0xf00_00000000
+ return addr
+
+ offset, size, ptecls = self.LEVELS[level]
+
+ cached, tbl = self.get_pt(table, size)
+ sparse = False
+ for i, pte in enumerate(tbl):
+ pte = ptecls(pte)
+ if not pte.valid():
+ sparse = True
+ continue
+
+ range_size = 1 << offset
+ start = extend(base + i * range_size)
+ end = start + range_size - 1
+
+ if level + 1 == len(self.LEVELS):
+ if page_fn:
+ page_fn(start, end, i, pte, level, sparse=sparse)
+ else:
+ if table_fn:
+ table_fn(start, end, i, pte, level, sparse=sparse)
+ self.recurse_level(level + 1, start, pte.offset(), page_fn, table_fn)
+
+ sparse = False
+
+ def foreach_page(self, ctx, page_fn):
+ self.recurse_level(0, 0, self.gpu_region + ctx * 16, page_fn)
+
+ def foreach_table(self, ctx, table_fn):
+ self.recurse_level(0, 0, self.gpu_region + ctx * 16, table_fn=table_fn)
+
+ def init(self):
+ if self.initialized:
+ return
+
+ print("[UAT] Initializing...")
+
+ # Clear out any stale kernel page tables
+ self.p.memset64(self.ttbr1_base + 0x10, 0, 0x3ff0)
+ self.u.inst("tlbi vmalle1os")
+
+ self.handoff.initialize()
+
+ with self.handoff.lock():
+ print(f"[UAT] TTBR0[0] = {self.ttbr0_base:#x}")
+ print(f"[UAT] TTBR1[0] = {self.ttbr1_base:#x}")
+ self.set_l0(0, 0, self.ttbr0_base)
+ self.set_l0(0, 1, self.ttbr1_base)
+ self.flush_dirty()
+ self.invalidate_cache()
+
+ print("[UAT] Init complete")
+
+ self.initialized = True
+
+ def bind_context(self, ctx, ttbr0_base):
+ assert ctx != 0
+
+ with self.handoff.lock():
+ self.set_l0(ctx, 0, ttbr0_base, ctx)
+ self.set_l0(ctx, 1, self.ttbr1_base, ctx)
+ self.flush_dirty()
+ self.invalidate_cache()
+
+ def dump(self, ctx, log=print):
+ def print_fn(start, end, i, pte, level, sparse):
+ type = "page" if level+1 == len(self.LEVELS) else "table"
+ if sparse:
+ log(f"{' ' * level}...")
+ log(f"{' ' * level}{type}({i:03}): {start:011x} ... {end:011x}"
+ f" -> {pte.describe()}")
+
+ self.recurse_level(0, 0, self.gpu_region + ctx * 16, print_fn, print_fn)
+
+__all__.extend(k for k, v in globals().items()
+ if (callable(v) or isinstance(v, type)) and v.__module__ == __name__)
diff --git a/tools/proxyclient/m1n1/loadobjs.py b/tools/proxyclient/m1n1/loadobjs.py
new file mode 100644
index 0000000..ef567b3
--- /dev/null
+++ b/tools/proxyclient/m1n1/loadobjs.py
@@ -0,0 +1,178 @@
+# SPDX-License-Identifier: MIT
+from contextlib import contextmanager, ExitStack
+import sys, pathlib, os
+import subprocess
+import tempfile
+import bisect
+
+from .asm import NM, LD, OBJCOPY, ARMAsm
+
+__all__ = ["LinkedProgram"]
+
+
+def tool_output_lines(progname, *args):
+ with subprocess.Popen([progname.replace("%ARCH", ARMAsm.ARCH)] + list(args),
+ stdout=subprocess.PIPE) as proc:
+ for line in proc.stdout:
+ yield line.decode("ascii")
+ proc.wait()
+ if proc.returncode:
+ raise Exception(f"{progname} (args: {args}) exited with status {proc.returncode}")
+
+def run_tool(progname, *args, silent=False):
+ subprocess.check_call([progname.replace("%ARCH", ARMAsm.ARCH)] + list(args),
+ stdout=subprocess.DEVNULL if silent else None)
+
+
+class LinkedProgram:
+ SOURCE_ROOT = str(pathlib.Path(__file__).resolve().parents[2])
+
+ def __init__(self, u, base_object=None):
+ self.u = u
+ self.symbols = []
+ self.symaddrs = dict()
+ self.base_object = base_object
+ self._alloced_bases = []
+ self._attrs_to_clear = []
+ self._load_base_symbols()
+
+ def _load_base_symbols(self):
+ if self.base_object is None:
+ suffix = "-raw" if not self.m1n1_is_macho() else ""
+ self.base_object = f"build/m1n1{suffix}.elf"
+
+ addrs = self._load_elf_symbols(self.base_object, self.u.proxy.get_base())
+
+ # sanity check: compare the .rela table between ELF and m1n1 image on target
+ rela_base = addrs["_rela_start"]
+ rela_length = addrs["_rela_end"] - rela_base
+ rela_target = self.u.iface.readmem(rela_base, rela_length)
+
+ tmp = os.path.join(tempfile.mkdtemp(), "bin")
+ path = os.path.join(self.SOURCE_ROOT, self.base_object)
+ run_tool(OBJCOPY, "-O", "binary", path, tmp, "--only-section=.rela.dyn")
+ rela_objfile = open(tmp, "rb").read()
+
+ if rela_objfile[:len(rela_target)] != rela_target:
+ raise Exception(f"Mismatch between {self.base_object} and image on target")
+
+ def m1n1_is_macho(self):
+ p = self.u.proxy
+ return p.read32(p.get_base()) == 0xfeedfacf
+
+ def _load_elf_symbols(self, relpath, offset=0,
+ objname=None, ignore=""):
+ path = pathlib.Path(self.SOURCE_ROOT, relpath)
+ symaddrs = dict()
+
+ for line in tool_output_lines(NM, "-g", path):
+ addr_str, t, name = line.split()
+ addr = int(addr_str, 16) + offset
+ if t in ignore:
+ continue
+ self.symbols.append((addr, name, objname))
+ symaddrs[name] = addr
+ if t in "T" and not hasattr(self, name):
+ setattr(self, name, self._wrap_call_to(addr))
+ if relpath != self.base_object:
+ self._attrs_to_clear.append(name)
+ self.symbols.sort()
+ return symaddrs
+
+ def load_obj(self, objfile, base=None):
+ ALLOC_SIZE = 16*4096
+
+ if base is None:
+ base = self.u.heap.memalign(0x4000, ALLOC_SIZE)
+ self._alloced_bases.append(base)
+
+ objfile = os.path.join(self.SOURCE_ROOT, objfile)
+ tmp = tempfile.mkdtemp() + os.sep
+ elffile = tmp + "elf"
+ ld_script = tmp + "ld"
+ binfile = tmp + "bin"
+ with open(ld_script, "w") as f:
+ f.write("SECTIONS {\n")
+ f.write(f". = 0x{base:x};\n")
+ f.write(".text : { *(.text .text.*) }\n")
+ f.write(".data : { *(.got .data .data.* .rodata .rodata.* .bss .bss.*) }\n")
+ f.write("}\n")
+ for sym in self.symbols:
+ f.write(f"{sym[1]} = 0x{sym[0]:x};\n")
+ run_tool(LD, "-EL", "-maarch64elf", "-T", ld_script, "-o", elffile, objfile)
+ run_tool(OBJCOPY, "-O", "binary", elffile, binfile)
+ #run_tool("objdump", "-d", elffile)
+ self._load_elf_symbols(elffile, ignore="A")
+ with open(binfile, "rb") as f:
+ buf = f.read()
+ assert len(buf) <= ALLOC_SIZE
+ self.u.iface.writemem(base, buf)
+ self.u.proxy.dc_cvau(base, len(buf))
+ self.u.proxy.ic_ivau(base, len(buf))
+
+ def clear_objs(self):
+ for name in self._attrs_to_clear:
+ delattr(self, name)
+ self._attrs_to_clear = []
+
+ for base in self._alloced_bases:
+ self.u.free(base)
+ self._alloced_bases = []
+
+ self.symbols = [(a, b, objname) for (a, b, objname) \
+ in self.symbols if objname == self.base_object]
+
+ @contextmanager
+ def _copy_args_to_target(self, args):
+ heap = self.u.heap
+ with ExitStack() as stack:
+ args_copied = []
+ for arg in args:
+ if type(arg) is str:
+ arg = arg.encode("ascii")
+ # fallthrough
+ if type(arg) is bytes:
+ p = stack.enter_context(heap.guarded_malloc(len(arg) + 1))
+ self.u.iface.writemem(p, arg + b"\0")
+ args_copied.append(p)
+ elif type(arg) is int:
+ args_copied.append(arg)
+ else:
+ raise NotImplementedError(type(arg))
+ yield args_copied
+
+ def _wrap_call_to(self, addr):
+ def call_symbol(*args, call=self.u.proxy.call):
+ with self._copy_args_to_target(args) as args_copied:
+ return call(addr, *args_copied)
+ return call_symbol
+
+ def lookup(self, addr):
+ idx = bisect.bisect_left(self.symbols, (addr + 1, "", "")) - 1
+ if idx < 0 or idx >= len(self.symbols):
+ return None, None
+ return self.symbols[idx]
+
+ def load_inline_c(self, source):
+ tmp = tempfile.mkdtemp()
+ cfile = tmp + ".c"
+ objfile = tmp + ".o"
+ with open(cfile, "w") as f:
+ f.write(source)
+ run_tool("make", "-C", self.SOURCE_ROOT, "invoke_cc",
+ f"OBJFILE={objfile}", f"CFILE={cfile}", silent=True)
+ self.load_obj(objfile)
+
+
+if __name__ == "__main__":
+ from m1n1.setup import *
+ lp = LinkedProgram(u)
+ lp.debug_printf("hello from the other side! (%d)\n", 42)
+ lp.load_inline_c('''
+ #include "utils.h"
+ int add(int a, int b) {
+ debug_printf("adding %d and %d\\n", a, b);
+ return a + b;
+ }
+ ''')
+ print(f"1 + 2 = {lp.add(1, 2)}")
diff --git a/tools/proxyclient/m1n1/macho.py b/tools/proxyclient/m1n1/macho.py
new file mode 100644
index 0000000..32c7639
--- /dev/null
+++ b/tools/proxyclient/m1n1/macho.py
@@ -0,0 +1,270 @@
+# SPDX-License-Identifier: MIT
+from io import BytesIO, SEEK_END, SEEK_SET
+import bisect
+from construct import *
+import subprocess
+
+from .utils import *
+
+__all__ = ["MachO"]
+
+MachOLoadCmdType = "LoadCmdType" / Enum(Int32ul,
+ SYMTAB = 0x02,
+ UNIXTHREAD = 0x05,
+ SEGMENT_64 = 0x19,
+ UUID = 0x1b,
+ BUILD_VERSION = 0x32,
+ DYLD_CHAINED_FIXUPS = 0x80000034,
+ FILESET_ENTRY = 0x80000035,
+)
+
+MachOArmThreadStateFlavor = "ThreadStateFlavor" / Enum(Int32ul,
+ THREAD64 = 6,
+)
+
+MachOHeader = Struct(
+ "magic" / Hex(Int32ul),
+ "cputype" / Hex(Int32ul),
+ "cpusubtype" / Hex(Int32ul),
+ "filetype" / Hex(Int32ul),
+ "ncmds" / Hex(Int32ul),
+ "sizeofcmds" / Hex(Int32ul),
+ "flags" / Hex(Int32ul),
+ "reserved" / Hex(Int32ul),
+)
+
+MachOVmProt = FlagsEnum(Int32sl,
+ PROT_READ = 0x01,
+ PROT_WRITE = 0x02,
+ PROT_EXECUTE = 0x04,
+)
+
+MachOCmdSymTab = Struct(
+ "symoff" / Hex(Int32ul),
+ "nsyms" / Int32ul,
+ "stroff" / Hex(Int32ul),
+ "strsize" / Hex(Int32ul),
+)
+
+MachOCmdUnixThread = GreedyRange(Struct(
+ "flavor" / MachOArmThreadStateFlavor,
+ "data" / Prefixed(ExprAdapter(Int32ul, obj_ * 4, obj_ / 4), Switch(this.flavor, {
+ MachOArmThreadStateFlavor.THREAD64: Struct(
+ "x" / Array(29, Hex(Int64ul)),
+ "fp" / Hex(Int64ul),
+ "lr" / Hex(Int64ul),
+ "sp" / Hex(Int64ul),
+ "pc" / Hex(Int64ul),
+ "cpsr" / Hex(Int32ul),
+ "flags" / Hex(Int32ul),
+ )
+ })),
+))
+
+NList = Struct(
+ "n_strx" / Hex(Int32ul),
+ "n_type" / Hex(Int8ul),
+ "n_sect" / Hex(Int8ul),
+ "n_desc" / Hex(Int16sl),
+ "n_value" / Hex(Int64ul),
+)
+
+MachOCmdSegment64 = Struct(
+ "segname" / PaddedString(16, "ascii"),
+ "vmaddr" / Hex(Int64ul),
+ "vmsize" / Hex(Int64ul),
+ "fileoff" / Hex(Int64ul),
+ "filesize" / Hex(Int64ul),
+ "maxprot" / MachOVmProt,
+ "initprot" / MachOVmProt,
+ "nsects" / Int32ul,
+ "flags" / Hex(Int32ul),
+ "sections" / GreedyRange(Struct(
+ "sectname" / PaddedString(16, "ascii"),
+ "segname" / PaddedString(16, "ascii"),
+ "addr" / Hex(Int64ul),
+ "size" / Hex(Int64ul),
+ "offset" / Hex(Int32ul),
+ "align" / Hex(Int32ul),
+ "reloff" / Hex(Int32ul),
+ "nreloc" / Hex(Int32ul),
+ "flags" / Hex(Int32ul),
+ "reserved1" / Hex(Int32ul),
+ "reserved2" / Hex(Int32ul),
+ "reserved3" / Hex(Int32ul),
+ )),
+)
+
+MachOFilesetEntry = Struct(
+ "addr" / Hex(Int64ul),
+ "offset" / Hex(Int64ul),
+ "entryid" / Hex(Int32ul),
+ "reserved" / Hex(Int32ul),
+ "name" / CString("ascii"),
+)
+
+MachOCmd = Struct(
+ "cmd" / Hex(MachOLoadCmdType),
+ "args" / Prefixed(ExprAdapter(Int32ul, obj_ - 8, obj_ + 8), Switch(this.cmd, {
+ MachOLoadCmdType.SYMTAB: MachOCmdSymTab,
+ MachOLoadCmdType.UNIXTHREAD: MachOCmdUnixThread,
+ MachOLoadCmdType.SEGMENT_64: MachOCmdSegment64,
+ MachOLoadCmdType.UUID: Hex(Bytes(16)),
+ MachOLoadCmdType.FILESET_ENTRY: MachOFilesetEntry,
+ }, default=GreedyBytes)),
+)
+
+MachOFile = Struct(
+ "header" / MachOHeader,
+ "cmds" / Array(this.header.ncmds, MachOCmd),
+)
+
+class MachO:
+ def __init__(self, data):
+ if isinstance(data, bytes):
+ self.io = BytesIO(data)
+ else:
+ self.io = data
+
+ self.off = self.io.tell()
+ self.io.seek(0, SEEK_END)
+ self.end = self.io.tell()
+ self.size = self.end - self.off
+ self.io.seek(self.off, SEEK_SET)
+ self.obj = MachOFile.parse_stream(self.io)
+ self.symbols = {}
+ self.load_info()
+ self.load_fileset()
+
+ def load_info(self):
+ self.vmin, self.vmax = (1 << 64), 0
+ self.entry = None
+ for cmd in self.obj.cmds:
+ if cmd.cmd == MachOLoadCmdType.SEGMENT_64:
+ self.vmin = min(self.vmin, cmd.args.vmaddr)
+ self.vmax = max(self.vmax, cmd.args.vmaddr + cmd.args.vmsize)
+ elif cmd.cmd == MachOLoadCmdType.UNIXTHREAD:
+ self.entry = cmd.args[0].data.pc
+
+ def prepare_image(self, load_hook=None):
+ memory_size = self.vmax - self.vmin
+
+ image = bytearray(memory_size)
+
+ for cmd in self.get_cmds(MachOLoadCmdType.SEGMENT_64):
+ dest = cmd.args.vmaddr - self.vmin
+ end = min(self.size, cmd.args.fileoff + cmd.args.filesize)
+ size = end - cmd.args.fileoff
+ print(f"LOAD: {cmd.args.segname} {size} bytes from {cmd.args.fileoff:x} to {dest:x}")
+ self.io.seek(self.off + cmd.args.fileoff)
+ data = self.io.read(size)
+ if load_hook is not None:
+ data = load_hook(data, cmd.args.segname, size, cmd.args.fileoff, dest)
+ image[dest:dest + size] = data
+ if cmd.args.vmsize > size:
+ clearsize = cmd.args.vmsize - size
+ if cmd.args.segname == "PYLD":
+ print("SKIP: %d bytes from 0x%x to 0x%x" % (clearsize, dest + size, dest + size + clearsize))
+ memory_size -= clearsize - 4 # leave a payload end marker
+ image = image[:memory_size]
+ else:
+ print("ZERO: %d bytes from 0x%x to 0x%x" % (clearsize, dest + size, dest + size + clearsize))
+ image[dest + size:dest + cmd.args.vmsize] = bytes(clearsize)
+
+ return image
+
+ def get_cmds(self, cmdtype):
+ for cmd in self.obj.cmds:
+ if cmd.cmd == cmdtype:
+ yield cmd
+
+ def get_cmd(self, cmdtype):
+ cmds = list(self.get_cmds(cmdtype))
+ if len(cmds) == 0:
+ raise Exception(f"No commands of type {cmdtype}")
+ if len(cmds) > 1:
+ raise Exception(f"More than one commands of type {cmdtype} (found {len(cmd)})")
+ return cmds[0]
+
+ def load_fileset(self):
+ self.subfiles = {}
+
+ for fe in self.get_cmds(MachOLoadCmdType.FILESET_ENTRY):
+ self.io.seek(self.off + fe.args.offset)
+ subfile = MachO(self.io)
+ self.subfiles[fe.args.name] = subfile
+ for seg in subfile.get_cmds(MachOLoadCmdType.SEGMENT_64):
+ self.symbols[f"{fe.args.name}:{seg.args.segname}"] = seg.args.vmaddr
+
+ def add_symbols(self, filename, syms, demangle=False):
+ try:
+ subfile = self.subfiles[filename]
+ except KeyError:
+ raise Exception(f"No fileset entry for {filename}")
+
+ sym_segs = {}
+ for sym_seg in syms.get_cmds(MachOLoadCmdType.SEGMENT_64):
+ sym_segs[sym_seg.args.segname] = sym_seg
+
+ syms.load_symbols(demangle=demangle)
+ symtab = [(v, k) for (k, v) in syms.symbols.items()]
+ symtab.sort()
+
+ for seg in subfile.get_cmds(MachOLoadCmdType.SEGMENT_64):
+ if seg.args.segname not in sym_segs:
+ continue
+
+ sym_seg = sym_segs[seg.args.segname]
+
+ start = bisect.bisect_left(symtab, (sym_seg.args.vmaddr, ""))
+ end = bisect.bisect_left(symtab, (sym_seg.args.vmaddr + sym_seg.args.vmsize, ""))
+
+ for addr, sym in symtab[start:end]:
+ sname = f"{filename}:{sym}"
+ self.symbols[sname] = addr - sym_seg.args.vmaddr + seg.args.vmaddr
+
+ def load_symbols(self, demangle=False):
+ self.symbols = {}
+
+ cmd = self.get_cmd(MachOLoadCmdType.SYMTAB)
+
+ nsyms = cmd.args.nsyms
+ length = NList.sizeof() * nsyms
+ self.io.seek(self.off + cmd.args.symoff)
+ symdata = self.io.read(length)
+
+ symbols = Array(nsyms, NList).parse(symdata)
+
+ symbols_dict = {}
+ for i in symbols:
+ off = cmd.args.stroff + i.n_strx
+ self.io.seek(self.off + off)
+ name = self.io.read(1024).split(b"\x00")[0].decode("ascii")
+ symbols_dict[name] = i.n_value
+
+ if demangle:
+ names = list(symbols_dict.keys())
+ argv = ["c++filt"]
+ argv += names
+
+ with subprocess.Popen(argv, stdin=subprocess.PIPE, stdout=subprocess.PIPE) as proc:
+ demangled, _ = proc.communicate()
+
+ demangled = demangled.decode("ascii").split("\n")[:-1]
+ for name_mangled, name_demangled in zip(names, demangled):
+ self.symbols[name_demangled] = symbols_dict[name_mangled]
+ else:
+ self.symbols = symbols_dict
+
+if __name__ == "__main__":
+ import sys
+ macho = MachO(open(sys.argv[1], "rb").read())
+
+ if len(sys.argv) > 2:
+ syms = MachO(open(sys.argv[2], "rb").read())
+ macho.add_symbols("com.apple.kernel", syms)
+
+ symtab = [(v, k) for (k, v) in macho.symbols.items()]
+ symtab.sort()
+ for addr, name in symtab:
+ print(f"0x{addr:x} {name}")
diff --git a/tools/proxyclient/m1n1/malloc.py b/tools/proxyclient/m1n1/malloc.py
new file mode 100644
index 0000000..909d55b
--- /dev/null
+++ b/tools/proxyclient/m1n1/malloc.py
@@ -0,0 +1,98 @@
+# SPDX-License-Identifier: MIT
+from contextlib import contextmanager
+
+__all__ = ["Heap"]
+
+class Heap(object):
+ def __init__(self, start, end, block=64):
+ if start%block:
+ raise ValueError("heap start not aligned")
+ if end%block:
+ raise ValueError("heap end not aligned")
+ self.offset = start
+ self.count = (end - start) // block
+ self.blocks = [(self.count,False)]
+ self.block = block
+
+ def malloc(self, size):
+ size = (size + self.block - 1) // self.block
+ pos = 0
+ for i, (bsize, full) in enumerate(self.blocks):
+ if not full and bsize >= size:
+ self.blocks[i] = (size, True)
+ if bsize > size:
+ self.blocks.insert(i+1, (bsize - size, False))
+ return self.offset + self.block * pos
+ pos += bsize
+ raise Exception("Out of memory")
+
+ def memalign(self, align, size):
+ assert (align & (align - 1)) == 0
+ align = max(align, self.block) // self.block
+ size = (size + self.block - 1) // self.block
+ pos = self.offset // self.block
+ for i, (bsize, full) in enumerate(self.blocks):
+ if not full:
+ offset = 0
+ if pos % align:
+ offset = align - (pos % align)
+ if bsize >= (size + offset):
+ if offset:
+ self.blocks.insert(i, (offset, False))
+ i += 1
+ self.blocks[i] = (size, True)
+ if bsize > (size + offset):
+ self.blocks.insert(i+1, (bsize - size - offset, False))
+ return self.block * (pos + offset)
+ pos += bsize
+ raise Exception("Out of memory")
+
+ def free(self, addr):
+ if addr%self.block:
+ raise ValueError("free address not aligned")
+ if addr<self.offset:
+ raise ValueError("free address before heap")
+ addr -= self.offset
+ addr //= self.block
+ if addr>=self.count:
+ raise ValueError("free address after heap")
+ pos = 0
+ for i, (bsize, used) in enumerate(self.blocks):
+ if pos > addr:
+ raise ValueError("bad free address")
+ if pos == addr:
+ if used == False:
+ raise ValueError("block already free")
+ if i!=0 and self.blocks[i-1][1] == False:
+ bsize += self.blocks[i-1][0]
+ del self.blocks[i]
+ i -= 1
+ if i!=(len(self.blocks)-1) and self.blocks[i+1][1] == False:
+ bsize += self.blocks[i+1][0]
+ del self.blocks[i]
+ self.blocks[i] = (bsize, False)
+ return
+ pos += bsize
+ raise ValueError("bad free address")
+
+ def check(self):
+ free = 0
+ inuse = 0
+ for i, (bsize, used) in enumerate(self.blocks):
+ if used:
+ inuse += bsize
+ else:
+ free += bsize
+ if free + inuse != self.count:
+ raise Exception("Total block size is inconsistent")
+ print("Heap stats:")
+ print(" In use: %8dkB"%(inuse * self.block // 1024))
+ print(" Free: %8dkB"%(free * self.block // 1024))
+
+ @contextmanager
+ def guarded_malloc(self, size):
+ addr = self.malloc(size)
+ try:
+ yield addr
+ finally:
+ self.free(addr)
diff --git a/tools/proxyclient/m1n1/proxy.py b/tools/proxyclient/m1n1/proxy.py
new file mode 100644
index 0000000..1869f3e
--- /dev/null
+++ b/tools/proxyclient/m1n1/proxy.py
@@ -0,0 +1,1104 @@
+# SPDX-License-Identifier: MIT
+import os, sys, struct, serial, time
+from construct import *
+from enum import IntEnum, IntFlag
+from serial.tools.miniterm import Miniterm
+
+from .utils import *
+from .sysreg import *
+
+__all__ = ["REGION_RWX_EL0", "REGION_RW_EL0", "REGION_RX_EL1"]
+
+# Hack to disable input buffer flushing
+class Serial(serial.Serial):
+ def _reset_input_buffer(self):
+ return
+
+ def reset_input_buffer(self):
+ return
+
+class UartError(RuntimeError):
+ pass
+
+class UartTimeout(UartError):
+ pass
+
+class UartCMDError(UartError):
+ pass
+
+class UartChecksumError(UartError):
+ pass
+
+class UartRemoteError(UartError):
+ pass
+
+class Feature(IntFlag):
+ DISABLE_DATA_CSUMS = 0x01 # Data transfers don't use checksums
+
+ @classmethod
+ def get_all(cls):
+ return cls.DISABLE_DATA_CSUMS
+
+ def __str__(self):
+ return ", ".join(feature.name for feature in self.__class__
+ if feature & self) or "<none>"
+
+
+class START(IntEnum):
+ BOOT = 0
+ EXCEPTION = 1
+ EXCEPTION_LOWER = 2
+ HV = 3
+
+class EXC(IntEnum):
+ SYNC = 0
+ IRQ = 1
+ FIQ = 2
+ SERROR = 3
+
+class EVENT(IntEnum):
+ MMIOTRACE = 1
+ IRQTRACE = 2
+
+class EXC_RET(IntEnum):
+ UNHANDLED = 1
+ HANDLED = 2
+ EXIT_GUEST = 3
+ STEP = 4
+
+class DCP_SHUTDOWN_MODE(IntEnum):
+ QUIESCED = 0
+ SLEEP_IF_EXTERNAL = 1
+ SLEEP = 2
+
+class PIX_FMT(IntEnum):
+ XRGB = 0
+ XBGR = 1
+
+class DART(IntEnum):
+ T8020 = 0
+ T8110 = 1
+ T6000 = 2
+
+ExcInfo = Struct(
+ "regs" / Array(32, Int64ul),
+ "spsr" / RegAdapter(SPSR),
+ "elr" / Int64ul,
+ "esr" / RegAdapter(ESR),
+ "far" / Int64ul,
+ "afsr1" / Int64ul,
+ "sp" / Array(3, Int64ul),
+ "cpu_id" / Int64ul,
+ "mpidr" / Int64ul,
+ "elr_phys" / Int64ul,
+ "far_phys" / Int64ul,
+ "sp_phys" / Int64ul,
+ "data" / Int64ul,
+)
+# Sends 56+ byte Commands and Expects 36 Byte Responses
+# Commands are format <I48sI
+# 4 byte command, 48 byte null padded data + 4 byte checksum
+# Responses are of the format: struct format <Ii24sI
+# 4byte Response , 4 byte status, 24 byte string, 4 byte Checksum
+# Response must start 0xff55aaXX where XX distiguishes between them
+# In little endian mode these numbers as listed as REQ_* constants
+# defined under UartInterface
+#
+# Event Response REQ_EVENT passed to registered Event Handler
+# Boot Response REQ_BOOT passed to handle_boot() which may
+# pass to a matching registered handler based on reason, code values
+# If the status is ST_OK returns the data field to caller
+# Otherwise reports a remote Error
+
+class UartInterface(Reloadable):
+ REQ_NOP = 0x00AA55FF
+ REQ_PROXY = 0x01AA55FF
+ REQ_MEMREAD = 0x02AA55FF
+ REQ_MEMWRITE = 0x03AA55FF
+ REQ_BOOT = 0x04AA55FF
+ REQ_EVENT = 0x05AA55FF
+
+ CHECKSUM_SENTINEL = 0xD0DECADE
+ DATA_END_SENTINEL = 0xB0CACC10
+
+ ST_OK = 0
+ ST_BADCMD = -1
+ ST_INVAL = -2
+ ST_XFERERR = -3
+ ST_CSUMERR = -4
+
+ CMD_LEN = 56
+ REPLY_LEN = 36
+ EVENT_HDR_LEN = 8
+
+ def __init__(self, device=None, debug=False):
+ self.debug = debug
+ self.devpath = None
+ if device is None:
+ device = os.environ.get("M1N1DEVICE", "/dev/m1n1:115200")
+ if isinstance(device, str):
+ baud = 115200
+ if ":" in device:
+ device, baud = device.rsplit(":", 1)
+ baud = int(baud)
+ self.devpath = device
+ self.baudrate = baud
+
+ device = Serial(self.devpath, baud)
+
+ self.dev = device
+ self.dev.timeout = 0
+ self.dev.flushOutput()
+ self.dev.flushInput()
+ self.pted = False
+ #d = self.dev.read(1)
+ #while d != "":
+ #d = self.dev.read(1)
+ self.dev.timeout = int(os.environ.get("M1N1TIMEOUT", "3"))
+ self.tty_enable = True
+ self.handlers = {}
+ self.evt_handlers = {}
+ self.enabled_features = Feature(0)
+
+ def checksum(self, data):
+ sum = 0xDEADBEEF;
+ for c in data:
+ sum *= 31337
+ sum += c ^ 0x5a
+ sum &= 0xFFFFFFFF
+
+ return (sum ^ 0xADDEDBAD) & 0xFFFFFFFF
+
+ def data_checksum(self, data):
+ if self.enabled_features & Feature.DISABLE_DATA_CSUMS:
+ return self.CHECKSUM_SENTINEL
+
+ return self.checksum(data)
+
+ def readfull(self, size):
+ d = b''
+ while len(d) < size:
+ block = self.dev.read(size - len(d))
+ if not block:
+ raise UartTimeout("Expected %d bytes, got %d bytes"%(size,len(d)))
+ d += block
+ return d
+
+ def cmd(self, cmd, payload=b""):
+ if len(payload) > self.CMD_LEN:
+ raise ValueError("Incorrect payload size %d"%len(payload))
+
+ payload = payload.ljust(self.CMD_LEN, b"\x00")
+ command = struct.pack("<I", cmd) + payload
+ command += struct.pack("<I", self.checksum(command))
+ if self.debug:
+ print("<<", hexdump(command))
+ self.dev.write(command)
+
+ def unkhandler(self, s):
+ if not self.tty_enable:
+ return
+ for c in s:
+ if not self.pted:
+ sys.stdout.write("TTY> ")
+ self.pted = True
+ if c == 10:
+ self.pted = False
+ sys.stdout.write(chr(c))
+ sys.stdout.flush()
+
+ def ttymode(self, dev=None):
+ if dev is None:
+ dev = self.dev
+
+ tout = dev.timeout
+ self.tty_enable = True
+ dev.timeout = None
+
+ term = Miniterm(dev, eol='cr')
+ term.exit_character = chr(0x1d) # GS/CTRL+]
+ term.menu_character = chr(0x14) # Menu: CTRL+T
+ term.raw = True
+ term.set_rx_encoding('UTF-8')
+ term.set_tx_encoding('UTF-8')
+
+ print('--- TTY mode | Quit: CTRL+] | Menu: CTRL+T ---')
+ term.start()
+ try:
+ term.join(True)
+ except KeyboardInterrupt:
+ pass
+
+ print('--- Exit TTY mode ---')
+ term.join()
+ term.close()
+
+ dev.timeout = tout
+ self.tty_enable = False
+
+ def reply(self, cmd):
+ reply = b''
+ while True:
+ if not reply or reply[-1] != 255:
+ reply = b''
+ reply += self.readfull(1)
+ if reply != b"\xff":
+ self.unkhandler(reply)
+ continue
+ else:
+ reply = b'\xff'
+ reply += self.readfull(1)
+ if reply != b"\xff\x55":
+ self.unkhandler(reply)
+ continue
+ reply += self.readfull(1)
+ if reply != b"\xff\x55\xaa":
+ self.unkhandler(reply)
+ continue
+ reply += self.readfull(1)
+ cmdin = struct.unpack("<I", reply)[0]
+ if cmdin == self.REQ_EVENT:
+ reply += self.readfull(self.EVENT_HDR_LEN - 4)
+ data_len, event_type = struct.unpack("<HH", reply[4:])
+ reply += self.readfull(data_len + 4)
+ if self.debug:
+ print(">>", hexdump(reply))
+ checksum = struct.unpack("<I", reply[-4:])[0]
+ ccsum = self.data_checksum(reply[:-4])
+ if checksum != ccsum:
+ print("Event checksum error: Expected 0x%08x, got 0x%08x"%(checksum, ccsum))
+ raise UartChecksumError()
+ self.handle_event(EVENT(event_type), reply[self.EVENT_HDR_LEN:-4])
+ reply = b''
+ continue
+
+ reply += self.readfull(self.REPLY_LEN - 4)
+ if self.debug:
+ print(">>", hexdump(reply))
+ status, data, checksum = struct.unpack("<i24sI", reply[4:])
+ ccsum = self.checksum(reply[:-4])
+ if checksum != ccsum:
+ print("Reply checksum error: Expected 0x%08x, got 0x%08x"%(checksum, ccsum))
+ raise UartChecksumError()
+
+ if cmdin != cmd:
+ if cmdin == self.REQ_BOOT and status == self.ST_OK:
+ self.handle_boot(data)
+ reply = b''
+ continue
+ raise UartCMDError("Reply command mismatch: Expected 0x%08x, got 0x%08x"%(cmd, cmdin))
+ if status != self.ST_OK:
+ if status == self.ST_BADCMD:
+ raise UartRemoteError("Reply error: Bad Command")
+ elif status == self.ST_INVAL:
+ raise UartRemoteError("Reply error: Invalid argument")
+ elif status == self.ST_XFERERR:
+ raise UartRemoteError("Reply error: Data transfer failed")
+ elif status == self.ST_CSUMERR:
+ raise UartRemoteError("Reply error: Data checksum failed")
+ else:
+ raise UartRemoteError("Reply error: Unknown error (%d)"%status)
+ return data
+
+ def handle_boot(self, data):
+ reason, code, info = struct.unpack("<IIQ", data[:16])
+ reason = START(reason)
+ if reason in (START.EXCEPTION, START.EXCEPTION_LOWER):
+ code = EXC(code)
+ if (reason, code) in self.handlers:
+ self.handlers[(reason, code)](reason, code, info)
+ elif reason != START.BOOT:
+ print(f"Proxy callback without handler: {reason}, {code}")
+
+ def set_handler(self, reason, code, handler):
+ self.handlers[(reason, code)] = handler
+
+ def handle_event(self, event_id, data):
+ if event_id in self.evt_handlers:
+ self.evt_handlers[event_id](data)
+
+ def set_event_handler(self, event_id, handler):
+ self.evt_handlers[event_id] = handler
+
+ def wait_boot(self):
+ try:
+ return self.reply(self.REQ_BOOT)
+ except:
+ # Over USB, reboots cause a reconnect
+ self.dev.close()
+ print("Waiting for reconnection... ", end="")
+ sys.stdout.flush()
+ for i in range(100):
+ print(".", end="")
+ sys.stdout.flush()
+ try:
+ self.dev.open()
+ except serial.serialutil.SerialException:
+ time.sleep(0.1)
+ else:
+ break
+ else:
+ raise UartTimeout("Reconnection timed out")
+ print(" Connected")
+
+ def wait_and_handle_boot(self):
+ self.handle_boot(self.wait_boot())
+
+ def nop(self):
+ features = Feature.get_all()
+
+ # Send the supported feature flags in the NOP message (has no effect
+ # if the target does not support it)
+ self.cmd(self.REQ_NOP, struct.pack("<Q", features.value))
+ result = self.reply(self.REQ_NOP)
+
+ # Get the enabled feature flags from the message response (returns
+ # 0 if the target does not support it)
+ features = Feature(struct.unpack("<QQQ", result)[0])
+
+ if self.debug:
+ print(f"Enabled features: {features}")
+
+ self.enabled_features = features
+
+ def proxyreq(self, req, reboot=False, no_reply=False, pre_reply=None):
+ self.cmd(self.REQ_PROXY, req)
+ if pre_reply:
+ pre_reply()
+ if no_reply:
+ return
+ elif reboot:
+ return self.wait_boot()
+ else:
+ return self.reply(self.REQ_PROXY)
+
+ def writemem(self, addr, data, progress=False):
+ checksum = self.data_checksum(data)
+ size = len(data)
+ req = struct.pack("<QQI", addr, size, checksum)
+ self.cmd(self.REQ_MEMWRITE, req)
+ if self.debug:
+ print("<< DATA:")
+ chexdump(data)
+ for i in range(0, len(data), 8192):
+ self.dev.write(data[i:i + 8192])
+ if progress:
+ sys.stdout.write(".")
+ sys.stdout.flush()
+ if progress:
+ print()
+ if self.enabled_features & Feature.DISABLE_DATA_CSUMS:
+ # Extra sentinel after the data to make sure no data is lost
+ self.dev.write(struct.pack("<I", self.DATA_END_SENTINEL))
+
+ # should automatically report a CRC failure
+ self.reply(self.REQ_MEMWRITE)
+
+ def readmem(self, addr, size):
+ if size == 0:
+ return b""
+
+ req = struct.pack("<QQ", addr, size)
+ self.cmd(self.REQ_MEMREAD, req)
+ reply = self.reply(self.REQ_MEMREAD)
+ checksum = struct.unpack("<I",reply[:4])[0]
+ data = self.readfull(size)
+ if self.debug:
+ print(">> DATA:")
+ chexdump(data)
+ ccsum = self.data_checksum(data)
+ if checksum != ccsum:
+ raise UartChecksumError("Reply data checksum error: Expected 0x%08x, got 0x%08x"%(checksum, ccsum))
+
+ if self.enabled_features & Feature.DISABLE_DATA_CSUMS:
+ # Extra sentinel after the data to make sure no data was lost
+ sentinel = struct.unpack("<I", self.readfull(4))[0]
+ if sentinel != self.DATA_END_SENTINEL:
+ raise UartChecksumError(f"Reply data sentinel error: Expected "
+ f"{self.DATA_END_SENTINEL:#x}, got {sentinel:#x}")
+
+ return data
+
+ def readstruct(self, addr, stype):
+ return stype.parse(self.readmem(addr, stype.sizeof()))
+
+class ProxyError(RuntimeError):
+ pass
+
+class ProxyReplyError(ProxyError):
+ pass
+
+class ProxyRemoteError(ProxyError):
+ pass
+
+class ProxyCommandError(ProxyRemoteError):
+ pass
+
+class AlignmentError(Exception):
+ pass
+
+class IODEV(IntEnum):
+ UART = 0
+ FB = 1
+ USB_VUART = 2
+ USB0 = 3
+ USB1 = 4
+ USB2 = 5
+ USB3 = 6
+ USB4 = 7
+ USB5 = 8
+ USB6 = 9
+ USB7 = 10
+
+class USAGE(IntFlag):
+ CONSOLE = (1 << 0)
+ UARTPROXY = (1 << 1)
+
+class GUARD(IntFlag):
+ OFF = 0
+ SKIP = 1
+ MARK = 2
+ RETURN = 3
+ SILENT = 0x100
+
+REGION_RWX_EL0 = 0x80000000000
+REGION_RW_EL0 = 0xa0000000000
+REGION_RX_EL1 = 0xc0000000000
+
+# Uses UartInterface.proxyreq() to send requests to M1N1 and process
+# reponses sent back.
+class M1N1Proxy(Reloadable):
+ S_OK = 0
+ S_BADCMD = -1
+
+ P_NOP = 0x000
+ P_EXIT = 0x001
+ P_CALL = 0x002
+ P_GET_BOOTARGS = 0x003
+ P_GET_BASE = 0x004
+ P_SET_BAUD = 0x005
+ P_UDELAY = 0x006
+ P_SET_EXC_GUARD = 0x007
+ P_GET_EXC_COUNT = 0x008
+ P_EL0_CALL = 0x009
+ P_EL1_CALL = 0x00a
+ P_VECTOR = 0x00b
+ P_GL1_CALL = 0x00c
+ P_GL2_CALL = 0x00d
+ P_GET_SIMD_STATE = 0x00e
+ P_PUT_SIMD_STATE = 0x00f
+ P_REBOOT = 0x010
+
+ P_WRITE64 = 0x100
+ P_WRITE32 = 0x101
+ P_WRITE16 = 0x102
+ P_WRITE8 = 0x103
+ P_READ64 = 0x104
+ P_READ32 = 0x105
+ P_READ16 = 0x106
+ P_READ8 = 0x107
+ P_SET64 = 0x108
+ P_SET32 = 0x109
+ P_SET16 = 0x10a
+ P_SET8 = 0x10b
+ P_CLEAR64 = 0x10c
+ P_CLEAR32 = 0x10d
+ P_CLEAR16 = 0x10e
+ P_CLEAR8 = 0x10f
+ P_MASK64 = 0x110
+ P_MASK32 = 0x111
+ P_MASK16 = 0x112
+ P_MASK8 = 0x113
+ P_WRITEREAD64 = 0x114
+ P_WRITEREAD32 = 0x115
+ P_WRITEREAD16 = 0x116
+ P_WRITEREAD8 = 0x117
+
+ P_MEMCPY64 = 0x200
+ P_MEMCPY32 = 0x201
+ P_MEMCPY16 = 0x202
+ P_MEMCPY8 = 0x203
+ P_MEMSET64 = 0x204
+ P_MEMSET32 = 0x205
+ P_MEMSET16 = 0x206
+ P_MEMSET8 = 0x207
+
+ P_IC_IALLUIS = 0x300
+ P_IC_IALLU = 0x301
+ P_IC_IVAU = 0x302
+ P_DC_IVAC = 0x303
+ P_DC_ISW = 0x304
+ P_DC_CSW = 0x305
+ P_DC_CISW = 0x306
+ P_DC_ZVA = 0x307
+ P_DC_CVAC = 0x308
+ P_DC_CVAU = 0x309
+ P_DC_CIVAC = 0x30a
+ P_MMU_SHUTDOWN = 0x30b
+ P_MMU_INIT = 0x30c
+ P_MMU_DISABLE = 0x30d
+ P_MMU_RESTORE = 0x30e
+ P_MMU_INIT_SECONDARY = 0x30f
+
+ P_XZDEC = 0x400
+ P_GZDEC = 0x401
+
+ P_SMP_START_SECONDARIES = 0x500
+ P_SMP_CALL = 0x501
+ P_SMP_CALL_SYNC = 0x502
+ P_SMP_WAIT = 0x503
+ P_SMP_SET_WFE_MODE = 0x504
+
+ P_HEAPBLOCK_ALLOC = 0x600
+ P_MALLOC = 0x601
+ P_MEMALIGN = 0x602
+ P_FREE = 0x602
+
+ P_KBOOT_BOOT = 0x700
+ P_KBOOT_SET_CHOSEN = 0x701
+ P_KBOOT_SET_INITRD = 0x702
+ P_KBOOT_PREPARE_DT = 0x703
+
+ P_PMGR_CLOCK_ENABLE = 0x800
+ P_PMGR_CLOCK_DISABLE = 0x801
+ P_PMGR_ADT_CLOCKS_ENABLE = 0x802
+ P_PMGR_ADT_CLOCKS_DISABLE = 0x803
+ P_PMGR_RESET = 0x804
+
+ P_IODEV_SET_USAGE = 0x900
+ P_IODEV_CAN_READ = 0x901
+ P_IODEV_CAN_WRITE = 0x902
+ P_IODEV_READ = 0x903
+ P_IODEV_WRITE = 0x904
+ P_IODEV_WHOAMI = 0x905
+ P_USB_IODEV_VUART_SETUP = 0x906
+
+ P_TUNABLES_APPLY_GLOBAL = 0xa00
+ P_TUNABLES_APPLY_LOCAL = 0xa01
+
+ P_DART_INIT = 0xb00
+ P_DART_SHUTDOWN = 0xb01
+ P_DART_MAP = 0xb02
+ P_DART_UNMAP = 0xb03
+
+ P_HV_INIT = 0xc00
+ P_HV_MAP = 0xc01
+ P_HV_START = 0xc02
+ P_HV_TRANSLATE = 0xc03
+ P_HV_PT_WALK = 0xc04
+ P_HV_MAP_VUART = 0xc05
+ P_HV_TRACE_IRQ = 0xc06
+ P_HV_WDT_START = 0xc07
+ P_HV_START_SECONDARY = 0xc08
+ P_HV_SWITCH_CPU = 0xc09
+ P_HV_SET_TIME_STEALING = 0xc0a
+ P_HV_PIN_CPU = 0xc0b
+ P_HV_WRITE_HCR = 0xc0c
+ P_HV_MAP_VIRTIO = 0xc0d
+ P_VIRTIO_PUT_BUFFER = 0xc0e
+
+ P_FB_INIT = 0xd00
+ P_FB_SHUTDOWN = 0xd01
+ P_FB_BLIT = 0xd02
+ P_FB_UNBLIT = 0xd03
+ P_FB_FILL = 0xd04
+ P_FB_CLEAR = 0xd05
+ P_FB_DISPLAY_LOGO = 0xd06
+ P_FB_RESTORE_LOGO = 0xd07
+ P_FB_IMPROVE_LOGO = 0xd08
+
+ P_PCIE_INIT = 0xe00
+ P_PCIE_SHUTDOWN = 0xe01
+
+ P_NVME_INIT = 0xf00
+ P_NVME_SHUTDOWN = 0xf01
+ P_NVME_READ = 0xf02
+ P_NVME_FLUSH = 0xf03
+
+ P_MCC_GET_CARVEOUTS = 0x1000
+
+ P_DISPLAY_INIT = 0x1100
+ P_DISPLAY_CONFIGURE = 0x1101
+ P_DISPLAY_SHUTDOWN = 0x1102
+ P_DISPLAY_START_DCP = 0x1103
+ P_DISPLAY_IS_EXTERNAL = 0x1104
+
+ P_DAPF_INIT_ALL = 0x1200
+ P_DAPF_INIT = 0x1201
+
+ def __init__(self, iface, debug=False):
+ self.debug = debug
+ self.iface = iface
+ self.heap = None
+
+ def _request(self, opcode, *args, reboot=False, signed=False, no_reply=False, pre_reply=None):
+ if len(args) > 6:
+ raise ValueError("Too many arguments")
+ args = list(args) + [0] * (6 - len(args))
+ req = struct.pack("<7Q", opcode, *args)
+ if self.debug:
+ print("<<<< %08x: %08x %08x %08x %08x %08x %08x"%tuple([opcode] + args))
+ reply = self.iface.proxyreq(req, reboot=reboot, no_reply=no_reply, pre_reply=None)
+ if no_reply or reboot and reply is None:
+ return
+ ret_fmt = "q" if signed else "Q"
+ rop, status, retval = struct.unpack("<Qq" + ret_fmt, reply)
+ if self.debug:
+ print(">>>> %08x: %d %08x"%(rop, status, retval))
+ if reboot:
+ return
+ if rop != opcode:
+ raise ProxyReplyError("Reply opcode mismatch: Expected 0x%08x, got 0x%08x"%(opcode,rop))
+ if status != self.S_OK:
+ if status == self.S_BADCMD:
+ raise ProxyCommandError("Reply error: Bad Command")
+ else:
+ raise ProxyRemoteError("Reply error: Unknown error (%d)"%status)
+ return retval
+
+ def request(self, opcode, *args, **kwargs):
+ free = []
+ args = list(args)
+ args2 = []
+ for i, arg in enumerate(args):
+ if isinstance(arg, str):
+ arg = arg.encode("utf-8") + b"\0"
+ if isinstance(arg, bytes) and self.heap:
+ p = self.heap.malloc(len(arg))
+ free.append(p)
+ self.iface.writemem(p, arg)
+ if (i < (len(args) - 1)) and args[i + 1] is None:
+ args[i + 1] = len(arg)
+ arg = p
+ args2.append(arg)
+ try:
+ return self._request(opcode, *args2, **kwargs)
+ finally:
+ for i in free:
+ self.heap.free(i)
+
+ def nop(self):
+ self.request(self.P_NOP)
+ def exit(self, retval=0):
+ self.request(self.P_EXIT, retval)
+ def call(self, addr, *args, reboot=False):
+ if len(args) > 5:
+ raise ValueError("Too many arguments")
+ return self.request(self.P_CALL, addr, *args, reboot=reboot)
+ def reload(self, addr, *args, el1=False):
+ if len(args) > 4:
+ raise ValueError("Too many arguments")
+ if el1:
+ self.request(self.P_EL1_CALL, addr, *args, no_reply=True)
+ else:
+ try:
+ self.request(self.P_VECTOR, addr, *args)
+ self.iface.wait_boot()
+ except ProxyCommandError: # old m1n1 does not support P_VECTOR
+ try:
+ self.mmu_shutdown()
+ except ProxyCommandError: # older m1n1 does not support MMU
+ pass
+ self.request(self.P_CALL, addr, *args, reboot=True)
+ def get_bootargs(self):
+ return self.request(self.P_GET_BOOTARGS)
+ def get_base(self):
+ return self.request(self.P_GET_BASE)
+ def set_baud(self, baudrate):
+ self.iface.tty_enable = False
+ def change():
+ self.iface.dev.baudrate = baudrate
+ try:
+ self.request(self.P_SET_BAUD, baudrate, 16, 0x005aa5f0, pre_reply=change)
+ finally:
+ self.iface.tty_enable = True
+ def udelay(self, usec):
+ self.request(self.P_UDELAY, usec)
+ def set_exc_guard(self, mode):
+ self.request(self.P_SET_EXC_GUARD, mode)
+ def get_exc_count(self):
+ return self.request(self.P_GET_EXC_COUNT)
+ def el0_call(self, addr, *args):
+ if len(args) > 4:
+ raise ValueError("Too many arguments")
+ return self.request(self.P_EL0_CALL, addr, *args)
+ def el1_call(self, addr, *args):
+ if len(args) > 4:
+ raise ValueError("Too many arguments")
+ return self.request(self.P_EL1_CALL, addr, *args)
+ def gl1_call(self, addr, *args):
+ if len(args) > 4:
+ raise ValueError("Too many arguments")
+ return self.request(self.P_GL1_CALL, addr, *args)
+ def gl2_call(self, addr, *args):
+ if len(args) > 4:
+ raise ValueError("Too many arguments")
+ return self.request(self.P_GL2_CALL, addr, *args)
+ def get_simd_state(self, buf):
+ self.request(self.P_GET_SIMD_STATE, buf)
+ def put_simd_state(self, buf):
+ self.request(self.P_PUT_SIMD_STATE, buf)
+ def reboot(self):
+ self.request(self.P_REBOOT, no_reply=True)
+
+ def write64(self, addr, data):
+ '''write 8 byte value to given address'''
+ if addr & 7:
+ raise AlignmentError()
+ self.request(self.P_WRITE64, addr, data)
+ def write32(self, addr, data):
+ '''write 4 byte value to given address'''
+ if addr & 3:
+ raise AlignmentError()
+ self.request(self.P_WRITE32, addr, data)
+ def write16(self, addr, data):
+ '''write 2 byte value to given address'''
+ if addr & 1:
+ raise AlignmentError()
+ self.request(self.P_WRITE16, addr, data)
+ def write8(self, addr, data):
+ '''write 1 byte value to given address'''
+ self.request(self.P_WRITE8, addr, data)
+
+ def read64(self, addr):
+ '''return 8 byte value from given address'''
+ if addr & 7:
+ raise AlignmentError()
+ return self.request(self.P_READ64, addr)
+ def read32(self, addr):
+ '''return 4 byte value given address'''
+ if addr & 3:
+ raise AlignmentError()
+ return self.request(self.P_READ32, addr)
+ def read16(self, addr):
+ '''return 2 byte value from given address'''
+ if addr & 1:
+ raise AlignmentError()
+ return self.request(self.P_READ16, addr)
+ def read8(self, addr):
+ '''return 1 byte value from given address'''
+ return self.request(self.P_READ8, addr)
+
+ def set64(self, addr, data):
+ '''Or 64 bit value of data into memory at addr and return result'''
+ if addr & 7:
+ raise AlignmentError()
+ return self.request(self.P_SET64, addr, data)
+ def set32(self, addr, data):
+ '''Or 32 bit value of data into memory at addr and return result'''
+ if addr & 3:
+ raise AlignmentError()
+ return self.request(self.P_SET32, addr, data)
+ def set16(self, addr, data):
+ '''Or 16 bit value of data into memory at addr and return result'''
+ if addr & 1:
+ raise AlignmentError()
+ return self.request(self.P_SET16, addr, data)
+ def set8(self, addr, data):
+ '''Or byte value of data into memory at addr and return result'''
+ return self.request(self.P_SET8, addr, data)
+
+ def clear64(self, addr, data):
+ '''Clear bits in 64 bit memory at address addr that are set
+ in parameter data and return result'''
+ if addr & 7:
+ raise AlignmentError()
+ return self.request(self.P_CLEAR64, addr, data)
+ def clear32(self, addr, data):
+ '''Clear bits in 32 bit memory at address addr that are set
+ in parameter data and return result'''
+ if addr & 3:
+ raise AlignmentError()
+ return self.request(self.P_CLEAR32, addr, data)
+ def clear16(self, addr, data):
+ '''Clear bits in 16 bit memory at address addr that are set
+ in parameter data and return result'''
+ if addr & 1:
+ raise AlignmentError()
+ return self.request(self.P_CLEAR16, addr, data)
+ def clear8(self, addr, data):
+ '''Clear bits in 8 bit memory at addr that are set in data
+ and return result'''
+ return self.request(self.P_CLEAR8, addr, data)
+
+ def mask64(self, addr, clear, set):
+ '''Clear bits in 64 bit memory at address addr that are
+ set in clear, then set the bits in set and return result'''
+ if addr & 7:
+ raise AlignmentError()
+ return self.request(self.P_MASK64, addr, clear, set)
+ def mask32(self, addr, clear, set):
+ '''Clear bits in 32 bit memory at address addr that are
+ set in clear, then set the bits in set and return result'''
+ if addr & 3:
+ raise AlignmentError()
+ return self.request(self.P_MASK32, addr, clear, set)
+ def mask16(self, addr, clear, set):
+ '''Clear select bits in 16 bit memory addr that are set
+ in clear parameter, then set the bits in set parameter and return result'''
+ if addr & 1:
+ raise AlignmentError()
+ return self.request(self.P_MASK16, addr, clear, set)
+ def mask8(self, addr, clear, set):
+ '''Clear bits in 1 byte memory at addr that are set
+ in clear parameter, then set the bits in set parameter
+ and return the result'''
+ return self.request(self.P_MASK8, addr, clear, set)
+
+ def writeread64(self, addr, data):
+ return self.request(self.P_WRITEREAD64, addr, data)
+ def writeread32(self, addr, data):
+ return self.request(self.P_WRITEREAD32, addr, data)
+ def writeread16(self, addr, data):
+ return self.request(self.P_WRITEREAD16, addr, data)
+ def writeread8(self, addr, data):
+ return self.request(self.P_WRITEREAD8, addr, data)
+
+ def memcpy64(self, dst, src, size):
+ if src & 7 or dst & 7:
+ raise AlignmentError()
+ self.request(self.P_MEMCPY64, dst, src, size)
+ def memcpy32(self, dst, src, size):
+ if src & 3 or dst & 3:
+ raise AlignmentError()
+ self.request(self.P_MEMCPY32, dst, src, size)
+ def memcpy16(self, dst, src, size):
+ if src & 1 or dst & 1:
+ raise AlignmentError()
+ self.request(self.P_MEMCPY16, dst, src, size)
+ def memcpy8(self, dst, src, size):
+ self.request(self.P_MEMCPY8, dst, src, size)
+
+ def memset64(self, dst, src, size):
+ if dst & 7:
+ raise AlignmentError()
+ self.request(self.P_MEMSET64, dst, src, size)
+ def memset32(self, dst, src, size):
+ if dst & 3:
+ raise AlignmentError()
+ self.request(self.P_MEMSET32, dst, src, size)
+ def memset16(self, dst, src, size):
+ if dst & 1:
+ raise AlignmentError()
+ self.request(self.P_MEMSET16, dst, src, size)
+ def memset8(self, dst, src, size):
+ self.request(self.P_MEMSET8, dst, src, size)
+
+ def ic_ialluis(self):
+ self.request(self.P_IC_IALLUIS)
+ def ic_iallu(self):
+ self.request(self.P_IC_IALLU)
+ def ic_ivau(self, addr, size):
+ self.request(self.P_IC_IVAU, addr, size)
+ def dc_ivac(self, addr, size):
+ self.request(self.P_DC_IVAC, addr, size)
+ def dc_isw(self, sw):
+ self.request(self.P_DC_ISW, sw)
+ def dc_csw(self, sw):
+ self.request(self.P_DC_CSW, sw)
+ def dc_cisw(self, sw):
+ self.request(self.P_DC_CISW, sw)
+ def dc_zva(self, addr, size):
+ self.request(self.P_DC_ZVA, addr, size)
+ def dc_cvac(self, addr, size):
+ self.request(self.P_DC_CVAC, addr, size)
+ def dc_cvau(self, addr, size):
+ self.request(self.P_DC_CVAU, addr, size)
+ def dc_civac(self, addr, size):
+ self.request(self.P_DC_CIVAC, addr, size)
+ def mmu_shutdown(self):
+ self.request(self.P_MMU_SHUTDOWN)
+ def mmu_init(self):
+ self.request(self.P_MMU_INIT)
+ def mmu_disable(self):
+ return self.request(self.P_MMU_DISABLE)
+ def mmu_restore(self, flags):
+ self.request(self.P_MMU_RESTORE, flags)
+ def mmu_init_secondary(self, cpu):
+ self.request(self.P_MMU_INIT_SECONDARY, cpu)
+
+
+ def xzdec(self, inbuf, insize, outbuf=0, outsize=0):
+ return self.request(self.P_XZDEC, inbuf, insize, outbuf,
+ outsize, signed=True)
+
+ def gzdec(self, inbuf, insize, outbuf, outsize):
+ return self.request(self.P_GZDEC, inbuf, insize, outbuf,
+ outsize, signed=True)
+
+ def smp_start_secondaries(self):
+ self.request(self.P_SMP_START_SECONDARIES)
+ def smp_call(self, cpu, addr, *args):
+ if len(args) > 4:
+ raise ValueError("Too many arguments")
+ self.request(self.P_SMP_CALL, cpu, addr, *args)
+ def smp_call_sync(self, cpu, addr, *args):
+ if len(args) > 4:
+ raise ValueError("Too many arguments")
+ return self.request(self.P_SMP_CALL_SYNC, cpu, addr, *args)
+ def smp_wait(self, cpu):
+ return self.request(self.P_SMP_WAIT, cpu)
+ def smp_set_wfe_mode(self, mode):
+ return self.request(self.P_SMP_SET_WFE_MODE, mode)
+
+ def heapblock_alloc(self, size):
+ return self.request(self.P_HEAPBLOCK_ALLOC, size)
+ def malloc(self, size):
+ return self.request(self.P_MALLOC, size)
+ def memalign(self, align, size):
+ return self.request(self.P_MEMALIGN, align, size)
+ def free(self, ptr):
+ self.request(self.P_FREE, ptr)
+
+ def kboot_boot(self, kernel):
+ self.request(self.P_KBOOT_BOOT, kernel)
+ def kboot_set_chosen(self, name, value):
+ self.request(self.P_KBOOT_SET_CHOSEN, name, value)
+ def kboot_set_initrd(self, base, size):
+ self.request(self.P_KBOOT_SET_INITRD, base, size)
+ def kboot_prepare_dt(self, dt_addr):
+ return self.request(self.P_KBOOT_PREPARE_DT, dt_addr)
+
+ def pmgr_clock_enable(self, clkid):
+ return self.request(self.P_PMGR_CLOCK_ENABLE, clkid)
+ def pmgr_clock_disable(self, clkid):
+ return self.request(self.P_PMGR_CLOCK_DISABLE, clkid)
+ def pmgr_adt_clocks_enable(self, path):
+ return self.request(self.P_PMGR_ADT_CLOCKS_ENABLE, path)
+ def pmgr_adt_clocks_disable(self, path):
+ return self.request(self.P_PMGR_ADT_CLOCKS_DISABLE, path)
+ def pmgr_reset(self, die, name):
+ return self.request(self.P_PMGR_RESET, die, name)
+
+ def iodev_set_usage(self, iodev, usage):
+ return self.request(self.P_IODEV_SET_USAGE, iodev, usage)
+ def iodev_can_read(self, iodev):
+ return self.request(self.P_IODEV_CAN_READ, iodev)
+ def iodev_can_write(self, iodev):
+ return self.request(self.P_IODEV_CAN_WRITE, iodev)
+ def iodev_read(self, iodev, buf, size=None):
+ return self.request(self.P_IODEV_READ, iodev, buf, size)
+ def iodev_write(self, iodev, buf, size=None):
+ return self.request(self.P_IODEV_WRITE, iodev, buf, size)
+ def iodev_whoami(self):
+ return IODEV(self.request(self.P_IODEV_WHOAMI))
+ def usb_iodev_vuart_setup(self, iodev):
+ return self.request(self.P_USB_IODEV_VUART_SETUP, iodev)
+
+ def tunables_apply_global(self, path, prop):
+ return self.request(self.P_TUNABLES_APPLY_GLOBAL, path, prop)
+ def tunables_apply_local(self, path, prop, reg_offset):
+ return self.request(self.P_TUNABLES_APPLY_LOCAL, path, prop, reg_offset)
+ def tunables_apply_local_addr(self, path, prop, base):
+ return self.request(self.P_TUNABLES_APPLY_LOCAL, path, prop, base)
+
+ def dart_init(self, base, sid, dart_type=DART.T8020):
+ return self.request(self.P_DART_INIT, base, sid, dart_type)
+ def dart_shutdown(self, dart):
+ return self.request(self.P_DART_SHUTDOWN, dart)
+ def dart_map(self, dart, iova, bfr, len):
+ return self.request(self.P_DART_MAP, dart, iova, bfr, len)
+ def dart_unmap(self, dart, iova, len):
+ return self.request(self.P_DART_UNMAP, dart, iova, len)
+
+ def hv_init(self):
+ return self.request(self.P_HV_INIT)
+ def hv_map(self, from_, to, size, incr):
+ return self.request(self.P_HV_MAP, from_, to, size, incr)
+ def hv_start(self, entry, *args):
+ return self.request(self.P_HV_START, entry, *args)
+ def hv_translate(self, addr, s1=False, w=False):
+ '''Translate virtual address
+ stage 1 only if s1, for write if w'''
+ return self.request(self.P_HV_TRANSLATE, addr, s1, w)
+ def hv_pt_walk(self, addr):
+ return self.request(self.P_HV_PT_WALK, addr)
+ def hv_map_vuart(self, base, irq, iodev):
+ return self.request(self.P_HV_MAP_VUART, base, irq, iodev)
+ def hv_trace_irq(self, evt_type, num, count, flags):
+ return self.request(self.P_HV_TRACE_IRQ, evt_type, num, count, flags)
+ def hv_wdt_start(self, cpu):
+ return self.request(self.P_HV_WDT_START, cpu)
+ def hv_start_secondary(self, cpu, entry, *args):
+ return self.request(self.P_HV_START_SECONDARY, cpu, entry, *args)
+ def hv_switch_cpu(self, cpu):
+ return self.request(self.P_HV_SWITCH_CPU, cpu)
+ def hv_set_time_stealing(self, enabled, reset):
+ return self.request(self.P_HV_SET_TIME_STEALING, int(bool(enabled)), int(bool(reset)))
+ def hv_pin_cpu(self, cpu):
+ return self.request(self.P_HV_PIN_CPU, cpu)
+ def hv_write_hcr(self, hcr):
+ return self.request(self.P_HV_WRITE_HCR, hcr)
+ def hv_map_virtio(self, base, config):
+ return self.request(self.P_HV_MAP_VIRTIO, base, config)
+ def virtio_put_buffer(self, base, qu, idx, length):
+ return self.request(self.P_VIRTIO_PUT_BUFFER, base, qu, idx, length)
+
+ def fb_init(self):
+ return self.request(self.P_FB_INIT)
+ def fb_shutdown(self, restore_logo=True):
+ return self.request(self.P_FB_SHUTDOWN, restore_logo)
+ def fb_blit(self, x, y, w, h, ptr, stride, pix_fmt=PIX_FMT.XRGB):
+ return self.request(self.P_FB_BLIT, x, y, w, h, ptr, stride | pix_fmt << 32)
+ def fb_unblit(self, x, y, w, h, ptr, stride):
+ return self.request(self.P_FB_UNBLIT, x, y, w, h, ptr, stride)
+ def fb_fill(self, x, y, w, h, color):
+ return self.request(self.P_FB_FILL, x, y, w, h, color)
+ def fb_clear(self, color):
+ return self.request(self.P_FB_CLEAR, color)
+ def fb_display_logo(self):
+ return self.request(self.P_FB_DISPLAY_LOGO)
+ def fb_restore_logo(self):
+ return self.request(self.P_FB_RESTORE_LOGO)
+ def fb_improve_logo(self):
+ return self.request(self.P_FB_IMPROVE_LOGO)
+
+ def pcie_init(self):
+ return self.request(self.P_PCIE_INIT)
+ def pcie_shutdown(self):
+ return self.request(self.P_PCIE_SHUTDOWN)
+
+ def nvme_init(self):
+ return self.request(self.P_NVME_INIT)
+ def nvme_shutdown(self):
+ return self.request(self.P_NVME_SHUTDOWN)
+ def nvme_read(self, nsid, lba, bfr):
+ return self.request(self.P_NVME_READ, nsid, lba, bfr)
+ def nvme_flush(self, nsid):
+ return self.request(self.P_NVME_FLUSH, nsid)
+
+ def mcc_get_carveouts(self):
+ return self.request(self.P_MCC_GET_CARVEOUTS)
+
+ def display_init(self):
+ return self.request(self.P_DISPLAY_INIT)
+ def display_configure(self, cfg):
+ return self.request(self.P_DISPLAY_CONFIGURE, cfg)
+ def display_shutdown(self, mode):
+ return self.request(self.P_DISPLAY_SHUTDOWN, mode)
+ def display_start_dcp(self):
+ return self.request(self.P_DISPLAY_START_DCP)
+ def display_is_external(self):
+ return self.request(self.P_DISPLAY_IS_EXTERNAL)
+
+ def dapf_init_all(self):
+ return self.request(self.P_DAPF_INIT_ALL)
+ def dapf_init(self, path):
+ return self.request(self.P_DAPF_INIT, path)
+
+__all__.extend(k for k, v in globals().items()
+ if (callable(v) or isinstance(v, type)) and v.__module__ == __name__)
+
+if __name__ == "__main__":
+ import serial
+ uartdev = os.environ.get("M1N1DEVICE", "/dev/m1n1")
+ usbuart = serial.Serial(uartdev, 115200)
+ uartif = UartInterface(usbuart, debug=True)
+ print("Sending NOP...", end=' ')
+ uartif.nop()
+ print("OK")
+ proxy = M1N1Proxy(uartif, debug=True)
+ print("Sending Proxy NOP...", end=' ')
+ proxy.nop()
+ print("OK")
+ print("Boot args: 0x%x" % proxy.get_bootargs())
diff --git a/tools/proxyclient/m1n1/proxyutils.py b/tools/proxyclient/m1n1/proxyutils.py
new file mode 100644
index 0000000..ca75c41
--- /dev/null
+++ b/tools/proxyclient/m1n1/proxyutils.py
@@ -0,0 +1,550 @@
+# SPDX-License-Identifier: MIT
+import serial, os, struct, sys, time, json, os.path, gzip, functools
+from contextlib import contextmanager
+from construct import *
+
+from .asm import ARMAsm
+from .proxy import *
+from .utils import Reloadable, chexdiff32
+from .tgtypes import *
+from .sysreg import *
+from .malloc import Heap
+from . import adt
+
+__all__ = ["ProxyUtils", "RegMonitor", "GuardedHeap", "bootstrap_port"]
+
+SIMD_B = Array(32, Array(16, Int8ul))
+SIMD_H = Array(32, Array(8, Int16ul))
+SIMD_S = Array(32, Array(4, Int32ul))
+SIMD_D = Array(32, Array(2, Int64ul))
+SIMD_Q = Array(32, BytesInteger(16, swapped=True))
+
+# This isn't perfect, since multiple versions could have the same
+# iBoot version, but it's good enough
+VERSION_MAP = {
+ "iBoot-7429.61.2": "V12_1",
+ "iBoot-7459.101.2": "V12_3",
+ "iBoot-7459.121.3": "V12_4",
+ "iBoot-8419.0.151.0.1": "V13_0B4",
+}
+
+class ProxyUtils(Reloadable):
+ CODE_BUFFER_SIZE = 0x10000
+ def __init__(self, p, heap_size=1024 * 1024 * 1024):
+ self.iface = p.iface
+ self.proxy = p
+ self.base = p.get_base()
+ self.ba_addr = p.get_bootargs()
+
+ self.ba = self.iface.readstruct(self.ba_addr, BootArgs)
+
+ # We allocate a 128MB heap, 128MB after the m1n1 heap, without telling it about it.
+ # This frees up from having to coordinate memory management or free stuff after a Python
+ # script runs, at the expense that if m1n1 ever uses more than 128MB of heap it will
+ # clash with Python (m1n1 will normally not use *any* heap when running proxy ops though,
+ # except when running very high-level operations like booting a kernel, so this should be
+ # OK).
+ self.heap_size = heap_size
+ try:
+ self.heap_base = p.heapblock_alloc(0)
+ except ProxyRemoteError:
+ # Compat with versions that don't have heapblock yet
+ self.heap_base = (self.base + ((self.ba.top_of_kernel_data + 0xffff) & ~0xffff) -
+ self.ba.phys_base)
+
+ if os.environ.get("M1N1HEAP", ""):
+ self.heap_base = int(os.environ.get("M1N1HEAP", ""), 16)
+
+ self.heap_base += 128 * 1024 * 1024 # We leave 128MB for m1n1 heap
+ self.heap_top = self.heap_base + self.heap_size
+ self.heap = Heap(self.heap_base, self.heap_top)
+ self.proxy.heap = self.heap
+
+ self.malloc = self.heap.malloc
+ self.memalign = self.heap.memalign
+ self.free = self.heap.free
+
+ self.code_buffer = self.malloc(self.CODE_BUFFER_SIZE)
+
+ self.adt_data = None
+ self.adt = LazyADT(self)
+
+ self.simd_buf = self.malloc(32 * 16)
+ self.simd_type = None
+ self.simd = None
+
+ self.mmu_off = False
+
+ self.inst_cache = {}
+
+ self.exec_modes = {
+ None: (self.proxy.call, REGION_RX_EL1),
+ "el2": (self.proxy.call, REGION_RX_EL1),
+ "el1": (self.proxy.el1_call, 0),
+ "el0": (self.proxy.el0_call, REGION_RWX_EL0),
+ "gl2": (self.proxy.gl2_call, REGION_RX_EL1),
+ "gl1": (self.proxy.gl1_call, 0),
+ }
+ self._read = {
+ 8: lambda addr: self.proxy.read8(addr),
+ 16: lambda addr: self.proxy.read16(addr),
+ 32: lambda addr: self.proxy.read32(addr),
+ 64: lambda addr: self.uread64(addr),
+ 128: lambda addr: [self.uread64(addr),
+ self.uread64(addr + 8)],
+ 256: lambda addr: [self.uread64(addr),
+ self.uread64(addr + 8),
+ self.uread64(addr + 16),
+ self.uread64(addr + 24)],
+ 512: lambda addr: [self.uread64(addr + i) for i in range(0, 64, 8)],
+ }
+ self._write = {
+ 8: lambda addr, data: self.proxy.write8(addr, data),
+ 16: lambda addr, data: self.proxy.write16(addr, data),
+ 32: lambda addr, data: self.proxy.write32(addr, data),
+ 64: lambda addr, data: self.uwrite64(addr, data),
+ 128: lambda addr, data: (self.uwrite64(addr, data[0]),
+ self.uwrite64(addr + 8, data[1])),
+ 256: lambda addr, data: (self.uwrite64(addr, data[0]),
+ self.uwrite64(addr + 8, data[1]),
+ self.uwrite64(addr + 16, data[2]),
+ self.uwrite64(addr + 24, data[3])),
+ 512: lambda addr, data: [self.uwrite64(addr + 8 * i, data[i])
+ for i in range(8)],
+ }
+
+ def uwrite64(self, addr, data):
+ '''write 8 byte value to given address, supporting split 4-byte halves'''
+ if addr & 3:
+ raise AlignmentError()
+ if addr & 4:
+ self.proxy.write32(addr, data & 0xffffffff)
+ self.proxy.write32(addr + 4, data >> 32)
+ else:
+ self.proxy.write64(addr, data)
+
+ def uread64(self, addr):
+ '''write 8 byte value to given address, supporting split 4-byte halves'''
+ if addr & 3:
+ raise AlignmentError()
+ if addr & 4:
+ return self.proxy.read32(addr) | (self.proxy.read32(addr + 4) << 32)
+ else:
+ return self.proxy.read64(addr)
+
+ def read(self, addr, width):
+ '''do a width read from addr and return it
+ width can be 8, 16, 21, 64, 128 or 256'''
+ val = self._read[width](addr)
+ if self.proxy.get_exc_count():
+ raise ProxyError("Exception occurred")
+ return val
+
+ def write(self, addr, data, width):
+ '''do a width write of data to addr
+ width can be 8, 16, 21, 64, 128 or 256'''
+ self._write[width](addr, data)
+ if self.proxy.get_exc_count():
+ raise ProxyError("Exception occurred")
+
+ def mrs(self, reg, *, silent=False, call=None):
+ '''read system register reg'''
+ op0, op1, CRn, CRm, op2 = sysreg_parse(reg)
+
+ op = ((op0 << 19) | (op1 << 16) | (CRn << 12) |
+ (CRm << 8) | (op2 << 5) | 0xd5200000)
+
+ return self.exec(op, call=call, silent=silent)
+
+ def msr(self, reg, val, *, silent=False, call=None):
+ '''Write val to system register reg'''
+ op0, op1, CRn, CRm, op2 = sysreg_parse(reg)
+
+ op = ((op0 << 19) | (op1 << 16) | (CRn << 12) |
+ (CRm << 8) | (op2 << 5) | 0xd5000000)
+
+ self.exec(op, val, call=call, silent=silent)
+
+ sys = msr
+ sysl = mrs
+
+ def exec(self, op, r0=0, r1=0, r2=0, r3=0, *, silent=False, call=None, ignore_exceptions=False):
+ if callable(call):
+ region = REGION_RX_EL1
+ elif isinstance(call, tuple):
+ call, region = call
+ else:
+ call, region = self.exec_modes[call]
+
+ if isinstance(op, list):
+ op = tuple(op)
+
+ if op in self.inst_cache:
+ func = self.inst_cache[op]
+ elif isinstance(op, tuple) or isinstance(op, list):
+ func = struct.pack(f"<{len(op)}II", *op, 0xd65f03c0) # ret
+ elif isinstance(op, int):
+ func = struct.pack("<II", op, 0xd65f03c0) # ret
+ elif isinstance(op, str):
+ c = ARMAsm(op + "; ret", self.code_buffer)
+ func = c.data
+ elif isinstance(op, bytes):
+ func = op
+ else:
+ raise ValueError()
+
+ if self.mmu_off:
+ region = 0
+
+ self.inst_cache[op] = func
+
+ assert len(func) < self.CODE_BUFFER_SIZE
+ self.iface.writemem(self.code_buffer, func)
+ self.proxy.dc_cvau(self.code_buffer, len(func))
+ self.proxy.ic_ivau(self.code_buffer, len(func))
+
+ self.proxy.set_exc_guard(GUARD.SKIP | (GUARD.SILENT if silent else 0))
+ ret = call(self.code_buffer | region, r0, r1, r2, r3)
+ if not ignore_exceptions:
+ cnt = self.proxy.get_exc_count()
+ self.proxy.set_exc_guard(GUARD.OFF)
+ if cnt:
+ raise ProxyError("Exception occurred")
+ else:
+ self.proxy.set_exc_guard(GUARD.OFF)
+
+ return ret
+
+ inst = exec
+
+ def compressed_writemem(self, dest, data, progress=None):
+ if not len(data):
+ return
+
+ payload = gzip.compress(data, compresslevel=1)
+ compressed_size = len(payload)
+
+ with self.heap.guarded_malloc(compressed_size) as compressed_addr:
+ self.iface.writemem(compressed_addr, payload, progress)
+ timeout = self.iface.dev.timeout
+ self.iface.dev.timeout = None
+ try:
+ decompressed_size = self.proxy.gzdec(compressed_addr, compressed_size, dest, len(data))
+ finally:
+ self.iface.dev.timeout = timeout
+
+ assert decompressed_size == len(data)
+
+ def get_adt(self):
+ if self.adt_data is not None:
+ return self.adt_data
+ adt_base = (self.ba.devtree - self.ba.virt_base + self.ba.phys_base) & 0xffffffffffffffff
+ adt_size = self.ba.devtree_size
+ print(f"Fetching ADT ({adt_size} bytes)...")
+ self.adt_data = self.iface.readmem(adt_base, self.ba.devtree_size)
+ return self.adt_data
+
+ def push_adt(self):
+ self.adt_data = self.adt.build()
+ adt_base = (self.ba.devtree - self.ba.virt_base + self.ba.phys_base) & 0xffffffffffffffff
+ adt_size = len(self.adt_data)
+ print(f"Pushing ADT ({adt_size} bytes)...")
+ self.iface.writemem(adt_base, self.adt_data)
+
+ def disassemble_at(self, start, size, pc=None, vstart=None, sym=None):
+ '''disassemble len bytes of memory from start
+ optional pc address will mark that line with a '*' '''
+ code = struct.unpack(f"<{size // 4}I", self.iface.readmem(start, size))
+ if vstart is None:
+ vstart = start
+
+ c = ARMAsm(".inst " + ",".join(str(i) for i in code), vstart)
+ lines = list()
+ for line in c.disassemble():
+ sl = line.split()
+ try:
+ addr = int(sl[0].rstrip(":"), 16)
+ except:
+ addr = None
+ if pc == addr:
+ line = " *" + line
+ else:
+ line = " " + line
+ if sym:
+ if s := sym(addr):
+ print()
+ print(f"{' '*len(sl[0])} {s}:")
+ print(line)
+
+ def print_l2c_regs(self):
+ print()
+ print(" == L2C Registers ==")
+ l2c_err_sts = self.mrs(L2C_ERR_STS_EL1)
+
+ print(f" L2C_ERR_STS: {l2c_err_sts:#x}")
+ print(f" L2C_ERR_ADR: {self.mrs(L2C_ERR_ADR_EL1):#x}");
+ print(f" L2C_ERR_INF: {self.mrs(L2C_ERR_INF_EL1):#x}");
+
+ self.msr(L2C_ERR_STS_EL1, l2c_err_sts) # Clear the flag bits
+ self.msr(DAIF, self.mrs(DAIF) | 0x100) # Re-enable SError exceptions
+
+ def print_context(self, ctx, is_fault=True, addr=lambda a: f"0x{a:x}", sym=None, num_ctx=9):
+ print(f" == Exception taken from {ctx.spsr.M.name} ==")
+ el = ctx.spsr.M >> 2
+ print(f" SPSR = {ctx.spsr}")
+ print(f" ELR = {addr(ctx.elr)}" + (f" (0x{ctx.elr_phys:x})" if ctx.elr_phys else ""))
+ print(f" SP_EL{el} = 0x{ctx.sp[el]:x}" + (f" (0x{ctx.sp_phys:x})" if ctx.sp_phys else ""))
+ if is_fault:
+ print(f" ESR = {ctx.esr}")
+ print(f" FAR = {addr(ctx.far)}" + (f" (0x{ctx.far_phys:x})" if ctx.far_phys else ""))
+
+ for i in range(0, 31, 4):
+ j = min(30, i + 3)
+ print(f" {f'x{i}-x{j}':>7} = {' '.join(f'{r:016x}' for r in ctx.regs[i:j + 1])}")
+
+ if ctx.elr_phys:
+ print()
+ print(" == Code context ==")
+
+ off = -(num_ctx // 2)
+
+ self.disassemble_at(ctx.elr_phys + 4 * off, num_ctx * 4, ctx.elr, ctx.elr + 4 * off, sym=sym)
+
+ if is_fault:
+ if ctx.esr.EC == ESR_EC.MSR or ctx.esr.EC == ESR_EC.IMPDEF and ctx.esr.ISS == 0x20:
+ print()
+ print(" == MRS/MSR fault decoding ==")
+ if ctx.esr.EC == ESR_EC.MSR:
+ iss = ESR_ISS_MSR(ctx.esr.ISS)
+ else:
+ iss = ESR_ISS_MSR(self.mrs(AFSR1_EL2))
+ enc = iss.Op0, iss.Op1, iss.CRn, iss.CRm, iss.Op2
+ if enc in sysreg_rev:
+ name = sysreg_rev[enc]
+ else:
+ name = f"s{iss.Op0}_{iss.Op1}_c{iss.CRn}_c{iss.CRm}_{iss.Op2}"
+ if iss.DIR == MSR_DIR.READ:
+ print(f" Instruction: mrs x{iss.Rt}, {name}")
+ else:
+ print(f" Instruction: msr {name}, x{iss.Rt}")
+
+ if ctx.esr.EC in (ESR_EC.DABORT, ESR_EC.DABORT_LOWER):
+ print()
+ print(" == Data abort decoding ==")
+ iss = ESR_ISS_DABORT(ctx.esr.ISS)
+ if iss.ISV:
+ print(f" ISS: {iss!s}")
+ else:
+ print(" No instruction syndrome available")
+
+ if iss.DFSC == DABORT_DFSC.ECC_ERROR:
+ self.print_l2c_regs()
+
+ if ctx.esr.EC == ESR_EC.SERROR and ctx.esr.ISS == 0:
+ self.print_l2c_regs()
+
+ print()
+
+ @contextmanager
+ def mmu_disabled(self):
+ flags = self.proxy.mmu_disable()
+ try:
+ yield
+ finally:
+ self.proxy.mmu_restore(flags)
+
+ def push_simd(self):
+ if self.simd is not None:
+ data = self.simd_type.build(self.simd)
+ self.iface.writemem(self.simd_buf, data)
+ self.proxy.put_simd_state(self.simd_buf)
+ self.simd = self.simd_type = None
+
+ def get_simd(self, simd_type):
+ if self.simd is not None and self.simd_type is not simd_type:
+ data = self.simd_type.build(self.simd)
+ self.simd = simd_type.parse(data)
+ self.simd_type = simd_type
+ elif self.simd is None:
+ self.proxy.get_simd_state(self.simd_buf)
+ data = self.iface.readmem(self.simd_buf, 32 * 16)
+ self.simd = simd_type.parse(data)
+ self.simd_type = simd_type
+
+ return self.simd
+
+ @property
+ def b(self):
+ return self.get_simd(SIMD_B)
+ @property
+ def h(self):
+ return self.get_simd(SIMD_H)
+ @property
+ def s(self):
+ return self.get_simd(SIMD_S)
+ @property
+ def d(self):
+ return self.get_simd(SIMD_D)
+ @property
+ def q(self):
+ return self.get_simd(SIMD_Q)
+
+ def get_version(self, v):
+ if isinstance(v, bytes):
+ v = v.split(b"\0")[0].decode("ascii")
+ return VERSION_MAP.get(v, None)
+
+ @property
+ def version(self):
+ return self.get_version(self.adt["/chosen"].firmware_version)
+
+ @property
+ def sfr_version(self):
+ return self.get_version(self.adt["/chosen"].system_firmware_version)
+
+class LazyADT:
+ def __init__(self, utils):
+ self.__dict__["_utils"] = utils
+
+ @functools.cached_property
+ def _adt(self):
+ return adt.load_adt(self._utils.get_adt())
+ def __getitem__(self, item):
+ return self._adt[item]
+ def __setitem__(self, item, value):
+ self._adt[item] = value
+ def __delitem__(self, item):
+ del self._adt[item]
+ def __contains__(self, item):
+ return item in self._adt
+ def __getattr__(self, attr):
+ return getattr(self._adt, attr)
+ def __setattr__(self, attr, value):
+ return setattr(self._adt, attr, value)
+ def __delattr__(self, attr):
+ return delattr(self._adt, attr)
+ def __str__(self, t=""):
+ return str(self._adt)
+ def __iter__(self):
+ return iter(self._adt)
+
+class RegMonitor(Reloadable):
+ def __init__(self, utils, bufsize=0x100000, ascii=False, log=None):
+ self.utils = utils
+ self.proxy = utils.proxy
+ self.iface = self.proxy.iface
+ self.ranges = []
+ self.last = []
+ self.bufsize = bufsize
+ self.ascii = ascii
+ self.log = log or print
+
+ if bufsize:
+ self.scratch = utils.malloc(bufsize)
+ else:
+ self.scratch = None
+
+ def readmem(self, start, size, readfn):
+ if readfn:
+ return readfn(start, size)
+ if self.scratch:
+ assert size < self.bufsize
+ self.proxy.memcpy32(self.scratch, start, size)
+ start = self.scratch
+ return self.proxy.iface.readmem(start, size)
+
+ def add(self, start, size, name=None, offset=None, readfn=None):
+ if offset is None:
+ offset = start
+ self.ranges.append((start, size, name, offset, readfn))
+ self.last.append(None)
+
+ def show_regions(self, log=print):
+ for start, size, name, offset, readfn in sorted(self.ranges):
+ end = start + size - 1
+ log(f"{start:#x}..{end:#x} ({size:#x})\t{name}")
+
+ def poll(self):
+ if not self.ranges:
+ return
+ cur = []
+ for (start, size, name, offset, readfn), last in zip(self.ranges, self.last):
+ count = size // 4
+ block = self.readmem(start, size, readfn)
+ if block is None:
+ if last is not None:
+ self.log(f"# Lost: {name} ({start:#x}..{start + size - 1:#x})")
+ cur.append(None)
+ continue
+
+ words = struct.unpack("<%dI" % count, block)
+ cur.append(block)
+ if last == block:
+ continue
+ if name:
+ header = f"# {name} ({start:#x}..{start + size - 1:#x})\n"
+ else:
+ header = f"# ({start:#x}..{start + size - 1:#x})\n"
+
+ self.log(header + chexdiff32(last, block, offset=offset))
+ self.last = cur
+
+class GuardedHeap:
+ def __init__(self, malloc, memalign=None, free=None):
+ if isinstance(malloc, Heap):
+ malloc, memalign, free = malloc.malloc, malloc.memalign, malloc.free
+
+ self.ptrs = set()
+ self._malloc = malloc
+ self._memalign = memalign
+ self._free = free
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *exc):
+ self.free_all()
+ return False
+
+ def malloc(self, sz):
+ ptr = self._malloc(sz)
+ self.ptrs.add(ptr)
+ return ptr
+
+ def memalign(self, align, sz):
+ ptr = self._memalign(align, sz)
+ self.ptrs.add(ptr)
+ return ptr
+
+ def free(self, ptr):
+ self.ptrs.remove(ptr)
+ self._free(ptr)
+
+ def free_all(self):
+ for ptr in self.ptrs:
+ self._free(ptr)
+ self.ptrs = set()
+
+def bootstrap_port(iface, proxy):
+ to = iface.dev.timeout
+ iface.dev.timeout = 0.15
+ try:
+ do_baud = proxy.iodev_whoami() == IODEV.UART
+ except ProxyCommandError:
+ # Old m1n1 version -- assume non-USB serial link, force baudrate adjust
+ do_baud = True
+ except UartTimeout:
+ # Assume the receiving end is already at 1500000
+ iface.dev.baudrate = 1500000
+ do_baud = False
+
+ if do_baud:
+ try:
+ iface.nop()
+ proxy.set_baud(1500000)
+ except UartTimeout:
+ # May fail even if the setting did get applied; checked by the .nop next
+ iface.dev.baudrate = 1500000
+
+ iface.nop()
+ iface.dev.timeout = to
diff --git a/tools/proxyclient/m1n1/setup.py b/tools/proxyclient/m1n1/setup.py
new file mode 100644
index 0000000..8a66287
--- /dev/null
+++ b/tools/proxyclient/m1n1/setup.py
@@ -0,0 +1,33 @@
+# SPDX-License-Identifier: MIT
+import os, struct, sys, time
+
+from .hv import HV
+from .proxy import *
+from .proxyutils import *
+from .sysreg import *
+from .tgtypes import *
+from .utils import *
+from .hw.pmu import PMU
+
+# Create serial connection
+iface = UartInterface()
+# Construct m1n1 proxy layer over serial connection
+p = M1N1Proxy(iface, debug=False)
+# Customise parameters of proxy and serial port
+# based on information sent over the connection
+bootstrap_port(iface, p)
+
+# Initialise the Proxy interface from values fetched from
+# the remote end
+u = ProxyUtils(p)
+# Build a Register Monitoring object on Proxy Interface
+mon = RegMonitor(u)
+hv = HV(iface, p, u)
+
+fb = u.ba.video.base
+
+PMU(u).reset_panic_counter()
+
+print(f"m1n1 base: 0x{u.base:x}")
+
+PMU(u).reset_panic_counter()
diff --git a/tools/proxyclient/m1n1/shell.py b/tools/proxyclient/m1n1/shell.py
new file mode 100644
index 0000000..06e0605
--- /dev/null
+++ b/tools/proxyclient/m1n1/shell.py
@@ -0,0 +1,213 @@
+# SPDX-License-Identifier: MIT
+import atexit, serial, os, struct, code, traceback, readline, rlcompleter, sys
+import __main__
+import builtins
+import re
+
+from .proxy import *
+from .proxyutils import *
+from .utils import *
+from . import sysreg
+from inspect import isfunction, signature
+
+__all__ = ["ExitConsole", "run_shell"]
+
+class HistoryConsole(code.InteractiveConsole):
+ def __init__(self, locals=None, filename="<console>",
+ histfile=os.path.expanduser("~/.m1n1-history")):
+ code.InteractiveConsole.__init__(self, locals, filename)
+ self.histfile = histfile
+ self.init_history(histfile)
+ self.poll_func = None
+
+ def init_history(self, histfile):
+ readline.parse_and_bind("tab: complete")
+ if hasattr(readline, "read_history_file"):
+ try:
+ readline.read_history_file(histfile)
+ except FileNotFoundError:
+ pass
+
+ def save_history(self):
+ readline.set_history_length(10000)
+ readline.write_history_file(self.histfile)
+
+ def showtraceback(self):
+ type, value, tb = sys.exc_info()
+ traceback.print_exception(type, value, tb)
+
+ def runcode(self, code):
+ super().runcode(code)
+ if self.poll_func:
+ self.poll_func()
+ if "mon" in self.locals:
+ try:
+ self.locals["mon"].poll()
+ except Exception as e:
+ print(f"mon.poll() failed: {e!r}")
+ if "u" in self.locals:
+ self.locals["u"].push_simd()
+
+
+class ExitConsole(SystemExit):
+ pass
+cmd_list = {}
+subcmd_list = {}
+# Debug levels
+DBL_NONE = 0
+DBL_INFO = 1
+DBL_TRACE = 2
+DBL_DEBUG = 3
+DBL_EDEBUG = 4
+
+db_level = DBL_NONE
+
+def debug_cmd(db=None):
+ '''Set debug level to integer %d(none)...%d(extreme debug)''' % (DBL_NONE, DBL_EDEBUG)
+ global db_level
+ if db:
+ db_level = db
+ print("debug level=%d" % db_level)
+
+def help_cmd(arg=None):
+ if db_level >= DBL_DEBUG:
+ print("arg=%s" % repr(arg))
+ if arg:
+ #cmd = arg.__qualname__
+ if callable(arg):
+ cmd = arg.__name__
+ elif isinstance(arg, str):
+ cmd = arg
+ else:
+ print("Unknown command: %s" % repr(arg))
+ return
+ if db_level >= DBL_DEBUG:
+ print("cmd=%s" % repr(cmd))
+ if cmd not in cmd_list:
+ print("Undocumented command %s" % cmd)
+ return
+ hinfo = cmd_list[cmd]
+ if isinstance(hinfo, str):
+ print("%-10s : %s" % (cmd, hinfo))
+ return
+ if cmd in subcmd_list:
+ clist = subcmd_list[cmd]
+ aname = cmd
+ if db_level >= DBL_DEBUG:
+ print("subcmd_list[%s] = %s" %
+ (repr(cmd), repr(clist)))
+ else:
+ print("command %s is not documented" % cmd)
+ return
+ else:
+ clist = cmd_list
+ aname = 'top level'
+ print("Note: To display a category's commands quote the name e.g. help('HV')")
+ print("List of %s commands:" % aname)
+ for cmd in clist.keys():
+ hinfo = clist[cmd]
+ if isinstance(hinfo, str):
+ msg = hinfo.strip().split('\n', 1)[0]
+ elif isinstance(hinfo, int):
+ msg = "%s category - %d subcommands" % (cmd, hinfo)
+ else:
+ print("%s ?" % cmd)
+ continue
+ if len(cmd) <= 10:
+ print("%-10s : %s" % (cmd, msg))
+ else:
+ print("%s:\n %s" % (cmd, msg))
+
+#locals is a dictionary for constructing the
+# InteractiveConsole with. It adds in the callables
+# in proxy utils iface and sysreg into locals
+def run_shell(locals, msg=None, exitmsg=None, poll_func=None):
+ saved_display = sys.displayhook
+ try:
+ def display(val):
+ if isinstance(val, int) and not isinstance(val, bool):
+ builtins._ = val
+ print(hex(val))
+ elif callable(val):
+ val()
+ else:
+ saved_display(val)
+
+ sys.displayhook = display
+
+ # convenience
+ locals["h"] = hex
+ locals["sysreg"] = sysreg
+
+ if "proxy" in locals and "p" not in locals:
+ locals["p"] = locals["proxy"]
+ if "utils" in locals and "u" not in locals:
+ locals["u"] = locals["utils"]
+
+ for obj_name in ("iface", "p", "u"):
+ obj = locals.get(obj_name)
+ obj_class = type(obj)
+ if obj is None:
+ continue
+
+ for attr in dir(obj_class):
+ if attr in locals or attr.startswith('_'):
+ continue
+
+ member = getattr(obj_class, attr)
+ if callable(member) and not isinstance(member, property):
+ cmd = getattr(obj, attr)
+ locals[attr] = cmd
+
+ for attr in dir(sysreg):
+ locals[attr] = getattr(sysreg, attr)
+
+ locals['help'] = help_cmd
+ locals['debug'] = debug_cmd
+ for obj_name in locals.keys():
+ obj = locals.get(obj_name)
+ if obj is None or obj_name.startswith('_'):
+ continue
+ if callable(obj) and not isinstance(obj, property):
+ try:
+ desc = obj_name + str(signature(obj))
+ except:
+ continue
+ qn = obj.__qualname__
+ if qn.find('.') > 0:
+ a = qn.split('.')
+ if a[0] not in subcmd_list:
+ subcmd_list[a[0]] = {}
+ if a[0] not in cmd_list:
+ cmd_list[a[0]] = 1
+ else:
+ cmd_list[a[0]] += 1
+ clist = subcmd_list[a[0]]
+ else:
+ clist = None
+ if locals[obj_name].__doc__:
+ desc += " - " + locals[obj_name].__doc__
+ cmd_list[obj_name] = desc
+ if isinstance(clist, dict):
+ clist[obj_name] = desc
+
+ con = HistoryConsole(locals)
+ con.poll_func = poll_func
+ try:
+ con.interact(msg, exitmsg)
+ except ExitConsole as e:
+ if len(e.args):
+ return e.args[0]
+ else:
+ return
+ finally:
+ con.save_history()
+
+ finally:
+ sys.displayhook = saved_display
+
+if __name__ == "__main__":
+ from .setup import *
+ locals = dict(__main__.__dict__)
+
+ run_shell(locals, msg="Have fun!")
diff --git a/tools/proxyclient/m1n1/sysreg.py b/tools/proxyclient/m1n1/sysreg.py
new file mode 100644
index 0000000..3c08238
--- /dev/null
+++ b/tools/proxyclient/m1n1/sysreg.py
@@ -0,0 +1,383 @@
+# SPDX-License-Identifier: MIT
+import json, os, re
+from enum import Enum, IntEnum, IntFlag
+from .utils import Register, Register64, Register32
+
+__all__ = ["sysreg_fwd", "sysreg_rev"]
+
+def _load_registers():
+ global sysreg_fwd, sysop_fwd
+
+ sysreg_fwd = {}
+ sysop_fwd = {}
+ for fname in ["arm_regs.json", "apple_regs.json"]:
+ data = json.load(open(os.path.join(os.path.dirname(__file__), "..", "..", "tools", fname)))
+ for reg in data:
+ if "accessors" in reg:
+ for acc in reg["accessors"]:
+ if acc in ("MRS", "MSR"):
+ sysreg_fwd[reg["name"]] = tuple(reg["enc"])
+ else:
+ sysop_fwd[acc + " " + reg["name"]] = tuple(reg["enc"])
+ else:
+ sysreg_fwd[reg["name"]] = tuple(reg["enc"])
+
+_load_registers()
+sysreg_rev = {v: k for k, v in sysreg_fwd.items()}
+sysop_rev = {v: k for k, v in sysop_fwd.items()}
+sysop_fwd_id = {k.replace(" ", "_"): v for k,v in sysop_fwd.items()}
+
+globals().update(sysreg_fwd)
+__all__.extend(sysreg_fwd.keys())
+globals().update(sysop_fwd_id)
+__all__.extend(sysop_fwd_id.keys())
+
+def sysreg_name(enc):
+ if enc in sysreg_rev:
+ return sysreg_rev[enc]
+ if enc in sysop_rev:
+ return sysop_rev[enc]
+ return f"s{enc[0]}_{enc[1]}_c{enc[2]}_c{enc[3]}_{enc[4]}"
+
+def sysreg_parse(s):
+ if isinstance(s, tuple) or isinstance(s, list):
+ return tuple(s)
+ s = s.strip()
+ for r in (r"s(\d+)_(\d+)_c(\d+)_c(\d+)_(\d+)", r"(\d+), *(\d+), *(\d+), *(\d+), *(\d+)"):
+ if m := re.match(r, s):
+ enc = tuple(map(int, m.groups()))
+ break
+ else:
+ for i in sysreg_fwd, sysop_fwd, sysop_fwd_id:
+ try:
+ enc = i[s]
+ except KeyError:
+ continue
+ break
+ else:
+ raise Exception(f"Unknown sysreg name {s}")
+ return enc
+
+def DBGBCRn_EL1(n):
+ return (2,0,0,n,5)
+
+def DBGBVRn_EL1(n):
+ return (2,0,0,n,4)
+
+def DBGWCRn_EL1(n):
+ return (2,0,0,n,7)
+
+def DBGWVRn_EL1(n):
+ return (2,0,0,n,6)
+
+class ESR_EC(IntEnum):
+ UNKNOWN = 0b000000
+ WFI = 0b000001
+ FP_TRAP = 0b000111
+ PAUTH_TRAP = 0b001000
+ LS64 = 0b001010
+ BTI = 0b001101
+ ILLEGAL = 0b001110
+ SVC = 0b010101
+ HVC = 0b010110
+ SMC = 0b010111
+ MSR = 0b011000
+ SVE = 0b011001
+ PAUTH_FAIL = 0b011100
+ IABORT_LOWER = 0b100000
+ IABORT = 0b100001
+ PC_ALIGN = 0b100010
+ DABORT_LOWER = 0b100100
+ DABORT = 0b100101
+ SP_ALIGN = 0b100110
+ FP_EXC = 0b101100
+ SERROR = 0b101111
+ BKPT_LOWER = 0b110000
+ BKPT = 0b110001
+ SSTEP_LOWER = 0b110010
+ SSTEP = 0b110011
+ WATCH_LOWER = 0b110100
+ WATCH = 0b110101
+ BRK = 0b111100
+ IMPDEF = 0b111111
+
+class MSR_DIR(IntEnum):
+ WRITE = 0
+ READ = 1
+
+class ESR_ISS_MSR(Register32):
+ Op0 = 21, 20
+ Op2 = 19, 17
+ Op1 = 16, 14
+ CRn = 13, 10
+ Rt = 9, 5
+ CRm = 4, 1
+ DIR = 0, 0, MSR_DIR
+
+class DABORT_DFSC(IntEnum):
+ ASIZE_L0 = 0b000000
+ ASIZE_L1 = 0b000001
+ ASIZE_L2 = 0b000010
+ ASIZE_L3 = 0b000011
+ XLAT_L0 = 0b000100
+ XLAT_L1 = 0b000101
+ XLAT_L2 = 0b000110
+ XLAT_L3 = 0b000111
+ AF_L0 = 0b001000
+ AF_L1 = 0b001001
+ AF_L2 = 0b001010
+ AF_L3 = 0b001011
+ PERM_L0 = 0b001100
+ PERM_L1 = 0b001101
+ PERM_L2 = 0b001110
+ PERM_L3 = 0b001111
+ EABORT = 0b010000
+ TAG_CHECK = 0b010001
+ PT_EABORT_Lm1 = 0b010011
+ PT_EABORT_L0 = 0b010100
+ PT_EABORT_L1 = 0b010101
+ PT_EABORT_L2 = 0b010110
+ PT_EABORT_L3 = 0b010111
+ ECC_ERROR = 0b011000
+ PT_ECC_ERROR_Lm1 = 0b011011
+ PT_ECC_ERROR_L0 = 0b011100
+ PT_ECC_ERROR_L1 = 0b011101
+ PT_ECC_ERROR_L2 = 0b011110
+ PT_ECC_ERROR_L3 = 0b011111
+ ALIGN = 0b100001
+ ASIZE_Lm1 = 0b101001
+ XLAT_Lm1 = 0b101011
+ TLB_CONFLICT = 0b110000
+ UNSUPP_ATOMIC = 0b110001
+ IMPDEF_LOCKDOWN = 0b110100
+ IMPDEF_ATOMIC = 0b110101
+
+class ESR_ISS_DABORT(Register32):
+ ISV = 24
+ SAS = 23, 22
+ SSE = 21
+ SRT = 20, 16
+ SF = 15
+ AR = 14
+ VNCR = 13
+ SET = 12, 11
+ LSR = 12, 11
+ FnV = 10
+ EA = 9
+ CM = 8
+ S1PTR = 7
+ WnR = 6
+ DFSC = 5, 0, DABORT_DFSC
+
+class ESR(Register64):
+ ISS2 = 36, 32
+ EC = 31, 26, ESR_EC
+ IL = 25
+ ISS = 24, 0
+
+class SPSR_M(IntEnum):
+ EL0t = 0
+ EL1t = 4
+ EL1h = 5
+ EL2t = 8
+ EL2h = 9
+
+class SPSR(Register64):
+ N = 31
+ Z = 30
+ C = 29
+ V = 28
+ TCO = 25
+ DIT = 24
+ UAO = 23
+ PAN = 22
+ SS = 21
+ IL = 20
+ SSBS = 12
+ BTYPE = 11, 10
+ D = 9
+ A = 8
+ I = 7
+ F = 6
+ M = 4, 0, SPSR_M
+
+class ACTLR(Register64):
+ EnMDSB = 12
+ EnPRSV = 6
+ EnAFP = 5
+ EnAPFLG = 4
+ DisHWP = 3
+ EnTSO = 1
+
+class HCR(Register64):
+ TWEDEL = 63, 60
+ TWEDEn = 59
+ TID5 = 58
+ DCT = 57
+ ATA = 56
+ TTLBOS = 55
+ TTLBIS = 54
+ EnSCXT = 53
+ TOCU = 52
+ AMVOFFEN = 51
+ TICAB = 50
+ TID4 = 49
+ FIEN = 47
+ FWB = 46
+ NV2 = 45
+ AT = 44
+ NV1 = 43
+ NV = 42
+ API = 41
+ APK = 40
+ MIOCNCE = 38
+ TEA = 37
+ TERR = 36
+ TLOR = 35
+ E2H = 34
+ ID = 33
+ CD = 32
+ RW = 31
+ TRVM = 30
+ HCD = 29
+ TDZ = 28
+ TGE = 27
+ TVM = 26
+ TTLB = 25
+ TPU = 24
+ TPCP = 23
+ TPC = 23
+ TSW = 22
+ TACR = 21
+ TIDCP = 20
+ TSC = 19
+ TID3 = 18
+ TID2 = 17
+ TID1 = 16
+ TID0 = 15
+ TWE = 14
+ TWI = 13
+ DC = 12
+ BSU = 11, 10
+ FB = 9
+ VSE = 8
+ VI = 7
+ VF = 6
+ AMO = 5
+ IMO = 4
+ FMO = 3
+ PTW = 2
+ SWIO = 1
+ VM = 0
+
+class HACR(Register64):
+ TRAP_CPU_EXT = 0
+ TRAP_AIDR = 4
+ TRAP_AMX = 10
+ TRAP_SPRR = 11
+ TRAP_GXF = 13
+ TRAP_CTRR = 14
+ TRAP_IPI = 16
+ TRAP_s3_4_c15_c5z6_x = 18
+ TRAP_s3_4_c15_c0z12_5 = 19
+ GIC_CNTV = 20
+ TRAP_s3_4_c15_c10_4 = 25
+ TRAP_SERROR_INFO = 48
+ TRAP_EHID = 49
+ TRAP_HID = 50
+ TRAP_s3_0_c15_c12_1z2 = 51
+ TRAP_ACC = 52
+ TRAP_PM = 57
+ TRAP_UPM = 58
+ TRAP_s3_1z7_c15_cx_3 = 59
+
+class AMX_CTL(Register64):
+ EN = 63
+ EN_EL1 = 62
+
+class MDCR(Register64):
+ TDE = 8
+ TDA = 9
+ TDOSA = 10
+ TDRA = 11
+
+class MDSCR(Register64):
+ SS = 0
+ MDE = 15
+
+class DBGBCR(Register32):
+ BT = 23, 20
+ LBN = 16, 16
+ SSC = 15, 14
+ HMC = 13
+ BAS = 8,5
+ PMC = 2,1
+ E = 0
+
+class DBGWCR_LSC(IntFlag):
+ L = 1
+ S = 2
+
+class DBGWCR(Register32):
+ SSCE = 29
+ MASK = 28, 24
+ WT = 20
+ LBN = 19, 16
+ SSC = 15, 14
+ HMC = 13
+ BAS = 12, 5
+ LSC = 4, 3
+ PAC = 2, 1
+ E = 0
+
+# TCR_EL1
+class TCR(Register64):
+ DS = 59
+ TCMA1 = 58
+ TCMA0 = 57
+ E0PD1 = 56
+ E0PD0 = 55
+ NFD1 = 54
+ NFD0 = 53
+ TBID1 = 52
+ TBID0 = 51
+ HWU162 = 50
+ HWU161 = 49
+ HWU160 = 48
+ HWU159 = 47
+ HWU062 = 46
+ HWU061 = 45
+ HWU060 = 44
+ HWU059 = 43
+ HPD1 = 42
+ HPD0 = 41
+ HD = 40
+ HA = 39
+ TBI1 = 38
+ TBI0 = 37
+ AS = 36
+ IPS = 34, 32
+ TG1 = 31, 30
+ SH1 = 29, 28
+ ORGN1 = 27, 26
+ IRGN1 = 25, 24
+ EPD1 = 23
+ A1 = 22
+ T1SZ = 21, 16
+ TG0 = 15, 14
+ SH0 = 13, 12
+ ORGN0 = 11, 10
+ IRGN0 = 9, 8
+ EPD0 = 7
+ T0SZ = 5, 0
+
+class TLBI_RVA(Register64):
+ ASID = 63, 48
+ TG = 47, 46
+ SCALE = 45, 44
+ NUM = 43, 39
+ TTL = 38, 37
+ BaseADDR = 36, 0
+
+__all__.extend(k for k, v in globals().items()
+ if (callable(v) or isinstance(v, type)) and v.__module__ == __name__)
diff --git a/tools/proxyclient/m1n1/tgtypes.py b/tools/proxyclient/m1n1/tgtypes.py
new file mode 100644
index 0000000..9081c8d
--- /dev/null
+++ b/tools/proxyclient/m1n1/tgtypes.py
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: MIT
+from construct import *
+
+__all__ = ["BootArgs"]
+
+BootArgs = Struct(
+ "revision" / Hex(Int16ul),
+ "version" / Hex(Int16ul),
+ Padding(4),
+ "virt_base" / Hex(Int64ul),
+ "phys_base" / Hex(Int64ul),
+ "mem_size" / Hex(Int64ul),
+ "top_of_kernel_data" / Hex(Int64ul),
+ "video" / Struct(
+ "base" / Hex(Int64ul),
+ "display" / Hex(Int64ul),
+ "stride" / Hex(Int64ul),
+ "width" / Hex(Int64ul),
+ "height" / Hex(Int64ul),
+ "depth" / Hex(Int64ul),
+ ),
+ "machine_type" / Hex(Int32ul),
+ Padding(4),
+ "devtree" / Hex(Int64ul),
+ "devtree_size" / Hex(Int32ul),
+ "cmdline" / PaddedString(608, "ascii"),
+ Padding(4),
+ "boot_flags" / Hex(Int64ul),
+ "mem_size_actual" / Hex(Int64ul),
+)
diff --git a/tools/proxyclient/m1n1/trace/__init__.py b/tools/proxyclient/m1n1/trace/__init__.py
new file mode 100644
index 0000000..73d1a10
--- /dev/null
+++ b/tools/proxyclient/m1n1/trace/__init__.py
@@ -0,0 +1,220 @@
+# SPDX-License-Identifier: MIT
+
+from ..hv import TraceMode
+from ..utils import *
+
+__all__ = []
+
+class RegCacheAlwaysCached(Reloadable):
+ def __init__(self, parent):
+ self.parent = parent
+
+ def read(self, addr, width):
+ return self.parent.read_cached(addr, width)
+
+ def write(self, addr, data, width):
+ raise Exception("Trying to write a register to the cache")
+
+class RegCache(Reloadable):
+ def __init__(self, hv):
+ self.hv = hv
+ self.u = hv.u
+ self.cache = {}
+
+ self.cached = RegCacheAlwaysCached(self)
+
+ def update(self, addr, data):
+ self.cache[addr] = data
+
+ def read(self, addr, width):
+ if self.hv.ctx or not self.hv.started:
+ data = self.u.read(addr, width)
+ self.cache[addr] = data
+ return data
+ else:
+ return self.read_cached(addr, width)
+
+ def read_cached(self, addr, width):
+ data = self.cache.get(addr, None)
+ if data is None:
+ print(f"RegCache: no cache for {addr:#x}")
+ return data
+
+ def write(self, addr, data, width):
+ if self.hv.ctx:
+ self.u.write(addr, data, width)
+ self.cache[addr] = data
+ else:
+ raise Exception("Cannot write register in asynchronous context")
+
+class TracerState:
+ pass
+
+class Tracer(Reloadable):
+ DEFAULT_MODE = TraceMode.ASYNC
+
+ def __init__(self, hv, verbose=False, ident=None):
+ self.hv = hv
+ self.ident = ident or type(self).__name__
+ self.regmaps = {}
+ self.verbose = verbose
+ self.state = TracerState()
+ self.init_state()
+ self._cache = RegCache(hv)
+ cache = hv.tracer_caches.get(self.ident, None)
+ if cache is not None:
+ self._cache.cache.update(cache.get("regcache", {}))
+ self.state.__dict__.update(cache.get("state", {}))
+ hv.tracer_caches[self.ident] = {
+ "regcache": self._cache.cache,
+ "state": self.state.__dict__
+ }
+
+ def init_state(self):
+ pass
+
+ def hook_w(self, addr, val, width, **kwargs):
+ self.hv.u.write(addr, val, width)
+
+ def hook_r(self, addr, width, **kwargs):
+ return self.hv.u.read(addr, width)
+
+ def evt_rw(self, evt, regmap=None, prefix=None):
+ self._cache.update(evt.addr, evt.data)
+ reg = rcls = None
+ value = evt.data
+
+ t = "w" if evt.flags.WRITE else "r"
+
+ if regmap is not None:
+ reg, index, rcls = regmap.lookup_addr(evt.addr)
+ if rcls is not None:
+ value = rcls(evt.data)
+
+ if self.verbose >= 3 or (reg is None and self.verbose >= 1):
+ if reg is None:
+ s = f"{evt.addr:#x} = {value:#x}"
+ else:
+ s = f"{regmap.get_name(evt.addr)} = {value!s}"
+ m = "+" if evt.flags.MULTI else " "
+ self.log(f"MMIO: {t.upper()}.{1<<evt.flags.WIDTH:<2}{m} " + s)
+
+ if reg is not None:
+ if prefix is not None:
+ attr = f"{t}_{prefix}_{reg}"
+ else:
+ attr = f"{t}_{reg}"
+ handler = getattr(self, attr, None)
+ if handler:
+ if index is not None:
+ handler(value, index)
+ else:
+ handler(value)
+ elif self.verbose == 2:
+ s = f"{regmap.get_name(evt.addr)} = {value!s}"
+ m = "+" if evt.flags.MULTI else " "
+ self.log(f"MMIO: {t.upper()}.{1<<evt.flags.WIDTH:<2}{m} " + s)
+
+ def trace(self, start, size, mode, read=True, write=True, **kwargs):
+ zone = irange(start, size)
+ if mode == TraceMode.HOOK:
+ self.hv.add_tracer(zone, self.ident, mode, self.hook_r if read else None,
+ self.hook_w if write else None, **kwargs)
+ else:
+ self.hv.add_tracer(zone, self.ident, mode, self.evt_rw if read else None,
+ self.evt_rw if write else None, **kwargs)
+
+ def trace_regmap(self, start, size, cls, mode=None, name=None, prefix=None, regmap_offset=0):
+ if mode is None:
+ mode = self.DEFAULT_MODE
+ if name is None:
+ name = cls.__name__
+
+ regmap = self.regmaps.get(start - regmap_offset, None)
+ if regmap is None:
+ regmap = cls(self._cache, start - regmap_offset)
+ regmap.cached = cls(self._cache.cached, start - regmap_offset)
+ self.regmaps[start - regmap_offset] = regmap
+ else:
+ assert isinstance(regmap, cls)
+
+ setattr(self, name, regmap)
+ self.trace(start, size, mode=mode, regmap=regmap, prefix=prefix)
+
+ def start(self):
+ pass
+
+ def stop(self):
+ self.hv.clear_tracers(self.ident)
+
+ def log(self, msg, show_cpu=True):
+ self.hv.log(f"[{self.ident}] {msg}", show_cpu=show_cpu)
+
+class PrintTracer(Tracer):
+ def __init__(self, hv, device_addr_tbl):
+ super().__init__(hv)
+ self.device_addr_tbl = device_addr_tbl
+ self.log_file = None
+
+ def event_mmio(self, evt, name=None, start=None):
+ dev, zone2 = self.device_addr_tbl.lookup(evt.addr)
+ if name is None:
+ name = dev
+ start = zone2.start
+ t = "W" if evt.flags.WRITE else "R"
+ m = "+" if evt.flags.MULTI else " "
+ logline = (f"[cpu{evt.flags.CPU}] [0x{evt.pc:016x}] MMIO: {t}.{1<<evt.flags.WIDTH:<2}{m} " +
+ f"0x{evt.addr:x} ({name}, offset {evt.addr - start:#04x}) = 0x{evt.data:x}")
+ print(logline)
+ if self.log_file:
+ self.log_file.write(f"# {logline}\n")
+ width = 8 << evt.flags.WIDTH
+ if evt.flags.WRITE:
+ stmt = f"p.write{width}({start:#x} + {evt.addr - start:#x}, {evt.data:#x})\n"
+ else:
+ stmt = f"p.read{width}({start:#x} + {evt.addr - start:#x})\n"
+ self.log_file.write(stmt)
+
+class ADTDevTracer(Tracer):
+ REGMAPS = []
+ NAMES = []
+ PREFIXES = []
+
+ def __init__(self, hv, devpath, verbose=False):
+ super().__init__(hv, verbose=verbose, ident=type(self).__name__ + "@" + devpath)
+ self.dev = hv.adt[devpath]
+
+ @classmethod
+ def _reloadcls(cls, force=False):
+ regmaps = []
+ for i in cls.REGMAPS:
+ if i is None:
+ reloaded = None
+ elif isinstance(i, tuple):
+ reloaded = (i[0]._reloadcls(force), i[1])
+ else:
+ reloaded = i._reloadcls(force)
+ regmaps.append(reloaded)
+ cls.REGMAPS = regmaps
+
+ return super()._reloadcls(force)
+
+ def start(self):
+ for i in range(len(self.dev.reg)):
+ if i >= len(self.REGMAPS) or (regmap := self.REGMAPS[i]) is None:
+ continue
+ if isinstance(regmap, tuple):
+ regmap, regmap_offset = regmap
+ else:
+ regmap_offset = 0
+ prefix = name = None
+ if i < len(self.NAMES):
+ name = self.NAMES[i]
+ if i < len(self.PREFIXES):
+ prefix = self.PREFIXES[i]
+
+ start, size = self.dev.get_reg(i)
+ self.trace_regmap(start, size, regmap, name=name, prefix=prefix, regmap_offset=regmap_offset)
+
+__all__.extend(k for k, v in globals().items()
+ if (callable(v) or isinstance(v, type)) and v.__module__.startswith(__name__))
diff --git a/tools/proxyclient/m1n1/trace/agx.py b/tools/proxyclient/m1n1/trace/agx.py
new file mode 100644
index 0000000..f46a063
--- /dev/null
+++ b/tools/proxyclient/m1n1/trace/agx.py
@@ -0,0 +1,1148 @@
+# SPDX-License-Identifier: MIT
+
+import textwrap
+from .asc import *
+from ..hw.uat import UAT, MemoryAttr, PTE, Page_PTE, TTBR
+from ..hw.agx import *
+
+from ..fw.agx.initdata import InitData
+from ..fw.agx.channels import *
+from ..fw.agx.cmdqueue import *
+from ..fw.agx.microsequence import *
+from ..fw.agx.handoff import *
+
+from m1n1.proxyutils import RegMonitor
+from m1n1.constructutils import *
+from m1n1.trace import Tracer
+
+from construct import *
+
+class ChannelTraceState(object):
+ pass
+
+class CommandQueueState(object):
+ pass
+
+class GpuMsg(Register64):
+ TYPE = 55, 48
+
+class PongMsg(GpuMsg):
+ TYPE = 59, 52
+ UNK = 47, 0
+
+class PongEp(EP):
+ # This endpoint recives pongs. The cpu code reads some status registers after receiving one
+ # Might be a "work done" message.
+ BASE_MESSAGE = GpuMsg
+
+ @msg(0x42, DIR.RX, PongMsg)
+ def pong_rx(self, msg):
+ if self.tracer.state.active:
+ self.log(f" Pong {msg!s}")
+ if msg.UNK != 0:
+ self.log(f" Pong had unexpected value{msg.UNK:x}")
+ self.hv.run_shell()
+
+ self.tracer.pong()
+ return True
+
+ @msg(0x81, DIR.TX, PongMsg)
+ def init_ep(self, msg):
+ self.log(f" Init {msg.UNK:x}")
+
+ self.tracer.pong_init(msg.UNK)
+ return True
+
+class KickMsg(GpuMsg):
+ TYPE = 59, 52
+ KICK = 7, 0 # Seen: 17, 16 (common), 9, 8, 1 (common), 0 (common)
+
+class KickEp(EP):
+ BASE_MESSAGE = GpuMsg
+
+ @msg(0x83, DIR.TX, KickMsg)
+ def kick(self, msg):
+ if self.tracer.state.active:
+ self.log(f" Kick {msg}")
+ self.tracer.kick(msg.KICK)
+
+ return True
+
+ @msg(0x84, DIR.TX, KickMsg)
+ def fwkick(self, msg):
+ if self.tracer.state.active:
+ self.log(f" FWRing Kick {msg}")
+ self.tracer.fwkick(msg.KICK)
+ return True
+
+class ChannelTracer(Reloadable):
+ STATE_FIELDS = ChannelStateFields
+ WPTR = 0x20
+ RPTR = 0x00
+
+ def __init__(self, tracer, info, index):
+ self.tracer = tracer
+ self.uat = tracer.uat
+ self.hv = tracer.hv
+ self.u = self.hv.u
+ self.ring_count = len(channelRings[index])
+ self.verbose = False
+
+ if index not in tracer.state.channels:
+ self.state = ChannelTraceState()
+ self.state.active = True
+ self.state.tail = [0] * self.ring_count
+ tracer.state.channels[index] = self.state
+ else:
+ self.state = tracer.state.channels[index]
+
+ self.index = index
+ self.name = channelNames[index]
+ self.info = info
+ base = None
+
+ if self.name == "FWLog":
+ base = self.tracer.state.fwlog_ring2
+
+ self.channel = Channel(self.u, self.uat, self.info, channelRings[index], base=base,
+ state_fields=self.STATE_FIELDS)
+ for i in range(self.ring_count):
+ for addr, size in self.channel.rb_maps[i]:
+ self.log(f"rb_map[{i}] {addr:#x} ({size:#x})")
+
+ self.set_active(self.state.active)
+
+ def state_read(self, evt, regmap=None, prefix=None, off=None):
+ ring = off // 0x30
+ off = off % 0x30
+
+ msgcls, size, count = self.channel.ring_defs[ring]
+
+ if off == self.WPTR:
+ if self.verbose:
+ self.log(f"RD [{evt.addr:#x}] WPTR[{ring}] = {evt.data:#x}")
+ self.poll_ring(ring)
+ elif off == self.RPTR:
+ if self.verbose:
+ self.log(f"RD [{evt.addr:#x}] RPTR[{ring}] = {evt.data:#x}")
+ self.poll_ring(ring)
+ else:
+ if self.verbose:
+ self.log(f"RD [{evt.addr:#x}] UNK[{ring}] {off:#x} = {evt.data:#x}")
+
+ def state_write(self, evt, regmap=None, prefix=None, off=None):
+ ring = off // 0x30
+ off = off % 0x30
+
+ msgcls, size, count = self.channel.ring_defs[ring]
+
+ if off == self.WPTR:
+ if self.verbose:
+ self.log(f"WR [{evt.addr:#x}] WPTR[{ring}] = {evt.data:#x}")
+ self.poll_ring(ring)
+ elif off == self.RPTR:
+ if self.verbose:
+ self.log(f"WR [{evt.addr:#x}] RPTR[{ring}] = {evt.data:#x}")
+ self.poll_ring(ring)
+ # Clear message with test pattern
+ idx = (evt.data - 1) % count
+ self.channel.clear_message(ring, idx)
+ else:
+ if self.verbose:
+ self.log(f"WR [{evt.addr:#x}] UNK[{ring}] {off:#x} = {evt.data:#x}")
+
+ def log(self, msg):
+ self.tracer.log(f"[{self.index}:{self.name}] {msg}")
+
+ def poll(self):
+ for i in range(self.ring_count):
+ self.poll_ring(i)
+
+ def poll_ring(self, ring):
+ msgcls, size, count = self.channel.ring_defs[ring]
+
+ cur = self.state.tail[ring]
+ tail = self.channel.state[ring].WRITE_PTR.val
+ if tail >= count:
+ raise Exception(f"Message index {tail:#x} >= {count:#x}")
+ if cur != tail:
+ #self.log(f"{cur:#x} -> {tail:#x}")
+ while cur != tail:
+ msg = self.channel.get_message(ring, cur, self.tracer.meta_gpuvm)
+ self.log(f"Message @{ring}.{cur}:\n{msg!s}")
+ self.tracer.handle_ringmsg(msg)
+ #if self.index < 12:
+ #self.hv.run_shell()
+ cur = (cur + 1) % count
+ self.state.tail[ring] = cur
+
+ def set_active(self, active=True):
+ if active:
+ if not self.state.active:
+ for ring in range(self.ring_count):
+ self.state.tail[ring] = self.channel.state[ring].WRITE_PTR.val
+
+ for base in range(0, 0x30 * self.ring_count, 0x30):
+ p = self.uat.iotranslate(0, self.channel.state_addr + base + self.RPTR, 4)[0][0]
+ self.hv.add_tracer(irange(p, 4),
+ f"ChannelTracer/{self.name}",
+ mode=TraceMode.SYNC,
+ read=self.state_read,
+ write=self.state_write,
+ off=base + self.RPTR)
+ p = self.uat.iotranslate(0, self.channel.state_addr + base + self.WPTR, 4)[0][0]
+ self.hv.add_tracer(irange(p, 4),
+ f"ChannelTracer/{self.name}",
+ mode=TraceMode.SYNC,
+ read=self.state_read,
+ write=self.state_write,
+ off=base + self.WPTR)
+ else:
+ self.hv.clear_tracers(f"ChannelTracer/{self.name}")
+ self.state.active = active
+
+ChannelTracer = ChannelTracer._reloadcls()
+CommandQueueInfo = CommandQueueInfo._reloadcls()
+
+class FWCtlChannelTracer(ChannelTracer):
+ STATE_FIELDS = FWControlStateFields
+ WPTR = 0x10
+ RPTR = 0x00
+
+class CommandQueueTracer(Reloadable):
+ def __init__(self, tracer, info_addr, new_queue):
+ self.tracer = tracer
+ self.uat = tracer.uat
+ self.hv = tracer.hv
+ self.u = self.hv.u
+ self.verbose = False
+ self.info_addr = info_addr
+
+ if info_addr not in tracer.state.queues:
+ self.state = CommandQueueState()
+ self.state.rptr = None
+ self.state.active = True
+ tracer.state.queues[info_addr] = self.state
+ else:
+ self.state = tracer.state.queues[info_addr]
+
+ if new_queue:
+ self.state.rptr = 0
+
+ self.update_info()
+
+ def update_info(self):
+ self.info = CommandQueueInfo.parse_stream(self.tracer.get_stream(0, self.info_addr))
+
+ def log(self, msg):
+ self.tracer.log(f"[CQ@{self.info_addr:#x}] {msg}")
+
+ @property
+ def rb_size(self):
+ return self.info.pointers.rb_size
+
+ def get_workitems(self, workmsg):
+ self.tracer.uat.invalidate_cache()
+ self.update_info()
+
+ if self.state.rptr is None:
+ self.state.rptr = int(self.info.pointers.gpu_doneptr)
+ self.log(f"Initializing rptr to {self.info.gpu_rptr1:#x}")
+
+ self.log(f"Got workmsg: wptr={workmsg.head:#x} rptr={self.state.rptr:#x}")
+ self.log(f"Queue info: {self.info}")
+
+
+ assert self.state.rptr < self.rb_size
+ assert workmsg.head < self.rb_size
+
+ stream = self.tracer.get_stream(0, self.info.rb_addr)
+
+ count = 0
+ orig_rptr = rptr = self.state.rptr
+ while rptr != workmsg.head:
+ count += 1
+ stream.seek(self.info.rb_addr + rptr * 8, 0)
+ pointer = Int64ul.parse_stream(stream)
+ self.log(f"WI item @{rptr:#x}: {pointer:#x}")
+ if pointer:
+ stream.seek(pointer, 0)
+ yield CmdBufWork.parse_stream(stream)
+ rptr = (rptr + 1) % self.rb_size
+
+ self.state.rptr = rptr
+
+ self.log(f"Parsed {count} items from {orig_rptr:#x} to {workmsg.head:#x}")
+
+ def set_active(self, active=True):
+ if not active:
+ self.state.rptr = None
+ self.state.active = active
+
+CmdBufWork = CmdBufWork._reloadcls()
+CommandQueueTracer = CommandQueueTracer._reloadcls()
+InitData = InitData._reloadcls(True)
+
+class HandoffTracer(Tracer):
+ DEFAULT_MODE = TraceMode.SYNC
+
+ def __init__(self, hv, agx_tracer, base, verbose=False):
+ super().__init__(hv, verbose=verbose)
+ self.agx_tracer = agx_tracer
+ self.base = base
+
+ def start(self):
+ self.trace_regmap(self.base, 0x4000, GFXHandoffStruct, name="regs")
+
+class SGXTracer(ADTDevTracer):
+ DEFAULT_MODE = TraceMode.HOOK
+
+ REGMAPS = [SGXRegs, SGXInfoRegs]
+ NAMES = ["sgx", "sgx-id"]
+
+ def __init__(self, hv, devpath, verbose=False):
+ super().__init__(hv, devpath, verbose=verbose)
+ self.hooks = {}
+
+ def hook_r(self, addr, width, **kwargs):
+ self.log(f"HOOK: {addr:#x}:{width}")
+
+ if addr in self.hooks:
+ val = self.hooks[addr]
+ self.log(f" Returning: {val:#x}")
+ else:
+ xval = val = super().hook_r(addr, width, **kwargs)
+ if isinstance(val, (list, tuple)):
+ xval = list(map(hex, val))
+ else:
+ xval = hex(val)
+ self.log(f" Read: {xval}")
+
+ return val
+
+ def hook_w(self, addr, val, width, **kwargs):
+ if isinstance(val, (list, tuple)):
+ xval = list(map(hex, val))
+ else:
+ xval = hex(val)
+
+ self.log(f"HOOK: {addr:#x}:{width} = {xval}")
+
+ super().hook_w(addr, val, width, **kwargs)
+
+class AGXTracer(ASCTracer):
+ ENDPOINTS = {
+ 0x20: PongEp,
+ 0x21: KickEp
+ }
+
+ REGMAPS = [ASCRegs]
+ NAMES = ["asc"]
+
+ PAGESIZE = 0x4000
+
+ def __init__(self, hv, devpath, verbose=False):
+ super().__init__(hv, devpath, verbose)
+ self.channels = []
+ self.uat = UAT(hv.iface, hv.u, hv)
+ self.mon = RegMonitor(hv.u, ascii=True, log=hv.log)
+ self.dev_sgx = hv.u.adt["/arm-io/sgx"]
+ self.sgx = SGXRegs(hv.u, self.dev_sgx.get_reg(0)[0])
+ self.gpu_region = getattr(self.dev_sgx, "gpu-region-base")
+ self.gpu_region_size = getattr(self.dev_sgx, "gpu-region-size")
+ self.gfx_shared_region = getattr(self.dev_sgx, "gfx-shared-region-base")
+ self.gfx_shared_region_size = getattr(self.dev_sgx, "gfx-shared-region-size")
+ self.gfx_handoff = getattr(self.dev_sgx, "gfx-handoff-base")
+ self.gfx_handoff_size = getattr(self.dev_sgx, "gfx-handoff-size")
+
+ self.handoff_tracer = HandoffTracer(hv, self, self.gfx_handoff, verbose=2)
+
+ self.ignorelist = []
+ self.last_msg = None
+
+ # self.mon.add(self.gpu_region, self.gpu_region_size, "contexts")
+ # self.mon.add(self.gfx_shared_region, self.gfx_shared_region_size, "gfx-shared")
+ # self.mon.add(self.gfx_handoff, self.gfx_handoff_size, "gfx-handoff")
+
+ self.trace_kernva = False
+ self.trace_userva = False
+ self.trace_kernmap = True
+ self.trace_usermap = True
+ self.pause_after_init = False
+ self.shell_after_init = False
+ self.after_init_hook = None
+ self.encoder_id_filter = None
+ self.redump = False
+
+ self.vmcnt = 0
+ self.readlog = {}
+ self.writelog = {}
+ self.cmdqueues = {}
+ self.va_to_pa = {}
+
+ self.last_ta = None
+ self.last_3d = None
+
+
+ def get_cmdqueue(self, info_addr, new_queue):
+ if info_addr in self.cmdqueues and not new_queue:
+ return self.cmdqueues[info_addr]
+
+ cmdqueue = CommandQueueTracer(self, info_addr, new_queue)
+ self.cmdqueues[info_addr] = cmdqueue
+
+ return cmdqueue
+
+ def clear_ttbr_tracers(self):
+ self.hv.clear_tracers(f"UATTTBRTracer")
+
+ def add_ttbr_tracers(self):
+ self.hv.add_tracer(irange(self.gpu_region, UAT.NUM_CONTEXTS * 16),
+ f"UATTTBRTracer",
+ mode=TraceMode.WSYNC,
+ write=self.uat_write,
+ iova=0,
+ base=self.gpu_region,
+ level=3)
+
+ def clear_uatmap_tracers(self, ctx=None):
+ if ctx is None:
+ for i in range(UAT.NUM_CONTEXTS):
+ self.clear_uatmap_tracers(i)
+ else:
+ self.hv.clear_tracers(f"UATMapTracer/{ctx}")
+
+ def add_uatmap_tracers(self, ctx=None):
+ self.log(f"add_uatmap_tracers({ctx})")
+ if ctx is None:
+ if self.trace_kernmap:
+ self.add_uatmap_tracers(0)
+ if self.trace_usermap:
+ for i in range(1, UAT.NUM_CONTEXTS):
+ self.add_uatmap_tracers(i)
+ return
+
+ if ctx != 0 and not self.trace_usermap:
+ return
+ if ctx == 0 and not self.trace_kernmap:
+ return
+
+ def trace_pt(start, end, idx, pte, level, sparse):
+ if start >= 0xf8000000000 and (ctx != 0 or not self.trace_kernmap):
+ return
+ if start < 0xf8000000000 and not self.trace_usermap:
+ return
+ self.log(f"Add UATMapTracer/{ctx} {start:#x}")
+ self.hv.add_tracer(irange(pte.offset(), 0x4000),
+ f"UATMapTracer/{ctx}",
+ mode=TraceMode.WSYNC,
+ write=self.uat_write,
+ iova=start,
+ base=pte.offset(),
+ level=2 - level,
+ ctx=ctx)
+
+ self.uat.foreach_table(ctx, trace_pt)
+
+ def clear_gpuvm_tracers(self, ctx=None):
+ if ctx is None:
+ for i in range(UAT.NUM_CONTEXTS):
+ self.clear_gpuvm_tracers(i)
+ else:
+ self.hv.clear_tracers(f"GPUVM/{ctx}")
+
+ def add_gpuvm_tracers(self, ctx=None):
+ self.log(f"add_gpuvm_tracers({ctx})")
+ if ctx is None:
+ self.add_gpuvm_tracers(0)
+ if self.trace_userva:
+ for i in range(1, UAT.NUM_CONTEXTS):
+ self.add_gpuvm_tracers(i)
+ return
+
+ def trace_page(start, end, idx, pte, level, sparse):
+ self.uat_page_mapped(start, pte, ctx)
+
+ self.uat.foreach_page(ctx, trace_page)
+
+ def uat_write(self, evt, level=3, base=0, iova=0, ctx=None):
+ off = (evt.addr - base) // 8
+ sh = ["NS", "??", "OS", "IS"]
+ a = f"{evt.flags.ATTR:02x}:{sh[evt.flags.SH]}"
+ self.log(f"UAT <{a}> write L{level} at {ctx}:{iova:#x} (#{off:#x}) -> {evt.data}")
+
+ if level == 3:
+ ctx = off // 2
+ is_kernel = off & 1
+ if ctx != 0 and is_kernel:
+ return
+
+ if is_kernel:
+ iova += 0xf8000000000
+ pte = TTBR(evt.data)
+ if not pte.valid():
+ self.log(f"Context {ctx} invalidated")
+ self.uat.invalidate_cache()
+ self.clear_uatmap_tracers(ctx)
+ self.clear_gpuvm_tracers(ctx)
+ return
+ self.log(f"Dumping UAT for context {ctx}")
+ self.uat.invalidate_cache()
+ _, pt = self.uat.get_pt(self.uat.gpu_region + ctx * 16, 2)
+ pt[off & 1] = evt.data
+ self.uat.dump(ctx, log=self.log)
+ self.add_uatmap_tracers(ctx)
+ self.add_gpuvm_tracers(ctx)
+ else:
+ is_kernel = iova >= 0xf8000000000
+ iova += off << (level * 11 + 14)
+ if level == 0:
+ pte = Page_PTE(evt.data)
+ self.uat_page_mapped(iova, pte, ctx)
+ return
+ else:
+ pte = PTE(evt.data)
+
+ if not pte.valid():
+ try:
+ paddr = self.va_to_pa[(ctx, level, iova)]
+ except KeyError:
+ return
+ self.hv.del_tracer(irange(paddr, 0x4000),
+ f"UATMapTracer/{ctx}")
+ del self.va_to_pa[(ctx, level, iova)]
+ return
+
+ if ctx != 0 and not self.trace_usermap:
+ return
+ if ctx == 0 and not self.trace_kernmap:
+ return
+
+ self.va_to_pa[(ctx, level, iova)] = pte.offset()
+ level -= 1
+ self.hv.add_tracer(irange(pte.offset(), 0x4000),
+ f"UATMapTracer/{ctx}",
+ mode=TraceMode.WSYNC,
+ write=self.uat_write,
+ iova=iova,
+ base=pte.offset(),
+ level=level,
+ ctx=ctx)
+
+ def uat_page_mapped(self, iova, pte, ctx=0):
+ if iova >= 0xf8000000000 and ctx != 0:
+ return
+ if not pte.valid():
+ self.log(f"UAT unmap {ctx}:{iova:#x} ({pte})")
+ try:
+ paddr = self.va_to_pa[(ctx, iova)]
+ except KeyError:
+ return
+ self.hv.del_tracer(irange(paddr, 0x4000), f"GPUVM/{ctx}")
+ del self.va_to_pa[(ctx, iova)]
+ return
+
+ paddr = pte.offset()
+ self.log(f"UAT map {ctx}:{iova:#x} -> {paddr:#x} ({pte})")
+ if paddr < 0x800000000:
+ return # MMIO, ignore
+
+ if not self.trace_userva and ctx != 0 and iova < 0x80_00000000:
+ return
+ if not self.trace_kernva and ctx == 0:
+ return
+
+ self.va_to_pa[(ctx, iova)] = paddr
+ self.hv.add_tracer(irange(paddr, 0x4000),
+ f"GPUVM/{ctx}",
+ mode=TraceMode.ASYNC,
+ read=self.event_gpuvm,
+ write=self.event_gpuvm,
+ iova=iova,
+ paddr=paddr,
+ ctx=ctx)
+
+ def event_gpuvm(self, evt, iova, paddr, name=None, base=None, ctx=None):
+ off = evt.addr - paddr
+ iova += off
+
+ if evt.flags.WRITE:
+ self.writelog[iova] = (self.vmcnt, evt)
+ else:
+ self.readlog[iova] = (self.vmcnt, evt)
+ t = "W" if evt.flags.WRITE else "R"
+ m = "+" if evt.flags.MULTI else " "
+ sh = ["NS", "??", "OS", "IS"]
+ a = f"{evt.flags.ATTR:02x}:{sh[evt.flags.SH]}"
+ dinfo = ""
+ if name is not None and base is not None:
+ dinfo = f"[{name} + {iova - base:#x}]"
+ logline = (f"[cpu{evt.flags.CPU}] GPUVM[{ctx}/{self.vmcnt:5}]: <{a}>{t}.{1<<evt.flags.WIDTH:<2}{m} " +
+ f"{iova:#x}({evt.addr:#x}){dinfo} = {evt.data:#x}")
+ self.log(logline, show_cpu=False)
+ self.vmcnt += 1
+ #self.mon.poll()
+
+ def meta_gpuvm(self, iova, size):
+ meta = ""
+ iova &= 0xfffffffffff
+ for off in range(size):
+ offva = iova + off
+ if offva in self.readlog:
+ ctr, evt = self.readlog[offva]
+ m = "+" if evt.flags.MULTI else " "
+ meta += f"[R.{1<<evt.flags.WIDTH:<2}{m} @{ctr} +{off:#x}]"
+
+ if offva in self.writelog:
+ ctr, evt = self.writelog[offva]
+ m = "+" if evt.flags.MULTI else " "
+ meta += f"[W.{1<<evt.flags.WIDTH:<2}{m} @{ctr} +{off:#x}]"
+
+ return meta or None
+
+ def get_stream(self, context, off):
+ stream = self.uat.iostream(context, off)
+ stream.meta_fn = self.meta_gpuvm
+ return stream
+
+ def start(self):
+ super().start()
+
+ self.clear_ttbr_tracers()
+ self.clear_uatmap_tracers()
+ self.add_ttbr_tracers()
+ self.add_uatmap_tracers()
+ self.clear_gpuvm_tracers()
+ self.add_mon_regions()
+
+ #self.handoff_tracer.start()
+ self.init_channels()
+ if self.state.active:
+ self.resume()
+ else:
+ self.pause()
+
+ def stop(self):
+ self.pause()
+ self.handoff_tracer.stop()
+ self.clear_ttbr_tracers()
+ self.clear_uatmap_tracers()
+ self.clear_gpuvm_tracers()
+ super().stop()
+
+ def mon_addva(self, ctx, va, size, name=""):
+ self.mon.add(va, size, name, readfn= lambda a, s: self.uat.ioread(ctx, a, s))
+
+ def handle_ringmsg(self, msg):
+ if isinstance(msg, FlagMsg):
+ self.log(f"== Event flag notification ==")
+ self.handle_event(msg)
+ return
+ elif isinstance(msg, RunCmdQueueMsg):
+ self.log(f"== Work notification (type {msg.queue_type})==")
+ queue = self.get_cmdqueue(msg.cmdqueue_addr, msg.new_queue)
+ work_items = list(queue.get_workitems(msg))
+ if self.encoder_id_filter is not None:
+ for wi in work_items:
+ if wi.cmd.magic == 0:
+ # TA
+ if not self.encoder_id_filter(wi.cmd.struct_3.encoder_id):
+ return True
+ if wi.cmd.magic == 1:
+ # 3D
+ if not self.encoder_id_filter(wi.cmd.struct_6.encoder_id):
+ return True
+ for wi in work_items:
+ self.log(str(wi))
+ if msg.queue_type == 2:
+ pass
+ #return self.handle_compute(wi)
+ elif msg.queue_type == 1:
+ self.handle_3d(wi)
+ self.queue_3d = queue
+ elif msg.queue_type == 0:
+ self.handle_ta(wi)
+ self.queue_ta = queue
+ return True
+
+ def handle_event(self, msg):
+ if self.last_ta and self.redump:
+ self.log("Redumping TA...")
+ stream = self.get_stream(0, self.last_ta._addr)
+ last_ta = CmdBufWork.parse_stream(stream)
+ self.log(str(last_ta))
+ self.handle_ta(last_ta)
+ self.queue_ta.update_info()
+ self.log(f"Queue info: {self.queue_ta.info}")
+ self.last_ta = None
+ if self.last_3d and self.redump:
+ self.log("Redumping 3D...")
+ stream = self.get_stream(0, self.last_3d._addr)
+ last_3d = CmdBufWork.parse_stream(stream)
+ self.log(str(last_3d))
+ self.handle_3d(last_3d)
+ self.queue_3d.update_info()
+ self.log(f"Queue info: {self.queue_3d.info}")
+ self.last_3d = None
+
+ def dump_buffer_manager(self, buffer_mgr, kread, read):
+ return
+
+ self.log(f" buffer_mgr @ {buffer_mgr._addr:#x}: {buffer_mgr!s}")
+ self.log(f" page_list @ {buffer_mgr.page_list_addr:#x}:")
+ chexdump(read(buffer_mgr.page_list_addr,
+ buffer_mgr.page_list_size), print_fn=self.log)
+ self.log(f" block_list @ {buffer_mgr.block_list_addr:#x}:")
+ chexdump(read(buffer_mgr.block_list_addr,
+ 0x8000), print_fn=self.log)
+ #self.log(f" unkptr_d8 @ {buffer_mgr.unkptr_d8:#x}:")
+ #chexdump(read(buffer_mgr.unkptr_d8, 0x4000), print_fn=self.log)
+
+
+ def handle_ta(self, wi):
+ self.log(f"Got TA WI{wi.cmd.magic:d}")
+ self.last_ta = wi
+
+ def kread(off, size):
+ return self.uat.ioread(0, off, size)
+
+ if wi.cmd.magic == 6:
+ wi6 = wi.cmd
+ #self.log(f" unkptr_14 @ {wi6.unkptr_14:#x}:")
+ #chexdump(kread(wi6.unkptr_14, 0x100), print_fn=self.log)
+
+ elif wi.cmd.magic == 0:
+ wi0 = wi.cmd
+ context = wi0.context_id
+
+ def read(off, size):
+ return self.uat.ioread(context, off & 0x7fff_ffff_ffff_ffff, size)
+
+ #chexdump(kread(wi0.addr, 0x600), print_fn=self.log)
+ self.log(f" context_id = {context:#x}")
+ self.dump_buffer_manager(wi0.buffer_mgr, kread, read)
+ #self.log(f" unk_emptybuf @ {wi0.unk_emptybuf_addr:#x}:")
+ #chexdump(kread(wi0.unk_emptybuf_addr, 0x1000), print_fn=self.log)
+
+ #self.log(f" unkptr_48 @ {wi0.unkptr_48:#x}:")
+ #chexdump(read(wi0.unkptr_48, 0x1000), print_fn=self.log)
+ #self.log(f" unkptr_58 @ {wi0.unkptr_58:#x}:")
+ #chexdump(read(wi0.unkptr_58, 0x4000), print_fn=self.log)
+ #self.log(f" unkptr_60 @ {wi0.unkptr_60:#x}:")
+ #chexdump(read(wi0.unkptr_60, 0x4000), print_fn=self.log)
+
+ #self.log(f" unkptr_45c @ {wi0.unkptr_45c:#x}:")
+ #chexdump(read(wi0.unkptr_45c, 0x1800), print_fn=self.log)
+
+ for i in wi0.microsequence.value:
+ i = i.cmd
+ if isinstance(i, StartTACmd):
+ self.log(f" # StartTACmd")
+ self.log(f" buf_thing @ {i.buf_thing_addr:#x}: {i.buf_thing!s}")
+ self.log(f" unkptr_18 @ {i.buf_thing.unkptr_18:#x}:")
+ chexdump(read(i.buf_thing.unkptr_18, 0x100), print_fn=self.log)
+ self.log(f" unkptr_24 @ {i.unkptr_24:#x}:")
+ chexdump(read(i.unkptr_24, 0x100), print_fn=self.log)
+ self.log(f" unk_5c @ {i.unkptr_5c:#x}:")
+ chexdump(read(i.unkptr_5c, 0x100), print_fn=self.log)
+
+ elif isinstance(i, FinalizeTACmd):
+ self.log(f" # FinalizeTACmd")
+
+
+ #self.uat.dump(context, self.log)
+
+ def handle_3d(self, wi):
+ self.log(f"Got 3D WI{wi.cmdid:d}")
+ if wi.cmdid != 1:
+ return
+
+ self.last_3d = wi
+
+ def kread(off, size):
+ return self.uat.ioread(0, off, size)
+
+ if wi.cmd.magic == 4:
+ wi4 = wi.cmd
+ #self.log(f" completion_buf @ {wi4.completion_buf_addr:#x}: {wi4.completion_buf!s} ")
+ #chexdump(kread(wi4.completion_buf_addr, 0x1000), print_fn=self.log)
+ elif wi.cmd.magic == 1:
+ wi1 = wi.cmd
+ context = wi1.context_id
+ def read(off, size):
+ return self.uat.ioread(context, off, size)
+
+ self.log(f" context_id = {context:#x}")
+ cmd3d = wi1.microsequence.value[0].cmd
+
+ self.log(f" 3D:")
+ self.log(f" struct1 @ {cmd3d.struct1_addr:#x}: {cmd3d.struct1!s}")
+ self.log(f" struct2 @ {cmd3d.struct2_addr:#x}: {cmd3d.struct2!s}")
+ #self.log(f" tvb_start_addr @ {cmd3d.struct2.tvb_start_addr:#x}:")
+ #if cmd3d.struct2.tvb_start_addr:
+ #chexdump(read(cmd3d.struct2.tvb_start_addr, 0x1000), print_fn=self.log)
+ #self.log(f" tvb_tilemap_addr @ {cmd3d.struct2.tvb_tilemap_addr:#x}:")
+ #if cmd3d.struct2.tvb_tilemap_addr:
+ #chexdump(read(cmd3d.struct2.tvb_tilemap_addr, 0x1000), print_fn=self.log)
+ #self.log(f" aux_fb_ptr @ {cmd3d.struct2.aux_fb_ptr:#x}:")
+ #chexdump(read(cmd3d.struct2.aux_fb_ptr, 0x100), print_fn=self.log)
+ #self.log(f" pipeline_base @ {cmd3d.struct2.pipeline_base:#x}:")
+ #chexdump(read(cmd3d.struct2.pipeline_base, 0x100), print_fn=self.log)
+
+ self.log(f" buf_thing @ {cmd3d.buf_thing_addr:#x}: {cmd3d.buf_thing!s}")
+ #self.log(f" unkptr_18 @ {cmd3d.buf_thing.unkptr_18:#x}:")
+ #chexdump(read(cmd3d.buf_thing.unkptr_18, 0x1000), print_fn=self.log)
+
+ #self.log(f" unk_24 @ {cmd3d.unkptr_24:#x}: {cmd3d.unk_24!s}")
+ self.log(f" struct6 @ {cmd3d.struct6_addr:#x}: {cmd3d.struct6!s}")
+ #self.log(f" unknown_buffer @ {cmd3d.struct6.unknown_buffer:#x}:")
+ #chexdump(read(cmd3d.struct6.unknown_buffer, 0x1000), print_fn=self.log)
+ self.log(f" struct7 @ {cmd3d.struct7_addr:#x}: {cmd3d.struct7!s}")
+ self.log(f" unk_buf_ptr @ {cmd3d.unk_buf_ptr:#x}:")
+ chexdump(kread(cmd3d.unk_buf_ptr, 0x11c), print_fn=self.log)
+ self.log(f" unk_buf2_ptr @ {cmd3d.unk_buf2_ptr:#x}:")
+ chexdump(kread(cmd3d.unk_buf2_ptr, 0x18), print_fn=self.log)
+
+ for i in wi1.microsequence.value:
+ i = i.cmd
+ if not isinstance(i, Finalize3DCmd):
+ continue
+ self.log(f" Finalize:")
+ cmdfin = i
+ #self.log(f" completion:")
+ #chexdump(kread(cmdfin.completion, 0x4), print_fn=self.log)
+ self.log(f" unkptr_1c @ {cmdfin.unkptr_1c:#x}:")
+ chexdump(kread(cmdfin.unkptr_1c, 0x1000), print_fn=self.log)
+ #self.log(f" unkptr_24 @ {cmdfin.unkptr_24:#x}:")
+ #chexdump(kread(cmdfin.unkptr_24, 0x100), print_fn=self.log)
+ self.log(f" unkptr_34 @ {cmdfin.unkptr_34:#x}:")
+ chexdump(kread(cmdfin.unkptr_34, 0x1000), print_fn=self.log)
+ self.log(f" unkptr_3c @ {cmdfin.unkptr_3c:#x}:")
+ chexdump(kread(cmdfin.unkptr_3c, 0x1c0), print_fn=self.log)
+ self.log(f" unkptr_44 @ {cmdfin.unkptr_44:#x}:")
+ chexdump(kread(cmdfin.unkptr_44, 0x40), print_fn=self.log)
+ self.log(f" unkptr_64 @ {cmdfin.unkptr_64:#x}:")
+ chexdump(kread(cmdfin.unkptr_64, 0x118), print_fn=self.log)
+
+ self.log(f" buf_thing @ {wi1.buf_thing_addr:#x}: {wi1.buf_thing!s}")
+ self.log(f" unkptr_18 @ {wi1.buf_thing.unkptr_18:#x}:")
+ chexdump(read(wi1.buf_thing.unkptr_18, 0x1000), print_fn=self.log)
+ self.dump_buffer_manager(wi1.buffer_mgr, kread, read)
+ #self.log(f" unk_emptybuf @ {wi1.unk_emptybuf_addr:#x}:")
+ #chexdump(kread(wi1.unk_emptybuf_addr, 0x1000), print_fn=self.log)
+ #self.log(f" tvb_addr @ {wi1.tvb_addr:#x}:")
+ #chexdump(read(wi1.tvb_addr, 0x1000), print_fn=self.log)
+
+ def handle_compute(self, msg):
+ self.log("Got Compute Work Item")
+
+ try:
+ wi = msg.workItems[0].cmd
+ except:
+ return
+
+ def kread(off, size):
+ return self.uat.ioread(0, off, size)
+
+ context = wi.context_id
+
+ def read(off, size):
+ return self.uat.ioread(context, off, size)
+
+ self.log(f" context_id = {context:#x}")
+ self.log(f" unk_c @ {wi.unkptr_c:#x}: {wi.unk_c!s} ")
+ #chexdump(kread(wi.unkptr_c, 0x100), print_fn=self.log)
+ self.log(f" unkptr_0:")
+ chexdump(kread(wi.unk_c.unkptr_0, 0x1000), print_fn=self.log)
+
+ self.log("StartComputeCmd:")
+ try:
+ ccmd = wi.microsequence.value[0].cmd
+ except:
+ self.log(" MISSING!")
+ return
+ self.log(f" unkptr_4: {ccmd.unkptr_4:#x}")
+ chexdump(kread(ccmd.unkptr_4, 0x54), print_fn=self.log)
+ self.log(f" unkptr_14: {ccmd.unkptr_14:#x}")
+ chexdump(kread(ccmd.unkptr_14, 0x1000), print_fn=self.log)
+ #self.log(f" unkptr_3c: {ccmd.unkptr_3c:#x}")
+ #chexdump(kread(ccmd.unkptr_3c, 0xb4), print_fn=self.log)
+
+ ci = ccmd.computeinfo
+ self.log(f" Compute Info: {ci!s}")
+ self.log(f" args:")
+ u0data = read(ci.args, 0x8000)
+ chexdump(u0data, print_fn=self.log)
+ args = struct.unpack("<8Q", u0data[0x7fa0:0x7fe0])
+ for i, p in enumerate(args):
+ if p:
+ self.log(f" args[{i}] @ {p:#x}")
+ chexdump(read(p, 0x1000), print_fn=self.log)
+ p0, p1 = struct.unpack("<QQ", u0data[0x7fe0:0x7ff0])
+ self.log(f" p0 @ {p0:#x}")
+ chexdump(read(p0, 0x100), print_fn=self.log)
+ self.log(f" p1 @ {p1:#x}")
+ chexdump(read(p1, 0x100), print_fn=self.log)
+ self.log(f" cmdlist:")
+ chexdump(read(ci.cmdlist, 0x8000), print_fn=self.log)
+ self.log(f" unkptr_10:")
+ chexdump(read(ci.unkptr_10, 8), print_fn=self.log)
+ self.log(f" unkptr_18:")
+ chexdump(read(ci.unkptr_18, 8), print_fn=self.log)
+ self.log(f" unkptr_20:")
+ chexdump(read(ci.unkptr_20, 8), print_fn=self.log)
+ self.log(f" unkptr_28:")
+ chexdump(read(ci.unkptr_28, 8), print_fn=self.log)
+ self.log(f" pipeline:")
+ chexdump(read(ci.pipeline_base, 0x1000), print_fn=self.log)
+ self.log(f" unkptr_48:")
+ chexdump(read(ci.unkptr_48, 0x8000), print_fn=self.log)
+
+ ci2 = ccmd.computeinfo2
+ self.log(f" Compute Info 2: {ci2!s}")
+ self.log(f" unknown_buffer:")
+ chexdump(read(ci2.unknown_buffer, 0x8000), print_fn=self.log)
+
+ def ignore(self, addr=None):
+ if addr is None:
+ addr = self.last_msg.cmdqueue_addr
+ self.ignorelist += [addr & 0xfff_ffffffff]
+
+ def kick(self, val):
+ if not self.state.active:
+ return
+
+ self.log(f"kick~! {val:#x}")
+ self.mon.poll()
+
+ if val == 0x10: # Kick Firmware
+ self.log("KickFirmware, polling")
+ self.uat.invalidate_cache()
+ for chan in self.channels:
+ chan.poll()
+ return
+
+ if val == 0x11: # Device Control
+ channel = 12
+ self.uat.invalidate_cache()
+
+ elif val < 0x10:
+ type = val & 3
+ assert type != 3
+ priority = (val >> 2) & 3
+ channel = type + priority * 3
+ self.uat.invalidate_cache()
+
+ else:
+ raise(Exception("Unknown kick type"))
+
+ self.channels[channel].poll()
+
+ ## if val not in [0x0, 0x1, 0x10, 0x11]:
+ #if self.last_msg and isinstance(self.last_msg, (RunCmdQueue, DeviceControl_17)):
+ #self.hv.run_shell()
+
+ #self.last_msg = None
+
+ # check the gfx -> cpu channels
+ for chan in self.channels[13:]:
+ chan.poll()
+
+ def fwkick(self, val):
+ if not self.state.active:
+ return
+
+ self.log(f"FW Kick~! {val:#x}")
+ self.mon.poll()
+
+ if val == 0x00: # Kick FW control
+ channel = len(self.channels) - 1
+ else:
+ raise(Exception("Unknown kick type"))
+
+ self.channels[channel].poll()
+
+ # check the gfx -> cpu channels
+ for chan in self.channels[13:]:
+ chan.poll()
+
+ def pong(self):
+ if not self.state.active:
+ return
+
+ self.log("pong~!");
+ self.mon.poll()
+
+ # check the gfx -> cpu channels
+ for chan in self.channels[13:]:
+ chan.poll()
+
+ def trace_uatrange(self, ctx, start, size, name=None, off=0):
+ start &= 0xfff_ffffffff
+ ranges = self.uat.iotranslate(ctx, start, size)
+ iova = start
+ for range in ranges:
+ pstart, psize = range
+ if pstart:
+ self.log(f"trace {name} {start:#x}/{iova:#x} [{pstart:#x}:{psize:#x}] +{off:#x}")
+ self.hv.add_tracer(irange(pstart, psize), f"GPUVM",
+ mode=TraceMode.ASYNC,
+ read=self.event_gpuvm,
+ write=self.event_gpuvm,
+ iova=iova,
+ paddr=pstart,
+ name=name,
+ base=start - off)
+ iova += psize
+
+ def untrace_uatrange(self, ctx, start, size):
+ ranges = self.uat.iotranslate(ctx, start, size)
+ for range in ranges:
+ start, size = range
+ if start:
+ self.hv.del_tracer(irange(start, size), f"GPUVM")
+
+ def dump_va(self, ctx):
+ data = b''
+ dataStart = 0
+
+ def dump_page(start, end, i, pte, level, sparse):
+ if i == 0 or sparse:
+ if len(data):
+ chexdump32(data, dataStart)
+ data = b''
+ dataStart = 0
+ if MemoryAttr(pte.AttrIndex) != MemoryAttr.Device and pte.OS:
+ if dataStart == 0:
+ dataStart = start
+ data += self.uat.ioread(0, start, 0x4000)
+
+ self.uat.foreach_page(0, dump_page)
+ if len(data):
+ chexdump32(data, dataStart)
+
+ def init_state(self):
+ super().init_state()
+ self.state.active = True
+ self.state.initdata = None
+ self.state.channel_info = []
+ self.state.channels = {}
+ self.state.queues = {}
+
+ def init_channels(self):
+ if self.channels:
+ return
+ #self.channels = []
+ for i, chan_info in enumerate(self.state.channel_info):
+ print(channelNames[i], chan_info)
+ if channelNames[i] == "Stats": # ignore stats
+ continue
+ elif channelNames[i] == "KTrace": # ignore KTrace
+ continue
+ elif channelNames[i] == "FWCtl":
+ channel_chan = FWCtlChannelTracer(self, chan_info, i)
+ else:
+ channel_chan = ChannelTracer(self, chan_info, i)
+ self.channels.append(channel_chan)
+
+ def pause(self):
+ self.clear_gpuvm_tracers()
+ if self.state.initdata is None:
+ return
+ self.clear_uatmap_tracers()
+ self.clear_ttbr_tracers()
+ self.log("Pausing tracing")
+ self.state.active = False
+ for chan in self.channels:
+ chan.set_active(False)
+ for queue in self.cmdqueues.values():
+ queue.set_active(False)
+ for info_addr in self.state.queues:
+ self.state.queues[info_addr].rptr = None
+ self.untrace_uatrange(0, self.state.initdata.regionA_addr, 0x4000)
+ self.untrace_uatrange(0, self.state.initdata.regionB_addr, 0x6bc0)
+ self.untrace_uatrange(0, self.state.initdata.regionC_addr, 0x11d40)
+
+ def resume(self):
+ self.add_gpuvm_tracers()
+ self.add_uatmap_tracers()
+ self.add_ttbr_tracers()
+ if self.state.initdata is None:
+ return
+ self.log("Resuming tracing")
+ self.state.active = True
+ for chan in self.channels:
+ if chan.name == "Stats":
+ continue
+ chan.set_active(True)
+ for queue in self.cmdqueues.values():
+ queue.set_active(True)
+ self.trace_uatrange(0, self.state.initdata.regionA_addr, 0x4000, name="regionA")
+ self.trace_uatrange(0, self.state.initdata.regionB_addr, 0x6bc0, name="regionB")
+ self.trace_uatrange(0, self.state.initdata.regionC_addr, 0x11d40, name="regionC")
+ self.trace_uatrange(0, self.state.initdata.regionB.buffer_mgr_ctl_addr, 0x4000, name="Buffer manager ctl")
+
+ def add_mon_regions(self):
+ return
+ initdata = self.state.initdata
+ if initdata is not None:
+ self.mon_addva(0, initdata.regionA_addr, 0x4000, "RegionA")
+ self.mon_addva(0, initdata.regionB_addr, 0x6bc0, "RegionB")
+ self.mon_addva(0, initdata.regionC_addr, 0x11d40, "RegionC")
+ #self.mon_addva(0, initdata.regionB.unkptr_170, 0xc0, "unkptr_170")
+ #self.mon_addva(0, initdata.regionB.unkptr_178, 0x1c0, "unkptr_178")
+ #self.mon_addva(0, initdata.regionB.unkptr_180, 0x140, "unkptr_180")
+ self.mon_addva(0, initdata.regionB.unkptr_190, 0x80, "unkptr_190")
+ self.mon_addva(0, initdata.regionB.unkptr_198, 0xc0, "unkptr_198")
+ self.mon_addva(0, initdata.regionB.buffer_mgr_ctl_addr, 0x4000, "Buffer manager ctl")
+ self.mon_addva(0, initdata.unkptr_20.unkptr_0, 0x40, "unkptr_20.unkptr_0")
+ self.mon_addva(0, initdata.unkptr_20.unkptr_8, 0x40, "unkptr_20.unkptr_8")
+
+ def pong_init(self, addr):
+ self.log("UAT at init time:")
+ self.uat.invalidate_cache()
+ self.uat.dump(0, log=self.log)
+ addr |= 0xfffff000_00000000
+ initdata = InitData.parse_stream(self.get_stream(0, addr))
+
+ self.log("Initdata:")
+ self.log(initdata)
+
+ self.add_mon_regions()
+
+ #self.initdata.regionB.mon(lambda addr, size, name: self.mon_addva(0, addr, size, name))
+
+ self.state.initdata_addr = addr
+ self.state.initdata = initdata
+ self.state.channel_info = []
+ self.state.fwlog_ring2 = initdata.regionB.fwlog_ring2
+ channels = initdata.regionB.channels
+ for i in channelNames:
+ if i == "FWCtl":
+ chan_info = initdata.fw_status.fwctl_channel
+ else:
+ chan_info = channels[i]
+ self.state.channel_info.append(chan_info)
+
+ self.init_channels()
+ self.mon.poll()
+
+ self.log("Initial commands::")
+ for chan in self.channels:
+ chan.poll()
+ self.log("Init done")
+
+ self.log("Mon regions")
+ self.mon.show_regions(log=self.log)
+
+
+ if self.pause_after_init:
+ self.log("Pausing tracing")
+ self.pause()
+ self.stop()
+ if self.shell_after_init:
+ self.hv.run_shell()
+ if self.after_init_hook:
+ self.after_init_hook()
+
+ChannelTracer = ChannelTracer._reloadcls()
diff --git a/tools/proxyclient/m1n1/trace/asc.py b/tools/proxyclient/m1n1/trace/asc.py
new file mode 100644
index 0000000..6d63a50
--- /dev/null
+++ b/tools/proxyclient/m1n1/trace/asc.py
@@ -0,0 +1,271 @@
+# SPDX-License-Identifier: MIT
+
+import struct
+from enum import IntEnum
+from ..hv import TraceMode
+from ..utils import *
+from . import ADTDevTracer
+from ..hw.asc import *
+
+class DIR(IntEnum):
+ RX = 0
+ TX = 1
+
+def msg(message, direction=None, regtype=None, name=None):
+ def f(x):
+ x.is_message = True
+ x.direction = direction
+ x.message = message
+ x.regtype = regtype
+ x.name = name
+ return x
+ return f
+
+def msg_log(*args, **kwargs):
+ def x(self, msg):
+ return False
+ return msg(*args, **kwargs)(x)
+
+def msg_ign(*args, **kwargs):
+ def x(self, msg):
+ return True
+ return msg(*args, **kwargs)(x)
+
+class EPState(object):
+ pass
+
+class EP(object):
+ NAME = None
+ BASE_MESSAGE = None
+
+ def __init__(self, tracer, epid):
+ self.tracer = tracer
+ self.epid = epid
+ self.present = False
+ self.started = False
+ self.name = self.NAME or type(self).__name__.lower()
+ self.state = EPState()
+ self.hv = self.tracer.hv
+ self.msgmap = {}
+ for name in dir(self):
+ i = getattr(self, name)
+ if not callable(i) or not getattr(i, "is_message", False):
+ continue
+ self.msgmap[i.direction, i.message] = getattr(self, name), name, i.regtype
+
+ def log(self, msg):
+ self.tracer.log(f"[{self.name}] {msg}")
+
+ def start(self):
+ pass
+
+ def handle_msg(self, direction, r0, r1):
+ msgtype = None
+ if self.BASE_MESSAGE:
+ r0 = self.BASE_MESSAGE(r0.value)
+ msgtype = r0.TYPE
+
+ handler = None
+ name = "<unknown>"
+ regtype = None
+
+ msgids = [
+ (direction, msgtype),
+ (None, msgtype),
+ (direction, None),
+ (None, None),
+ ]
+
+ for msgid in msgids:
+ handler, name, regtype = self.msgmap.get(msgid, (None, None, None))
+ if handler:
+ break
+
+ if regtype is not None:
+ r0 = regtype(r0.value)
+
+ if handler:
+ if handler.name is not None:
+ name = handler.name
+ if handler(r0):
+ return True
+
+ d = ">" if direction == DIR.TX else "<"
+ self.log(f"{d}{msgtype:#x}({name}) {r0.value:016x} ({r0.str_fields()})")
+ return True
+
+class EPContainer(object):
+ pass
+
+class BaseASCTracer(ADTDevTracer):
+ DEFAULT_MODE = TraceMode.SYNC
+
+ REGMAPS = [ASCRegs, None]
+ NAMES = ["asc", None]
+
+ ENDPOINTS = {}
+
+ def w_OUTBOX_CTRL(self, val):
+ self.log(f"OUTBOX_CTRL = {val!s}")
+
+ def w_INBOX_CTRL(self, val):
+ self.log(f"INBOX_CTRL = {val!s}")
+
+ def w_CPU_CONTROL(self, val):
+ self.log(f"CPU_CONTROL = {val!s}")
+
+ def w_INBOX1(self, inbox1):
+ inbox0 = self.asc.cached.INBOX0.reg
+ if self.verbose >= 2:
+ self.log(f"SEND: {inbox0.value:016x}:{inbox1.value:016x} " +
+ f"{inbox0.str_fields()} | {inbox1.str_fields()}")
+ self.handle_msg(DIR.TX, inbox0, inbox1)
+
+ def r_OUTBOX1(self, outbox1):
+ outbox0 = self.asc.cached.OUTBOX0.reg
+ if self.verbose >= 2:
+ self.log(f"RECV: {outbox0.value:016x}:{outbox1.value:016x} " +
+ f"{outbox0.str_fields()} | {outbox1.str_fields()}")
+ self.handle_msg(DIR.RX, outbox0, outbox1)
+
+ def init_state(self):
+ self.state.ep = {}
+
+ def handle_msg(self, direction, r0, r1):
+ if r1.EP in self.epmap:
+ if self.epmap[r1.EP].handle_msg(direction, r0, r1):
+ return
+
+ d = ">" if direction == DIR.TX else "<"
+ self.log(f"{d}ep:{r1.EP:02x} {r0.value:016x} ({r0.str_fields()})")
+
+ def ioread(self, dva, size):
+ if self.dart:
+ return self.dart.ioread(self.stream, dva & 0xFFFFFFFFF, size)
+ else:
+ return self.hv.iface.readmem(dva, size)
+
+ def iowrite(self, dva, data):
+ if self.dart:
+ return self.dart.iowrite(self.stream, dva & 0xFFFFFFFFF, data)
+ else:
+ return self.hv.iface.writemem(dva, data)
+
+ def start(self, dart=None, stream=0):
+ super().start()
+ self.dart = dart
+ self.stream = stream
+ self.msgmap = {}
+ for name in dir(self):
+ i = getattr(self, name)
+ if not callable(i) or not getattr(i, "is_message", False):
+ continue
+ self.msgmap[i.direction, i.endpoint, i.message] = getattr(self, name), name, i.regtype
+
+ self.epmap = {}
+ self.ep = EPContainer()
+ for cls in type(self).mro():
+ eps = getattr(cls, "ENDPOINTS", None)
+ if eps is None:
+ break
+ for k, v in eps.items():
+ if k in self.epmap:
+ continue
+ ep = v(self, k)
+ ep.dart = dart
+ ep.stream = stream
+ self.epmap[k] = ep
+ if k in self.state.ep:
+ ep.state.__dict__.update(self.state.ep[k])
+ self.state.ep[k] = ep.state.__dict__
+ if getattr(self.ep, ep.name, None):
+ ep.name = f"{ep.name}{k:02x}"
+ setattr(self.ep, ep.name, ep)
+ ep.start()
+
+# System endpoints
+
+## Management endpoint
+
+from ..fw.asc.mgmt import ManagementMessage, Mgmt_EPMap, Mgmt_EPMap_Ack, Mgmt_StartEP, Mgmt_SetAPPower, Mgmt_SetIOPPower, Mgmt_IOPPowerAck
+
+class Management(EP):
+ BASE_MESSAGE = ManagementMessage
+
+ HELLO = msg_log(1, DIR.RX)
+ HELLO_ACK = msg_log(2, DIR.TX)
+
+ @msg(5, DIR.TX, Mgmt_StartEP)
+ def StartEP(self, msg):
+ ep = self.tracer.epmap.get(msg.EP, None)
+ if ep:
+ ep.started = True
+ self.log(f" Starting endpoint #{msg.EP:#02x} ({ep.name})")
+ else:
+ self.log(f" Starting endpoint #{msg.EP:#02x}")
+ #return True
+
+ Init = msg_log(6, DIR.TX)
+
+ @msg(8, DIR.RX, Mgmt_EPMap)
+ def EPMap(self, msg):
+ for i in range(32):
+ if msg.BITMAP & (1 << i):
+ epno = 32 * msg.BASE + i
+ ep = self.tracer.epmap.get(epno, None)
+ if ep:
+ ep.present = True
+ self.log(f" Adding endpoint #{epno:#02x} ({ep.name})")
+ else:
+ self.log(f" Adding endpoint #{epno:#02x}")
+
+ EPMap_Ack = msg_log(8, DIR.TX, Mgmt_EPMap_Ack)
+
+ SetIOPPower = msg_log(6, DIR.TX, Mgmt_SetIOPPower)
+ SetIOPPowerAck = msg_log(7, DIR.TX, Mgmt_IOPPowerAck)
+
+ SetAPPower = msg_log(0x0b, DIR.TX, Mgmt_SetAPPower)
+ SetAPPowerAck = msg_log(0x0b, DIR.RX, Mgmt_SetAPPower)
+
+## Syslog endpoint
+
+from ..fw.asc.syslog import SyslogMessage, Syslog_Init, Syslog_GetBuf, Syslog_Log
+
+class Syslog(EP):
+ BASE_MESSAGE = SyslogMessage
+
+ @msg(8, DIR.RX, Syslog_Init)
+ def Init(self, msg):
+ self.state.count = msg.COUNT
+ self.state.entrysize = msg.ENTRYSIZE
+
+ @msg(1, DIR.RX, Syslog_GetBuf)
+ def GetBuf(self, msg):
+ if msg.DVA:
+ self.state.syslog_buf = msg.DVA
+
+ @msg(1, DIR.TX, Syslog_GetBuf)
+ def GetBuf_Ack(self, msg):
+ self.state.syslog_buf = msg.DVA
+
+ @msg(5, DIR.RX, Syslog_Log)
+ def Log(self, msg):
+ buf = self.state.syslog_buf
+ stride = 0x20 + self.state.entrysize
+ log = self.tracer.ioread(buf + msg.INDEX * stride, stride)
+ hdr, unk, context, logmsg = struct.unpack(f"<II24s{self.state.entrysize}s", log)
+ context = context.split(b"\x00")[0].decode("ascii")
+ logmsg = logmsg.split(b"\x00")[0].decode("ascii").rstrip("\n")
+ self.log(f"* [{context}]{logmsg}")
+ return True
+
+ Log_Ack = msg_ign(5, DIR.TX, Syslog_Log)
+
+class ASCTracer(BaseASCTracer):
+ ENDPOINTS = {
+ 0: Management,
+ #1: CrashLog,
+ 2: Syslog,
+ #3: KDebug,
+ #4: IOReporting,
+ }
diff --git a/tools/proxyclient/m1n1/trace/dart.py b/tools/proxyclient/m1n1/trace/dart.py
new file mode 100644
index 0000000..b1324f7
--- /dev/null
+++ b/tools/proxyclient/m1n1/trace/dart.py
@@ -0,0 +1,69 @@
+# SPDX-License-Identifier: MIT
+
+from ..hw.dart import *
+from ..hw.dart8020 import *
+from ..hw.dart8110 import *
+from ..hv import TraceMode
+from ..utils import *
+from . import ADTDevTracer
+
+class DARTTracer(ADTDevTracer):
+
+ DEFAULT_MODE = TraceMode.ASYNC
+
+ NAMES = ["regs"]
+
+ @classmethod
+ def _reloadcls(cls, force=False):
+ global DART8020
+ global DART8020Regs
+ global DART8110
+ global DART8110Regs
+ DART8020 = DART8020._reloadcls(force)
+ DART8020Regs = DART8020Regs._reloadcls(force)
+ DART8110 = DART8110._reloadcls(force)
+ DART8110Regs = DART8110Regs._reloadcls(force)
+ return super()._reloadcls(force)
+
+ def __init__(self, hv, devpath, **kwargs):
+ compat = hv.adt[devpath].compatible[0]
+ if compat in ["dart,t6000", "dart,t8020"]:
+ self.REGMAPS = [DART8020Regs]
+ elif compat in ["dart,t8110"]:
+ self.REGMAPS = [DART8110Regs]
+
+ return super().__init__(hv, devpath, **kwargs)
+
+ def start(self):
+ super().start()
+ # prime cache
+ if self.dev.compatible[0] == "dart,t8110":
+ for i in range(16):
+ self.regs.TCR[i].val
+ self.regs.TTBR[i].val
+ for _ in range(8):
+ self.regs.ENABLE_STREAMS[_].val
+ else:
+ for i in range(16):
+ self.regs.TCR[i].val
+ for j in range(4):
+ self.regs.TTBR[i, j].val
+ self.regs.ENABLED_STREAMS.val
+
+ self.dart = DART(self.hv.iface, self.regs, compat=self.dev.compatible[0])
+
+
+ def w_STREAM_COMMAND(self, stream_command):
+ if stream_command.INVALIDATE:
+ self.log(f"Invalidate Stream: {self.regs.cached.STREAM_SELECT.reg}")
+ self.dart.invalidate_cache()
+
+ def w_TLB_OP(self, tlb_op):
+ if tlb_op.OP == 0:
+ self.log(f"Invalidate all")
+ self.dart.invalidate_cache()
+ elif tlb_op.OP == 1:
+ self.log(f"Invalidate Stream: {tlb_op.STREAM}")
+ self.dart.invalidate_cache()
+ else:
+ self.log(f"Unknown TLB op {tlb_op}")
diff --git a/tools/proxyclient/m1n1/trace/dockchannel.py b/tools/proxyclient/m1n1/trace/dockchannel.py
new file mode 100644
index 0000000..10ada58
--- /dev/null
+++ b/tools/proxyclient/m1n1/trace/dockchannel.py
@@ -0,0 +1,37 @@
+# SPDX-License-Identifier: MIT
+import struct
+
+from ..hw.dockchannel import DockChannelIRQRegs, DockChannelConfigRegs, DockChannelDataRegs
+from ..hv import TraceMode
+from ..utils import *
+from . import ADTDevTracer
+
+class DockChannelTracer(ADTDevTracer):
+ DEFAULT_MODE = TraceMode.SYNC
+
+ REGMAPS = [None, DockChannelIRQRegs, DockChannelConfigRegs, DockChannelDataRegs]
+ NAMES = [None, "irq", "config", "data"]
+
+ def w_TX_8(self, d):
+ self.tx(struct.pack("<I", d.value)[0:1])
+ def w_TX_16(self, d):
+ self.tx(struct.pack("<I", d.value)[0:2])
+ def w_TX_24(self, d):
+ self.tx(struct.pack("<I", d.value)[0:3])
+ def w_TX_32(self, d):
+ self.tx(struct.pack("<I", d.value))
+
+ def r_RX_8(self, d):
+ self.rx(struct.pack("<I", d.value)[1:2])
+ def r_RX_16(self, d):
+ self.rx(struct.pack("<I", d.value)[1:3])
+ def r_RX_24(self, d):
+ self.rx(struct.pack("<I", d.value)[1:4])
+ def r_RX_32(self, d):
+ self.rx(struct.pack("<I", d.value))
+
+ def tx(self, d):
+ pass
+
+ def rx(self, d):
+ pass
diff --git a/tools/proxyclient/m1n1/trace/gpio.py b/tools/proxyclient/m1n1/trace/gpio.py
new file mode 100644
index 0000000..386f886
--- /dev/null
+++ b/tools/proxyclient/m1n1/trace/gpio.py
@@ -0,0 +1,67 @@
+# SPDX-License-Identifier: MIT
+
+from ..hv import TraceMode
+from ..utils import *
+from . import ADTDevTracer
+
+class R_PIN(Register32):
+ DRIVE_STRENGTH1 = 23, 22
+ LOCK = 21
+ GROUP = 18, 16
+ SCHMITT = 15
+ DRIVE_STRENGTH0 = 11, 10
+ INPUT_ENABLE = 9
+ PULL = 8, 7
+ PERIPH = 6, 5
+ MODE = 3, 1
+ DATA = 0
+
+class GPIORegs(RegMap):
+ PIN = irange(0x000, 212, 4), R_PIN
+
+ IRQ_GROUP = (irange(0x800, 7, 0x40), irange(0, (212 + 31) // 32, 4)), Register32
+
+def bits32(val, start):
+ return [start + i for i in range(0, 32) if int(val) & (1 << i)]
+
+class GPIOTracer(ADTDevTracer):
+ DEFAULT_MODE = TraceMode.UNBUF
+
+ REGMAPS = [GPIORegs]
+ NAMES = ["gpio"]
+
+ PIN_NAMES = {}
+
+ def __init__(self, hv, devpath, pin_names={}, verbose=False):
+ super().__init__(hv, devpath, verbose)
+ self.PIN_NAMES = pin_names
+
+ def pn(self, pin):
+ return self.PIN_NAMES.get(pin, f"Pin-{pin}")
+
+ def r_PIN(self, val, index):
+ if index not in self.PIN_NAMES and self.verbose < 2:
+ return
+ self.log(f"{self.pn(index):14} R {val!s} ")
+
+ def w_PIN(self, val, index):
+ if index not in self.PIN_NAMES and self.verbose < 2:
+ return
+ self.log(f"{self.pn(index):14} W {val!s} ")
+
+ def r_IRQ_GROUP(self, val, index):
+ (grp, index) = index
+ if int(val) == 0:
+ return
+ pins = [self.pn(x) for x in bits32(val, index * 32) if self.verbose >= 2 or x in self.PIN_NAMES]
+ if len(pins):
+ self.log(f"IRQ[{grp}] ACT {pins}")
+
+ def w_IRQ_GROUP(self, val, index):
+ (grp, index) = index
+ if int(val) == 0:
+ return
+ pins = [self.pn(x) for x in bits32(val, index * 32) if self.verbose >= 2 or x in self.PIN_NAMES]
+ if len(pins):
+ self.log(f"IRQ[{grp}] ACK {pins}")
+
diff --git a/tools/proxyclient/m1n1/trace/i2c.py b/tools/proxyclient/m1n1/trace/i2c.py
new file mode 100644
index 0000000..9c4bbb6
--- /dev/null
+++ b/tools/proxyclient/m1n1/trace/i2c.py
@@ -0,0 +1,225 @@
+# SPDX-License-Identifier: MIT
+
+from ..hv import TraceMode
+from ..utils import *
+from ..hw import i2c
+from . import ADTDevTracer
+
+class I2CTracer(ADTDevTracer):
+ DEFAULT_MODE = TraceMode.ASYNC
+ REGMAPS = [i2c.I2CRegs]
+ NAMES = ["i2c"]
+
+ def __init__(self, hv, devpath, verbose=False):
+ super().__init__(hv, devpath, verbose=verbose)
+ self.default_dev = I2CDevTracer()
+ self.default_dev.i2c_tracer = self
+
+ def init_state(self):
+ self.state.txn = []
+ self.state.devices = {}
+
+ def w_MTXFIFO(self, mtxfifo):
+ if self.state.txn is None:
+ self.state.txn = []
+
+ d = mtxfifo.DATA
+ if mtxfifo.START:
+ self.state.txn += ["S"]
+ if mtxfifo.READ:
+ self.state.txn += [None] * d
+ else:
+ self.state.txn.append(d)
+
+ if mtxfifo.STOP:
+ self.state.txn.append("P")
+ self.flush_txn()
+
+ def r_MRXFIFO(self, mrxfifo):
+ if mrxfifo.EMPTY:
+ self.log(f"Read while FIFO empty")
+ return
+
+ if not self.state.txn:
+ self.log(f"Stray read: {mrxfifo}")
+ return
+
+ try:
+ pos = self.state.txn.index(None)
+ self.state.txn[pos] = mrxfifo.DATA
+ except ValueError:
+ self.log(f"Stray read: {mrxfifo}")
+
+ self.flush_txn()
+
+ def flush_txn(self):
+ if not self.state.txn:
+ return
+
+ if self.state.txn[-1] != "P":
+ return
+
+ if not any(i is None for i in self.state.txn):
+ self.handle_txn(self.state.txn)
+ self.state.txn = None
+
+ def handle_txn(self, txn):
+ st = False
+ dev = self.default_dev
+ read = False
+ for i in txn:
+ if i == "S":
+ st = True
+ continue
+ if st:
+ addr = i >> 1
+ dev = self.state.devices.get(addr, self.default_dev)
+ read = bool(i & 1)
+ dev.start(addr, read=read)
+ elif i == "P":
+ dev.stop()
+ elif read:
+ dev.read(i)
+ else:
+ dev.write(i)
+ st = False
+
+ def add_device(self, addr, device):
+ device.hv = self.hv
+ device.i2c_tracer = self
+ self.state.devices[addr] = device
+
+class I2CDevTracer(Reloadable):
+ def __init__(self, addr=None, name=None, verbose=True):
+ self.addr = addr
+ self.name = name
+ self.verbose = verbose
+ self.txn = []
+
+ def log(self, msg, *args, **kwargs):
+ if self.name:
+ msg = f"[{self.name}] {msg}"
+ self.i2c_tracer.log(msg, *args, **kwargs)
+
+ def start(self, addr, read):
+ self.txn.append("S")
+ if read:
+ self.txn.append(f"{addr:02x}.r")
+ else:
+ self.txn.append(f"{addr:02x}.w")
+
+ def stop(self):
+ self.txn.append("P")
+ if self.verbose:
+ self.log(f"Txn: {' '.join(self.txn)}")
+ self.txn = []
+
+ def read(self, data):
+ self.txn.append(f"{data:02x}")
+
+ def write(self, data):
+ self.txn.append(f"{data:02x}")
+
+class I2CRegCache:
+ def __init__(self):
+ self.cache = {}
+
+ def update(self, addr, data):
+ self.cache[addr] = data
+
+ def read(self, addr, width):
+ data = self.cache.get(addr, None)
+ if data is None:
+ print(f"I2CRegCache: no cache for {addr:#x}")
+ return data
+
+ def write(self, addr, data, width):
+ raise NotImplementedError("No write on I2CRegCache")
+
+class I2CRegMapTracer(I2CDevTracer):
+ REGMAP = RegMap
+ ADDRESSING = (0, 1)
+
+ def __init__(self, verbose=False, **kwargs):
+ super().__init__(verbose=verbose, **kwargs)
+ self._cache = I2CRegCache()
+ self.regmap = self.REGMAP(self._cache, 0)
+ self.page = 0x0
+ self.reg = None
+ self.regbytes = []
+
+ self.npagebytes, nimmbytes = self.ADDRESSING
+ self.pageshift = 8 * nimmbytes
+ self.paged = self.npagebytes != 0
+
+ def _reloadme(self):
+ self.regmap._reloadme()
+ return super()._reloadme()
+
+ def start(self, addr, read):
+ if not read:
+ self.reg = None
+ self.regbytes = []
+ super().start(addr, read)
+
+ def stop(self):
+ super().stop()
+
+ def handle_addressing(self, data):
+ if self.reg is not None:
+ return False
+
+ self.regbytes.append(data)
+ if len(self.regbytes)*8 >= self.pageshift:
+ immediate = int.from_bytes(bytes(self.regbytes),
+ byteorder="big")
+ self.reg = self.page << self.pageshift | immediate
+ return True
+
+ @property
+ def reg_imm(self):
+ '''Returns the 'immediate' part of current register address'''
+ return self.reg & ~(~0 << self.pageshift)
+
+ def handle_page_register(self, data):
+ if not self.paged:
+ return False
+
+ if self.reg_imm >= self.npagebytes:
+ return False
+
+ shift = 8 * self.reg_imm
+ self.page &= ~(0xff << shift)
+ self.page |= data << shift
+ return True
+
+ def write(self, data):
+ if self.handle_addressing(data):
+ return
+ elif self.handle_page_register(data):
+ pass
+ else:
+ self.regwrite(self.reg, data)
+
+ self.reg += 1
+ super().write(data)
+
+ def read(self, data):
+ if self.reg_imm >= self.npagebytes:
+ self.regread(self.reg, data)
+ self.reg += 1
+ super().read(data)
+
+ def regwrite(self, reg, val):
+ self.regevent(reg, val, False)
+
+ def regread(self, reg, val):
+ self.regevent(reg, val, True)
+
+ def regevent(self, reg, val, read):
+ self._cache.update(reg, val)
+ r, index, rcls = self.regmap.lookup_addr(reg)
+ val = rcls(val) if rcls is not None else f"{val:#x}"
+ regname = self.regmap.get_name(reg) if r else f"{reg:#x}"
+ t = "R" if read else "W"
+ self.log(f"REG: {t.upper()}.8 {regname} = {val!s}")
diff --git a/tools/proxyclient/m1n1/trace/isp.py b/tools/proxyclient/m1n1/trace/isp.py
new file mode 100644
index 0000000..924bce9
--- /dev/null
+++ b/tools/proxyclient/m1n1/trace/isp.py
@@ -0,0 +1,118 @@
+from . import ADTDevTracer
+from .dart import DARTTracer
+from ..hv import TraceMode
+from ..hw.dart import DART, DARTRegs
+from ..hw.isp import *
+
+class ISPTracer(ADTDevTracer):
+
+ DEFAULT_MODE = TraceMode.SYNC
+
+ REGMAPS = [ISPRegs, PSReg, SPMIReg, SPMIReg, SPMIReg]
+ NAMES = ["isp", "ps", "spmi0", "spmi1", "spmi2"]
+
+ ALLOWLISTED_CHANNELS = ["TERMINAL", "IO", "BUF_H2T", "BUF_T2H", "SHAREDMALLOC", "IO_T2H"]
+
+ def __init__(self, hv, dev_path, dart_dev_path, verbose):
+ super().__init__(hv, dev_path, verbose)
+
+ hv.p.pmgr_adt_clocks_enable("/arm-io/dart-isp")
+
+ self.dart_tracer = DARTTracer(hv, "/arm-io/dart-isp")
+ self.dart_tracer.start()
+ self.dart = self.dart_tracer.dart
+
+ self.ignored_ranges = [
+ # -----------------------------------------------------------------
+ # ## System clock counter (24 mhz)
+ (0x23b734004, 4),
+ (0x23b734008, 4),
+ # ## Noisy memory addresses that are always zero
+ (0x23b734868, 4),
+ (0x23b73486c, 4),
+ (0x23b734b38, 4),
+ (0x23b734b3c, 4),
+ (0x23b734b58, 4),
+ (0x23b734b5c, 4),
+ (0x23b734bd8, 4),
+ (0x23b734bdc, 4),
+ (0x23b734c18, 4),
+ (0x23b734c1c, 4),
+ (0x23b778128, 4),
+ (0x23b77812c, 4),
+ (0x23b77c128, 4),
+ (0x23b77c12c, 4),
+ # # Noisy memory addresses that change value
+ (0x23b700248, 4),
+ (0x23b700258, 4),
+ (0x23b7003f8, 4),
+ (0x23b700470, 4),
+ # # ECPU/PCPU state report
+ (0x23b738004, 4), # ecpu state report
+ (0x23b738008, 4), # pcpu state report
+ # -----------------------------------------------------------------
+ ]
+
+ def r_ISP_GPR0(self, val):
+ # I have no idea how many channels may be available in other platforms
+ # but, at least for M1 I know they are seven (7), so using 64 as safe value here
+ if val.value == 0x8042006:
+ self.log(f"ISP_GPR0 = ACK")
+ elif val.value < 64:
+ self.log(f"ISP_IPC_CHANNELS = {val!s}")
+ self.number_of_channels = val.value
+ elif val.value > 0:
+ self.log(f"ISP_IPC_CHANNEL_TABLE_IOVA = {val!s}")
+ self.channel_table = ISPChannelTable(self, self.number_of_channels, val.value)
+ self.log(f"{str(self.channel_table)}")
+
+ def r_ISP_IRQ_INTERRUPT(self, val):
+ pending_irq = int(val.value)
+ self.log(f"======== BEGIN IRQ ========")
+ #self.channel_table.dump()
+ self.channel_table.get_last_read_command(pending_irq)
+ self.log(f"======== END IRQ ========")
+
+ def w_ISP_DOORBELL_RING0(self, val):
+ doorbell_value = int(val.value)
+ self.log(f"======== BEGIN DOORBELL ========")
+ #self.channel_table.dump()
+ self.channel_table.get_last_write_command(doorbell_value)
+ self.log(f"======== END DOORBELL ========")
+
+ def w_ISP_GPR0(self, val):
+ self.log(f"ISP_GPR0 = ({val!s})")
+ if val.value == 0x1812f80:
+ if self.dart:
+ self.init_struct = self.dart.ioread(0, val.value & 0xFFFFFFFF, 0x190)
+
+ def w_ISP_IRQ_INTERRUPT(self, val):
+ self.log(f"IRQ_INTERRUPT = ({val!s}).")
+ if val.value == 0xf:
+ self.log(f"ISP Interrupts enabled")
+
+ def ioread(self, dva, size):
+ if self.dart:
+ return self.dart.ioread(0, dva & 0xFFFFFFFF, size)
+ else:
+ return self.hv.iface.readmem(dva, size)
+
+ def iowrite(self, dva, data):
+ if self.dart:
+ return self.dart.iowrite(0, dva & 0xFFFFFFFF, data)
+ else:
+ return self.hv.iface.writemem(dva, data)
+
+ def start(self):
+ super().start()
+
+ self.msgmap = {}
+ for name in dir(self):
+ arg = getattr(self, name)
+ if not callable(arg) or not getattr(arg, "is_message", False):
+ continue
+ self.msgmap[arg.direction, arg.endpoint, arg.message] = getattr(self, name), name, arg.regtype
+
+ # Disable trace of memory regions
+ for addr, size in self.ignored_ranges:
+ self.trace(addr, size, TraceMode.OFF) \ No newline at end of file
diff --git a/tools/proxyclient/m1n1/trace/pcie.py b/tools/proxyclient/m1n1/trace/pcie.py
new file mode 100644
index 0000000..191da84
--- /dev/null
+++ b/tools/proxyclient/m1n1/trace/pcie.py
@@ -0,0 +1,108 @@
+from . import Tracer, TraceMode
+from ..utils import *
+
+class R_BAR(Register32):
+ BASE = 31, 4
+ PREFETCH = 3
+ ADDR64 = 2
+ BELOW_1M = 1
+ SPACE = 0
+
+class PCICfgSpace(RegMap):
+ VENDOR_ID = 0x00, Register16
+ PRODUCT_ID = 0x02, Register16
+ COMMAND = 0x04, Register16
+ STATUS = 0x06, Register16
+
+ BAR = irange(0x10, 6, 4), R_BAR
+ ROMADDR = 0x30, Register32
+
+class PCIeDevTracer(Tracer):
+ CFGMAP = PCICfgSpace
+ BARMAPS = []
+ NAMES = []
+ PREFIXES = []
+
+ def __init__(self, hv, apcie, bus, dev, fn, verbose=False):
+ super().__init__(hv, verbose=verbose, ident=f"{type(self).__name__}@{apcie}/{bus:02x}:{dev:02x}.{fn:1x}")
+ self.busn = bus
+ self.devn = dev
+ self.fn = fn
+ self.ecam_off = (bus << 20) | (dev << 15) | (fn << 12)
+ self.apcie = hv.adt[apcie]
+ self.bars = [0] * 6
+ self.bar_ranges = [None] * 6
+ self.cfg_space = self.CFGMAP(hv.u, self.apcie.get_reg(0)[0] + self.ecam_off)
+ self.verbose = 3
+
+ def init_state(self):
+ self.state.bars = [R_BAR(0) for i in range(6)]
+ self.state.barsize = [None] * 6
+
+ @classmethod
+ def _reloadcls(cls, force=False):
+ cls.BARMAPS = [i._reloadcls(force) if i else None for i in cls.BARMAPS]
+ return super()._reloadcls(force)
+
+ def r_cfg_BAR(self, val, index):
+ if self.state.bars[index].BASE == 0xfffffff:
+ size = (0x10000000 - val.BASE) << 4
+ self.log(f"BAR{index} size = {size:#x}")
+ self.state.barsize[index] = size
+
+ def w_cfg_BAR(self, val, index):
+ self.state.bars[index] = val
+ self.update_tracers(val, index)
+
+ def update_tracers(self, val = None, index = None):
+ self.hv.clear_tracers(self.ident)
+ ecam = self.apcie.get_reg(0)[0]
+ self.trace_regmap(ecam + self.ecam_off, 0x1000, self.CFGMAP, prefix="cfg",
+ mode=TraceMode.WSYNC)
+ i = 0
+ while i < 6:
+ idx = i
+ if i == index:
+ bar = val
+ else:
+ bar = self.cfg_space.BAR[i].reg
+ addr = bar.BASE << 4
+ if bar.ADDR64 and i != 5:
+ if i + 1 == index:
+ barh = val
+ else:
+ barh = self.cfg_space.BAR[i + 1].reg
+ addr |= barh.value << 32
+ i += 2
+ else:
+ i += 1
+
+ if addr in (0, 0xfffffff0, 0xffffffff00000000, 0xfffffffffffffff0):
+ continue
+
+ size = self.state.barsize[idx]
+
+ if not size:
+ self.log(f"BAR{idx} size is unknown!")
+ continue
+
+ # Add in PCIe DT addr flags to get the correct translation
+ start = self.apcie.translate(addr | (0x02 << 88))
+
+ self.log(f"Tracing BAR{idx} : {addr:#x} -> {start:#x}..{start+size-1:#x}")
+ self.bar_ranges[idx] = irange(start, size)
+ self.trace_bar(idx, start, size)
+
+ def trace_bar(self, idx, start, size):
+ if idx >= len(self.BARMAPS) or (regmap := self.BARMAPS[idx]) is None:
+ return
+ prefix = name = None
+ if idx < len(self.NAMES):
+ name = self.NAMES[i]
+ if idx < len(self.PREFIXES):
+ prefix = self.PREFIXES[i]
+
+ self.trace_regmap(start, size, regmap, name=name, prefix=prefix)
+
+ def start(self):
+ self.update_tracers()
diff --git a/tools/proxyclient/m1n1/trace/spi.py b/tools/proxyclient/m1n1/trace/spi.py
new file mode 100644
index 0000000..dd4f967
--- /dev/null
+++ b/tools/proxyclient/m1n1/trace/spi.py
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: MIT
+
+from ..hw.spi import *
+from ..hv import TraceMode
+from ..utils import *
+from . import ADTDevTracer
+
+class SPITracer(ADTDevTracer):
+ REGMAPS = [SPIRegs]
+ NAMES = ["spi"]
diff --git a/tools/proxyclient/m1n1/utils.py b/tools/proxyclient/m1n1/utils.py
new file mode 100644
index 0000000..d401575
--- /dev/null
+++ b/tools/proxyclient/m1n1/utils.py
@@ -0,0 +1,1144 @@
+# SPDX-License-Identifier: MIT
+from enum import Enum
+import threading, traceback, bisect, copy, heapq, importlib, sys, itertools, time, os, functools, struct, re, signal
+from construct import Adapter, Int64ul, Int32ul, Int16ul, Int8ul, ExprAdapter, GreedyRange, ListContainer, StopFieldError, ExplicitError, StreamError
+
+__all__ = ["FourCC"]
+
+def align_up(v, a=16384):
+ return (v + a - 1) & ~(a - 1)
+
+align = align_up
+
+def align_down(v, a=16384):
+ return v & ~(a - 1)
+
+def align_pot(v):
+ out = 1
+ while out < v:
+ out *= 2
+ return out
+
+def hexdump(s, sep=" "):
+ return sep.join(["%02x"%x for x in s])
+
+def hexdump32(s, sep=" "):
+ vals = struct.unpack("<%dI" % (len(s)//4), s)
+ return sep.join(["%08x"%x for x in vals])
+
+def _ascii(s):
+ s2 = ""
+ for c in s:
+ if c < 0x20 or c > 0x7e:
+ s2 += "."
+ else:
+ s2 += chr(c)
+ return s2
+
+def chexdump(s, st=0, abbreviate=True, stride=16, indent="", print_fn=print):
+ last = None
+ skip = False
+ for i in range(0,len(s),stride):
+ val = s[i:i+stride]
+ if val == last and abbreviate:
+ if not skip:
+ print_fn(indent+"%08x *" % (i + st))
+ skip = True
+ else:
+ print_fn(indent+"%08x %s |%s|" % (
+ i + st,
+ " ".join(hexdump(val[i:i+8], ' ').ljust(23)
+ for i in range(0, stride, 8)),
+ _ascii(val).ljust(stride)))
+ last = val
+ skip = False
+
+def chexdiff32(prev, cur, ascii=True, offset=0, offset2=None):
+ assert len(cur) % 4 == 0
+ count = len(cur) // 4
+ words = struct.unpack("<%dI" % count, cur)
+
+ if prev is None:
+ last = None
+ else:
+ assert len(prev) == len(cur)
+ last = struct.unpack("<%dI" % count, prev)
+
+ row = 8
+ skipping = False
+ out = []
+ for i in range(0, count, row):
+ off_text = f"{offset + i * 4:016x}"
+ if offset2 is not None:
+ off_text += f"/{offset2 + i * 4:08x}"
+ if not last:
+ if i != 0 and words[i:i+row] == words[i-row:i]:
+ if not skipping:
+ out.append(f"{off_text} *\n")
+ skipping = True
+ else:
+ out.append(f"{off_text} ")
+ for new in words[i:i+row]:
+ out.append("%08x " % new)
+ if ascii:
+ out.append("| " + _ascii(cur[4*i:4*(i+row)]))
+ out.append("\n")
+ skipping = False
+ elif last[i:i+row] != words[i:i+row]:
+ out.append(f"{off_text} ")
+ for old, new in zip(last[i:i+row], words[i:i+row]):
+ so = "%08x" % old
+ sn = s = "%08x" % new
+ if old != new:
+ s = "\x1b[32m"
+ ld = False
+ for a,b in zip(so, sn):
+ d = a != b
+ if ld != d:
+ s += "\x1b[31;1;4m" if d else "\x1b[32m"
+ ld = d
+ s += b
+ s += "\x1b[m"
+ out.append(s + " ")
+ if ascii:
+ out.append("| " + _ascii(cur[4*i:4*(i+row)]))
+ out.append("\n")
+ return "".join(out)
+
+def chexundump(dump, base=0):
+ if type(dump) is bytes:
+ dump = dump.decode("ascii")
+ elif type(dump) is str:
+ pass
+ else:
+ dump = dump.read()
+
+ decoded = bytearray()
+ for line in dump.splitlines():
+ if not line:
+ continue
+ try:
+ cropped = line.split("|", 2)[0]
+ mark, data = cropped.split(" ", 1)
+ if data.strip() == "*":
+ continue
+ offset = int(mark, 16)
+ data = data.replace(" ", "")
+ if len(data) % 2 != 0:
+ raise ValueError("odd sized data")
+ if offset > len(decoded):
+ decoded.extend([0] * (offset - len(decoded) - base))
+ decoded.extend([int(data[i:i+2], 16) for i \
+ in range(0, len(data), 2)])
+ except (ValueError, TypeError) as exc:
+ raise ValueError(f"can't decode line: {line:r}") from exc
+
+ return decoded
+
+_extascii_table_low = [
+ "▪", "☺", "☻", "♥", "♦", "♣", "♠", "•",
+ "◘", "○", "◙", "♂", "♀", "♪", "♫", "☼",
+ "►", "◄", "↕", "‼", "¶", "§", "▬", "↨",
+ "↑", "↓", "→", "←", "∟", "↔", "▲", "▼"]
+
+_extascii_table_high = [
+ "⌂",
+ "█", "⡀", "⢀", "⣀", "⠠", "⡠", "⢠", "⣠",
+ "⠄", "⡄", "⢄", "⣄", "⠤", "⡤", "⢤", "⣤",
+ "⠁", "⡁", "⢁", "⣁", "⠡", "⡡", "⢡", "⣡",
+ "⠅", "⡅", "⢅", "⣅", "⠥", "⡥", "⢥", "⣥",
+ "⠃", "⡃", "⢃", "⣃", "⠣", "⡣", "⢣", "⣣",
+ "⠇", "⡇", "⢇", "⣇", "⠧", "⡧", "⢧", "⣧",
+ "⠉", "⡉", "⢉", "⣉", "⠩", "⡩", "⢩", "⣩",
+ "⠍", "⡍", "⢍", "⣍", "⠭", "⡭", "⢭", "⣭",
+ "⠊", "⡊", "⢊", "⣊", "⠪", "⡪", "⢪", "⣪",
+ "⠎", "⡎", "⢎", "⣎", "⠮", "⡮", "⢮", "⣮",
+ "⠑", "⡑", "⢑", "⣑", "⠱", "⡱", "⢱", "⣱",
+ "⠕", "⡕", "⢕", "⣕", "⠵", "⡵", "⢵", "⣵",
+ "⠚", "⡚", "⢚", "⣚", "⠺", "⡺", "⢺", "⣺",
+ "⠞", "⡞", "⢞", "⣞", "⠾", "⡾", "⢾", "⣾",
+ "⠛", "⡛", "⢛", "⣛", "⠻", "⡻", "⢻", "⣻",
+ "⠟", "⡟", "⢟", "⣟", "⠿", "⡿", "⢿", "⣿"]
+
+def _extascii(s):
+ s2 = ""
+ for c in s:
+ if c < 0x20:
+ s2 += _extascii_table_low[c]
+ elif c > 0x7e:
+ s2 += _extascii_table_high[c-0x7f]
+ else:
+ s2 += chr(c)
+ return s2
+
+def ehexdump(s, st=0, abbreviate=True, indent="", print_fn=print):
+ last = None
+ skip = False
+ for i in range(0,len(s),16):
+ val = s[i:i+16]
+ if val == last and abbreviate:
+ if not skip:
+ print_fn(indent+"%08x *" % (i + st))
+ skip = True
+ else:
+ print_fn(indent+"%08x %s %s |%s|" % (
+ i + st,
+ hexdump(val[:8], ' ').ljust(23),
+ hexdump(val[8:], ' ').ljust(23),
+ _extascii(val).ljust(16)))
+ last = val
+ skip = False
+
+def chexdump32(s, st=0, abbreviate=True):
+ last = None
+ skip = False
+ for i in range(0,len(s),32):
+ val = s[i:i+32]
+ if val == last and abbreviate:
+ if not skip:
+ print("%08x *" % (i + st))
+ skip = True
+ else:
+ print("%08x %s" % (
+ i + st,
+ hexdump32(val, ' ')))
+ last = val
+ skip = False
+
+def unhex(s):
+ s = re.sub(r"/\*.*?\*/", "", s)
+ return bytes.fromhex(s.replace(" ", "").replace("\n", ""))
+
+def dumpstacks(signal, frame):
+ id2name = dict([(th.ident, th.name) for th in threading.enumerate()])
+ code = []
+ for threadId, stack in sys._current_frames().items():
+ code.append("\n# Thread: %s(%d)" % (id2name.get(threadId,""), threadId))
+ for filename, lineno, name, line in traceback.extract_stack(stack):
+ code.append('File: "%s", line %d, in %s' % (filename, lineno, name))
+ if line:
+ code.append(" %s" % (line.strip()))
+ print("\n".join(code))
+ sys.exit(1)
+
+def set_sigquit_stackdump_handler():
+ signal.signal(signal.SIGQUIT, dumpstacks)
+
+def parse_indexlist(s):
+ items = set()
+ for i in s.split(","):
+ if "-" in i:
+ a, b = map(int, i.split("-", 1))
+ for i in range(a, b + 1):
+ items.add(i)
+ else:
+ items.add(int(i))
+ return items
+
+FourCC = ExprAdapter(Int32ul,
+ lambda d, ctx: d.to_bytes(4, "big").decode("latin-1"),
+ lambda d, ctx: int.from_bytes(d.encode("latin-1"), "big"))
+
+class SafeGreedyRange(GreedyRange):
+ def __init__(self, subcon, discard=False):
+ super().__init__(subcon)
+ self.discard = discard
+
+ def _parse(self, stream, context, path):
+ discard = self.discard
+ obj = ListContainer()
+ try:
+ for i in itertools.count():
+ context._index = i
+ e = self.subcon._parsereport(stream, context, path)
+ if not discard:
+ obj.append(e)
+ except StreamError:
+ pass
+ return obj
+
+class ReloadableMeta(type):
+ def __new__(cls, name, bases, dct):
+ m = super().__new__(cls, name, bases, dct)
+ m._load_time = time.time()
+ return m
+
+class Reloadable(metaclass=ReloadableMeta):
+ @classmethod
+ def _reloadcls(cls, force=False):
+ mods = []
+ for c in cls.mro():
+ mod = sys.modules[c.__module__]
+ cur_cls = getattr(mod, c.__name__)
+ mods.append((cur_cls, mod))
+ if c.__name__ == "Reloadable":
+ break
+
+ reloaded = set()
+ newest = 0
+ for pcls, mod in mods[::-1]:
+ source = getattr(mod, "__file__", None)
+ if not source:
+ continue
+ newest = max(newest, os.stat(source).st_mtime, pcls._load_time)
+ if (force or reloaded or pcls._load_time < newest) and mod.__name__ not in reloaded:
+ print(f"Reload: {mod.__name__}")
+ mod = importlib.reload(mod)
+ reloaded.add(mod.__name__)
+
+ return getattr(mods[0][1], cls.__name__)
+
+ def _reloadme(self):
+ self.__class__ = self._reloadcls()
+
+class Constant:
+ def __init__(self, value):
+ self.value = value
+
+ def __call__(self, v):
+ assert v == self.value
+ return v
+
+class RegisterMeta(ReloadableMeta):
+ def __new__(cls, name, bases, dct):
+ m = super().__new__(cls, name, bases, dct)
+
+ f = {}
+
+ if bases and bases[0] is not Reloadable:
+ for cls in bases[0].mro():
+ if cls is Reloadable:
+ break
+ f.update({k: None for k,v in cls.__dict__.items()
+ if not k.startswith("_") and isinstance(v, (int, tuple))})
+
+ f.update({k: None for k, v in dct.items()
+ if not k.startswith("_") and isinstance(v, (int, tuple))})
+
+ m._fields_list = list(f.keys())
+ m._fields = set(f.keys())
+
+ return m
+
+class Register(Reloadable, metaclass=RegisterMeta):
+ _Constant = Constant
+ def __init__(self, v=None, **kwargs):
+ if v is not None:
+ self._value = v
+ for k in self._fields_list:
+ getattr(self, k) # validate
+ else:
+ self._value = 0
+ for k in self._fields_list:
+ field = getattr(self.__class__, k)
+ if isinstance(field, tuple) and len(field) >= 3 and isinstance(field[2], self._Constant):
+ setattr(self, k, field[2].value)
+
+ for k,v in kwargs.items():
+ setattr(self, k, v)
+
+ def __getattribute__(self, attr):
+ if attr.startswith("_") or attr not in self._fields:
+ return object.__getattribute__(self, attr)
+
+ field = getattr(self.__class__, attr)
+ value = self._value
+
+ if isinstance(field, int):
+ return (value >> field) & 1
+ elif isinstance(field, tuple):
+ if len(field) == 2:
+ msb, lsb = field
+ ftype = int
+ else:
+ msb, lsb, ftype = field
+ return ftype((value >> lsb) & ((1 << ((msb + 1) - lsb)) - 1))
+ else:
+ raise AttributeError(f"Invalid field definition {attr} = {field!r}")
+
+ def __setattr__(self, attr, fvalue):
+ if attr.startswith("_"):
+ self.__dict__[attr] = fvalue
+ return
+
+ field = getattr(self.__class__, attr)
+
+ value = self._value
+
+ if isinstance(field, int):
+ self._value = (value & ~(1 << field)) | ((fvalue & 1) << field)
+ elif isinstance(field, tuple):
+ if len(field) == 2:
+ msb, lsb = field
+ else:
+ msb, lsb, ftype = field
+ mask = ((1 << ((msb + 1) - lsb)) - 1)
+ self._value = (value & ~(mask << lsb)) | ((fvalue & mask) << lsb)
+ else:
+ raise AttributeError(f"Invalid field definition {attr} = {field!r}")
+
+ def __int__(self):
+ return self._value
+
+ def _field_val(self, field_name, as_repr=False):
+ field = getattr(self.__class__, field_name)
+ val = getattr(self, field_name)
+ if isinstance(val, Enum):
+ if as_repr:
+ return str(val)
+ else:
+ msb, lsb = field[:2]
+ if (msb - lsb + 1) > 3:
+ return f"0x{val.value:x}({val.name})"
+ else:
+ return f"{val.value}({val.name})"
+ elif not isinstance(val, int):
+ return val
+ elif isinstance(field, int):
+ return val
+ elif isinstance(field, tuple):
+ msb, lsb = field[:2]
+ if (msb - lsb + 1) > 3:
+ return f"0x{val:x}"
+
+ return val
+
+ @property
+ def fields(self):
+ return {k: getattr(self, k) for k in self._fields_list}
+
+ def str_fields(self):
+ return ', '.join(f'{k}={self._field_val(k)}' for k in self._fields_list)
+
+ def __str__(self):
+ return f"0x{self._value:x} ({self.str_fields()})"
+
+ def __repr__(self):
+ return f"{type(self).__name__}({', '.join(f'{k}={self._field_val(k, True)}' for k in self._fields_list)})"
+
+ def copy(self):
+ return type(self)(self._value)
+
+ @property
+ def value(self):
+ return self._value
+ @value.setter
+ def value(self, val):
+ self._value = val
+
+class Register8(Register):
+ __WIDTH__ = 8
+
+class Register16(Register):
+ __WIDTH__ = 16
+
+class Register32(Register):
+ __WIDTH__ = 32
+
+class Register64(Register):
+ __WIDTH__ = 64
+
+class RegAdapter(Adapter):
+ def __init__(self, register):
+ if register.__WIDTH__ == 64:
+ subcon = Int64ul
+ elif register.__WIDTH__ == 32:
+ subcon = Int32ul
+ elif register.__WIDTH__ == 16:
+ subcon = Int16ul
+ elif register.__WIDTH__ == 8:
+ subcon = Int8ul
+ else:
+ raise ValueError("Invalid reg width")
+
+ self.reg = register
+ super().__init__(subcon)
+
+ def _decode(self, obj, context, path):
+ return self.reg(obj)
+
+ def _encode(self, obj, context, path):
+ return obj.value
+
+class RangeMap(Reloadable):
+ def __init__(self):
+ self.__start = []
+ self.__end = []
+ self.__value = []
+
+ def clone(self):
+ r = type(self)()
+ r.__start = list(self.__start)
+ r.__end = list(self.__end)
+ r.__value = [copy.copy(i) for i in self.__value]
+ return r
+
+ def __len__(self):
+ return len(self.__start)
+
+ def __nonzero__(self):
+ return bool(self.__start)
+
+ def __contains(self, pos, addr):
+ if pos < 0 or pos >= len(self.__start):
+ return False
+
+ return self.__start[pos] <= addr and addr <= self.__end[pos]
+
+ def __split(self, pos, addr):
+ self.__start.insert(pos + 1, addr)
+ self.__end.insert(pos, addr - 1)
+ self.__value.insert(pos + 1, copy.copy(self.__value[pos]))
+
+ def __zone(self, zone):
+ if isinstance(zone, slice):
+ zone = range(zone.start if zone.start is not None else 0,
+ zone.stop if zone.stop is not None else 1 << 64)
+ elif isinstance(zone, int):
+ zone = range(zone, zone + 1)
+
+ return zone
+
+ def lookup(self, addr, default=None):
+ addr = int(addr)
+
+ pos = bisect.bisect_left(self.__end, addr)
+ if self.__contains(pos, addr):
+ return self.__value[pos]
+ else:
+ return default
+
+ def __iter__(self):
+ return self.ranges()
+
+ def ranges(self):
+ return (range(s, e + 1) for s, e in zip(self.__start, self.__end))
+
+ def items(self):
+ return ((range(s, e + 1), v) for s, e, v in zip(self.__start, self.__end, self.__value))
+
+ def _overlap_range(self, zone, split=False):
+ zone = self.__zone(zone)
+ if not zone:
+ return 0, 0
+
+ start = bisect.bisect_left(self.__end, zone.start)
+
+ if split:
+ # Handle left-side overlap
+ if self.__contains(start, zone.start) and self.__start[start] != zone.start:
+ self.__split(start, zone.start)
+ start += 1
+ assert self.__start[start] == zone.start
+
+ for pos in range(start, len(self.__start)):
+ if self.__start[pos] >= zone.stop:
+ return start, pos
+ if split and (self.__end[pos] + 1) > zone.stop:
+ self.__split(pos, zone.stop)
+ return start, pos + 1
+
+ return start, len(self.__start)
+
+ def populate(self, zone, default=[]):
+ zone = self.__zone(zone)
+ if len(zone) == 0:
+ return
+
+ start, stop = zone.start, zone.stop
+
+ # Starting insertion point, overlap inclusive
+ pos = bisect.bisect_left(self.__end, zone.start)
+
+ # Handle left-side overlap
+ if self.__contains(pos, zone.start) and self.__start[pos] != zone.start:
+ self.__split(pos, zone.start)
+ pos += 1
+ assert self.__start[pos] == zone.start
+
+ # Iterate through overlapping ranges
+ while start < stop:
+ if pos == len(self.__start):
+ # Append to end
+ val = copy.copy(default)
+ self.__start.append(start)
+ self.__end.append(stop - 1)
+ self.__value.append(val)
+ yield range(start, stop), val
+ break
+
+ assert self.__start[pos] >= start
+ if self.__start[pos] > start:
+ # Insert new range
+ boundary = stop
+ if pos < len(self.__start):
+ boundary = min(stop, self.__start[pos])
+ val = copy.copy(default)
+ self.__start.insert(pos, start)
+ self.__end.insert(pos, boundary - 1)
+ self.__value.insert(pos, val)
+ yield range(start, boundary), val
+ start = boundary
+ else:
+ # Handle right-side overlap
+ if self.__end[pos] > stop - 1:
+ self.__split(pos, stop)
+ # Add to existing range
+ yield range(self.__start[pos], self.__end[pos] + 1), self.__value[pos]
+ start = self.__end[pos] + 1
+
+ pos += 1
+ else:
+ assert start == stop
+
+ def overlaps(self, zone, split=False):
+ start, stop = self._overlap_range(zone, split)
+ for pos in range(start, stop):
+ yield range(self.__start[pos], self.__end[pos] + 1), self.__value[pos]
+
+ def replace(self, zone, val):
+ zone = self.__zone(zone)
+ if zone.start == zone.stop:
+ return
+ start, stop = self._overlap_range(zone, True)
+ self.__start = self.__start[:start] + [zone.start] + self.__start[stop:]
+ self.__end = self.__end[:start] + [zone.stop - 1] + self.__end[stop:]
+ self.__value = self.__value[:start] + [val] + self.__value[stop:]
+
+ def clear(self, zone=None):
+ if zone is None:
+ self.__start = []
+ self.__end = []
+ self.__value = []
+ else:
+ zone = self.__zone(zone)
+ if zone.start == zone.stop:
+ return
+ start, stop = self._overlap_range(zone, True)
+ self.__start = self.__start[:start] + self.__start[stop:]
+ self.__end = self.__end[:start] + self.__end[stop:]
+ self.__value = self.__value[:start] + self.__value[stop:]
+
+ def compact(self, equal=lambda a, b: a == b, empty=lambda a: not a):
+ if len(self) == 0:
+ return
+
+ new_s, new_e, new_v = [], [], []
+
+ for pos in range(len(self)):
+ s, e, v = self.__start[pos], self.__end[pos], self.__value[pos]
+ if empty(v):
+ continue
+ if new_v and equal(last, v) and s == new_e[-1] + 1:
+ new_e[-1] = e
+ else:
+ new_s.append(s)
+ new_e.append(e)
+ new_v.append(v)
+ last = v
+
+ self.__start, self.__end, self.__value = new_s, new_e, new_v
+
+ def _assert(self, expect, val=lambda a:a):
+ state = []
+ for i, j, v in zip(self.__start, self.__end, self.__value):
+ state.append((i, j, val(v)))
+ if state != expect:
+ print(f"Expected: {expect}")
+ print(f"Got: {state}")
+
+class AddrLookup(RangeMap):
+ def __str__(self):
+ b = [""]
+ for zone, values in self.items():
+ b.append(f"{zone.start:#11x} - {zone.stop - 1:#11x}")
+ if len(values) == 0:
+ b.append(f" (empty range)")
+ elif len(values) == 1:
+ b.append(f" : {values[0][0]}\n")
+ if len(values) > 1:
+ b.append(f" ({len(values):d} devices)\n")
+ for value, r in sorted(values, key=lambda r: r[1].start):
+ b.append(f" {r.start:#10x} - {r.stop - 1:#8x} : {value}\n")
+
+ return "".join(b)
+
+ def add(self, zone, value):
+ for r, values in self.populate(zone):
+ values.append((value, zone))
+
+ def remove(self, zone, value):
+ for r, values in self.overlaps(zone):
+ try:
+ values.remove((value, zone))
+ except:
+ pass
+
+ def lookup(self, addr, default='unknown'):
+ maps = super().lookup(addr)
+ return maps[0] if maps else (default, range(0, 1 << 64))
+
+ def lookup_all(self, addr):
+ return super().lookup(addr, [])
+
+ def _assert(self, expect, val=lambda a:a):
+ super()._assert(expect, lambda v: [i[0] for i in v])
+
+class ScalarRangeMap(RangeMap):
+ def get(self, addr, default=None):
+ return self.lookup(addr, default)
+
+ def __setitem__(self, zone, value):
+ self.replace(zone, value)
+
+ def __delitem__(self, zone):
+ self.clear(zone)
+
+ def __getitem__(self, addr):
+ value = self.lookup(addr, default=KeyError)
+ if value is KeyError:
+ raise KeyError(f"Address {addr:#x} has no value")
+ return value
+
+class BoolRangeMap(RangeMap):
+ def set(self, zone):
+ self.replace(zone, True)
+
+ def __delitem__(self, zone):
+ self.clear(zone)
+
+ def __getitem__(self, addr):
+ return self.lookup(addr, False)
+
+class DictRangeMap(RangeMap):
+ def __setitem__(self, k, value):
+ if not isinstance(k, tuple):
+ self.replace(k, dict(value))
+ else:
+ zone, key = k
+ for r, values in self.populate(zone, {}):
+ values[key] = value
+
+ def __delitem__(self, k):
+ if not isinstance(k, tuple):
+ self.clear(k)
+ else:
+ zone, key = k
+ for r, values in self.overlaps(zone, True):
+ values.pop(key, None)
+
+ def __getitem__(self, k):
+ if isinstance(k, tuple):
+ addr, k = k
+ values = self.lookup(addr)
+ return values.get(k, None) if values else None
+ else:
+ values = self.lookup(k)
+ return values or {}
+
+class SetRangeMap(RangeMap):
+ def add(self, zone, key):
+ for r, values in self.populate(zone, set()):
+ values.add(key)
+
+ def discard(self, zone, key):
+ for r, values in self.overlaps(zone, split=True):
+ if values:
+ values.discard(key)
+ remove = discard
+
+ def __setitem__(self, k, value):
+ self.replace(k, set(value))
+
+ def __delitem__(self, k):
+ self.clear(k)
+
+ def __getitem__(self, addr):
+ values = super().lookup(addr)
+ return frozenset(values) if values else frozenset()
+
+class NdRange:
+ def __init__(self, rng, min_step=1):
+ if isinstance(rng, range):
+ self.ranges = [rng]
+ else:
+ self.ranges = list(rng)
+ least_step = self.ranges[0].step
+ for i, rng in enumerate(self.ranges):
+ if rng.step == 1:
+ self.ranges[i] = range(rng.start, rng.stop, min_step)
+ least_step = min_step
+ else:
+ assert rng.step >= min_step
+ least_step = min(least_step, rng.step)
+ self.start = sum(rng[0] for rng in self.ranges)
+ self.stop = sum(rng[-1] for rng in self.ranges) + least_step
+ self.rev = {}
+ for i in itertools.product(*map(enumerate, self.ranges)):
+ index = tuple(j[0] for j in i)
+ addr = sum(j[1] for j in i)
+ if len(self.ranges) == 1:
+ index = index[0]
+ self.rev[addr] = index
+
+ def index(self, item):
+ return self.rev[item]
+
+ def __len__(self):
+ return self.stop - self.start
+
+ def __contains__(self, item):
+ return item in self.rev
+
+ def __getitem__(self, item):
+ if not isinstance(item, tuple):
+ assert len(self.ranges) == 1
+ return self.ranges[0][item]
+
+ assert len(self.ranges) == len(item)
+ if all(isinstance(i, int) for i in item):
+ return sum((i[j] for i, j in zip(self.ranges, item)))
+ else:
+ iters = (i[j] for i, j in zip(self.ranges, item))
+ return map(sum, itertools.product(*(([i] if isinstance(i, int) else i) for i in iters)))
+
+class RegMapMeta(ReloadableMeta):
+ def __new__(cls, name, bases, dct):
+ m = super().__new__(cls, name, bases, dct)
+ if getattr(m, "_addrmap", None) is None:
+ m._addrmap = {}
+ m._rngmap = SetRangeMap()
+ m._namemap = {}
+ else:
+ m._addrmap = dict(m._addrmap)
+ m._rngmap = m._rngmap.clone()
+ m._namemap = dict(m._namemap)
+
+ for k, v in dct.items():
+ if k.startswith("_") or not isinstance(v, tuple):
+ continue
+ addr, rtype = v
+
+ if isinstance(addr, int):
+ m._addrmap[addr] = k, rtype
+ else:
+ addr = NdRange(addr, rtype.__WIDTH__ // 8)
+ m._rngmap.add(addr, (addr, k, rtype))
+
+ m._namemap[k] = addr, rtype
+
+ def prop(k):
+ def getter(self):
+ return self._accessor[k]
+ def setter(self, val):
+ self._accessor[k].val = val
+ return property(getter, setter)
+
+ setattr(m, k, prop(k))
+
+ return m
+
+class RegAccessor(Reloadable):
+ def __init__(self, cls, rd, wr, addr):
+ self.cls = cls
+ self.rd = rd
+ self.wr = wr
+ self.addr = addr
+
+ def __int__(self):
+ return self.rd(self.addr)
+
+ @property
+ def val(self):
+ return self.rd(self.addr)
+
+ @val.setter
+ def val(self, value):
+ self.wr(self.addr, int(value))
+
+ @property
+ def reg(self):
+ val = self.val
+ if val is None:
+ return None
+ return self.cls(val)
+
+ @reg.setter
+ def reg(self, value):
+ self.wr(self.addr, int(value))
+
+ def set(self, **kwargs):
+ r = self.reg
+ for k, v in kwargs.items():
+ setattr(r, k, v)
+ self.wr(self.addr, int(r))
+
+ def __str__(self):
+ return str(self.reg)
+
+class RegArrayAccessor(Reloadable):
+ def __init__(self, range, cls, rd, wr, addr):
+ self.range = range
+ self.cls = cls
+ self.rd = rd
+ self.wr = wr
+ self.addr = addr
+
+ def __getitem__(self, item):
+ off = self.range[item]
+ if isinstance(off, int):
+ return RegAccessor(self.cls, self.rd, self.wr, self.addr + off)
+ else:
+ return [RegAccessor(self.cls, self.rd, self.wr, self.addr + i) for i in off]
+
+class BaseRegMap(Reloadable):
+ def __init__(self, backend, base):
+ self._base = base
+ self._backend = backend
+ self._accessor = {}
+
+ for name, (addr, rcls) in self._namemap.items():
+ width = rcls.__WIDTH__
+ rd = functools.partial(backend.read, width=width)
+ wr = functools.partial(backend.write, width=width)
+ if type(addr).__name__ == "NdRange":
+ self._accessor[name] = RegArrayAccessor(addr, rcls, rd, wr, base)
+ else:
+ self._accessor[name] = RegAccessor(rcls, rd, wr, base + addr)
+
+ def _lookup_offset(cls, offset):
+ reg = cls._addrmap.get(offset, None)
+ if reg is not None:
+ name, rcls = reg
+ return name, None, rcls
+ ret = cls._rngmap[offset]
+ if ret:
+ for rng, name, rcls in ret:
+ if offset in rng:
+ return name, rng.index(offset), rcls
+ return None, None, None
+ lookup_offset = classmethod(_lookup_offset)
+
+ def lookup_addr(self, addr):
+ return self.lookup_offset(addr - self._base)
+
+ def get_name(self, addr):
+ name, index, rcls = self.lookup_addr(addr)
+ if index is not None:
+ return f"{name}[{index}]"
+ else:
+ return name
+
+ def _lookup_name(cls, name):
+ return cls._namemap.get(name, None)
+ lookup_name = classmethod(_lookup_name)
+
+ def _scalar_regs(self):
+ for addr, (name, rtype) in self._addrmap.items():
+ yield addr, name, self._accessor[name], rtype
+
+ def _array_reg(self, zone, map):
+ addrs, name, rtype = map
+ def index(addr):
+ idx = addrs.index(addr)
+ if isinstance(idx, tuple):
+ idx = str(idx)[1:-1]
+ return idx
+ reg = ((addr, f"{name}[{index(addr)}]", self._accessor[name][addrs.index(addr)], rtype)
+ for addr in zone if addr in addrs)
+ return reg
+
+ def _array_regs(self):
+ for zone, maps in self._rngmap.items():
+ yield from heapq.merge(*(self._array_reg(zone, map) for map in maps))
+
+ def dump_regs(self):
+ for addr, name, acc, rtype in heapq.merge(sorted(self._scalar_regs()), self._array_regs()):
+ print(f"{self._base:#x}+{addr:06x} {name} = {acc.reg}")
+
+class RegMap(BaseRegMap, metaclass=RegMapMeta):
+ pass
+
+def irange(start, count, step=1):
+ return range(start, start + count * step, step)
+
+# Table generated by:
+#
+# tbl = [0] * 256
+# crc = 1
+# for i in [2**x for x in irange(7, 0, -1)]:
+# if crc & 1:
+# crc = (crc >> 1) ^ 0xA001
+# else:
+# crc = crc >> 1
+# for j in range(0, 255, 2*i):
+# tbl[i + j] = crc ^ tbl[j]
+#
+# for i in range(0, 255, 8):
+# print(f"{tbl[i]:#06x}, {tbl[i+1]:#06x}, {tbl[i+2]:#06x}, {tbl[i+3]:#06x}, {tbl[i+4]:#06x}, {tbl[i+5]:#06x}, {tbl[i+6]:#06x}, {tbl[i+7]:#06x}, ")
+
+_crc16_table = [
+ 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
+ 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
+ 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
+ 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
+ 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
+ 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
+ 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
+ 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
+ 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
+ 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
+ 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
+ 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
+ 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
+ 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
+ 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
+ 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
+ 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
+ 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
+ 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
+ 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
+ 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
+ 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
+ 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
+ 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
+ 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
+ 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
+ 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
+ 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
+ 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
+ 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
+ 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
+ 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040
+]
+
+def crc16USB(crc, data):
+ for x in data:
+ crc = (crc >> 8) ^ _crc16_table[(crc ^ x) & 0xff]
+ return crc
+
+__all__.extend(k for k, v in globals().items()
+ if (callable(v) or isinstance(v, type)) and v.__module__ == __name__)
+
+if __name__ == "__main__":
+ # AddrLookup test
+ a = AddrLookup()
+ a.add(range(0, 10), 0)
+ a._assert([
+ (0, 9, [0])
+ ])
+ a.add(range(10, 20), 1)
+ a._assert([
+ (0, 9, [0]), (10, 19, [1])
+ ])
+ a.add(range(20, 25), 2)
+ a._assert([
+ (0, 9, [0]), (10, 19, [1]), (20, 24, [2])
+ ])
+ a.add(range(30, 40), 3)
+ a._assert([
+ (0, 9, [0]), (10, 19, [1]), (20, 24, [2]), (30, 39, [3])
+ ])
+ a.add(range(0, 15), 4)
+ a._assert([
+ (0, 9, [0, 4]), (10, 14, [1, 4]), (15, 19, [1]), (20, 24, [2]), (30, 39, [3])
+ ])
+ a.add(range(0, 15), 5)
+ a._assert([
+ (0, 9, [0, 4, 5]), (10, 14, [1, 4, 5]), (15, 19, [1]), (20, 24, [2]), (30, 39, [3])
+ ])
+ a.add(range(21, 44), 6)
+ a._assert([
+ (0, 9, [0, 4, 5]), (10, 14, [1, 4, 5]), (15, 19, [1]), (20, 20, [2]), (21, 24, [2, 6]),
+ (25, 29, [6]), (30, 39, [3, 6]), (40, 43, [6])
+ ])
+ a.add(range(70, 80), 7)
+ a._assert([
+ (0, 9, [0, 4, 5]), (10, 14, [1, 4, 5]), (15, 19, [1]), (20, 20, [2]), (21, 24, [2, 6]),
+ (25, 29, [6]), (30, 39, [3, 6]), (40, 43, [6]), (70, 79, [7])
+ ])
+ a.add(range(0, 100), 8)
+ a._assert([
+ (0, 9, [0, 4, 5, 8]), (10, 14, [1, 4, 5, 8]), (15, 19, [1, 8]), (20, 20, [2, 8]),
+ (21, 24, [2, 6, 8]), (25, 29, [6, 8]), (30, 39, [3, 6, 8]), (40, 43, [6, 8]),
+ (44, 69, [8]), (70, 79, [7, 8]), (80, 99, [8])
+ ])
+ a.remove(range(21, 44), 6)
+ a._assert([
+ (0, 9, [0, 4, 5, 8]), (10, 14, [1, 4, 5, 8]), (15, 19, [1, 8]), (20, 20, [2, 8]),
+ (21, 24, [2, 8]), (25, 29, [8]), (30, 39, [3, 8]), (40, 43, [8]),
+ (44, 69, [8]), (70, 79, [7, 8]), (80, 99, [8])
+ ])
+ a.compact()
+ a._assert([
+ (0, 9, [0, 4, 5, 8]), (10, 14, [1, 4, 5, 8]), (15, 19, [1, 8]), (20, 24, [2, 8]),
+ (25, 29, [8]), (30, 39, [3, 8]), (40, 69, [8]), (70, 79, [7, 8]),
+ (80, 99, [8])
+ ])
+ a.remove(range(0, 100), 8)
+ a._assert([
+ (0, 9, [0, 4, 5]), (10, 14, [1, 4, 5]), (15, 19, [1]), (20, 24, [2]), (25, 29, []),
+ (30, 39, [3]), (40, 69, []), (70, 79, [7]), (80, 99, [])
+ ])
+ a.compact()
+ a._assert([
+ (0, 9, [0, 4, 5]), (10, 14, [1, 4, 5]), (15, 19, [1]), (20, 24, [2]), (30, 39, [3]),
+ (70, 79, [7])
+ ])
+ a.clear(range(12, 21))
+ a._assert([
+ (0, 9, [0, 4, 5]), (10, 11, [1, 4, 5]), (21, 24, [2]), (30, 39, [3]),
+ (70, 79, [7])
+ ])
+
+ # ScalarRangeMap test
+ a = ScalarRangeMap()
+ a[0:5] = 1
+ a[5:10] = 2
+ a[4:8] = 3
+ del a[2:4]
+ expect = [1, 1, None, None, 3, 3, 3, 3, 2, 2, None]
+ for i,j in enumerate(expect):
+ assert a.get(i) == j
+ if j is not None:
+ assert a[i] == j
+ try:
+ a[10]
+ except KeyError:
+ pass
+ else:
+ assert False
+
+ # DictRangeMap test
+ a = DictRangeMap()
+ a[0:5, 0] = 10
+ a[5:8, 1] = 11
+ a[4:6, 2] = 12
+ del a[2:4]
+ expect = [{0: 10}, {0: 10}, {}, {}, {0: 10, 2: 12}, {1: 11, 2: 12}, {1: 11}, {1: 11}, {}]
+ for i,j in enumerate(expect):
+ assert a[i] == j
+ for k, v in j.items():
+ assert a[i, k] == v
+
+ # SetRangeMap test
+ a = SetRangeMap()
+ a[0:2] = {1,}
+ a[2:7] = {2,}
+ a.add(range(1, 4), 3)
+ a.discard(0, -1)
+ a.discard(3, 2)
+ del a[4]
+ expect = [{1,}, {1,3}, {2,3}, {3,}, set(), {2,}, {2,}, set()]
+ for i,j in enumerate(expect):
+ assert a[i] == j
+
+ # BoolRangeMap test
+ a = BoolRangeMap()
+ a.set(range(0, 2))
+ a.set(range(4, 6))
+ a.set(range(5, 5))
+ a.clear(range(3, 5))
+ expect = [True, True, False, False, False, True, False]
+ for i,j in enumerate(expect):
+ assert a[i] == j
diff --git a/tools/proxyclient/m1n1/xnutools.py b/tools/proxyclient/m1n1/xnutools.py
new file mode 100644
index 0000000..88c1a49
--- /dev/null
+++ b/tools/proxyclient/m1n1/xnutools.py
@@ -0,0 +1,117 @@
+# SPDX-License-Identifier: MIT
+import re
+from construct import *
+
+__all__ = []
+
+DebuggerState = Struct(
+ "panic_options" / Hex(Int64ul),
+ "current_op" / Hex(Int32ul),
+ "proceed_on_sync_failre" / Int32ul,
+ "message" / Hex(Int64ul),
+ "panic_str" / Hex(Int64ul),
+ "panic_args" / Hex(Int64ul),
+ "panic_data_ptr" / Hex(Int64ul),
+ "panic_caller" / Hex(Int64ul),
+ "entry_count" / Hex(Int32ul),
+ "kern_return" / Hex(Int32sl)
+)
+
+# Darwin va_list is just a stack pointer...
+VaList = Struct(
+ "stack" / Hex(Int64ul),
+)
+
+def decode_debugger_state(u, ctx):
+ p = u.proxy
+ iface = u.iface
+
+ def hv_readmem(addr, size):
+ addr = p.hv_translate(addr, False, False)
+ assert addr != 0
+ return iface.readmem(addr, size)
+
+ p_state = p.hv_translate(ctx.regs[25], False, False)
+ assert p_state != 0
+ di = iface.readstruct(p_state, DebuggerState)
+ print(di)
+
+ message = hv_readmem(di.message, 1024).split(b"\x00")[0].decode("ascii")
+ print()
+ print(f"Message: {message}")
+
+ print("===== Panic string =====")
+ decode_panic(u, di.panic_str, di.panic_args)
+ print("========================")
+
+def decode_panic_call(u, ctx):
+ decode_panic(u, ctx.regs[0], ctx.regs[1])
+
+def decode_panic(u, p_string, p_args):
+ p = u.proxy
+ iface = u.iface
+
+ def hv_readmem(addr, size):
+ addr = p.hv_translate(addr, False, False)
+ assert addr != 0
+ return iface.readmem(addr, size)
+
+ string = hv_readmem(p_string, 1024).split(b"\x00")[0].decode("ascii")
+ p_args = p.hv_translate(p_args, False, False)
+
+ args = iface.readstruct(p_args, VaList)
+
+ stack = hv_readmem(args.stack, 504)
+
+ def va_arg(t):
+ nonlocal stack
+ d, stack = stack[:8], stack[8:]
+ return t.parse(d)
+
+ utypes = {
+ "hh": Int8ul,
+ "h": Int16ul,
+ None: Int32ul,
+ "l": Int64ul,
+ "ll": Int64ul,
+ "q": Int64ul,
+ "s": Int64ul,
+ "t": Int64ul,
+ }
+
+ stypes = {
+ "hh": Int8sl,
+ "h": Int16sl,
+ None: Int32sl,
+ "l": Int64sl,
+ "ll": Int64sl,
+ "q": Int64sl,
+ "s": Int64sl,
+ "t": Int64sl,
+ }
+
+ #print(string)
+
+ def format_arg(match):
+ pat, flags, width, mod, conv = match.group(0, 1, 2, 3, 4)
+ if conv == "%":
+ return "%"
+ elif conv == "s":
+ return hv_readmem(va_arg(Int64ul), 1024).split(b"\x00")[0].decode("ascii")
+ elif conv in "di":
+ v = va_arg(stypes[mod])
+ return f"%{flags or ''}{width or ''}{conv or ''}" % v
+ elif conv in "ouxX":
+ v = va_arg(utypes[mod])
+ return f"%{flags or ''}{width or ''}{conv or ''}" % v
+ elif conv in "p":
+ return f"0x{va_arg(Int64ul):x}"
+ else:
+ return f"[{pat!r}:{va_arg(Int64ul):x}]"
+
+ string = re.sub('%([-#0 +]*)([1-9][0-9]*)?(hh|h|l|ll|q|L|j|z|Z|t)?([diouxXeEfFgGaAcsCSpnm%])',
+ format_arg, string)
+ print(string + "\n", end="")
+
+__all__.extend(k for k, v in globals().items()
+ if (callable(v) or isinstance(v, type)) and v.__module__ == __name__)
diff --git a/tools/proxyclient/tools/admac_stream.py b/tools/proxyclient/tools/admac_stream.py
new file mode 100755
index 0000000..2c30599
--- /dev/null
+++ b/tools/proxyclient/tools/admac_stream.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+import time
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+import argparse
+from m1n1.hw.dart import DART
+from m1n1.hw.admac import *
+
+argparser = argparse.ArgumentParser()
+argparser.add_argument("-b", "--bufsize", type=int, default=1024*32,
+ help="size of one DMA buffer (covered by one descriptor)")
+argparser.add_argument("-n", "--node", type=str, default="admac-sio",
+ help="name of ADT node")
+argparser.add_argument("-c", "--chan", "--channel", type=int, default=0,
+ help="channel no")
+argparser.add_argument("-w", "--buswidth", type=E_BUSWIDTH, default=E_BUSWIDTH.W_32BIT,
+ help="DMA device-facing bus width")
+argparser.add_argument("-v", "--verbose", action='store_true')
+args = argparser.parse_args()
+
+from m1n1.setup import p, u
+
+# find full ADT path
+path = None
+for node in u.adt["/arm-io"]:
+ if node.name == args.node:
+ path = node._path.removeprefix("/device-tree")
+ print(f"Found {path}", file=sys.stderr)
+ if "clock-gates" in node._properties:
+ print(f"Enabling {path}", file=sys.stderr)
+ p.pmgr_adt_clocks_enable(path)
+ break
+
+if path is None:
+ print(f"No instance named {args.node:r} found!", file=sys.stderr)
+ sys.exit(1)
+
+admac_node = u.adt[path]
+
+iommu_mappers = dict()
+for node in u.adt["/arm-io"].walk_tree():
+ if "compatible" in node._properties and node.compatible == ["iommu-mapper"]:
+ iommu_mappers[getattr(node, "AAPL,phandle")] = node
+
+mapper = iommu_mappers[admac_node.iommu_parent]
+dart_path = mapper._parent_path.removeprefix("/device-tree")[:-1]
+dart_idx = mapper.reg
+
+if "clock-gates" in u.adt[dart_path]._properties:
+ print(f"Enabling {dart_path}", file=sys.stderr)
+ p.pmgr_adt_clocks_enable(path)
+
+dart = DART.from_adt(u, dart_path)
+admac = ADMAC(u, admac_node.get_reg(0)[0], dart,
+ dart_stream=dart_idx, debug=args.verbose)
+
+chan = admac.chans[args.chan]
+chan.disable()
+chan.reset()
+chan.read_reports()
+chan.buswidth = args.buswidth
+chan.framesize = E_FRAME.F_1_WORD
+chan.sram_carveout = (0x0, 0x1000)
+
+if chan.tx:
+ chan.submit(bytearray(args.bufsize))
+else:
+ chan.submit(buflen=args.bufsize)
+chan.enable()
+
+try:
+ if chan.tx:
+ while (buf := sys.stdin.buffer.read(args.bufsize)):
+ while not chan.can_submit():
+ chan.poll()
+ chan.submit(buf)
+ else:
+ while True:
+ while chan.can_submit():
+ chan.submit(buflen=args.bufsize)
+ sys.stdout.buffer.write(chan.poll())
+except KeyboardInterrupt:
+ pass
+
+chan.disable()
diff --git a/tools/proxyclient/tools/chainload.py b/tools/proxyclient/tools/chainload.py
new file mode 100755
index 0000000..691a78a
--- /dev/null
+++ b/tools/proxyclient/tools/chainload.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+import argparse, pathlib, time
+
+parser = argparse.ArgumentParser(description='Mach-O loader for m1n1')
+parser.add_argument('-q', '--quiet', action="store_true", help="Disable framebuffer")
+parser.add_argument('-n', '--no-sepfw', action="store_true", help="Do not preserve SEPFW")
+parser.add_argument('-c', '--call', action="store_true", help="Use call mode")
+parser.add_argument('-r', '--raw', action="store_true", help="Image is raw")
+parser.add_argument('-E', '--entry-point', action="store", type=int, help="Entry point for the raw image", default=0x800)
+parser.add_argument('-x', '--xnu', action="store_true", help="Set up for chainloading XNU")
+parser.add_argument('payload', type=pathlib.Path)
+parser.add_argument('boot_args', default=[], nargs="*")
+args = parser.parse_args()
+
+from m1n1.setup import *
+from m1n1.tgtypes import BootArgs
+from m1n1.macho import MachO
+from m1n1 import asm
+
+new_base = u.base
+
+if args.raw:
+ image = args.payload.read_bytes()
+ image += b"\x00\x00\x00\x00"
+ entry = new_base + args.entry_point
+else:
+ macho = MachO(args.payload.read_bytes())
+ image = macho.prepare_image()
+ image += b"\x00\x00\x00\x00"
+ entry = macho.entry
+ entry -= macho.vmin
+ entry += new_base
+
+if args.quiet:
+ p.iodev_set_usage(IODEV.FB, 0)
+
+if args.no_sepfw:
+ sepfw_start, sepfw_length = 0, 0
+else:
+ sepfw_start, sepfw_length = u.adt["chosen"]["memory-map"].SEPFW
+
+image_size = align(len(image))
+sepfw_off = image_size
+image_size += align(sepfw_length)
+bootargs_off = image_size
+bootargs_size = 0x4000
+image_size += bootargs_size
+
+print(f"Total region size: 0x{image_size:x} bytes")
+image_addr = u.malloc(image_size)
+
+print(f"Loading kernel image (0x{len(image):x} bytes)...")
+u.compressed_writemem(image_addr, image, True)
+p.dc_cvau(image_addr, len(image))
+
+if not args.no_sepfw:
+ print(f"Copying SEPFW (0x{sepfw_length:x} bytes)...")
+ p.memcpy8(image_addr + sepfw_off, sepfw_start, sepfw_length)
+ print(f"Adjusting addresses in ADT...")
+ u.adt["chosen"]["memory-map"].SEPFW = (new_base + sepfw_off, sepfw_length)
+ u.adt["chosen"]["memory-map"].BootArgs = (image_addr + bootargs_off, bootargs_size)
+
+ u.push_adt()
+
+ print("Setting secondary CPU RVBARs...")
+
+ rvbar = entry & ~0xfff
+ for cpu in u.adt["cpus"][1:]:
+ addr, size = cpu.cpu_impl_reg
+ print(f" {cpu.name}: [0x{addr:x}] = 0x{rvbar:x}")
+ p.write64(addr, rvbar)
+
+print("Setting up bootargs...")
+tba = u.ba.copy()
+
+tba.top_of_kernel_data = new_base + image_size
+
+if len(args.boot_args) > 0:
+ boot_args = " ".join(args.boot_args)
+ if "-v" in boot_args.split():
+ tba.video.display = 0
+ else:
+ tba.video.display = 1
+ print(f"Setting boot arguments to {boot_args!r}")
+ tba.cmdline = boot_args
+
+if args.xnu:
+ # Fix virt_base, since we often install m1n1 with it set to 0 which xnu does not like
+ tba.virt_base = 0xfffffe0010000000 + (tba.phys_base & (32 * 1024 * 1024 - 1))
+ tba.devtree = u.ba.devtree - u.ba.virt_base + tba.virt_base
+
+iface.writemem(image_addr + bootargs_off, BootArgs.build(tba))
+
+print(f"Copying stub...")
+
+stub = asm.ARMAsm(f"""
+1:
+ ldp x4, x5, [x1], #16
+ stp x4, x5, [x2]
+ dc cvau, x2
+ ic ivau, x2
+ add x2, x2, #16
+ sub x3, x3, #16
+ cbnz x3, 1b
+
+ ldr x1, ={entry}
+ br x1
+""", image_addr + image_size)
+
+iface.writemem(stub.addr, stub.data)
+p.dc_cvau(stub.addr, stub.len)
+p.ic_ivau(stub.addr, stub.len)
+
+print(f"Entry point: 0x{entry:x}")
+
+if args.xnu and p.display_is_external():
+ if p.display_start_dcp() >= 0:
+ p.display_shutdown(0)
+
+if args.call:
+ print(f"Shutting down MMU...")
+ try:
+ p.mmu_shutdown()
+ except ProxyCommandError:
+ pass
+ print(f"Jumping to stub at 0x{stub.addr:x}")
+ p.call(stub.addr, new_base + bootargs_off, image_addr, new_base, image_size, reboot=True)
+else:
+ print(f"Reloading into stub at 0x{stub.addr:x}")
+ p.reload(stub.addr, new_base + bootargs_off, image_addr, new_base, image_size)
+
+iface.nop()
+print("Proxy is alive again")
diff --git a/tools/proxyclient/tools/codecshell.py b/tools/proxyclient/tools/codecshell.py
new file mode 100755
index 0000000..b875925
--- /dev/null
+++ b/tools/proxyclient/tools/codecshell.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+import argparse
+
+parser = argparse.ArgumentParser(description='Enter a shell for codec-poking')
+parser.add_argument('-n', '--no-reset', action="store_true")
+args = parser.parse_args()
+
+from m1n1.setup import *
+from m1n1.shell import run_shell
+from m1n1.hw.i2c import I2C, I2CRegMapDev
+from m1n1.hw.codecs import *
+
+i2c = {}
+
+class TAS5770(I2CRegMapDev):
+ REGMAP = TAS5770Regs
+ ADDRESSING = (1, 1)
+
+class SN012776(I2CRegMapDev):
+ REGMAP = SN012776Regs
+ ADDRESSING = (1, 1)
+
+class CS42L84(I2CRegMapDev):
+ REGMAP = CS42L84Regs
+ ADDRESSING = (0, 2)
+
+gpios = {}
+for node in u.adt["/arm-io"]:
+ if node.name.endswith("gpio") or node.name.endswith("gpio0"):
+ gpios[node._properties["AAPL,phandle"]] = node
+
+spks = []
+for node in u.adt["/arm-io"]:
+ if not node.name.startswith("i2c"):
+ continue
+
+ n = int(node.name[3:])
+ i2c[n] = bus = I2C(u, f"/arm-io/{node.name}")
+
+ for devnode in node:
+ if "compatible" not in devnode._properties:
+ continue
+
+ dcls = {
+ "audio-control,tas5770": TAS5770,
+ "audio-control,sn012776": SN012776,
+ "audio-control,cs42l84": CS42L84,
+ }.get(devnode.compatible[0], None)
+
+ if not dcls:
+ continue
+
+ dev = dcls.from_adt(bus, f"/arm-io/{node.name}/{devnode.name}")
+ dev.node = devnode
+ i2c[n].devs.append(dev)
+
+ if type(dev) in [TAS5770, SN012776]:
+ spks.append(dev)
+ else:
+ hp = dev
+
+ if "function-reset" in devnode._properties:
+ prop = devnode.function_reset
+ gpio_host = gpios[prop.phandle]
+ addr = gpio_host.get_reg(0)[0] + prop.args[0] * 4
+ if not args.no_reset:
+ print(f"Releasing #RST of {devnode.name}")
+ p.mask32(addr, 1, 0)
+ print(f"Pulling #RST of {devnode.name}")
+ p.mask32(addr, 1, 1)
+
+ if "interrupts" in devnode._properties \
+ and devnode.interrupt_parent in gpios:
+ gpio_host = gpios[devnode.interrupt_parent]
+ addr = gpio_host.get_reg(0)[0] + devnode.interrupts[0] * 4
+ print(f"Monitoring IRQ of {devnode.name}")
+ mon.add(addr, 4)
+
+run_shell(globals(), msg="Have fun!")
diff --git a/tools/proxyclient/tools/dump_pmgr.py b/tools/proxyclient/tools/dump_pmgr.py
new file mode 100755
index 0000000..7159349
--- /dev/null
+++ b/tools/proxyclient/tools/dump_pmgr.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1 import adt
+from m1n1.setup import *
+from m1n1.hw.nco import NCO
+
+dt = u.adt
+
+pmgr = dt["/arm-io/pmgr"]
+
+dev_by_id = {dev.id: dev for dev in pmgr.devices}
+pd_by_id = {pd.id: pd for pd in pmgr.power_domains}
+clk_by_id = {clk.id: clk for clk in pmgr.clocks}
+
+print("=== PS Regs ===")
+for i, r in enumerate(pmgr.ps_regs):
+ print(f" #{i:2d} reg: {r.reg} off: {r.offset:05x} mask:{r.mask:08x}")
+
+print()
+print("=== Perf Regs ===")
+for i, r in enumerate(pmgr.perf_regs):
+ print(f" #{i:2d} reg: {r.reg} off: {r.offset:05x} size:{r.size:05x} unk:{r.unk:08x}")
+
+#print()
+#print("=== PWR Gate Regs ===")
+#for i, r in enumerate(pmgr.pwrgate_regs):
+ #print(f" #{i:2d} reg: {r.reg} off: {r.offset:05x} mask:{r.mask:08x} unk:{r.unk:08x}")
+
+clock_users = {}
+dev_users = {}
+
+for dev in dt["/arm-io"]:
+ if hasattr(dev, "clock_ids") and dev.clock_ids:
+ for i, clk in enumerate(dev.clock_ids):
+ clock_users.setdefault(clk, []).append(f"{dev._path}.clk[{i}]")
+ if hasattr(dev, "clock_gates") and dev.clock_gates:
+ for i, pdev in enumerate(dev.clock_gates):
+ dev_users.setdefault(pdev, []).append(f"{dev._path}.clkgate[{i}]")
+ if hasattr(dev, "power_gates") and dev.power_gates:
+ for i, pdev in enumerate(dev.power_gates):
+ dev_users.setdefault(pdev, []).append(f"{dev._path}.pwrgate[{i}]")
+
+print()
+print("=== Devices ===")
+for i, dev in enumerate(pmgr.devices):
+ flags = ", ".join(k for k in dev.flags if k[0] != "_" and dev.flags[k])
+ s = f" #{i:3d} {dev.name:20s} id: {dev.id:3d} psreg: {dev.psreg:2d}:{dev.psidx:2d} "
+ s += f" flags: {flags:24s} unk1_0: {dev.unk1_0} unk1_1: {dev.unk1_1} unk1_2: {dev.unk1_2} "
+ s += f" perf_reg: {dev.perf_block}:{dev.perf_idx:#04x} unk3: {dev.unk3:3d} {dev.unk2_0:2d} {dev.ps_cfg16:2d} {dev.unk2_3:3d}"
+
+ if not dev.flags.no_ps:
+ ps = pmgr.ps_regs[dev.psreg]
+ addr = pmgr.get_reg(ps.reg)[0] + ps.offset + dev.psidx * 8
+ val = p.read32(addr)
+ s += f" @ {addr:#x} = {val:#010x}"
+ else:
+ s += f" @ "
+ if dev.pd:
+ pd = pd_by_id[dev.pd]
+ s += f" pd: {pd.name:20s}"
+ else:
+ s += " "
+ if any(dev.parents):
+ s += " parents: " + ", ".join(dev_by_id[idx].name if idx in dev_by_id else f"#{idx}" for idx in dev.parents if idx)
+ print(s)
+ for i in dev_users.get(dev.id, []):
+ print(f" User: {i}")
+
+print()
+print("=== Clocks ===")
+for i, clk in enumerate(pmgr.clocks):
+ perf = pmgr.perf_regs[clk.perf_block]
+ reg = pmgr.get_reg(perf.reg)[0] + 0x100 + clk.perf_idx * 0x10
+ print(f" #{i:3d} {clk.name:20s} id: {clk.id:3d} reg:{clk.perf_block}:{clk.perf_idx:#4x} ({reg:#x}) {clk.unk:#x}")
+
+print()
+print("=== Power Domains ===")
+for i, pd in enumerate(pmgr.power_domains):
+ perf = pmgr.perf_regs[pd.perf_block]
+ reg = pmgr.get_reg(perf.reg)[0] + 0x100 + pd.perf_idx * 0x10
+ print(f" #{i:3d} {pd.name:20s} id: {pd.id:3d} reg:{pd.perf_block}:{pd.perf_idx:#4x} ({reg:#x})")
+
+print()
+print("=== Events ===")
+for i, ev in enumerate(pmgr.events):
+ perf = pmgr.perf_regs[ev.perf_block]
+ reg = pmgr.get_reg(perf.reg)[0] + 0x100 + ev.perf_idx * 0x10
+ v = f" #{i:3d} {ev.name:20s} unk:{ev.unk1:#3x}/{ev.unk2}/{ev.unk3} id: {ev.id:3d} reg:{ev.perf_block}:{ev.perf_idx:#4x} ({reg:#x})"
+ if ev.perf2_idx:
+ perf2 = pmgr.perf_regs[ev.perf2_block]
+ reg2 = pmgr.get_reg(perf2.reg)[0] + 0x100 + ev.perf2_idx * 0x10
+ v += f" reg2:{ev.perf2_block}:{ev.perf2_idx:#4x} ({reg2:#x})"
+ print(v)
+
+arm_io = dt["/arm-io"]
+
+print()
+print("=== Fixed clocks ===")
+for clk in range(256):
+ users = clock_users.get(clk, [])
+ if users:
+ print(f" #{clk}")
+ for j in users:
+ print(f" User: {j}")
+
+print()
+print("=== Boot clocks ===")
+for i, (freq, reg, nclk) in enumerate(zip(arm_io.clock_frequencies,
+ arm_io.clock_frequencies_regs,
+ arm_io.clock_frequencies_nclk)):
+ v = ""
+ clk_type = reg >> 56
+ reg = reg & 0xFFFFFFFFFFFFFF
+
+ if clk_type == 0x9c:
+ v = f"fixed: {reg}"
+ elif clk_type in (0xa0, 0xa1, 0xa4, 0xa5):
+ v = f"regval: {p.read32(reg):#x}"
+ elif clk_type == 0xa8:
+ regvals = [p.read32(reg+off*4) for off in range(5)]
+ try:
+ # we are using the freq value from ADT as fin of NCO,
+ # that's not exactly correct
+ nco_freq = NCO.calc_rate(freq, regvals)
+ except ValueError as e:
+ nco_freq = 0
+
+ v = f"nco: (calculated rate: {nco_freq:d}) "
+ for val in regvals:
+ v += f"{val:#x} "
+
+
+ print(f"#{i:3}: {freq:10d} {nclk} {clk_type:#x}/{reg:#x}: {v}")
+ for j in clock_users.get(i + 256, []):
+ print(f" User: {j}")
diff --git a/tools/proxyclient/tools/freebsd.py b/tools/proxyclient/tools/freebsd.py
new file mode 100755
index 0000000..14124c6
--- /dev/null
+++ b/tools/proxyclient/tools/freebsd.py
@@ -0,0 +1,136 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+import serial
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+import argparse, pathlib
+
+# FreeBSD's setup differs from Linux's in the following primary ways:
+#
+# 1.) We pretend our kernel is an initramfs and pick it up as such in a modified
+# loader build. This is the simplest way to avoid having ad-hoc
+# interpretations of stuff in m1n1 for our quirky development setup.
+#
+# 2.) U-Boot and dtb are required, loader/kernel are not. The latter are
+# assumed to be discoverable by the standard U-Boot process on disk if they
+# are not specified. Otherwise, we'll load them from memory if they are
+# provided.
+#
+parser = argparse.ArgumentParser(description='(FreeBSD) kernel loader for m1n1')
+parser.add_argument('u_boot', type=pathlib.Path, help="load u-boot before linux")
+parser.add_argument('dtb', type=pathlib.Path)
+parser.add_argument('-l', '--loader', type=pathlib.Path)
+parser.add_argument('-k', '--kernel', type=pathlib.Path)
+parser.add_argument('-b', '--bootargs', type=str, metavar='"boot arguments"')
+parser.add_argument('-t', '--tty', type=str)
+args = parser.parse_args()
+
+from m1n1.setup import *
+
+if args.tty is not None:
+ tty_dev = serial.Serial(args.tty)
+ tty_dev.reset_input_buffer()
+ tty_dev.baudrate = 1500000
+else:
+ tty_dev = None
+
+if args.loader is not None:
+ loader = args.loader.read_bytes()
+ loader_size = len(loader)
+else:
+ loader = None
+ loader_size = 0
+
+dtb = args.dtb.read_bytes()
+
+if args.kernel is not None:
+ kernel = args.kernel.read_bytes()
+ kernel_size = len(kernel)
+else:
+ kernel = None
+ kernel_size = 0
+
+if args.bootargs is not None:
+ print('Setting boot args: "{}"'.format(args.bootargs))
+ p.kboot_set_chosen("bootarg", args.bootargs)
+
+dtb_addr = u.malloc(len(dtb))
+print("Loading DTB to 0x%x..." % dtb_addr)
+
+iface.writemem(dtb_addr, dtb)
+
+loader_base = u.memalign(2 * 1024 * 1024, loader_size)
+
+print("loader_base: 0x%x" % loader_base)
+
+assert not (loader_base & 0xffff)
+
+if kernel is not None:
+ kernel_base = u.memalign(65536, kernel_size)
+ print("Loading %d kernel bytes to 0x%x..." % (kernel_size, kernel_base))
+ iface.writemem(kernel_base, kernel, True)
+ p.kboot_set_initrd(kernel_base, kernel_size)
+
+uboot = bytearray(args.u_boot.read_bytes())
+uboot_size = len(uboot)
+uboot_addr = u.memalign(2*1024*1024, len(uboot))
+print("Loading u-boot to 0x%x..." % uboot_addr)
+
+bootenv_start = uboot.find(b"bootcmd=run distro_bootcmd")
+bootenv_len = uboot[bootenv_start:].find(b"\x00\x00")
+bootenv_old = uboot[bootenv_start:bootenv_start+bootenv_len]
+bootenv = str(bootenv_old, "ascii").split("\x00")
+bootenv = list(filter(lambda x: not (x.startswith("baudrate") or (x.startswith("boot_") and not x.startswith("boot_efi_")) or x.startswith("distro_bootcmd")), bootenv))
+
+if loader is not None:
+ # dtb_addr not used here, the prepared fdt's at a different location. If
+ # we use this one, we won't get any of our /chosen additions, for instance.
+ bootcmd = "distro_bootcmd=bootefi 0x%x - 0x%x" % (loader_base, loader_size)
+else:
+ bootcmd = "distro_bootcmd=devnum=0; run usb_boot"
+
+if tty_dev is not None:
+ bootenv.append("baudrate=%d" % tty_dev.baudrate)
+bootenv.append(bootcmd)
+if args.bootargs is not None:
+ bootenv.append("bootargs=" + args.bootargs)
+
+bootenv_new = b"\x00".join(map(lambda x: bytes(x, "ascii"), bootenv))
+bootenv_new = bootenv_new.ljust(len(bootenv_old), b"\x00")
+
+if len(bootenv_new) > len(bootenv_old):
+ raise Exception("New bootenv cannot be larger than original bootenv")
+uboot[bootenv_start:bootenv_start+bootenv_len] = bootenv_new
+
+u.compressed_writemem(uboot_addr, uboot, True)
+p.dc_cvau(uboot_addr, uboot_size)
+p.ic_ivau(uboot_addr, uboot_size)
+
+boot_addr = uboot_addr
+
+p.smp_start_secondaries()
+
+if p.kboot_prepare_dt(dtb_addr):
+ print("DT prepare failed")
+ sys.exit(1)
+
+iface.dev.timeout = 40
+
+if loader is not None:
+ print("Loading %d bytes to 0x%x..0x%x..." % (loader_size, loader_base, loader_base + loader_size))
+ iface.writemem(loader_base, loader, True)
+
+ p.dc_cvau(loader_base, loader_size)
+ p.ic_ivau(loader_base, loader_size)
+
+print("Ready to boot")
+
+daif = u.mrs(DAIF)
+daif = 0xc0
+u.msr(DAIF, daif)
+print("DAIF: %x" % daif)
+
+p.kboot_boot(boot_addr)
+
+iface.ttymode(tty_dev)
diff --git a/tools/proxyclient/tools/linux.py b/tools/proxyclient/tools/linux.py
new file mode 100755
index 0000000..2c1d517
--- /dev/null
+++ b/tools/proxyclient/tools/linux.py
@@ -0,0 +1,153 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+import serial
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+import argparse, pathlib
+
+parser = argparse.ArgumentParser(description='(Linux) kernel loader for m1n1')
+parser.add_argument('payload', type=pathlib.Path)
+parser.add_argument('dtb', type=pathlib.Path)
+parser.add_argument('initramfs', nargs='?', type=pathlib.Path)
+parser.add_argument('--compression', choices=['auto', 'none', 'gz', 'xz'], default='auto')
+parser.add_argument('-b', '--bootargs', type=str, metavar='"boot arguments"')
+parser.add_argument('-t', '--tty', type=str)
+parser.add_argument('-u', '--u-boot', type=pathlib.Path, help="load u-boot before linux")
+args = parser.parse_args()
+
+from m1n1.setup import *
+
+if args.compression == 'auto':
+ suffix = args.payload.suffix
+ if suffix == '.gz':
+ args.compression = 'gz'
+ elif suffix == '.xz':
+ args.compression = 'xz'
+ else:
+ raise ValueError('unknown compression for {}'.format(args.payload))
+
+if args.tty is not None:
+ tty_dev = serial.Serial(args.tty)
+ tty_dev.reset_input_buffer()
+ tty_dev.baudrate = 1500000
+else:
+ tty_dev = None
+
+payload = args.payload.read_bytes()
+dtb = args.dtb.read_bytes()
+if args.initramfs is not None:
+ initramfs = args.initramfs.read_bytes()
+ initramfs_size = len(initramfs)
+else:
+ initramfs = None
+ initramfs_size = 0
+
+if args.bootargs is not None:
+ print('Setting boot args: "{}"'.format(args.bootargs))
+ p.kboot_set_chosen("bootargs", args.bootargs)
+
+if args.compression != 'none':
+ compressed_size = len(payload)
+ compressed_addr = u.malloc(compressed_size)
+
+ print("Loading %d bytes to 0x%x..0x%x..." % (compressed_size, compressed_addr, compressed_addr + compressed_size))
+ iface.writemem(compressed_addr, payload, True)
+
+dtb_addr = u.malloc(len(dtb))
+print("Loading DTB to 0x%x..." % dtb_addr)
+
+iface.writemem(dtb_addr, dtb)
+
+kernel_size = 512 * 1024 * 1024
+kernel_base = u.memalign(2 * 1024 * 1024, kernel_size)
+boot_addr = kernel_base
+
+print("Kernel_base: 0x%x" % kernel_base)
+
+assert not (kernel_base & 0xffff)
+
+if initramfs is not None:
+ initramfs_base = u.memalign(65536, initramfs_size)
+ print("Loading %d initramfs bytes to 0x%x..." % (initramfs_size, initramfs_base))
+ iface.writemem(initramfs_base, initramfs, True)
+ p.kboot_set_initrd(initramfs_base, initramfs_size)
+
+
+if args.u_boot:
+ uboot = bytearray(args.u_boot.read_bytes())
+ uboot_size = len(uboot)
+ uboot_addr = u.memalign(2*1024*1024, len(uboot))
+ print("Loading u-boot to 0x%x..." % uboot_addr)
+
+ bootenv_start = uboot.find(b"bootcmd=run distro_bootcmd")
+ bootenv_len = uboot[bootenv_start:].find(b"\x00\x00")
+ bootenv_old = uboot[bootenv_start:bootenv_start+bootenv_len]
+ bootenv = str(bootenv_old, "ascii").split("\x00")
+ bootenv = list(filter(lambda x: not (x.startswith("baudrate") or x.startswith("boot_") or x.startswith("distro_bootcmd")), bootenv))
+
+ if initramfs is not None:
+ bootcmd = "distro_bootcmd=booti 0x%x 0x%x:0x%x $fdtcontroladdr" % (kernel_base, initramfs_base, initramfs_size)
+ else:
+ bootcmd = "distro_bootcmd=booti 0x%x - $fdtcontroladdr" % (kernel_base)
+
+ if tty_dev is not None:
+ bootenv.append("baudrate=%d" % tty_dev.baudrate)
+ bootenv.append(bootcmd)
+ if args.bootargs is not None:
+ bootenv.append("bootargs=" + args.bootargs)
+
+ bootenv_new = b"\x00".join(map(lambda x: bytes(x, "ascii"), bootenv))
+ bootenv_new = bootenv_new.ljust(len(bootenv_old), b"\x00")
+
+ if len(bootenv_new) > len(bootenv_old):
+ raise Exception("New bootenv cannot be larger than original bootenv")
+ uboot[bootenv_start:bootenv_start+bootenv_len] = bootenv_new
+
+ u.compressed_writemem(uboot_addr, uboot, True)
+ p.dc_cvau(uboot_addr, uboot_size)
+ p.ic_ivau(uboot_addr, uboot_size)
+
+ boot_addr = uboot_addr
+
+p.smp_start_secondaries()
+
+if p.kboot_prepare_dt(dtb_addr):
+ print("DT prepare failed")
+ sys.exit(1)
+
+iface.dev.timeout = 40
+
+if args.compression == 'none':
+ kernel_size = len(payload)
+ print("Loading %d bytes to 0x%x..0x%x..." % (kernel_size, kernel_base, kernel_base + kernel_size))
+ iface.writemem(kernel_base, payload, True)
+elif args.compression == 'gz':
+ print("Uncompressing gz ...")
+ kernel_size = p.gzdec(compressed_addr, compressed_size, kernel_base, kernel_size)
+elif args.compression == 'xz':
+ print("Uncompressing xz ...")
+ kernel_size = p.xzdec(compressed_addr, compressed_size, kernel_base, kernel_size)
+else:
+ raise ValueError('unsupported compression {}'.format(args.compression))
+
+print(kernel_size)
+
+if kernel_size < 0:
+ raise Exception("Decompression error!")
+
+print("Decompress OK...")
+
+p.dc_cvau(kernel_base, kernel_size)
+p.ic_ivau(kernel_base, kernel_size)
+
+print("Ready to boot")
+
+daif = u.mrs(DAIF)
+daif = 0xc0
+u.msr(DAIF, daif)
+print("DAIF: %x" % daif)
+
+p.kboot_boot(boot_addr)
+
+iface.ttymode(tty_dev)
diff --git a/tools/proxyclient/tools/picocom-sec.sh b/tools/proxyclient/tools/picocom-sec.sh
new file mode 100755
index 0000000..053df37
--- /dev/null
+++ b/tools/proxyclient/tools/picocom-sec.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+while true; do
+ while [ ! -e /dev/m1n1-sec ]; do sleep 1; done
+ picocom --omap crlf --imap lfcrlf -b 500000 /dev/m1n1-sec
+ sleep 1
+done
diff --git a/tools/proxyclient/tools/pmgr_adt2dt.py b/tools/proxyclient/tools/pmgr_adt2dt.py
new file mode 100755
index 0000000..bb2b3d9
--- /dev/null
+++ b/tools/proxyclient/tools/pmgr_adt2dt.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+import serial
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+import argparse, pathlib
+
+from m1n1 import adt
+
+parser = argparse.ArgumentParser(description='Convert ADT PMGR nodes to Device Tree format')
+parser.add_argument('input', type=pathlib.Path)
+args = parser.parse_args()
+
+adt_data = args.input.read_bytes()
+dt = adt.load_adt(adt_data)
+
+pmgr = dt["/arm-io/pmgr"]
+
+dev_by_id = {dev.id: dev for dev in pmgr.devices}
+
+blocks = {}
+maxaddr = {}
+
+for i, dev in enumerate(pmgr.devices):
+ if dev.flags.no_ps:
+ continue
+ ps = pmgr.ps_regs[dev.psreg]
+ block = pmgr.get_reg(ps.reg)
+ blocks.setdefault(block, []).append(dev)
+ offset = ps.offset + dev.psidx * 8
+ maxaddr[block[0]] = max(maxaddr.get(block[0], 0), offset)
+
+pmgr_compat = pmgr.compatible[0].split(",")[1]
+compatible = f'"apple,{pmgr_compat}-pmgr", "apple,pmgr", "syscon", "simple-mfd"'
+ps_compatible = f'"apple,{pmgr_compat}-pmgr-pwrstate", "apple,pmgr-pwrstate"'
+
+for i, ((base, size), devices) in enumerate(sorted(blocks.items())):
+
+ size = min(size, (maxaddr[base] + 0x3fff) & ~0x3fff)
+
+ print(f"pmgr{i}: power-management@{base:x} {{")
+ print(f"\tcompatible = {compatible};")
+ print( "\t#address-cells = <1>;")
+ print( "\t#size-cells = <1>;")
+ print()
+ print(f"\treg = <{base >> 32:#x} {base & 0xffffffff:#x} 0 {size:#x}>;")
+ print( "};")
+ print()
+
+for i, ((base, size), devices) in enumerate(sorted(blocks.items())):
+ print(f"&pmgr{i} {{")
+
+ for dev in sorted(devices, key=lambda d: pmgr.ps_regs[d.psreg].offset + dev.psidx * 8):
+ if dev.flags.no_ps:
+ continue
+
+ ps = pmgr.ps_regs[dev.psreg]
+ offset = ps.offset + dev.psidx * 8
+ addr = pmgr.get_reg(ps.reg)[0] + offset
+ assert base <= addr <= (base + size)
+
+ print()
+ print(f"\tps_{dev.name.lower()}: power-controller@{offset:x} {{")
+ print(f"\t\tcompatible = {ps_compatible};")
+ print(f"\t\treg = <{offset:#x} 4>;")
+ print( "\t\t#power-domain-cells = <0>;")
+ print( "\t\t#reset-cells = <0>;")
+ print(f'\t\tlabel = "{dev.name.lower()}";')
+ if dev.flags.critical:
+ print("\t\tapple,always-on;")
+
+ if any(dev.parents):
+ domains = [f"<&ps_{dev_by_id[idx].name.lower()}>" for idx in dev.parents if idx]
+ print(f"\t\tpower-domains = {', '.join(domains)};")
+
+ print( "\t};")
+ print( "};")
+ print()
diff --git a/tools/proxyclient/tools/reboot.py b/tools/proxyclient/tools/reboot.py
new file mode 100755
index 0000000..770ad1d
--- /dev/null
+++ b/tools/proxyclient/tools/reboot.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+from m1n1.hw.pmu import PMU
+
+PMU(u).reset_panic_counter()
+
+p.reboot()
diff --git a/tools/proxyclient/tools/reset_panic_counter.py b/tools/proxyclient/tools/reset_panic_counter.py
new file mode 100755
index 0000000..c6351f0
--- /dev/null
+++ b/tools/proxyclient/tools/reset_panic_counter.py
@@ -0,0 +1,9 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+from m1n1.hw.pmu import PMU
+
+PMU(u).reset_panic_counter()
diff --git a/tools/proxyclient/tools/run_guest.py b/tools/proxyclient/tools/run_guest.py
new file mode 100755
index 0000000..1346a6f
--- /dev/null
+++ b/tools/proxyclient/tools/run_guest.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib, traceback
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+import argparse, pathlib
+from io import BytesIO
+
+def volumespec(s):
+ return tuple(s.split(":", 2))
+
+parser = argparse.ArgumentParser(description='Run a Mach-O payload under the hypervisor')
+parser.add_argument('-s', '--symbols', type=pathlib.Path)
+parser.add_argument('-m', '--script', type=pathlib.Path, action='append', default=[])
+parser.add_argument('-c', '--command', action="append", default=[])
+parser.add_argument('-S', '--shell', action="store_true")
+parser.add_argument('-e', '--hook-exceptions', action="store_true")
+parser.add_argument('-d', '--debug-xnu', action="store_true")
+parser.add_argument('-l', '--logfile', type=pathlib.Path)
+parser.add_argument('-C', '--cpus', default=None)
+parser.add_argument('-r', '--raw', action="store_true")
+parser.add_argument('-E', '--entry-point', action="store", type=int, help="Entry point for the raw image", default=0x800)
+parser.add_argument('-a', '--append-payload', type=pathlib.Path, action="append", default=[])
+parser.add_argument('-v', '--volume', type=volumespec, action='append',
+ help='Attach a 9P virtio device for file export to the guest. The argument is a host path to the '
+ 'exported tree, joined by colon (\':\') with a tag under which the tree will be advertised '
+ 'on the guest side.')
+parser.add_argument('payload', type=pathlib.Path)
+parser.add_argument('boot_args', default=[], nargs="*")
+args = parser.parse_args()
+
+from m1n1.proxy import *
+from m1n1.proxyutils import *
+from m1n1.utils import *
+from m1n1.shell import run_shell
+from m1n1.hv import HV
+from m1n1.hv.virtio import Virtio9PTransport
+from m1n1.hw.pmu import PMU
+
+iface = UartInterface()
+p = M1N1Proxy(iface, debug=False)
+bootstrap_port(iface, p)
+u = ProxyUtils(p, heap_size = 128 * 1024 * 1024)
+
+hv = HV(iface, p, u)
+
+hv.hook_exceptions = args.hook_exceptions
+
+hv.init()
+
+if args.cpus:
+ avail = [i.name for i in hv.adt["/cpus"]]
+ want = set(f"cpu{i}" for i in args.cpus)
+ for cpu in avail:
+ if cpu in want:
+ continue
+ try:
+ del hv.adt[f"/cpus/{cpu}"]
+ print(f"Disabled {cpu}")
+ except KeyError:
+ continue
+
+if args.debug_xnu:
+ hv.adt["chosen"].debug_enabled = 1
+
+if args.volume:
+ for path, tag in args.volume:
+ hv.attach_virtio(Virtio9PTransport(root=path, tag=tag))
+
+if args.logfile:
+ hv.set_logfile(args.logfile.open("w"))
+
+if len(args.boot_args) > 0:
+ boot_args = " ".join(args.boot_args)
+ hv.set_bootargs(boot_args)
+
+symfile = None
+if args.symbols:
+ symfile = args.symbols.open("rb")
+
+payload = args.payload.open("rb")
+
+if args.append_payload:
+ concat = BytesIO()
+ concat.write(payload.read())
+ for part in args.append_payload:
+ concat.write(part.open("rb").read())
+ concat.seek(0)
+ payload = concat
+
+if args.raw:
+ hv.load_raw(payload.read(), args.entry_point)
+else:
+ hv.load_macho(payload, symfile=symfile)
+
+PMU(u).reset_panic_counter()
+
+for i in args.script:
+ try:
+ hv.run_script(i)
+ except:
+ traceback.print_exc()
+ args.shell = True
+
+for i in args.command:
+ try:
+ hv.run_code(i)
+ except:
+ traceback.print_exc()
+ args.shell = True
+
+if args.shell:
+ run_shell(hv.shell_locals, "Entering hypervisor shell. Type ^D to start the guest.")
+
+hv.start()
diff --git a/tools/proxyclient/tools/run_guest_kernel.sh b/tools/proxyclient/tools/run_guest_kernel.sh
new file mode 100755
index 0000000..849f67a
--- /dev/null
+++ b/tools/proxyclient/tools/run_guest_kernel.sh
@@ -0,0 +1,58 @@
+#!/bin/sh
+set -e
+
+: ${TMPDIR:=$XDG_RUNTIME_DIR}
+: ${TMPDIR:=/tmp}
+
+if [ "$1" == "-k" ]; then
+ kernel="$(realpath "$2")"
+ shift 2
+fi
+
+if [ ! -d "$1" ]; then
+ echo "Usage:"
+ echo " $0 <kernel build root> [kernel commandline] [initramfs]"
+ exit 1
+fi
+
+kernel_base="$(realpath "$1")"
+args="$2"
+initramfs=""
+shift 2
+
+if [ "$1" == "--" ]; then
+ shift
+elif [ -n "$1" ]; then
+ initramfs="$(realpath "$1")"
+ shift
+fi
+
+if [ -z "$kernel" ]; then
+ kernel="$kernel_base"/arch/arm64/boot/Image.gz
+fi
+
+base="$(dirname "$0")"
+
+echo "Creating m1n1+kernel image"
+cp "$base"/../../build/m1n1.bin "$TMPDIR/m1n1-linux.bin"
+if [ -n "$args" ]; then
+ echo "chosen.bootargs=$args" >>"$TMPDIR/m1n1-linux.bin"
+fi
+
+cat "$kernel_base"/arch/arm64/boot/dts/apple/*.dtb >>"$TMPDIR/m1n1-linux.bin"
+if [[ "$kernel" == *.gz ]]; then
+ cat "$kernel" >>"$TMPDIR/m1n1-linux.bin"
+else
+ gzip -c <"$kernel" >>"$TMPDIR/m1n1-linux.bin"
+fi
+
+if [ -n "$initramfs" ]; then
+ cat "$initramfs" >>"$TMPDIR/m1n1-linux.bin"
+fi
+
+echo "Chainloading to updated m1n1..."
+python "$base"/chainload.py -r "$base"/../../build/m1n1.bin
+echo "Starting guest..."
+exec python "$base"/run_guest.py \
+ -c "load_system_map('$kernel_base/System.map')" "$@" \
+ -r "$TMPDIR/m1n1-linux.bin"
diff --git a/tools/proxyclient/tools/second_proxy.py b/tools/proxyclient/tools/second_proxy.py
new file mode 100755
index 0000000..46d3c69
--- /dev/null
+++ b/tools/proxyclient/tools/second_proxy.py
@@ -0,0 +1,10 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+import time
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+
+p.usb_iodev_vuart_setup(p.iodev_whoami())
+p.iodev_set_usage(IODEV.USB_VUART, USAGE.UARTPROXY)
diff --git a/tools/proxyclient/tools/shell.py b/tools/proxyclient/tools/shell.py
new file mode 100755
index 0000000..2ee07d8
--- /dev/null
+++ b/tools/proxyclient/tools/shell.py
@@ -0,0 +1,9 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+from m1n1.shell import run_shell
+
+run_shell(globals(), msg="Have fun!")
diff --git a/tools/proxyclient/tools/smccli.py b/tools/proxyclient/tools/smccli.py
new file mode 100755
index 0000000..146168d
--- /dev/null
+++ b/tools/proxyclient/tools/smccli.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+import sys, pathlib
+sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
+
+from m1n1.setup import *
+from m1n1.shell import run_shell
+from m1n1.fw.smc import SMCClient
+
+smc_addr = u.adt["arm-io/smc"].get_reg(0)[0]
+smc = SMCClient(u, smc_addr, None)
+smc.verbose = 3
+
+smc.start()
+smc.start_ep(0x20)
+
+run_shell(globals(), msg="Have fun!")
+
+smc.stop()
diff --git a/tools/rust/Cargo.lock b/tools/rust/Cargo.lock
new file mode 100644
index 0000000..7376cd5
--- /dev/null
+++ b/tools/rust/Cargo.lock
@@ -0,0 +1,56 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+
+[[package]]
+name = "cstr_core"
+version = "0.2.5"
+dependencies = [
+ "cty",
+ "memchr",
+]
+
+[[package]]
+name = "cty"
+version = "0.2.2"
+
+[[package]]
+name = "fatfs"
+version = "0.4.0"
+dependencies = [
+ "bitflags",
+ "log",
+]
+
+[[package]]
+name = "log"
+version = "0.4.14"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "memchr"
+version = "2.4.1"
+
+[[package]]
+name = "rust"
+version = "0.1.0"
+dependencies = [
+ "cstr_core",
+ "cty",
+ "fatfs",
+ "uuid",
+]
+
+[[package]]
+name = "uuid"
+version = "1.0.0-alpha.1"
diff --git a/tools/rust/Cargo.toml b/tools/rust/Cargo.toml
new file mode 100644
index 0000000..0bef46c
--- /dev/null
+++ b/tools/rust/Cargo.toml
@@ -0,0 +1,26 @@
+[package]
+name = "rust"
+version = "0.1.0"
+edition = "2021"
+repository = "https://github.com/AsahiLinux/m1n1"
+license = "MIT"
+publish = false
+
+[lib]
+crate-type = [ "staticlib" ]
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+[dependencies]
+fatfs = { path = "vendor/rust-fatfs", default-features = false, features = ["lfn", "alloc"] }
+cstr_core = "0.2.5"
+uuid = { version = "1.0.0-alpha.1", default-features = false }
+cty = "0.2.2"
+
+[patch.crates-io]
+uuid = { path = "vendor/uuid" }
+cty = { path = "vendor/cty" }
+cstr_core = { path = "vendor/cstr_core" }
+memchr = { path = "vendor/memchr" }
+log = { path = "vendor/log" }
+bitflags = { path = "vendor/bitflags" }
+cfg-if = { path = "vendor/cfg-if" }
diff --git a/tools/rust/src/chainload.rs b/tools/rust/src/chainload.rs
new file mode 100644
index 0000000..093158d
--- /dev/null
+++ b/tools/rust/src/chainload.rs
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: MIT
+#![deny(unsafe_op_in_unsafe_fn)]
+
+use crate::gpt;
+use crate::nvme;
+use crate::println;
+use alloc::vec::Vec;
+use core::ffi::c_void;
+use cstr_core::CStr;
+use cty::*;
+use fatfs::{FileSystem, FsOptions, Read, Seek, SeekFrom};
+use uuid::Uuid;
+
+#[derive(Debug)]
+pub enum Error {
+ FATError(fatfs::Error<nvme::Error>),
+ GPTError(gpt::Error<nvme::Error>),
+ BadArgs,
+ PartitionNotFound,
+ Unknown,
+}
+
+impl From<fatfs::Error<nvme::Error>> for Error {
+ fn from(err: fatfs::Error<nvme::Error>) -> Error {
+ Error::FATError(err)
+ }
+}
+
+impl From<gpt::Error<nvme::Error>> for Error {
+ fn from(err: gpt::Error<nvme::Error>) -> Error {
+ Error::GPTError(err)
+ }
+}
+
+fn load_image(spec: &str) -> Result<Vec<u8>, Error> {
+ println!("Chainloading {}", spec);
+
+ let mut args = spec.split(';');
+
+ let uuid = Uuid::parse_str(args.next().ok_or(Error::BadArgs)?).or(Err(Error::BadArgs))?;
+ let path = args.next().ok_or(Error::BadArgs)?;
+
+ let part = {
+ let storage = nvme::NVMEStorage::new(1, 0);
+ let mut pt = gpt::GPT::new(storage)?;
+
+ //println!("Partitions:");
+ //pt.dump();
+
+ println!("Searching for partition UUID: {}", uuid);
+ pt.find_by_partuuid(uuid)?.ok_or(Error::PartitionNotFound)?
+ };
+
+ let offset = part.get_starting_lba();
+
+ println!("Partition offset: {}", offset);
+
+ let storage = nvme::NVMEStorage::new(1, offset);
+ let opts = FsOptions::new().update_accessed_date(false);
+
+ let fs = FileSystem::new(storage, opts)?;
+ let mut file = fs.root_dir().open_file(path)?;
+
+ let size = file.seek(SeekFrom::End(0))? as usize;
+ file.seek(SeekFrom::Start(0))?;
+
+ println!("File size: {}", size);
+
+ let mut buf: Vec<u8> = vec![0; size];
+ let mut slice = &mut buf[..];
+ while !slice.is_empty() {
+ let read = file.read(slice)?;
+ slice = &mut slice[read..];
+ }
+ println!("File read successfully");
+
+ Ok(buf)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rust_load_image(
+ raw_spec: *const c_char,
+ image: *mut *mut c_void,
+ size: *mut size_t,
+) -> c_int {
+ let spec = unsafe { CStr::from_ptr(raw_spec).to_str().unwrap() };
+
+ match load_image(spec) {
+ Ok(buf) => {
+ unsafe {
+ *size = buf.len();
+ *image = buf.leak().as_mut_ptr() as *mut c_void;
+ }
+ 0
+ }
+ Err(err) => {
+ println!("Chainload failed: {:?}", err);
+ -1
+ }
+ }
+}
diff --git a/tools/rust/src/dlmalloc.rs b/tools/rust/src/dlmalloc.rs
new file mode 100644
index 0000000..e60b2d1
--- /dev/null
+++ b/tools/rust/src/dlmalloc.rs
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: MIT
+
+use core::alloc::{GlobalAlloc, Layout};
+use core::ffi::c_void;
+use core::ptr;
+use cty::*;
+
+extern "C" {
+ pub fn malloc(size: size_t) -> *mut c_void;
+ pub fn realloc_in_place(p: *mut c_void, size: size_t) -> *mut c_void;
+ pub fn free(p: *mut c_void);
+ pub fn posix_memalign(p: *mut *mut c_void, alignment: size_t, size: size_t) -> c_int;
+}
+
+pub struct DLMalloc;
+
+unsafe impl GlobalAlloc for DLMalloc {
+ #[inline]
+ unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+ let mut ptr = ptr::null_mut();
+ let ret = unsafe {
+ posix_memalign(
+ &mut ptr,
+ layout.align().max(core::mem::size_of::<usize>()),
+ layout.size(),
+ )
+ };
+ if ret == 0 {
+ ptr as *mut u8
+ } else {
+ ptr::null_mut()
+ }
+ }
+
+ #[inline]
+ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
+ // Unfortunately, calloc doesn't make any alignment guarantees, so the memory
+ // has to be manually zeroed-out.
+ let ptr = unsafe { self.alloc(layout) };
+ if !ptr.is_null() {
+ unsafe { ptr::write_bytes(ptr, 0, layout.size()) };
+ }
+ ptr
+ }
+
+ #[inline]
+ unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
+ unsafe {
+ free(ptr as *mut c_void);
+ }
+ }
+
+ #[inline]
+ unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
+ // Unfortunately, realloc doesn't make any alignment guarantees, so the memory
+ // has to be manually allocated as aligned memory if it cannot be resized
+ // in-place.
+ let mut new_ptr = unsafe { realloc_in_place(ptr as *mut c_void, new_size) as *mut u8 };
+
+ // return early if in-place resize succeeded
+ if !new_ptr.is_null() {
+ return new_ptr;
+ }
+
+ // allocate new aligned storage with correct layout
+ new_ptr =
+ unsafe { self.alloc(Layout::from_size_align_unchecked(new_size, layout.align())) };
+
+ // return early if allocation failed
+ if new_ptr.is_null() {
+ return ptr::null_mut();
+ }
+
+ // copy over the data and deallocate the old storage
+ unsafe { ptr::copy(ptr, new_ptr, layout.size().min(new_size)) };
+ unsafe { self.dealloc(ptr, layout) };
+ new_ptr
+ }
+}
diff --git a/tools/rust/src/gpt.rs b/tools/rust/src/gpt.rs
new file mode 100644
index 0000000..0c65d0b
--- /dev/null
+++ b/tools/rust/src/gpt.rs
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: MIT
+
+use crate::println;
+use core::convert::TryInto;
+use core::result::Result;
+use fatfs::{Read, Seek};
+use uuid::Uuid;
+
+const EFI_SIGNATURE: u64 = 0x5452415020494645;
+
+const SECTOR_SIZE: usize = 4096;
+
+#[derive(Debug)]
+pub enum Error<T> {
+ Io(T),
+ InvalidGPTHeader,
+}
+
+impl<T> From<T> for Error<T> {
+ fn from(err: T) -> Error<T> {
+ Error::Io(err)
+ }
+}
+
+struct TableHeader {
+ bytes: [u8; Self::SIZE],
+ my_lba: u64,
+}
+
+impl TableHeader {
+ const SIZE: usize = 0x5C;
+
+ fn read<R: Read + Seek>(rdr: &mut R, lba: u64) -> Result<Self, Error<R::Error>> {
+ let mut hdr = Self {
+ bytes: [0; Self::SIZE],
+ my_lba: lba,
+ };
+ let off = SECTOR_SIZE * (lba as usize);
+ rdr.seek(fatfs::SeekFrom::Start(off as u64))?;
+ rdr.read_exact(&mut hdr.bytes)?;
+ match hdr.is_valid() {
+ true => Ok(hdr),
+ false => Err(Error::InvalidGPTHeader),
+ }
+ }
+
+ fn get_signature(&self) -> u64 {
+ u64::from_le_bytes(self.bytes[0..8].try_into().unwrap())
+ }
+ fn get_my_lba(&self) -> u64 {
+ u64::from_le_bytes(self.bytes[24..32].try_into().unwrap())
+ }
+ fn get_partition_entry_lba(&self) -> u64 {
+ u64::from_le_bytes(self.bytes[72..80].try_into().unwrap())
+ }
+ fn get_partition_entry_count(&self) -> usize {
+ u32::from_le_bytes(self.bytes[80..84].try_into().unwrap()) as usize
+ }
+ fn get_partition_entry_size(&self) -> usize {
+ u32::from_le_bytes(self.bytes[84..88].try_into().unwrap()) as usize
+ }
+ fn is_valid(&self) -> bool {
+ self.get_signature() == EFI_SIGNATURE && self.get_my_lba() == self.my_lba
+ }
+}
+
+pub struct PartitionEntry {
+ bytes: [u8; Self::SIZE],
+}
+
+impl PartitionEntry {
+ const SIZE: usize = 0x80;
+
+ fn read<R: Read + Seek>(rdr: &mut R, off: usize) -> Result<Self, Error<R::Error>> {
+ let mut part = Self {
+ bytes: [0; Self::SIZE],
+ };
+ rdr.seek(fatfs::SeekFrom::Start(off as u64))?;
+ rdr.read_exact(&mut part.bytes)?;
+ Ok(part)
+ }
+
+ #[allow(dead_code)]
+ pub fn get_type_guid(&self) -> Uuid {
+ Uuid::from_bytes_le(self.bytes[0..16].try_into().unwrap())
+ }
+ pub fn get_partition_guid(&self) -> Uuid {
+ Uuid::from_bytes_le(self.bytes[16..32].try_into().unwrap())
+ }
+ pub fn get_starting_lba(&self) -> u64 {
+ u64::from_le_bytes(self.bytes[32..40].try_into().unwrap())
+ }
+ pub fn get_ending_lba(&self) -> u64 {
+ u64::from_le_bytes(self.bytes[40..48].try_into().unwrap())
+ }
+ pub fn get_attributes(&self) -> u64 {
+ u64::from_le_bytes(self.bytes[48..56].try_into().unwrap())
+ }
+ pub fn get_name(&self) -> &[u8] {
+ &self.bytes[56..72]
+ }
+}
+
+pub struct GPT<T: fatfs::ReadWriteSeek> {
+ disk: T,
+ hdr: TableHeader,
+}
+
+impl<IO: fatfs::ReadWriteSeek> GPT<IO> {
+ pub fn new<T: fatfs::IntoStorage<IO>>(storage: T) -> Result<Self, Error<IO::Error>> {
+ let mut disk = storage.into_storage();
+
+ let hdr = TableHeader::read(&mut disk, 1)?;
+
+ let gpt = Self { disk, hdr };
+ Ok(gpt)
+ }
+
+ pub fn count(&self) -> usize {
+ self.hdr.get_partition_entry_count()
+ }
+
+ pub fn index(&mut self, index: usize) -> Result<PartitionEntry, Error<IO::Error>> {
+ let off = (self.hdr.get_partition_entry_lba() as usize * SECTOR_SIZE)
+ + index * self.hdr.get_partition_entry_size();
+ PartitionEntry::read(&mut self.disk, off)
+ }
+
+ pub fn find_by_partuuid(
+ &mut self,
+ uuid: Uuid,
+ ) -> Result<Option<PartitionEntry>, Error<IO::Error>> {
+ for i in 0..self.count() {
+ let part = self.index(i)?;
+ if part.get_type_guid().is_nil() {
+ continue;
+ }
+ if part.get_partition_guid() == uuid {
+ return Ok(Some(part));
+ }
+ }
+ Ok(None)
+ }
+
+ pub fn dump(&mut self) {
+ for i in 0..self.count() {
+ let part = self.index(i).unwrap();
+ let guid = part.get_type_guid();
+ if guid.is_nil() {
+ continue;
+ }
+ println!(
+ "{}: {}..{} {:x} {:x}",
+ i,
+ part.get_starting_lba(),
+ part.get_ending_lba(),
+ guid,
+ part.get_partition_guid()
+ );
+ }
+ }
+}
diff --git a/tools/rust/src/lib.rs b/tools/rust/src/lib.rs
new file mode 100644
index 0000000..a578b91
--- /dev/null
+++ b/tools/rust/src/lib.rs
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: MIT
+#![no_std]
+#![deny(unsafe_op_in_unsafe_fn)]
+#![feature(alloc_error_handler)]
+#![feature(mixed_integer_ops)]
+#![feature(new_uninit)]
+
+#[macro_use]
+extern crate alloc;
+
+pub mod chainload;
+pub mod dlmalloc;
+pub mod gpt;
+pub mod nvme;
+pub mod print;
+
+use crate::dlmalloc::DLMalloc;
+
+#[global_allocator]
+static GLOBAL: DLMalloc = dlmalloc::DLMalloc;
+
+extern "C" {
+ fn flush_and_reboot();
+}
+
+#[panic_handler]
+fn panic(info: &::core::panic::PanicInfo) -> ! {
+ println!("{}", info);
+ unsafe { flush_and_reboot() };
+ loop {}
+}
+
+#[alloc_error_handler]
+fn alloc_error(layout: core::alloc::Layout) -> ! {
+ panic!("memory allocation of {} bytes failed", layout.size())
+}
diff --git a/tools/rust/src/nvme.rs b/tools/rust/src/nvme.rs
new file mode 100644
index 0000000..fdcec0e
--- /dev/null
+++ b/tools/rust/src/nvme.rs
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: MIT
+use crate::println;
+use alloc::boxed::Box;
+use core::cmp::min;
+use core::ffi::c_void;
+use fatfs::SeekFrom;
+
+extern "C" {
+ fn nvme_read(nsid: u32, lba: u64, buffer: *mut c_void) -> bool;
+}
+
+const SECTOR_SIZE: usize = 4096;
+
+pub type Error = ();
+
+#[repr(C, align(4096))]
+struct SectorBuffer([u8; SECTOR_SIZE]);
+
+fn alloc_sector_buf() -> Box<SectorBuffer> {
+ let p: Box<SectorBuffer> = unsafe { Box::new_zeroed().assume_init() };
+ debug_assert_eq!(0, p.0.as_ptr().align_offset(4096));
+ p
+}
+
+pub struct NVMEStorage {
+ nsid: u32,
+ offset: u64,
+ lba: Option<u64>,
+ buf: Box<SectorBuffer>,
+ pos: u64,
+}
+
+impl NVMEStorage {
+ pub fn new(nsid: u32, offset: u64) -> NVMEStorage {
+ NVMEStorage {
+ nsid: nsid,
+ offset: offset,
+ lba: None,
+ buf: alloc_sector_buf(),
+ pos: 0,
+ }
+ }
+}
+
+impl fatfs::IoBase for NVMEStorage {
+ type Error = Error;
+}
+
+impl fatfs::Read for NVMEStorage {
+ fn read(&mut self, mut buf: &mut [u8]) -> Result<usize, Self::Error> {
+ let mut read = 0;
+
+ while !buf.is_empty() {
+ let lba = self.pos / SECTOR_SIZE as u64;
+ let off = self.pos as usize % SECTOR_SIZE;
+
+ if Some(lba) != self.lba {
+ self.lba = Some(lba);
+ let lba = lba + self.offset;
+ if !unsafe { nvme_read(self.nsid, lba, self.buf.0.as_mut_ptr() as *mut c_void) } {
+ println!("nvme_read({}, {}) failed", self.nsid, lba);
+ return Err(());
+ }
+ }
+ let copy_len = min(SECTOR_SIZE - off, buf.len());
+ buf[..copy_len].copy_from_slice(&self.buf.0[off..off + copy_len]);
+ buf = &mut buf[copy_len..];
+ read += copy_len;
+ self.pos += copy_len as u64;
+ }
+ Ok(read)
+ }
+}
+
+impl fatfs::Write for NVMEStorage {
+ fn write(&mut self, _buf: &[u8]) -> Result<usize, Self::Error> {
+ Err(())
+ }
+ fn flush(&mut self) -> Result<(), Self::Error> {
+ Err(())
+ }
+}
+
+impl fatfs::Seek for NVMEStorage {
+ fn seek(&mut self, from: SeekFrom) -> Result<u64, Self::Error> {
+ self.pos = match from {
+ SeekFrom::Start(n) => n,
+ SeekFrom::End(_n) => panic!("SeekFrom::End not supported"),
+ SeekFrom::Current(n) => self.pos.checked_add_signed(n).ok_or(())?,
+ };
+ Ok(self.pos)
+ }
+}
diff --git a/tools/rust/src/print.rs b/tools/rust/src/print.rs
new file mode 100644
index 0000000..b9f0ead
--- /dev/null
+++ b/tools/rust/src/print.rs
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: MIT
+use core::ffi::c_void;
+
+extern "C" {
+ fn iodev_console_write(buf: *const c_void, len: u64);
+}
+
+pub struct IODevConsoleWriter;
+
+impl core::fmt::Write for IODevConsoleWriter {
+ #[inline]
+ fn write_str(&mut self, msg: &str) -> core::fmt::Result {
+ write(msg)
+ }
+}
+
+impl IODevConsoleWriter {
+ #[inline]
+ pub fn write_fmt(args: core::fmt::Arguments) -> core::fmt::Result {
+ core::fmt::Write::write_fmt(&mut Self, args)
+ }
+
+ #[inline]
+ pub fn write_str(msg: &str) -> core::fmt::Result {
+ write(msg)
+ }
+
+ #[inline]
+ pub fn write_nl() -> core::fmt::Result {
+ write("\n")
+ }
+}
+
+#[inline]
+fn write(msg: &str) -> core::fmt::Result {
+ unsafe { iodev_console_write(msg.as_ptr() as _, msg.len() as u64) };
+ Ok(())
+}
+
+#[macro_export]
+macro_rules! println {
+ () => { $crate::println!("") };
+ ($($arg:tt)*) => {
+ #[allow(unused_must_use)]
+ {
+ $crate::print::IODevConsoleWriter::write_fmt(format_args!($($arg)*));
+ $crate::print::IODevConsoleWriter::write_nl();
+ }
+ };
+}
+
+#[macro_export]
+macro_rules! print {
+ ($($arg:tt)*) => {
+ #[allow(unused_must_use)]
+ {
+ $crate::print::IODevConsoleWriter::write_fmt(format_args!($($arg)*));
+ }
+ };
+}
diff --git a/tools/src/adt.c b/tools/src/adt.c
new file mode 100644
index 0000000..4189974
--- /dev/null
+++ b/tools/src/adt.c
@@ -0,0 +1,375 @@
+/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */
+
+#include "adt.h"
+#include "string.h"
+
+/* This API is designed to match libfdt's read-only API */
+
+#define ADT_CHECK_HEADER(adt) \
+ { \
+ int err; \
+ if ((err = adt_check_header(adt)) != 0) \
+ return err; \
+ }
+
+// #define DEBUG
+
+#ifdef DEBUG
+#include "utils.h"
+#define dprintf printf
+#else
+#define dprintf(...) \
+ do { \
+ } while (0)
+#endif
+
+int _adt_check_node_offset(const void *adt, int offset)
+{
+ if ((offset < 0) || (offset % ADT_ALIGN))
+ return -ADT_ERR_BADOFFSET;
+
+ const struct adt_node_hdr *node = ADT_NODE(adt, offset);
+
+ // Sanity check
+ if (node->property_count > 2048 || !node->property_count || node->child_count > 2048)
+ return -ADT_ERR_BADOFFSET;
+
+ return 0;
+}
+
+int _adt_check_prop_offset(const void *adt, int offset)
+{
+ if ((offset < 0) || (offset % ADT_ALIGN))
+ return -ADT_ERR_BADOFFSET;
+
+ const struct adt_property *prop = ADT_PROP(adt, offset);
+
+ if (prop->size & 0x7ff00000) // up to 1MB properties
+ return -ADT_ERR_BADOFFSET;
+
+ return 0;
+}
+
+int adt_check_header(const void *adt)
+{
+ return _adt_check_node_offset(adt, 0);
+}
+
+static int _adt_string_eq(const char *a, const char *b, size_t len)
+{
+ return (strlen(a) == len) && (memcmp(a, b, len) == 0);
+}
+
+static int _adt_nodename_eq(const char *a, const char *b, size_t len)
+{
+ if (memcmp(a, b, len) != 0)
+ return 0;
+
+ if (a[len] == '\0')
+ return 1;
+ else if (!memchr(b, '@', len) && (a[len] == '@'))
+ return 1;
+ else
+ return 0;
+}
+
+const struct adt_property *adt_get_property_namelen(const void *adt, int offset, const char *name,
+ size_t namelen)
+{
+ dprintf("adt_get_property_namelen(%p, %d, \"%s\", %u)\n", adt, offset, name, namelen);
+
+ ADT_FOREACH_PROPERTY(adt, offset, prop)
+ {
+ dprintf(" off=0x%x name=\"%s\"\n", offset, prop->name);
+ if (_adt_string_eq(prop->name, name, namelen))
+ return prop;
+ }
+
+ return NULL;
+}
+
+const struct adt_property *adt_get_property(const void *adt, int nodeoffset, const char *name)
+{
+ return adt_get_property_namelen(adt, nodeoffset, name, strlen(name));
+}
+
+const void *adt_getprop_namelen(const void *adt, int nodeoffset, const char *name, size_t namelen,
+ u32 *lenp)
+{
+ const struct adt_property *prop;
+
+ prop = adt_get_property_namelen(adt, nodeoffset, name, namelen);
+
+ if (!prop)
+ return NULL;
+
+ if (lenp)
+ *lenp = prop->size;
+
+ return prop->value;
+}
+
+const void *adt_getprop_by_offset(const void *adt, int offset, const char **namep, u32 *lenp)
+{
+ const struct adt_property *prop;
+
+ prop = adt_get_property_by_offset(adt, offset);
+ if (!prop)
+ return NULL;
+
+ if (namep)
+ *namep = prop->name;
+ if (lenp)
+ *lenp = prop->size;
+ return prop->value;
+}
+
+const void *adt_getprop(const void *adt, int nodeoffset, const char *name, u32 *lenp)
+{
+ return adt_getprop_namelen(adt, nodeoffset, name, strlen(name), lenp);
+}
+
+int adt_setprop(void *adt, int nodeoffset, const char *name, void *value, size_t len)
+{
+ u32 plen;
+ void *prop = (void *)adt_getprop(adt, nodeoffset, name, &plen);
+ if (!prop)
+ return -ADT_ERR_NOTFOUND;
+
+ if (len != plen)
+ return -ADT_ERR_BADLENGTH;
+
+ memcpy(prop, value, len);
+ return len;
+}
+
+int adt_getprop_copy(const void *adt, int nodeoffset, const char *name, void *out, size_t len)
+{
+ u32 plen;
+
+ const void *p = adt_getprop(adt, nodeoffset, name, &plen);
+
+ if (!p)
+ return -ADT_ERR_NOTFOUND;
+
+ if (plen != len)
+ return -ADT_ERR_BADLENGTH;
+
+ memcpy(out, p, len);
+ return len;
+}
+
+int adt_first_child_offset(const void *adt, int offset)
+{
+ const struct adt_node_hdr *node = ADT_NODE(adt, offset);
+
+ u32 cnt = node->property_count;
+ offset = adt_first_property_offset(adt, offset);
+
+ while (cnt--) {
+ offset = adt_next_property_offset(adt, offset);
+ }
+
+ return offset;
+}
+
+int adt_next_sibling_offset(const void *adt, int offset)
+{
+ const struct adt_node_hdr *node = ADT_NODE(adt, offset);
+
+ u32 cnt = node->child_count;
+ offset = adt_first_child_offset(adt, offset);
+
+ while (cnt--) {
+ offset = adt_next_sibling_offset(adt, offset);
+ }
+
+ return offset;
+}
+
+int adt_subnode_offset_namelen(const void *adt, int offset, const char *name, size_t namelen)
+{
+ ADT_CHECK_HEADER(adt);
+
+ ADT_FOREACH_CHILD(adt, offset)
+ {
+ const char *cname = adt_get_name(adt, offset);
+
+ if (_adt_nodename_eq(cname, name, namelen))
+ return offset;
+ }
+
+ return -ADT_ERR_NOTFOUND;
+}
+
+int adt_subnode_offset(const void *adt, int parentoffset, const char *name)
+{
+ return adt_subnode_offset_namelen(adt, parentoffset, name, strlen(name));
+}
+
+int adt_path_offset(const void *adt, const char *path)
+{
+ return adt_path_offset_trace(adt, path, NULL);
+}
+
+int adt_path_offset_trace(const void *adt, const char *path, int *offsets)
+{
+ const char *end = path + strlen(path);
+ const char *p = path;
+ int offset = 0;
+
+ ADT_CHECK_HEADER(adt);
+
+ while (*p) {
+ const char *q;
+
+ while (*p == '/')
+ p++;
+ if (!*p)
+ break;
+ q = strchr(p, '/');
+ if (!q)
+ q = end;
+
+ offset = adt_subnode_offset_namelen(adt, offset, p, q - p);
+ if (offset < 0)
+ break;
+
+ if (offsets)
+ *offsets++ = offset;
+
+ p = q;
+ }
+
+ if (offsets)
+ *offsets++ = 0;
+
+ return offset;
+}
+
+const char *adt_get_name(const void *adt, int nodeoffset)
+{
+ return adt_getprop(adt, nodeoffset, "name", NULL);
+}
+
+static void get_cells(u64 *dst, const u32 **src, int cells)
+{
+ *dst = 0;
+ for (int i = 0; i < cells; i++)
+ *dst |= ((u64) * ((*src)++)) << (32 * i);
+}
+
+int adt_get_reg(const void *adt, int *path, const char *prop, int idx, u64 *paddr, u64 *psize)
+{
+ int cur = 0;
+
+ if (!*path)
+ return -ADT_ERR_BADOFFSET;
+
+ while (path[cur + 1])
+ cur++;
+
+ int node = path[cur];
+ int parent = cur > 0 ? path[cur - 1] : 0;
+ u32 a_cells = 2, s_cells = 1;
+
+ ADT_GETPROP(adt, parent, "#address-cells", &a_cells);
+ ADT_GETPROP(adt, parent, "#size-cells", &s_cells);
+
+ dprintf("adt_get_reg: node '%s' @ %d, parent @ %d, address-cells=%d size-cells=%d idx=%d\n",
+ adt_get_name(adt, node), node, parent, a_cells, s_cells, idx);
+
+ if (a_cells < 1 || a_cells > 2 || s_cells > 2) {
+ dprintf("bad n-cells\n");
+ return ADT_ERR_BADNCELLS;
+ }
+
+ u32 reg_len = 0;
+ const u32 *reg = adt_getprop(adt, node, prop, &reg_len);
+
+ if (!reg || !reg_len) {
+ dprintf("reg not found or empty\n");
+ return -ADT_ERR_NOTFOUND;
+ }
+
+ if (reg_len < (idx + 1) * (a_cells + s_cells) * 4) {
+ dprintf("bad reg property length %d\n", reg_len);
+ return -ADT_ERR_BADVALUE;
+ }
+
+ reg += idx * (a_cells + s_cells);
+
+ u64 addr, size = 0;
+ get_cells(&addr, &reg, a_cells);
+ get_cells(&size, &reg, s_cells);
+
+ dprintf(" addr=0x%lx size=0x%lx\n", addr, size);
+
+ while (parent) {
+ cur--;
+ node = parent;
+ parent = cur > 0 ? path[cur - 1] : 0;
+
+ dprintf(" walking up to %s\n", adt_get_name(adt, node));
+
+ u32 ranges_len;
+ const u32 *ranges = adt_getprop(adt, node, "ranges", &ranges_len);
+ if (!ranges)
+ break;
+
+ u32 pa_cells = 2, ps_cells = 1;
+ ADT_GETPROP(adt, parent, "#address-cells", &pa_cells);
+ ADT_GETPROP(adt, parent, "#size-cells", &ps_cells);
+
+ dprintf(" translate range to address-cells=%d size-cells=%d\n", pa_cells, ps_cells);
+
+ if (pa_cells < 1 || pa_cells > 2 || ps_cells > 2)
+ return ADT_ERR_BADNCELLS;
+
+ int range_cnt = ranges_len / (4 * (pa_cells + a_cells + s_cells));
+
+ while (range_cnt--) {
+ u64 c_addr, p_addr, c_size;
+ get_cells(&c_addr, &ranges, a_cells);
+ get_cells(&p_addr, &ranges, pa_cells);
+ get_cells(&c_size, &ranges, s_cells);
+
+ dprintf(" ranges %lx %lx %lx\n", c_addr, p_addr, c_size);
+
+ if (addr >= c_addr && (addr + size) <= (c_addr + c_size)) {
+ dprintf(" translate %lx", addr);
+ addr = addr - c_addr + p_addr;
+ dprintf(" -> %lx\n", addr);
+ break;
+ }
+ }
+
+ a_cells = pa_cells;
+ s_cells = ps_cells;
+ }
+
+ if (paddr)
+ *paddr = addr;
+ if (psize)
+ *psize = size;
+
+ return 0;
+}
+
+bool adt_is_compatible(const void *adt, int nodeoffset, const char *compat)
+{
+ u32 len;
+ const char *list = adt_getprop(adt, nodeoffset, "compatible", &len);
+ if (!list)
+ return false;
+
+ const char *end = list + len;
+
+ while (list != end) {
+ if (!strcmp(list, compat))
+ return true;
+ list += strlen(list) + 1;
+ }
+
+ return false;
+}
diff --git a/tools/src/adt.h b/tools/src/adt.h
new file mode 100644
index 0000000..6373c86
--- /dev/null
+++ b/tools/src/adt.h
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */
+
+#ifndef XDT_H
+#define XDT_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "types.h"
+
+#define ADT_ERR_NOTFOUND 1
+#define ADT_ERR_BADOFFSET 4
+#define ADT_ERR_BADPATH 5
+#define ADT_ERR_BADNCELLS 14
+#define ADT_ERR_BADVALUE 15
+#define ADT_ERR_BADLENGTH 20
+
+#define ADT_ALIGN 4
+
+extern void *adt;
+
+struct adt_property {
+ char name[32];
+ u32 size;
+ u8 value[];
+};
+
+struct adt_node_hdr {
+ u32 property_count;
+ u32 child_count;
+};
+
+#define ADT_NODE(adt, offset) ((const struct adt_node_hdr *)(((u8 *)(adt)) + (offset)))
+#define ADT_PROP(adt, offset) ((const struct adt_property *)(((u8 *)(adt)) + (offset)))
+#define ADT_SIZE(node) ((node)->size & 0x7fffffff)
+
+/* This API is designed to match libfdt's read-only API */
+
+/* Basic sanity check */
+int adt_check_header(const void *adt);
+
+static inline int adt_get_property_count(const void *adt, int offset)
+{
+ return ADT_NODE(adt, offset)->property_count;
+}
+
+static inline int adt_first_property_offset(const void *adt, int offset)
+{
+ UNUSED(adt);
+ return offset + sizeof(struct adt_node_hdr);
+}
+
+static inline int adt_next_property_offset(const void *adt, int offset)
+{
+ const struct adt_property *prop = ADT_PROP(adt, offset);
+ return offset + sizeof(struct adt_property) + ((prop->size + ADT_ALIGN - 1) & ~(ADT_ALIGN - 1));
+}
+
+static inline const struct adt_property *adt_get_property_by_offset(const void *adt, int offset)
+{
+ return ADT_PROP(adt, offset);
+}
+
+static inline int adt_get_child_count(const void *adt, int offset)
+{
+ return ADT_NODE(adt, offset)->child_count;
+}
+
+int adt_first_child_offset(const void *adt, int offset);
+int adt_next_sibling_offset(const void *adt, int offset);
+
+int adt_subnode_offset_namelen(const void *adt, int parentoffset, const char *name, size_t namelen);
+int adt_subnode_offset(const void *adt, int parentoffset, const char *name);
+int adt_path_offset(const void *adt, const char *path);
+int adt_path_offset_trace(const void *adt, const char *path, int *offsets);
+
+const char *adt_get_name(const void *adt, int nodeoffset);
+const struct adt_property *adt_get_property_namelen(const void *adt, int nodeoffset,
+ const char *name, size_t namelen);
+const struct adt_property *adt_get_property(const void *adt, int nodeoffset, const char *name);
+const void *adt_getprop_by_offset(const void *adt, int offset, const char **namep, u32 *lenp);
+const void *adt_getprop_namelen(const void *adt, int nodeoffset, const char *name, size_t namelen,
+ u32 *lenp);
+const void *adt_getprop(const void *adt, int nodeoffset, const char *name, u32 *lenp);
+int adt_setprop(void *adt, int nodeoffset, const char *name, void *value, size_t len);
+int adt_getprop_copy(const void *adt, int nodeoffset, const char *name, void *out, size_t len);
+
+#define ADT_GETPROP(adt, nodeoffset, name, val) \
+ adt_getprop_copy(adt, nodeoffset, name, (val), sizeof(*(val)))
+
+#define ADT_GETPROP_ARRAY(adt, nodeoffset, name, arr) \
+ adt_getprop_copy(adt, nodeoffset, name, (arr), sizeof(arr))
+
+int adt_get_reg(const void *adt, int *path, const char *prop, int idx, u64 *addr, u64 *size);
+bool adt_is_compatible(const void *adt, int nodeoffset, const char *compat);
+
+#define ADT_FOREACH_CHILD(adt, node) \
+ for (int _child_count = adt_get_child_count(adt, node); _child_count; _child_count = 0) \
+ 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)))
+
+#endif
diff --git a/tools/src/afk.c b/tools/src/afk.c
new file mode 100644
index 0000000..7191a21
--- /dev/null
+++ b/tools/src/afk.c
@@ -0,0 +1,545 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "afk.h"
+#include "assert.h"
+#include "malloc.h"
+#include "string.h"
+#include "utils.h"
+
+struct afk_rb_hdr {
+ u32 bufsz;
+ u32 unk;
+ u32 _pad1[14];
+ u32 rptr;
+ u32 _pad2[15];
+ u32 wptr;
+ u32 _pad3[15];
+};
+
+struct afk_rb {
+ bool ready;
+ struct afk_rb_hdr *hdr;
+ u32 rptr;
+ void *buf;
+ size_t bufsz;
+};
+
+enum EPICType {
+ TYPE_NOTIFY = 0,
+ TYPE_COMMAND = 3,
+ TYPE_REPLY = 4,
+ TYPE_NOTIFY_ACK = 8,
+};
+
+enum EPICCategory {
+ CAT_REPORT = 0x00,
+ CAT_NOTIFY = 0x10,
+ CAT_REPLY = 0x20,
+ CAT_COMMAND = 0x30,
+};
+
+enum EPICMessage {
+ CODE_ANNOUNCE = 0x30,
+};
+
+struct afk_qe {
+ u32 magic;
+ u32 size;
+ u32 channel;
+ u32 type;
+ u8 data[];
+};
+
+struct epic_hdr {
+ u8 version;
+ u16 seq;
+ u8 _pad;
+ u32 unk;
+ u64 timestamp;
+} PACKED;
+
+struct epic_sub_hdr {
+ u32 length;
+ u8 version;
+ u8 category;
+ u16 code;
+ u64 timestamp;
+ u16 seq;
+ u16 unk;
+ u32 unk2;
+} PACKED;
+
+struct epic_announce {
+ char name[32];
+ u8 props[];
+} PACKED;
+
+struct epic_cmd {
+ u32 retcode;
+ u64 rxbuf;
+ u64 txbuf;
+ u32 rxlen;
+ u32 txlen;
+} PACKED;
+
+struct afk_epic_ep {
+ int ep;
+ rtkit_dev_t *rtk;
+
+ struct rtkit_buffer buf;
+ u16 tag;
+
+ struct afk_rb tx;
+ struct afk_rb rx;
+
+ struct rtkit_buffer txbuf;
+ struct rtkit_buffer rxbuf;
+
+ bool started;
+};
+
+enum RBEP_MSG {
+ RBEP_INIT = 0x80,
+ RBEP_INIT_ACK = 0xa0,
+ RBEP_GETBUF = 0x89,
+ RBEP_GETBUF_ACK = 0xa1,
+ RBEP_INIT_TX = 0x8a,
+ RBEP_INIT_RX = 0x8b,
+ RBEP_START = 0xa3,
+ RBEP_START_ACK = 0x86,
+ RBEP_SEND = 0xa2,
+ RBEP_RECV = 0x85,
+ RBEP_SHUTDOWN = 0xc0,
+ RBEP_SHUTDOWN_ACK = 0xc1,
+};
+
+#define BLOCK_SHIFT 6
+#define QE_MAGIC ' POI'
+
+#define RBEP_TYPE GENMASK(63, 48)
+
+#define GETBUF_SIZE GENMASK(31, 16)
+#define GETBUF_TAG GENMASK(15, 0)
+#define GETBUF_ACK_DVA GENMASK(47, 0)
+
+#define INITRB_OFFSET GENMASK(47, 32)
+#define INITRB_SIZE GENMASK(31, 16)
+#define INITRB_TAG GENMASK(15, 0)
+
+#define SEND_WPTR GENMASK(31, 0)
+
+bool afk_rb_init(afk_epic_ep_t *epic, struct afk_rb *rb, u64 base, u64 size)
+{
+ rb->hdr = epic->buf.bfr + base;
+
+ if (rb->hdr->bufsz + sizeof(*rb->hdr) != size) {
+ printf("AFK: ring buffer size mismatch\n");
+ return false;
+ }
+
+ rb->buf = rb->hdr + 1;
+ rb->bufsz = rb->hdr->bufsz;
+ rb->ready = true;
+
+ return true;
+}
+
+static int afk_epic_poll(afk_epic_ep_t *epic)
+{
+ int ret;
+ struct rtkit_message msg;
+
+ while ((ret = rtkit_recv(epic->rtk, &msg)) == 0)
+ ;
+
+ if (ret < 0) {
+ printf("EPIC: rtkit_recv failed!\n");
+ return ret;
+ }
+
+ if (msg.ep != epic->ep) {
+ printf("EPIC: received message for unexpected endpoint %d\n", msg.ep);
+ return 0;
+ }
+
+ int type = FIELD_GET(RBEP_TYPE, msg.msg);
+ u64 base, size, tag;
+ switch (type) {
+ case RBEP_INIT_ACK:
+ break;
+
+ case RBEP_GETBUF:
+ size = FIELD_GET(GETBUF_SIZE, msg.msg) << BLOCK_SHIFT;
+ epic->tag = FIELD_GET(GETBUF_TAG, msg.msg);
+ if (!rtkit_alloc_buffer(epic->rtk, &epic->buf, size)) {
+ printf("EPIC: failed to allocate buffer\n");
+ return -1;
+ }
+ msg.msg = (FIELD_PREP(RBEP_TYPE, RBEP_GETBUF_ACK) |
+ FIELD_PREP(GETBUF_ACK_DVA, epic->buf.dva));
+ if (!rtkit_send(epic->rtk, &msg)) {
+ printf("EPIC: failed to send buffer address\n");
+ return -1;
+ }
+ break;
+
+ case RBEP_INIT_TX:
+ case RBEP_INIT_RX:
+ base = FIELD_GET(INITRB_OFFSET, msg.msg) << BLOCK_SHIFT;
+ size = FIELD_GET(INITRB_SIZE, msg.msg) << BLOCK_SHIFT;
+ tag = FIELD_GET(INITRB_TAG, msg.msg);
+ if (tag != epic->tag) {
+ printf("EPIC: wrong tag (0x%x != 0x%lx)\n", epic->tag, tag);
+ return -1;
+ }
+
+ struct afk_rb *rb;
+ if (type == RBEP_INIT_RX)
+ rb = &epic->rx;
+ else
+ rb = &epic->tx;
+
+ if (!afk_rb_init(epic, rb, base, size))
+ return -1;
+
+ if (epic->rx.ready && epic->tx.ready) {
+ msg.msg = FIELD_PREP(RBEP_TYPE, RBEP_START);
+ if (!rtkit_send(epic->rtk, &msg)) {
+ printf("EPIC: failed to send start\n");
+ return -1;
+ }
+ }
+ break;
+
+ case RBEP_RECV:
+ return 1;
+
+ case RBEP_START_ACK:
+ epic->started = true;
+ break;
+
+ case RBEP_SHUTDOWN_ACK:
+ epic->started = false;
+ break;
+
+ default:
+ printf("EPIC: received unknown message type 0x%x\n", type);
+ return 0;
+ break;
+ }
+
+ return 0;
+}
+
+static int afk_epic_rx(afk_epic_ep_t *epic, struct afk_qe **qe)
+{
+ int ret;
+ struct afk_rb *rb = &epic->rx;
+
+ u32 rptr = rb->hdr->rptr;
+
+ while (rptr == rb->hdr->wptr) {
+ do {
+ ret = afk_epic_poll(epic);
+ if (ret < 0)
+ return ret;
+ } while (ret == 0);
+ dma_rmb();
+ }
+
+ struct afk_qe *hdr = rb->buf + rptr;
+
+ if (hdr->magic != QE_MAGIC) {
+ printf("EPIC: bad queue entry magic!\n");
+ return -1;
+ }
+
+ if (rptr + hdr->size > rb->bufsz) {
+ rptr = 0;
+ hdr = rb->buf + rptr;
+ if (hdr->magic != QE_MAGIC) {
+ printf("EPIC: bad queue entry magic!\n");
+ return -1;
+ }
+ rb->hdr->rptr = rptr;
+ }
+
+ *qe = hdr;
+
+ return 1;
+}
+
+static int afk_epic_tx(afk_epic_ep_t *epic, u32 channel, u32 type, void *data, size_t size)
+{
+ struct afk_rb *rb = &epic->tx;
+
+ u32 rptr = rb->hdr->rptr;
+ u32 wptr = rb->hdr->wptr;
+ struct afk_qe *hdr = rb->buf + wptr;
+
+ if (wptr < rptr && (wptr + sizeof(struct afk_qe) > rptr)) {
+ printf("EPIC: TX ring buffer is full\n");
+ return -1;
+ }
+
+ hdr->magic = QE_MAGIC;
+ hdr->channel = channel;
+ hdr->type = type;
+ hdr->size = size;
+
+ wptr += sizeof(struct afk_qe);
+
+ if (size > rb->bufsz - wptr) {
+ if (rptr < sizeof(struct afk_qe)) {
+ printf("EPIC: TX ring buffer is full\n");
+ return -1;
+ }
+ *(struct afk_qe *)rb->buf = *hdr;
+ hdr = rb->buf;
+ wptr = sizeof(struct afk_qe);
+ }
+
+ if (wptr < rptr && (wptr + size > rptr)) {
+ printf("EPIC: TX ring buffer is full\n");
+ return -1;
+ }
+
+ wptr += size;
+ wptr = ALIGN_UP(wptr, 1 << BLOCK_SHIFT);
+
+ memcpy(hdr + 1, data, size);
+
+ dma_mb();
+ rb->hdr->wptr = wptr;
+ dma_wmb();
+
+ struct rtkit_message msg = {
+ epic->ep,
+ FIELD_PREP(RBEP_TYPE, RBEP_SEND) | FIELD_PREP(SEND_WPTR, wptr),
+ };
+
+ if (!rtkit_send(epic->rtk, &msg)) {
+ printf("EPIC: failed to send TX WPTR message\n");
+ return -1;
+ }
+
+ return 1;
+}
+
+static void afk_epic_rx_ack(afk_epic_ep_t *epic)
+{
+ struct afk_rb *rb = &epic->rx;
+ u32 rptr = rb->hdr->rptr;
+ struct afk_qe *hdr = rb->buf + rptr;
+
+ if (hdr->magic != QE_MAGIC) {
+ printf("EPIC: bad queue entry magic!\n");
+ }
+
+ dma_mb();
+
+ rptr = ALIGN_UP(rptr + sizeof(*hdr) + hdr->size, 1 << BLOCK_SHIFT);
+ assert(rptr < rb->bufsz);
+ if (rptr == rb->bufsz)
+ rptr = 0;
+ rb->hdr->rptr = rptr;
+}
+
+int afk_epic_command(afk_epic_ep_t *epic, int channel, u16 code, void *txbuf, size_t txsize,
+ void *rxbuf, size_t *rxsize)
+{
+ struct {
+ struct epic_hdr hdr;
+ struct epic_sub_hdr sub;
+ struct epic_cmd cmd;
+ } PACKED msg;
+
+ assert(txsize <= epic->txbuf.sz);
+ assert(!rxsize || *rxsize <= epic->rxbuf.sz);
+
+ memset(&msg, 0, sizeof(msg));
+
+ msg.hdr.version = 2;
+ msg.hdr.seq = 0;
+ msg.sub.length = sizeof(msg.cmd);
+ msg.sub.version = 3;
+ msg.sub.category = CAT_COMMAND;
+ msg.sub.code = code;
+ msg.sub.seq = 0;
+ msg.cmd.txbuf = epic->txbuf.dva;
+ msg.cmd.txlen = txsize;
+ msg.cmd.rxbuf = epic->rxbuf.dva;
+ msg.cmd.rxlen = rxsize ? *rxsize : 0;
+
+ memcpy(epic->txbuf.bfr, txbuf, txsize);
+
+ int ret = afk_epic_tx(epic, channel, TYPE_COMMAND, &msg, sizeof msg);
+ if (ret < 0) {
+ printf("EPIC: failed to transmit command\n");
+ return ret;
+ }
+
+ struct afk_qe *rmsg;
+ struct epic_cmd *rcmd;
+
+ while (true) {
+ ret = afk_epic_rx(epic, &rmsg);
+ if (ret < 0)
+ return ret;
+
+ if (rmsg->type != TYPE_REPLY && rmsg->type != TYPE_NOTIFY) {
+ printf("EPIC: got unexpected message type %d during command\n", rmsg->type);
+ afk_epic_rx_ack(epic);
+ continue;
+ }
+
+ struct epic_hdr *hdr = (void *)(rmsg + 1);
+ struct epic_sub_hdr *sub = (void *)(hdr + 1);
+
+ if (sub->category != CAT_REPLY || sub->code != code) {
+ printf("EPIC: got unexpected message %02x:%04x during command\n", sub->category,
+ sub->code);
+ afk_epic_rx_ack(epic);
+ continue;
+ }
+
+ rcmd = (void *)(sub + 1);
+ break;
+ }
+
+ if (rcmd->retcode != 0) {
+ printf("EPIC: IOP returned 0x%x\n", rcmd->retcode);
+ afk_epic_rx_ack(epic);
+ return rcmd->retcode; // should be negative already
+ }
+
+ assert(*rxsize >= rcmd->rxlen);
+ *rxsize = rcmd->rxlen;
+
+ if (rxsize && *rxsize && rcmd->rxbuf)
+ memcpy(rxbuf, epic->rxbuf.bfr, *rxsize);
+
+ afk_epic_rx_ack(epic);
+
+ return 0;
+}
+
+afk_epic_ep_t *afk_epic_init(rtkit_dev_t *rtk, int endpoint)
+{
+ afk_epic_ep_t *epic = malloc(sizeof(afk_epic_ep_t));
+ if (!epic)
+ return NULL;
+
+ memset(epic, 0, sizeof(*epic));
+ epic->ep = endpoint;
+ epic->rtk = rtk;
+
+ if (!rtkit_start_ep(rtk, endpoint)) {
+ printf("EPIC: failed to start endpoint %d\n", endpoint);
+ goto err;
+ }
+
+ struct rtkit_message msg = {endpoint, FIELD_PREP(RBEP_TYPE, RBEP_INIT)};
+ if (!rtkit_send(rtk, &msg)) {
+ printf("EPIC: failed to send init message\n");
+ goto err;
+ }
+
+ while (!epic->started) {
+ int ret = afk_epic_poll(epic);
+ if (ret < 0)
+ break;
+ else if (ret > 0)
+ printf("EPIC: received unexpected message during init\n");
+ }
+
+ return epic;
+
+err:
+ free(epic);
+ return NULL;
+}
+
+int afk_epic_shutdown(afk_epic_ep_t *epic)
+{
+ struct rtkit_message msg = {epic->ep, FIELD_PREP(RBEP_TYPE, RBEP_SHUTDOWN)};
+ if (!rtkit_send(epic->rtk, &msg)) {
+ printf("EPIC: failed to send shutdown message\n");
+ return -1;
+ }
+
+ while (epic->started) {
+ int ret = afk_epic_poll(epic);
+ if (ret < 0)
+ break;
+ }
+
+ rtkit_free_buffer(epic->rtk, &epic->buf);
+ rtkit_free_buffer(epic->rtk, &epic->rxbuf);
+ rtkit_free_buffer(epic->rtk, &epic->txbuf);
+
+ free(epic);
+ return 0;
+}
+
+int afk_epic_start_interface(afk_epic_ep_t *epic, char *name, size_t txsize, size_t rxsize)
+{
+ int channel = -1;
+ struct afk_qe *msg;
+ struct epic_announce *announce;
+
+ for (int tries = 0; tries < 20; tries += 1) {
+
+ int ret = afk_epic_rx(epic, &msg);
+ if (ret < 0)
+ return ret;
+
+ if (msg->type != TYPE_NOTIFY) {
+ printf("EPIC: got unexpected message type %d during iface start\n", msg->type);
+ afk_epic_rx_ack(epic);
+ continue;
+ }
+
+ struct epic_hdr *hdr = (void *)(msg + 1);
+ struct epic_sub_hdr *sub = (void *)(hdr + 1);
+
+ if (sub->category != CAT_REPORT || sub->code != CODE_ANNOUNCE) {
+ printf("EPIC: got unexpected message %02x:%04x during iface start\n", sub->category,
+ sub->code);
+ afk_epic_rx_ack(epic);
+ continue;
+ }
+
+ announce = (void *)(sub + 1);
+
+ if (strncmp(name, announce->name, sizeof(announce->name))) {
+ printf("EPIC: ignoring channel %d: %s\n", msg->channel, announce->name);
+ afk_epic_rx_ack(epic);
+ continue;
+ }
+
+ channel = msg->channel;
+ break;
+ }
+
+ if (channel == -1) {
+ printf("EPIC: too many unexpected messages, giving up\n");
+ return -1;
+ }
+
+ if (!rtkit_alloc_buffer(epic->rtk, &epic->rxbuf, rxsize)) {
+ printf("EPIC: failed to allocate rx buffer\n");
+ return -1;
+ }
+
+ if (!rtkit_alloc_buffer(epic->rtk, &epic->txbuf, txsize)) {
+ printf("EPIC: failed to allocate tx buffer\n");
+ return -1;
+ }
+
+ printf("EPIC: started interface %d (%s)\n", msg->channel, announce->name);
+
+ afk_epic_rx_ack(epic);
+
+ return channel;
+}
diff --git a/tools/src/afk.h b/tools/src/afk.h
new file mode 100644
index 0000000..e76ade3
--- /dev/null
+++ b/tools/src/afk.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef DCP_AFK_H
+#define DCP_AFK_H
+
+#include "rtkit.h"
+
+typedef struct afk_epic_ep afk_epic_ep_t;
+
+afk_epic_ep_t *afk_epic_init(rtkit_dev_t *rtkit, int endpoint);
+int afk_epic_shutdown(afk_epic_ep_t *epic);
+
+int afk_epic_start_interface(afk_epic_ep_t *epic, char *name, size_t insize, size_t outsize);
+int afk_epic_command(afk_epic_ep_t *epic, int channel, u16 code, void *txbuf, size_t txsize,
+ void *rxbuf, size_t *rxsize);
+
+#endif
diff --git a/tools/src/aic.c b/tools/src/aic.c
new file mode 100644
index 0000000..6974aac
--- /dev/null
+++ b/tools/src/aic.c
@@ -0,0 +1,153 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "aic.h"
+#include "adt.h"
+#include "aic_regs.h"
+#include "assert.h"
+#include "utils.h"
+
+#define MASK_REG(x) (4 * ((x) >> 5))
+#define MASK_BIT(x) BIT((x)&GENMASK(4, 0))
+
+static struct aic aic1 = {
+ .version = 1,
+ .nr_die = 1,
+ .max_die = 1,
+ .regs =
+ {
+ .reg_size = AIC_REG_SIZE,
+ .event = AIC_EVENT,
+ .tgt_cpu = AIC_TARGET_CPU,
+ .sw_set = AIC_SW_SET,
+ .sw_clr = AIC_SW_CLR,
+ .mask_set = AIC_MASK_SET,
+ .mask_clr = AIC_MASK_CLR,
+ },
+};
+
+static struct aic aic2 = {
+ .version = 2,
+ .regs =
+ {
+ .config = AIC2_IRQ_CFG,
+ },
+};
+
+struct aic *aic;
+
+static int aic2_init(int node)
+{
+ int ret = ADT_GETPROP(adt, node, "aic-iack-offset", &aic->regs.event);
+ if (ret < 0) {
+ printf("AIC: failed to get property aic-iack-offset\n");
+ return ret;
+ }
+
+ u32 info1 = read32(aic->base + AIC2_INFO1);
+ aic->nr_die = FIELD_GET(AIC2_INFO1_LAST_DIE, info1) + 1;
+ aic->nr_irq = FIELD_GET(AIC2_INFO1_NR_IRQ, info1);
+
+ u32 info3 = read32(aic->base + AIC2_INFO3);
+ aic->max_die = FIELD_GET(AIC2_INFO3_MAX_DIE, info3);
+ aic->max_irq = FIELD_GET(AIC2_INFO3_MAX_IRQ, info3);
+
+ if (aic->nr_die > AIC_MAX_DIES) {
+ printf("AIC: more dies than supported: %u\n", aic->max_die);
+ return -1;
+ }
+
+ if (aic->max_irq > AIC_MAX_HW_NUM) {
+ printf("AIC: more IRQs than supported: %u\n", aic->max_irq);
+ return -1;
+ }
+
+ const u64 start_off = aic->regs.config;
+ u64 off = start_off + sizeof(u32) * aic->max_irq; /* IRQ_CFG */
+
+ aic->regs.sw_set = off;
+ off += sizeof(u32) * (aic->max_irq >> 5); /* SW_SET */
+ aic->regs.sw_clr = off;
+ off += sizeof(u32) * (aic->max_irq >> 5); /* SW_CLR */
+ aic->regs.mask_set = off;
+ off += sizeof(u32) * (aic->max_irq >> 5); /* MASK_SET */
+ aic->regs.mask_clr = off;
+ off += sizeof(u32) * (aic->max_irq >> 5); /* MASK_CLR */
+ off += sizeof(u32) * (aic->max_irq >> 5); /* HW_STATE */
+
+ aic->die_stride = off - start_off;
+ aic->regs.reg_size = aic->regs.event + 4;
+
+ printf("AIC: AIC2 with %u/%u dies, %u/%u IRQs, reg_size:%05lx die_stride:%05x\n", aic->nr_die,
+ aic->max_die, aic->nr_irq, aic->max_irq, aic->regs.reg_size, aic->die_stride);
+
+ u32 ext_intr_config_len;
+ const u8 *ext_intr_config = adt_getprop(adt, node, "aic-ext-intr-cfg", &ext_intr_config_len);
+
+ if (ext_intr_config) {
+ printf("AIC: Configuring %d external interrupts\n", ext_intr_config_len / 3);
+ for (u32 i = 0; i < ext_intr_config_len; i += 3) {
+ u8 die = ext_intr_config[i + 1] >> 4;
+ u16 irq = ext_intr_config[i] | ((ext_intr_config[i + 1] & 0xf) << 8);
+ u8 target = ext_intr_config[i + 2];
+ assert(die < aic->nr_die);
+ assert(irq < aic->nr_irq);
+ mask32(aic->base + aic->regs.config + die * aic->die_stride + 4 * irq,
+ AIC2_IRQ_CFG_TARGET, FIELD_PREP(AIC2_IRQ_CFG_TARGET, target));
+ }
+ }
+
+ return 0;
+}
+
+void aic_init(void)
+{
+ int path[8];
+ int node = adt_path_offset_trace(adt, "/arm-io/aic", path);
+
+ if (node < 0) {
+ printf("AIC node not found!\n");
+ return;
+ }
+
+ if (adt_is_compatible(adt, node, "aic,1")) {
+ aic = &aic1;
+ } else if (adt_is_compatible(adt, node, "aic,2")) {
+ aic = &aic2;
+ } else {
+ printf("AIC: Error: Unsupported version\n");
+ return;
+ }
+
+ if (adt_get_reg(adt, path, "reg", 0, &aic->base, NULL)) {
+ printf("Failed to get AIC reg property!\n");
+ return;
+ }
+
+ if (aic->version == 1) {
+ printf("AIC: Version 1 @ 0x%lx\n", aic->base);
+ aic->nr_irq = FIELD_GET(AIC_INFO_NR_HW, read32(aic->base + AIC_INFO));
+ aic->max_irq = AIC1_MAX_IRQ;
+ } else if (aic->version == 2) {
+ printf("AIC: Version 2 @ 0x%lx\n", aic->base);
+ int ret = aic2_init(node);
+ if (ret < 0)
+ aic = NULL;
+ }
+}
+
+void aic_set_sw(int irq, bool active)
+{
+ u32 die = irq / aic->max_irq;
+ irq = irq % aic->max_irq;
+ if (active)
+ write32(aic->base + aic->regs.sw_set + die * aic->die_stride + MASK_REG(irq),
+ MASK_BIT(irq));
+ else
+ write32(aic->base + aic->regs.sw_clr + die * aic->die_stride + MASK_REG(irq),
+ MASK_BIT(irq));
+}
+
+uint32_t aic_ack(void)
+{
+ return read32(aic->base + aic->regs.event);
+}
diff --git a/tools/src/aic.h b/tools/src/aic.h
new file mode 100644
index 0000000..1f401b1
--- /dev/null
+++ b/tools/src/aic.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef AIC_H
+#define AIC_H
+
+#include "types.h"
+
+#define AIC_MAX_DIES 4
+
+struct aic_regs {
+ uint64_t reg_size;
+ uint64_t event;
+ uint64_t tgt_cpu;
+ uint64_t config;
+ uint64_t sw_set;
+ uint64_t sw_clr;
+ uint64_t mask_set;
+ uint64_t mask_clr;
+};
+
+struct aic {
+ uint64_t base;
+ uint32_t version;
+
+ uint32_t nr_irq;
+ uint32_t nr_die;
+ uint32_t max_irq;
+ uint32_t max_die;
+ uint32_t die_stride;
+
+ struct aic_regs regs;
+};
+
+extern struct aic *aic;
+
+void aic_init(void);
+void aic_set_sw(int irq, bool active);
+uint32_t aic_ack(void);
+
+#endif
diff --git a/tools/src/aic_regs.h b/tools/src/aic_regs.h
new file mode 100644
index 0000000..8cc360b
--- /dev/null
+++ b/tools/src/aic_regs.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: MIT */
+
+#define AIC_REG_SIZE 0x8000
+#define AIC_INFO 0x0004
+#define AIC_WHOAMI 0x2000
+#define AIC_EVENT 0x2004
+#define AIC_IPI_SEND 0x2008
+#define AIC_IPI_ACK 0x200c
+#define AIC_IPI_MASK_SET 0x2024
+#define AIC_IPI_MASK_CLR 0x2028
+#define AIC_TARGET_CPU 0x3000
+#define AIC_SW_SET 0x4000
+#define AIC_SW_CLR 0x4080
+#define AIC_MASK_SET 0x4100
+#define AIC_MASK_CLR 0x4180
+
+#define AIC_CPU_IPI_SET(cpu) (0x5008 + ((cpu) << 7))
+#define AIC_CPU_IPI_CLR(cpu) (0x500c + ((cpu) << 7))
+#define AIC_CPU_IPI_MASK_SET(cpu) (0x5024 + ((cpu) << 7))
+#define AIC_CPU_IPI_MASK_CLR(cpu) (0x5028 + ((cpu) << 7))
+
+#define AIC2_INFO1 0x0004
+#define AIC2_INFO2 0x0008
+#define AIC2_INFO3 0x000c
+#define AIC2_LATENCY 0x0204
+#define AIC2_IRQ_CFG 0x2000
+
+#define AIC2_IRQ_CFG_TARGET GENMASK(3, 0)
+
+#define AIC_INFO_NR_HW GENMASK(15, 0)
+
+#define AIC2_INFO1_NR_IRQ GENMASK(15, 0)
+#define AIC2_INFO1_LAST_DIE GENMASK(27, 24)
+
+#define AIC2_INFO3_MAX_IRQ GENMASK(15, 0)
+#define AIC2_INFO3_MAX_DIE GENMASK(27, 24)
+
+#define AIC_EVENT_DIE GENMASK(31, 24)
+#define AIC_EVENT_TYPE GENMASK(23, 16)
+#define AIC_EVENT_NUM GENMASK(15, 0)
+
+#define AIC_EVENT_TYPE_HW 1
+#define AIC_EVENT_TYPE_IPI 4
+#define AIC_EVENT_IPI_OTHER 1
+#define AIC_EVENT_IPI_SELF 2
+
+#define AIC_IPI_SEND_CPU(cpu) BIT(cpu)
+
+#define AIC_IPI_OTHER BIT(0)
+#define AIC_IPI_SELF BIT(31)
+
+#define AIC1_MAX_IRQ 0x400
+#define AIC_MAX_HW_NUM (0x80 * 32) // max_irq of the M1 Max
diff --git a/tools/src/arm_cpu_regs.h b/tools/src/arm_cpu_regs.h
new file mode 100644
index 0000000..06cc919
--- /dev/null
+++ b/tools/src/arm_cpu_regs.h
@@ -0,0 +1,338 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "types.h"
+
+#define SYS_ACTLR_EL1 sys_reg(3, 0, 1, 0, 1)
+#define SYS_ACTLR_EL2 sys_reg(3, 4, 1, 0, 1)
+#define SYS_ACTLR_EL3 sys_reg(3, 6, 1, 0, 1)
+
+#define SYS_CNTHCTL_EL2 sys_reg(3, 4, 14, 1, 0)
+// HCR_EL2.E2H == 1
+#define CNTHCTL_EVNTIS BIT(17)
+#define CNTHCTL_EL1NVVCT BIT(16)
+#define CNTHCTL_EL1NVPCT BIT(15)
+#define CNTHCTL_EL1TVCT BIT(14)
+#define CNTHCTL_EL1TVT BIT(13)
+#define CNTHCTL_ECV BIT(12)
+#define CNTHCTL_EL1PTEN BIT(11)
+#define CNTHCTL_EL1PCTEN BIT(10)
+#define CNTHCTL_EL0PTEN BIT(9)
+#define CNTHCTL_EL0VTEN BIT(8)
+#define CNTHCTL_EVNTI GENMASK(7, 4)
+#define CNTHCTL_EVNTDIR BIT(3)
+#define CNTHCTL_EVNTEN BIT(2)
+#define CNTHCTL_EL0VCTEN BIT(1)
+#define CNTHCTL_EL0PCTEN BIT(0)
+
+#define SYS_CNTV_CTL_EL0 sys_reg(3, 3, 14, 3, 1)
+#define SYS_CNTP_CTL_EL0 sys_reg(3, 3, 14, 2, 1)
+#define SYS_CNTHV_CTL_EL2 sys_reg(3, 4, 14, 3, 1)
+#define SYS_CNTHP_CTL_EL2 sys_reg(3, 4, 14, 2, 1)
+#define CNTx_CTL_ISTATUS BIT(2)
+#define CNTx_CTL_IMASK BIT(1)
+#define CNTx_CTL_ENABLE BIT(0)
+
+#define SYS_ESR_EL2 sys_reg(3, 4, 5, 2, 0)
+#define ESR_ISS2 GENMASK(36, 32)
+#define ESR_EC GENMASK(31, 26)
+#define ESR_IL BIT(25)
+#define ESR_ISS GENMASK(24, 0)
+
+#define ESR_EC_UNKNOWN 0b000000
+#define ESR_EC_WFI 0b000001
+#define ESR_EC_FP_TRAP 0b000111
+#define ESR_EC_PAUTH_TRAP 0b001000
+#define ESR_EC_LS64 0b001010
+#define ESR_EC_BTI 0b001101
+#define ESR_EC_ILLEGAL 0b001110
+#define ESR_EC_SVC 0b010101
+#define ESR_EC_HVC 0b010110
+#define ESR_EC_SMC 0b010111
+#define ESR_EC_MSR 0b011000
+#define ESR_EC_SVE 0b011001
+#define ESR_EC_PAUTH_FAIL 0b011100
+#define ESR_EC_IABORT_LOWER 0b100000
+#define ESR_EC_IABORT 0b100001
+#define ESR_EC_PC_ALIGN 0b100010
+#define ESR_EC_DABORT_LOWER 0b100100
+#define ESR_EC_DABORT 0b100101
+#define ESR_EC_SP_ALIGN 0b100110
+#define ESR_EC_FP_EXC 0b101100
+#define ESR_EC_SERROR 0b101111
+#define ESR_EC_BKPT_LOWER 0b110000
+#define ESR_EC_BKPT 0b110001
+#define ESR_EC_SSTEP_LOWER 0b110010
+#define ESR_EC_SSTEP 0b110011
+#define ESR_EC_WATCH_LOWER 0b110100
+#define ESR_EC_WATCH 0b110101
+#define ESR_EC_BRK 0b111100
+
+#define ESR_ISS_DABORT_ISV BIT(24)
+#define ESR_ISS_DABORT_SAS GENMASK(23, 22)
+#define ESR_ISS_DABORT_SSE BIT(21)
+#define ESR_ISS_DABORT_SRT GENMASK(20, 16)
+#define ESR_ISS_DABORT_SF BIT(15)
+#define ESR_ISS_DABORT_AR BIT(14)
+#define ESR_ISS_DABORT_VNCR BIT(13)
+#define ESR_ISS_DABORT_SET GENMASK(12, 11)
+#define ESR_ISS_DABORT_LSR GENMASK(12, 11)
+#define ESR_ISS_DABORT_FnV BIT(10)
+#define ESR_ISS_DABORT_EA BIT(9)
+#define ESR_ISS_DABORT_CM BIT(8)
+#define ESR_ISS_DABORT_S1PTR BIT(7)
+#define ESR_ISS_DABORT_WnR BIT(6)
+#define ESR_ISS_DABORT_DFSC GENMASK(5, 0)
+
+#define SAS_8B 0
+#define SAS_16B 1
+#define SAS_32B 2
+#define SAS_64B 3
+
+#define ESR_ISS_MSR_OP0 GENMASK(21, 20)
+#define ESR_ISS_MSR_OP0_SHIFT 20
+#define ESR_ISS_MSR_OP2 GENMASK(19, 17)
+#define ESR_ISS_MSR_OP2_SHIFT 17
+#define ESR_ISS_MSR_OP1 GENMASK(16, 14)
+#define ESR_ISS_MSR_OP1_SHIFT 14
+#define ESR_ISS_MSR_CRn GENMASK(13, 10)
+#define ESR_ISS_MSR_CRn_SHIFT 10
+#define ESR_ISS_MSR_Rt GENMASK(9, 5)
+#define ESR_ISS_MSR_CRm GENMASK(4, 1)
+#define ESR_ISS_MSR_CRm_SHIFT 1
+#define ESR_ISS_MSR_DIR BIT(0)
+
+#define SYS_HCR_EL2 sys_reg(3, 4, 1, 1, 0)
+#define HCR_TWEDEL GENMASK(63, 60)
+#define HCR_TWEDEn BIT(59)
+#define HCR_TID5 BIT(58)
+#define HCR_DCT BIT(57)
+#define HCR_ATA BIT(56)
+#define HCR_TTLBOS BIT(55)
+#define HCR_TTLBIS BIT(54)
+#define HCR_EnSCXT BIT(53)
+#define HCR_TOCU BIT(52)
+#define HCR_AMVOFFEN BIT(51)
+#define HCR_TICAB BIT(50)
+#define HCR_TID4 BIT(49)
+#define HCR_FIEN BIT(47)
+#define HCR_FWB BIT(46)
+#define HCR_NV2 BIT(45)
+#define HCR_AT BIT(44)
+#define HCR_NV1 BIT(43)
+#define HCR_NV1 BIT(43)
+#define HCR_NV BIT(42)
+#define HCR_NV BIT(42)
+#define HCR_API BIT(41)
+#define HCR_APK BIT(40)
+#define HCR_MIOCNCE BIT(38)
+#define HCR_TEA BIT(37)
+#define HCR_TERR BIT(36)
+#define HCR_TLOR BIT(35)
+#define HCR_E2H BIT(34)
+#define HCR_ID BIT(33)
+#define HCR_CD BIT(32)
+#define HCR_RW BIT(31)
+#define HCR_TRVM BIT(30)
+#define HCR_HCD BIT(29)
+#define HCR_TDZ BIT(28)
+#define HCR_TGE BIT(27)
+#define HCR_TVM BIT(26)
+#define HCR_TTLB BIT(25)
+#define HCR_TPU BIT(24)
+#define HCR_TPCP BIT(23)
+#define HCR_TPC BIT(23)
+#define HCR_TSW BIT(22)
+#define HCR_TACR BIT(21)
+#define HCR_TIDCP BIT(20)
+#define HCR_TSC BIT(19)
+#define HCR_TID3 BIT(18)
+#define HCR_TID2 BIT(17)
+#define HCR_TID1 BIT(16)
+#define HCR_TID0 BIT(15)
+#define HCR_TWE BIT(14)
+#define HCR_TWI BIT(13)
+#define HCR_DC BIT(12)
+#define HCR_BSU GENMASK(11, 10)
+#define HCR_FB BIT(9)
+#define HCR_VSE BIT(8)
+#define HCR_VI BIT(7)
+#define HCR_VF BIT(6)
+#define HCR_AMO BIT(5)
+#define HCR_IMO BIT(4)
+#define HCR_FMO BIT(3)
+#define HCR_PTW BIT(2)
+#define HCR_SWIO BIT(1)
+#define HCR_VM BIT(0)
+
+#define SYS_ID_AA64MMFR0_EL1 sys_reg(3, 0, 0, 7, 0)
+#define ID_AA64MMFR0_ECV GENMASK(63, 60)
+#define ID_AA64MMFR0_FGT GENMASK(59, 56)
+#define ID_AA64MMFR0_ExS GENMASK(47, 44)
+#define ID_AA64MMFR0_TGran4_2 GENMASK(43, 40)
+#define ID_AA64MMFR0_TGran64_2 GENMASK(39, 36)
+#define ID_AA64MMFR0_TGran16_2 GENMASK(35, 32)
+#define ID_AA64MMFR0_TGran4 GENMASK(31, 28)
+#define ID_AA64MMFR0_TGran64 GENMASK(27, 24)
+#define ID_AA64MMFR0_TGran16 GENMASK(23, 20)
+#define ID_AA64MMFR0_BigEndEL0 GENMASK(19, 16)
+#define ID_AA64MMFR0_SNSMem GENMASK(15, 12)
+#define ID_AA64MMFR0_BigEnd GENMASK(11, 8)
+#define ID_AA64MMFR0_ASIDBits GENMASK(7, 4)
+#define ID_AA64MMFR0_PARange GENMASK(3, 0)
+
+#define SYS_PAR_EL1 sys_reg(3, 0, 7, 4, 0)
+// AArch64-PAR_EL1.F == 0b0
+#define PAR_ATTR GENMASK(63, 56)
+#define PAR_PA GENMASK(51, 12)
+#define PAR_NS BIT(9)
+#define PAR_SH GENMASK(8, 7)
+#define PAR_F BIT(0)
+// AArch64-PAR_EL1.F == 0b1
+#define PAR_S BIT(9)
+#define PAR_PTW BIT(8)
+#define PAR_FST GENMASK(6, 1)
+
+#define SYS_SCTLR_EL1 sys_reg(3, 0, 1, 0, 0)
+#define SYS_SCTLR_EL12 sys_reg(3, 5, 1, 0, 0)
+#define SCTLR_EPAN BIT(57)
+#define SCTLR_EnALS BIT(56)
+#define SCTLR_EnAS0 BIT(55)
+#define SCTLR_EnASR BIT(54)
+#define SCTLR_TWEDEL GENMASK(49, 46)
+#define SCTLR_TWEDEn BIT(45)
+#define SCTLR_DSSBS BIT(44)
+#define SCTLR_ATA BIT(43)
+#define SCTLR_ATA0 BIT(42)
+#define SCTLR_TCF GENMASK(41, 40)
+#define SCTLR_TCF0 GENMASK(39, 38)
+#define SCTLR_ITFSB BIT(37)
+#define SCTLR_BT1 BIT(36)
+#define SCTLR_BT0 BIT(35)
+#define SCTLR_EnIA BIT(31)
+#define SCTLR_EnIB BIT(30)
+#define SCTLR_LSMAOE BIT(29)
+#define SCTLR_nTLSMD BIT(28)
+#define SCTLR_EnDA BIT(27)
+#define SCTLR_UCI BIT(26)
+#define SCTLR_EE BIT(25)
+#define SCTLR_E0E BIT(24)
+#define SCTLR_SPAN BIT(23)
+#define SCTLR_EIS BIT(22)
+#define SCTLR_IESB BIT(21)
+#define SCTLR_TSCXT BIT(20)
+#define SCTLR_WXN BIT(19)
+#define SCTLR_nTWE BIT(18)
+#define SCTLR_nTWI BIT(16)
+#define SCTLR_UCT BIT(15)
+#define SCTLR_DZE BIT(14)
+#define SCTLR_EnDB BIT(13)
+#define SCTLR_I BIT(12)
+#define SCTLR_EOS BIT(11)
+#define SCTLR_EnRCTX BIT(10)
+#define SCTLR_UMA BIT(9)
+#define SCTLR_SED BIT(8)
+#define SCTLR_ITD BIT(7)
+#define SCTLR_nAA BIT(6)
+#define SCTLR_CP15BEN BIT(5)
+#define SCTLR_SA0 BIT(4)
+#define SCTLR_SA BIT(3)
+#define SCTLR_C BIT(2)
+#define SCTLR_A BIT(1)
+#define SCTLR_M BIT(0)
+
+#define SYS_SPSR_EL1 sys_reg(3, 0, 4, 0, 0)
+#define SYS_SPSR_EL12 sys_reg(3, 5, 4, 0, 0)
+#define SYS_SPSR_EL2 sys_reg(3, 4, 4, 0, 0)
+// exception taken from AArch64
+#define SPSR_N BIT(31)
+#define SPSR_Z BIT(30)
+#define SPSR_C BIT(29)
+#define SPSR_V BIT(28)
+#define SPSR_TCO BIT(25)
+#define SPSR_DIT BIT(24)
+#define SPSR_UAO BIT(23)
+#define SPSR_PAN BIT(22)
+#define SPSR_SS BIT(21)
+#define SPSR_IL BIT(20)
+#define SPSR_SSBS BIT(12)
+#define SPSR_BTYPE GENMASK(11, 10)
+#define SPSR_D BIT(9)
+#define SPSR_A BIT(8)
+#define SPSR_I BIT(7)
+#define SPSR_F BIT(6)
+#define SPSR_M GENMASK(4, 0)
+
+#define SYS_TCR_EL1 sys_reg(3, 0, 2, 0, 2)
+#define TCR_DS BIT(59)
+#define TCR_TCMA1 BIT(58)
+#define TCR_TCMA0 BIT(57)
+#define TCR_E0PD1 BIT(56)
+#define TCR_E0PD0 BIT(55)
+#define TCR_NFD1 BIT(54)
+#define TCR_NFD0 BIT(53)
+#define TCR_TBID1 BIT(52)
+#define TCR_TBID0 BIT(51)
+#define TCR_HWU162 BIT(50)
+#define TCR_HWU161 BIT(49)
+#define TCR_HWU160 BIT(48)
+#define TCR_HWU159 BIT(47)
+#define TCR_HWU062 BIT(46)
+#define TCR_HWU061 BIT(45)
+#define TCR_HWU060 BIT(44)
+#define TCR_HWU059 BIT(43)
+#define TCR_HPD1 BIT(42)
+#define TCR_HPD0 BIT(41)
+#define TCR_HD BIT(40)
+#define TCR_HA BIT(39)
+#define TCR_TBI1 BIT(38)
+#define TCR_TBI0 BIT(37)
+#define TCR_AS BIT(36)
+#define TCR_IPS GENMASK(34, 32)
+#define TCR_IPS_1TB 0b010UL
+#define TCR_IPS_4TB 0b011UL
+#define TCR_IPS_16TB 0b100UL
+#define TCR_TG1 GENMASK(31, 30)
+#define TCR_TG1_16K 0b01UL
+#define TCR_SH1 GENMASK(29, 28)
+#define TCR_SH1_IS 0b11UL
+#define TCR_ORGN1 GENMASK(27, 26)
+#define TCR_ORGN1_WBWA 0b01UL
+#define TCR_IRGN1 GENMASK(25, 24)
+#define TCR_IRGN1_WBWA 0b01UL
+#define TCR_EPD1 BIT(23)
+#define TCR_A1 BIT(22)
+#define TCR_T1SZ GENMASK(21, 16)
+#define TCR_T1SZ_48BIT 16UL
+#define TCR_TG0 GENMASK(15, 14)
+#define TCR_TG0_16K 0b10UL
+#define TCR_SH0 GENMASK(13, 12)
+#define TCR_SH0_IS 0b11UL
+#define TCR_ORGN0 GENMASK(11, 10)
+#define TCR_ORGN0_WBWA 0b01UL
+#define TCR_IRGN0 GENMASK(9, 8)
+#define TCR_IRGN0_WBWA 0b01UL
+#define TCR_EPD0 BIT(7)
+#define TCR_T0SZ GENMASK(5, 0)
+#define TCR_T0SZ_48BIT 16UL
+
+#define SYS_VTCR_EL2 sys_reg(3, 4, 2, 1, 2)
+// Profile(A)
+#define VTCR_SL2 BIT(33)
+#define VTCR_DS BIT(32)
+#define VTCR_NSA BIT(30)
+#define VTCR_NSW BIT(29)
+#define VTCR_HWU62 BIT(28)
+#define VTCR_HWU61 BIT(27)
+#define VTCR_HWU60 BIT(26)
+#define VTCR_HWU59 BIT(25)
+#define VTCR_HD BIT(22)
+#define VTCR_HA BIT(21)
+#define VTCR_VS BIT(19)
+#define VTCR_PS GENMASK(18, 16)
+#define VTCR_TG0 GENMASK(15, 14)
+#define VTCR_SH0 GENMASK(13, 12)
+#define VTCR_ORGN0 GENMASK(11, 10)
+#define VTCR_IRGN0 GENMASK(9, 8)
+#define VTCR_SL0 GENMASK(7, 6)
+#define VTCR_SL0 GENMASK(7, 6)
+#define VTCR_T0SZ GENMASK(5, 0)
diff --git a/tools/src/asc.c b/tools/src/asc.c
new file mode 100644
index 0000000..67c9d46
--- /dev/null
+++ b/tools/src/asc.c
@@ -0,0 +1,126 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "adt.h"
+#include "asc.h"
+#include "malloc.h"
+#include "utils.h"
+
+#define ASC_CPU_CONTROL 0x44
+#define ASC_CPU_CONTROL_START 0x10
+
+#define ASC_MBOX_CONTROL_FULL BIT(16)
+#define ASC_MBOX_CONTROL_EMPTY BIT(17)
+
+#define ASC_MBOX_A2I_CONTROL 0x110
+#define ASC_MBOX_A2I_SEND0 0x800
+#define ASC_MBOX_A2I_SEND1 0x808
+#define ASC_MBOX_A2I_RECV0 0x810
+#define ASC_MBOX_A2I_RECV1 0x818
+
+#define ASC_MBOX_I2A_CONTROL 0x114
+#define ASC_MBOX_I2A_SEND0 0x820
+#define ASC_MBOX_I2A_SEND1 0x828
+#define ASC_MBOX_I2A_RECV0 0x830
+#define ASC_MBOX_I2A_RECV1 0x838
+
+struct asc_dev {
+ uintptr_t cpu_base;
+ uintptr_t base;
+ int iop_node;
+};
+
+asc_dev_t *asc_init(const char *path)
+{
+ int asc_path[8];
+ int node = adt_path_offset_trace(adt, path, asc_path);
+ if (node < 0) {
+ printf("asc: Error getting ASC node %s\n", path);
+ return NULL;
+ }
+
+ u64 base;
+ if (adt_get_reg(adt, asc_path, "reg", 0, &base, NULL) < 0) {
+ printf("asc: Error getting ASC %s base address.\n", path);
+ return NULL;
+ }
+
+ asc_dev_t *asc = malloc(sizeof(*asc));
+ if (!asc)
+ return NULL;
+
+ asc->iop_node = adt_first_child_offset(adt, node);
+ asc->cpu_base = base;
+ asc->base = base + 0x8000;
+
+ clear32(base + ASC_CPU_CONTROL, ASC_CPU_CONTROL_START);
+ return asc;
+}
+
+void asc_free(asc_dev_t *asc)
+{
+ free(asc);
+}
+
+int asc_get_iop_node(asc_dev_t *asc)
+{
+ return asc->iop_node;
+}
+
+void asc_cpu_start(asc_dev_t *asc)
+{
+ set32(asc->cpu_base + ASC_CPU_CONTROL, ASC_CPU_CONTROL_START);
+}
+
+void asc_cpu_stop(asc_dev_t *asc)
+{
+ clear32(asc->cpu_base + ASC_CPU_CONTROL, ASC_CPU_CONTROL_START);
+}
+
+bool asc_can_recv(asc_dev_t *asc)
+{
+ return !(read32(asc->base + ASC_MBOX_I2A_CONTROL) & ASC_MBOX_CONTROL_EMPTY);
+}
+
+bool asc_recv(asc_dev_t *asc, struct asc_message *msg)
+{
+ if (!asc_can_recv(asc))
+ return false;
+
+ msg->msg0 = read64(asc->base + ASC_MBOX_I2A_RECV0);
+ msg->msg1 = (u32)read64(asc->base + ASC_MBOX_I2A_RECV1);
+ dma_rmb();
+
+ // printf("received msg: %lx %x\n", msg->msg0, msg->msg1);
+
+ return true;
+}
+
+bool asc_recv_timeout(asc_dev_t *asc, struct asc_message *msg, u32 delay_usec)
+{
+ u64 timeout = timeout_calculate(delay_usec);
+ while (!timeout_expired(timeout)) {
+ if (asc_recv(asc, msg))
+ return true;
+ }
+ return false;
+}
+
+bool asc_can_send(asc_dev_t *asc)
+{
+ return !(read32(asc->base + ASC_MBOX_A2I_CONTROL) & ASC_MBOX_CONTROL_FULL);
+}
+
+bool asc_send(asc_dev_t *asc, const struct asc_message *msg)
+{
+ if (poll32(asc->base + ASC_MBOX_A2I_CONTROL, ASC_MBOX_CONTROL_FULL, 0, 200000)) {
+ printf("asc: A2I mailbox full for 200ms. Is the ASC stuck?");
+ return false;
+ }
+
+ dma_wmb();
+ write64(asc->base + ASC_MBOX_A2I_SEND0, msg->msg0);
+ write64(asc->base + ASC_MBOX_A2I_SEND1, msg->msg1);
+
+ // printf("sent msg: %lx %x\n", msg->msg0, msg->msg1);
+ return true;
+}
diff --git a/tools/src/asc.h b/tools/src/asc.h
new file mode 100644
index 0000000..0aac349
--- /dev/null
+++ b/tools/src/asc.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef ASC_H
+#define ASC_H
+
+#include "types.h"
+
+struct asc_message {
+ u64 msg0;
+ u32 msg1;
+};
+
+typedef struct asc_dev asc_dev_t;
+
+asc_dev_t *asc_init(const char *path);
+void asc_free(asc_dev_t *asc);
+
+int asc_get_iop_node(asc_dev_t *asc);
+
+void asc_cpu_start(asc_dev_t *asc);
+void asc_cpu_stop(asc_dev_t *asc);
+
+bool asc_can_recv(asc_dev_t *asc);
+bool asc_can_send(asc_dev_t *asc);
+
+bool asc_recv(asc_dev_t *asc, struct asc_message *msg);
+bool asc_recv_timeout(asc_dev_t *asc, struct asc_message *msg, u32 delay_usec);
+bool asc_send(asc_dev_t *asc, const struct asc_message *msg);
+
+#endif
diff --git a/tools/src/chainload.c b/tools/src/chainload.c
new file mode 100644
index 0000000..1dd7c68
--- /dev/null
+++ b/tools/src/chainload.c
@@ -0,0 +1,148 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "../build/build_cfg.h"
+
+#include "chainload.h"
+#include "adt.h"
+#include "malloc.h"
+#include "memory.h"
+#include "nvme.h"
+#include "string.h"
+#include "types.h"
+#include "utils.h"
+#include "xnuboot.h"
+
+#ifdef CHAINLOADING
+int rust_load_image(const char *spec, void **image, size_t *size);
+#endif
+
+extern u8 _chainload_stub_start[];
+extern u8 _chainload_stub_end[];
+
+int chainload_image(void *image, size_t size, char **vars, size_t var_cnt)
+{
+ u64 new_base = (u64)_base;
+ size_t image_size = size;
+
+ printf("chainload: Preparing image...\n");
+
+ // m1n1 variables
+ for (size_t i = 0; i < var_cnt; i++)
+ image_size += strlen(vars[i]) + 1;
+
+ // pad to end payload
+ image_size += 4;
+ image_size = ALIGN_UP(image_size, SZ_16K);
+
+ // SEPFW
+ size_t sepfw_off = image_size;
+
+ int anode = adt_path_offset(adt, "/chosen/memory-map");
+ if (anode < 0) {
+ printf("chainload: /chosen/memory-map not found\n");
+ return -1;
+ }
+ u64 sepfw[2];
+ if (ADT_GETPROP_ARRAY(adt, anode, "SEPFW", sepfw) < 0) {
+ printf("chainload: Failed to find SEPFW\n");
+ return -1;
+ }
+
+ image_size += sepfw[1];
+ image_size = ALIGN_UP(image_size, SZ_16K);
+
+ // Bootargs
+ size_t bootargs_off = image_size;
+ const size_t bootargs_size = SZ_16K;
+ image_size += bootargs_size;
+
+ printf("chainload: Total image size: 0x%lx\n", image_size);
+
+ size_t stub_size = _chainload_stub_end - _chainload_stub_start;
+
+ void *new_image = malloc(image_size + stub_size);
+
+ // Copy m1n1
+ memcpy(new_image, image, size);
+
+ // Add vars
+ u8 *p = new_image + size;
+ for (size_t i = 0; i < var_cnt; i++) {
+ size_t len = strlen(vars[i]);
+
+ memcpy(p, vars[i], len);
+ p[len] = '\n';
+ p += len + 1;
+ }
+
+ // Add end padding
+ memset(p, 0, 4);
+
+ // Copy SEPFW
+ memcpy(new_image + sepfw_off, (void *)sepfw[0], sepfw[1]);
+
+ // Adjust ADT SEPFW address
+ sepfw[0] = new_base + sepfw_off;
+ if (adt_setprop(adt, anode, "SEPFW", &sepfw, sizeof(sepfw)) < 0) {
+ printf("chainload: Failed to set SEPFW prop\n");
+ free(new_image);
+ return -1;
+ }
+
+ // Copy bootargs
+ struct boot_args *new_boot_args = new_image + bootargs_off;
+ *new_boot_args = cur_boot_args;
+ new_boot_args->top_of_kernel_data = new_base + image_size;
+
+ // Copy chainload stub
+ void *stub = new_image + image_size;
+ memcpy(stub, _chainload_stub_start, stub_size);
+ dc_cvau_range(stub, stub_size);
+ ic_ivau_range(stub, stub_size);
+
+ // Set up next stage
+ next_stage.entry = stub;
+ next_stage.args[0] = new_base + bootargs_off;
+ next_stage.args[1] = (u64)new_image;
+ next_stage.args[2] = new_base;
+ next_stage.args[3] = image_size;
+ next_stage.args[4] = new_base + 0x800; // m1n1 entrypoint
+ next_stage.restore_logo = false;
+
+ return 0;
+}
+
+#ifdef CHAINLOADING
+
+int chainload_load(const char *spec, char **vars, size_t var_cnt)
+{
+ void *image;
+ size_t size;
+ int ret;
+
+ if (!nvme_init()) {
+ printf("chainload: NVME init failed\n");
+ return -1;
+ }
+
+ ret = rust_load_image(spec, &image, &size);
+ nvme_shutdown();
+ if (ret < 0)
+ return ret;
+
+ return chainload_image(image, size, vars, var_cnt);
+}
+
+#else
+
+int chainload_load(const char *spec, char **vars, size_t var_cnt)
+{
+ UNUSED(spec);
+ UNUSED(vars);
+ UNUSED(var_cnt);
+
+ printf("Chainloading files not supported in this build!\n");
+ return -1;
+}
+
+#endif
diff --git a/tools/src/chainload.h b/tools/src/chainload.h
new file mode 100644
index 0000000..206f482
--- /dev/null
+++ b/tools/src/chainload.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef __CHAINLOAD_H__
+#define __CHAINLOAD_H__
+
+#include "types.h"
+
+int chainload_image(void *base, size_t size, char **vars, size_t var_cnt);
+int chainload_load(const char *spec, char **vars, size_t var_cnt);
+
+#endif
diff --git a/tools/src/chainload_asm.S b/tools/src/chainload_asm.S
new file mode 100644
index 0000000..361ec8f
--- /dev/null
+++ b/tools/src/chainload_asm.S
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: MIT */
+
+.text
+
+.globl _chainload_stub_start
+.globl _chainload_stub_end
+.type _chainload_stub_start, @function
+
+_chainload_stub_start:
+1:
+ ldp x5, x6, [x1], #16
+ stp x5, x6, [x2]
+ dc cvau, x2
+ ic ivau, x2
+ add x2, x2, #16
+ sub x3, x3, #16
+ cbnz x3, 1b
+
+ br x4
+_chainload_stub_end:
diff --git a/tools/src/chickens.c b/tools/src/chickens.c
new file mode 100644
index 0000000..68a7eee
--- /dev/null
+++ b/tools/src/chickens.c
@@ -0,0 +1,118 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "chickens.h"
+#include "cpu_regs.h"
+#include "uart.h"
+#include "utils.h"
+
+/* Part IDs in MIDR_EL1 */
+#define MIDR_PART_T8181_ICESTORM 0x20
+#define MIDR_PART_T8181_FIRESTORM 0x21
+#define MIDR_PART_T8103_ICESTORM 0x22
+#define MIDR_PART_T8103_FIRESTORM 0x23
+#define MIDR_PART_T6000_ICESTORM 0x24
+#define MIDR_PART_T6000_FIRESTORM 0x25
+#define MIDR_PART_T6001_ICESTORM 0x28
+#define MIDR_PART_T6001_FIRESTORM 0x29
+#define MIDR_PART_T8110_BLIZZARD 0x30
+#define MIDR_PART_T8110_AVALANCHE 0x31
+#define MIDR_PART_T8112_BLIZZARD 0x32
+#define MIDR_PART_T8112_AVALANCHE 0x33
+
+#define MIDR_REV_LOW GENMASK(3, 0)
+#define MIDR_PART GENMASK(15, 4)
+#define MIDR_REV_HIGH GENMASK(23, 20)
+
+void init_m1_icestorm(void);
+void init_t8103_firestorm(int rev);
+void init_t6000_firestorm(int rev);
+void init_t6001_firestorm(int rev);
+
+void init_m2_blizzard(void);
+void init_t8112_avalanche(int rev);
+
+const char *init_cpu(void)
+{
+ const char *cpu = "Unknown";
+
+ msr(OSLAR_EL1, 0);
+
+ /* This is performed unconditionally on all cores (necessary?) */
+ if (is_ecore())
+ reg_set(SYS_IMP_APL_EHID4, HID4_DISABLE_DC_MVA | HID4_DISABLE_DC_SW_L2_OPS);
+ else
+ reg_set(SYS_IMP_APL_HID4, HID4_DISABLE_DC_MVA | HID4_DISABLE_DC_SW_L2_OPS);
+
+ uint64_t midr = mrs(MIDR_EL1);
+ int part = FIELD_GET(MIDR_PART, midr);
+ int rev = (FIELD_GET(MIDR_REV_HIGH, midr) << 4) | FIELD_GET(MIDR_REV_LOW, midr);
+
+ printf(" CPU part: 0x%x rev: 0x%x\n", part, rev);
+
+ switch (part) {
+ case MIDR_PART_T8103_FIRESTORM:
+ cpu = "M1 Firestorm";
+ init_t8103_firestorm(rev);
+ break;
+
+ case MIDR_PART_T6000_FIRESTORM:
+ cpu = "M1 Pro Firestorm";
+ init_t6000_firestorm(rev);
+ break;
+
+ case MIDR_PART_T6001_FIRESTORM:
+ cpu = "M1 Max Firestorm";
+ init_t6001_firestorm(rev);
+ break;
+
+ case MIDR_PART_T8103_ICESTORM:
+ cpu = "M1 Icestorm";
+ init_m1_icestorm();
+ break;
+
+ case MIDR_PART_T6000_ICESTORM:
+ cpu = "M1 Pro Icestorm";
+ init_m1_icestorm();
+ break;
+
+ case MIDR_PART_T6001_ICESTORM:
+ cpu = "M1 Max Icestorm";
+ init_m1_icestorm();
+ break;
+
+ case MIDR_PART_T8112_AVALANCHE:
+ cpu = "M2 Avalanche";
+ init_t8112_avalanche(rev);
+ break;
+
+ case MIDR_PART_T8112_BLIZZARD:
+ cpu = "M2 Blizzard";
+ init_m2_blizzard();
+ break;
+
+ default:
+ uart_puts(" Unknown CPU type");
+ break;
+ }
+
+ int core = mrs(MPIDR_EL1) & 0xff;
+
+ // Unknown, related to SMP?
+ msr(s3_4_c15_c5_0, core);
+ msr(SYS_IMP_APL_AMX_CTL_EL1, 0x100);
+
+ // Enable IRQs (at least necessary on t600x)
+ msr(s3_4_c15_c10_4, 0);
+
+ sysop("isb");
+
+ /* Unmask external IRQs, set WFI mode to up (2) */
+ reg_mask(SYS_IMP_APL_CYC_OVRD,
+ CYC_OVRD_FIQ_MODE_MASK | CYC_OVRD_IRQ_MODE_MASK | CYC_OVRD_WFI_MODE_MASK,
+ CYC_OVRD_FIQ_MODE(0) | CYC_OVRD_IRQ_MODE(0) | CYC_OVRD_WFI_MODE(2));
+
+ /* Enable branch prediction state retention across ACC sleep */
+ reg_mask(SYS_IMP_APL_ACC_CFG, ACC_CFG_BP_SLEEP_MASK, ACC_CFG_BP_SLEEP(3));
+
+ return cpu;
+}
diff --git a/tools/src/chickens.h b/tools/src/chickens.h
new file mode 100644
index 0000000..c1cb5a6
--- /dev/null
+++ b/tools/src/chickens.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef __CHICKENS_H__
+#define __CHICKENS_H__
+
+const char *init_cpu(void);
+
+#endif
diff --git a/tools/src/chickens_avalanche.c b/tools/src/chickens_avalanche.c
new file mode 100644
index 0000000..faf7a6b
--- /dev/null
+++ b/tools/src/chickens_avalanche.c
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "cpu_regs.h"
+#include "utils.h"
+
+static void init_common_avalanche(void)
+{
+ reg_mask(SYS_IMP_APL_HID1, HID1_ZCL_RF_MISPREDICT_THRESHOLD_MASK,
+ HID1_ZCL_RF_MISPREDICT_THRESHOLD(1));
+ reg_mask(SYS_IMP_APL_HID1, HID1_ZCL_RF_RESTART_THRESHOLD_MASK,
+ HID1_ZCL_RF_RESTART_THRESHOLD(3));
+
+ reg_set(SYS_IMP_APL_HID11, HID11_DISABLE_LD_NT_WIDGET);
+
+ reg_set(SYS_IMP_APL_HID9, HID9_TSO_ALLOW_DC_ZVA_WC | HID9_AVL_UNK17);
+
+ // "configure dummy cycles to work around incorrect temp sensor readings on
+ // NEX power gating" (maybe)
+ reg_mask(SYS_IMP_APL_HID13,
+ HID13_POST_OFF_CYCLES_MASK | HID13_POST_ON_CYCLES_MASK | HID13_PRE_CYCLES_MASK |
+ HID13_GROUP0_FF1_DELAY_MASK | HID13_GROUP0_FF2_DELAY_MASK |
+ HID13_GROUP0_FF3_DELAY_MASK | HID13_GROUP0_FF4_DELAY_MASK |
+ HID13_GROUP0_FF5_DELAY_MASK | HID13_GROUP0_FF6_DELAY_MASK |
+ HID13_GROUP0_FF7_DELAY_MASK | HID13_RESET_CYCLES_MASK,
+ HID13_POST_OFF_CYCLES(8) | HID13_POST_ON_CYCLES(8) | HID13_PRE_CYCLES(1) |
+ HID13_GROUP0_FF1_DELAY(4) | HID13_GROUP0_FF2_DELAY(4) | HID13_GROUP0_FF3_DELAY(4) |
+ HID13_GROUP0_FF4_DELAY(4) | HID13_GROUP0_FF5_DELAY(4) | HID13_GROUP0_FF6_DELAY(4) |
+ HID13_GROUP0_FF7_DELAY(4) | HID13_RESET_CYCLES(0));
+
+ reg_mask(SYS_IMP_APL_HID26, HID26_GROUP1_OFFSET_MASK | HID26_GROUP2_OFFSET_MASK,
+ HID26_GROUP1_OFFSET(26) | HID26_GROUP2_OFFSET(31));
+ reg_mask(SYS_IMP_APL_HID27, HID27_GROUP3_OFFSET_MASK, HID27_GROUP3_OFFSET(31));
+}
+
+static void init_m2_avalanche(void)
+{
+ init_common_avalanche();
+
+ reg_mask(SYS_IMP_APL_HID3, HID3_DEV_PCIE_THROTTLE_LIMIT_MASK, HID3_DEV_PCIE_THROTTLE_LIMIT(60));
+ reg_set(SYS_IMP_APL_HID3, HID3_DEV_PCIE_THROTTLE_ENABLE);
+ reg_set(SYS_IMP_APL_HID18, HID18_AVL_UNK27 | HID18_AVL_UNK29);
+ reg_set(SYS_IMP_APL_HID16, HID16_AVL_UNK12);
+}
+
+void init_t8112_avalanche(int rev)
+{
+ UNUSED(rev);
+
+ init_m2_avalanche();
+}
diff --git a/tools/src/chickens_blizzard.c b/tools/src/chickens_blizzard.c
new file mode 100644
index 0000000..8b88b6c
--- /dev/null
+++ b/tools/src/chickens_blizzard.c
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "cpu_regs.h"
+#include "utils.h"
+
+static void init_common_blizzard(void)
+{
+ reg_set(SYS_IMP_APL_EHID0, EHID0_BLI_UNK32);
+}
+
+void init_m2_blizzard(void)
+{
+ init_common_blizzard();
+
+ reg_mask(SYS_IMP_APL_EHID9, EHID9_DEV_2_THROTTLE_LIMIT_MASK, EHID9_DEV_2_THROTTLE_LIMIT(60));
+ reg_set(SYS_IMP_APL_EHID9, EHID9_DEV_2_THROTTLE_ENABLE);
+ reg_set(SYS_IMP_APL_EHID18, EHID18_BLZ_UNK34);
+}
diff --git a/tools/src/chickens_firestorm.c b/tools/src/chickens_firestorm.c
new file mode 100644
index 0000000..7754820
--- /dev/null
+++ b/tools/src/chickens_firestorm.c
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "cpu_regs.h"
+#include "utils.h"
+
+static void init_common_firestorm(void)
+{
+ reg_set(SYS_IMP_APL_HID0, HID0_SAME_PG_POWER_OPTIMIZATION);
+
+ // Disable SMC trapping to EL2
+ reg_clr(SYS_IMP_APL_HID1, HID1_TRAP_SMC);
+
+ reg_clr(SYS_IMP_APL_HID3, HID3_DEV_PCIE_THROTTLE_ENABLE | HID3_DISABLE_ARBITER_FIX_BIF_CRD);
+
+ // "Post-silicon tuning of STNT widget contiguous counter threshold"
+ reg_mask(SYS_IMP_APL_HID4, HID4_STNT_COUNTER_THRESHOLD_MASK, HID4_STNT_COUNTER_THRESHOLD(3));
+
+ // "Sibling Merge in LLC can cause UC load to violate ARM Memory Ordering Rules."
+ reg_set(SYS_IMP_APL_HID5, HID5_DISABLE_FILL_2C_MERGE);
+
+ reg_set(SYS_IMP_APL_HID9, HID9_TSO_ALLOW_DC_ZVA_WC);
+
+ reg_set(SYS_IMP_APL_HID11, HID11_DISABLE_LD_NT_WIDGET);
+
+ // "configure dummy cycles to work around incorrect temp sensor readings on
+ // NEX power gating"
+ reg_mask(SYS_IMP_APL_HID13, HID13_PRE_CYCLES_MASK, HID13_PRE_CYCLES(4));
+
+ // Best bit names...
+ // Maybe: "RF bank and Multipass conflict forward progress widget does not
+ // handle 3+ cycle livelock"
+ reg_set(SYS_IMP_APL_HID16, HID16_SPAREBIT0 | HID16_SPAREBIT3 | HID16_ENABLE_MPX_PICK_45 |
+ HID16_ENABLE_MP_CYCLONE_7);
+}
+
+static void init_m1_firestorm(void)
+{
+ init_common_firestorm();
+
+ // "Cross-beat Crypto(AES/PMUL) ICache fusion is not disabled for branch
+ // uncondtional "recoded instruction."
+ reg_set(SYS_IMP_APL_HID0, HID0_FETCH_WIDTH_DISABLE | HID0_CACHE_FUSION_DISABLE);
+
+ reg_set(SYS_IMP_APL_HID7, HID7_FORCE_NONSPEC_IF_STEPPING |
+ HID7_FORCE_NONSPEC_IF_SPEC_FLUSH_POINTER_INVALID_AND_MP_VALID);
+
+ reg_mask(SYS_IMP_APL_HID7, HID7_FORCE_NONSPEC_TARGET_TIMER_SEL_MASK,
+ HID7_FORCE_NONSPEC_TARGET_TIMER_SEL(3));
+
+ reg_set(SYS_IMP_APL_HID9, HID9_TSO_SERIALIZE_VLD_MICROOPS | HID9_FIX_BUG_51667805);
+
+ reg_set(SYS_IMP_APL_HID18, HID18_HVC_SPECULATION_DISABLE);
+
+ reg_clr(SYS_IMP_APL_HID21, HID21_ENABLE_LDREX_FILL_REPLY);
+}
+
+void init_t8103_firestorm(int rev)
+{
+ init_m1_firestorm();
+
+ reg_mask(SYS_IMP_APL_HID6, HID6_UP_CRD_TKN_INIT_C2_MASK, HID6_UP_CRD_TKN_INIT_C2(0));
+
+ if (rev >= 0x10) {
+ reg_set(SYS_IMP_APL_HID4,
+ HID4_ENABLE_LFSR_STALL_LOAD_PIPE_2_ISSUE | HID4_ENABLE_LFSR_STALL_STQ_REPLAY);
+
+ reg_set(SYS_IMP_APL_HID9, HID9_FIX_BUG_55719865);
+ reg_set(SYS_IMP_APL_HID11, HID11_ENABLE_FIX_UC_55719865);
+ }
+
+ if (rev == 0x11)
+ reg_set(SYS_IMP_APL_HID1, HID1_ENABLE_MDSB_STALL_PIPELINE_ECO | HID1_ENABLE_BR_KILL_LIMIT);
+
+ if (rev >= 0x11)
+ reg_set(SYS_IMP_APL_HID18, HID18_SPAREBIT17);
+}
+
+void init_t6000_firestorm(int rev)
+{
+ init_m1_firestorm();
+
+ reg_set(SYS_IMP_APL_HID9, HID9_FIX_BUG_55719865);
+ reg_set(SYS_IMP_APL_HID11, HID11_ENABLE_FIX_UC_55719865);
+
+ if (rev >= 0x10) {
+ reg_set(SYS_IMP_APL_HID1, HID1_ENABLE_MDSB_STALL_PIPELINE_ECO | HID1_ENABLE_BR_KILL_LIMIT);
+
+ reg_set(SYS_IMP_APL_HID4,
+ HID4_ENABLE_LFSR_STALL_LOAD_PIPE_2_ISSUE | HID4_ENABLE_LFSR_STALL_STQ_REPLAY);
+
+ reg_set(SYS_IMP_APL_HID18, HID18_SPAREBIT17);
+ }
+}
+
+void init_t6001_firestorm(int rev)
+{
+ init_m1_firestorm();
+
+ reg_set(SYS_IMP_APL_HID1, HID1_ENABLE_MDSB_STALL_PIPELINE_ECO);
+
+ reg_set(SYS_IMP_APL_HID4,
+ HID4_ENABLE_LFSR_STALL_LOAD_PIPE_2_ISSUE | HID4_ENABLE_LFSR_STALL_STQ_REPLAY);
+
+ reg_set(SYS_IMP_APL_HID9, HID9_FIX_BUG_55719865);
+
+ reg_set(SYS_IMP_APL_HID11, HID11_ENABLE_FIX_UC_55719865);
+
+ if (rev >= 0x10) {
+ reg_set(SYS_IMP_APL_HID1, HID1_ENABLE_BR_KILL_LIMIT);
+
+ reg_set(SYS_IMP_APL_HID18, HID18_SPAREBIT17);
+ }
+}
diff --git a/tools/src/chickens_icestorm.c b/tools/src/chickens_icestorm.c
new file mode 100644
index 0000000..bc0cfb8
--- /dev/null
+++ b/tools/src/chickens_icestorm.c
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "cpu_regs.h"
+#include "utils.h"
+
+static void init_common_icestorm(void)
+{
+ // "Sibling Merge in LLC can cause UC load to violate ARM Memory Ordering Rules."
+ reg_set(SYS_IMP_APL_HID5, HID5_DISABLE_FILL_2C_MERGE);
+
+ reg_clr(SYS_IMP_APL_EHID9, EHID9_DEV_2_THROTTLE_ENABLE);
+
+ // "Prevent store-to-load forwarding for UC memory to avoid barrier ordering
+ // violation"
+ reg_set(SYS_IMP_APL_EHID10, HID10_FORCE_WAIT_STATE_DRAIN_UC | HID10_DISABLE_ZVA_TEMPORAL_TSO);
+
+ // Disable SMC trapping to EL2
+ reg_clr(SYS_IMP_APL_EHID20, EHID20_TRAP_SMC);
+}
+
+void init_m1_icestorm(void)
+{
+ init_common_icestorm();
+
+ reg_set(SYS_IMP_APL_EHID20, EHID20_FORCE_NONSPEC_IF_OLDEST_REDIR_VALID_AND_OLDER |
+ EHID20_FORCE_NONSPEC_IF_SPEC_FLUSH_POINTER_NE_BLK_RTR_POINTER);
+
+ reg_mask(SYS_IMP_APL_EHID20, EHID20_FORCE_NONSPEC_TARGETED_TIMER_SEL_MASK,
+ EHID20_FORCE_NONSPEC_TARGETED_TIMER_SEL(3));
+}
diff --git a/tools/src/clk.c b/tools/src/clk.c
new file mode 100644
index 0000000..ec0c77d
--- /dev/null
+++ b/tools/src/clk.c
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "clk.h"
+#include "adt.h"
+#include "types.h"
+#include "utils.h"
+
+#define CLK_MUX GENMASK(27, 24)
+
+#define NCO_BASE 5
+#define NUM_NCOS 5
+
+void clk_init(void)
+{
+ int path[8];
+ int node = adt_path_offset_trace(adt, "/arm-io/mca-switch", path);
+
+ if (node < 0) {
+ printf("mca-switch node not found!\n");
+ return;
+ }
+
+ u64 mca_clk_base, mca_clk_size;
+ if (adt_get_reg(adt, path, "reg", 2, &mca_clk_base, &mca_clk_size)) {
+ printf("Failed to get mca-switch reg property!\n");
+ return;
+ }
+
+ printf("CLK: MCA clock registers @ 0x%lx (0x%lx)\n", mca_clk_base, mca_clk_size);
+
+ unsigned int i;
+ for (i = 0; i < (mca_clk_size / 4); i++)
+ mask32(mca_clk_base + 4 * i, CLK_MUX, FIELD_PREP(CLK_MUX, NCO_BASE + min(NUM_NCOS - 1, i)));
+
+ printf("CLK: Initialized %d MCA clock muxes\n", i);
+}
diff --git a/tools/src/clk.h b/tools/src/clk.h
new file mode 100644
index 0000000..bb79fa4
--- /dev/null
+++ b/tools/src/clk.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef __CLK_H__
+#define __CLK_H__
+
+void clk_init(void);
+
+#endif
diff --git a/tools/src/cpu_regs.h b/tools/src/cpu_regs.h
new file mode 100644
index 0000000..236b53e
--- /dev/null
+++ b/tools/src/cpu_regs.h
@@ -0,0 +1,290 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "arm_cpu_regs.h"
+#include "types.h"
+
+/* ARM extensions */
+#define ESR_EC_IMPDEF 0b111111
+#define ESR_ISS_IMPDEF_MSR 0x20
+
+#define SYS_IMP_APL_ACTLR_EL12 sys_reg(3, 6, 15, 14, 6)
+
+#define SYS_IMP_APL_AMX_CTL_EL1 sys_reg(3, 4, 15, 1, 4)
+#define SYS_IMP_APL_AMX_CTL_EL2 sys_reg(3, 4, 15, 4, 7)
+#define SYS_IMP_APL_AMX_CTL_EL12 sys_reg(3, 4, 15, 4, 6)
+
+#define AMX_CTL_EN BIT(63)
+#define AMX_CTL_EN_EL1 BIT(62)
+
+#define SYS_IMP_APL_CNTVCT_ALIAS_EL0 sys_reg(3, 4, 15, 10, 6)
+
+/* HID registers */
+#define SYS_IMP_APL_HID0 sys_reg(3, 0, 15, 0, 0)
+#define HID0_FETCH_WIDTH_DISABLE BIT(28)
+#define HID0_CACHE_FUSION_DISABLE BIT(36)
+#define HID0_SAME_PG_POWER_OPTIMIZATION BIT(45)
+
+#define SYS_IMP_APL_EHID0 sys_reg(3, 0, 15, 0, 1)
+#define EHID0_BLI_UNK32 BIT(32)
+
+#define SYS_IMP_APL_HID1 sys_reg(3, 0, 15, 1, 0)
+#define HID1_TRAP_SMC BIT(54)
+#define HID1_ENABLE_MDSB_STALL_PIPELINE_ECO BIT(58)
+#define HID1_ENABLE_BR_KILL_LIMIT BIT(60)
+
+#define HID1_ZCL_RF_RESTART_THRESHOLD_MASK GENMASK(23, 22)
+#define HID1_ZCL_RF_RESTART_THRESHOLD(x) (((unsigned long)x) << 22)
+#define HID1_ZCL_RF_MISPREDICT_THRESHOLD_MASK GENMASK(43, 42)
+#define HID1_ZCL_RF_MISPREDICT_THRESHOLD(x) (((unsigned long)x) << 42)
+
+#define SYS_IMP_APL_HID3 sys_reg(3, 0, 15, 3, 0)
+#define HID3_DISABLE_ARBITER_FIX_BIF_CRD BIT(44)
+#define HID3_DEV_PCIE_THROTTLE_LIMIT_MASK GENMASK(62, 57)
+#define HID3_DEV_PCIE_THROTTLE_LIMIT(x) (((unsigned long)x) << 57)
+#define HID3_DEV_PCIE_THROTTLE_ENABLE BIT(63)
+
+#define SYS_IMP_APL_HID4 sys_reg(3, 0, 15, 4, 0)
+#define SYS_IMP_APL_EHID4 sys_reg(3, 0, 15, 4, 1)
+#define HID4_DISABLE_DC_MVA BIT(11)
+#define HID4_DISABLE_DC_SW_L2_OPS BIT(44)
+#define HID4_STNT_COUNTER_THRESHOLD(x) (((unsigned long)x) << 40)
+#define HID4_STNT_COUNTER_THRESHOLD_MASK (3UL << 40)
+#define HID4_ENABLE_LFSR_STALL_LOAD_PIPE_2_ISSUE BIT(49)
+#define HID4_ENABLE_LFSR_STALL_STQ_REPLAY BIT(53)
+
+#define SYS_IMP_APL_HID5 sys_reg(3, 0, 15, 5, 0)
+#define HID5_DISABLE_FILL_2C_MERGE BIT(61)
+
+#define SYS_IMP_APL_HID6 sys_reg(3, 0, 15, 6, 0)
+#define HID6_UP_CRD_TKN_INIT_C2(x) (((unsigned long)x) << 5)
+#define HID6_UP_CRD_TKN_INIT_C2_MASK (0x1FUL << 5)
+
+#define SYS_IMP_APL_HID7 sys_reg(3, 0, 15, 7, 0)
+#define HID7_FORCE_NONSPEC_IF_SPEC_FLUSH_POINTER_INVALID_AND_MP_VALID BIT(16)
+#define HID7_FORCE_NONSPEC_IF_STEPPING BIT(20)
+#define HID7_FORCE_NONSPEC_TARGET_TIMER_SEL(x) (((unsigned long)x) << 24)
+#define HID7_FORCE_NONSPEC_TARGET_TIMER_SEL_MASK (3UL << 24)
+
+#define SYS_IMP_APL_HID9 sys_reg(3, 0, 15, 9, 0)
+#define HID9_AVL_UNK17 BIT(17)
+#define HID9_TSO_ALLOW_DC_ZVA_WC BIT(26)
+#define HID9_TSO_SERIALIZE_VLD_MICROOPS BIT(29)
+#define HID9_FIX_BUG_51667805 BIT(48)
+#define HID9_FIX_BUG_55719865 BIT(55)
+
+#define SYS_IMP_APL_EHID9 sys_reg(3, 0, 15, 9, 1)
+#define EHID9_DEV_2_THROTTLE_ENABLE BIT(5)
+#define EHID9_DEV_2_THROTTLE_LIMIT_MASK GENMASK(11, 6)
+#define EHID9_DEV_2_THROTTLE_LIMIT(x) (((unsigned long)x) << 6)
+
+#define SYS_IMP_APL_HID10 sys_reg(3, 0, 15, 10, 0)
+#define SYS_IMP_APL_EHID10 sys_reg(3, 0, 15, 10, 1)
+#define HID10_FORCE_WAIT_STATE_DRAIN_UC BIT(32)
+#define HID10_DISABLE_ZVA_TEMPORAL_TSO BIT(49)
+
+#define SYS_IMP_APL_HID11 sys_reg(3, 0, 15, 11, 0)
+#define HID11_ENABLE_FIX_UC_55719865 BIT(15)
+#define HID11_DISABLE_LD_NT_WIDGET BIT(59)
+
+#define SYS_IMP_APL_HID13 sys_reg(3, 0, 15, 14, 0)
+#define HID13_POST_OFF_CYCLES(x) (((unsigned long)x))
+#define HID13_POST_OFF_CYCLES_MASK GENMASK(6, 0)
+#define HID13_POST_ON_CYCLES(x) (((unsigned long)x) << 7)
+#define HID13_POST_ON_CYCLES_MASK GENMASK(13, 7)
+#define HID13_PRE_CYCLES(x) (((unsigned long)x) << 14)
+#define HID13_PRE_CYCLES_MASK GENMASK(17, 14)
+#define HID13_GROUP0_FF1_DELAY(x) (((unsigned long)x) << 26)
+#define HID13_GROUP0_FF1_DELAY_MASK GENMASK(29, 26)
+#define HID13_GROUP0_FF2_DELAY(x) (((unsigned long)x) << 30)
+#define HID13_GROUP0_FF2_DELAY_MASK GENMASK(33, 30)
+#define HID13_GROUP0_FF3_DELAY(x) (((unsigned long)x) << 34)
+#define HID13_GROUP0_FF3_DELAY_MASK GENMASK(37, 34)
+#define HID13_GROUP0_FF4_DELAY(x) (((unsigned long)x) << 38)
+#define HID13_GROUP0_FF4_DELAY_MASK GENMASK(41, 38)
+#define HID13_GROUP0_FF5_DELAY(x) (((unsigned long)x) << 42)
+#define HID13_GROUP0_FF5_DELAY_MASK GENMASK(45, 42)
+#define HID13_GROUP0_FF6_DELAY(x) (((unsigned long)x) << 46)
+#define HID13_GROUP0_FF6_DELAY_MASK GENMASK(49, 46)
+#define HID13_GROUP0_FF7_DELAY(x) (((unsigned long)x) << 50)
+#define HID13_GROUP0_FF7_DELAY_MASK GENMASK(53, 50)
+#define HID13_RESET_CYCLES(x) (((unsigned long)x) << 60)
+#define HID13_RESET_CYCLES_MASK (0xFUL << 60)
+
+#define SYS_IMP_APL_HID16 sys_reg(3, 0, 15, 15, 2)
+#define HID16_AVL_UNK12 BIT(12)
+#define HID16_SPAREBIT0 BIT(56)
+#define HID16_SPAREBIT3 BIT(59)
+#define HID16_ENABLE_MPX_PICK_45 BIT(61)
+#define HID16_ENABLE_MP_CYCLONE_7 BIT(62)
+
+#define SYS_IMP_APL_HID18 sys_reg(3, 0, 15, 11, 2)
+#define HID18_HVC_SPECULATION_DISABLE BIT(14)
+#define HID18_AVL_UNK27 BIT(27)
+#define HID18_AVL_UNK29 BIT(29)
+#define HID18_SPAREBIT7 BIT(39)
+#define HID18_SPAREBIT17 BIT(49)
+
+#define SYS_IMP_APL_EHID18 sys_reg(3, 0, 15, 11, 3)
+#define EHID18_BLZ_UNK34 BIT(34)
+
+#define SYS_IMP_APL_EHID20 sys_reg(3, 0, 15, 1, 2)
+#define EHID20_TRAP_SMC BIT(8)
+#define EHID20_FORCE_NONSPEC_IF_OLDEST_REDIR_VALID_AND_OLDER BIT(15)
+#define EHID20_FORCE_NONSPEC_IF_SPEC_FLUSH_POINTER_NE_BLK_RTR_POINTER BIT(16)
+#define EHID20_FORCE_NONSPEC_TARGETED_TIMER_SEL(x) (((unsigned long)x) << 21)
+#define EHID20_FORCE_NONSPEC_TARGETED_TIMER_SEL_MASK (3UL << 21)
+
+#define SYS_IMP_APL_HID21 sys_reg(3, 0, 15, 1, 3)
+#define HID21_ENABLE_LDREX_FILL_REPLY BIT(19)
+#define HID21_LDQ_RTR_WAIT_FOR_OLD_ST_REL_COMPLETION BIT(33)
+#define HID21_DISABLE_CDP_REPLY_PURGED_TRANSACTION BIT(34)
+#define HID21_AVL_UNK52 BIT(52)
+
+#define SYS_IMP_APL_HID26 sys_reg(3, 0, 15, 0, 3)
+#define HID26_GROUP1_OFFSET(x) (((unsigned long)x) << 0)
+#define HID26_GROUP1_OFFSET_MASK (0xffUL << 0)
+#define HID26_GROUP2_OFFSET(x) (((unsigned long)x) << 36)
+#define HID26_GROUP2_OFFSET_MASK (0xffUL << 36)
+
+#define SYS_IMP_APL_HID27 sys_reg(3, 0, 15, 0, 4)
+#define HID27_GROUP3_OFFSET(x) (((unsigned long)x) << 8)
+#define HID27_GROUP3_OFFSET_MASK (0xffUL << 8)
+
+#define SYS_IMP_APL_PMCR0 sys_reg(3, 1, 15, 0, 0)
+#define PMCR0_CNT_EN_MASK (MASK(8) | GENMASK(33, 32))
+#define PMCR0_IMODE_OFF (0 << 8)
+#define PMCR0_IMODE_PMI (1 << 8)
+#define PMCR0_IMODE_AIC (2 << 8)
+#define PMCR0_IMODE_HALT (3 << 8)
+#define PMCR0_IMODE_FIQ (4 << 8)
+#define PMCR0_IMODE_MASK (7 << 8)
+#define PMCR0_IACT (BIT(11))
+#define PMCR0_PMI_SHIFT 12
+#define PMCR0_CNT_MASK (PMCR0_CNT_EN_MASK | (PMCR0_CNT_EN_MASK << PMCR0_PMI_SHIFT))
+
+#define SYS_IMP_APL_PMCR1 sys_reg(3, 1, 15, 1, 0)
+#define SYS_IMP_APL_PMCR2 sys_reg(3, 1, 15, 2, 0)
+#define SYS_IMP_APL_PMCR3 sys_reg(3, 1, 15, 3, 0)
+#define SYS_IMP_APL_PMCR4 sys_reg(3, 1, 15, 4, 0)
+
+#define SYS_IMP_APL_PMESR0 sys_reg(3, 1, 15, 5, 0)
+#define SYS_IMP_APL_PMESR1 sys_reg(3, 1, 15, 6, 0)
+
+#define SYS_IMP_APL_PMSR sys_reg(3, 1, 15, 13, 0)
+
+#define SYS_IMP_APL_PMC0 sys_reg(3, 2, 15, 0, 0)
+#define SYS_IMP_APL_PMC1 sys_reg(3, 2, 15, 1, 0)
+#define SYS_IMP_APL_PMC2 sys_reg(3, 2, 15, 2, 0)
+#define SYS_IMP_APL_PMC3 sys_reg(3, 2, 15, 3, 0)
+#define SYS_IMP_APL_PMC4 sys_reg(3, 2, 15, 4, 0)
+#define SYS_IMP_APL_PMC5 sys_reg(3, 2, 15, 5, 0)
+#define SYS_IMP_APL_PMC6 sys_reg(3, 2, 15, 6, 0)
+#define SYS_IMP_APL_PMC7 sys_reg(3, 2, 15, 7, 0)
+#define SYS_IMP_APL_PMC8 sys_reg(3, 2, 15, 9, 0)
+#define SYS_IMP_APL_PMC9 sys_reg(3, 2, 15, 10, 0)
+
+#define SYS_IMP_APL_LSU_ERR_STS sys_reg(3, 3, 15, 0, 0)
+#define SYS_IMP_APL_E_LSU_ERR_STS sys_reg(3, 3, 15, 2, 0)
+
+#define SYS_IMP_APL_L2C_ERR_STS sys_reg(3, 3, 15, 8, 0)
+
+#define L2C_ERR_STS_RECURSIVE_FAULT BIT(1)
+#define L2C_ERR_STS_ACCESS_FAULT BIT(7)
+#define L2C_ERR_STS_ENABLE_W1C BIT(56)
+
+#define SYS_IMP_APL_L2C_ERR_ADR sys_reg(3, 3, 15, 9, 0)
+#define SYS_IMP_APL_L2C_ERR_INF sys_reg(3, 3, 15, 10, 0)
+
+#define SYS_IMP_APL_FED_ERR_STS sys_reg(3, 4, 15, 0, 0)
+#define SYS_IMP_APL_E_FED_ERR_STS sys_reg(3, 4, 15, 0, 2)
+
+#define SYS_IMP_APL_MMU_ERR_STS sys_reg(3, 6, 15, 0, 0)
+#define SYS_IMP_APL_E_MMU_ERR_STS sys_reg(3, 6, 15, 2, 0)
+
+/* ACC/CYC Registers */
+#define SYS_IMP_APL_ACC_CFG sys_reg(3, 5, 15, 4, 0)
+#define ACC_CFG_BP_SLEEP(x) (((unsigned long)x) << 2)
+#define ACC_CFG_BP_SLEEP_MASK (3UL << 2)
+
+#define SYS_IMP_APL_CYC_OVRD sys_reg(3, 5, 15, 5, 0)
+#define CYC_OVRD_FIQ_MODE(x) (((unsigned long)x) << 20)
+#define CYC_OVRD_FIQ_MODE_MASK (3UL << 20)
+#define CYC_OVRD_IRQ_MODE(x) (((unsigned long)x) << 22)
+#define CYC_OVRD_IRQ_MODE_MASK (3UL << 22)
+#define CYC_OVRD_WFI_MODE(x) (((unsigned long)x) << 24)
+#define CYC_OVRD_WFI_MODE_MASK (3UL << 24)
+#define CYC_OVRD_DISABLE_WFI_RET BIT(0)
+
+#define SYS_IMP_APL_UPMCR0 sys_reg(3, 7, 15, 0, 4)
+#define UPMCR0_IMODE_OFF (0 << 16)
+#define UPMCR0_IMODE_AIC (2 << 16)
+#define UPMCR0_IMODE_HALT (3 << 16)
+#define UPMCR0_IMODE_FIQ (4 << 16)
+#define UPMCR0_IMODE_MASK (7 << 16)
+
+#define SYS_IMP_APL_UPMSR sys_reg(3, 7, 15, 6, 4)
+#define UPMSR_IACT (BIT(0))
+
+/* SPRR and GXF registers */
+#define SYS_IMP_APL_SPRR_CONFIG_EL1 sys_reg(3, 6, 15, 1, 0)
+#define SPRR_CONFIG_EN BIT(0)
+#define SPRR_CONFIG_LOCK_CONFIG BIT(1)
+#define SPRR_CONFIG_LOCK_PERM BIT(4)
+#define SPRR_CONFIG_LOCK_KERNEL_PERM BIT(5)
+
+#define SYS_IMP_APL_GXF_CONFIG_EL1 sys_reg(3, 6, 15, 1, 2)
+#define GXF_CONFIG_EN BIT(0)
+
+#define SYS_IMP_APL_GXF_STATUS_EL1 sys_reg(3, 6, 15, 8, 0)
+#define GXF_STATUS_GUARDED BIT(0)
+
+#define SYS_IMP_APL_GXF_ABORT_EL1 sys_reg(3, 6, 15, 8, 2)
+#define SYS_IMP_APL_GXF_ENTER_EL1 sys_reg(3, 6, 15, 8, 1)
+
+#define SYS_IMP_APL_GXF_ABORT_EL12 sys_reg(3, 6, 15, 15, 3)
+#define SYS_IMP_APL_GXF_ENTER_EL12 sys_reg(3, 6, 15, 15, 2)
+
+#define SYS_IMP_APL_SPRR_PERM_EL0 sys_reg(3, 6, 15, 1, 5)
+#define SYS_IMP_APL_SPRR_PERM_EL1 sys_reg(3, 6, 15, 1, 6)
+#define SYS_IMP_APL_SPRR_PERM_EL02 sys_reg(3, 4, 15, 5, 2)
+#define SYS_IMP_APL_SPRR_PERM_EL12 sys_reg(3, 6, 15, 15, 7)
+
+#define SYS_IMP_APL_TPIDR_GL1 sys_reg(3, 6, 15, 10, 1)
+#define SYS_IMP_APL_VBAR_GL1 sys_reg(3, 6, 15, 10, 2)
+#define SYS_IMP_APL_SPSR_GL1 sys_reg(3, 6, 15, 10, 3)
+#define SYS_IMP_APL_ASPSR_GL1 sys_reg(3, 6, 15, 10, 4)
+#define SYS_IMP_APL_ESR_GL1 sys_reg(3, 6, 15, 10, 5)
+#define SYS_IMP_APL_ELR_GL1 sys_reg(3, 6, 15, 10, 6)
+#define SYS_IMP_APL_FAR_GL1 sys_reg(3, 6, 15, 10, 7)
+
+#define SYS_IMP_APL_VBAR_GL12 sys_reg(3, 6, 15, 9, 2)
+#define SYS_IMP_APL_SPSR_GL12 sys_reg(3, 6, 15, 9, 3)
+#define SYS_IMP_APL_ASPSR_GL12 sys_reg(3, 6, 15, 9, 4)
+#define SYS_IMP_APL_ESR_GL12 sys_reg(3, 6, 15, 9, 5)
+#define SYS_IMP_APL_ELR_GL12 sys_reg(3, 6, 15, 9, 6)
+#define SYS_IMP_APL_SP_GL12 sys_reg(3, 6, 15, 10, 0)
+
+#define SYS_IMP_APL_AFSR1_GL1 sys_reg(3, 6, 15, 0, 1)
+
+/* PAuth registers */
+#define SYS_IMP_APL_APVMKEYLO_EL2 sys_reg(3, 6, 15, 14, 4)
+#define SYS_IMP_APL_APVMKEYHI_EL2 sys_reg(3, 6, 15, 14, 5)
+#define SYS_IMP_APL_APSTS_EL12 sys_reg(3, 6, 15, 14, 7)
+
+#define SYS_IMP_APL_APCTL_EL1 sys_reg(3, 4, 15, 0, 4)
+#define SYS_IMP_APL_APCTL_EL2 sys_reg(3, 6, 15, 12, 2)
+#define SYS_IMP_APL_APCTL_EL12 sys_reg(3, 6, 15, 15, 0)
+
+/* VM registers */
+#define SYS_IMP_APL_VM_TMR_FIQ_ENA_EL2 sys_reg(3, 5, 15, 1, 3)
+#define VM_TMR_FIQ_ENA_ENA_V BIT(0)
+#define VM_TMR_FIQ_ENA_ENA_P BIT(1)
+
+/* IPI registers */
+#define SYS_IMP_APL_IPI_RR_LOCAL_EL1 sys_reg(3, 5, 15, 0, 0)
+#define SYS_IMP_APL_IPI_RR_GLOBAL_EL1 sys_reg(3, 5, 15, 0, 1)
+
+#define SYS_IMP_APL_IPI_SR_EL1 sys_reg(3, 5, 15, 1, 1)
+#define IPI_SR_PENDING BIT(0)
+
+#define SYS_IMP_APL_IPI_CR_EL1 sys_reg(3, 5, 15, 3, 1)
diff --git a/tools/src/cpufreq.c b/tools/src/cpufreq.c
new file mode 100644
index 0000000..e7c4f41
--- /dev/null
+++ b/tools/src/cpufreq.c
@@ -0,0 +1,120 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "cpufreq.h"
+#include "adt.h"
+#include "soc.h"
+#include "utils.h"
+
+#define CLUSTER_PSTATE 0x20
+#define CLUSTER_CONFIG 0x6b8
+
+#define CLUSTER_PSTATE_BUSY BIT(31)
+#define CLUSTER_PSTATE_SET BIT(25)
+#define CLUSTER_PSTATE_DESIRED2 GENMASK(15, 12)
+#define CLUSTER_PSTATE_DESIRED1 GENMASK(3, 0)
+
+#define CLUSTER_CONFIG_ENABLE BIT(63)
+#define CLUSTER_CONFIG_DVMR1 BIT(32)
+#define CLUSTER_CONFIG_DVMR2 BIT(31)
+
+#define CLUSTER_SWITCH_TIMEOUT 100
+
+struct cluster_t {
+ const char *name;
+ u64 base;
+ bool dvmr;
+ uint32_t boot_pstate;
+};
+
+int cpufreq_init_cluster(const struct cluster_t *cluster)
+{
+ u64 enable = CLUSTER_CONFIG_ENABLE;
+ if (cluster->dvmr)
+ enable |= CLUSTER_CONFIG_DVMR1 | CLUSTER_CONFIG_DVMR2;
+
+ u64 val = read64(cluster->base + CLUSTER_CONFIG);
+ if ((val & enable) != enable) {
+ printf("cpufreq: Configuring cluster %s (dvmr: %d)\n", cluster->name, cluster->dvmr);
+ write64(cluster->base + CLUSTER_CONFIG, val | enable);
+ }
+
+ val = read64(cluster->base + CLUSTER_PSTATE);
+
+ if (FIELD_GET(CLUSTER_PSTATE_DESIRED1, val) != cluster->boot_pstate) {
+ val &= CLUSTER_PSTATE_DESIRED1 | CLUSTER_PSTATE_DESIRED2;
+ val |= CLUSTER_PSTATE_SET | FIELD_PREP(CLUSTER_PSTATE_DESIRED1, cluster->boot_pstate) |
+ FIELD_PREP(CLUSTER_PSTATE_DESIRED2, cluster->boot_pstate);
+ printf("cpufreq: Switching cluster %s to P-State %d\n", cluster->name,
+ cluster->boot_pstate);
+ write64(cluster->base + CLUSTER_PSTATE, val);
+ if (poll32(cluster->base + CLUSTER_PSTATE, CLUSTER_PSTATE_BUSY, 0, CLUSTER_SWITCH_TIMEOUT) <
+ 0) {
+ printf("cpufreq: Timed out waiting for cluster %s P-State switch\n", cluster->name);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static const struct cluster_t t8103_clusters[] = {
+ {"ECPU", 0x210e20000, false, 5},
+ {"PCPU", 0x211e20000, true, 7},
+ {},
+};
+
+static const struct cluster_t t6000_clusters[] = {
+ {"ECPU0", 0x210e20000, false, 5},
+ {"PCPU0", 0x211e20000, false, 7},
+ {"PCPU1", 0x212e20000, false, 7},
+ {},
+};
+
+static const struct cluster_t t6002_clusters[] = {
+ {"ECPU0", 0x0210e20000, false, 5},
+ {"PCPU0", 0x0211e20000, false, 7},
+ {"PCPU1", 0x0212e20000, false, 7},
+ {"ECPU1", 0x2210e20000, false, 5},
+ {"PCPU2", 0x2211e20000, false, 7},
+ {"PCPU3", 0x2212e20000, false, 7},
+ {},
+};
+
+static const struct cluster_t t8112_clusters[] = {
+ {"ECPU", 0x210e20000, false, 7},
+ {"PCPU", 0x211e20000, true, 6},
+ {},
+};
+
+int cpufreq_init(void)
+{
+ printf("cpufreq: Initializing clusters\n");
+
+ const struct cluster_t *cluster;
+
+ switch (chip_id) {
+ case T8103:
+ cluster = t8103_clusters;
+ break;
+ case T6000:
+ case T6001:
+ cluster = t6000_clusters;
+ break;
+ case T6002:
+ cluster = t6002_clusters;
+ break;
+ case T8112:
+ cluster = t8112_clusters;
+ break;
+ default:
+ printf("cpufreq: Chip 0x%x is unsupported\n", chip_id);
+ return -1;
+ }
+
+ bool err = false;
+ while (cluster->base) {
+ err |= cpufreq_init_cluster(cluster++);
+ }
+
+ return err ? -1 : 0;
+}
diff --git a/tools/src/cpufreq.h b/tools/src/cpufreq.h
new file mode 100644
index 0000000..7710f20
--- /dev/null
+++ b/tools/src/cpufreq.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef CPUFREQ_H
+#define CPUFREQ_H
+
+int cpufreq_init(void);
+
+#endif
diff --git a/tools/src/dapf.c b/tools/src/dapf.c
new file mode 100644
index 0000000..cbeb576
--- /dev/null
+++ b/tools/src/dapf.c
@@ -0,0 +1,137 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "dapf.h"
+#include "adt.h"
+#include "assert.h"
+#include "malloc.h"
+#include "memory.h"
+#include "string.h"
+#include "utils.h"
+
+struct dapf_t8020_config {
+ u64 start;
+ u64 end;
+ u8 unk1;
+ u8 r0_hi;
+ u8 r0_lo;
+ u8 unk2;
+ u32 r4;
+} PACKED;
+
+static int dapf_init_t8020(const char *path, u64 base, int node)
+{
+ u32 length;
+ const char *prop = "filter-data-instance-0";
+ const struct dapf_t8020_config *config = adt_getprop(adt, node, prop, &length);
+
+ if (!config || !length || (length % sizeof(*config)) != 0) {
+ printf("dapf: Error getting ADT node %s property %s.\n", path, prop);
+ return -1;
+ }
+
+ int count = length / sizeof(*config);
+
+ for (int i = 0; i < count; i++) {
+ write32(base + 0x04, config[i].r4);
+ write64(base + 0x08, config[i].start);
+ write64(base + 0x10, config[i].end);
+ write32(base + 0x00, (config[i].r0_hi << 4) | config[i].r0_lo);
+ base += 0x40;
+ }
+ return 0;
+}
+
+struct dapf_t8110_config {
+ u64 start;
+ u64 end;
+ u32 r20;
+ u32 unk1;
+ u32 r4;
+ u32 unk2[5];
+ u8 unk3;
+ u8 r0_hi;
+ u8 r0_lo;
+ u8 unk4;
+} PACKED;
+
+static int dapf_init_t8110(const char *path, u64 base, int node)
+{
+ u32 length;
+ const char *prop = "dapf-instance-0";
+ const struct dapf_t8110_config *config = adt_getprop(adt, node, prop, &length);
+
+ if (!config || !length) {
+ printf("dapf: Error getting ADT node %s property %s.\n", path, prop);
+ return -1;
+ }
+
+ if (length % sizeof(*config) != 0) {
+ printf("dapf: Invalid length for %s property %s\n", path, prop);
+ return -1;
+ }
+
+ int count = length / sizeof(*config);
+
+ for (int i = 0; i < count; i++) {
+ write32(base + 0x04, config[i].r4);
+ write64(base + 0x08, config[i].start);
+ write64(base + 0x10, config[i].end);
+ write32(base + 0x00, (config[i].r0_hi << 4) | config[i].r0_lo);
+ write32(base + 0x20, config[i].r20);
+ base += 0x40;
+ }
+ return 0;
+}
+
+int dapf_init(const char *path)
+{
+ int ret;
+ int dart_path[8];
+ int node = adt_path_offset_trace(adt, path, dart_path);
+ if (node < 0) {
+ printf("dapf: Error getting DAPF %s node.\n", path);
+ return -1;
+ }
+
+ u64 base;
+ if (adt_get_reg(adt, dart_path, "reg", 1, &base, NULL) < 0) {
+ printf("dapf: Error getting DAPF %s base address.\n", path);
+ return -1;
+ }
+
+ if (adt_is_compatible(adt, node, "dart,t8020")) {
+ ret = dapf_init_t8020(path, base, node);
+ } else if (adt_is_compatible(adt, node, "dart,t6000")) {
+ ret = dapf_init_t8020(path, base, node);
+ } else if (adt_is_compatible(adt, node, "dart,t8110")) {
+ ret = dapf_init_t8110(path, base, node);
+ } else {
+ printf("dapf: DAPF %s at 0x%lx is of an unknown type\n", path, base);
+ return -1;
+ }
+
+ if (!ret)
+ printf("dapf: Initialized %s\n", path);
+
+ return ret;
+}
+
+const char *dapf_paths[] = {"/arm-io/dart-aop", "/arm-io/dart-mtp", "/arm-io/dart-pmp", NULL};
+
+int dapf_init_all(void)
+{
+ int ret = 0;
+ int count = 0;
+
+ for (const char **path = dapf_paths; *path; path++) {
+ if (adt_path_offset(adt, *path) < 0)
+ continue;
+
+ if (dapf_init(*path) < 0) {
+ ret = -1;
+ }
+ count += 1;
+ }
+
+ return ret ? ret : count;
+}
diff --git a/tools/src/dapf.h b/tools/src/dapf.h
new file mode 100644
index 0000000..2a7e1bf
--- /dev/null
+++ b/tools/src/dapf.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef DAPF_H
+#define DAPF_H
+
+int dapf_init_all(void);
+int dapf_init(const char *path);
+
+#endif
diff --git a/tools/src/dart.c b/tools/src/dart.c
new file mode 100644
index 0000000..96c4261
--- /dev/null
+++ b/tools/src/dart.c
@@ -0,0 +1,714 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "dart.h"
+#include "adt.h"
+#include "assert.h"
+#include "devicetree.h"
+#include "malloc.h"
+#include "memory.h"
+#include "string.h"
+#include "utils.h"
+
+#include "libfdt/libfdt.h"
+
+#define DART_T8020_CONFIG 0x60
+#define DART_T8020_CONFIG_LOCK BIT(15)
+
+#define DART_T8020_ERROR 0x40
+#define DART_T8020_ERROR_STREAM_SHIFT 24
+#define DART_T8020_ERROR_STREAM_MASK 0xf
+#define DART_T8020_ERROR_CODE_MASK 0xffffff
+#define DART_T8020_ERROR_FLAG BIT(31)
+#define DART_T8020_ERROR_READ_FAULT BIT(4)
+#define DART_T8020_ERROR_WRITE_FAULT BIT(3)
+#define DART_T8020_ERROR_NO_PTE BIT(2)
+#define DART_T8020_ERROR_NO_PMD BIT(1)
+#define DART_T8020_ERROR_NO_TTBR BIT(0)
+
+#define DART_T8020_STREAM_SELECT 0x34
+
+#define DART_T8020_STREAM_COMMAND 0x20
+#define DART_T8020_STREAM_COMMAND_BUSY BIT(2)
+#define DART_T8020_STREAM_COMMAND_INVALIDATE BIT(20)
+
+#define DART_T8020_STREAM_COMMAND_BUSY_TIMEOUT 100
+
+#define DART_T8020_STREAM_REMAP 0x80
+
+#define DART_T8020_ERROR_ADDR_HI 0x54
+#define DART_T8020_ERROR_ADDR_LO 0x50
+
+#define DART_T8020_ENABLED_STREAMS 0xfc
+
+#define DART_T8020_TCR_OFF 0x100
+#define DART_T8020_TCR_TRANSLATE_ENABLE BIT(7)
+#define DART_T8020_TCR_BYPASS_DART BIT(8)
+#define DART_T8020_TCR_BYPASS_DAPF BIT(12)
+
+#define DART_T8020_TTBR_OFF 0x200
+#define DART_T8020_TTBR_VALID BIT(31)
+#define DART_T8020_TTBR_ADDR GENMASK(30, 0)
+#define DART_T8020_TTBR_SHIFT 12
+
+#define DART_PTE_OFFSET_SHIFT 14
+#define DART_PTE_SP_START GENMASK(63, 52)
+#define DART_PTE_SP_END GENMASK(51, 40)
+#define DART_T8020_PTE_OFFSET GENMASK(39, 14)
+#define DART_T6000_PTE_OFFSET GENMASK(39, 10)
+#define DART_T8020_PTE_DISABLE_SP BIT(1)
+#define DART_T6000_PTE_REALTIME BIT(1)
+#define DART_PTE_VALID BIT(0)
+
+#define DART_T8110_TTBR_OFF 0x1400
+#define DART_T8110_TTBR_VALID BIT(0)
+#define DART_T8110_TTBR_ADDR GENMASK(29, 2)
+#define DART_T8110_TTBR_SHIFT 14
+
+#define DART_T8110_TCR_OFF 0x1000
+#define DART_T8110_TCR_REMAP GENMASK(11, 8)
+#define DART_T8110_TCR_REMAP_EN BIT(7)
+#define DART_T8110_TCR_BYPASS_DAPF BIT(2)
+#define DART_T8110_TCR_BYPASS_DART BIT(1)
+#define DART_T8110_TCR_TRANSLATE_ENABLE BIT(0)
+
+#define DART_T8110_TLB_CMD 0x80
+#define DART_T8110_TLB_CMD_BUSY BIT(31)
+#define DART_T8110_TLB_CMD_OP GENMASK(10, 8)
+#define DART_T8110_TLB_CMD_OP_FLUSH_ALL 0
+#define DART_T8110_TLB_CMD_OP_FLUSH_SID 1
+#define DART_T8110_TLB_CMD_STREAM GENMASK(7, 0)
+
+#define DART_T8110_PROTECT 0x200
+#define DART_T8110_PROTECT_TTBR_TCR BIT(0)
+
+#define DART_T8110_ENABLE_STREAMS 0xc00
+#define DART_T8110_DISABLE_STREAMS 0xc20
+
+#define DART_MAX_TTBR_COUNT 4
+
+#define DART_TCR(dart) (dart->regs + dart->params->tcr_off + 4 * dart->device)
+#define DART_TTBR(dart, idx) \
+ (dart->regs + dart->params->ttbr_off + 4 * dart->params->ttbr_count * dart->device + 4 * idx)
+
+struct dart_params {
+ int sid_count;
+
+ u64 pte_flags;
+ u64 offset_mask;
+
+ u64 tcr_enabled;
+ u64 tcr_disabled;
+ u64 tcr_off;
+
+ u64 ttbr_valid;
+ u64 ttbr_addr;
+ u64 ttbr_shift;
+ u64 ttbr_off;
+ int ttbr_count;
+
+ void (*tlb_invalidate)(dart_dev_t *dart);
+};
+
+struct dart_dev {
+ bool locked;
+ bool keep;
+ uintptr_t regs;
+ u8 device;
+ enum dart_type_t type;
+ const struct dart_params *params;
+ u64 vm_base;
+
+ u64 *l1[DART_MAX_TTBR_COUNT];
+};
+
+static void dart_t8020_tlb_invalidate(dart_dev_t *dart)
+{
+ write32(dart->regs + DART_T8020_STREAM_SELECT, BIT(dart->device));
+
+ /* ensure that the DART can see the updated pagetables before invalidating */
+ dma_wmb();
+ write32(dart->regs + DART_T8020_STREAM_COMMAND, DART_T8020_STREAM_COMMAND_INVALIDATE);
+
+ if (poll32(dart->regs + DART_T8020_STREAM_COMMAND, DART_T8020_STREAM_COMMAND_BUSY, 0, 100))
+ printf("dart: DART_T8020_STREAM_COMMAND_BUSY did not clear.\n");
+}
+
+static void dart_t8110_tlb_invalidate(dart_dev_t *dart)
+{
+ /* ensure that the DART can see the updated pagetables before invalidating */
+ dma_wmb();
+ write32(dart->regs + DART_T8110_TLB_CMD,
+ FIELD_PREP(DART_T8110_TLB_CMD_OP, DART_T8110_TLB_CMD_OP_FLUSH_SID) |
+ FIELD_PREP(DART_T8110_TLB_CMD_STREAM, dart->device));
+
+ if (poll32(dart->regs + DART_T8110_TLB_CMD_OP, DART_T8110_TLB_CMD_BUSY, 0, 100))
+ printf("dart: DART_T8110_TLB_CMD_BUSY did not clear.\n");
+}
+
+const struct dart_params dart_t8020 = {
+ .sid_count = 32,
+ .pte_flags = FIELD_PREP(DART_PTE_SP_END, 0xfff) | FIELD_PREP(DART_PTE_SP_START, 0) |
+ DART_T8020_PTE_DISABLE_SP | DART_PTE_VALID,
+ .offset_mask = DART_T8020_PTE_OFFSET,
+ .tcr_enabled = DART_T8020_TCR_TRANSLATE_ENABLE,
+ .tcr_disabled = DART_T8020_TCR_BYPASS_DAPF | DART_T8020_TCR_BYPASS_DART,
+ .tcr_off = DART_T8020_TCR_OFF,
+ .ttbr_valid = DART_T8020_TTBR_VALID,
+ .ttbr_addr = DART_T8020_TTBR_ADDR,
+ .ttbr_shift = DART_T8020_TTBR_SHIFT,
+ .ttbr_off = DART_T8020_TTBR_OFF,
+ .ttbr_count = 4,
+ .tlb_invalidate = dart_t8020_tlb_invalidate,
+};
+
+const struct dart_params dart_t6000 = {
+ .sid_count = 32,
+ .pte_flags =
+ FIELD_PREP(DART_PTE_SP_END, 0xfff) | FIELD_PREP(DART_PTE_SP_START, 0) | DART_PTE_VALID,
+ .offset_mask = DART_T6000_PTE_OFFSET,
+ .tcr_enabled = DART_T8020_TCR_TRANSLATE_ENABLE,
+ .tcr_disabled = DART_T8020_TCR_BYPASS_DAPF | DART_T8020_TCR_BYPASS_DART,
+ .tcr_off = DART_T8020_TCR_OFF,
+ .ttbr_valid = DART_T8020_TTBR_VALID,
+ .ttbr_addr = DART_T8020_TTBR_ADDR,
+ .ttbr_shift = DART_T8020_TTBR_SHIFT,
+ .ttbr_off = DART_T8020_TTBR_OFF,
+ .ttbr_count = 4,
+ .tlb_invalidate = dart_t8020_tlb_invalidate,
+};
+
+const struct dart_params dart_t8110 = {
+ .sid_count = 256,
+ .pte_flags =
+ FIELD_PREP(DART_PTE_SP_END, 0xfff) | FIELD_PREP(DART_PTE_SP_START, 0) | DART_PTE_VALID,
+ .offset_mask = DART_T6000_PTE_OFFSET,
+ .tcr_enabled = DART_T8110_TCR_TRANSLATE_ENABLE,
+ .tcr_disabled = DART_T8110_TCR_BYPASS_DAPF | DART_T8110_TCR_BYPASS_DART,
+ .tcr_off = DART_T8110_TCR_OFF,
+ .ttbr_valid = DART_T8110_TTBR_VALID,
+ .ttbr_addr = DART_T8110_TTBR_ADDR,
+ .ttbr_shift = DART_T8110_TTBR_SHIFT,
+ .ttbr_off = DART_T8110_TTBR_OFF,
+ .ttbr_count = 1,
+ .tlb_invalidate = dart_t8110_tlb_invalidate,
+};
+
+dart_dev_t *dart_init(uintptr_t base, u8 device, bool keep_pts, enum dart_type_t type)
+{
+ dart_dev_t *dart = malloc(sizeof(*dart));
+ if (!dart)
+ return NULL;
+
+ memset(dart, 0, sizeof(*dart));
+
+ dart->regs = base;
+ dart->device = device;
+ dart->type = type;
+
+ switch (type) {
+ case DART_T8020:
+ dart->params = &dart_t8020;
+ break;
+ case DART_T8110:
+ dart->params = &dart_t8110;
+ break;
+ case DART_T6000:
+ dart->params = &dart_t6000;
+ break;
+ }
+
+ if (device >= dart->params->sid_count) {
+ printf("dart: device %d is too big for this DART type\n", device);
+ free(dart);
+ return NULL;
+ }
+
+ switch (type) {
+ case DART_T8020:
+ case DART_T6000:
+ if (read32(dart->regs + DART_T8020_CONFIG) & DART_T8020_CONFIG_LOCK)
+ dart->locked = true;
+ set32(dart->regs + DART_T8020_ENABLED_STREAMS, BIT(device & 0x1f));
+ break;
+ case DART_T8110:
+ // TODO locked dart
+ write32(dart->regs + DART_T8110_ENABLE_STREAMS + 4 * (device >> 5), BIT(device & 0x1f));
+ break;
+ }
+
+ dart->keep = keep_pts;
+
+ if (dart->locked || keep_pts) {
+ for (int i = 0; i < dart->params->ttbr_count; i++) {
+ u32 ttbr = read32(DART_TTBR(dart, i));
+ if (ttbr & dart->params->ttbr_valid)
+ dart->l1[i] =
+ (u64 *)(FIELD_GET(dart->params->ttbr_addr, ttbr) << dart->params->ttbr_shift);
+ }
+ }
+
+ for (int i = 0; i < dart->params->ttbr_count; i++) {
+ if (dart->l1[i])
+ continue;
+
+ dart->l1[i] = memalign(SZ_16K, SZ_16K);
+ if (!dart->l1[i])
+ goto error;
+ memset(dart->l1[i], 0, SZ_16K);
+
+ write32(DART_TTBR(dart, i),
+ dart->params->ttbr_valid |
+ FIELD_PREP(dart->params->ttbr_addr,
+ ((uintptr_t)dart->l1[i]) >> dart->params->ttbr_shift));
+ }
+
+ if (!dart->locked && !keep_pts)
+ write32(DART_TCR(dart), dart->params->tcr_enabled);
+
+ dart->params->tlb_invalidate(dart);
+ return dart;
+
+error:
+ if (!dart->locked)
+ free(dart->l1);
+ free(dart);
+ return NULL;
+}
+
+dart_dev_t *dart_init_adt(const char *path, int instance, int device, bool keep_pts)
+{
+ int dart_path[8];
+ int node = adt_path_offset_trace(adt, path, dart_path);
+ if (node < 0) {
+ printf("dart: Error getting DART node %s\n", path);
+ return NULL;
+ }
+
+ u64 base;
+ if (adt_get_reg(adt, dart_path, "reg", instance, &base, NULL) < 0) {
+ printf("dart: Error getting DART %s base address.\n", path);
+ return NULL;
+ }
+
+ enum dart_type_t type;
+ const char *type_s;
+
+ if (adt_is_compatible(adt, node, "dart,t8020")) {
+ type = DART_T8020;
+ type_s = "t8020";
+ } else if (adt_is_compatible(adt, node, "dart,t6000")) {
+ type = DART_T6000;
+ type_s = "t6000";
+ } else if (adt_is_compatible(adt, node, "dart,t8110")) {
+ type = DART_T8110;
+ type_s = "t8110";
+ } else {
+ printf("dart: dart %s at 0x%lx is of an unknown type\n", path, base);
+ return NULL;
+ }
+
+ dart_dev_t *dart = dart_init(base, device, keep_pts, type);
+
+ if (!dart)
+ return NULL;
+
+ printf("dart: dart %s at 0x%lx is a %s%s\n", path, base, type_s,
+ dart->locked ? " (locked)" : "");
+
+ if (adt_getprop(adt, node, "real-time", NULL)) {
+ for (int i = 0; i < dart->params->ttbr_count; i++) {
+ printf("dart: dart %s.%d.%d L1 %d is real-time at %p\n", path, instance, device, i,
+ dart->l1[i]);
+ }
+ }
+ if (ADT_GETPROP(adt, node, "vm-base", &dart->vm_base) < 0)
+ dart->vm_base = 0;
+
+ return dart;
+}
+
+void dart_lock_adt(const char *path, int instance)
+{
+ int dart_path[8];
+ int node = adt_path_offset_trace(adt, path, dart_path);
+ if (node < 0) {
+ printf("dart: Error getting DART node %s\n", path);
+ return;
+ }
+
+ u64 base;
+ if (adt_get_reg(adt, dart_path, "reg", instance, &base, NULL) < 0) {
+ printf("dart: Error getting DART %s base address.\n", path);
+ return;
+ }
+
+ if (adt_is_compatible(adt, node, "dart,t8020") || adt_is_compatible(adt, node, "dart,t6000")) {
+ if (!(read32(base + DART_T8020_CONFIG) & DART_T8020_CONFIG_LOCK))
+ set32(base + DART_T8020_CONFIG, DART_T8020_CONFIG_LOCK);
+ } else if (adt_is_compatible(adt, node, "dart,t8110")) {
+ if (!(read32(base + DART_T8110_PROTECT) & DART_T8110_PROTECT_TTBR_TCR))
+ set32(base + DART_T8110_PROTECT, DART_T8110_PROTECT_TTBR_TCR);
+ } else {
+ printf("dart: dart %s at 0x%lx is of an unknown type\n", path, base);
+ }
+}
+
+dart_dev_t *dart_init_fdt(void *dt, u32 phandle, int device, bool keep_pts)
+{
+ int node = fdt_node_offset_by_phandle(dt, phandle);
+ if (node < 0) {
+ printf("FDT: node for phandle %u not found\n", phandle);
+ return NULL;
+ }
+
+ u64 base = dt_get_address(dt, node);
+ if (!base)
+ return NULL;
+
+ enum dart_type_t type;
+ const char *type_s;
+ const char *name = fdt_get_name(dt, node, NULL);
+
+ if (fdt_node_check_compatible(dt, node, "apple,t8103-dart") == 0) {
+ type = DART_T8020;
+ type_s = "t8020";
+ } else if (fdt_node_check_compatible(dt, node, "apple,t6000-dart") == 0) {
+ type = DART_T6000;
+ type_s = "t6000";
+ } else if (fdt_node_check_compatible(dt, node, "apple,t8110-dart") == 0) {
+ type = DART_T8110;
+ type_s = "t8110";
+ } else {
+ printf("dart: dart %s at 0x%lx is of an unknown type\n", name, base);
+ return NULL;
+ }
+
+ dart_dev_t *dart = dart_init(base, device, keep_pts, type);
+
+ if (!dart)
+ return NULL;
+
+ printf("dart: dart %s at 0x%lx is a %s%s\n", name, base, type_s,
+ dart->locked ? " (locked)" : "");
+
+ return dart;
+}
+
+int dart_setup_pt_region(dart_dev_t *dart, const char *path, int device, u64 vm_base)
+{
+ int node = adt_path_offset(adt, path);
+ if (node < 0) {
+ printf("dart: Error getting DART node %s\n", path);
+ return -1;
+ }
+ char pt_region_str[24];
+ snprintf(pt_region_str, sizeof(pt_region_str), "pt-region-%d", device);
+ char l2_tt_str[24];
+ snprintf(l2_tt_str, sizeof(l2_tt_str), "l2-tt-%d", device);
+
+ const struct adt_property *pt_region = adt_get_property(adt, node, pt_region_str);
+ if (pt_region && pt_region->size == 16) {
+ u64 region[2];
+ memcpy(region, pt_region->value, sizeof(region));
+ u64 tbl_count = (region[1] - region[0]) / SZ_16K;
+ if (tbl_count > 64) {
+ printf("dart: dart %s ignoring large %s, %lu L2 tables\n", path, pt_region_str,
+ tbl_count);
+ return -1;
+ }
+ /* first index is the l1 table, cap at 2 or else macOS hates it */
+ tbl_count = min(2, tbl_count - 1);
+ u64 l2_start = region[0] + SZ_16K;
+ u64 vmstart = vm_base >> (14 + 11);
+ for (u64 index = 0; index < tbl_count; index++) {
+ int ttbr = (vmstart + index) >> 11;
+ int idx = (vmstart + index) & 0x7ff;
+ u64 l2tbl = l2_start + index * SZ_16K;
+
+ if (dart->l1[ttbr][idx] & DART_PTE_VALID) {
+ u64 off = FIELD_GET(dart->params->offset_mask, dart->l1[ttbr][idx])
+ << DART_PTE_OFFSET_SHIFT;
+ if (off != l2tbl)
+ printf("dart: unexpected L2 tbl at index:%lu. 0x%016lx != 0x%016lx\n", index,
+ off, l2tbl);
+ continue;
+ } else {
+ printf("dart: allocating L2 tbl at %d, %d to 0x%lx\n", ttbr, idx, l2tbl);
+ memset((void *)l2tbl, 0, SZ_16K);
+ }
+
+ u64 offset = FIELD_PREP(dart->params->offset_mask, l2tbl >> DART_PTE_OFFSET_SHIFT);
+ dart->l1[ttbr][idx] = offset | DART_PTE_VALID;
+ }
+
+ u64 l2_tt[2] = {region[0], tbl_count};
+ int ret = adt_setprop(adt, node, l2_tt_str, &l2_tt, sizeof(l2_tt));
+ if (ret < 0) {
+ printf("dart: failed to update '%s/%s'\n", path, l2_tt_str);
+ }
+
+ dart->params->tlb_invalidate(dart);
+ }
+
+ return 0;
+}
+
+static u64 *dart_get_l2(dart_dev_t *dart, u32 idx)
+{
+ int ttbr = idx >> 11;
+ idx &= 0x7ff;
+
+ if (dart->l1[ttbr][idx] & DART_PTE_VALID) {
+ u64 off = FIELD_GET(dart->params->offset_mask, dart->l1[ttbr][idx])
+ << DART_PTE_OFFSET_SHIFT;
+ return (u64 *)off;
+ }
+
+ u64 *tbl = memalign(SZ_16K, SZ_16K);
+ if (!tbl)
+ return NULL;
+
+ memset(tbl, 0, SZ_16K);
+
+ u64 offset = FIELD_PREP(dart->params->offset_mask, ((u64)tbl) >> DART_PTE_OFFSET_SHIFT);
+
+ dart->l1[ttbr][idx] = offset | DART_PTE_VALID;
+
+ return tbl;
+}
+
+static int dart_map_page(dart_dev_t *dart, uintptr_t iova, uintptr_t paddr)
+{
+ u32 l1_index = (iova >> 25) & 0x1fff;
+ u32 l2_index = (iova >> 14) & 0x7ff;
+
+ u64 *l2 = dart_get_l2(dart, l1_index);
+ if (!l2) {
+ printf("dart: couldn't create l2 for iova %lx\n", iova);
+ return -1;
+ }
+
+ if (l2[l2_index] & DART_PTE_VALID) {
+ printf("dart: iova %lx already has a valid PTE: %lx\n", iova, l2[l2_index]);
+ return -1;
+ }
+
+ u64 offset = FIELD_PREP(dart->params->offset_mask, paddr >> DART_PTE_OFFSET_SHIFT);
+
+ l2[l2_index] = offset | dart->params->pte_flags;
+
+ return 0;
+}
+
+int dart_map(dart_dev_t *dart, uintptr_t iova, void *bfr, size_t len)
+{
+ uintptr_t paddr = (uintptr_t)bfr;
+ u64 offset = 0;
+
+ if (len % SZ_16K)
+ return -1;
+ if (paddr % SZ_16K)
+ return -1;
+ if (iova % SZ_16K)
+ return -1;
+
+ while (offset < len) {
+ int ret = dart_map_page(dart, iova + offset, paddr + offset);
+
+ if (ret) {
+ dart_unmap(dart, iova, offset);
+ return ret;
+ }
+
+ offset += SZ_16K;
+ }
+
+ dart->params->tlb_invalidate(dart);
+ return 0;
+}
+
+static void dart_unmap_page(dart_dev_t *dart, uintptr_t iova)
+{
+ u32 ttbr = (iova >> 36) & 0x3;
+ u32 l1_index = (iova >> 25) & 0x7ff;
+ u32 l2_index = (iova >> 14) & 0x7ff;
+
+ if (!(dart->l1[ttbr][l1_index] & DART_PTE_VALID))
+ return;
+
+ u64 *l2 = dart_get_l2(dart, l1_index);
+ l2[l2_index] = 0;
+}
+
+void dart_unmap(dart_dev_t *dart, uintptr_t iova, size_t len)
+{
+ if (len % SZ_16K)
+ return;
+ if (iova % SZ_16K)
+ return;
+
+ while (len) {
+ dart_unmap_page(dart, iova);
+
+ len -= SZ_16K;
+ iova += SZ_16K;
+ }
+
+ dart->params->tlb_invalidate(dart);
+}
+
+void dart_free_l2(dart_dev_t *dart, uintptr_t iova)
+{
+ if (iova & ((1 << 25) - 1)) {
+ printf("dart: %08lx is not at the start of L2 table\n", iova);
+ return;
+ }
+
+ u32 ttbr = (iova >> 36) & 0x3;
+ u32 l1_index = (iova >> 25) & 0x7ff;
+
+ if (!(dart->l1[ttbr][l1_index] & DART_PTE_VALID))
+ return;
+
+ u64 *l2 = dart_get_l2(dart, l1_index);
+
+ for (u32 idx = 0; idx < 2048; idx++) {
+ if (l2[idx] & DART_PTE_VALID) {
+ printf("dart: %08lx is still mapped\n", iova + (idx << 14));
+ return;
+ }
+ }
+ dart->l1[ttbr][l1_index] = 0;
+ free(l2);
+}
+
+static void *dart_translate_internal(dart_dev_t *dart, uintptr_t iova, int silent)
+{
+ u32 ttbr = (iova >> 36) & 0x3;
+ u32 l1_index = (iova >> 25) & 0x7ff;
+
+ if ((int)ttbr >= dart->params->ttbr_count) {
+ printf("dart[%lx %u]: ttbr out of range: %d\n", dart->regs, dart->device, ttbr);
+ return NULL;
+ }
+
+ if (!dart->l1[ttbr]) {
+ printf("dart[%lx %u]: l1[%u] is not set\n", dart->regs, dart->device, ttbr);
+ return NULL;
+ }
+
+ if (!(dart->l1[ttbr][l1_index] & DART_PTE_VALID) && !silent) {
+ printf("dart[%lx %u]: l1 translation failure %x %lx\n", dart->regs, dart->device, l1_index,
+ iova);
+ return NULL;
+ }
+
+ u32 l2_index = (iova >> 14) & 0x7ff;
+ u64 *l2 = (u64 *)(FIELD_GET(dart->params->offset_mask, dart->l1[ttbr][l1_index])
+ << DART_PTE_OFFSET_SHIFT);
+
+ if (!(l2[l2_index] & DART_PTE_VALID) && !silent) {
+ printf("dart[%lx %u]: l2 translation failure %x:%x %lx\n", dart->regs, dart->device,
+ l1_index, l2_index, iova);
+ return NULL;
+ }
+
+ u32 offset = iova & 0x3fff;
+ void *base =
+ (void *)(FIELD_GET(dart->params->offset_mask, l2[l2_index]) << DART_PTE_OFFSET_SHIFT);
+
+ return base + offset;
+}
+
+void *dart_translate(dart_dev_t *dart, uintptr_t iova)
+{
+ return dart_translate_internal(dart, iova, 0);
+}
+
+u64 dart_search(dart_dev_t *dart, void *paddr)
+{
+ for (int ttbr = 0; ttbr < dart->params->ttbr_count; ++ttbr) {
+ if (!dart->l1[ttbr])
+ continue;
+ for (u32 l1_index = 0; l1_index < 0x7ff; l1_index++) {
+ if (!(dart->l1[ttbr][l1_index] & DART_PTE_VALID))
+ continue;
+
+ u64 *l2 = (u64 *)(FIELD_GET(dart->params->offset_mask, dart->l1[ttbr][l1_index])
+ << DART_PTE_OFFSET_SHIFT);
+ for (u32 l2_index = 0; l2_index < 0x7ff; l2_index++) {
+ if (!(l2[l2_index] & DART_PTE_VALID))
+ continue;
+ u64 *dst = (u64 *)(FIELD_GET(dart->params->offset_mask, l2[l2_index])
+ << DART_PTE_OFFSET_SHIFT);
+ if (dst == paddr)
+ return ((u64)ttbr << 36) | ((u64)l1_index << 25) | (l2_index << 14);
+ }
+ }
+ }
+
+ return DART_PTR_ERR;
+}
+
+u64 dart_find_iova(dart_dev_t *dart, s64 start, size_t len)
+{
+ if (len % SZ_16K)
+ return -1;
+ if (start < 0 || start % SZ_16K)
+ return -1;
+
+ uintptr_t end = 1LLU << 36;
+ uintptr_t iova = start;
+
+ while (iova + len <= end) {
+
+ if (dart_translate_internal(dart, iova, 1) == NULL) {
+ size_t size;
+ for (size = SZ_16K; size < len; size += SZ_16K) {
+ if (dart_translate_internal(dart, iova + size, 1) != NULL)
+ break;
+ }
+ if (size == len)
+ return iova;
+
+ iova += size + SZ_16K;
+ } else
+ iova += SZ_16K;
+ }
+
+ return DART_PTR_ERR;
+}
+
+void dart_shutdown(dart_dev_t *dart)
+{
+ if (!dart->locked && !dart->keep)
+ write32(DART_TCR(dart), dart->params->tcr_disabled);
+
+ for (int i = 0; i < dart->params->ttbr_count; ++i)
+ if (is_heap(dart->l1[i]))
+ write32(DART_TTBR(dart, i), 0);
+
+ for (int ttbr = 0; ttbr < dart->params->ttbr_count; ++ttbr) {
+ for (int i = 0; i < SZ_16K / 8; ++i) {
+ if (dart->l1[ttbr][i] & DART_PTE_VALID) {
+ void *l2 = dart_get_l2(dart, i);
+ if (is_heap(l2)) {
+ free(l2);
+ dart->l1[ttbr][i] = 0;
+ }
+ }
+ }
+ }
+
+ dart->params->tlb_invalidate(dart);
+
+ for (int i = 0; i < dart->params->ttbr_count; ++i)
+ if (is_heap(dart->l1[i]))
+ free(dart->l1[i]);
+ free(dart);
+}
+
+u64 dart_vm_base(dart_dev_t *dart)
+{
+ return dart->vm_base;
+}
diff --git a/tools/src/dart.h b/tools/src/dart.h
new file mode 100644
index 0000000..7d8474b
--- /dev/null
+++ b/tools/src/dart.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef DART_H
+#define DART_H
+
+#include "types.h"
+
+#define DART_PTR_ERR BIT(63)
+#define DART_IS_ERR(val) FIELD_GET(DART_PTR_ERR, val)
+
+typedef struct dart_dev dart_dev_t;
+
+enum dart_type_t {
+ DART_T8020,
+ DART_T8110,
+ DART_T6000,
+};
+
+dart_dev_t *dart_init(uintptr_t base, u8 device, bool keep_pts, enum dart_type_t type);
+dart_dev_t *dart_init_adt(const char *path, int instance, int device, bool keep_pts);
+void dart_lock_adt(const char *path, int instance);
+dart_dev_t *dart_init_fdt(void *dt, u32 phandle, int device, bool keep_pts);
+int dart_setup_pt_region(dart_dev_t *dart, const char *path, int device, u64 vm_base);
+int dart_map(dart_dev_t *dart, uintptr_t iova, void *bfr, size_t len);
+void dart_unmap(dart_dev_t *dart, uintptr_t iova, size_t len);
+void dart_free_l2(dart_dev_t *dart, uintptr_t iova);
+void *dart_translate(dart_dev_t *dart, uintptr_t iova);
+u64 dart_search(dart_dev_t *dart, void *paddr);
+u64 dart_find_iova(dart_dev_t *dart, s64 start, size_t len);
+void dart_shutdown(dart_dev_t *dart);
+u64 dart_vm_base(dart_dev_t *dart);
+
+#endif
diff --git a/tools/src/dcp.c b/tools/src/dcp.c
new file mode 100644
index 0000000..e9f0503
--- /dev/null
+++ b/tools/src/dcp.c
@@ -0,0 +1,92 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "dcp.h"
+#include "adt.h"
+#include "malloc.h"
+#include "pmgr.h"
+#include "rtkit.h"
+#include "utils.h"
+
+dcp_dev_t *dcp_init(const char *dcp_path, const char *dcp_dart_path, const char *disp_dart_path)
+{
+ u32 sid;
+
+ int node = adt_path_offset(adt, "/arm-io/dart-dcp/mapper-dcp");
+ if (node < 0) {
+ printf("dcp: mapper-dcp not found!\n");
+ return NULL;
+ }
+ if (ADT_GETPROP(adt, node, "reg", &sid) < 0) {
+ printf("dcp: failed to read dart stream ID!\n");
+ return NULL;
+ }
+
+ dcp_dev_t *dcp = malloc(sizeof(dcp_dev_t));
+ if (!dcp)
+ return NULL;
+
+ dcp->dart_dcp = dart_init_adt(dcp_dart_path, 0, sid, true);
+ if (!dcp->dart_dcp) {
+ printf("dcp: failed to initialize DCP DART\n");
+ goto out_free;
+ }
+ u64 vm_base = dart_vm_base(dcp->dart_dcp);
+ dart_setup_pt_region(dcp->dart_dcp, dcp_dart_path, sid, vm_base);
+
+ dcp->dart_disp = dart_init_adt(disp_dart_path, 0, 0, true);
+ if (!dcp->dart_disp) {
+ printf("dcp: failed to initialize DISP DART\n");
+ goto out_dart_dcp;
+ }
+ // set disp0's page tables at dart-dcp's vm-base
+ dart_setup_pt_region(dcp->dart_disp, disp_dart_path, 0, vm_base);
+
+ dcp->iovad_dcp = iovad_init(vm_base + 0x10000000, vm_base + 0x20000000);
+
+ dcp->asc = asc_init(dcp_path);
+ if (!dcp->asc) {
+ printf("dcp: failed to initialize ASC\n");
+ goto out_iovad;
+ }
+
+ dcp->rtkit = rtkit_init("dcp", dcp->asc, dcp->dart_dcp, dcp->iovad_dcp, NULL);
+ if (!dcp->rtkit) {
+ printf("dcp: failed to initialize RTKit\n");
+ goto out_iovad;
+ }
+
+ if (!rtkit_boot(dcp->rtkit)) {
+ printf("dcp: failed to boot RTKit\n");
+ goto out_iovad;
+ }
+
+ return dcp;
+
+ rtkit_quiesce(dcp->rtkit);
+ rtkit_free(dcp->rtkit);
+out_iovad:
+ iovad_shutdown(dcp->iovad_dcp, dcp->dart_dcp);
+ dart_shutdown(dcp->dart_disp);
+out_dart_dcp:
+ dart_shutdown(dcp->dart_dcp);
+out_free:
+ free(dcp);
+ return NULL;
+}
+
+int dcp_shutdown(dcp_dev_t *dcp, bool sleep)
+{
+ if (sleep) {
+ rtkit_sleep(dcp->rtkit);
+ pmgr_reset(0, "DISP0_CPU0");
+ } else {
+ rtkit_quiesce(dcp->rtkit);
+ }
+ rtkit_free(dcp->rtkit);
+ dart_shutdown(dcp->dart_disp);
+ iovad_shutdown(dcp->iovad_dcp, dcp->dart_dcp);
+ dart_shutdown(dcp->dart_dcp);
+ free(dcp);
+
+ return 0;
+}
diff --git a/tools/src/dcp.h b/tools/src/dcp.h
new file mode 100644
index 0000000..c9de8f2
--- /dev/null
+++ b/tools/src/dcp.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef DCP_H
+#define DCP_H
+
+#include "asc.h"
+#include "dart.h"
+#include "rtkit.h"
+
+typedef struct {
+ dart_dev_t *dart_dcp;
+ dart_dev_t *dart_disp;
+ iova_domain_t *iovad_dcp;
+ asc_dev_t *asc;
+ rtkit_dev_t *rtkit;
+} dcp_dev_t;
+
+dcp_dev_t *dcp_init(const char *dcp_path, const char *dcp_dart_path, const char *disp_dart_path);
+
+int dcp_shutdown(dcp_dev_t *dcp, bool sleep);
+
+#endif
diff --git a/tools/src/dcp_iboot.c b/tools/src/dcp_iboot.c
new file mode 100644
index 0000000..8f8a374
--- /dev/null
+++ b/tools/src/dcp_iboot.c
@@ -0,0 +1,224 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "dcp_iboot.h"
+#include "afk.h"
+#include "assert.h"
+#include "malloc.h"
+#include "string.h"
+#include "utils.h"
+
+#define DCP_IBOOT_ENDPOINT 0x23
+
+#define TXBUF_LEN 0x4000
+#define RXBUF_LEN 0x4000
+
+struct txcmd {
+ u32 op;
+ u32 len;
+ u32 unk1;
+ u32 unk2;
+ u8 payload[];
+};
+
+struct rxcmd {
+ u32 op;
+ u32 len;
+ u8 payload[];
+};
+
+struct dcp_iboot_if {
+ dcp_dev_t *dcp;
+ afk_epic_ep_t *epic;
+ int channel;
+
+ union {
+ u8 txbuf[TXBUF_LEN];
+ struct txcmd txcmd;
+ };
+
+ union {
+ u8 rxbuf[RXBUF_LEN];
+ struct rxcmd rxcmd;
+ };
+};
+
+enum IBootCmd {
+ IBOOT_SET_POWER = 2,
+ IBOOT_GET_HPD = 3,
+ IBOOT_GET_TIMING_MODES = 4,
+ IBOOT_GET_COLOR_MODES = 5,
+ IBOOT_SET_MODE = 6,
+ IBOOT_SWAP_BEGIN = 15,
+ IBOOT_SWAP_SET_LAYER = 16,
+ IBOOT_SWAP_END = 18,
+};
+
+struct get_hpd_resp {
+ u8 hpd;
+ u8 pad[3];
+ u32 timing_cnt;
+ u32 color_cnt;
+};
+
+struct get_tmode_resp {
+ u32 count;
+ dcp_timing_mode_t modes[];
+};
+
+struct get_cmode_resp {
+ u32 count;
+ dcp_color_mode_t modes[];
+};
+
+struct swap_start_resp {
+ u32 unk1, unk2, unk3;
+ u32 swap_id;
+ u32 unk4;
+};
+
+struct swap_set_layer_cmd {
+ u32 unk;
+ u32 layer_id;
+ dcp_layer_t layer;
+ dcp_rect_t src;
+ dcp_rect_t dst;
+ u32 unk2;
+} PACKED;
+
+dcp_iboot_if_t *dcp_ib_init(dcp_dev_t *dcp)
+{
+ dcp_iboot_if_t *iboot = malloc(sizeof(dcp_iboot_if_t));
+ if (!iboot)
+ return NULL;
+
+ iboot->dcp = dcp;
+ iboot->epic = afk_epic_init(dcp->rtkit, DCP_IBOOT_ENDPOINT);
+ if (!iboot->epic) {
+ printf("dcp-iboot: failed to initialize EPIC\n");
+ goto err_free;
+ }
+
+ iboot->channel = afk_epic_start_interface(iboot->epic, "disp0-service", TXBUF_LEN, RXBUF_LEN);
+
+ if (iboot->channel < 0) {
+ printf("dcp-iboot: failed to initialize disp0 service\n");
+ goto err_shutdown;
+ }
+
+ return iboot;
+
+err_shutdown:
+ afk_epic_shutdown(iboot->epic);
+err_free:
+ free(iboot);
+ return NULL;
+}
+
+int dcp_ib_shutdown(dcp_iboot_if_t *iboot)
+{
+ afk_epic_shutdown(iboot->epic);
+
+ free(iboot);
+ return 0;
+}
+
+static int dcp_ib_cmd(dcp_iboot_if_t *iboot, int op, size_t in_size)
+{
+ size_t rxsize = RXBUF_LEN;
+ assert(in_size <= TXBUF_LEN - sizeof(struct txcmd));
+
+ iboot->txcmd.op = op;
+ iboot->txcmd.len = sizeof(struct txcmd) + in_size;
+
+ return afk_epic_command(iboot->epic, iboot->channel, 0xc0, iboot->txbuf,
+ sizeof(struct txcmd) + in_size, iboot->rxbuf, &rxsize);
+}
+
+int dcp_ib_set_power(dcp_iboot_if_t *iboot, bool power)
+{
+ u32 *pwr = (void *)iboot->txcmd.payload;
+ *pwr = power;
+
+ return dcp_ib_cmd(iboot, IBOOT_SET_POWER, 1);
+}
+
+int dcp_ib_get_hpd(dcp_iboot_if_t *iboot, int *timing_cnt, int *color_cnt)
+{
+ struct get_hpd_resp *resp = (void *)iboot->rxcmd.payload;
+ int ret = dcp_ib_cmd(iboot, IBOOT_GET_HPD, 0);
+
+ if (ret < 0)
+ return ret;
+
+ if (timing_cnt)
+ *timing_cnt = resp->timing_cnt;
+ if (color_cnt)
+ *color_cnt = resp->color_cnt;
+
+ return !!resp->hpd;
+}
+
+int dcp_ib_get_timing_modes(dcp_iboot_if_t *iboot, dcp_timing_mode_t **modes)
+{
+ struct get_tmode_resp *resp = (void *)iboot->rxcmd.payload;
+ int ret = dcp_ib_cmd(iboot, IBOOT_GET_TIMING_MODES, 0);
+
+ if (ret < 0)
+ return ret;
+
+ *modes = resp->modes;
+ return resp->count;
+}
+
+int dcp_ib_get_color_modes(dcp_iboot_if_t *iboot, dcp_color_mode_t **modes)
+{
+ struct get_cmode_resp *resp = (void *)iboot->rxcmd.payload;
+ int ret = dcp_ib_cmd(iboot, IBOOT_GET_COLOR_MODES, 0);
+
+ if (ret < 0)
+ return ret;
+
+ *modes = resp->modes;
+ return resp->count;
+}
+
+int dcp_ib_set_mode(dcp_iboot_if_t *iboot, dcp_timing_mode_t *tmode, dcp_color_mode_t *cmode)
+{
+ struct {
+ dcp_timing_mode_t tmode;
+ dcp_color_mode_t cmode;
+ } *cmd = (void *)iboot->txcmd.payload;
+
+ cmd->tmode = *tmode;
+ cmd->cmode = *cmode;
+ return dcp_ib_cmd(iboot, IBOOT_SET_MODE, sizeof(*cmd));
+}
+
+int dcp_ib_swap_begin(dcp_iboot_if_t *iboot)
+{
+ struct swap_start_resp *resp = (void *)iboot->rxcmd.payload;
+ int ret = dcp_ib_cmd(iboot, IBOOT_SWAP_BEGIN, 0);
+ if (ret < 0)
+ return ret;
+
+ return resp->swap_id;
+}
+
+int dcp_ib_swap_set_layer(dcp_iboot_if_t *iboot, int layer_id, dcp_layer_t *layer,
+ dcp_rect_t *src_rect, dcp_rect_t *dst_rect)
+{
+ struct swap_set_layer_cmd *cmd = (void *)iboot->txcmd.payload;
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->layer_id = layer_id;
+ cmd->layer = *layer;
+ cmd->src = *src_rect;
+ cmd->dst = *dst_rect;
+
+ return dcp_ib_cmd(iboot, IBOOT_SWAP_SET_LAYER, sizeof(*cmd));
+}
+
+int dcp_ib_swap_end(dcp_iboot_if_t *iboot)
+{
+ memset(iboot->txcmd.payload, 0, 12);
+ return dcp_ib_cmd(iboot, IBOOT_SWAP_END, 12);
+}
diff --git a/tools/src/dcp_iboot.h b/tools/src/dcp_iboot.h
new file mode 100644
index 0000000..adb449e
--- /dev/null
+++ b/tools/src/dcp_iboot.h
@@ -0,0 +1,111 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef DCP_IBOOT_H
+#define DCP_IBOOT_H
+
+#include "dcp.h"
+
+typedef struct dcp_iboot_if dcp_iboot_if_t;
+
+enum DCPEOTF {
+ EOTF_GAMMA_SDR = 1,
+ EOTF_GAMMA_HDR = 2,
+};
+
+enum DCPEncoding {
+ ENC_RGB = 1,
+ ENC_YCBCR_444 = 3,
+ ENC_YCBCR_422 = 4,
+ ENC_YCBCR_420 = 5,
+};
+
+enum DCPColorimetry {
+ CLR_BT601_709 = 1,
+ CLR_BT2020 = 2,
+ CLR_DCIP3 = 3,
+};
+
+enum DCPSurfaceFmt {
+ FMT_BGRA = 1,
+ FMT_RGBA = 3,
+ FMT_w18p = 4,
+ FMT_444v = 6,
+ FMT_422v = 7,
+ FMT_420v = 8,
+ FMT_w30r = 9,
+ FMT_w40a = 10,
+};
+
+enum DCPTransform {
+ XFRM_NONE = 0,
+ XFRM_XFLIP = 1,
+ XFRM_YFLIP = 2,
+ XFRM_ROT_90 = 3,
+ XFRM_ROT_180 = 4,
+ XFRM_ROT_270 = 5,
+};
+
+enum AddrFormat {
+ ADDR_PLANAR = 1,
+ ADDR_TILED = 2,
+ ADDR_AGX = 3,
+};
+
+typedef struct {
+ u32 valid;
+ u32 width;
+ u32 height;
+ u32 fps;
+ u8 pad[8];
+} PACKED dcp_timing_mode_t;
+
+typedef struct {
+ u32 valid;
+ u32 colorimetry;
+ u32 eotf;
+ u32 encoding;
+ u32 bpp;
+ u8 pad[4];
+} PACKED dcp_color_mode_t;
+
+typedef struct {
+ u32 unk1;
+ u64 addr;
+ u32 tile_size;
+ u32 stride;
+ u32 unk2[4];
+ u32 addr_format;
+ u32 unk3;
+} PACKED dcp_plane_t;
+
+typedef struct {
+ dcp_plane_t planes[3];
+ u32 unk;
+ u32 plane_cnt;
+ u32 width;
+ u32 height;
+ u32 surface_fmt;
+ u32 colorspace;
+ u32 eotf;
+ u8 transform;
+ u8 padding[3];
+} PACKED dcp_layer_t;
+
+typedef struct {
+ u32 w, h, x, y;
+} PACKED dcp_rect_t;
+
+dcp_iboot_if_t *dcp_ib_init(dcp_dev_t *dcp);
+int dcp_ib_shutdown(dcp_iboot_if_t *iboot);
+
+int dcp_ib_set_power(dcp_iboot_if_t *iboot, bool power);
+int dcp_ib_get_hpd(dcp_iboot_if_t *iboot, int *timing_cnt, int *color_cnt);
+int dcp_ib_get_timing_modes(dcp_iboot_if_t *iboot, dcp_timing_mode_t **modes);
+int dcp_ib_get_color_modes(dcp_iboot_if_t *iboot, dcp_color_mode_t **modes);
+int dcp_ib_set_mode(dcp_iboot_if_t *iboot, dcp_timing_mode_t *timing, dcp_color_mode_t *color);
+int dcp_ib_swap_begin(dcp_iboot_if_t *iboot);
+int dcp_ib_swap_set_layer(dcp_iboot_if_t *iboot, int layer_id, dcp_layer_t *layer,
+ dcp_rect_t *src_rect, dcp_rect_t *dst_rect);
+int dcp_ib_swap_end(dcp_iboot_if_t *iboot);
+
+#endif
diff --git a/tools/src/devicetree.c b/tools/src/devicetree.c
new file mode 100644
index 0000000..f0c9193
--- /dev/null
+++ b/tools/src/devicetree.c
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "devicetree.h"
+
+#include "libfdt/libfdt.h"
+
+void dt_parse_ranges(void *dt, int node, struct dt_ranges_tbl *ranges)
+{
+ int len;
+ const struct fdt_property *ranges_prop = fdt_get_property(dt, node, "ranges", &len);
+ if (ranges_prop && len > 0) {
+ int idx = 0;
+ int num_entries = len / sizeof(fdt64_t);
+ if (num_entries > DT_MAX_RANGES)
+ num_entries = DT_MAX_RANGES;
+
+ const fdt64_t *entry = (const fdt64_t *)ranges_prop->data;
+ for (int i = 0; i < num_entries; ++i) {
+ u64 start = fdt64_ld(entry++);
+ u64 parent = fdt64_ld(entry++);
+ u64 size = fdt64_ld(entry++);
+ if (size) {
+ ranges[idx].start = start;
+ ranges[idx].parent = parent;
+ ranges[idx].size = size;
+ idx++;
+ }
+ }
+ }
+}
+
+u64 dt_translate(struct dt_ranges_tbl *ranges, const fdt64_t *reg)
+{
+ u64 addr = fdt64_ld(reg);
+ for (int idx = 0; idx < DT_MAX_RANGES; ++idx) {
+ if (ranges[idx].size == 0)
+ break;
+ if (addr >= ranges[idx].start && addr < ranges[idx].start + ranges[idx].size)
+ return ranges[idx].parent - ranges[idx].start + addr;
+ }
+
+ return addr;
+}
+
+u64 dt_get_address(void *dt, int node)
+{
+ int parent = fdt_parent_offset(dt, node);
+
+ // find parent with "ranges" property
+ while (parent >= 0) {
+ if (fdt_getprop(dt, parent, "ranges", NULL))
+ break;
+
+ parent = fdt_parent_offset(dt, parent);
+ }
+
+ if (parent < 0)
+ return 0;
+
+ // parse ranges for address translation
+ struct dt_ranges_tbl ranges[DT_MAX_RANGES] = {0};
+ dt_parse_ranges(dt, parent, ranges);
+
+ const fdt64_t *reg = fdt_getprop(dt, node, "reg", NULL);
+ if (!reg)
+ return 0;
+
+ return dt_translate(ranges, reg);
+}
diff --git a/tools/src/devicetree.h b/tools/src/devicetree.h
new file mode 100644
index 0000000..855f038
--- /dev/null
+++ b/tools/src/devicetree.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef DEVICETREE_H
+#define DEVICETREE_H
+
+#include "types.h"
+
+#include "libfdt/libfdt.h"
+
+#define DT_MAX_RANGES 8
+
+struct dt_ranges_tbl {
+ u64 start;
+ u64 parent;
+ u64 size;
+};
+
+void dt_parse_ranges(void *dt, int node, struct dt_ranges_tbl *ranges);
+u64 dt_translate(struct dt_ranges_tbl *ranges, const fdt64_t *reg);
+u64 dt_get_address(void *dt, int node);
+
+#endif
diff --git a/tools/src/display.c b/tools/src/display.c
new file mode 100644
index 0000000..3dbf49e
--- /dev/null
+++ b/tools/src/display.c
@@ -0,0 +1,514 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "display.h"
+#include "adt.h"
+#include "assert.h"
+#include "dcp.h"
+#include "dcp_iboot.h"
+#include "fb.h"
+#include "memory.h"
+#include "string.h"
+#include "utils.h"
+#include "xnuboot.h"
+
+#define DISPLAY_STATUS_DELAY 100
+#define DISPLAY_STATUS_RETRIES 20
+
+#define COMPARE(a, b) \
+ if ((a) > (b)) { \
+ *best = modes[i]; \
+ continue; \
+ } else if ((a) < (b)) { \
+ continue; \
+ }
+
+static dcp_dev_t *dcp;
+static dcp_iboot_if_t *iboot;
+static u64 fb_dva;
+static u64 fb_size;
+bool display_is_external;
+
+#define abs(x) ((x) >= 0 ? (x) : -(x))
+
+u64 display_mode_fb_size(dcp_timing_mode_t *mode)
+{
+ // assume 4 byte per pixel (either BGRA x2r10b10g10)
+ return mode->width * mode->height * 4;
+}
+
+static void display_choose_timing_mode(dcp_timing_mode_t *modes, int cnt, dcp_timing_mode_t *best,
+ dcp_timing_mode_t *want)
+{
+ *best = modes[0];
+
+ for (int i = 1; i < cnt; i++) {
+ COMPARE(modes[i].valid, best->valid);
+ if (want && want->valid) {
+ COMPARE(modes[i].width == want->width && modes[i].height == want->height,
+ best->width == want->width && best->height == want->height);
+ COMPARE(-abs((long)modes[i].fps - (long)want->fps),
+ -abs((long)best->fps - (long)want->fps));
+ } else {
+ COMPARE(display_mode_fb_size(&modes[i]) <= fb_size,
+ display_mode_fb_size(best) <= fb_size);
+ }
+
+ COMPARE(modes[i].width <= 1920, best->width <= 1920);
+ COMPARE(modes[i].height <= 1200, best->height <= 1200);
+ COMPARE(modes[i].fps <= 60 << 16, best->fps <= 60 << 16);
+ COMPARE(modes[i].width, best->width);
+ COMPARE(modes[i].height, best->height);
+ COMPARE(modes[i].fps, best->fps);
+ }
+
+ printf("display: timing mode: valid=%d %dx%d %d.%02d Hz\n", best->valid, best->width,
+ best->height, best->fps >> 16, ((best->fps & 0xffff) * 100 + 0x7fff) >> 16);
+}
+
+static void display_choose_color_mode(dcp_color_mode_t *modes, int cnt, dcp_color_mode_t *best)
+{
+ *best = modes[0];
+
+ for (int i = 1; i < cnt; i++) {
+ COMPARE(modes[i].valid, best->valid);
+ COMPARE(modes[i].bpp <= 32, best->bpp <= 32);
+ COMPARE(modes[i].bpp, best->bpp);
+ COMPARE(-modes[i].colorimetry, -best->colorimetry);
+ COMPARE(-modes[i].encoding, -best->encoding);
+ COMPARE(-modes[i].eotf, -best->eotf);
+ }
+
+ printf("display: color mode: valid=%d colorimetry=%d eotf=%d encoding=%d bpp=%d\n", best->valid,
+ best->colorimetry, best->eotf, best->encoding, best->bpp);
+}
+
+int display_get_vram(u64 *paddr, u64 *size)
+{
+ int ret = 0;
+ int adt_path[4];
+ int node = adt_path_offset_trace(adt, "/vram", adt_path);
+
+ if (node < 0) {
+ printf("display: '/vram' not found\n");
+ return -1;
+ }
+
+ int pp = 0;
+ while (adt_path[pp])
+ pp++;
+ adt_path[pp + 1] = 0;
+
+ ret = adt_get_reg(adt, adt_path, "reg", 0, paddr, size);
+ if (ret < 0) {
+ printf("display: failed to read /vram/reg\n");
+ return -1;
+ }
+
+ if (*paddr != cur_boot_args.video.base) {
+ printf("display: vram does not match boot_args.video.base\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static uintptr_t display_map_fb(uintptr_t iova, u64 paddr, u64 size)
+{
+ if (iova == 0) {
+ u64 iova_disp0 = 0;
+ u64 iova_dcp = 0;
+
+ // start scanning for free iova space on vm-base
+ iova_dcp = dart_find_iova(dcp->dart_dcp, dart_vm_base(dcp->dart_dcp), size);
+ if (DART_IS_ERR(iova_dcp)) {
+ printf("display: failed to find IOVA for fb of %06zx bytes (dcp)\n", size);
+ return iova_dcp;
+ }
+
+ // try to map the fb to the same IOVA on disp0
+ iova_disp0 = dart_find_iova(dcp->dart_disp, iova_dcp, size);
+ if (DART_IS_ERR(iova_disp0)) {
+ printf("display: failed to find IOVA for fb of %06zx bytes (disp0)\n", size);
+ return iova_disp0;
+ }
+
+ // assume this results in the same IOVA, not sure if this is required but matches what iboot
+ // does on other models.
+ if (iova_disp0 != iova_dcp) {
+ printf("display: IOVA mismatch for fb between dcp (%08lx) and disp0 (%08lx)\n",
+ (u64)iova_dcp, (u64)iova_disp0);
+ return DART_PTR_ERR;
+ }
+
+ iova = iova_dcp;
+ }
+
+ int ret = dart_map(dcp->dart_disp, iova, (void *)paddr, size);
+ if (ret < 0) {
+ printf("display: failed to map fb to dart-disp0\n");
+ return DART_PTR_ERR;
+ }
+
+ ret = dart_map(dcp->dart_dcp, iova, (void *)paddr, size);
+ if (ret < 0) {
+ printf("display: failed to map fb to dart-dcp\n");
+ dart_unmap(dcp->dart_disp, iova, size);
+ return DART_PTR_ERR;
+ }
+
+ return iova;
+}
+
+int display_start_dcp(void)
+{
+ if (iboot)
+ return 0;
+
+ dcp = dcp_init("/arm-io/dcp", "/arm-io/dart-dcp", "/arm-io/dart-disp0");
+ if (!dcp) {
+ printf("display: failed to initialize DCP\n");
+ return -1;
+ }
+
+ // determine frame buffer PA and size from "/vram"
+ u64 pa, size;
+ if (display_get_vram(&pa, &size)) {
+ // use a safe fb_size
+ fb_size = cur_boot_args.video.stride * cur_boot_args.video.height *
+ ((cur_boot_args.video.depth + 7) / 8);
+ } else {
+ fb_size = size;
+ }
+
+ // Find the framebuffer DVA
+ fb_dva = dart_search(dcp->dart_disp, (void *)cur_boot_args.video.base);
+ // framebuffer is not mapped on the M1 Ultra Mac Studio
+ if (DART_IS_ERR(fb_dva))
+ fb_dva = display_map_fb(0, pa, size);
+ if (DART_IS_ERR(fb_dva)) {
+ printf("display: failed to find display DVA\n");
+ fb_dva = 0;
+ dcp_shutdown(dcp, false);
+ return -1;
+ }
+
+ iboot = dcp_ib_init(dcp);
+ if (!iboot) {
+ printf("display: failed to initialize DCP iBoot interface\n");
+ dcp_shutdown(dcp, false);
+ return -1;
+ }
+
+ return 0;
+}
+
+struct display_options {
+ bool retina;
+};
+
+int display_parse_mode(const char *config, dcp_timing_mode_t *mode, struct display_options *opts)
+{
+ memset(mode, 0, sizeof(*mode));
+
+ if (!config || !strcmp(config, "auto"))
+ return 0;
+
+ const char *s_w = config;
+ const char *s_h = strchr(config, 'x');
+ const char *s_fps = strchr(config, '@');
+
+ if (s_w && s_h) {
+ mode->width = atol(s_w);
+ mode->height = atol(s_h + 1);
+ mode->valid = mode->width && mode->height;
+ }
+
+ if (s_fps) {
+ mode->fps = atol(s_fps + 1) << 16;
+
+ const char *s_fps_frac = strchr(s_fps + 1, '.');
+ if (s_fps_frac) {
+ // Assumes two decimals...
+ mode->fps += (atol(s_fps_frac + 1) << 16) / 100;
+ }
+ }
+
+ const char *option = config;
+ while (option && opts) {
+ if (!strncmp(option + 1, "retina", 6))
+ opts->retina = true;
+ option = strchr(option + 1, ',');
+ }
+
+ printf("display: want mode: valid=%d %dx%d %d.%02d Hz\n", mode->valid, mode->width,
+ mode->height, mode->fps >> 16, ((mode->fps & 0xffff) * 100 + 0x7fff) >> 16);
+
+ return mode->valid;
+}
+
+static int display_swap(u64 iova, u32 stride, u32 width, u32 height)
+{
+ int ret;
+ int swap_id = ret = dcp_ib_swap_begin(iboot);
+ if (swap_id < 0) {
+ printf("display: failed to start swap\n");
+ return -1;
+ }
+
+ dcp_layer_t layer = {
+ .planes = {{
+ .addr = iova,
+ .stride = stride,
+ .addr_format = ADDR_PLANAR,
+ }},
+ .plane_cnt = 1,
+ .width = width,
+ .height = height,
+ .surface_fmt = FMT_w30r,
+ .colorspace = 2,
+ .eotf = EOTF_GAMMA_SDR,
+ .transform = XFRM_NONE,
+ };
+
+ dcp_rect_t rect = {width, height, 0, 0};
+
+ if ((ret = dcp_ib_swap_set_layer(iboot, 0, &layer, &rect, &rect)) < 0) {
+ printf("display: failed to set layer\n");
+ return -1;
+ }
+
+ if ((ret = dcp_ib_swap_end(iboot)) < 0) {
+ printf("display: failed to complete swap\n");
+ return -1;
+ }
+
+ return swap_id;
+}
+
+int display_configure(const char *config)
+{
+ dcp_timing_mode_t want;
+ struct display_options opts = {0};
+
+ display_parse_mode(config, &want, &opts);
+
+ u64 start_time = get_ticks();
+
+ int ret = display_start_dcp();
+ if (ret < 0)
+ return ret;
+
+ // Power on
+ if ((ret = dcp_ib_set_power(iboot, true)) < 0) {
+ printf("display: failed to set power\n");
+ return ret;
+ }
+
+ // Detect if display is connected
+ int timing_cnt, color_cnt;
+ int hpd = 0, retries = 0;
+
+ /* After boot DCP does not immediately report a connected display. Retry getting display
+ * information for 2 seconds.
+ */
+ while (retries++ < DISPLAY_STATUS_RETRIES) {
+ hpd = dcp_ib_get_hpd(iboot, &timing_cnt, &color_cnt);
+ if (hpd < 0)
+ ret = hpd;
+ else if (hpd && timing_cnt && color_cnt)
+ break;
+ if (retries < DISPLAY_STATUS_RETRIES)
+ mdelay(DISPLAY_STATUS_DELAY);
+ }
+ printf("display: waited %d ms for display status\n", (retries - 1) * DISPLAY_STATUS_DELAY);
+ if (ret < 0) {
+ printf("display: failed to get display status\n");
+ return 0;
+ }
+
+ printf("display: connected:%d timing_cnt:%d color_cnt:%d\n", hpd, timing_cnt, color_cnt);
+
+ if (!hpd || !timing_cnt || !color_cnt)
+ return 0;
+
+ // Find best modes
+ dcp_timing_mode_t *tmodes, tbest;
+ if ((ret = dcp_ib_get_timing_modes(iboot, &tmodes)) < 0) {
+ printf("display: failed to get timing modes\n");
+ return -1;
+ }
+ assert(ret == timing_cnt);
+ display_choose_timing_mode(tmodes, timing_cnt, &tbest, &want);
+
+ dcp_color_mode_t *cmodes, cbest;
+ if ((ret = dcp_ib_get_color_modes(iboot, &cmodes)) < 0) {
+ printf("display: failed to get color modes\n");
+ return -1;
+ }
+ assert(ret == color_cnt);
+ display_choose_color_mode(cmodes, color_cnt, &cbest);
+
+ // Set mode
+ if ((ret = dcp_ib_set_mode(iboot, &tbest, &cbest)) < 0) {
+ printf("display: failed to set mode\n");
+ return -1;
+ }
+
+ u64 fb_pa = cur_boot_args.video.base;
+ u64 tmp_dva = 0;
+
+ size_t size =
+ ALIGN_UP(tbest.width * tbest.height * ((cbest.bpp + 7) / 8) + 24 * SZ_16K, SZ_16K);
+
+ if (fb_size < size) {
+ printf("display: current framebuffer is too small for new mode\n");
+
+ /* rtkit uses 0x10000000 as DVA offset, FB starts in the first page */
+ if ((s64)size > 7 * SZ_32M) {
+ printf("display: not enough reserved L2 DVA space for fb size 0x%zx\n", size);
+ return -1;
+ }
+
+ cur_boot_args.mem_size -= size;
+ fb_pa = cur_boot_args.phys_base + cur_boot_args.mem_size;
+ /* add guard page between RAM and framebuffer */
+ // TODO: update mapping?
+ cur_boot_args.mem_size -= SZ_16K;
+
+ memset((void *)fb_pa, 0, size);
+
+ tmp_dva = iova_alloc(dcp->iovad_dcp, size);
+
+ tmp_dva = display_map_fb(tmp_dva, fb_pa, size);
+ if (DART_IS_ERR(tmp_dva)) {
+ printf("display: failed to map new fb\n");
+ return -1;
+ }
+
+ // Swap!
+ u32 stride = tbest.width * 4;
+ ret = display_swap(tmp_dva, stride, tbest.width, tbest.height);
+ if (ret < 0)
+ return ret;
+
+ /* wait for swap durations + 1ms */
+ u32 delay = (((1000 << 16) + tbest.fps - 1) / tbest.fps) + 1;
+ mdelay(delay);
+ dart_unmap(dcp->dart_disp, fb_dva, fb_size);
+ dart_unmap(dcp->dart_dcp, fb_dva, fb_size);
+
+ fb_dva = display_map_fb(fb_dva, fb_pa, size);
+ if (DART_IS_ERR(fb_dva)) {
+ printf("display: failed to map new fb\n");
+ fb_dva = 0;
+ return -1;
+ }
+
+ fb_size = size;
+ mmu_map_framebuffer(fb_pa, fb_size);
+
+ /* update ADT with the physical address of the new framebuffer */
+ u64 fb_reg[2] = {fb_pa, size};
+ int node = adt_path_offset(adt, "vram");
+ if (node >= 0) {
+ // TODO: adt_set_reg(adt, node, "vram", fb_pa, size);?
+ ret = adt_setprop(adt, node, "reg", &fb_reg, sizeof(fb_reg));
+ if (ret < 0)
+ printf("display: failed to update '/vram'\n");
+ }
+ node = adt_path_offset(adt, "/chosen/carveout-memory-map");
+ if (node >= 0) {
+ // TODO: adt_set_reg(adt, node, "vram", fb_pa, size);?
+ ret = adt_setprop(adt, node, "region-id-14", &fb_reg, sizeof(fb_reg));
+ if (ret < 0)
+ printf("display: failed to update '/chosen/carveout-memory-map/region-id-14'\n");
+ }
+ }
+
+ // Swap!
+ u32 stride = tbest.width * 4;
+ ret = display_swap(fb_dva, stride, tbest.width, tbest.height);
+ if (ret < 0)
+ return ret;
+
+ printf("display: swapped! (swap_id=%d)\n", ret);
+
+ if (fb_pa != cur_boot_args.video.base || cur_boot_args.video.stride != stride ||
+ cur_boot_args.video.width != tbest.width || cur_boot_args.video.height != tbest.height ||
+ cur_boot_args.video.depth != 30) {
+ cur_boot_args.video.base = fb_pa;
+ cur_boot_args.video.stride = stride;
+ cur_boot_args.video.width = tbest.width;
+ cur_boot_args.video.height = tbest.height;
+ cur_boot_args.video.depth = 30 | (opts.retina ? FB_DEPTH_FLAG_RETINA : 0);
+ fb_reinit();
+ }
+
+ /* Update for python / subsequent stages */
+ memcpy((void *)boot_args_addr, &cur_boot_args, sizeof(cur_boot_args));
+
+ if (tmp_dva) {
+ // unmap / free temporary dva
+ dart_unmap(dcp->dart_disp, tmp_dva, size);
+ dart_unmap(dcp->dart_dcp, tmp_dva, size);
+ iova_free(dcp->iovad_dcp, tmp_dva, size);
+ }
+
+ u64 msecs = ticks_to_msecs(get_ticks() - start_time);
+ printf("display: Modeset took %ld ms\n", msecs);
+
+ return 1;
+}
+
+int display_init(void)
+{
+ int node = adt_path_offset(adt, "/arm-io/disp0");
+
+ if (node < 0) {
+ printf("DISP0 node not found!\n");
+ return -1;
+ }
+
+ display_is_external = adt_getprop(adt, node, "external", NULL);
+ if (display_is_external)
+ printf("display: Display is external\n");
+ else
+ printf("display: Display is internal\n");
+
+ if (cur_boot_args.video.width == 640 && cur_boot_args.video.height == 1136) {
+ printf("display: Dummy framebuffer found, initializing display\n");
+ return display_configure(NULL);
+ } else if (display_is_external) {
+ printf("display: External display found, reconfiguring\n");
+ return display_configure(NULL);
+ } else {
+ printf("display: Display is already initialized (%ldx%ld)\n", cur_boot_args.video.width,
+ cur_boot_args.video.height);
+ return 0;
+ }
+}
+
+void display_shutdown(dcp_shutdown_mode mode)
+{
+ if (iboot) {
+ dcp_ib_shutdown(iboot);
+ switch (mode) {
+ case DCP_QUIESCED:
+ printf("display: Quiescing DCP (unconditional)\n");
+ dcp_shutdown(dcp, false);
+ break;
+ case DCP_SLEEP_IF_EXTERNAL:
+ if (!display_is_external)
+ printf("display: Quiescing DCP (internal)\n");
+ else
+ printf("display: Sleeping DCP (external)\n");
+ dcp_shutdown(dcp, display_is_external);
+ break;
+ case DCP_SLEEP:
+ printf("display: Sleeping DCP (unconditional)\n");
+ dcp_shutdown(dcp, true);
+ break;
+ }
+ iboot = NULL;
+ }
+}
diff --git a/tools/src/display.h b/tools/src/display.h
new file mode 100644
index 0000000..992088e
--- /dev/null
+++ b/tools/src/display.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef DISPLAY_H
+#define DISPLAY_H
+
+#include "types.h"
+
+typedef enum _dcp_shutdown_mode {
+ DCP_QUIESCED = 0,
+ DCP_SLEEP_IF_EXTERNAL = 1,
+ DCP_SLEEP = 2,
+} dcp_shutdown_mode;
+
+extern bool display_is_external;
+
+int display_init(void);
+int display_start_dcp(void);
+int display_configure(const char *config);
+void display_shutdown(dcp_shutdown_mode mode);
+
+#endif
diff --git a/tools/src/dlmalloc/malloc.c b/tools/src/dlmalloc/malloc.c
new file mode 100644
index 0000000..31c9d21
--- /dev/null
+++ b/tools/src/dlmalloc/malloc.c
@@ -0,0 +1,6286 @@
+#include "malloc_config.h"
+
+/*
+ This is a version (aka dlmalloc) of malloc/free/realloc written by
+ Doug Lea and released to the public domain, as explained at
+ http://creativecommons.org/publicdomain/zero/1.0/ Send questions,
+ comments, complaints, performance data, etc to dl@cs.oswego.edu
+
+* Version 2.8.6 Wed Aug 29 06:57:58 2012 Doug Lea
+ Note: There may be an updated version of this malloc obtainable at
+ ftp://gee.cs.oswego.edu/pub/misc/malloc.c
+ Check before installing!
+
+* Quickstart
+
+ This library is all in one file to simplify the most common usage:
+ ftp it, compile it (-O3), and link it into another program. All of
+ the compile-time options default to reasonable values for use on
+ most platforms. You might later want to step through various
+ compile-time and dynamic tuning options.
+
+ For convenience, an include file for code using this malloc is at:
+ ftp://gee.cs.oswego.edu/pub/misc/malloc-2.8.6.h
+ You don't really need this .h file unless you call functions not
+ defined in your system include files. The .h file contains only the
+ excerpts from this file needed for using this malloc on ANSI C/C++
+ systems, so long as you haven't changed compile-time options about
+ naming and tuning parameters. If you do, then you can create your
+ own malloc.h that does include all settings by cutting at the point
+ indicated below. Note that you may already by default be using a C
+ library containing a malloc that is based on some version of this
+ malloc (for example in linux). You might still want to use the one
+ in this file to customize settings or to avoid overheads associated
+ with library versions.
+
+* Vital statistics:
+
+ Supported pointer/size_t representation: 4 or 8 bytes
+ size_t MUST be an unsigned type of the same width as
+ pointers. (If you are using an ancient system that declares
+ size_t as a signed type, or need it to be a different width
+ than pointers, you can use a previous release of this malloc
+ (e.g. 2.7.2) supporting these.)
+
+ Alignment: 8 bytes (minimum)
+ This suffices for nearly all current machines and C compilers.
+ However, you can define MALLOC_ALIGNMENT to be wider than this
+ if necessary (up to 128bytes), at the expense of using more space.
+
+ Minimum overhead per allocated chunk: 4 or 8 bytes (if 4byte sizes)
+ 8 or 16 bytes (if 8byte sizes)
+ Each malloced chunk has a hidden word of overhead holding size
+ and status information, and additional cross-check word
+ if FOOTERS is defined.
+
+ Minimum allocated size: 4-byte ptrs: 16 bytes (including overhead)
+ 8-byte ptrs: 32 bytes (including overhead)
+
+ Even a request for zero bytes (i.e., malloc(0)) returns a
+ pointer to something of the minimum allocatable size.
+ The maximum overhead wastage (i.e., number of extra bytes
+ allocated than were requested in malloc) is less than or equal
+ to the minimum size, except for requests >= mmap_threshold that
+ are serviced via mmap(), where the worst case wastage is about
+ 32 bytes plus the remainder from a system page (the minimal
+ mmap unit); typically 4096 or 8192 bytes.
+
+ Security: static-safe; optionally more or less
+ The "security" of malloc refers to the ability of malicious
+ code to accentuate the effects of errors (for example, freeing
+ space that is not currently malloc'ed or overwriting past the
+ ends of chunks) in code that calls malloc. This malloc
+ guarantees not to modify any memory locations below the base of
+ heap, i.e., static variables, even in the presence of usage
+ errors. The routines additionally detect most improper frees
+ and reallocs. All this holds as long as the static bookkeeping
+ for malloc itself is not corrupted by some other means. This
+ is only one aspect of security -- these checks do not, and
+ cannot, detect all possible programming errors.
+
+ If FOOTERS is defined nonzero, then each allocated chunk
+ carries an additional check word to verify that it was malloced
+ from its space. These check words are the same within each
+ execution of a program using malloc, but differ across
+ executions, so externally crafted fake chunks cannot be
+ freed. This improves security by rejecting frees/reallocs that
+ could corrupt heap memory, in addition to the checks preventing
+ writes to statics that are always on. This may further improve
+ security at the expense of time and space overhead. (Note that
+ FOOTERS may also be worth using with MSPACES.)
+
+ By default detected errors cause the program to abort (calling
+ "abort()"). You can override this to instead proceed past
+ errors by defining PROCEED_ON_ERROR. In this case, a bad free
+ has no effect, and a malloc that encounters a bad address
+ caused by user overwrites will ignore the bad address by
+ dropping pointers and indices to all known memory. This may
+ be appropriate for programs that should continue if at all
+ possible in the face of programming errors, although they may
+ run out of memory because dropped memory is never reclaimed.
+
+ If you don't like either of these options, you can define
+ CORRUPTION_ERROR_ACTION and USAGE_ERROR_ACTION to do anything
+ else. And if if you are sure that your program using malloc has
+ no errors or vulnerabilities, you can define INSECURE to 1,
+ which might (or might not) provide a small performance improvement.
+
+ It is also possible to limit the maximum total allocatable
+ space, using malloc_set_footprint_limit. This is not
+ designed as a security feature in itself (calls to set limits
+ are not screened or privileged), but may be useful as one
+ aspect of a secure implementation.
+
+ Thread-safety: NOT thread-safe unless USE_LOCKS defined non-zero
+ When USE_LOCKS is defined, each public call to malloc, free,
+ etc is surrounded with a lock. By default, this uses a plain
+ pthread mutex, win32 critical section, or a spin-lock if if
+ available for the platform and not disabled by setting
+ USE_SPIN_LOCKS=0. However, if USE_RECURSIVE_LOCKS is defined,
+ recursive versions are used instead (which are not required for
+ base functionality but may be needed in layered extensions).
+ Using a global lock is not especially fast, and can be a major
+ bottleneck. It is designed only to provide minimal protection
+ in concurrent environments, and to provide a basis for
+ extensions. If you are using malloc in a concurrent program,
+ consider instead using nedmalloc
+ (http://www.nedprod.com/programs/portable/nedmalloc/) or
+ ptmalloc (See http://www.malloc.de), which are derived from
+ versions of this malloc.
+
+ System requirements: Any combination of MORECORE and/or MMAP/MUNMAP
+ This malloc can use unix sbrk or any emulation (invoked using
+ the CALL_MORECORE macro) and/or mmap/munmap or any emulation
+ (invoked using CALL_MMAP/CALL_MUNMAP) to get and release system
+ memory. On most unix systems, it tends to work best if both
+ MORECORE and MMAP are enabled. On Win32, it uses emulations
+ based on VirtualAlloc. It also uses common C library functions
+ like memset.
+
+ Compliance: I believe it is compliant with the Single Unix Specification
+ (See http://www.unix.org). Also SVID/XPG, ANSI C, and probably
+ others as well.
+
+* Overview of algorithms
+
+ This is not the fastest, most space-conserving, most portable, or
+ most tunable malloc ever written. However it is among the fastest
+ while also being among the most space-conserving, portable and
+ tunable. Consistent balance across these factors results in a good
+ general-purpose allocator for malloc-intensive programs.
+
+ In most ways, this malloc is a best-fit allocator. Generally, it
+ chooses the best-fitting existing chunk for a request, with ties
+ broken in approximately least-recently-used order. (This strategy
+ normally maintains low fragmentation.) However, for requests less
+ than 256bytes, it deviates from best-fit when there is not an
+ exactly fitting available chunk by preferring to use space adjacent
+ to that used for the previous small request, as well as by breaking
+ ties in approximately most-recently-used order. (These enhance
+ locality of series of small allocations.) And for very large requests
+ (>= 256Kb by default), it relies on system memory mapping
+ facilities, if supported. (This helps avoid carrying around and
+ possibly fragmenting memory used only for large chunks.)
+
+ All operations (except malloc_stats and mallinfo) have execution
+ times that are bounded by a constant factor of the number of bits in
+ a size_t, not counting any clearing in calloc or copying in realloc,
+ or actions surrounding MORECORE and MMAP that have times
+ proportional to the number of non-contiguous regions returned by
+ system allocation routines, which is often just 1. In real-time
+ applications, you can optionally suppress segment traversals using
+ NO_SEGMENT_TRAVERSAL, which assures bounded execution even when
+ system allocators return non-contiguous spaces, at the typical
+ expense of carrying around more memory and increased fragmentation.
+
+ The implementation is not very modular and seriously overuses
+ macros. Perhaps someday all C compilers will do as good a job
+ inlining modular code as can now be done by brute-force expansion,
+ but now, enough of them seem not to.
+
+ Some compilers issue a lot of warnings about code that is
+ dead/unreachable only on some platforms, and also about intentional
+ uses of negation on unsigned types. All known cases of each can be
+ ignored.
+
+ For a longer but out of date high-level description, see
+ http://gee.cs.oswego.edu/dl/html/malloc.html
+
+* MSPACES
+ If MSPACES is defined, then in addition to malloc, free, etc.,
+ this file also defines mspace_malloc, mspace_free, etc. These
+ are versions of malloc routines that take an "mspace" argument
+ obtained using create_mspace, to control all internal bookkeeping.
+ If ONLY_MSPACES is defined, only these versions are compiled.
+ So if you would like to use this allocator for only some allocations,
+ and your system malloc for others, you can compile with
+ ONLY_MSPACES and then do something like...
+ static mspace mymspace = create_mspace(0,0); // for example
+ #define mymalloc(bytes) mspace_malloc(mymspace, bytes)
+
+ (Note: If you only need one instance of an mspace, you can instead
+ use "USE_DL_PREFIX" to relabel the global malloc.)
+
+ You can similarly create thread-local allocators by storing
+ mspaces as thread-locals. For example:
+ static __thread mspace tlms = 0;
+ void* tlmalloc(size_t bytes) {
+ if (tlms == 0) tlms = create_mspace(0, 0);
+ return mspace_malloc(tlms, bytes);
+ }
+ void tlfree(void* mem) { mspace_free(tlms, mem); }
+
+ Unless FOOTERS is defined, each mspace is completely independent.
+ You cannot allocate from one and free to another (although
+ conformance is only weakly checked, so usage errors are not always
+ caught). If FOOTERS is defined, then each chunk carries around a tag
+ indicating its originating mspace, and frees are directed to their
+ originating spaces. Normally, this requires use of locks.
+
+ ------------------------- Compile-time options ---------------------------
+
+Be careful in setting #define values for numerical constants of type
+size_t. On some systems, literal values are not automatically extended
+to size_t precision unless they are explicitly casted. You can also
+use the symbolic values MAX_SIZE_T, SIZE_T_ONE, etc below.
+
+WIN32 default: defined if _WIN32 defined
+ Defining WIN32 sets up defaults for MS environment and compilers.
+ Otherwise defaults are for unix. Beware that there seem to be some
+ cases where this malloc might not be a pure drop-in replacement for
+ Win32 malloc: Random-looking failures from Win32 GDI API's (eg;
+ SetDIBits()) may be due to bugs in some video driver implementations
+ when pixel buffers are malloc()ed, and the region spans more than
+ one VirtualAlloc()ed region. Because dlmalloc uses a small (64Kb)
+ default granularity, pixel buffers may straddle virtual allocation
+ regions more often than when using the Microsoft allocator. You can
+ avoid this by using VirtualAlloc() and VirtualFree() for all pixel
+ buffers rather than using malloc(). If this is not possible,
+ recompile this malloc with a larger DEFAULT_GRANULARITY. Note:
+ in cases where MSC and gcc (cygwin) are known to differ on WIN32,
+ conditions use _MSC_VER to distinguish them.
+
+DLMALLOC_EXPORT default: extern
+ Defines how public APIs are declared. If you want to export via a
+ Windows DLL, you might define this as
+ #define DLMALLOC_EXPORT extern __declspec(dllexport)
+ If you want a POSIX ELF shared object, you might use
+ #define DLMALLOC_EXPORT extern __attribute__((visibility("default")))
+
+MALLOC_ALIGNMENT default: (size_t)(2 * sizeof(void *))
+ Controls the minimum alignment for malloc'ed chunks. It must be a
+ power of two and at least 8, even on machines for which smaller
+ alignments would suffice. It may be defined as larger than this
+ though. Note however that code and data structures are optimized for
+ the case of 8-byte alignment.
+
+MSPACES default: 0 (false)
+ If true, compile in support for independent allocation spaces.
+ This is only supported if HAVE_MMAP is true.
+
+ONLY_MSPACES default: 0 (false)
+ If true, only compile in mspace versions, not regular versions.
+
+USE_LOCKS default: 0 (false)
+ Causes each call to each public routine to be surrounded with
+ pthread or WIN32 mutex lock/unlock. (If set true, this can be
+ overridden on a per-mspace basis for mspace versions.) If set to a
+ non-zero value other than 1, locks are used, but their
+ implementation is left out, so lock functions must be supplied manually,
+ as described below.
+
+USE_SPIN_LOCKS default: 1 iff USE_LOCKS and spin locks available
+ If true, uses custom spin locks for locking. This is currently
+ supported only gcc >= 4.1, older gccs on x86 platforms, and recent
+ MS compilers. Otherwise, posix locks or win32 critical sections are
+ used.
+
+USE_RECURSIVE_LOCKS default: not defined
+ If defined nonzero, uses recursive (aka reentrant) locks, otherwise
+ uses plain mutexes. This is not required for malloc proper, but may
+ be needed for layered allocators such as nedmalloc.
+
+LOCK_AT_FORK default: not defined
+ If defined nonzero, performs pthread_atfork upon initialization
+ to initialize child lock while holding parent lock. The implementation
+ assumes that pthread locks (not custom locks) are being used. In other
+ cases, you may need to customize the implementation.
+
+FOOTERS default: 0
+ If true, provide extra checking and dispatching by placing
+ information in the footers of allocated chunks. This adds
+ space and time overhead.
+
+INSECURE default: 0
+ If true, omit checks for usage errors and heap space overwrites.
+
+USE_DL_PREFIX default: NOT defined
+ Causes compiler to prefix all public routines with the string 'dl'.
+ This can be useful when you only want to use this malloc in one part
+ of a program, using your regular system malloc elsewhere.
+
+MALLOC_INSPECT_ALL default: NOT defined
+ If defined, compiles malloc_inspect_all and mspace_inspect_all, that
+ perform traversal of all heap space. Unless access to these
+ functions is otherwise restricted, you probably do not want to
+ include them in secure implementations.
+
+ABORT default: defined as abort()
+ Defines how to abort on failed checks. On most systems, a failed
+ check cannot die with an "assert" or even print an informative
+ message, because the underlying print routines in turn call malloc,
+ which will fail again. Generally, the best policy is to simply call
+ abort(). It's not very useful to do more than this because many
+ errors due to overwriting will show up as address faults (null, odd
+ addresses etc) rather than malloc-triggered checks, so will also
+ abort. Also, most compilers know that abort() does not return, so
+ can better optimize code conditionally calling it.
+
+PROCEED_ON_ERROR default: defined as 0 (false)
+ Controls whether detected bad addresses cause them to bypassed
+ rather than aborting. If set, detected bad arguments to free and
+ realloc are ignored. And all bookkeeping information is zeroed out
+ upon a detected overwrite of freed heap space, thus losing the
+ ability to ever return it from malloc again, but enabling the
+ application to proceed. If PROCEED_ON_ERROR is defined, the
+ static variable malloc_corruption_error_count is compiled in
+ and can be examined to see if errors have occurred. This option
+ generates slower code than the default abort policy.
+
+DEBUG default: NOT defined
+ The DEBUG setting is mainly intended for people trying to modify
+ this code or diagnose problems when porting to new platforms.
+ However, it may also be able to better isolate user errors than just
+ using runtime checks. The assertions in the check routines spell
+ out in more detail the assumptions and invariants underlying the
+ algorithms. The checking is fairly extensive, and will slow down
+ execution noticeably. Calling malloc_stats or mallinfo with DEBUG
+ set will attempt to check every non-mmapped allocated and free chunk
+ in the course of computing the summaries.
+
+ABORT_ON_ASSERT_FAILURE default: defined as 1 (true)
+ Debugging assertion failures can be nearly impossible if your
+ version of the assert macro causes malloc to be called, which will
+ lead to a cascade of further failures, blowing the runtime stack.
+ ABORT_ON_ASSERT_FAILURE cause assertions failures to call abort(),
+ which will usually make debugging easier.
+
+MALLOC_FAILURE_ACTION default: sets errno to ENOMEM, or no-op on win32
+ The action to take before "return 0" when malloc fails to be able to
+ return memory because there is none available.
+
+HAVE_MORECORE default: 1 (true) unless win32 or ONLY_MSPACES
+ True if this system supports sbrk or an emulation of it.
+
+MORECORE default: sbrk
+ The name of the sbrk-style system routine to call to obtain more
+ memory. See below for guidance on writing custom MORECORE
+ functions. The type of the argument to sbrk/MORECORE varies across
+ systems. It cannot be size_t, because it supports negative
+ arguments, so it is normally the signed type of the same width as
+ size_t (sometimes declared as "intptr_t"). It doesn't much matter
+ though. Internally, we only call it with arguments less than half
+ the max value of a size_t, which should work across all reasonable
+ possibilities, although sometimes generating compiler warnings.
+
+MORECORE_CONTIGUOUS default: 1 (true) if HAVE_MORECORE
+ If true, take advantage of fact that consecutive calls to MORECORE
+ with positive arguments always return contiguous increasing
+ addresses. This is true of unix sbrk. It does not hurt too much to
+ set it true anyway, since malloc copes with non-contiguities.
+ Setting it false when definitely non-contiguous saves time
+ and possibly wasted space it would take to discover this though.
+
+MORECORE_CANNOT_TRIM default: NOT defined
+ True if MORECORE cannot release space back to the system when given
+ negative arguments. This is generally necessary only if you are
+ using a hand-crafted MORECORE function that cannot handle negative
+ arguments.
+
+NO_SEGMENT_TRAVERSAL default: 0
+ If non-zero, suppresses traversals of memory segments
+ returned by either MORECORE or CALL_MMAP. This disables
+ merging of segments that are contiguous, and selectively
+ releasing them to the OS if unused, but bounds execution times.
+
+HAVE_MMAP default: 1 (true)
+ True if this system supports mmap or an emulation of it. If so, and
+ HAVE_MORECORE is not true, MMAP is used for all system
+ allocation. If set and HAVE_MORECORE is true as well, MMAP is
+ primarily used to directly allocate very large blocks. It is also
+ used as a backup strategy in cases where MORECORE fails to provide
+ space from system. Note: A single call to MUNMAP is assumed to be
+ able to unmap memory that may have be allocated using multiple calls
+ to MMAP, so long as they are adjacent.
+
+HAVE_MREMAP default: 1 on linux, else 0
+ If true realloc() uses mremap() to re-allocate large blocks and
+ extend or shrink allocation spaces.
+
+MMAP_CLEARS default: 1 except on WINCE.
+ True if mmap clears memory so calloc doesn't need to. This is true
+ for standard unix mmap using /dev/zero and on WIN32 except for WINCE.
+
+USE_BUILTIN_FFS default: 0 (i.e., not used)
+ Causes malloc to use the builtin ffs() function to compute indices.
+ Some compilers may recognize and intrinsify ffs to be faster than the
+ supplied C version. Also, the case of x86 using gcc is special-cased
+ to an asm instruction, so is already as fast as it can be, and so
+ this setting has no effect. Similarly for Win32 under recent MS compilers.
+ (On most x86s, the asm version is only slightly faster than the C version.)
+
+malloc_getpagesize default: derive from system includes, or 4096.
+ The system page size. To the extent possible, this malloc manages
+ memory from the system in page-size units. This may be (and
+ usually is) a function rather than a constant. This is ignored
+ if WIN32, where page size is determined using getSystemInfo during
+ initialization.
+
+USE_DEV_RANDOM default: 0 (i.e., not used)
+ Causes malloc to use /dev/random to initialize secure magic seed for
+ stamping footers. Otherwise, the current time is used.
+
+NO_MALLINFO default: 0
+ If defined, don't compile "mallinfo". This can be a simple way
+ of dealing with mismatches between system declarations and
+ those in this file.
+
+MALLINFO_FIELD_TYPE default: size_t
+ The type of the fields in the mallinfo struct. This was originally
+ defined as "int" in SVID etc, but is more usefully defined as
+ size_t. The value is used only if HAVE_USR_INCLUDE_MALLOC_H is not set
+
+NO_MALLOC_STATS default: 0
+ If defined, don't compile "malloc_stats". This avoids calls to
+ fprintf and bringing in stdio dependencies you might not want.
+
+REALLOC_ZERO_BYTES_FREES default: not defined
+ This should be set if a call to realloc with zero bytes should
+ be the same as a call to free. Some people think it should. Otherwise,
+ since this malloc returns a unique pointer for malloc(0), so does
+ realloc(p, 0).
+
+LACKS_UNISTD_H, LACKS_FCNTL_H, LACKS_SYS_PARAM_H, LACKS_SYS_MMAN_H
+LACKS_STRINGS_H, LACKS_STRING_H, LACKS_SYS_TYPES_H, LACKS_ERRNO_H
+LACKS_STDLIB_H LACKS_SCHED_H LACKS_TIME_H default: NOT defined unless on WIN32
+ Define these if your system does not have these header files.
+ You might need to manually insert some of the declarations they provide.
+
+DEFAULT_GRANULARITY default: page size if MORECORE_CONTIGUOUS,
+ system_info.dwAllocationGranularity in WIN32,
+ otherwise 64K.
+ Also settable using mallopt(M_GRANULARITY, x)
+ The unit for allocating and deallocating memory from the system. On
+ most systems with contiguous MORECORE, there is no reason to
+ make this more than a page. However, systems with MMAP tend to
+ either require or encourage larger granularities. You can increase
+ this value to prevent system allocation functions to be called so
+ often, especially if they are slow. The value must be at least one
+ page and must be a power of two. Setting to 0 causes initialization
+ to either page size or win32 region size. (Note: In previous
+ versions of malloc, the equivalent of this option was called
+ "TOP_PAD")
+
+DEFAULT_TRIM_THRESHOLD default: 2MB
+ Also settable using mallopt(M_TRIM_THRESHOLD, x)
+ The maximum amount of unused top-most memory to keep before
+ releasing via malloc_trim in free(). Automatic trimming is mainly
+ useful in long-lived programs using contiguous MORECORE. Because
+ trimming via sbrk can be slow on some systems, and can sometimes be
+ wasteful (in cases where programs immediately afterward allocate
+ more large chunks) the value should be high enough so that your
+ overall system performance would improve by releasing this much
+ memory. As a rough guide, you might set to a value close to the
+ average size of a process (program) running on your system.
+ Releasing this much memory would allow such a process to run in
+ memory. Generally, it is worth tuning trim thresholds when a
+ program undergoes phases where several large chunks are allocated
+ and released in ways that can reuse each other's storage, perhaps
+ mixed with phases where there are no such chunks at all. The trim
+ value must be greater than page size to have any useful effect. To
+ disable trimming completely, you can set to MAX_SIZE_T. Note that the trick
+ some people use of mallocing a huge space and then freeing it at
+ program startup, in an attempt to reserve system memory, doesn't
+ have the intended effect under automatic trimming, since that memory
+ will immediately be returned to the system.
+
+DEFAULT_MMAP_THRESHOLD default: 256K
+ Also settable using mallopt(M_MMAP_THRESHOLD, x)
+ The request size threshold for using MMAP to directly service a
+ request. Requests of at least this size that cannot be allocated
+ using already-existing space will be serviced via mmap. (If enough
+ normal freed space already exists it is used instead.) Using mmap
+ segregates relatively large chunks of memory so that they can be
+ individually obtained and released from the host system. A request
+ serviced through mmap is never reused by any other request (at least
+ not directly; the system may just so happen to remap successive
+ requests to the same locations). Segregating space in this way has
+ the benefits that: Mmapped space can always be individually released
+ back to the system, which helps keep the system level memory demands
+ of a long-lived program low. Also, mapped memory doesn't become
+ `locked' between other chunks, as can happen with normally allocated
+ chunks, which means that even trimming via malloc_trim would not
+ release them. However, it has the disadvantage that the space
+ cannot be reclaimed, consolidated, and then used to service later
+ requests, as happens with normal chunks. The advantages of mmap
+ nearly always outweigh disadvantages for "large" chunks, but the
+ value of "large" may vary across systems. The default is an
+ empirically derived value that works well in most systems. You can
+ disable mmap by setting to MAX_SIZE_T.
+
+MAX_RELEASE_CHECK_RATE default: 4095 unless not HAVE_MMAP
+ The number of consolidated frees between checks to release
+ unused segments when freeing. When using non-contiguous segments,
+ especially with multiple mspaces, checking only for topmost space
+ doesn't always suffice to trigger trimming. To compensate for this,
+ free() will, with a period of MAX_RELEASE_CHECK_RATE (or the
+ current number of segments, if greater) try to release unused
+ segments to the OS when freeing chunks that result in
+ consolidation. The best value for this parameter is a compromise
+ between slowing down frees with relatively costly checks that
+ rarely trigger versus holding on to unused memory. To effectively
+ disable, set to MAX_SIZE_T. This may lead to a very slight speed
+ improvement at the expense of carrying around more memory.
+*/
+
+/* Version identifier to allow people to support multiple versions */
+#ifndef DLMALLOC_VERSION
+#define DLMALLOC_VERSION 20806
+#endif /* DLMALLOC_VERSION */
+
+#ifndef DLMALLOC_EXPORT
+#define DLMALLOC_EXPORT extern
+#endif
+
+#ifndef WIN32
+#ifdef _WIN32
+#define WIN32 1
+#endif /* _WIN32 */
+#ifdef _WIN32_WCE
+#define LACKS_FCNTL_H
+#define WIN32 1
+#endif /* _WIN32_WCE */
+#endif /* WIN32 */
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <tchar.h>
+#define HAVE_MMAP 1
+#define HAVE_MORECORE 0
+#define LACKS_UNISTD_H
+#define LACKS_SYS_PARAM_H
+#define LACKS_SYS_MMAN_H
+#define LACKS_STRING_H
+#define LACKS_STRINGS_H
+#define LACKS_SYS_TYPES_H
+#define LACKS_ERRNO_H
+#define LACKS_SCHED_H
+#ifndef MALLOC_FAILURE_ACTION
+#define MALLOC_FAILURE_ACTION
+#endif /* MALLOC_FAILURE_ACTION */
+#ifndef MMAP_CLEARS
+#ifdef _WIN32_WCE /* WINCE reportedly does not clear */
+#define MMAP_CLEARS 0
+#else
+#define MMAP_CLEARS 1
+#endif /* _WIN32_WCE */
+#endif /*MMAP_CLEARS */
+#endif /* WIN32 */
+
+#if defined(DARWIN) || defined(_DARWIN)
+/* Mac OSX docs advise not to use sbrk; it seems better to use mmap */
+#ifndef HAVE_MORECORE
+#define HAVE_MORECORE 0
+#define HAVE_MMAP 1
+/* OSX allocators provide 16 byte alignment */
+#ifndef MALLOC_ALIGNMENT
+#define MALLOC_ALIGNMENT ((size_t)16U)
+#endif
+#endif /* HAVE_MORECORE */
+#endif /* DARWIN */
+
+#ifndef LACKS_SYS_TYPES_H
+#include <sys/types.h> /* For size_t */
+#endif /* LACKS_SYS_TYPES_H */
+
+/* The maximum possible size_t value has all bits set */
+#define MAX_SIZE_T (~(size_t)0)
+
+#ifndef USE_LOCKS /* ensure true if spin or recursive locks set */
+#if ((defined(USE_SPIN_LOCKS) && USE_SPIN_LOCKS != 0) || \
+ (defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0))
+#define USE_LOCKS 1
+#else
+#define USE_LOCKS 0
+#endif
+#endif /* USE_LOCKS */
+
+#if USE_LOCKS /* Spin locks for gcc >= 4.1, older gcc on x86, MSC >= 1310 */
+#if ((defined(__GNUC__) && \
+ ((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) || \
+ defined(__i386__) || defined(__x86_64__))) || \
+ (defined(_MSC_VER) && _MSC_VER>=1310))
+#ifndef USE_SPIN_LOCKS
+#define USE_SPIN_LOCKS 1
+#endif /* USE_SPIN_LOCKS */
+#elif USE_SPIN_LOCKS
+#error "USE_SPIN_LOCKS defined without implementation"
+#endif /* ... locks available... */
+#elif !defined(USE_SPIN_LOCKS)
+#define USE_SPIN_LOCKS 0
+#endif /* USE_LOCKS */
+
+#ifndef ONLY_MSPACES
+#define ONLY_MSPACES 0
+#endif /* ONLY_MSPACES */
+#ifndef MSPACES
+#if ONLY_MSPACES
+#define MSPACES 1
+#else /* ONLY_MSPACES */
+#define MSPACES 0
+#endif /* ONLY_MSPACES */
+#endif /* MSPACES */
+#ifndef MALLOC_ALIGNMENT
+#define MALLOC_ALIGNMENT ((size_t)(2 * sizeof(void *)))
+#endif /* MALLOC_ALIGNMENT */
+#ifndef FOOTERS
+#define FOOTERS 0
+#endif /* FOOTERS */
+#ifndef ABORT
+#define ABORT abort()
+#endif /* ABORT */
+#ifndef ABORT_ON_ASSERT_FAILURE
+#define ABORT_ON_ASSERT_FAILURE 1
+#endif /* ABORT_ON_ASSERT_FAILURE */
+#ifndef PROCEED_ON_ERROR
+#define PROCEED_ON_ERROR 0
+#endif /* PROCEED_ON_ERROR */
+
+#ifndef INSECURE
+#define INSECURE 0
+#endif /* INSECURE */
+#ifndef MALLOC_INSPECT_ALL
+#define MALLOC_INSPECT_ALL 0
+#endif /* MALLOC_INSPECT_ALL */
+#ifndef HAVE_MMAP
+#define HAVE_MMAP 1
+#endif /* HAVE_MMAP */
+#ifndef MMAP_CLEARS
+#define MMAP_CLEARS 1
+#endif /* MMAP_CLEARS */
+#ifndef HAVE_MREMAP
+#ifdef linux
+#define HAVE_MREMAP 1
+#define _GNU_SOURCE /* Turns on mremap() definition */
+#else /* linux */
+#define HAVE_MREMAP 0
+#endif /* linux */
+#endif /* HAVE_MREMAP */
+#ifndef MALLOC_FAILURE_ACTION
+#define MALLOC_FAILURE_ACTION errno = ENOMEM;
+#endif /* MALLOC_FAILURE_ACTION */
+#ifndef HAVE_MORECORE
+#if ONLY_MSPACES
+#define HAVE_MORECORE 0
+#else /* ONLY_MSPACES */
+#define HAVE_MORECORE 1
+#endif /* ONLY_MSPACES */
+#endif /* HAVE_MORECORE */
+#if !HAVE_MORECORE
+#define MORECORE_CONTIGUOUS 0
+#else /* !HAVE_MORECORE */
+#define MORECORE_DEFAULT sbrk
+#ifndef MORECORE_CONTIGUOUS
+#define MORECORE_CONTIGUOUS 1
+#endif /* MORECORE_CONTIGUOUS */
+#endif /* HAVE_MORECORE */
+#ifndef DEFAULT_GRANULARITY
+#if (MORECORE_CONTIGUOUS || defined(WIN32))
+#define DEFAULT_GRANULARITY (0) /* 0 means to compute in init_mparams */
+#else /* MORECORE_CONTIGUOUS */
+#define DEFAULT_GRANULARITY ((size_t)64U * (size_t)1024U)
+#endif /* MORECORE_CONTIGUOUS */
+#endif /* DEFAULT_GRANULARITY */
+#ifndef DEFAULT_TRIM_THRESHOLD
+#ifndef MORECORE_CANNOT_TRIM
+#define DEFAULT_TRIM_THRESHOLD ((size_t)2U * (size_t)1024U * (size_t)1024U)
+#else /* MORECORE_CANNOT_TRIM */
+#define DEFAULT_TRIM_THRESHOLD MAX_SIZE_T
+#endif /* MORECORE_CANNOT_TRIM */
+#endif /* DEFAULT_TRIM_THRESHOLD */
+#ifndef DEFAULT_MMAP_THRESHOLD
+#if HAVE_MMAP
+#define DEFAULT_MMAP_THRESHOLD ((size_t)256U * (size_t)1024U)
+#else /* HAVE_MMAP */
+#define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T
+#endif /* HAVE_MMAP */
+#endif /* DEFAULT_MMAP_THRESHOLD */
+#ifndef MAX_RELEASE_CHECK_RATE
+#if HAVE_MMAP
+#define MAX_RELEASE_CHECK_RATE 4095
+#else
+#define MAX_RELEASE_CHECK_RATE MAX_SIZE_T
+#endif /* HAVE_MMAP */
+#endif /* MAX_RELEASE_CHECK_RATE */
+#ifndef USE_BUILTIN_FFS
+#define USE_BUILTIN_FFS 0
+#endif /* USE_BUILTIN_FFS */
+#ifndef USE_DEV_RANDOM
+#define USE_DEV_RANDOM 0
+#endif /* USE_DEV_RANDOM */
+#ifndef NO_MALLINFO
+#define NO_MALLINFO 0
+#endif /* NO_MALLINFO */
+#ifndef MALLINFO_FIELD_TYPE
+#define MALLINFO_FIELD_TYPE size_t
+#endif /* MALLINFO_FIELD_TYPE */
+#ifndef NO_MALLOC_STATS
+#define NO_MALLOC_STATS 0
+#endif /* NO_MALLOC_STATS */
+#ifndef NO_SEGMENT_TRAVERSAL
+#define NO_SEGMENT_TRAVERSAL 0
+#endif /* NO_SEGMENT_TRAVERSAL */
+
+/*
+ mallopt tuning options. SVID/XPG defines four standard parameter
+ numbers for mallopt, normally defined in malloc.h. None of these
+ are used in this malloc, so setting them has no effect. But this
+ malloc does support the following options.
+*/
+
+#define M_TRIM_THRESHOLD (-1)
+#define M_GRANULARITY (-2)
+#define M_MMAP_THRESHOLD (-3)
+
+/* ------------------------ Mallinfo declarations ------------------------ */
+
+#if !NO_MALLINFO
+/*
+ This version of malloc supports the standard SVID/XPG mallinfo
+ routine that returns a struct containing usage properties and
+ statistics. It should work on any system that has a
+ /usr/include/malloc.h defining struct mallinfo. The main
+ declaration needed is the mallinfo struct that is returned (by-copy)
+ by mallinfo(). The malloinfo struct contains a bunch of fields that
+ are not even meaningful in this version of malloc. These fields are
+ are instead filled by mallinfo() with other numbers that might be of
+ interest.
+
+ HAVE_USR_INCLUDE_MALLOC_H should be set if you have a
+ /usr/include/malloc.h file that includes a declaration of struct
+ mallinfo. If so, it is included; else a compliant version is
+ declared below. These must be precisely the same for mallinfo() to
+ work. The original SVID version of this struct, defined on most
+ systems with mallinfo, declares all fields as ints. But some others
+ define as unsigned long. If your system defines the fields using a
+ type of different width than listed here, you MUST #include your
+ system version and #define HAVE_USR_INCLUDE_MALLOC_H.
+*/
+
+/* #define HAVE_USR_INCLUDE_MALLOC_H */
+
+#ifdef HAVE_USR_INCLUDE_MALLOC_H
+#include "/usr/include/malloc.h"
+#else /* HAVE_USR_INCLUDE_MALLOC_H */
+#ifndef STRUCT_MALLINFO_DECLARED
+/* HP-UX (and others?) redefines mallinfo unless _STRUCT_MALLINFO is defined */
+#define _STRUCT_MALLINFO
+#define STRUCT_MALLINFO_DECLARED 1
+struct mallinfo {
+ MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */
+ MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */
+ MALLINFO_FIELD_TYPE smblks; /* always 0 */
+ MALLINFO_FIELD_TYPE hblks; /* always 0 */
+ MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */
+ MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */
+ MALLINFO_FIELD_TYPE fsmblks; /* always 0 */
+ MALLINFO_FIELD_TYPE uordblks; /* total allocated space */
+ MALLINFO_FIELD_TYPE fordblks; /* total free space */
+ MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */
+};
+#endif /* STRUCT_MALLINFO_DECLARED */
+#endif /* HAVE_USR_INCLUDE_MALLOC_H */
+#endif /* NO_MALLINFO */
+
+/*
+ Try to persuade compilers to inline. The most critical functions for
+ inlining are defined as macros, so these aren't used for them.
+*/
+
+#ifndef FORCEINLINE
+ #if defined(__GNUC__)
+#define FORCEINLINE __inline __attribute__ ((always_inline))
+ #elif defined(_MSC_VER)
+ #define FORCEINLINE __forceinline
+ #endif
+#endif
+#ifndef NOINLINE
+ #if defined(__GNUC__)
+ #define NOINLINE __attribute__ ((noinline))
+ #elif defined(_MSC_VER)
+ #define NOINLINE __declspec(noinline)
+ #else
+ #define NOINLINE
+ #endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#ifndef FORCEINLINE
+ #define FORCEINLINE inline
+#endif
+#endif /* __cplusplus */
+#ifndef FORCEINLINE
+ #define FORCEINLINE
+#endif
+
+#if !ONLY_MSPACES
+
+/* ------------------- Declarations of public routines ------------------- */
+
+#ifndef USE_DL_PREFIX
+#define dlcalloc calloc
+#define dlfree free
+#define dlmalloc malloc
+#define dlmemalign memalign
+#define dlposix_memalign posix_memalign
+#define dlrealloc realloc
+#define dlrealloc_in_place realloc_in_place
+#define dlvalloc valloc
+#define dlpvalloc pvalloc
+#define dlmallinfo mallinfo
+#define dlmallopt mallopt
+#define dlmalloc_trim malloc_trim
+#define dlmalloc_stats malloc_stats
+#define dlmalloc_usable_size malloc_usable_size
+#define dlmalloc_footprint malloc_footprint
+#define dlmalloc_max_footprint malloc_max_footprint
+#define dlmalloc_footprint_limit malloc_footprint_limit
+#define dlmalloc_set_footprint_limit malloc_set_footprint_limit
+#define dlmalloc_inspect_all malloc_inspect_all
+#define dlindependent_calloc independent_calloc
+#define dlindependent_comalloc independent_comalloc
+#define dlbulk_free bulk_free
+#endif /* USE_DL_PREFIX */
+
+/*
+ malloc(size_t n)
+ Returns a pointer to a newly allocated chunk of at least n bytes, or
+ null if no space is available, in which case errno is set to ENOMEM
+ on ANSI C systems.
+
+ If n is zero, malloc returns a minimum-sized chunk. (The minimum
+ size is 16 bytes on most 32bit systems, and 32 bytes on 64bit
+ systems.) Note that size_t is an unsigned type, so calls with
+ arguments that would be negative if signed are interpreted as
+ requests for huge amounts of space, which will often fail. The
+ maximum supported value of n differs across systems, but is in all
+ cases less than the maximum representable value of a size_t.
+*/
+DLMALLOC_EXPORT void* dlmalloc(size_t);
+
+/*
+ free(void* p)
+ Releases the chunk of memory pointed to by p, that had been previously
+ allocated using malloc or a related routine such as realloc.
+ It has no effect if p is null. If p was not malloced or already
+ freed, free(p) will by default cause the current program to abort.
+*/
+DLMALLOC_EXPORT void dlfree(void*);
+
+/*
+ calloc(size_t n_elements, size_t element_size);
+ Returns a pointer to n_elements * element_size bytes, with all locations
+ set to zero.
+*/
+DLMALLOC_EXPORT void* dlcalloc(size_t, size_t);
+
+/*
+ realloc(void* p, size_t n)
+ Returns a pointer to a chunk of size n that contains the same data
+ as does chunk p up to the minimum of (n, p's size) bytes, or null
+ if no space is available.
+
+ The returned pointer may or may not be the same as p. The algorithm
+ prefers extending p in most cases when possible, otherwise it
+ employs the equivalent of a malloc-copy-free sequence.
+
+ If p is null, realloc is equivalent to malloc.
+
+ If space is not available, realloc returns null, errno is set (if on
+ ANSI) and p is NOT freed.
+
+ if n is for fewer bytes than already held by p, the newly unused
+ space is lopped off and freed if possible. realloc with a size
+ argument of zero (re)allocates a minimum-sized chunk.
+
+ The old unix realloc convention of allowing the last-free'd chunk
+ to be used as an argument to realloc is not supported.
+*/
+DLMALLOC_EXPORT void* dlrealloc(void*, size_t);
+
+/*
+ realloc_in_place(void* p, size_t n)
+ Resizes the space allocated for p to size n, only if this can be
+ done without moving p (i.e., only if there is adjacent space
+ available if n is greater than p's current allocated size, or n is
+ less than or equal to p's size). This may be used instead of plain
+ realloc if an alternative allocation strategy is needed upon failure
+ to expand space; for example, reallocation of a buffer that must be
+ memory-aligned or cleared. You can use realloc_in_place to trigger
+ these alternatives only when needed.
+
+ Returns p if successful; otherwise null.
+*/
+DLMALLOC_EXPORT void* dlrealloc_in_place(void*, size_t);
+
+/*
+ memalign(size_t alignment, size_t n);
+ Returns a pointer to a newly allocated chunk of n bytes, aligned
+ in accord with the alignment argument.
+
+ The alignment argument should be a power of two. If the argument is
+ not a power of two, the nearest greater power is used.
+ 8-byte alignment is guaranteed by normal malloc calls, so don't
+ bother calling memalign with an argument of 8 or less.
+
+ Overreliance on memalign is a sure way to fragment space.
+*/
+DLMALLOC_EXPORT void* dlmemalign(size_t, size_t);
+
+/*
+ int posix_memalign(void** pp, size_t alignment, size_t n);
+ Allocates a chunk of n bytes, aligned in accord with the alignment
+ argument. Differs from memalign only in that it (1) assigns the
+ allocated memory to *pp rather than returning it, (2) fails and
+ returns EINVAL if the alignment is not a power of two (3) fails and
+ returns ENOMEM if memory cannot be allocated.
+*/
+DLMALLOC_EXPORT int dlposix_memalign(void**, size_t, size_t);
+
+/*
+ valloc(size_t n);
+ Equivalent to memalign(pagesize, n), where pagesize is the page
+ size of the system. If the pagesize is unknown, 4096 is used.
+*/
+DLMALLOC_EXPORT void* dlvalloc(size_t);
+
+/*
+ mallopt(int parameter_number, int parameter_value)
+ Sets tunable parameters The format is to provide a
+ (parameter-number, parameter-value) pair. mallopt then sets the
+ corresponding parameter to the argument value if it can (i.e., so
+ long as the value is meaningful), and returns 1 if successful else
+ 0. To workaround the fact that mallopt is specified to use int,
+ not size_t parameters, the value -1 is specially treated as the
+ maximum unsigned size_t value.
+
+ SVID/XPG/ANSI defines four standard param numbers for mallopt,
+ normally defined in malloc.h. None of these are use in this malloc,
+ so setting them has no effect. But this malloc also supports other
+ options in mallopt. See below for details. Briefly, supported
+ parameters are as follows (listed defaults are for "typical"
+ configurations).
+
+ Symbol param # default allowed param values
+ M_TRIM_THRESHOLD -1 2*1024*1024 any (-1 disables)
+ M_GRANULARITY -2 page size any power of 2 >= page size
+ M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support)
+*/
+DLMALLOC_EXPORT int dlmallopt(int, int);
+
+/*
+ malloc_footprint();
+ Returns the number of bytes obtained from the system. The total
+ number of bytes allocated by malloc, realloc etc., is less than this
+ value. Unlike mallinfo, this function returns only a precomputed
+ result, so can be called frequently to monitor memory consumption.
+ Even if locks are otherwise defined, this function does not use them,
+ so results might not be up to date.
+*/
+DLMALLOC_EXPORT size_t dlmalloc_footprint(void);
+
+/*
+ malloc_max_footprint();
+ Returns the maximum number of bytes obtained from the system. This
+ value will be greater than current footprint if deallocated space
+ has been reclaimed by the system. The peak number of bytes allocated
+ by malloc, realloc etc., is less than this value. Unlike mallinfo,
+ this function returns only a precomputed result, so can be called
+ frequently to monitor memory consumption. Even if locks are
+ otherwise defined, this function does not use them, so results might
+ not be up to date.
+*/
+DLMALLOC_EXPORT size_t dlmalloc_max_footprint(void);
+
+/*
+ malloc_footprint_limit();
+ Returns the number of bytes that the heap is allowed to obtain from
+ the system, returning the last value returned by
+ malloc_set_footprint_limit, or the maximum size_t value if
+ never set. The returned value reflects a permission. There is no
+ guarantee that this number of bytes can actually be obtained from
+ the system.
+*/
+DLMALLOC_EXPORT size_t dlmalloc_footprint_limit(void);
+
+/*
+ malloc_set_footprint_limit();
+ Sets the maximum number of bytes to obtain from the system, causing
+ failure returns from malloc and related functions upon attempts to
+ exceed this value. The argument value may be subject to page
+ rounding to an enforceable limit; this actual value is returned.
+ Using an argument of the maximum possible size_t effectively
+ disables checks. If the argument is less than or equal to the
+ current malloc_footprint, then all future allocations that require
+ additional system memory will fail. However, invocation cannot
+ retroactively deallocate existing used memory.
+*/
+DLMALLOC_EXPORT size_t dlmalloc_set_footprint_limit(size_t bytes);
+
+#if MALLOC_INSPECT_ALL
+/*
+ malloc_inspect_all(void(*handler)(void *start,
+ void *end,
+ size_t used_bytes,
+ void* callback_arg),
+ void* arg);
+ Traverses the heap and calls the given handler for each managed
+ region, skipping all bytes that are (or may be) used for bookkeeping
+ purposes. Traversal does not include include chunks that have been
+ directly memory mapped. Each reported region begins at the start
+ address, and continues up to but not including the end address. The
+ first used_bytes of the region contain allocated data. If
+ used_bytes is zero, the region is unallocated. The handler is
+ invoked with the given callback argument. If locks are defined, they
+ are held during the entire traversal. It is a bad idea to invoke
+ other malloc functions from within the handler.
+
+ For example, to count the number of in-use chunks with size greater
+ than 1000, you could write:
+ static int count = 0;
+ void count_chunks(void* start, void* end, size_t used, void* arg) {
+ if (used >= 1000) ++count;
+ }
+ then:
+ malloc_inspect_all(count_chunks, NULL);
+
+ malloc_inspect_all is compiled only if MALLOC_INSPECT_ALL is defined.
+*/
+DLMALLOC_EXPORT void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*),
+ void* arg);
+
+#endif /* MALLOC_INSPECT_ALL */
+
+#if !NO_MALLINFO
+/*
+ mallinfo()
+ Returns (by copy) a struct containing various summary statistics:
+
+ arena: current total non-mmapped bytes allocated from system
+ ordblks: the number of free chunks
+ smblks: always zero.
+ hblks: current number of mmapped regions
+ hblkhd: total bytes held in mmapped regions
+ usmblks: the maximum total allocated space. This will be greater
+ than current total if trimming has occurred.
+ fsmblks: always zero
+ uordblks: current total allocated space (normal or mmapped)
+ fordblks: total free space
+ keepcost: the maximum number of bytes that could ideally be released
+ back to system via malloc_trim. ("ideally" means that
+ it ignores page restrictions etc.)
+
+ Because these fields are ints, but internal bookkeeping may
+ be kept as longs, the reported values may wrap around zero and
+ thus be inaccurate.
+*/
+DLMALLOC_EXPORT struct mallinfo dlmallinfo(void);
+#endif /* NO_MALLINFO */
+
+/*
+ independent_calloc(size_t n_elements, size_t element_size, void* chunks[]);
+
+ independent_calloc is similar to calloc, but instead of returning a
+ single cleared space, it returns an array of pointers to n_elements
+ independent elements that can hold contents of size elem_size, each
+ of which starts out cleared, and can be independently freed,
+ realloc'ed etc. The elements are guaranteed to be adjacently
+ allocated (this is not guaranteed to occur with multiple callocs or
+ mallocs), which may also improve cache locality in some
+ applications.
+
+ The "chunks" argument is optional (i.e., may be null, which is
+ probably the most typical usage). If it is null, the returned array
+ is itself dynamically allocated and should also be freed when it is
+ no longer needed. Otherwise, the chunks array must be of at least
+ n_elements in length. It is filled in with the pointers to the
+ chunks.
+
+ In either case, independent_calloc returns this pointer array, or
+ null if the allocation failed. If n_elements is zero and "chunks"
+ is null, it returns a chunk representing an array with zero elements
+ (which should be freed if not wanted).
+
+ Each element must be freed when it is no longer needed. This can be
+ done all at once using bulk_free.
+
+ independent_calloc simplifies and speeds up implementations of many
+ kinds of pools. It may also be useful when constructing large data
+ structures that initially have a fixed number of fixed-sized nodes,
+ but the number is not known at compile time, and some of the nodes
+ may later need to be freed. For example:
+
+ struct Node { int item; struct Node* next; };
+
+ struct Node* build_list() {
+ struct Node** pool;
+ int n = read_number_of_nodes_needed();
+ if (n <= 0) return 0;
+ pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0);
+ if (pool == 0) die();
+ // organize into a linked list...
+ struct Node* first = pool[0];
+ for (i = 0; i < n-1; ++i)
+ pool[i]->next = pool[i+1];
+ free(pool); // Can now free the array (or not, if it is needed later)
+ return first;
+ }
+*/
+DLMALLOC_EXPORT void** dlindependent_calloc(size_t, size_t, void**);
+
+/*
+ independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]);
+
+ independent_comalloc allocates, all at once, a set of n_elements
+ chunks with sizes indicated in the "sizes" array. It returns
+ an array of pointers to these elements, each of which can be
+ independently freed, realloc'ed etc. The elements are guaranteed to
+ be adjacently allocated (this is not guaranteed to occur with
+ multiple callocs or mallocs), which may also improve cache locality
+ in some applications.
+
+ The "chunks" argument is optional (i.e., may be null). If it is null
+ the returned array is itself dynamically allocated and should also
+ be freed when it is no longer needed. Otherwise, the chunks array
+ must be of at least n_elements in length. It is filled in with the
+ pointers to the chunks.
+
+ In either case, independent_comalloc returns this pointer array, or
+ null if the allocation failed. If n_elements is zero and chunks is
+ null, it returns a chunk representing an array with zero elements
+ (which should be freed if not wanted).
+
+ Each element must be freed when it is no longer needed. This can be
+ done all at once using bulk_free.
+
+ independent_comallac differs from independent_calloc in that each
+ element may have a different size, and also that it does not
+ automatically clear elements.
+
+ independent_comalloc can be used to speed up allocation in cases
+ where several structs or objects must always be allocated at the
+ same time. For example:
+
+ struct Head { ... }
+ struct Foot { ... }
+
+ void send_message(char* msg) {
+ int msglen = strlen(msg);
+ size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) };
+ void* chunks[3];
+ if (independent_comalloc(3, sizes, chunks) == 0)
+ die();
+ struct Head* head = (struct Head*)(chunks[0]);
+ char* body = (char*)(chunks[1]);
+ struct Foot* foot = (struct Foot*)(chunks[2]);
+ // ...
+ }
+
+ In general though, independent_comalloc is worth using only for
+ larger values of n_elements. For small values, you probably won't
+ detect enough difference from series of malloc calls to bother.
+
+ Overuse of independent_comalloc can increase overall memory usage,
+ since it cannot reuse existing noncontiguous small chunks that
+ might be available for some of the elements.
+*/
+DLMALLOC_EXPORT void** dlindependent_comalloc(size_t, size_t*, void**);
+
+/*
+ bulk_free(void* array[], size_t n_elements)
+ Frees and clears (sets to null) each non-null pointer in the given
+ array. This is likely to be faster than freeing them one-by-one.
+ If footers are used, pointers that have been allocated in different
+ mspaces are not freed or cleared, and the count of all such pointers
+ is returned. For large arrays of pointers with poor locality, it
+ may be worthwhile to sort this array before calling bulk_free.
+*/
+DLMALLOC_EXPORT size_t dlbulk_free(void**, size_t n_elements);
+
+/*
+ pvalloc(size_t n);
+ Equivalent to valloc(minimum-page-that-holds(n)), that is,
+ round up n to nearest pagesize.
+ */
+DLMALLOC_EXPORT void* dlpvalloc(size_t);
+
+/*
+ malloc_trim(size_t pad);
+
+ If possible, gives memory back to the system (via negative arguments
+ to sbrk) if there is unused memory at the `high' end of the malloc
+ pool or in unused MMAP segments. You can call this after freeing
+ large blocks of memory to potentially reduce the system-level memory
+ requirements of a program. However, it cannot guarantee to reduce
+ memory. Under some allocation patterns, some large free blocks of
+ memory will be locked between two used chunks, so they cannot be
+ given back to the system.
+
+ The `pad' argument to malloc_trim represents the amount of free
+ trailing space to leave untrimmed. If this argument is zero, only
+ the minimum amount of memory to maintain internal data structures
+ will be left. Non-zero arguments can be supplied to maintain enough
+ trailing space to service future expected allocations without having
+ to re-obtain memory from the system.
+
+ Malloc_trim returns 1 if it actually released any memory, else 0.
+*/
+DLMALLOC_EXPORT int dlmalloc_trim(size_t);
+
+/*
+ malloc_stats();
+ Prints on stderr the amount of space obtained from the system (both
+ via sbrk and mmap), the maximum amount (which may be more than
+ current if malloc_trim and/or munmap got called), and the current
+ number of bytes allocated via malloc (or realloc, etc) but not yet
+ freed. Note that this is the number of bytes allocated, not the
+ number requested. It will be larger than the number requested
+ because of alignment and bookkeeping overhead. Because it includes
+ alignment wastage as being in use, this figure may be greater than
+ zero even when no user-level chunks are allocated.
+
+ The reported current and maximum system memory can be inaccurate if
+ a program makes other calls to system memory allocation functions
+ (normally sbrk) outside of malloc.
+
+ malloc_stats prints only the most commonly interesting statistics.
+ More information can be obtained by calling mallinfo.
+*/
+DLMALLOC_EXPORT void dlmalloc_stats(void);
+
+/*
+ malloc_usable_size(void* p);
+
+ Returns the number of bytes you can actually use in
+ an allocated chunk, which may be more than you requested (although
+ often not) due to alignment and minimum size constraints.
+ You can use this many bytes without worrying about
+ overwriting other allocated objects. This is not a particularly great
+ programming practice. malloc_usable_size can be more useful in
+ debugging and assertions, for example:
+
+ p = malloc(n);
+ assert(malloc_usable_size(p) >= 256);
+*/
+size_t dlmalloc_usable_size(void*);
+
+#endif /* ONLY_MSPACES */
+
+#if MSPACES
+
+/*
+ mspace is an opaque type representing an independent
+ region of space that supports mspace_malloc, etc.
+*/
+typedef void* mspace;
+
+/*
+ create_mspace creates and returns a new independent space with the
+ given initial capacity, or, if 0, the default granularity size. It
+ returns null if there is no system memory available to create the
+ space. If argument locked is non-zero, the space uses a separate
+ lock to control access. The capacity of the space will grow
+ dynamically as needed to service mspace_malloc requests. You can
+ control the sizes of incremental increases of this space by
+ compiling with a different DEFAULT_GRANULARITY or dynamically
+ setting with mallopt(M_GRANULARITY, value).
+*/
+DLMALLOC_EXPORT mspace create_mspace(size_t capacity, int locked);
+
+/*
+ destroy_mspace destroys the given space, and attempts to return all
+ of its memory back to the system, returning the total number of
+ bytes freed. After destruction, the results of access to all memory
+ used by the space become undefined.
+*/
+DLMALLOC_EXPORT size_t destroy_mspace(mspace msp);
+
+/*
+ create_mspace_with_base uses the memory supplied as the initial base
+ of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this
+ space is used for bookkeeping, so the capacity must be at least this
+ large. (Otherwise 0 is returned.) When this initial space is
+ exhausted, additional memory will be obtained from the system.
+ Destroying this space will deallocate all additionally allocated
+ space (if possible) but not the initial base.
+*/
+DLMALLOC_EXPORT mspace create_mspace_with_base(void* base, size_t capacity, int locked);
+
+/*
+ mspace_track_large_chunks controls whether requests for large chunks
+ are allocated in their own untracked mmapped regions, separate from
+ others in this mspace. By default large chunks are not tracked,
+ which reduces fragmentation. However, such chunks are not
+ necessarily released to the system upon destroy_mspace. Enabling
+ tracking by setting to true may increase fragmentation, but avoids
+ leakage when relying on destroy_mspace to release all memory
+ allocated using this space. The function returns the previous
+ setting.
+*/
+DLMALLOC_EXPORT int mspace_track_large_chunks(mspace msp, int enable);
+
+
+/*
+ mspace_malloc behaves as malloc, but operates within
+ the given space.
+*/
+DLMALLOC_EXPORT void* mspace_malloc(mspace msp, size_t bytes);
+
+/*
+ mspace_free behaves as free, but operates within
+ the given space.
+
+ If compiled with FOOTERS==1, mspace_free is not actually needed.
+ free may be called instead of mspace_free because freed chunks from
+ any space are handled by their originating spaces.
+*/
+DLMALLOC_EXPORT void mspace_free(mspace msp, void* mem);
+
+/*
+ mspace_realloc behaves as realloc, but operates within
+ the given space.
+
+ If compiled with FOOTERS==1, mspace_realloc is not actually
+ needed. realloc may be called instead of mspace_realloc because
+ realloced chunks from any space are handled by their originating
+ spaces.
+*/
+DLMALLOC_EXPORT void* mspace_realloc(mspace msp, void* mem, size_t newsize);
+
+/*
+ mspace_calloc behaves as calloc, but operates within
+ the given space.
+*/
+DLMALLOC_EXPORT void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size);
+
+/*
+ mspace_memalign behaves as memalign, but operates within
+ the given space.
+*/
+DLMALLOC_EXPORT void* mspace_memalign(mspace msp, size_t alignment, size_t bytes);
+
+/*
+ mspace_independent_calloc behaves as independent_calloc, but
+ operates within the given space.
+*/
+DLMALLOC_EXPORT void** mspace_independent_calloc(mspace msp, size_t n_elements,
+ size_t elem_size, void* chunks[]);
+
+/*
+ mspace_independent_comalloc behaves as independent_comalloc, but
+ operates within the given space.
+*/
+DLMALLOC_EXPORT void** mspace_independent_comalloc(mspace msp, size_t n_elements,
+ size_t sizes[], void* chunks[]);
+
+/*
+ mspace_footprint() returns the number of bytes obtained from the
+ system for this space.
+*/
+DLMALLOC_EXPORT size_t mspace_footprint(mspace msp);
+
+/*
+ mspace_max_footprint() returns the peak number of bytes obtained from the
+ system for this space.
+*/
+DLMALLOC_EXPORT size_t mspace_max_footprint(mspace msp);
+
+
+#if !NO_MALLINFO
+/*
+ mspace_mallinfo behaves as mallinfo, but reports properties of
+ the given space.
+*/
+DLMALLOC_EXPORT struct mallinfo mspace_mallinfo(mspace msp);
+#endif /* NO_MALLINFO */
+
+/*
+ malloc_usable_size(void* p) behaves the same as malloc_usable_size;
+*/
+DLMALLOC_EXPORT size_t mspace_usable_size(const void* mem);
+
+/*
+ mspace_malloc_stats behaves as malloc_stats, but reports
+ properties of the given space.
+*/
+DLMALLOC_EXPORT void mspace_malloc_stats(mspace msp);
+
+/*
+ mspace_trim behaves as malloc_trim, but
+ operates within the given space.
+*/
+DLMALLOC_EXPORT int mspace_trim(mspace msp, size_t pad);
+
+/*
+ An alias for mallopt.
+*/
+DLMALLOC_EXPORT int mspace_mallopt(int, int);
+
+#endif /* MSPACES */
+
+#ifdef __cplusplus
+} /* end of extern "C" */
+#endif /* __cplusplus */
+
+/*
+ ========================================================================
+ To make a fully customizable malloc.h header file, cut everything
+ above this line, put into file malloc.h, edit to suit, and #include it
+ on the next line, as well as in programs that use this malloc.
+ ========================================================================
+*/
+
+/* #include "malloc.h" */
+
+/*------------------------------ internal #includes ---------------------- */
+
+#ifdef _MSC_VER
+#pragma warning( disable : 4146 ) /* no "unsigned" warnings */
+#endif /* _MSC_VER */
+#if !NO_MALLOC_STATS
+#include <stdio.h> /* for printing in malloc_stats */
+#endif /* NO_MALLOC_STATS */
+#ifndef LACKS_ERRNO_H
+#include <errno.h> /* for MALLOC_FAILURE_ACTION */
+#endif /* LACKS_ERRNO_H */
+#ifdef DEBUG
+#if ABORT_ON_ASSERT_FAILURE
+#undef assert
+#define assert(x) if(!(x)) ABORT
+#else /* ABORT_ON_ASSERT_FAILURE */
+#include <assert.h>
+#endif /* ABORT_ON_ASSERT_FAILURE */
+#else /* DEBUG */
+#ifndef assert
+#define assert(x)
+#endif
+#define DEBUG 0
+#endif /* DEBUG */
+#if !defined(WIN32) && !defined(LACKS_TIME_H)
+#include <time.h> /* for magic initialization */
+#endif /* WIN32 */
+#ifndef LACKS_STDLIB_H
+#include <stdlib.h> /* for abort() */
+#endif /* LACKS_STDLIB_H */
+#ifndef LACKS_STRING_H
+#include <string.h> /* for memset etc */
+#endif /* LACKS_STRING_H */
+#if USE_BUILTIN_FFS
+#ifndef LACKS_STRINGS_H
+#include <strings.h> /* for ffs */
+#endif /* LACKS_STRINGS_H */
+#endif /* USE_BUILTIN_FFS */
+#if HAVE_MMAP
+#ifndef LACKS_SYS_MMAN_H
+/* On some versions of linux, mremap decl in mman.h needs __USE_GNU set */
+#if (defined(linux) && !defined(__USE_GNU))
+#define __USE_GNU 1
+#include <sys/mman.h> /* for mmap */
+#undef __USE_GNU
+#else
+#include <sys/mman.h> /* for mmap */
+#endif /* linux */
+#endif /* LACKS_SYS_MMAN_H */
+#ifndef LACKS_FCNTL_H
+#include <fcntl.h>
+#endif /* LACKS_FCNTL_H */
+#endif /* HAVE_MMAP */
+#ifndef LACKS_UNISTD_H
+#include <unistd.h> /* for sbrk, sysconf */
+#else /* LACKS_UNISTD_H */
+#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__)
+extern void* sbrk(ptrdiff_t);
+#endif /* FreeBSD etc */
+#endif /* LACKS_UNISTD_H */
+
+/* Declarations for locking */
+#if USE_LOCKS
+#ifndef WIN32
+#if defined (__SVR4) && defined (__sun) /* solaris */
+#include <thread.h>
+#elif !defined(LACKS_SCHED_H)
+#include <sched.h>
+#endif /* solaris or LACKS_SCHED_H */
+#if (defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0) || !USE_SPIN_LOCKS
+#include <pthread.h>
+#endif /* USE_RECURSIVE_LOCKS ... */
+#elif defined(_MSC_VER)
+#ifndef _M_AMD64
+/* These are already defined on AMD64 builds */
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+LONG __cdecl _InterlockedCompareExchange(LONG volatile *Dest, LONG Exchange, LONG Comp);
+LONG __cdecl _InterlockedExchange(LONG volatile *Target, LONG Value);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* _M_AMD64 */
+#pragma intrinsic (_InterlockedCompareExchange)
+#pragma intrinsic (_InterlockedExchange)
+#define interlockedcompareexchange _InterlockedCompareExchange
+#define interlockedexchange _InterlockedExchange
+#elif defined(WIN32) && defined(__GNUC__)
+#define interlockedcompareexchange(a, b, c) __sync_val_compare_and_swap(a, c, b)
+#define interlockedexchange __sync_lock_test_and_set
+#endif /* Win32 */
+#else /* USE_LOCKS */
+#endif /* USE_LOCKS */
+
+#ifndef LOCK_AT_FORK
+#define LOCK_AT_FORK 0
+#endif
+
+/* Declarations for bit scanning on win32 */
+#if defined(_MSC_VER) && _MSC_VER>=1300
+#ifndef BitScanForward /* Try to avoid pulling in WinNT.h */
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+unsigned char _BitScanForward(unsigned long *index, unsigned long mask);
+unsigned char _BitScanReverse(unsigned long *index, unsigned long mask);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#define BitScanForward _BitScanForward
+#define BitScanReverse _BitScanReverse
+#pragma intrinsic(_BitScanForward)
+#pragma intrinsic(_BitScanReverse)
+#endif /* BitScanForward */
+#endif /* defined(_MSC_VER) && _MSC_VER>=1300 */
+
+#ifndef WIN32
+#ifndef malloc_getpagesize
+# ifdef _SC_PAGESIZE /* some SVR4 systems omit an underscore */
+# ifndef _SC_PAGE_SIZE
+# define _SC_PAGE_SIZE _SC_PAGESIZE
+# endif
+# endif
+# ifdef _SC_PAGE_SIZE
+# define malloc_getpagesize sysconf(_SC_PAGE_SIZE)
+# else
+# if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE)
+ extern size_t getpagesize();
+# define malloc_getpagesize getpagesize()
+# else
+# ifdef WIN32 /* use supplied emulation of getpagesize */
+# define malloc_getpagesize getpagesize()
+# else
+# ifndef LACKS_SYS_PARAM_H
+# include <sys/param.h>
+# endif
+# ifdef EXEC_PAGESIZE
+# define malloc_getpagesize EXEC_PAGESIZE
+# else
+# ifdef NBPG
+# ifndef CLSIZE
+# define malloc_getpagesize NBPG
+# else
+# define malloc_getpagesize (NBPG * CLSIZE)
+# endif
+# else
+# ifdef NBPC
+# define malloc_getpagesize NBPC
+# else
+# ifdef PAGESIZE
+# define malloc_getpagesize PAGESIZE
+# else /* just guess */
+# define malloc_getpagesize ((size_t)4096U)
+# endif
+# endif
+# endif
+# endif
+# endif
+# endif
+# endif
+#endif
+#endif
+
+/* ------------------- size_t and alignment properties -------------------- */
+
+/* The byte and bit size of a size_t */
+#define SIZE_T_SIZE (sizeof(size_t))
+#define SIZE_T_BITSIZE (sizeof(size_t) << 3)
+
+/* Some constants coerced to size_t */
+/* Annoying but necessary to avoid errors on some platforms */
+#define SIZE_T_ZERO ((size_t)0)
+#define SIZE_T_ONE ((size_t)1)
+#define SIZE_T_TWO ((size_t)2)
+#define SIZE_T_FOUR ((size_t)4)
+#define TWO_SIZE_T_SIZES (SIZE_T_SIZE<<1)
+#define FOUR_SIZE_T_SIZES (SIZE_T_SIZE<<2)
+#define SIX_SIZE_T_SIZES (FOUR_SIZE_T_SIZES+TWO_SIZE_T_SIZES)
+#define HALF_MAX_SIZE_T (MAX_SIZE_T / 2U)
+
+/* The bit mask value corresponding to MALLOC_ALIGNMENT */
+#define CHUNK_ALIGN_MASK (MALLOC_ALIGNMENT - SIZE_T_ONE)
+
+/* True if address a has acceptable alignment */
+#define is_aligned(A) (((size_t)((A)) & (CHUNK_ALIGN_MASK)) == 0)
+
+/* the number of bytes to offset an address to align it */
+#define align_offset(A)\
+ ((((size_t)(A) & CHUNK_ALIGN_MASK) == 0)? 0 :\
+ ((MALLOC_ALIGNMENT - ((size_t)(A) & CHUNK_ALIGN_MASK)) & CHUNK_ALIGN_MASK))
+
+/* -------------------------- MMAP preliminaries ------------------------- */
+
+/*
+ If HAVE_MORECORE or HAVE_MMAP are false, we just define calls and
+ checks to fail so compiler optimizer can delete code rather than
+ using so many "#if"s.
+*/
+
+
+/* MORECORE and MMAP must return MFAIL on failure */
+#define MFAIL ((void*)(MAX_SIZE_T))
+#define CMFAIL ((char*)(MFAIL)) /* defined for convenience */
+
+#if HAVE_MMAP
+
+#ifndef WIN32
+#define MUNMAP_DEFAULT(a, s) munmap((a), (s))
+#define MMAP_PROT (PROT_READ|PROT_WRITE)
+#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
+#define MAP_ANONYMOUS MAP_ANON
+#endif /* MAP_ANON */
+#ifdef MAP_ANONYMOUS
+#define MMAP_FLAGS (MAP_PRIVATE|MAP_ANONYMOUS)
+#define MMAP_DEFAULT(s) mmap(0, (s), MMAP_PROT, MMAP_FLAGS, -1, 0)
+#else /* MAP_ANONYMOUS */
+/*
+ Nearly all versions of mmap support MAP_ANONYMOUS, so the following
+ is unlikely to be needed, but is supplied just in case.
+*/
+#define MMAP_FLAGS (MAP_PRIVATE)
+static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */
+#define MMAP_DEFAULT(s) ((dev_zero_fd < 0) ? \
+ (dev_zero_fd = open("/dev/zero", O_RDWR), \
+ mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) : \
+ mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0))
+#endif /* MAP_ANONYMOUS */
+
+#define DIRECT_MMAP_DEFAULT(s) MMAP_DEFAULT(s)
+
+#else /* WIN32 */
+
+/* Win32 MMAP via VirtualAlloc */
+static FORCEINLINE void* win32mmap(size_t size) {
+ void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
+ return (ptr != 0)? ptr: MFAIL;
+}
+
+/* For direct MMAP, use MEM_TOP_DOWN to minimize interference */
+static FORCEINLINE void* win32direct_mmap(size_t size) {
+ void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN,
+ PAGE_READWRITE);
+ return (ptr != 0)? ptr: MFAIL;
+}
+
+/* This function supports releasing coalesed segments */
+static FORCEINLINE int win32munmap(void* ptr, size_t size) {
+ MEMORY_BASIC_INFORMATION minfo;
+ char* cptr = (char*)ptr;
+ while (size) {
+ if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0)
+ return -1;
+ if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr ||
+ minfo.State != MEM_COMMIT || minfo.RegionSize > size)
+ return -1;
+ if (VirtualFree(cptr, 0, MEM_RELEASE) == 0)
+ return -1;
+ cptr += minfo.RegionSize;
+ size -= minfo.RegionSize;
+ }
+ return 0;
+}
+
+#define MMAP_DEFAULT(s) win32mmap(s)
+#define MUNMAP_DEFAULT(a, s) win32munmap((a), (s))
+#define DIRECT_MMAP_DEFAULT(s) win32direct_mmap(s)
+#endif /* WIN32 */
+#endif /* HAVE_MMAP */
+
+#if HAVE_MREMAP
+#ifndef WIN32
+#define MREMAP_DEFAULT(addr, osz, nsz, mv) mremap((addr), (osz), (nsz), (mv))
+#endif /* WIN32 */
+#endif /* HAVE_MREMAP */
+
+/**
+ * Define CALL_MORECORE
+ */
+#if HAVE_MORECORE
+ #ifdef MORECORE
+ #define CALL_MORECORE(S) MORECORE(S)
+ #else /* MORECORE */
+ #define CALL_MORECORE(S) MORECORE_DEFAULT(S)
+ #endif /* MORECORE */
+#else /* HAVE_MORECORE */
+ #define CALL_MORECORE(S) MFAIL
+#endif /* HAVE_MORECORE */
+
+/**
+ * Define CALL_MMAP/CALL_MUNMAP/CALL_DIRECT_MMAP
+ */
+#if HAVE_MMAP
+ #define USE_MMAP_BIT (SIZE_T_ONE)
+
+ #ifdef MMAP
+ #define CALL_MMAP(s) MMAP(s)
+ #else /* MMAP */
+ #define CALL_MMAP(s) MMAP_DEFAULT(s)
+ #endif /* MMAP */
+ #ifdef MUNMAP
+ #define CALL_MUNMAP(a, s) MUNMAP((a), (s))
+ #else /* MUNMAP */
+ #define CALL_MUNMAP(a, s) MUNMAP_DEFAULT((a), (s))
+ #endif /* MUNMAP */
+ #ifdef DIRECT_MMAP
+ #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s)
+ #else /* DIRECT_MMAP */
+ #define CALL_DIRECT_MMAP(s) DIRECT_MMAP_DEFAULT(s)
+ #endif /* DIRECT_MMAP */
+#else /* HAVE_MMAP */
+ #define USE_MMAP_BIT (SIZE_T_ZERO)
+
+ #define MMAP(s) MFAIL
+ #define MUNMAP(a, s) (-1)
+ #define DIRECT_MMAP(s) MFAIL
+ #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s)
+ #define CALL_MMAP(s) MMAP(s)
+ #define CALL_MUNMAP(a, s) MUNMAP((a), (s))
+#endif /* HAVE_MMAP */
+
+/**
+ * Define CALL_MREMAP
+ */
+#if HAVE_MMAP && HAVE_MREMAP
+ #ifdef MREMAP
+ #define CALL_MREMAP(addr, osz, nsz, mv) MREMAP((addr), (osz), (nsz), (mv))
+ #else /* MREMAP */
+ #define CALL_MREMAP(addr, osz, nsz, mv) MREMAP_DEFAULT((addr), (osz), (nsz), (mv))
+ #endif /* MREMAP */
+#else /* HAVE_MMAP && HAVE_MREMAP */
+ #define CALL_MREMAP(addr, osz, nsz, mv) MFAIL
+#endif /* HAVE_MMAP && HAVE_MREMAP */
+
+/* mstate bit set if continguous morecore disabled or failed */
+#define USE_NONCONTIGUOUS_BIT (4U)
+
+/* segment bit set in create_mspace_with_base */
+#define EXTERN_BIT (8U)
+
+
+/* --------------------------- Lock preliminaries ------------------------ */
+
+/*
+ When locks are defined, there is one global lock, plus
+ one per-mspace lock.
+
+ The global lock_ensures that mparams.magic and other unique
+ mparams values are initialized only once. It also protects
+ sequences of calls to MORECORE. In many cases sys_alloc requires
+ two calls, that should not be interleaved with calls by other
+ threads. This does not protect against direct calls to MORECORE
+ by other threads not using this lock, so there is still code to
+ cope the best we can on interference.
+
+ Per-mspace locks surround calls to malloc, free, etc.
+ By default, locks are simple non-reentrant mutexes.
+
+ Because lock-protected regions generally have bounded times, it is
+ OK to use the supplied simple spinlocks. Spinlocks are likely to
+ improve performance for lightly contended applications, but worsen
+ performance under heavy contention.
+
+ If USE_LOCKS is > 1, the definitions of lock routines here are
+ bypassed, in which case you will need to define the type MLOCK_T,
+ and at least INITIAL_LOCK, DESTROY_LOCK, ACQUIRE_LOCK, RELEASE_LOCK
+ and TRY_LOCK. You must also declare a
+ static MLOCK_T malloc_global_mutex = { initialization values };.
+
+*/
+
+#if !USE_LOCKS
+#define USE_LOCK_BIT (0U)
+#define INITIAL_LOCK(l) (0)
+#define DESTROY_LOCK(l) (0)
+#define ACQUIRE_MALLOC_GLOBAL_LOCK()
+#define RELEASE_MALLOC_GLOBAL_LOCK()
+
+#else
+#if USE_LOCKS > 1
+/* ----------------------- User-defined locks ------------------------ */
+/* Define your own lock implementation here */
+/* #define INITIAL_LOCK(lk) ... */
+/* #define DESTROY_LOCK(lk) ... */
+/* #define ACQUIRE_LOCK(lk) ... */
+/* #define RELEASE_LOCK(lk) ... */
+/* #define TRY_LOCK(lk) ... */
+/* static MLOCK_T malloc_global_mutex = ... */
+
+#elif USE_SPIN_LOCKS
+
+/* First, define CAS_LOCK and CLEAR_LOCK on ints */
+/* Note CAS_LOCK defined to return 0 on success */
+
+#if defined(__GNUC__)&& (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1))
+#define CAS_LOCK(sl) __sync_lock_test_and_set(sl, 1)
+#define CLEAR_LOCK(sl) __sync_lock_release(sl)
+
+#elif (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)))
+/* Custom spin locks for older gcc on x86 */
+static FORCEINLINE int x86_cas_lock(int *sl) {
+ int ret;
+ int val = 1;
+ int cmp = 0;
+ __asm__ __volatile__ ("lock; cmpxchgl %1, %2"
+ : "=a" (ret)
+ : "r" (val), "m" (*(sl)), "0"(cmp)
+ : "memory", "cc");
+ return ret;
+}
+
+static FORCEINLINE void x86_clear_lock(int* sl) {
+ assert(*sl != 0);
+ int prev = 0;
+ int ret;
+ __asm__ __volatile__ ("lock; xchgl %0, %1"
+ : "=r" (ret)
+ : "m" (*(sl)), "0"(prev)
+ : "memory");
+}
+
+#define CAS_LOCK(sl) x86_cas_lock(sl)
+#define CLEAR_LOCK(sl) x86_clear_lock(sl)
+
+#else /* Win32 MSC */
+#define CAS_LOCK(sl) interlockedexchange(sl, (LONG)1)
+#define CLEAR_LOCK(sl) interlockedexchange (sl, (LONG)0)
+
+#endif /* ... gcc spins locks ... */
+
+/* How to yield for a spin lock */
+#define SPINS_PER_YIELD 63
+#if defined(_MSC_VER)
+#define SLEEP_EX_DURATION 50 /* delay for yield/sleep */
+#define SPIN_LOCK_YIELD SleepEx(SLEEP_EX_DURATION, FALSE)
+#elif defined (__SVR4) && defined (__sun) /* solaris */
+#define SPIN_LOCK_YIELD thr_yield();
+#elif !defined(LACKS_SCHED_H)
+#define SPIN_LOCK_YIELD sched_yield();
+#else
+#define SPIN_LOCK_YIELD
+#endif /* ... yield ... */
+
+#if !defined(USE_RECURSIVE_LOCKS) || USE_RECURSIVE_LOCKS == 0
+/* Plain spin locks use single word (embedded in malloc_states) */
+static int spin_acquire_lock(int *sl) {
+ int spins = 0;
+ while (*(volatile int *)sl != 0 || CAS_LOCK(sl)) {
+ if ((++spins & SPINS_PER_YIELD) == 0) {
+ SPIN_LOCK_YIELD;
+ }
+ }
+ return 0;
+}
+
+#define MLOCK_T int
+#define TRY_LOCK(sl) !CAS_LOCK(sl)
+#define RELEASE_LOCK(sl) CLEAR_LOCK(sl)
+#define ACQUIRE_LOCK(sl) (CAS_LOCK(sl)? spin_acquire_lock(sl) : 0)
+#define INITIAL_LOCK(sl) (*sl = 0)
+#define DESTROY_LOCK(sl) (0)
+static MLOCK_T malloc_global_mutex = 0;
+
+#else /* USE_RECURSIVE_LOCKS */
+/* types for lock owners */
+#ifdef WIN32
+#define THREAD_ID_T DWORD
+#define CURRENT_THREAD GetCurrentThreadId()
+#define EQ_OWNER(X,Y) ((X) == (Y))
+#else
+/*
+ Note: the following assume that pthread_t is a type that can be
+ initialized to (casted) zero. If this is not the case, you will need to
+ somehow redefine these or not use spin locks.
+*/
+#define THREAD_ID_T pthread_t
+#define CURRENT_THREAD pthread_self()
+#define EQ_OWNER(X,Y) pthread_equal(X, Y)
+#endif
+
+struct malloc_recursive_lock {
+ int sl;
+ unsigned int c;
+ THREAD_ID_T threadid;
+};
+
+#define MLOCK_T struct malloc_recursive_lock
+static MLOCK_T malloc_global_mutex = { 0, 0, (THREAD_ID_T)0};
+
+static FORCEINLINE void recursive_release_lock(MLOCK_T *lk) {
+ assert(lk->sl != 0);
+ if (--lk->c == 0) {
+ CLEAR_LOCK(&lk->sl);
+ }
+}
+
+static FORCEINLINE int recursive_acquire_lock(MLOCK_T *lk) {
+ THREAD_ID_T mythreadid = CURRENT_THREAD;
+ int spins = 0;
+ for (;;) {
+ if (*((volatile int *)(&lk->sl)) == 0) {
+ if (!CAS_LOCK(&lk->sl)) {
+ lk->threadid = mythreadid;
+ lk->c = 1;
+ return 0;
+ }
+ }
+ else if (EQ_OWNER(lk->threadid, mythreadid)) {
+ ++lk->c;
+ return 0;
+ }
+ if ((++spins & SPINS_PER_YIELD) == 0) {
+ SPIN_LOCK_YIELD;
+ }
+ }
+}
+
+static FORCEINLINE int recursive_try_lock(MLOCK_T *lk) {
+ THREAD_ID_T mythreadid = CURRENT_THREAD;
+ if (*((volatile int *)(&lk->sl)) == 0) {
+ if (!CAS_LOCK(&lk->sl)) {
+ lk->threadid = mythreadid;
+ lk->c = 1;
+ return 1;
+ }
+ }
+ else if (EQ_OWNER(lk->threadid, mythreadid)) {
+ ++lk->c;
+ return 1;
+ }
+ return 0;
+}
+
+#define RELEASE_LOCK(lk) recursive_release_lock(lk)
+#define TRY_LOCK(lk) recursive_try_lock(lk)
+#define ACQUIRE_LOCK(lk) recursive_acquire_lock(lk)
+#define INITIAL_LOCK(lk) ((lk)->threadid = (THREAD_ID_T)0, (lk)->sl = 0, (lk)->c = 0)
+#define DESTROY_LOCK(lk) (0)
+#endif /* USE_RECURSIVE_LOCKS */
+
+#elif defined(WIN32) /* Win32 critical sections */
+#define MLOCK_T CRITICAL_SECTION
+#define ACQUIRE_LOCK(lk) (EnterCriticalSection(lk), 0)
+#define RELEASE_LOCK(lk) LeaveCriticalSection(lk)
+#define TRY_LOCK(lk) TryEnterCriticalSection(lk)
+#define INITIAL_LOCK(lk) (!InitializeCriticalSectionAndSpinCount((lk), 0x80000000|4000))
+#define DESTROY_LOCK(lk) (DeleteCriticalSection(lk), 0)
+#define NEED_GLOBAL_LOCK_INIT
+
+static MLOCK_T malloc_global_mutex;
+static volatile LONG malloc_global_mutex_status;
+
+/* Use spin loop to initialize global lock */
+static void init_malloc_global_mutex() {
+ for (;;) {
+ long stat = malloc_global_mutex_status;
+ if (stat > 0)
+ return;
+ /* transition to < 0 while initializing, then to > 0) */
+ if (stat == 0 &&
+ interlockedcompareexchange(&malloc_global_mutex_status, (LONG)-1, (LONG)0) == 0) {
+ InitializeCriticalSection(&malloc_global_mutex);
+ interlockedexchange(&malloc_global_mutex_status, (LONG)1);
+ return;
+ }
+ SleepEx(0, FALSE);
+ }
+}
+
+#else /* pthreads-based locks */
+#define MLOCK_T pthread_mutex_t
+#define ACQUIRE_LOCK(lk) pthread_mutex_lock(lk)
+#define RELEASE_LOCK(lk) pthread_mutex_unlock(lk)
+#define TRY_LOCK(lk) (!pthread_mutex_trylock(lk))
+#define INITIAL_LOCK(lk) pthread_init_lock(lk)
+#define DESTROY_LOCK(lk) pthread_mutex_destroy(lk)
+
+#if defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0 && defined(linux) && !defined(PTHREAD_MUTEX_RECURSIVE)
+/* Cope with old-style linux recursive lock initialization by adding */
+/* skipped internal declaration from pthread.h */
+extern int pthread_mutexattr_setkind_np __P ((pthread_mutexattr_t *__attr,
+ int __kind));
+#define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP
+#define pthread_mutexattr_settype(x,y) pthread_mutexattr_setkind_np(x,y)
+#endif /* USE_RECURSIVE_LOCKS ... */
+
+static MLOCK_T malloc_global_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static int pthread_init_lock (MLOCK_T *lk) {
+ pthread_mutexattr_t attr;
+ if (pthread_mutexattr_init(&attr)) return 1;
+#if defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0
+ if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) return 1;
+#endif
+ if (pthread_mutex_init(lk, &attr)) return 1;
+ if (pthread_mutexattr_destroy(&attr)) return 1;
+ return 0;
+}
+
+#endif /* ... lock types ... */
+
+/* Common code for all lock types */
+#define USE_LOCK_BIT (2U)
+
+#ifndef ACQUIRE_MALLOC_GLOBAL_LOCK
+#define ACQUIRE_MALLOC_GLOBAL_LOCK() ACQUIRE_LOCK(&malloc_global_mutex);
+#endif
+
+#ifndef RELEASE_MALLOC_GLOBAL_LOCK
+#define RELEASE_MALLOC_GLOBAL_LOCK() RELEASE_LOCK(&malloc_global_mutex);
+#endif
+
+#endif /* USE_LOCKS */
+
+/* ----------------------- Chunk representations ------------------------ */
+
+/*
+ (The following includes lightly edited explanations by Colin Plumb.)
+
+ The malloc_chunk declaration below is misleading (but accurate and
+ necessary). It declares a "view" into memory allowing access to
+ necessary fields at known offsets from a given base.
+
+ Chunks of memory are maintained using a `boundary tag' method as
+ originally described by Knuth. (See the paper by Paul Wilson
+ ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a survey of such
+ techniques.) Sizes of free chunks are stored both in the front of
+ each chunk and at the end. This makes consolidating fragmented
+ chunks into bigger chunks fast. The head fields also hold bits
+ representing whether chunks are free or in use.
+
+ Here are some pictures to make it clearer. They are "exploded" to
+ show that the state of a chunk can be thought of as extending from
+ the high 31 bits of the head field of its header through the
+ prev_foot and PINUSE_BIT bit of the following chunk header.
+
+ A chunk that's in use looks like:
+
+ chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Size of previous chunk (if P = 0) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P|
+ | Size of this chunk 1| +-+
+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ +- -+
+ | |
+ +- -+
+ | :
+ +- size - sizeof(size_t) available payload bytes -+
+ : |
+ chunk-> +- -+
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1|
+ | Size of next chunk (may or may not be in use) | +-+
+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ And if it's free, it looks like this:
+
+ chunk-> +- -+
+ | User payload (must be in use, or we would have merged!) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P|
+ | Size of this chunk 0| +-+
+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Next pointer |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Prev pointer |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | :
+ +- size - sizeof(struct chunk) unused bytes -+
+ : |
+ chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Size of this chunk |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0|
+ | Size of next chunk (must be in use, or we would have merged)| +-+
+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | :
+ +- User payload -+
+ : |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |0|
+ +-+
+ Note that since we always merge adjacent free chunks, the chunks
+ adjacent to a free chunk must be in use.
+
+ Given a pointer to a chunk (which can be derived trivially from the
+ payload pointer) we can, in O(1) time, find out whether the adjacent
+ chunks are free, and if so, unlink them from the lists that they
+ are on and merge them with the current chunk.
+
+ Chunks always begin on even word boundaries, so the mem portion
+ (which is returned to the user) is also on an even word boundary, and
+ thus at least double-word aligned.
+
+ The P (PINUSE_BIT) bit, stored in the unused low-order bit of the
+ chunk size (which is always a multiple of two words), is an in-use
+ bit for the *previous* chunk. If that bit is *clear*, then the
+ word before the current chunk size contains the previous chunk
+ size, and can be used to find the front of the previous chunk.
+ The very first chunk allocated always has this bit set, preventing
+ access to non-existent (or non-owned) memory. If pinuse is set for
+ any given chunk, then you CANNOT determine the size of the
+ previous chunk, and might even get a memory addressing fault when
+ trying to do so.
+
+ The C (CINUSE_BIT) bit, stored in the unused second-lowest bit of
+ the chunk size redundantly records whether the current chunk is
+ inuse (unless the chunk is mmapped). This redundancy enables usage
+ checks within free and realloc, and reduces indirection when freeing
+ and consolidating chunks.
+
+ Each freshly allocated chunk must have both cinuse and pinuse set.
+ That is, each allocated chunk borders either a previously allocated
+ and still in-use chunk, or the base of its memory arena. This is
+ ensured by making all allocations from the `lowest' part of any
+ found chunk. Further, no free chunk physically borders another one,
+ so each free chunk is known to be preceded and followed by either
+ inuse chunks or the ends of memory.
+
+ Note that the `foot' of the current chunk is actually represented
+ as the prev_foot of the NEXT chunk. This makes it easier to
+ deal with alignments etc but can be very confusing when trying
+ to extend or adapt this code.
+
+ The exceptions to all this are
+
+ 1. The special chunk `top' is the top-most available chunk (i.e.,
+ the one bordering the end of available memory). It is treated
+ specially. Top is never included in any bin, is used only if
+ no other chunk is available, and is released back to the
+ system if it is very large (see M_TRIM_THRESHOLD). In effect,
+ the top chunk is treated as larger (and thus less well
+ fitting) than any other available chunk. The top chunk
+ doesn't update its trailing size field since there is no next
+ contiguous chunk that would have to index off it. However,
+ space is still allocated for it (TOP_FOOT_SIZE) to enable
+ separation or merging when space is extended.
+
+ 3. Chunks allocated via mmap, have both cinuse and pinuse bits
+ cleared in their head fields. Because they are allocated
+ one-by-one, each must carry its own prev_foot field, which is
+ also used to hold the offset this chunk has within its mmapped
+ region, which is needed to preserve alignment. Each mmapped
+ chunk is trailed by the first two fields of a fake next-chunk
+ for sake of usage checks.
+
+*/
+
+struct malloc_chunk {
+ size_t prev_foot; /* Size of previous chunk (if free). */
+ size_t head; /* Size and inuse bits. */
+ struct malloc_chunk* fd; /* double links -- used only if free. */
+ struct malloc_chunk* bk;
+};
+
+typedef struct malloc_chunk mchunk;
+typedef struct malloc_chunk* mchunkptr;
+typedef struct malloc_chunk* sbinptr; /* The type of bins of chunks */
+typedef unsigned int bindex_t; /* Described below */
+typedef unsigned int binmap_t; /* Described below */
+typedef unsigned int flag_t; /* The type of various bit flag sets */
+
+/* ------------------- Chunks sizes and alignments ----------------------- */
+
+#define MCHUNK_SIZE (sizeof(mchunk))
+
+#if FOOTERS
+#define CHUNK_OVERHEAD (TWO_SIZE_T_SIZES)
+#else /* FOOTERS */
+#define CHUNK_OVERHEAD (SIZE_T_SIZE)
+#endif /* FOOTERS */
+
+/* MMapped chunks need a second word of overhead ... */
+#define MMAP_CHUNK_OVERHEAD (TWO_SIZE_T_SIZES)
+/* ... and additional padding for fake next-chunk at foot */
+#define MMAP_FOOT_PAD (FOUR_SIZE_T_SIZES)
+
+/* The smallest size we can malloc is an aligned minimal chunk */
+#define MIN_CHUNK_SIZE\
+ ((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK)
+
+/* conversion from malloc headers to user pointers, and back */
+#define chunk2mem(p) ((void*)((char*)(p) + TWO_SIZE_T_SIZES))
+#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - TWO_SIZE_T_SIZES))
+/* chunk associated with aligned address A */
+#define align_as_chunk(A) (mchunkptr)((A) + align_offset(chunk2mem(A)))
+
+/* Bounds on request (not chunk) sizes. */
+#define MAX_REQUEST ((-MIN_CHUNK_SIZE) << 2)
+#define MIN_REQUEST (MIN_CHUNK_SIZE - CHUNK_OVERHEAD - SIZE_T_ONE)
+
+/* pad request bytes into a usable size */
+#define pad_request(req) \
+ (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK)
+
+/* pad request, checking for minimum (but not maximum) */
+#define request2size(req) \
+ (((req) < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(req))
+
+
+/* ------------------ Operations on head and foot fields ----------------- */
+
+/*
+ The head field of a chunk is or'ed with PINUSE_BIT when previous
+ adjacent chunk in use, and or'ed with CINUSE_BIT if this chunk is in
+ use, unless mmapped, in which case both bits are cleared.
+
+ FLAG4_BIT is not used by this malloc, but might be useful in extensions.
+*/
+
+#define PINUSE_BIT (SIZE_T_ONE)
+#define CINUSE_BIT (SIZE_T_TWO)
+#define FLAG4_BIT (SIZE_T_FOUR)
+#define INUSE_BITS (PINUSE_BIT|CINUSE_BIT)
+#define FLAG_BITS (PINUSE_BIT|CINUSE_BIT|FLAG4_BIT)
+
+/* Head value for fenceposts */
+#define FENCEPOST_HEAD (INUSE_BITS|SIZE_T_SIZE)
+
+/* extraction of fields from head words */
+#define cinuse(p) ((p)->head & CINUSE_BIT)
+#define pinuse(p) ((p)->head & PINUSE_BIT)
+#define flag4inuse(p) ((p)->head & FLAG4_BIT)
+#define is_inuse(p) (((p)->head & INUSE_BITS) != PINUSE_BIT)
+#define is_mmapped(p) (((p)->head & INUSE_BITS) == 0)
+
+#define chunksize(p) ((p)->head & ~(FLAG_BITS))
+
+#define clear_pinuse(p) ((p)->head &= ~PINUSE_BIT)
+#define set_flag4(p) ((p)->head |= FLAG4_BIT)
+#define clear_flag4(p) ((p)->head &= ~FLAG4_BIT)
+
+/* Treat space at ptr +/- offset as a chunk */
+#define chunk_plus_offset(p, s) ((mchunkptr)(((char*)(p)) + (s)))
+#define chunk_minus_offset(p, s) ((mchunkptr)(((char*)(p)) - (s)))
+
+/* Ptr to next or previous physical malloc_chunk. */
+#define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->head & ~FLAG_BITS)))
+#define prev_chunk(p) ((mchunkptr)( ((char*)(p)) - ((p)->prev_foot) ))
+
+/* extract next chunk's pinuse bit */
+#define next_pinuse(p) ((next_chunk(p)->head) & PINUSE_BIT)
+
+/* Get/set size at footer */
+#define get_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot)
+#define set_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot = (s))
+
+/* Set size, pinuse bit, and foot */
+#define set_size_and_pinuse_of_free_chunk(p, s)\
+ ((p)->head = (s|PINUSE_BIT), set_foot(p, s))
+
+/* Set size, pinuse bit, foot, and clear next pinuse */
+#define set_free_with_pinuse(p, s, n)\
+ (clear_pinuse(n), set_size_and_pinuse_of_free_chunk(p, s))
+
+/* Get the internal overhead associated with chunk p */
+#define overhead_for(p)\
+ (is_mmapped(p)? MMAP_CHUNK_OVERHEAD : CHUNK_OVERHEAD)
+
+/* Return true if malloced space is not necessarily cleared */
+#if MMAP_CLEARS
+#define calloc_must_clear(p) (!is_mmapped(p))
+#else /* MMAP_CLEARS */
+#define calloc_must_clear(p) (1)
+#endif /* MMAP_CLEARS */
+
+/* ---------------------- Overlaid data structures ----------------------- */
+
+/*
+ When chunks are not in use, they are treated as nodes of either
+ lists or trees.
+
+ "Small" chunks are stored in circular doubly-linked lists, and look
+ like this:
+
+ chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Size of previous chunk |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ `head:' | Size of chunk, in bytes |P|
+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Forward pointer to next chunk in list |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Back pointer to previous chunk in list |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Unused space (may be 0 bytes long) .
+ . .
+ . |
+nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ `foot:' | Size of chunk, in bytes |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ Larger chunks are kept in a form of bitwise digital trees (aka
+ tries) keyed on chunksizes. Because malloc_tree_chunks are only for
+ free chunks greater than 256 bytes, their size doesn't impose any
+ constraints on user chunk sizes. Each node looks like:
+
+ chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Size of previous chunk |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ `head:' | Size of chunk, in bytes |P|
+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Forward pointer to next chunk of same size |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Back pointer to previous chunk of same size |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Pointer to left child (child[0]) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Pointer to right child (child[1]) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Pointer to parent |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | bin index of this chunk |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Unused space .
+ . |
+nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ `foot:' | Size of chunk, in bytes |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ Each tree holding treenodes is a tree of unique chunk sizes. Chunks
+ of the same size are arranged in a circularly-linked list, with only
+ the oldest chunk (the next to be used, in our FIFO ordering)
+ actually in the tree. (Tree members are distinguished by a non-null
+ parent pointer.) If a chunk with the same size an an existing node
+ is inserted, it is linked off the existing node using pointers that
+ work in the same way as fd/bk pointers of small chunks.
+
+ Each tree contains a power of 2 sized range of chunk sizes (the
+ smallest is 0x100 <= x < 0x180), which is is divided in half at each
+ tree level, with the chunks in the smaller half of the range (0x100
+ <= x < 0x140 for the top nose) in the left subtree and the larger
+ half (0x140 <= x < 0x180) in the right subtree. This is, of course,
+ done by inspecting individual bits.
+
+ Using these rules, each node's left subtree contains all smaller
+ sizes than its right subtree. However, the node at the root of each
+ subtree has no particular ordering relationship to either. (The
+ dividing line between the subtree sizes is based on trie relation.)
+ If we remove the last chunk of a given size from the interior of the
+ tree, we need to replace it with a leaf node. The tree ordering
+ rules permit a node to be replaced by any leaf below it.
+
+ The smallest chunk in a tree (a common operation in a best-fit
+ allocator) can be found by walking a path to the leftmost leaf in
+ the tree. Unlike a usual binary tree, where we follow left child
+ pointers until we reach a null, here we follow the right child
+ pointer any time the left one is null, until we reach a leaf with
+ both child pointers null. The smallest chunk in the tree will be
+ somewhere along that path.
+
+ The worst case number of steps to add, find, or remove a node is
+ bounded by the number of bits differentiating chunks within
+ bins. Under current bin calculations, this ranges from 6 up to 21
+ (for 32 bit sizes) or up to 53 (for 64 bit sizes). The typical case
+ is of course much better.
+*/
+
+struct malloc_tree_chunk {
+ /* The first four fields must be compatible with malloc_chunk */
+ size_t prev_foot;
+ size_t head;
+ struct malloc_tree_chunk* fd;
+ struct malloc_tree_chunk* bk;
+
+ struct malloc_tree_chunk* child[2];
+ struct malloc_tree_chunk* parent;
+ bindex_t index;
+};
+
+typedef struct malloc_tree_chunk tchunk;
+typedef struct malloc_tree_chunk* tchunkptr;
+typedef struct malloc_tree_chunk* tbinptr; /* The type of bins of trees */
+
+/* A little helper macro for trees */
+#define leftmost_child(t) ((t)->child[0] != 0? (t)->child[0] : (t)->child[1])
+
+/* ----------------------------- Segments -------------------------------- */
+
+/*
+ Each malloc space may include non-contiguous segments, held in a
+ list headed by an embedded malloc_segment record representing the
+ top-most space. Segments also include flags holding properties of
+ the space. Large chunks that are directly allocated by mmap are not
+ included in this list. They are instead independently created and
+ destroyed without otherwise keeping track of them.
+
+ Segment management mainly comes into play for spaces allocated by
+ MMAP. Any call to MMAP might or might not return memory that is
+ adjacent to an existing segment. MORECORE normally contiguously
+ extends the current space, so this space is almost always adjacent,
+ which is simpler and faster to deal with. (This is why MORECORE is
+ used preferentially to MMAP when both are available -- see
+ sys_alloc.) When allocating using MMAP, we don't use any of the
+ hinting mechanisms (inconsistently) supported in various
+ implementations of unix mmap, or distinguish reserving from
+ committing memory. Instead, we just ask for space, and exploit
+ contiguity when we get it. It is probably possible to do
+ better than this on some systems, but no general scheme seems
+ to be significantly better.
+
+ Management entails a simpler variant of the consolidation scheme
+ used for chunks to reduce fragmentation -- new adjacent memory is
+ normally prepended or appended to an existing segment. However,
+ there are limitations compared to chunk consolidation that mostly
+ reflect the fact that segment processing is relatively infrequent
+ (occurring only when getting memory from system) and that we
+ don't expect to have huge numbers of segments:
+
+ * Segments are not indexed, so traversal requires linear scans. (It
+ would be possible to index these, but is not worth the extra
+ overhead and complexity for most programs on most platforms.)
+ * New segments are only appended to old ones when holding top-most
+ memory; if they cannot be prepended to others, they are held in
+ different segments.
+
+ Except for the top-most segment of an mstate, each segment record
+ is kept at the tail of its segment. Segments are added by pushing
+ segment records onto the list headed by &mstate.seg for the
+ containing mstate.
+
+ Segment flags control allocation/merge/deallocation policies:
+ * If EXTERN_BIT set, then we did not allocate this segment,
+ and so should not try to deallocate or merge with others.
+ (This currently holds only for the initial segment passed
+ into create_mspace_with_base.)
+ * If USE_MMAP_BIT set, the segment may be merged with
+ other surrounding mmapped segments and trimmed/de-allocated
+ using munmap.
+ * If neither bit is set, then the segment was obtained using
+ MORECORE so can be merged with surrounding MORECORE'd segments
+ and deallocated/trimmed using MORECORE with negative arguments.
+*/
+
+struct malloc_segment {
+ char* base; /* base address */
+ size_t size; /* allocated size */
+ struct malloc_segment* next; /* ptr to next segment */
+ flag_t sflags; /* mmap and extern flag */
+};
+
+#define is_mmapped_segment(S) ((S)->sflags & USE_MMAP_BIT)
+#define is_extern_segment(S) ((S)->sflags & EXTERN_BIT)
+
+typedef struct malloc_segment msegment;
+typedef struct malloc_segment* msegmentptr;
+
+/* ---------------------------- malloc_state ----------------------------- */
+
+/*
+ A malloc_state holds all of the bookkeeping for a space.
+ The main fields are:
+
+ Top
+ The topmost chunk of the currently active segment. Its size is
+ cached in topsize. The actual size of topmost space is
+ topsize+TOP_FOOT_SIZE, which includes space reserved for adding
+ fenceposts and segment records if necessary when getting more
+ space from the system. The size at which to autotrim top is
+ cached from mparams in trim_check, except that it is disabled if
+ an autotrim fails.
+
+ Designated victim (dv)
+ This is the preferred chunk for servicing small requests that
+ don't have exact fits. It is normally the chunk split off most
+ recently to service another small request. Its size is cached in
+ dvsize. The link fields of this chunk are not maintained since it
+ is not kept in a bin.
+
+ SmallBins
+ An array of bin headers for free chunks. These bins hold chunks
+ with sizes less than MIN_LARGE_SIZE bytes. Each bin contains
+ chunks of all the same size, spaced 8 bytes apart. To simplify
+ use in double-linked lists, each bin header acts as a malloc_chunk
+ pointing to the real first node, if it exists (else pointing to
+ itself). This avoids special-casing for headers. But to avoid
+ waste, we allocate only the fd/bk pointers of bins, and then use
+ repositioning tricks to treat these as the fields of a chunk.
+
+ TreeBins
+ Treebins are pointers to the roots of trees holding a range of
+ sizes. There are 2 equally spaced treebins for each power of two
+ from TREE_SHIFT to TREE_SHIFT+16. The last bin holds anything
+ larger.
+
+ Bin maps
+ There is one bit map for small bins ("smallmap") and one for
+ treebins ("treemap). Each bin sets its bit when non-empty, and
+ clears the bit when empty. Bit operations are then used to avoid
+ bin-by-bin searching -- nearly all "search" is done without ever
+ looking at bins that won't be selected. The bit maps
+ conservatively use 32 bits per map word, even if on 64bit system.
+ For a good description of some of the bit-based techniques used
+ here, see Henry S. Warren Jr's book "Hacker's Delight" (and
+ supplement at http://hackersdelight.org/). Many of these are
+ intended to reduce the branchiness of paths through malloc etc, as
+ well as to reduce the number of memory locations read or written.
+
+ Segments
+ A list of segments headed by an embedded malloc_segment record
+ representing the initial space.
+
+ Address check support
+ The least_addr field is the least address ever obtained from
+ MORECORE or MMAP. Attempted frees and reallocs of any address less
+ than this are trapped (unless INSECURE is defined).
+
+ Magic tag
+ A cross-check field that should always hold same value as mparams.magic.
+
+ Max allowed footprint
+ The maximum allowed bytes to allocate from system (zero means no limit)
+
+ Flags
+ Bits recording whether to use MMAP, locks, or contiguous MORECORE
+
+ Statistics
+ Each space keeps track of current and maximum system memory
+ obtained via MORECORE or MMAP.
+
+ Trim support
+ Fields holding the amount of unused topmost memory that should trigger
+ trimming, and a counter to force periodic scanning to release unused
+ non-topmost segments.
+
+ Locking
+ If USE_LOCKS is defined, the "mutex" lock is acquired and released
+ around every public call using this mspace.
+
+ Extension support
+ A void* pointer and a size_t field that can be used to help implement
+ extensions to this malloc.
+*/
+
+/* Bin types, widths and sizes */
+#define NSMALLBINS (32U)
+#define NTREEBINS (32U)
+#define SMALLBIN_SHIFT (3U)
+#define SMALLBIN_WIDTH (SIZE_T_ONE << SMALLBIN_SHIFT)
+#define TREEBIN_SHIFT (8U)
+#define MIN_LARGE_SIZE (SIZE_T_ONE << TREEBIN_SHIFT)
+#define MAX_SMALL_SIZE (MIN_LARGE_SIZE - SIZE_T_ONE)
+#define MAX_SMALL_REQUEST (MAX_SMALL_SIZE - CHUNK_ALIGN_MASK - CHUNK_OVERHEAD)
+
+struct malloc_state {
+ binmap_t smallmap;
+ binmap_t treemap;
+ size_t dvsize;
+ size_t topsize;
+ char* least_addr;
+ mchunkptr dv;
+ mchunkptr top;
+ size_t trim_check;
+ size_t release_checks;
+ size_t magic;
+ mchunkptr smallbins[(NSMALLBINS+1)*2];
+ tbinptr treebins[NTREEBINS];
+ size_t footprint;
+ size_t max_footprint;
+ size_t footprint_limit; /* zero means no limit */
+ flag_t mflags;
+#if USE_LOCKS
+ MLOCK_T mutex; /* locate lock among fields that rarely change */
+#endif /* USE_LOCKS */
+ msegment seg;
+ void* extp; /* Unused but available for extensions */
+ size_t exts;
+};
+
+typedef struct malloc_state* mstate;
+
+/* ------------- Global malloc_state and malloc_params ------------------- */
+
+/*
+ malloc_params holds global properties, including those that can be
+ dynamically set using mallopt. There is a single instance, mparams,
+ initialized in init_mparams. Note that the non-zeroness of "magic"
+ also serves as an initialization flag.
+*/
+
+struct malloc_params {
+ size_t magic;
+ size_t page_size;
+ size_t granularity;
+ size_t mmap_threshold;
+ size_t trim_threshold;
+ flag_t default_mflags;
+};
+
+static struct malloc_params mparams;
+
+/* Ensure mparams initialized */
+#define ensure_initialization() (void)(mparams.magic != 0 || init_mparams())
+
+#if !ONLY_MSPACES
+
+/* The global malloc_state used for all non-"mspace" calls */
+static struct malloc_state _gm_;
+#define gm (&_gm_)
+#define is_global(M) ((M) == &_gm_)
+
+#endif /* !ONLY_MSPACES */
+
+#define is_initialized(M) ((M)->top != 0)
+
+/* -------------------------- system alloc setup ------------------------- */
+
+/* Operations on mflags */
+
+#define use_lock(M) ((M)->mflags & USE_LOCK_BIT)
+#define enable_lock(M) ((M)->mflags |= USE_LOCK_BIT)
+#if USE_LOCKS
+#define disable_lock(M) ((M)->mflags &= ~USE_LOCK_BIT)
+#else
+#define disable_lock(M)
+#endif
+
+#define use_mmap(M) ((M)->mflags & USE_MMAP_BIT)
+#define enable_mmap(M) ((M)->mflags |= USE_MMAP_BIT)
+#if HAVE_MMAP
+#define disable_mmap(M) ((M)->mflags &= ~USE_MMAP_BIT)
+#else
+#define disable_mmap(M)
+#endif
+
+#define use_noncontiguous(M) ((M)->mflags & USE_NONCONTIGUOUS_BIT)
+#define disable_contiguous(M) ((M)->mflags |= USE_NONCONTIGUOUS_BIT)
+
+#define set_lock(M,L)\
+ ((M)->mflags = (L)?\
+ ((M)->mflags | USE_LOCK_BIT) :\
+ ((M)->mflags & ~USE_LOCK_BIT))
+
+/* page-align a size */
+#define page_align(S)\
+ (((S) + (mparams.page_size - SIZE_T_ONE)) & ~(mparams.page_size - SIZE_T_ONE))
+
+/* granularity-align a size */
+#define granularity_align(S)\
+ (((S) + (mparams.granularity - SIZE_T_ONE))\
+ & ~(mparams.granularity - SIZE_T_ONE))
+
+
+/* For mmap, use granularity alignment on windows, else page-align */
+#ifdef WIN32
+#define mmap_align(S) granularity_align(S)
+#else
+#define mmap_align(S) page_align(S)
+#endif
+
+/* For sys_alloc, enough padding to ensure can malloc request on success */
+#define SYS_ALLOC_PADDING (TOP_FOOT_SIZE + MALLOC_ALIGNMENT)
+
+#define is_page_aligned(S)\
+ (((size_t)(S) & (mparams.page_size - SIZE_T_ONE)) == 0)
+#define is_granularity_aligned(S)\
+ (((size_t)(S) & (mparams.granularity - SIZE_T_ONE)) == 0)
+
+/* True if segment S holds address A */
+#define segment_holds(S, A)\
+ ((char*)(A) >= S->base && (char*)(A) < S->base + S->size)
+
+/* Return segment holding given address */
+static msegmentptr segment_holding(mstate m, char* addr) {
+ msegmentptr sp = &m->seg;
+ for (;;) {
+ if (addr >= sp->base && addr < sp->base + sp->size)
+ return sp;
+ if ((sp = sp->next) == 0)
+ return 0;
+ }
+}
+
+/* Return true if segment contains a segment link */
+static int has_segment_link(mstate m, msegmentptr ss) {
+ msegmentptr sp = &m->seg;
+ for (;;) {
+ if ((char*)sp >= ss->base && (char*)sp < ss->base + ss->size)
+ return 1;
+ if ((sp = sp->next) == 0)
+ return 0;
+ }
+}
+
+#ifndef MORECORE_CANNOT_TRIM
+#define should_trim(M,s) ((s) > (M)->trim_check)
+#else /* MORECORE_CANNOT_TRIM */
+#define should_trim(M,s) (0)
+#endif /* MORECORE_CANNOT_TRIM */
+
+/*
+ TOP_FOOT_SIZE is padding at the end of a segment, including space
+ that may be needed to place segment records and fenceposts when new
+ noncontiguous segments are added.
+*/
+#define TOP_FOOT_SIZE\
+ (align_offset(chunk2mem(0))+pad_request(sizeof(struct malloc_segment))+MIN_CHUNK_SIZE)
+
+
+/* ------------------------------- Hooks -------------------------------- */
+
+/*
+ PREACTION should be defined to return 0 on success, and nonzero on
+ failure. If you are not using locking, you can redefine these to do
+ anything you like.
+*/
+
+#if USE_LOCKS
+#define PREACTION(M) ((use_lock(M))? ACQUIRE_LOCK(&(M)->mutex) : 0)
+#define POSTACTION(M) { if (use_lock(M)) RELEASE_LOCK(&(M)->mutex); }
+#else /* USE_LOCKS */
+
+#ifndef PREACTION
+#define PREACTION(M) (0)
+#endif /* PREACTION */
+
+#ifndef POSTACTION
+#define POSTACTION(M)
+#endif /* POSTACTION */
+
+#endif /* USE_LOCKS */
+
+/*
+ CORRUPTION_ERROR_ACTION is triggered upon detected bad addresses.
+ USAGE_ERROR_ACTION is triggered on detected bad frees and
+ reallocs. The argument p is an address that might have triggered the
+ fault. It is ignored by the two predefined actions, but might be
+ useful in custom actions that try to help diagnose errors.
+*/
+
+#if PROCEED_ON_ERROR
+
+/* A count of the number of corruption errors causing resets */
+int malloc_corruption_error_count;
+
+/* default corruption action */
+static void reset_on_error(mstate m);
+
+#define CORRUPTION_ERROR_ACTION(m) reset_on_error(m)
+#define USAGE_ERROR_ACTION(m, p)
+
+#else /* PROCEED_ON_ERROR */
+
+#ifndef CORRUPTION_ERROR_ACTION
+#define CORRUPTION_ERROR_ACTION(m) ABORT
+#endif /* CORRUPTION_ERROR_ACTION */
+
+#ifndef USAGE_ERROR_ACTION
+#define USAGE_ERROR_ACTION(m,p) ABORT
+#endif /* USAGE_ERROR_ACTION */
+
+#endif /* PROCEED_ON_ERROR */
+
+
+/* -------------------------- Debugging setup ---------------------------- */
+
+#if ! DEBUG
+
+#define check_free_chunk(M,P)
+#define check_inuse_chunk(M,P)
+#define check_malloced_chunk(M,P,N)
+#define check_mmapped_chunk(M,P)
+#define check_malloc_state(M)
+#define check_top_chunk(M,P)
+
+#else /* DEBUG */
+#define check_free_chunk(M,P) do_check_free_chunk(M,P)
+#define check_inuse_chunk(M,P) do_check_inuse_chunk(M,P)
+#define check_top_chunk(M,P) do_check_top_chunk(M,P)
+#define check_malloced_chunk(M,P,N) do_check_malloced_chunk(M,P,N)
+#define check_mmapped_chunk(M,P) do_check_mmapped_chunk(M,P)
+#define check_malloc_state(M) do_check_malloc_state(M)
+
+static void do_check_any_chunk(mstate m, mchunkptr p);
+static void do_check_top_chunk(mstate m, mchunkptr p);
+static void do_check_mmapped_chunk(mstate m, mchunkptr p);
+static void do_check_inuse_chunk(mstate m, mchunkptr p);
+static void do_check_free_chunk(mstate m, mchunkptr p);
+static void do_check_malloced_chunk(mstate m, void* mem, size_t s);
+static void do_check_tree(mstate m, tchunkptr t);
+static void do_check_treebin(mstate m, bindex_t i);
+static void do_check_smallbin(mstate m, bindex_t i);
+static void do_check_malloc_state(mstate m);
+static int bin_find(mstate m, mchunkptr x);
+static size_t traverse_and_check(mstate m);
+#endif /* DEBUG */
+
+/* ---------------------------- Indexing Bins ---------------------------- */
+
+#define is_small(s) (((s) >> SMALLBIN_SHIFT) < NSMALLBINS)
+#define small_index(s) (bindex_t)((s) >> SMALLBIN_SHIFT)
+#define small_index2size(i) ((i) << SMALLBIN_SHIFT)
+#define MIN_SMALL_INDEX (small_index(MIN_CHUNK_SIZE))
+
+/* addressing by index. See above about smallbin repositioning */
+#define smallbin_at(M, i) ((sbinptr)((char*)&((M)->smallbins[(i)<<1])))
+#define treebin_at(M,i) (&((M)->treebins[i]))
+
+/* assign tree index for size S to variable I. Use x86 asm if possible */
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+#define compute_tree_index(S, I)\
+{\
+ unsigned int X = S >> TREEBIN_SHIFT;\
+ if (X == 0)\
+ I = 0;\
+ else if (X > 0xFFFF)\
+ I = NTREEBINS-1;\
+ else {\
+ unsigned int K = (unsigned) sizeof(X)*__CHAR_BIT__ - 1 - (unsigned) __builtin_clz(X); \
+ I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\
+ }\
+}
+
+#elif defined (__INTEL_COMPILER)
+#define compute_tree_index(S, I)\
+{\
+ size_t X = S >> TREEBIN_SHIFT;\
+ if (X == 0)\
+ I = 0;\
+ else if (X > 0xFFFF)\
+ I = NTREEBINS-1;\
+ else {\
+ unsigned int K = _bit_scan_reverse (X); \
+ I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\
+ }\
+}
+
+#elif defined(_MSC_VER) && _MSC_VER>=1300
+#define compute_tree_index(S, I)\
+{\
+ size_t X = S >> TREEBIN_SHIFT;\
+ if (X == 0)\
+ I = 0;\
+ else if (X > 0xFFFF)\
+ I = NTREEBINS-1;\
+ else {\
+ unsigned int K;\
+ _BitScanReverse((DWORD *) &K, (DWORD) X);\
+ I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\
+ }\
+}
+
+#else /* GNUC */
+#define compute_tree_index(S, I)\
+{\
+ size_t X = S >> TREEBIN_SHIFT;\
+ if (X == 0)\
+ I = 0;\
+ else if (X > 0xFFFF)\
+ I = NTREEBINS-1;\
+ else {\
+ unsigned int Y = (unsigned int)X;\
+ unsigned int N = ((Y - 0x100) >> 16) & 8;\
+ unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4;\
+ N += K;\
+ N += K = (((Y <<= K) - 0x4000) >> 16) & 2;\
+ K = 14 - N + ((Y <<= K) >> 15);\
+ I = (K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1));\
+ }\
+}
+#endif /* GNUC */
+
+/* Bit representing maximum resolved size in a treebin at i */
+#define bit_for_tree_index(i) \
+ (i == NTREEBINS-1)? (SIZE_T_BITSIZE-1) : (((i) >> 1) + TREEBIN_SHIFT - 2)
+
+/* Shift placing maximum resolved bit in a treebin at i as sign bit */
+#define leftshift_for_tree_index(i) \
+ ((i == NTREEBINS-1)? 0 : \
+ ((SIZE_T_BITSIZE-SIZE_T_ONE) - (((i) >> 1) + TREEBIN_SHIFT - 2)))
+
+/* The size of the smallest chunk held in bin with index i */
+#define minsize_for_tree_index(i) \
+ ((SIZE_T_ONE << (((i) >> 1) + TREEBIN_SHIFT)) | \
+ (((size_t)((i) & SIZE_T_ONE)) << (((i) >> 1) + TREEBIN_SHIFT - 1)))
+
+
+/* ------------------------ Operations on bin maps ----------------------- */
+
+/* bit corresponding to given index */
+#define idx2bit(i) ((binmap_t)(1) << (i))
+
+/* Mark/Clear bits with given index */
+#define mark_smallmap(M,i) ((M)->smallmap |= idx2bit(i))
+#define clear_smallmap(M,i) ((M)->smallmap &= ~idx2bit(i))
+#define smallmap_is_marked(M,i) ((M)->smallmap & idx2bit(i))
+
+#define mark_treemap(M,i) ((M)->treemap |= idx2bit(i))
+#define clear_treemap(M,i) ((M)->treemap &= ~idx2bit(i))
+#define treemap_is_marked(M,i) ((M)->treemap & idx2bit(i))
+
+/* isolate the least set bit of a bitmap */
+#define least_bit(x) ((x) & -(x))
+
+/* mask with all bits to left of least bit of x on */
+#define left_bits(x) ((x<<1) | -(x<<1))
+
+/* mask with all bits to left of or equal to least bit of x on */
+#define same_or_left_bits(x) ((x) | -(x))
+
+/* index corresponding to given bit. Use x86 asm if possible */
+
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+#define compute_bit2idx(X, I)\
+{\
+ unsigned int J;\
+ J = __builtin_ctz(X); \
+ I = (bindex_t)J;\
+}
+
+#elif defined (__INTEL_COMPILER)
+#define compute_bit2idx(X, I)\
+{\
+ unsigned int J;\
+ J = _bit_scan_forward (X); \
+ I = (bindex_t)J;\
+}
+
+#elif defined(_MSC_VER) && _MSC_VER>=1300
+#define compute_bit2idx(X, I)\
+{\
+ unsigned int J;\
+ _BitScanForward((DWORD *) &J, X);\
+ I = (bindex_t)J;\
+}
+
+#elif USE_BUILTIN_FFS
+#define compute_bit2idx(X, I) I = ffs(X)-1
+
+#else
+#define compute_bit2idx(X, I)\
+{\
+ unsigned int Y = X - 1;\
+ unsigned int K = Y >> (16-4) & 16;\
+ unsigned int N = K; Y >>= K;\
+ N += K = Y >> (8-3) & 8; Y >>= K;\
+ N += K = Y >> (4-2) & 4; Y >>= K;\
+ N += K = Y >> (2-1) & 2; Y >>= K;\
+ N += K = Y >> (1-0) & 1; Y >>= K;\
+ I = (bindex_t)(N + Y);\
+}
+#endif /* GNUC */
+
+
+/* ----------------------- Runtime Check Support ------------------------- */
+
+/*
+ For security, the main invariant is that malloc/free/etc never
+ writes to a static address other than malloc_state, unless static
+ malloc_state itself has been corrupted, which cannot occur via
+ malloc (because of these checks). In essence this means that we
+ believe all pointers, sizes, maps etc held in malloc_state, but
+ check all of those linked or offsetted from other embedded data
+ structures. These checks are interspersed with main code in a way
+ that tends to minimize their run-time cost.
+
+ When FOOTERS is defined, in addition to range checking, we also
+ verify footer fields of inuse chunks, which can be used guarantee
+ that the mstate controlling malloc/free is intact. This is a
+ streamlined version of the approach described by William Robertson
+ et al in "Run-time Detection of Heap-based Overflows" LISA'03
+ http://www.usenix.org/events/lisa03/tech/robertson.html The footer
+ of an inuse chunk holds the xor of its mstate and a random seed,
+ that is checked upon calls to free() and realloc(). This is
+ (probabalistically) unguessable from outside the program, but can be
+ computed by any code successfully malloc'ing any chunk, so does not
+ itself provide protection against code that has already broken
+ security through some other means. Unlike Robertson et al, we
+ always dynamically check addresses of all offset chunks (previous,
+ next, etc). This turns out to be cheaper than relying on hashes.
+*/
+
+#if !INSECURE
+/* Check if address a is at least as high as any from MORECORE or MMAP */
+#define ok_address(M, a) ((char*)(a) >= (M)->least_addr)
+/* Check if address of next chunk n is higher than base chunk p */
+#define ok_next(p, n) ((char*)(p) < (char*)(n))
+/* Check if p has inuse status */
+#define ok_inuse(p) is_inuse(p)
+/* Check if p has its pinuse bit on */
+#define ok_pinuse(p) pinuse(p)
+
+#else /* !INSECURE */
+#define ok_address(M, a) (1)
+#define ok_next(b, n) (1)
+#define ok_inuse(p) (1)
+#define ok_pinuse(p) (1)
+#endif /* !INSECURE */
+
+#if (FOOTERS && !INSECURE)
+/* Check if (alleged) mstate m has expected magic field */
+#define ok_magic(M) ((M)->magic == mparams.magic)
+#else /* (FOOTERS && !INSECURE) */
+#define ok_magic(M) (1)
+#endif /* (FOOTERS && !INSECURE) */
+
+/* In gcc, use __builtin_expect to minimize impact of checks */
+#if !INSECURE
+#if defined(__GNUC__) && __GNUC__ >= 3
+#define RTCHECK(e) __builtin_expect(e, 1)
+#else /* GNUC */
+#define RTCHECK(e) (e)
+#endif /* GNUC */
+#else /* !INSECURE */
+#define RTCHECK(e) (1)
+#endif /* !INSECURE */
+
+/* macros to set up inuse chunks with or without footers */
+
+#if !FOOTERS
+
+#define mark_inuse_foot(M,p,s)
+
+/* Macros for setting head/foot of non-mmapped chunks */
+
+/* Set cinuse bit and pinuse bit of next chunk */
+#define set_inuse(M,p,s)\
+ ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\
+ ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT)
+
+/* Set cinuse and pinuse of this chunk and pinuse of next chunk */
+#define set_inuse_and_pinuse(M,p,s)\
+ ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\
+ ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT)
+
+/* Set size, cinuse and pinuse bit of this chunk */
+#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\
+ ((p)->head = (s|PINUSE_BIT|CINUSE_BIT))
+
+#else /* FOOTERS */
+
+/* Set foot of inuse chunk to be xor of mstate and seed */
+#define mark_inuse_foot(M,p,s)\
+ (((mchunkptr)((char*)(p) + (s)))->prev_foot = ((size_t)(M) ^ mparams.magic))
+
+#define get_mstate_for(p)\
+ ((mstate)(((mchunkptr)((char*)(p) +\
+ (chunksize(p))))->prev_foot ^ mparams.magic))
+
+#define set_inuse(M,p,s)\
+ ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\
+ (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT), \
+ mark_inuse_foot(M,p,s))
+
+#define set_inuse_and_pinuse(M,p,s)\
+ ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\
+ (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT),\
+ mark_inuse_foot(M,p,s))
+
+#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\
+ ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\
+ mark_inuse_foot(M, p, s))
+
+#endif /* !FOOTERS */
+
+/* ---------------------------- setting mparams -------------------------- */
+
+#if LOCK_AT_FORK
+static void pre_fork(void) { ACQUIRE_LOCK(&(gm)->mutex); }
+static void post_fork_parent(void) { RELEASE_LOCK(&(gm)->mutex); }
+static void post_fork_child(void) { INITIAL_LOCK(&(gm)->mutex); }
+#endif /* LOCK_AT_FORK */
+
+/* Initialize mparams */
+static int init_mparams(void) {
+#ifdef NEED_GLOBAL_LOCK_INIT
+ if (malloc_global_mutex_status <= 0)
+ init_malloc_global_mutex();
+#endif
+
+ ACQUIRE_MALLOC_GLOBAL_LOCK();
+ if (mparams.magic == 0) {
+ size_t magic;
+ size_t psize;
+ size_t gsize;
+
+#ifndef WIN32
+ psize = malloc_getpagesize;
+ gsize = ((DEFAULT_GRANULARITY != 0)? DEFAULT_GRANULARITY : psize);
+#else /* WIN32 */
+ {
+ SYSTEM_INFO system_info;
+ GetSystemInfo(&system_info);
+ psize = system_info.dwPageSize;
+ gsize = ((DEFAULT_GRANULARITY != 0)?
+ DEFAULT_GRANULARITY : system_info.dwAllocationGranularity);
+ }
+#endif /* WIN32 */
+
+ /* Sanity-check configuration:
+ size_t must be unsigned and as wide as pointer type.
+ ints must be at least 4 bytes.
+ alignment must be at least 8.
+ Alignment, min chunk size, and page size must all be powers of 2.
+ */
+ if ((sizeof(size_t) != sizeof(char*)) ||
+ (MAX_SIZE_T < MIN_CHUNK_SIZE) ||
+ (sizeof(int) < 4) ||
+ (MALLOC_ALIGNMENT < (size_t)8U) ||
+ ((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT-SIZE_T_ONE)) != 0) ||
+ ((MCHUNK_SIZE & (MCHUNK_SIZE-SIZE_T_ONE)) != 0) ||
+ ((gsize & (gsize-SIZE_T_ONE)) != 0) ||
+ ((psize & (psize-SIZE_T_ONE)) != 0))
+ ABORT;
+ mparams.granularity = gsize;
+ mparams.page_size = psize;
+ mparams.mmap_threshold = DEFAULT_MMAP_THRESHOLD;
+ mparams.trim_threshold = DEFAULT_TRIM_THRESHOLD;
+#if MORECORE_CONTIGUOUS
+ mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT;
+#else /* MORECORE_CONTIGUOUS */
+ mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT|USE_NONCONTIGUOUS_BIT;
+#endif /* MORECORE_CONTIGUOUS */
+
+#if !ONLY_MSPACES
+ /* Set up lock for main malloc area */
+ gm->mflags = mparams.default_mflags;
+ (void)INITIAL_LOCK(&gm->mutex);
+#endif
+#if LOCK_AT_FORK
+ pthread_atfork(&pre_fork, &post_fork_parent, &post_fork_child);
+#endif
+
+ {
+#if USE_DEV_RANDOM
+ int fd;
+ unsigned char buf[sizeof(size_t)];
+ /* Try to use /dev/urandom, else fall back on using time */
+ if ((fd = open("/dev/urandom", O_RDONLY)) >= 0 &&
+ read(fd, buf, sizeof(buf)) == sizeof(buf)) {
+ magic = *((size_t *) buf);
+ close(fd);
+ }
+ else
+#endif /* USE_DEV_RANDOM */
+#ifdef WIN32
+ magic = (size_t)(GetTickCount() ^ (size_t)0x55555555U);
+#elif defined(LACKS_TIME_H)
+ magic = (size_t)&magic ^ (size_t)0x55555555U;
+#else
+ magic = (size_t)(time(0) ^ (size_t)0x55555555U);
+#endif
+ magic |= (size_t)8U; /* ensure nonzero */
+ magic &= ~(size_t)7U; /* improve chances of fault for bad values */
+ /* Until memory modes commonly available, use volatile-write */
+ (*(volatile size_t *)(&(mparams.magic))) = magic;
+ }
+ }
+
+ RELEASE_MALLOC_GLOBAL_LOCK();
+ return 1;
+}
+
+/* support for mallopt */
+static int change_mparam(int param_number, int value) {
+ size_t val;
+ ensure_initialization();
+ val = (value == -1)? MAX_SIZE_T : (size_t)value;
+ switch(param_number) {
+ case M_TRIM_THRESHOLD:
+ mparams.trim_threshold = val;
+ return 1;
+ case M_GRANULARITY:
+ if (val >= mparams.page_size && ((val & (val-1)) == 0)) {
+ mparams.granularity = val;
+ return 1;
+ }
+ else
+ return 0;
+ case M_MMAP_THRESHOLD:
+ mparams.mmap_threshold = val;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+#if DEBUG
+/* ------------------------- Debugging Support --------------------------- */
+
+/* Check properties of any chunk, whether free, inuse, mmapped etc */
+static void do_check_any_chunk(mstate m, mchunkptr p) {
+ assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD));
+ assert(ok_address(m, p));
+}
+
+/* Check properties of top chunk */
+static void do_check_top_chunk(mstate m, mchunkptr p) {
+ msegmentptr sp = segment_holding(m, (char*)p);
+ size_t sz = p->head & ~INUSE_BITS; /* third-lowest bit can be set! */
+ assert(sp != 0);
+ assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD));
+ assert(ok_address(m, p));
+ assert(sz == m->topsize);
+ assert(sz > 0);
+ assert(sz == ((sp->base + sp->size) - (char*)p) - TOP_FOOT_SIZE);
+ assert(pinuse(p));
+ assert(!pinuse(chunk_plus_offset(p, sz)));
+}
+
+/* Check properties of (inuse) mmapped chunks */
+static void do_check_mmapped_chunk(mstate m, mchunkptr p) {
+ size_t sz = chunksize(p);
+ size_t len = (sz + (p->prev_foot) + MMAP_FOOT_PAD);
+ assert(is_mmapped(p));
+ assert(use_mmap(m));
+ assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD));
+ assert(ok_address(m, p));
+ assert(!is_small(sz));
+ assert((len & (mparams.page_size-SIZE_T_ONE)) == 0);
+ assert(chunk_plus_offset(p, sz)->head == FENCEPOST_HEAD);
+ assert(chunk_plus_offset(p, sz+SIZE_T_SIZE)->head == 0);
+}
+
+/* Check properties of inuse chunks */
+static void do_check_inuse_chunk(mstate m, mchunkptr p) {
+ do_check_any_chunk(m, p);
+ assert(is_inuse(p));
+ assert(next_pinuse(p));
+ /* If not pinuse and not mmapped, previous chunk has OK offset */
+ assert(is_mmapped(p) || pinuse(p) || next_chunk(prev_chunk(p)) == p);
+ if (is_mmapped(p))
+ do_check_mmapped_chunk(m, p);
+}
+
+/* Check properties of free chunks */
+static void do_check_free_chunk(mstate m, mchunkptr p) {
+ size_t sz = chunksize(p);
+ mchunkptr next = chunk_plus_offset(p, sz);
+ do_check_any_chunk(m, p);
+ assert(!is_inuse(p));
+ assert(!next_pinuse(p));
+ assert (!is_mmapped(p));
+ if (p != m->dv && p != m->top) {
+ if (sz >= MIN_CHUNK_SIZE) {
+ assert((sz & CHUNK_ALIGN_MASK) == 0);
+ assert(is_aligned(chunk2mem(p)));
+ assert(next->prev_foot == sz);
+ assert(pinuse(p));
+ assert (next == m->top || is_inuse(next));
+ assert(p->fd->bk == p);
+ assert(p->bk->fd == p);
+ }
+ else /* markers are always of size SIZE_T_SIZE */
+ assert(sz == SIZE_T_SIZE);
+ }
+}
+
+/* Check properties of malloced chunks at the point they are malloced */
+static void do_check_malloced_chunk(mstate m, void* mem, size_t s) {
+ if (mem != 0) {
+ mchunkptr p = mem2chunk(mem);
+ size_t sz = p->head & ~INUSE_BITS;
+ do_check_inuse_chunk(m, p);
+ assert((sz & CHUNK_ALIGN_MASK) == 0);
+ assert(sz >= MIN_CHUNK_SIZE);
+ assert(sz >= s);
+ /* unless mmapped, size is less than MIN_CHUNK_SIZE more than request */
+ assert(is_mmapped(p) || sz < (s + MIN_CHUNK_SIZE));
+ }
+}
+
+/* Check a tree and its subtrees. */
+static void do_check_tree(mstate m, tchunkptr t) {
+ tchunkptr head = 0;
+ tchunkptr u = t;
+ bindex_t tindex = t->index;
+ size_t tsize = chunksize(t);
+ bindex_t idx;
+ compute_tree_index(tsize, idx);
+ assert(tindex == idx);
+ assert(tsize >= MIN_LARGE_SIZE);
+ assert(tsize >= minsize_for_tree_index(idx));
+ assert((idx == NTREEBINS-1) || (tsize < minsize_for_tree_index((idx+1))));
+
+ do { /* traverse through chain of same-sized nodes */
+ do_check_any_chunk(m, ((mchunkptr)u));
+ assert(u->index == tindex);
+ assert(chunksize(u) == tsize);
+ assert(!is_inuse(u));
+ assert(!next_pinuse(u));
+ assert(u->fd->bk == u);
+ assert(u->bk->fd == u);
+ if (u->parent == 0) {
+ assert(u->child[0] == 0);
+ assert(u->child[1] == 0);
+ }
+ else {
+ assert(head == 0); /* only one node on chain has parent */
+ head = u;
+ assert(u->parent != u);
+ assert (u->parent->child[0] == u ||
+ u->parent->child[1] == u ||
+ *((tbinptr*)(u->parent)) == u);
+ if (u->child[0] != 0) {
+ assert(u->child[0]->parent == u);
+ assert(u->child[0] != u);
+ do_check_tree(m, u->child[0]);
+ }
+ if (u->child[1] != 0) {
+ assert(u->child[1]->parent == u);
+ assert(u->child[1] != u);
+ do_check_tree(m, u->child[1]);
+ }
+ if (u->child[0] != 0 && u->child[1] != 0) {
+ assert(chunksize(u->child[0]) < chunksize(u->child[1]));
+ }
+ }
+ u = u->fd;
+ } while (u != t);
+ assert(head != 0);
+}
+
+/* Check all the chunks in a treebin. */
+static void do_check_treebin(mstate m, bindex_t i) {
+ tbinptr* tb = treebin_at(m, i);
+ tchunkptr t = *tb;
+ int empty = (m->treemap & (1U << i)) == 0;
+ if (t == 0)
+ assert(empty);
+ if (!empty)
+ do_check_tree(m, t);
+}
+
+/* Check all the chunks in a smallbin. */
+static void do_check_smallbin(mstate m, bindex_t i) {
+ sbinptr b = smallbin_at(m, i);
+ mchunkptr p = b->bk;
+ unsigned int empty = (m->smallmap & (1U << i)) == 0;
+ if (p == b)
+ assert(empty);
+ if (!empty) {
+ for (; p != b; p = p->bk) {
+ size_t size = chunksize(p);
+ mchunkptr q;
+ /* each chunk claims to be free */
+ do_check_free_chunk(m, p);
+ /* chunk belongs in bin */
+ assert(small_index(size) == i);
+ assert(p->bk == b || chunksize(p->bk) == chunksize(p));
+ /* chunk is followed by an inuse chunk */
+ q = next_chunk(p);
+ if (q->head != FENCEPOST_HEAD)
+ do_check_inuse_chunk(m, q);
+ }
+ }
+}
+
+/* Find x in a bin. Used in other check functions. */
+static int bin_find(mstate m, mchunkptr x) {
+ size_t size = chunksize(x);
+ if (is_small(size)) {
+ bindex_t sidx = small_index(size);
+ sbinptr b = smallbin_at(m, sidx);
+ if (smallmap_is_marked(m, sidx)) {
+ mchunkptr p = b;
+ do {
+ if (p == x)
+ return 1;
+ } while ((p = p->fd) != b);
+ }
+ }
+ else {
+ bindex_t tidx;
+ compute_tree_index(size, tidx);
+ if (treemap_is_marked(m, tidx)) {
+ tchunkptr t = *treebin_at(m, tidx);
+ size_t sizebits = size << leftshift_for_tree_index(tidx);
+ while (t != 0 && chunksize(t) != size) {
+ t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1];
+ sizebits <<= 1;
+ }
+ if (t != 0) {
+ tchunkptr u = t;
+ do {
+ if (u == (tchunkptr)x)
+ return 1;
+ } while ((u = u->fd) != t);
+ }
+ }
+ }
+ return 0;
+}
+
+/* Traverse each chunk and check it; return total */
+static size_t traverse_and_check(mstate m) {
+ size_t sum = 0;
+ if (is_initialized(m)) {
+ msegmentptr s = &m->seg;
+ sum += m->topsize + TOP_FOOT_SIZE;
+ while (s != 0) {
+ mchunkptr q = align_as_chunk(s->base);
+ mchunkptr lastq = 0;
+ assert(pinuse(q));
+ while (segment_holds(s, q) &&
+ q != m->top && q->head != FENCEPOST_HEAD) {
+ sum += chunksize(q);
+ if (is_inuse(q)) {
+ assert(!bin_find(m, q));
+ do_check_inuse_chunk(m, q);
+ }
+ else {
+ assert(q == m->dv || bin_find(m, q));
+ assert(lastq == 0 || is_inuse(lastq)); /* Not 2 consecutive free */
+ do_check_free_chunk(m, q);
+ }
+ lastq = q;
+ q = next_chunk(q);
+ }
+ s = s->next;
+ }
+ }
+ return sum;
+}
+
+
+/* Check all properties of malloc_state. */
+static void do_check_malloc_state(mstate m) {
+ bindex_t i;
+ size_t total;
+ /* check bins */
+ for (i = 0; i < NSMALLBINS; ++i)
+ do_check_smallbin(m, i);
+ for (i = 0; i < NTREEBINS; ++i)
+ do_check_treebin(m, i);
+
+ if (m->dvsize != 0) { /* check dv chunk */
+ do_check_any_chunk(m, m->dv);
+ assert(m->dvsize == chunksize(m->dv));
+ assert(m->dvsize >= MIN_CHUNK_SIZE);
+ assert(bin_find(m, m->dv) == 0);
+ }
+
+ if (m->top != 0) { /* check top chunk */
+ do_check_top_chunk(m, m->top);
+ /*assert(m->topsize == chunksize(m->top)); redundant */
+ assert(m->topsize > 0);
+ assert(bin_find(m, m->top) == 0);
+ }
+
+ total = traverse_and_check(m);
+ assert(total <= m->footprint);
+ assert(m->footprint <= m->max_footprint);
+}
+#endif /* DEBUG */
+
+/* ----------------------------- statistics ------------------------------ */
+
+#if !NO_MALLINFO
+static struct mallinfo internal_mallinfo(mstate m) {
+ struct mallinfo nm = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ ensure_initialization();
+ if (!PREACTION(m)) {
+ check_malloc_state(m);
+ if (is_initialized(m)) {
+ size_t nfree = SIZE_T_ONE; /* top always free */
+ size_t mfree = m->topsize + TOP_FOOT_SIZE;
+ size_t sum = mfree;
+ msegmentptr s = &m->seg;
+ while (s != 0) {
+ mchunkptr q = align_as_chunk(s->base);
+ while (segment_holds(s, q) &&
+ q != m->top && q->head != FENCEPOST_HEAD) {
+ size_t sz = chunksize(q);
+ sum += sz;
+ if (!is_inuse(q)) {
+ mfree += sz;
+ ++nfree;
+ }
+ q = next_chunk(q);
+ }
+ s = s->next;
+ }
+
+ nm.arena = sum;
+ nm.ordblks = nfree;
+ nm.hblkhd = m->footprint - sum;
+ nm.usmblks = m->max_footprint;
+ nm.uordblks = m->footprint - mfree;
+ nm.fordblks = mfree;
+ nm.keepcost = m->topsize;
+ }
+
+ POSTACTION(m);
+ }
+ return nm;
+}
+#endif /* !NO_MALLINFO */
+
+#if !NO_MALLOC_STATS
+static void internal_malloc_stats(mstate m) {
+ ensure_initialization();
+ if (!PREACTION(m)) {
+ size_t maxfp = 0;
+ size_t fp = 0;
+ size_t used = 0;
+ check_malloc_state(m);
+ if (is_initialized(m)) {
+ msegmentptr s = &m->seg;
+ maxfp = m->max_footprint;
+ fp = m->footprint;
+ used = fp - (m->topsize + TOP_FOOT_SIZE);
+
+ while (s != 0) {
+ mchunkptr q = align_as_chunk(s->base);
+ while (segment_holds(s, q) &&
+ q != m->top && q->head != FENCEPOST_HEAD) {
+ if (!is_inuse(q))
+ used -= chunksize(q);
+ q = next_chunk(q);
+ }
+ s = s->next;
+ }
+ }
+ POSTACTION(m); /* drop lock */
+ fprintf(stderr, "max system bytes = %10lu\n", (unsigned long)(maxfp));
+ fprintf(stderr, "system bytes = %10lu\n", (unsigned long)(fp));
+ fprintf(stderr, "in use bytes = %10lu\n", (unsigned long)(used));
+ }
+}
+#endif /* NO_MALLOC_STATS */
+
+/* ----------------------- Operations on smallbins ----------------------- */
+
+/*
+ Various forms of linking and unlinking are defined as macros. Even
+ the ones for trees, which are very long but have very short typical
+ paths. This is ugly but reduces reliance on inlining support of
+ compilers.
+*/
+
+/* Link a free chunk into a smallbin */
+#define insert_small_chunk(M, P, S) {\
+ bindex_t I = small_index(S);\
+ mchunkptr B = smallbin_at(M, I);\
+ mchunkptr F = B;\
+ assert(S >= MIN_CHUNK_SIZE);\
+ if (!smallmap_is_marked(M, I))\
+ mark_smallmap(M, I);\
+ else if (RTCHECK(ok_address(M, B->fd)))\
+ F = B->fd;\
+ else {\
+ CORRUPTION_ERROR_ACTION(M);\
+ }\
+ B->fd = P;\
+ F->bk = P;\
+ P->fd = F;\
+ P->bk = B;\
+}
+
+/* Unlink a chunk from a smallbin */
+#define unlink_small_chunk(M, P, S) {\
+ mchunkptr F = P->fd;\
+ mchunkptr B = P->bk;\
+ bindex_t I = small_index(S);\
+ assert(P != B);\
+ assert(P != F);\
+ assert(chunksize(P) == small_index2size(I));\
+ if (RTCHECK(F == smallbin_at(M,I) || (ok_address(M, F) && F->bk == P))) { \
+ if (B == F) {\
+ clear_smallmap(M, I);\
+ }\
+ else if (RTCHECK(B == smallbin_at(M,I) ||\
+ (ok_address(M, B) && B->fd == P))) {\
+ F->bk = B;\
+ B->fd = F;\
+ }\
+ else {\
+ CORRUPTION_ERROR_ACTION(M);\
+ }\
+ }\
+ else {\
+ CORRUPTION_ERROR_ACTION(M);\
+ }\
+}
+
+/* Unlink the first chunk from a smallbin */
+#define unlink_first_small_chunk(M, B, P, I) {\
+ mchunkptr F = P->fd;\
+ assert(P != B);\
+ assert(P != F);\
+ assert(chunksize(P) == small_index2size(I));\
+ if (B == F) {\
+ clear_smallmap(M, I);\
+ }\
+ else if (RTCHECK(ok_address(M, F) && F->bk == P)) {\
+ F->bk = B;\
+ B->fd = F;\
+ }\
+ else {\
+ CORRUPTION_ERROR_ACTION(M);\
+ }\
+}
+
+/* Replace dv node, binning the old one */
+/* Used only when dvsize known to be small */
+#define replace_dv(M, P, S) {\
+ size_t DVS = M->dvsize;\
+ assert(is_small(DVS));\
+ if (DVS != 0) {\
+ mchunkptr DV = M->dv;\
+ insert_small_chunk(M, DV, DVS);\
+ }\
+ M->dvsize = S;\
+ M->dv = P;\
+}
+
+/* ------------------------- Operations on trees ------------------------- */
+
+/* Insert chunk into tree */
+#define insert_large_chunk(M, X, S) {\
+ tbinptr* H;\
+ bindex_t I;\
+ compute_tree_index(S, I);\
+ H = treebin_at(M, I);\
+ X->index = I;\
+ X->child[0] = X->child[1] = 0;\
+ if (!treemap_is_marked(M, I)) {\
+ mark_treemap(M, I);\
+ *H = X;\
+ X->parent = (tchunkptr)H;\
+ X->fd = X->bk = X;\
+ }\
+ else {\
+ tchunkptr T = *H;\
+ size_t K = S << leftshift_for_tree_index(I);\
+ for (;;) {\
+ if (chunksize(T) != S) {\
+ tchunkptr* C = &(T->child[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]);\
+ K <<= 1;\
+ if (*C != 0)\
+ T = *C;\
+ else if (RTCHECK(ok_address(M, C))) {\
+ *C = X;\
+ X->parent = T;\
+ X->fd = X->bk = X;\
+ break;\
+ }\
+ else {\
+ CORRUPTION_ERROR_ACTION(M);\
+ break;\
+ }\
+ }\
+ else {\
+ tchunkptr F = T->fd;\
+ if (RTCHECK(ok_address(M, T) && ok_address(M, F))) {\
+ T->fd = F->bk = X;\
+ X->fd = F;\
+ X->bk = T;\
+ X->parent = 0;\
+ break;\
+ }\
+ else {\
+ CORRUPTION_ERROR_ACTION(M);\
+ break;\
+ }\
+ }\
+ }\
+ }\
+}
+
+/*
+ Unlink steps:
+
+ 1. If x is a chained node, unlink it from its same-sized fd/bk links
+ and choose its bk node as its replacement.
+ 2. If x was the last node of its size, but not a leaf node, it must
+ be replaced with a leaf node (not merely one with an open left or
+ right), to make sure that lefts and rights of descendents
+ correspond properly to bit masks. We use the rightmost descendent
+ of x. We could use any other leaf, but this is easy to locate and
+ tends to counteract removal of leftmosts elsewhere, and so keeps
+ paths shorter than minimally guaranteed. This doesn't loop much
+ because on average a node in a tree is near the bottom.
+ 3. If x is the base of a chain (i.e., has parent links) relink
+ x's parent and children to x's replacement (or null if none).
+*/
+
+#define unlink_large_chunk(M, X) {\
+ tchunkptr XP = X->parent;\
+ tchunkptr R;\
+ if (X->bk != X) {\
+ tchunkptr F = X->fd;\
+ R = X->bk;\
+ if (RTCHECK(ok_address(M, F) && F->bk == X && R->fd == X)) {\
+ F->bk = R;\
+ R->fd = F;\
+ }\
+ else {\
+ CORRUPTION_ERROR_ACTION(M);\
+ }\
+ }\
+ else {\
+ tchunkptr* RP;\
+ if (((R = *(RP = &(X->child[1]))) != 0) ||\
+ ((R = *(RP = &(X->child[0]))) != 0)) {\
+ tchunkptr* CP;\
+ while ((*(CP = &(R->child[1])) != 0) ||\
+ (*(CP = &(R->child[0])) != 0)) {\
+ R = *(RP = CP);\
+ }\
+ if (RTCHECK(ok_address(M, RP)))\
+ *RP = 0;\
+ else {\
+ CORRUPTION_ERROR_ACTION(M);\
+ }\
+ }\
+ }\
+ if (XP != 0) {\
+ tbinptr* H = treebin_at(M, X->index);\
+ if (X == *H) {\
+ if ((*H = R) == 0) \
+ clear_treemap(M, X->index);\
+ }\
+ else if (RTCHECK(ok_address(M, XP))) {\
+ if (XP->child[0] == X) \
+ XP->child[0] = R;\
+ else \
+ XP->child[1] = R;\
+ }\
+ else\
+ CORRUPTION_ERROR_ACTION(M);\
+ if (R != 0) {\
+ if (RTCHECK(ok_address(M, R))) {\
+ tchunkptr C0, C1;\
+ R->parent = XP;\
+ if ((C0 = X->child[0]) != 0) {\
+ if (RTCHECK(ok_address(M, C0))) {\
+ R->child[0] = C0;\
+ C0->parent = R;\
+ }\
+ else\
+ CORRUPTION_ERROR_ACTION(M);\
+ }\
+ if ((C1 = X->child[1]) != 0) {\
+ if (RTCHECK(ok_address(M, C1))) {\
+ R->child[1] = C1;\
+ C1->parent = R;\
+ }\
+ else\
+ CORRUPTION_ERROR_ACTION(M);\
+ }\
+ }\
+ else\
+ CORRUPTION_ERROR_ACTION(M);\
+ }\
+ }\
+}
+
+/* Relays to large vs small bin operations */
+
+#define insert_chunk(M, P, S)\
+ if (is_small(S)) insert_small_chunk(M, P, S)\
+ else { tchunkptr TP = (tchunkptr)(P); insert_large_chunk(M, TP, S); }
+
+#define unlink_chunk(M, P, S)\
+ if (is_small(S)) unlink_small_chunk(M, P, S)\
+ else { tchunkptr TP = (tchunkptr)(P); unlink_large_chunk(M, TP); }
+
+
+/* Relays to internal calls to malloc/free from realloc, memalign etc */
+
+#if ONLY_MSPACES
+#define internal_malloc(m, b) mspace_malloc(m, b)
+#define internal_free(m, mem) mspace_free(m,mem);
+#else /* ONLY_MSPACES */
+#if MSPACES
+#define internal_malloc(m, b)\
+ ((m == gm)? dlmalloc(b) : mspace_malloc(m, b))
+#define internal_free(m, mem)\
+ if (m == gm) dlfree(mem); else mspace_free(m,mem);
+#else /* MSPACES */
+#define internal_malloc(m, b) dlmalloc(b)
+#define internal_free(m, mem) dlfree(mem)
+#endif /* MSPACES */
+#endif /* ONLY_MSPACES */
+
+/* ----------------------- Direct-mmapping chunks ----------------------- */
+
+/*
+ Directly mmapped chunks are set up with an offset to the start of
+ the mmapped region stored in the prev_foot field of the chunk. This
+ allows reconstruction of the required argument to MUNMAP when freed,
+ and also allows adjustment of the returned chunk to meet alignment
+ requirements (especially in memalign).
+*/
+
+/* Malloc using mmap */
+static void* mmap_alloc(mstate m, size_t nb) {
+ size_t mmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK);
+ if (m->footprint_limit != 0) {
+ size_t fp = m->footprint + mmsize;
+ if (fp <= m->footprint || fp > m->footprint_limit)
+ return 0;
+ }
+ if (mmsize > nb) { /* Check for wrap around 0 */
+ char* mm = (char*)(CALL_DIRECT_MMAP(mmsize));
+ if (mm != CMFAIL) {
+ size_t offset = align_offset(chunk2mem(mm));
+ size_t psize = mmsize - offset - MMAP_FOOT_PAD;
+ mchunkptr p = (mchunkptr)(mm + offset);
+ p->prev_foot = offset;
+ p->head = psize;
+ mark_inuse_foot(m, p, psize);
+ chunk_plus_offset(p, psize)->head = FENCEPOST_HEAD;
+ chunk_plus_offset(p, psize+SIZE_T_SIZE)->head = 0;
+
+ if (m->least_addr == 0 || mm < m->least_addr)
+ m->least_addr = mm;
+ if ((m->footprint += mmsize) > m->max_footprint)
+ m->max_footprint = m->footprint;
+ assert(is_aligned(chunk2mem(p)));
+ check_mmapped_chunk(m, p);
+ return chunk2mem(p);
+ }
+ }
+ return 0;
+}
+
+/* Realloc using mmap */
+static mchunkptr mmap_resize(mstate m, mchunkptr oldp, size_t nb, int flags) {
+ size_t oldsize = chunksize(oldp);
+ (void)flags; /* placate people compiling -Wunused */
+ if (is_small(nb)) /* Can't shrink mmap regions below small size */
+ return 0;
+ /* Keep old chunk if big enough but not too big */
+ if (oldsize >= nb + SIZE_T_SIZE &&
+ (oldsize - nb) <= (mparams.granularity << 1))
+ return oldp;
+ else {
+ size_t offset = oldp->prev_foot;
+ size_t oldmmsize = oldsize + offset + MMAP_FOOT_PAD;
+ size_t newmmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK);
+ char* cp = (char*)CALL_MREMAP((char*)oldp - offset,
+ oldmmsize, newmmsize, flags);
+ if (cp != CMFAIL) {
+ mchunkptr newp = (mchunkptr)(cp + offset);
+ size_t psize = newmmsize - offset - MMAP_FOOT_PAD;
+ newp->head = psize;
+ mark_inuse_foot(m, newp, psize);
+ chunk_plus_offset(newp, psize)->head = FENCEPOST_HEAD;
+ chunk_plus_offset(newp, psize+SIZE_T_SIZE)->head = 0;
+
+ if (cp < m->least_addr)
+ m->least_addr = cp;
+ if ((m->footprint += newmmsize - oldmmsize) > m->max_footprint)
+ m->max_footprint = m->footprint;
+ check_mmapped_chunk(m, newp);
+ return newp;
+ }
+ }
+ return 0;
+}
+
+
+/* -------------------------- mspace management -------------------------- */
+
+/* Initialize top chunk and its size */
+static void init_top(mstate m, mchunkptr p, size_t psize) {
+ /* Ensure alignment */
+ size_t offset = align_offset(chunk2mem(p));
+ p = (mchunkptr)((char*)p + offset);
+ psize -= offset;
+
+ m->top = p;
+ m->topsize = psize;
+ p->head = psize | PINUSE_BIT;
+ /* set size of fake trailing chunk holding overhead space only once */
+ chunk_plus_offset(p, psize)->head = TOP_FOOT_SIZE;
+ m->trim_check = mparams.trim_threshold; /* reset on each update */
+}
+
+/* Initialize bins for a new mstate that is otherwise zeroed out */
+static void init_bins(mstate m) {
+ /* Establish circular links for smallbins */
+ bindex_t i;
+ for (i = 0; i < NSMALLBINS; ++i) {
+ sbinptr bin = smallbin_at(m,i);
+ bin->fd = bin->bk = bin;
+ }
+}
+
+#if PROCEED_ON_ERROR
+
+/* default corruption action */
+static void reset_on_error(mstate m) {
+ int i;
+ ++malloc_corruption_error_count;
+ /* Reinitialize fields to forget about all memory */
+ m->smallmap = m->treemap = 0;
+ m->dvsize = m->topsize = 0;
+ m->seg.base = 0;
+ m->seg.size = 0;
+ m->seg.next = 0;
+ m->top = m->dv = 0;
+ for (i = 0; i < NTREEBINS; ++i)
+ *treebin_at(m, i) = 0;
+ init_bins(m);
+}
+#endif /* PROCEED_ON_ERROR */
+
+/* Allocate chunk and prepend remainder with chunk in successor base. */
+static void* prepend_alloc(mstate m, char* newbase, char* oldbase,
+ size_t nb) {
+ mchunkptr p = align_as_chunk(newbase);
+ mchunkptr oldfirst = align_as_chunk(oldbase);
+ size_t psize = (char*)oldfirst - (char*)p;
+ mchunkptr q = chunk_plus_offset(p, nb);
+ size_t qsize = psize - nb;
+ set_size_and_pinuse_of_inuse_chunk(m, p, nb);
+
+ assert((char*)oldfirst > (char*)q);
+ assert(pinuse(oldfirst));
+ assert(qsize >= MIN_CHUNK_SIZE);
+
+ /* consolidate remainder with first chunk of old base */
+ if (oldfirst == m->top) {
+ size_t tsize = m->topsize += qsize;
+ m->top = q;
+ q->head = tsize | PINUSE_BIT;
+ check_top_chunk(m, q);
+ }
+ else if (oldfirst == m->dv) {
+ size_t dsize = m->dvsize += qsize;
+ m->dv = q;
+ set_size_and_pinuse_of_free_chunk(q, dsize);
+ }
+ else {
+ if (!is_inuse(oldfirst)) {
+ size_t nsize = chunksize(oldfirst);
+ unlink_chunk(m, oldfirst, nsize);
+ oldfirst = chunk_plus_offset(oldfirst, nsize);
+ qsize += nsize;
+ }
+ set_free_with_pinuse(q, qsize, oldfirst);
+ insert_chunk(m, q, qsize);
+ check_free_chunk(m, q);
+ }
+
+ check_malloced_chunk(m, chunk2mem(p), nb);
+ return chunk2mem(p);
+}
+
+/* Add a segment to hold a new noncontiguous region */
+static void add_segment(mstate m, char* tbase, size_t tsize, flag_t mmapped) {
+ /* Determine locations and sizes of segment, fenceposts, old top */
+ char* old_top = (char*)m->top;
+ msegmentptr oldsp = segment_holding(m, old_top);
+ char* old_end = oldsp->base + oldsp->size;
+ size_t ssize = pad_request(sizeof(struct malloc_segment));
+ char* rawsp = old_end - (ssize + FOUR_SIZE_T_SIZES + CHUNK_ALIGN_MASK);
+ size_t offset = align_offset(chunk2mem(rawsp));
+ char* asp = rawsp + offset;
+ char* csp = (asp < (old_top + MIN_CHUNK_SIZE))? old_top : asp;
+ mchunkptr sp = (mchunkptr)csp;
+ msegmentptr ss = (msegmentptr)(chunk2mem(sp));
+ mchunkptr tnext = chunk_plus_offset(sp, ssize);
+ mchunkptr p = tnext;
+ int nfences = 0;
+
+ /* reset top to new space */
+ init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE);
+
+ /* Set up segment record */
+ assert(is_aligned(ss));
+ set_size_and_pinuse_of_inuse_chunk(m, sp, ssize);
+ *ss = m->seg; /* Push current record */
+ m->seg.base = tbase;
+ m->seg.size = tsize;
+ m->seg.sflags = mmapped;
+ m->seg.next = ss;
+
+ /* Insert trailing fenceposts */
+ for (;;) {
+ mchunkptr nextp = chunk_plus_offset(p, SIZE_T_SIZE);
+ p->head = FENCEPOST_HEAD;
+ ++nfences;
+ if ((char*)(&(nextp->head)) < old_end)
+ p = nextp;
+ else
+ break;
+ }
+ assert(nfences >= 2);
+
+ /* Insert the rest of old top into a bin as an ordinary free chunk */
+ if (csp != old_top) {
+ mchunkptr q = (mchunkptr)old_top;
+ size_t psize = csp - old_top;
+ mchunkptr tn = chunk_plus_offset(q, psize);
+ set_free_with_pinuse(q, psize, tn);
+ insert_chunk(m, q, psize);
+ }
+
+ check_top_chunk(m, m->top);
+}
+
+/* -------------------------- System allocation -------------------------- */
+
+/* Get memory from system using MORECORE or MMAP */
+static void* sys_alloc(mstate m, size_t nb) {
+ char* tbase = CMFAIL;
+ size_t tsize = 0;
+ flag_t mmap_flag = 0;
+ size_t asize; /* allocation size */
+
+ ensure_initialization();
+
+ /* Directly map large chunks, but only if already initialized */
+ if (use_mmap(m) && nb >= mparams.mmap_threshold && m->topsize != 0) {
+ void* mem = mmap_alloc(m, nb);
+ if (mem != 0)
+ return mem;
+ }
+
+ asize = granularity_align(nb + SYS_ALLOC_PADDING);
+ if (asize <= nb)
+ return 0; /* wraparound */
+ if (m->footprint_limit != 0) {
+ size_t fp = m->footprint + asize;
+ if (fp <= m->footprint || fp > m->footprint_limit)
+ return 0;
+ }
+
+ /*
+ Try getting memory in any of three ways (in most-preferred to
+ least-preferred order):
+ 1. A call to MORECORE that can normally contiguously extend memory.
+ (disabled if not MORECORE_CONTIGUOUS or not HAVE_MORECORE or
+ or main space is mmapped or a previous contiguous call failed)
+ 2. A call to MMAP new space (disabled if not HAVE_MMAP).
+ Note that under the default settings, if MORECORE is unable to
+ fulfill a request, and HAVE_MMAP is true, then mmap is
+ used as a noncontiguous system allocator. This is a useful backup
+ strategy for systems with holes in address spaces -- in this case
+ sbrk cannot contiguously expand the heap, but mmap may be able to
+ find space.
+ 3. A call to MORECORE that cannot usually contiguously extend memory.
+ (disabled if not HAVE_MORECORE)
+
+ In all cases, we need to request enough bytes from system to ensure
+ we can malloc nb bytes upon success, so pad with enough space for
+ top_foot, plus alignment-pad to make sure we don't lose bytes if
+ not on boundary, and round this up to a granularity unit.
+ */
+
+ if (MORECORE_CONTIGUOUS && !use_noncontiguous(m)) {
+ char* br = CMFAIL;
+ size_t ssize = asize; /* sbrk call size */
+ msegmentptr ss = (m->top == 0)? 0 : segment_holding(m, (char*)m->top);
+ ACQUIRE_MALLOC_GLOBAL_LOCK();
+
+ if (ss == 0) { /* First time through or recovery */
+ char* base = (char*)CALL_MORECORE(0);
+ if (base != CMFAIL) {
+ size_t fp;
+ /* Adjust to end on a page boundary */
+ if (!is_page_aligned(base))
+ ssize += (page_align((size_t)base) - (size_t)base);
+ fp = m->footprint + ssize; /* recheck limits */
+ if (ssize > nb && ssize < HALF_MAX_SIZE_T &&
+ (m->footprint_limit == 0 ||
+ (fp > m->footprint && fp <= m->footprint_limit)) &&
+ (br = (char*)(CALL_MORECORE(ssize))) == base) {
+ tbase = base;
+ tsize = ssize;
+ }
+ }
+ }
+ else {
+ /* Subtract out existing available top space from MORECORE request. */
+ ssize = granularity_align(nb - m->topsize + SYS_ALLOC_PADDING);
+ /* Use mem here only if it did continuously extend old space */
+ if (ssize < HALF_MAX_SIZE_T &&
+ (br = (char*)(CALL_MORECORE(ssize))) == ss->base+ss->size) {
+ tbase = br;
+ tsize = ssize;
+ }
+ }
+
+ if (tbase == CMFAIL) { /* Cope with partial failure */
+ if (br != CMFAIL) { /* Try to use/extend the space we did get */
+ if (ssize < HALF_MAX_SIZE_T &&
+ ssize < nb + SYS_ALLOC_PADDING) {
+ size_t esize = granularity_align(nb + SYS_ALLOC_PADDING - ssize);
+ if (esize < HALF_MAX_SIZE_T) {
+ char* end = (char*)CALL_MORECORE(esize);
+ if (end != CMFAIL)
+ ssize += esize;
+ else { /* Can't use; try to release */
+ (void) CALL_MORECORE(-ssize);
+ br = CMFAIL;
+ }
+ }
+ }
+ }
+ if (br != CMFAIL) { /* Use the space we did get */
+ tbase = br;
+ tsize = ssize;
+ }
+ else
+ disable_contiguous(m); /* Don't try contiguous path in the future */
+ }
+
+ RELEASE_MALLOC_GLOBAL_LOCK();
+ }
+
+ if (HAVE_MMAP && tbase == CMFAIL) { /* Try MMAP */
+ char* mp = (char*)(CALL_MMAP(asize));
+ if (mp != CMFAIL) {
+ tbase = mp;
+ tsize = asize;
+ mmap_flag = USE_MMAP_BIT;
+ }
+ }
+
+ if (HAVE_MORECORE && tbase == CMFAIL) { /* Try noncontiguous MORECORE */
+ if (asize < HALF_MAX_SIZE_T) {
+ char* br = CMFAIL;
+ char* end = CMFAIL;
+ ACQUIRE_MALLOC_GLOBAL_LOCK();
+ br = (char*)(CALL_MORECORE(asize));
+ end = (char*)(CALL_MORECORE(0));
+ RELEASE_MALLOC_GLOBAL_LOCK();
+ if (br != CMFAIL && end != CMFAIL && br < end) {
+ size_t ssize = end - br;
+ if (ssize > nb + TOP_FOOT_SIZE) {
+ tbase = br;
+ tsize = ssize;
+ }
+ }
+ }
+ }
+
+ if (tbase != CMFAIL) {
+
+ if ((m->footprint += tsize) > m->max_footprint)
+ m->max_footprint = m->footprint;
+
+ if (!is_initialized(m)) { /* first-time initialization */
+ if (m->least_addr == 0 || tbase < m->least_addr)
+ m->least_addr = tbase;
+ m->seg.base = tbase;
+ m->seg.size = tsize;
+ m->seg.sflags = mmap_flag;
+ m->magic = mparams.magic;
+ m->release_checks = MAX_RELEASE_CHECK_RATE;
+ init_bins(m);
+#if !ONLY_MSPACES
+ if (is_global(m))
+ init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE);
+ else
+#endif
+ {
+ /* Offset top by embedded malloc_state */
+ mchunkptr mn = next_chunk(mem2chunk(m));
+ init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) -TOP_FOOT_SIZE);
+ }
+ }
+
+ else {
+ /* Try to merge with an existing segment */
+ msegmentptr sp = &m->seg;
+ /* Only consider most recent segment if traversal suppressed */
+ while (sp != 0 && tbase != sp->base + sp->size)
+ sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next;
+ if (sp != 0 &&
+ !is_extern_segment(sp) &&
+ (sp->sflags & USE_MMAP_BIT) == mmap_flag &&
+ segment_holds(sp, m->top)) { /* append */
+ sp->size += tsize;
+ init_top(m, m->top, m->topsize + tsize);
+ }
+ else {
+ if (tbase < m->least_addr)
+ m->least_addr = tbase;
+ sp = &m->seg;
+ while (sp != 0 && sp->base != tbase + tsize)
+ sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next;
+ if (sp != 0 &&
+ !is_extern_segment(sp) &&
+ (sp->sflags & USE_MMAP_BIT) == mmap_flag) {
+ char* oldbase = sp->base;
+ sp->base = tbase;
+ sp->size += tsize;
+ return prepend_alloc(m, tbase, oldbase, nb);
+ }
+ else
+ add_segment(m, tbase, tsize, mmap_flag);
+ }
+ }
+
+ if (nb < m->topsize) { /* Allocate from new or extended top space */
+ size_t rsize = m->topsize -= nb;
+ mchunkptr p = m->top;
+ mchunkptr r = m->top = chunk_plus_offset(p, nb);
+ r->head = rsize | PINUSE_BIT;
+ set_size_and_pinuse_of_inuse_chunk(m, p, nb);
+ check_top_chunk(m, m->top);
+ check_malloced_chunk(m, chunk2mem(p), nb);
+ return chunk2mem(p);
+ }
+ }
+
+ MALLOC_FAILURE_ACTION;
+ return 0;
+}
+
+/* ----------------------- system deallocation -------------------------- */
+
+/* Unmap and unlink any mmapped segments that don't contain used chunks */
+static size_t release_unused_segments(mstate m) {
+ size_t released = 0;
+ int nsegs = 0;
+ msegmentptr pred = &m->seg;
+ msegmentptr sp = pred->next;
+ while (sp != 0) {
+ char* base = sp->base;
+ size_t size = sp->size;
+ msegmentptr next = sp->next;
+ ++nsegs;
+ if (is_mmapped_segment(sp) && !is_extern_segment(sp)) {
+ mchunkptr p = align_as_chunk(base);
+ size_t psize = chunksize(p);
+ /* Can unmap if first chunk holds entire segment and not pinned */
+ if (!is_inuse(p) && (char*)p + psize >= base + size - TOP_FOOT_SIZE) {
+ tchunkptr tp = (tchunkptr)p;
+ assert(segment_holds(sp, (char*)sp));
+ if (p == m->dv) {
+ m->dv = 0;
+ m->dvsize = 0;
+ }
+ else {
+ unlink_large_chunk(m, tp);
+ }
+ if (CALL_MUNMAP(base, size) == 0) {
+ released += size;
+ m->footprint -= size;
+ /* unlink obsoleted record */
+ sp = pred;
+ sp->next = next;
+ }
+ else { /* back out if cannot unmap */
+ insert_large_chunk(m, tp, psize);
+ }
+ }
+ }
+ if (NO_SEGMENT_TRAVERSAL) /* scan only first segment */
+ break;
+ pred = sp;
+ sp = next;
+ }
+ /* Reset check counter */
+ m->release_checks = (((size_t) nsegs > (size_t) MAX_RELEASE_CHECK_RATE)?
+ (size_t) nsegs : (size_t) MAX_RELEASE_CHECK_RATE);
+ return released;
+}
+
+static int sys_trim(mstate m, size_t pad) {
+ size_t released = 0;
+ ensure_initialization();
+ if (pad < MAX_REQUEST && is_initialized(m)) {
+ pad += TOP_FOOT_SIZE; /* ensure enough room for segment overhead */
+
+ if (m->topsize > pad) {
+ /* Shrink top space in granularity-size units, keeping at least one */
+ size_t unit = mparams.granularity;
+ size_t extra = ((m->topsize - pad + (unit - SIZE_T_ONE)) / unit -
+ SIZE_T_ONE) * unit;
+ msegmentptr sp = segment_holding(m, (char*)m->top);
+
+ if (!is_extern_segment(sp)) {
+ if (is_mmapped_segment(sp)) {
+ if (HAVE_MMAP &&
+ sp->size >= extra &&
+ !has_segment_link(m, sp)) { /* can't shrink if pinned */
+ size_t newsize = sp->size - extra;
+ (void)newsize; /* placate people compiling -Wunused-variable */
+ /* Prefer mremap, fall back to munmap */
+ if ((CALL_MREMAP(sp->base, sp->size, newsize, 0) != MFAIL) ||
+ (CALL_MUNMAP(sp->base + newsize, extra) == 0)) {
+ released = extra;
+ }
+ }
+ }
+ else if (HAVE_MORECORE) {
+ if (extra >= HALF_MAX_SIZE_T) /* Avoid wrapping negative */
+ extra = (HALF_MAX_SIZE_T) + SIZE_T_ONE - unit;
+ ACQUIRE_MALLOC_GLOBAL_LOCK();
+ {
+ /* Make sure end of memory is where we last set it. */
+ char* old_br = (char*)(CALL_MORECORE(0));
+ if (old_br == sp->base + sp->size) {
+ char* rel_br = (char*)(CALL_MORECORE(-extra));
+ char* new_br = (char*)(CALL_MORECORE(0));
+ if (rel_br != CMFAIL && new_br < old_br)
+ released = old_br - new_br;
+ }
+ }
+ RELEASE_MALLOC_GLOBAL_LOCK();
+ }
+ }
+
+ if (released != 0) {
+ sp->size -= released;
+ m->footprint -= released;
+ init_top(m, m->top, m->topsize - released);
+ check_top_chunk(m, m->top);
+ }
+ }
+
+ /* Unmap any unused mmapped segments */
+ if (HAVE_MMAP)
+ released += release_unused_segments(m);
+
+ /* On failure, disable autotrim to avoid repeated failed future calls */
+ if (released == 0 && m->topsize > m->trim_check)
+ m->trim_check = MAX_SIZE_T;
+ }
+
+ return (released != 0)? 1 : 0;
+}
+
+/* Consolidate and bin a chunk. Differs from exported versions
+ of free mainly in that the chunk need not be marked as inuse.
+*/
+static void dispose_chunk(mstate m, mchunkptr p, size_t psize) {
+ mchunkptr next = chunk_plus_offset(p, psize);
+ if (!pinuse(p)) {
+ mchunkptr prev;
+ size_t prevsize = p->prev_foot;
+ if (is_mmapped(p)) {
+ psize += prevsize + MMAP_FOOT_PAD;
+ if (CALL_MUNMAP((char*)p - prevsize, psize) == 0)
+ m->footprint -= psize;
+ return;
+ }
+ prev = chunk_minus_offset(p, prevsize);
+ psize += prevsize;
+ p = prev;
+ if (RTCHECK(ok_address(m, prev))) { /* consolidate backward */
+ if (p != m->dv) {
+ unlink_chunk(m, p, prevsize);
+ }
+ else if ((next->head & INUSE_BITS) == INUSE_BITS) {
+ m->dvsize = psize;
+ set_free_with_pinuse(p, psize, next);
+ return;
+ }
+ }
+ else {
+ CORRUPTION_ERROR_ACTION(m);
+ return;
+ }
+ }
+ if (RTCHECK(ok_address(m, next))) {
+ if (!cinuse(next)) { /* consolidate forward */
+ if (next == m->top) {
+ size_t tsize = m->topsize += psize;
+ m->top = p;
+ p->head = tsize | PINUSE_BIT;
+ if (p == m->dv) {
+ m->dv = 0;
+ m->dvsize = 0;
+ }
+ return;
+ }
+ else if (next == m->dv) {
+ size_t dsize = m->dvsize += psize;
+ m->dv = p;
+ set_size_and_pinuse_of_free_chunk(p, dsize);
+ return;
+ }
+ else {
+ size_t nsize = chunksize(next);
+ psize += nsize;
+ unlink_chunk(m, next, nsize);
+ set_size_and_pinuse_of_free_chunk(p, psize);
+ if (p == m->dv) {
+ m->dvsize = psize;
+ return;
+ }
+ }
+ }
+ else {
+ set_free_with_pinuse(p, psize, next);
+ }
+ insert_chunk(m, p, psize);
+ }
+ else {
+ CORRUPTION_ERROR_ACTION(m);
+ }
+}
+
+/* ---------------------------- malloc --------------------------- */
+
+/* allocate a large request from the best fitting chunk in a treebin */
+static void* tmalloc_large(mstate m, size_t nb) {
+ tchunkptr v = 0;
+ size_t rsize = -nb; /* Unsigned negation */
+ tchunkptr t;
+ bindex_t idx;
+ compute_tree_index(nb, idx);
+ if ((t = *treebin_at(m, idx)) != 0) {
+ /* Traverse tree for this bin looking for node with size == nb */
+ size_t sizebits = nb << leftshift_for_tree_index(idx);
+ tchunkptr rst = 0; /* The deepest untaken right subtree */
+ for (;;) {
+ tchunkptr rt;
+ size_t trem = chunksize(t) - nb;
+ if (trem < rsize) {
+ v = t;
+ if ((rsize = trem) == 0)
+ break;
+ }
+ rt = t->child[1];
+ t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1];
+ if (rt != 0 && rt != t)
+ rst = rt;
+ if (t == 0) {
+ t = rst; /* set t to least subtree holding sizes > nb */
+ break;
+ }
+ sizebits <<= 1;
+ }
+ }
+ if (t == 0 && v == 0) { /* set t to root of next non-empty treebin */
+ binmap_t leftbits = left_bits(idx2bit(idx)) & m->treemap;
+ if (leftbits != 0) {
+ bindex_t i;
+ binmap_t leastbit = least_bit(leftbits);
+ compute_bit2idx(leastbit, i);
+ t = *treebin_at(m, i);
+ }
+ }
+
+ while (t != 0) { /* find smallest of tree or subtree */
+ size_t trem = chunksize(t) - nb;
+ if (trem < rsize) {
+ rsize = trem;
+ v = t;
+ }
+ t = leftmost_child(t);
+ }
+
+ /* If dv is a better fit, return 0 so malloc will use it */
+ if (v != 0 && rsize < (size_t)(m->dvsize - nb)) {
+ if (RTCHECK(ok_address(m, v))) { /* split */
+ mchunkptr r = chunk_plus_offset(v, nb);
+ assert(chunksize(v) == rsize + nb);
+ if (RTCHECK(ok_next(v, r))) {
+ unlink_large_chunk(m, v);
+ if (rsize < MIN_CHUNK_SIZE)
+ set_inuse_and_pinuse(m, v, (rsize + nb));
+ else {
+ set_size_and_pinuse_of_inuse_chunk(m, v, nb);
+ set_size_and_pinuse_of_free_chunk(r, rsize);
+ insert_chunk(m, r, rsize);
+ }
+ return chunk2mem(v);
+ }
+ }
+ CORRUPTION_ERROR_ACTION(m);
+ }
+ return 0;
+}
+
+/* allocate a small request from the best fitting chunk in a treebin */
+static void* tmalloc_small(mstate m, size_t nb) {
+ tchunkptr t, v;
+ size_t rsize;
+ bindex_t i;
+ binmap_t leastbit = least_bit(m->treemap);
+ compute_bit2idx(leastbit, i);
+ v = t = *treebin_at(m, i);
+ rsize = chunksize(t) - nb;
+
+ while ((t = leftmost_child(t)) != 0) {
+ size_t trem = chunksize(t) - nb;
+ if (trem < rsize) {
+ rsize = trem;
+ v = t;
+ }
+ }
+
+ if (RTCHECK(ok_address(m, v))) {
+ mchunkptr r = chunk_plus_offset(v, nb);
+ assert(chunksize(v) == rsize + nb);
+ if (RTCHECK(ok_next(v, r))) {
+ unlink_large_chunk(m, v);
+ if (rsize < MIN_CHUNK_SIZE)
+ set_inuse_and_pinuse(m, v, (rsize + nb));
+ else {
+ set_size_and_pinuse_of_inuse_chunk(m, v, nb);
+ set_size_and_pinuse_of_free_chunk(r, rsize);
+ replace_dv(m, r, rsize);
+ }
+ return chunk2mem(v);
+ }
+ }
+
+ CORRUPTION_ERROR_ACTION(m);
+ return 0;
+}
+
+#if !ONLY_MSPACES
+
+void* dlmalloc(size_t bytes) {
+ /*
+ Basic algorithm:
+ If a small request (< 256 bytes minus per-chunk overhead):
+ 1. If one exists, use a remainderless chunk in associated smallbin.
+ (Remainderless means that there are too few excess bytes to
+ represent as a chunk.)
+ 2. If it is big enough, use the dv chunk, which is normally the
+ chunk adjacent to the one used for the most recent small request.
+ 3. If one exists, split the smallest available chunk in a bin,
+ saving remainder in dv.
+ 4. If it is big enough, use the top chunk.
+ 5. If available, get memory from system and use it
+ Otherwise, for a large request:
+ 1. Find the smallest available binned chunk that fits, and use it
+ if it is better fitting than dv chunk, splitting if necessary.
+ 2. If better fitting than any binned chunk, use the dv chunk.
+ 3. If it is big enough, use the top chunk.
+ 4. If request size >= mmap threshold, try to directly mmap this chunk.
+ 5. If available, get memory from system and use it
+
+ The ugly goto's here ensure that postaction occurs along all paths.
+ */
+
+#if USE_LOCKS
+ ensure_initialization(); /* initialize in sys_alloc if not using locks */
+#endif
+
+ if (!PREACTION(gm)) {
+ void* mem;
+ size_t nb;
+ if (bytes <= MAX_SMALL_REQUEST) {
+ bindex_t idx;
+ binmap_t smallbits;
+ nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes);
+ idx = small_index(nb);
+ smallbits = gm->smallmap >> idx;
+
+ if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */
+ mchunkptr b, p;
+ idx += ~smallbits & 1; /* Uses next bin if idx empty */
+ b = smallbin_at(gm, idx);
+ p = b->fd;
+ assert(chunksize(p) == small_index2size(idx));
+ unlink_first_small_chunk(gm, b, p, idx);
+ set_inuse_and_pinuse(gm, p, small_index2size(idx));
+ mem = chunk2mem(p);
+ check_malloced_chunk(gm, mem, nb);
+ goto postaction;
+ }
+
+ else if (nb > gm->dvsize) {
+ if (smallbits != 0) { /* Use chunk in next nonempty smallbin */
+ mchunkptr b, p, r;
+ size_t rsize;
+ bindex_t i;
+ binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx));
+ binmap_t leastbit = least_bit(leftbits);
+ compute_bit2idx(leastbit, i);
+ b = smallbin_at(gm, i);
+ p = b->fd;
+ assert(chunksize(p) == small_index2size(i));
+ unlink_first_small_chunk(gm, b, p, i);
+ rsize = small_index2size(i) - nb;
+ /* Fit here cannot be remainderless if 4byte sizes */
+ if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE)
+ set_inuse_and_pinuse(gm, p, small_index2size(i));
+ else {
+ set_size_and_pinuse_of_inuse_chunk(gm, p, nb);
+ r = chunk_plus_offset(p, nb);
+ set_size_and_pinuse_of_free_chunk(r, rsize);
+ replace_dv(gm, r, rsize);
+ }
+ mem = chunk2mem(p);
+ check_malloced_chunk(gm, mem, nb);
+ goto postaction;
+ }
+
+ else if (gm->treemap != 0 && (mem = tmalloc_small(gm, nb)) != 0) {
+ check_malloced_chunk(gm, mem, nb);
+ goto postaction;
+ }
+ }
+ }
+ else if (bytes >= MAX_REQUEST)
+ nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */
+ else {
+ nb = pad_request(bytes);
+ if (gm->treemap != 0 && (mem = tmalloc_large(gm, nb)) != 0) {
+ check_malloced_chunk(gm, mem, nb);
+ goto postaction;
+ }
+ }
+
+ if (nb <= gm->dvsize) {
+ size_t rsize = gm->dvsize - nb;
+ mchunkptr p = gm->dv;
+ if (rsize >= MIN_CHUNK_SIZE) { /* split dv */
+ mchunkptr r = gm->dv = chunk_plus_offset(p, nb);
+ gm->dvsize = rsize;
+ set_size_and_pinuse_of_free_chunk(r, rsize);
+ set_size_and_pinuse_of_inuse_chunk(gm, p, nb);
+ }
+ else { /* exhaust dv */
+ size_t dvs = gm->dvsize;
+ gm->dvsize = 0;
+ gm->dv = 0;
+ set_inuse_and_pinuse(gm, p, dvs);
+ }
+ mem = chunk2mem(p);
+ check_malloced_chunk(gm, mem, nb);
+ goto postaction;
+ }
+
+ else if (nb < gm->topsize) { /* Split top */
+ size_t rsize = gm->topsize -= nb;
+ mchunkptr p = gm->top;
+ mchunkptr r = gm->top = chunk_plus_offset(p, nb);
+ r->head = rsize | PINUSE_BIT;
+ set_size_and_pinuse_of_inuse_chunk(gm, p, nb);
+ mem = chunk2mem(p);
+ check_top_chunk(gm, gm->top);
+ check_malloced_chunk(gm, mem, nb);
+ goto postaction;
+ }
+
+ mem = sys_alloc(gm, nb);
+
+ postaction:
+ POSTACTION(gm);
+ return mem;
+ }
+
+ return 0;
+}
+
+/* ---------------------------- free --------------------------- */
+
+void dlfree(void* mem) {
+ /*
+ Consolidate freed chunks with preceeding or succeeding bordering
+ free chunks, if they exist, and then place in a bin. Intermixed
+ with special cases for top, dv, mmapped chunks, and usage errors.
+ */
+
+ if (mem != 0) {
+ mchunkptr p = mem2chunk(mem);
+#if FOOTERS
+ mstate fm = get_mstate_for(p);
+ if (!ok_magic(fm)) {
+ USAGE_ERROR_ACTION(fm, p);
+ return;
+ }
+#else /* FOOTERS */
+#define fm gm
+#endif /* FOOTERS */
+ if (!PREACTION(fm)) {
+ check_inuse_chunk(fm, p);
+ if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) {
+ size_t psize = chunksize(p);
+ mchunkptr next = chunk_plus_offset(p, psize);
+ if (!pinuse(p)) {
+ size_t prevsize = p->prev_foot;
+ if (is_mmapped(p)) {
+ psize += prevsize + MMAP_FOOT_PAD;
+ if (CALL_MUNMAP((char*)p - prevsize, psize) == 0)
+ fm->footprint -= psize;
+ goto postaction;
+ }
+ else {
+ mchunkptr prev = chunk_minus_offset(p, prevsize);
+ psize += prevsize;
+ p = prev;
+ if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */
+ if (p != fm->dv) {
+ unlink_chunk(fm, p, prevsize);
+ }
+ else if ((next->head & INUSE_BITS) == INUSE_BITS) {
+ fm->dvsize = psize;
+ set_free_with_pinuse(p, psize, next);
+ goto postaction;
+ }
+ }
+ else
+ goto erroraction;
+ }
+ }
+
+ if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) {
+ if (!cinuse(next)) { /* consolidate forward */
+ if (next == fm->top) {
+ size_t tsize = fm->topsize += psize;
+ fm->top = p;
+ p->head = tsize | PINUSE_BIT;
+ if (p == fm->dv) {
+ fm->dv = 0;
+ fm->dvsize = 0;
+ }
+ if (should_trim(fm, tsize))
+ sys_trim(fm, 0);
+ goto postaction;
+ }
+ else if (next == fm->dv) {
+ size_t dsize = fm->dvsize += psize;
+ fm->dv = p;
+ set_size_and_pinuse_of_free_chunk(p, dsize);
+ goto postaction;
+ }
+ else {
+ size_t nsize = chunksize(next);
+ psize += nsize;
+ unlink_chunk(fm, next, nsize);
+ set_size_and_pinuse_of_free_chunk(p, psize);
+ if (p == fm->dv) {
+ fm->dvsize = psize;
+ goto postaction;
+ }
+ }
+ }
+ else
+ set_free_with_pinuse(p, psize, next);
+
+ if (is_small(psize)) {
+ insert_small_chunk(fm, p, psize);
+ check_free_chunk(fm, p);
+ }
+ else {
+ tchunkptr tp = (tchunkptr)p;
+ insert_large_chunk(fm, tp, psize);
+ check_free_chunk(fm, p);
+ if (--fm->release_checks == 0)
+ release_unused_segments(fm);
+ }
+ goto postaction;
+ }
+ }
+ erroraction:
+ USAGE_ERROR_ACTION(fm, p);
+ postaction:
+ POSTACTION(fm);
+ }
+ }
+#if !FOOTERS
+#undef fm
+#endif /* FOOTERS */
+}
+
+void* dlcalloc(size_t n_elements, size_t elem_size) {
+ void* mem;
+ size_t req = 0;
+ if (n_elements != 0) {
+ req = n_elements * elem_size;
+ if (((n_elements | elem_size) & ~(size_t)0xffff) &&
+ (req / n_elements != elem_size))
+ req = MAX_SIZE_T; /* force downstream failure on overflow */
+ }
+ mem = dlmalloc(req);
+ if (mem != 0 && calloc_must_clear(mem2chunk(mem)))
+ memset(mem, 0, req);
+ return mem;
+}
+
+#endif /* !ONLY_MSPACES */
+
+/* ------------ Internal support for realloc, memalign, etc -------------- */
+
+/* Try to realloc; only in-place unless can_move true */
+static mchunkptr try_realloc_chunk(mstate m, mchunkptr p, size_t nb,
+ int can_move) {
+ mchunkptr newp = 0;
+ size_t oldsize = chunksize(p);
+ mchunkptr next = chunk_plus_offset(p, oldsize);
+ if (RTCHECK(ok_address(m, p) && ok_inuse(p) &&
+ ok_next(p, next) && ok_pinuse(next))) {
+ if (is_mmapped(p)) {
+ newp = mmap_resize(m, p, nb, can_move);
+ }
+ else if (oldsize >= nb) { /* already big enough */
+ size_t rsize = oldsize - nb;
+ if (rsize >= MIN_CHUNK_SIZE) { /* split off remainder */
+ mchunkptr r = chunk_plus_offset(p, nb);
+ set_inuse(m, p, nb);
+ set_inuse(m, r, rsize);
+ dispose_chunk(m, r, rsize);
+ }
+ newp = p;
+ }
+ else if (next == m->top) { /* extend into top */
+ if (oldsize + m->topsize > nb) {
+ size_t newsize = oldsize + m->topsize;
+ size_t newtopsize = newsize - nb;
+ mchunkptr newtop = chunk_plus_offset(p, nb);
+ set_inuse(m, p, nb);
+ newtop->head = newtopsize |PINUSE_BIT;
+ m->top = newtop;
+ m->topsize = newtopsize;
+ newp = p;
+ }
+ }
+ else if (next == m->dv) { /* extend into dv */
+ size_t dvs = m->dvsize;
+ if (oldsize + dvs >= nb) {
+ size_t dsize = oldsize + dvs - nb;
+ if (dsize >= MIN_CHUNK_SIZE) {
+ mchunkptr r = chunk_plus_offset(p, nb);
+ mchunkptr n = chunk_plus_offset(r, dsize);
+ set_inuse(m, p, nb);
+ set_size_and_pinuse_of_free_chunk(r, dsize);
+ clear_pinuse(n);
+ m->dvsize = dsize;
+ m->dv = r;
+ }
+ else { /* exhaust dv */
+ size_t newsize = oldsize + dvs;
+ set_inuse(m, p, newsize);
+ m->dvsize = 0;
+ m->dv = 0;
+ }
+ newp = p;
+ }
+ }
+ else if (!cinuse(next)) { /* extend into next free chunk */
+ size_t nextsize = chunksize(next);
+ if (oldsize + nextsize >= nb) {
+ size_t rsize = oldsize + nextsize - nb;
+ unlink_chunk(m, next, nextsize);
+ if (rsize < MIN_CHUNK_SIZE) {
+ size_t newsize = oldsize + nextsize;
+ set_inuse(m, p, newsize);
+ }
+ else {
+ mchunkptr r = chunk_plus_offset(p, nb);
+ set_inuse(m, p, nb);
+ set_inuse(m, r, rsize);
+ dispose_chunk(m, r, rsize);
+ }
+ newp = p;
+ }
+ }
+ }
+ else {
+ USAGE_ERROR_ACTION(m, chunk2mem(p));
+ }
+ return newp;
+}
+
+static void* internal_memalign(mstate m, size_t alignment, size_t bytes) {
+ void* mem = 0;
+ if (alignment < MIN_CHUNK_SIZE) /* must be at least a minimum chunk size */
+ alignment = MIN_CHUNK_SIZE;
+ if ((alignment & (alignment-SIZE_T_ONE)) != 0) {/* Ensure a power of 2 */
+ size_t a = MALLOC_ALIGNMENT << 1;
+ while (a < alignment) a <<= 1;
+ alignment = a;
+ }
+ if (bytes >= MAX_REQUEST - alignment) {
+ if (m != 0) { /* Test isn't needed but avoids compiler warning */
+ MALLOC_FAILURE_ACTION;
+ }
+ }
+ else {
+ size_t nb = request2size(bytes);
+ size_t req = nb + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD;
+ mem = internal_malloc(m, req);
+ if (mem != 0) {
+ mchunkptr p = mem2chunk(mem);
+ if (PREACTION(m))
+ return 0;
+ if ((((size_t)(mem)) & (alignment - 1)) != 0) { /* misaligned */
+ /*
+ Find an aligned spot inside chunk. Since we need to give
+ back leading space in a chunk of at least MIN_CHUNK_SIZE, if
+ the first calculation places us at a spot with less than
+ MIN_CHUNK_SIZE leader, we can move to the next aligned spot.
+ We've allocated enough total room so that this is always
+ possible.
+ */
+ char* br = (char*)mem2chunk((size_t)(((size_t)((char*)mem + alignment -
+ SIZE_T_ONE)) &
+ -alignment));
+ char* pos = ((size_t)(br - (char*)(p)) >= MIN_CHUNK_SIZE)?
+ br : br+alignment;
+ mchunkptr newp = (mchunkptr)pos;
+ size_t leadsize = pos - (char*)(p);
+ size_t newsize = chunksize(p) - leadsize;
+
+ if (is_mmapped(p)) { /* For mmapped chunks, just adjust offset */
+ newp->prev_foot = p->prev_foot + leadsize;
+ newp->head = newsize;
+ }
+ else { /* Otherwise, give back leader, use the rest */
+ set_inuse(m, newp, newsize);
+ set_inuse(m, p, leadsize);
+ dispose_chunk(m, p, leadsize);
+ }
+ p = newp;
+ }
+
+ /* Give back spare room at the end */
+ if (!is_mmapped(p)) {
+ size_t size = chunksize(p);
+ if (size > nb + MIN_CHUNK_SIZE) {
+ size_t remainder_size = size - nb;
+ mchunkptr remainder = chunk_plus_offset(p, nb);
+ set_inuse(m, p, nb);
+ set_inuse(m, remainder, remainder_size);
+ dispose_chunk(m, remainder, remainder_size);
+ }
+ }
+
+ mem = chunk2mem(p);
+ assert (chunksize(p) >= nb);
+ assert(((size_t)mem & (alignment - 1)) == 0);
+ check_inuse_chunk(m, p);
+ POSTACTION(m);
+ }
+ }
+ return mem;
+}
+
+/*
+ Common support for independent_X routines, handling
+ all of the combinations that can result.
+ The opts arg has:
+ bit 0 set if all elements are same size (using sizes[0])
+ bit 1 set if elements should be zeroed
+*/
+static void** ialloc(mstate m,
+ size_t n_elements,
+ size_t* sizes,
+ int opts,
+ void* chunks[]) {
+
+ size_t element_size; /* chunksize of each element, if all same */
+ size_t contents_size; /* total size of elements */
+ size_t array_size; /* request size of pointer array */
+ void* mem; /* malloced aggregate space */
+ mchunkptr p; /* corresponding chunk */
+ size_t remainder_size; /* remaining bytes while splitting */
+ void** marray; /* either "chunks" or malloced ptr array */
+ mchunkptr array_chunk; /* chunk for malloced ptr array */
+ flag_t was_enabled; /* to disable mmap */
+ size_t size;
+ size_t i;
+
+ ensure_initialization();
+ /* compute array length, if needed */
+ if (chunks != 0) {
+ if (n_elements == 0)
+ return chunks; /* nothing to do */
+ marray = chunks;
+ array_size = 0;
+ }
+ else {
+ /* if empty req, must still return chunk representing empty array */
+ if (n_elements == 0)
+ return (void**)internal_malloc(m, 0);
+ marray = 0;
+ array_size = request2size(n_elements * (sizeof(void*)));
+ }
+
+ /* compute total element size */
+ if (opts & 0x1) { /* all-same-size */
+ element_size = request2size(*sizes);
+ contents_size = n_elements * element_size;
+ }
+ else { /* add up all the sizes */
+ element_size = 0;
+ contents_size = 0;
+ for (i = 0; i != n_elements; ++i)
+ contents_size += request2size(sizes[i]);
+ }
+
+ size = contents_size + array_size;
+
+ /*
+ Allocate the aggregate chunk. First disable direct-mmapping so
+ malloc won't use it, since we would not be able to later
+ free/realloc space internal to a segregated mmap region.
+ */
+ was_enabled = use_mmap(m);
+ disable_mmap(m);
+ mem = internal_malloc(m, size - CHUNK_OVERHEAD);
+ if (was_enabled)
+ enable_mmap(m);
+ if (mem == 0)
+ return 0;
+
+ if (PREACTION(m)) return 0;
+ p = mem2chunk(mem);
+ remainder_size = chunksize(p);
+
+ assert(!is_mmapped(p));
+
+ if (opts & 0x2) { /* optionally clear the elements */
+ memset((size_t*)mem, 0, remainder_size - SIZE_T_SIZE - array_size);
+ }
+
+ /* If not provided, allocate the pointer array as final part of chunk */
+ if (marray == 0) {
+ size_t array_chunk_size;
+ array_chunk = chunk_plus_offset(p, contents_size);
+ array_chunk_size = remainder_size - contents_size;
+ marray = (void**) (chunk2mem(array_chunk));
+ set_size_and_pinuse_of_inuse_chunk(m, array_chunk, array_chunk_size);
+ remainder_size = contents_size;
+ }
+
+ /* split out elements */
+ for (i = 0; ; ++i) {
+ marray[i] = chunk2mem(p);
+ if (i != n_elements-1) {
+ if (element_size != 0)
+ size = element_size;
+ else
+ size = request2size(sizes[i]);
+ remainder_size -= size;
+ set_size_and_pinuse_of_inuse_chunk(m, p, size);
+ p = chunk_plus_offset(p, size);
+ }
+ else { /* the final element absorbs any overallocation slop */
+ set_size_and_pinuse_of_inuse_chunk(m, p, remainder_size);
+ break;
+ }
+ }
+
+#if DEBUG
+ if (marray != chunks) {
+ /* final element must have exactly exhausted chunk */
+ if (element_size != 0) {
+ assert(remainder_size == element_size);
+ }
+ else {
+ assert(remainder_size == request2size(sizes[i]));
+ }
+ check_inuse_chunk(m, mem2chunk(marray));
+ }
+ for (i = 0; i != n_elements; ++i)
+ check_inuse_chunk(m, mem2chunk(marray[i]));
+
+#endif /* DEBUG */
+
+ POSTACTION(m);
+ return marray;
+}
+
+/* Try to free all pointers in the given array.
+ Note: this could be made faster, by delaying consolidation,
+ at the price of disabling some user integrity checks, We
+ still optimize some consolidations by combining adjacent
+ chunks before freeing, which will occur often if allocated
+ with ialloc or the array is sorted.
+*/
+static size_t internal_bulk_free(mstate m, void* array[], size_t nelem) {
+ size_t unfreed = 0;
+ if (!PREACTION(m)) {
+ void** a;
+ void** fence = &(array[nelem]);
+ for (a = array; a != fence; ++a) {
+ void* mem = *a;
+ if (mem != 0) {
+ mchunkptr p = mem2chunk(mem);
+ size_t psize = chunksize(p);
+#if FOOTERS
+ if (get_mstate_for(p) != m) {
+ ++unfreed;
+ continue;
+ }
+#endif
+ check_inuse_chunk(m, p);
+ *a = 0;
+ if (RTCHECK(ok_address(m, p) && ok_inuse(p))) {
+ void ** b = a + 1; /* try to merge with next chunk */
+ mchunkptr next = next_chunk(p);
+ if (b != fence && *b == chunk2mem(next)) {
+ size_t newsize = chunksize(next) + psize;
+ set_inuse(m, p, newsize);
+ *b = chunk2mem(p);
+ }
+ else
+ dispose_chunk(m, p, psize);
+ }
+ else {
+ CORRUPTION_ERROR_ACTION(m);
+ break;
+ }
+ }
+ }
+ if (should_trim(m, m->topsize))
+ sys_trim(m, 0);
+ POSTACTION(m);
+ }
+ return unfreed;
+}
+
+/* Traversal */
+#if MALLOC_INSPECT_ALL
+static void internal_inspect_all(mstate m,
+ void(*handler)(void *start,
+ void *end,
+ size_t used_bytes,
+ void* callback_arg),
+ void* arg) {
+ if (is_initialized(m)) {
+ mchunkptr top = m->top;
+ msegmentptr s;
+ for (s = &m->seg; s != 0; s = s->next) {
+ mchunkptr q = align_as_chunk(s->base);
+ while (segment_holds(s, q) && q->head != FENCEPOST_HEAD) {
+ mchunkptr next = next_chunk(q);
+ size_t sz = chunksize(q);
+ size_t used;
+ void* start;
+ if (is_inuse(q)) {
+ used = sz - CHUNK_OVERHEAD; /* must not be mmapped */
+ start = chunk2mem(q);
+ }
+ else {
+ used = 0;
+ if (is_small(sz)) { /* offset by possible bookkeeping */
+ start = (void*)((char*)q + sizeof(struct malloc_chunk));
+ }
+ else {
+ start = (void*)((char*)q + sizeof(struct malloc_tree_chunk));
+ }
+ }
+ if (start < (void*)next) /* skip if all space is bookkeeping */
+ handler(start, next, used, arg);
+ if (q == top)
+ break;
+ q = next;
+ }
+ }
+ }
+}
+#endif /* MALLOC_INSPECT_ALL */
+
+/* ------------------ Exported realloc, memalign, etc -------------------- */
+
+#if !ONLY_MSPACES
+
+void* dlrealloc(void* oldmem, size_t bytes) {
+ void* mem = 0;
+ if (oldmem == 0) {
+ mem = dlmalloc(bytes);
+ }
+ else if (bytes >= MAX_REQUEST) {
+ MALLOC_FAILURE_ACTION;
+ }
+#ifdef REALLOC_ZERO_BYTES_FREES
+ else if (bytes == 0) {
+ dlfree(oldmem);
+ }
+#endif /* REALLOC_ZERO_BYTES_FREES */
+ else {
+ size_t nb = request2size(bytes);
+ mchunkptr oldp = mem2chunk(oldmem);
+#if ! FOOTERS
+ mstate m = gm;
+#else /* FOOTERS */
+ mstate m = get_mstate_for(oldp);
+ if (!ok_magic(m)) {
+ USAGE_ERROR_ACTION(m, oldmem);
+ return 0;
+ }
+#endif /* FOOTERS */
+ if (!PREACTION(m)) {
+ mchunkptr newp = try_realloc_chunk(m, oldp, nb, 1);
+ POSTACTION(m);
+ if (newp != 0) {
+ check_inuse_chunk(m, newp);
+ mem = chunk2mem(newp);
+ }
+ else {
+ mem = internal_malloc(m, bytes);
+ if (mem != 0) {
+ size_t oc = chunksize(oldp) - overhead_for(oldp);
+ memcpy(mem, oldmem, (oc < bytes)? oc : bytes);
+ internal_free(m, oldmem);
+ }
+ }
+ }
+ }
+ return mem;
+}
+
+void* dlrealloc_in_place(void* oldmem, size_t bytes) {
+ void* mem = 0;
+ if (oldmem != 0) {
+ if (bytes >= MAX_REQUEST) {
+ MALLOC_FAILURE_ACTION;
+ }
+ else {
+ size_t nb = request2size(bytes);
+ mchunkptr oldp = mem2chunk(oldmem);
+#if ! FOOTERS
+ mstate m = gm;
+#else /* FOOTERS */
+ mstate m = get_mstate_for(oldp);
+ if (!ok_magic(m)) {
+ USAGE_ERROR_ACTION(m, oldmem);
+ return 0;
+ }
+#endif /* FOOTERS */
+ if (!PREACTION(m)) {
+ mchunkptr newp = try_realloc_chunk(m, oldp, nb, 0);
+ POSTACTION(m);
+ if (newp == oldp) {
+ check_inuse_chunk(m, newp);
+ mem = oldmem;
+ }
+ }
+ }
+ }
+ return mem;
+}
+
+void* dlmemalign(size_t alignment, size_t bytes) {
+ if (alignment <= MALLOC_ALIGNMENT) {
+ return dlmalloc(bytes);
+ }
+ return internal_memalign(gm, alignment, bytes);
+}
+
+int dlposix_memalign(void** pp, size_t alignment, size_t bytes) {
+ void* mem = 0;
+ if (alignment == MALLOC_ALIGNMENT)
+ mem = dlmalloc(bytes);
+ else {
+ size_t d = alignment / sizeof(void*);
+ size_t r = alignment % sizeof(void*);
+ if (r != 0 || d == 0 || (d & (d-SIZE_T_ONE)) != 0)
+ return EINVAL;
+ else if (bytes <= MAX_REQUEST - alignment) {
+ if (alignment < MIN_CHUNK_SIZE)
+ alignment = MIN_CHUNK_SIZE;
+ mem = internal_memalign(gm, alignment, bytes);
+ }
+ }
+ if (mem == 0)
+ return ENOMEM;
+ else {
+ *pp = mem;
+ return 0;
+ }
+}
+
+void* dlvalloc(size_t bytes) {
+ size_t pagesz;
+ ensure_initialization();
+ pagesz = mparams.page_size;
+ return dlmemalign(pagesz, bytes);
+}
+
+void* dlpvalloc(size_t bytes) {
+ size_t pagesz;
+ ensure_initialization();
+ pagesz = mparams.page_size;
+ return dlmemalign(pagesz, (bytes + pagesz - SIZE_T_ONE) & ~(pagesz - SIZE_T_ONE));
+}
+
+void** dlindependent_calloc(size_t n_elements, size_t elem_size,
+ void* chunks[]) {
+ size_t sz = elem_size; /* serves as 1-element array */
+ return ialloc(gm, n_elements, &sz, 3, chunks);
+}
+
+void** dlindependent_comalloc(size_t n_elements, size_t sizes[],
+ void* chunks[]) {
+ return ialloc(gm, n_elements, sizes, 0, chunks);
+}
+
+size_t dlbulk_free(void* array[], size_t nelem) {
+ return internal_bulk_free(gm, array, nelem);
+}
+
+#if MALLOC_INSPECT_ALL
+void dlmalloc_inspect_all(void(*handler)(void *start,
+ void *end,
+ size_t used_bytes,
+ void* callback_arg),
+ void* arg) {
+ ensure_initialization();
+ if (!PREACTION(gm)) {
+ internal_inspect_all(gm, handler, arg);
+ POSTACTION(gm);
+ }
+}
+#endif /* MALLOC_INSPECT_ALL */
+
+int dlmalloc_trim(size_t pad) {
+ int result = 0;
+ ensure_initialization();
+ if (!PREACTION(gm)) {
+ result = sys_trim(gm, pad);
+ POSTACTION(gm);
+ }
+ return result;
+}
+
+size_t dlmalloc_footprint(void) {
+ return gm->footprint;
+}
+
+size_t dlmalloc_max_footprint(void) {
+ return gm->max_footprint;
+}
+
+size_t dlmalloc_footprint_limit(void) {
+ size_t maf = gm->footprint_limit;
+ return maf == 0 ? MAX_SIZE_T : maf;
+}
+
+size_t dlmalloc_set_footprint_limit(size_t bytes) {
+ size_t result; /* invert sense of 0 */
+ if (bytes == 0)
+ result = granularity_align(1); /* Use minimal size */
+ if (bytes == MAX_SIZE_T)
+ result = 0; /* disable */
+ else
+ result = granularity_align(bytes);
+ return gm->footprint_limit = result;
+}
+
+#if !NO_MALLINFO
+struct mallinfo dlmallinfo(void) {
+ return internal_mallinfo(gm);
+}
+#endif /* NO_MALLINFO */
+
+#if !NO_MALLOC_STATS
+void dlmalloc_stats() {
+ internal_malloc_stats(gm);
+}
+#endif /* NO_MALLOC_STATS */
+
+int dlmallopt(int param_number, int value) {
+ return change_mparam(param_number, value);
+}
+
+size_t dlmalloc_usable_size(void* mem) {
+ if (mem != 0) {
+ mchunkptr p = mem2chunk(mem);
+ if (is_inuse(p))
+ return chunksize(p) - overhead_for(p);
+ }
+ return 0;
+}
+
+#endif /* !ONLY_MSPACES */
+
+/* ----------------------------- user mspaces ---------------------------- */
+
+#if MSPACES
+
+static mstate init_user_mstate(char* tbase, size_t tsize) {
+ size_t msize = pad_request(sizeof(struct malloc_state));
+ mchunkptr mn;
+ mchunkptr msp = align_as_chunk(tbase);
+ mstate m = (mstate)(chunk2mem(msp));
+ memset(m, 0, msize);
+ (void)INITIAL_LOCK(&m->mutex);
+ msp->head = (msize|INUSE_BITS);
+ m->seg.base = m->least_addr = tbase;
+ m->seg.size = m->footprint = m->max_footprint = tsize;
+ m->magic = mparams.magic;
+ m->release_checks = MAX_RELEASE_CHECK_RATE;
+ m->mflags = mparams.default_mflags;
+ m->extp = 0;
+ m->exts = 0;
+ disable_contiguous(m);
+ init_bins(m);
+ mn = next_chunk(mem2chunk(m));
+ init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) - TOP_FOOT_SIZE);
+ check_top_chunk(m, m->top);
+ return m;
+}
+
+mspace create_mspace(size_t capacity, int locked) {
+ mstate m = 0;
+ size_t msize;
+ ensure_initialization();
+ msize = pad_request(sizeof(struct malloc_state));
+ if (capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) {
+ size_t rs = ((capacity == 0)? mparams.granularity :
+ (capacity + TOP_FOOT_SIZE + msize));
+ size_t tsize = granularity_align(rs);
+ char* tbase = (char*)(CALL_MMAP(tsize));
+ if (tbase != CMFAIL) {
+ m = init_user_mstate(tbase, tsize);
+ m->seg.sflags = USE_MMAP_BIT;
+ set_lock(m, locked);
+ }
+ }
+ return (mspace)m;
+}
+
+mspace create_mspace_with_base(void* base, size_t capacity, int locked) {
+ mstate m = 0;
+ size_t msize;
+ ensure_initialization();
+ msize = pad_request(sizeof(struct malloc_state));
+ if (capacity > msize + TOP_FOOT_SIZE &&
+ capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) {
+ m = init_user_mstate((char*)base, capacity);
+ m->seg.sflags = EXTERN_BIT;
+ set_lock(m, locked);
+ }
+ return (mspace)m;
+}
+
+int mspace_track_large_chunks(mspace msp, int enable) {
+ int ret = 0;
+ mstate ms = (mstate)msp;
+ if (!PREACTION(ms)) {
+ if (!use_mmap(ms)) {
+ ret = 1;
+ }
+ if (!enable) {
+ enable_mmap(ms);
+ } else {
+ disable_mmap(ms);
+ }
+ POSTACTION(ms);
+ }
+ return ret;
+}
+
+size_t destroy_mspace(mspace msp) {
+ size_t freed = 0;
+ mstate ms = (mstate)msp;
+ if (ok_magic(ms)) {
+ msegmentptr sp = &ms->seg;
+ (void)DESTROY_LOCK(&ms->mutex); /* destroy before unmapped */
+ while (sp != 0) {
+ char* base = sp->base;
+ size_t size = sp->size;
+ flag_t flag = sp->sflags;
+ (void)base; /* placate people compiling -Wunused-variable */
+ sp = sp->next;
+ if ((flag & USE_MMAP_BIT) && !(flag & EXTERN_BIT) &&
+ CALL_MUNMAP(base, size) == 0)
+ freed += size;
+ }
+ }
+ else {
+ USAGE_ERROR_ACTION(ms,ms);
+ }
+ return freed;
+}
+
+/*
+ mspace versions of routines are near-clones of the global
+ versions. This is not so nice but better than the alternatives.
+*/
+
+void* mspace_malloc(mspace msp, size_t bytes) {
+ mstate ms = (mstate)msp;
+ if (!ok_magic(ms)) {
+ USAGE_ERROR_ACTION(ms,ms);
+ return 0;
+ }
+ if (!PREACTION(ms)) {
+ void* mem;
+ size_t nb;
+ if (bytes <= MAX_SMALL_REQUEST) {
+ bindex_t idx;
+ binmap_t smallbits;
+ nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes);
+ idx = small_index(nb);
+ smallbits = ms->smallmap >> idx;
+
+ if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */
+ mchunkptr b, p;
+ idx += ~smallbits & 1; /* Uses next bin if idx empty */
+ b = smallbin_at(ms, idx);
+ p = b->fd;
+ assert(chunksize(p) == small_index2size(idx));
+ unlink_first_small_chunk(ms, b, p, idx);
+ set_inuse_and_pinuse(ms, p, small_index2size(idx));
+ mem = chunk2mem(p);
+ check_malloced_chunk(ms, mem, nb);
+ goto postaction;
+ }
+
+ else if (nb > ms->dvsize) {
+ if (smallbits != 0) { /* Use chunk in next nonempty smallbin */
+ mchunkptr b, p, r;
+ size_t rsize;
+ bindex_t i;
+ binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx));
+ binmap_t leastbit = least_bit(leftbits);
+ compute_bit2idx(leastbit, i);
+ b = smallbin_at(ms, i);
+ p = b->fd;
+ assert(chunksize(p) == small_index2size(i));
+ unlink_first_small_chunk(ms, b, p, i);
+ rsize = small_index2size(i) - nb;
+ /* Fit here cannot be remainderless if 4byte sizes */
+ if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE)
+ set_inuse_and_pinuse(ms, p, small_index2size(i));
+ else {
+ set_size_and_pinuse_of_inuse_chunk(ms, p, nb);
+ r = chunk_plus_offset(p, nb);
+ set_size_and_pinuse_of_free_chunk(r, rsize);
+ replace_dv(ms, r, rsize);
+ }
+ mem = chunk2mem(p);
+ check_malloced_chunk(ms, mem, nb);
+ goto postaction;
+ }
+
+ else if (ms->treemap != 0 && (mem = tmalloc_small(ms, nb)) != 0) {
+ check_malloced_chunk(ms, mem, nb);
+ goto postaction;
+ }
+ }
+ }
+ else if (bytes >= MAX_REQUEST)
+ nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */
+ else {
+ nb = pad_request(bytes);
+ if (ms->treemap != 0 && (mem = tmalloc_large(ms, nb)) != 0) {
+ check_malloced_chunk(ms, mem, nb);
+ goto postaction;
+ }
+ }
+
+ if (nb <= ms->dvsize) {
+ size_t rsize = ms->dvsize - nb;
+ mchunkptr p = ms->dv;
+ if (rsize >= MIN_CHUNK_SIZE) { /* split dv */
+ mchunkptr r = ms->dv = chunk_plus_offset(p, nb);
+ ms->dvsize = rsize;
+ set_size_and_pinuse_of_free_chunk(r, rsize);
+ set_size_and_pinuse_of_inuse_chunk(ms, p, nb);
+ }
+ else { /* exhaust dv */
+ size_t dvs = ms->dvsize;
+ ms->dvsize = 0;
+ ms->dv = 0;
+ set_inuse_and_pinuse(ms, p, dvs);
+ }
+ mem = chunk2mem(p);
+ check_malloced_chunk(ms, mem, nb);
+ goto postaction;
+ }
+
+ else if (nb < ms->topsize) { /* Split top */
+ size_t rsize = ms->topsize -= nb;
+ mchunkptr p = ms->top;
+ mchunkptr r = ms->top = chunk_plus_offset(p, nb);
+ r->head = rsize | PINUSE_BIT;
+ set_size_and_pinuse_of_inuse_chunk(ms, p, nb);
+ mem = chunk2mem(p);
+ check_top_chunk(ms, ms->top);
+ check_malloced_chunk(ms, mem, nb);
+ goto postaction;
+ }
+
+ mem = sys_alloc(ms, nb);
+
+ postaction:
+ POSTACTION(ms);
+ return mem;
+ }
+
+ return 0;
+}
+
+void mspace_free(mspace msp, void* mem) {
+ if (mem != 0) {
+ mchunkptr p = mem2chunk(mem);
+#if FOOTERS
+ mstate fm = get_mstate_for(p);
+ (void)msp; /* placate people compiling -Wunused */
+#else /* FOOTERS */
+ mstate fm = (mstate)msp;
+#endif /* FOOTERS */
+ if (!ok_magic(fm)) {
+ USAGE_ERROR_ACTION(fm, p);
+ return;
+ }
+ if (!PREACTION(fm)) {
+ check_inuse_chunk(fm, p);
+ if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) {
+ size_t psize = chunksize(p);
+ mchunkptr next = chunk_plus_offset(p, psize);
+ if (!pinuse(p)) {
+ size_t prevsize = p->prev_foot;
+ if (is_mmapped(p)) {
+ psize += prevsize + MMAP_FOOT_PAD;
+ if (CALL_MUNMAP((char*)p - prevsize, psize) == 0)
+ fm->footprint -= psize;
+ goto postaction;
+ }
+ else {
+ mchunkptr prev = chunk_minus_offset(p, prevsize);
+ psize += prevsize;
+ p = prev;
+ if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */
+ if (p != fm->dv) {
+ unlink_chunk(fm, p, prevsize);
+ }
+ else if ((next->head & INUSE_BITS) == INUSE_BITS) {
+ fm->dvsize = psize;
+ set_free_with_pinuse(p, psize, next);
+ goto postaction;
+ }
+ }
+ else
+ goto erroraction;
+ }
+ }
+
+ if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) {
+ if (!cinuse(next)) { /* consolidate forward */
+ if (next == fm->top) {
+ size_t tsize = fm->topsize += psize;
+ fm->top = p;
+ p->head = tsize | PINUSE_BIT;
+ if (p == fm->dv) {
+ fm->dv = 0;
+ fm->dvsize = 0;
+ }
+ if (should_trim(fm, tsize))
+ sys_trim(fm, 0);
+ goto postaction;
+ }
+ else if (next == fm->dv) {
+ size_t dsize = fm->dvsize += psize;
+ fm->dv = p;
+ set_size_and_pinuse_of_free_chunk(p, dsize);
+ goto postaction;
+ }
+ else {
+ size_t nsize = chunksize(next);
+ psize += nsize;
+ unlink_chunk(fm, next, nsize);
+ set_size_and_pinuse_of_free_chunk(p, psize);
+ if (p == fm->dv) {
+ fm->dvsize = psize;
+ goto postaction;
+ }
+ }
+ }
+ else
+ set_free_with_pinuse(p, psize, next);
+
+ if (is_small(psize)) {
+ insert_small_chunk(fm, p, psize);
+ check_free_chunk(fm, p);
+ }
+ else {
+ tchunkptr tp = (tchunkptr)p;
+ insert_large_chunk(fm, tp, psize);
+ check_free_chunk(fm, p);
+ if (--fm->release_checks == 0)
+ release_unused_segments(fm);
+ }
+ goto postaction;
+ }
+ }
+ erroraction:
+ USAGE_ERROR_ACTION(fm, p);
+ postaction:
+ POSTACTION(fm);
+ }
+ }
+}
+
+void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size) {
+ void* mem;
+ size_t req = 0;
+ mstate ms = (mstate)msp;
+ if (!ok_magic(ms)) {
+ USAGE_ERROR_ACTION(ms,ms);
+ return 0;
+ }
+ if (n_elements != 0) {
+ req = n_elements * elem_size;
+ if (((n_elements | elem_size) & ~(size_t)0xffff) &&
+ (req / n_elements != elem_size))
+ req = MAX_SIZE_T; /* force downstream failure on overflow */
+ }
+ mem = internal_malloc(ms, req);
+ if (mem != 0 && calloc_must_clear(mem2chunk(mem)))
+ memset(mem, 0, req);
+ return mem;
+}
+
+void* mspace_realloc(mspace msp, void* oldmem, size_t bytes) {
+ void* mem = 0;
+ if (oldmem == 0) {
+ mem = mspace_malloc(msp, bytes);
+ }
+ else if (bytes >= MAX_REQUEST) {
+ MALLOC_FAILURE_ACTION;
+ }
+#ifdef REALLOC_ZERO_BYTES_FREES
+ else if (bytes == 0) {
+ mspace_free(msp, oldmem);
+ }
+#endif /* REALLOC_ZERO_BYTES_FREES */
+ else {
+ size_t nb = request2size(bytes);
+ mchunkptr oldp = mem2chunk(oldmem);
+#if ! FOOTERS
+ mstate m = (mstate)msp;
+#else /* FOOTERS */
+ mstate m = get_mstate_for(oldp);
+ if (!ok_magic(m)) {
+ USAGE_ERROR_ACTION(m, oldmem);
+ return 0;
+ }
+#endif /* FOOTERS */
+ if (!PREACTION(m)) {
+ mchunkptr newp = try_realloc_chunk(m, oldp, nb, 1);
+ POSTACTION(m);
+ if (newp != 0) {
+ check_inuse_chunk(m, newp);
+ mem = chunk2mem(newp);
+ }
+ else {
+ mem = mspace_malloc(m, bytes);
+ if (mem != 0) {
+ size_t oc = chunksize(oldp) - overhead_for(oldp);
+ memcpy(mem, oldmem, (oc < bytes)? oc : bytes);
+ mspace_free(m, oldmem);
+ }
+ }
+ }
+ }
+ return mem;
+}
+
+void* mspace_realloc_in_place(mspace msp, void* oldmem, size_t bytes) {
+ void* mem = 0;
+ if (oldmem != 0) {
+ if (bytes >= MAX_REQUEST) {
+ MALLOC_FAILURE_ACTION;
+ }
+ else {
+ size_t nb = request2size(bytes);
+ mchunkptr oldp = mem2chunk(oldmem);
+#if ! FOOTERS
+ mstate m = (mstate)msp;
+#else /* FOOTERS */
+ mstate m = get_mstate_for(oldp);
+ (void)msp; /* placate people compiling -Wunused */
+ if (!ok_magic(m)) {
+ USAGE_ERROR_ACTION(m, oldmem);
+ return 0;
+ }
+#endif /* FOOTERS */
+ if (!PREACTION(m)) {
+ mchunkptr newp = try_realloc_chunk(m, oldp, nb, 0);
+ POSTACTION(m);
+ if (newp == oldp) {
+ check_inuse_chunk(m, newp);
+ mem = oldmem;
+ }
+ }
+ }
+ }
+ return mem;
+}
+
+void* mspace_memalign(mspace msp, size_t alignment, size_t bytes) {
+ mstate ms = (mstate)msp;
+ if (!ok_magic(ms)) {
+ USAGE_ERROR_ACTION(ms,ms);
+ return 0;
+ }
+ if (alignment <= MALLOC_ALIGNMENT)
+ return mspace_malloc(msp, bytes);
+ return internal_memalign(ms, alignment, bytes);
+}
+
+void** mspace_independent_calloc(mspace msp, size_t n_elements,
+ size_t elem_size, void* chunks[]) {
+ size_t sz = elem_size; /* serves as 1-element array */
+ mstate ms = (mstate)msp;
+ if (!ok_magic(ms)) {
+ USAGE_ERROR_ACTION(ms,ms);
+ return 0;
+ }
+ return ialloc(ms, n_elements, &sz, 3, chunks);
+}
+
+void** mspace_independent_comalloc(mspace msp, size_t n_elements,
+ size_t sizes[], void* chunks[]) {
+ mstate ms = (mstate)msp;
+ if (!ok_magic(ms)) {
+ USAGE_ERROR_ACTION(ms,ms);
+ return 0;
+ }
+ return ialloc(ms, n_elements, sizes, 0, chunks);
+}
+
+size_t mspace_bulk_free(mspace msp, void* array[], size_t nelem) {
+ return internal_bulk_free((mstate)msp, array, nelem);
+}
+
+#if MALLOC_INSPECT_ALL
+void mspace_inspect_all(mspace msp,
+ void(*handler)(void *start,
+ void *end,
+ size_t used_bytes,
+ void* callback_arg),
+ void* arg) {
+ mstate ms = (mstate)msp;
+ if (ok_magic(ms)) {
+ if (!PREACTION(ms)) {
+ internal_inspect_all(ms, handler, arg);
+ POSTACTION(ms);
+ }
+ }
+ else {
+ USAGE_ERROR_ACTION(ms,ms);
+ }
+}
+#endif /* MALLOC_INSPECT_ALL */
+
+int mspace_trim(mspace msp, size_t pad) {
+ int result = 0;
+ mstate ms = (mstate)msp;
+ if (ok_magic(ms)) {
+ if (!PREACTION(ms)) {
+ result = sys_trim(ms, pad);
+ POSTACTION(ms);
+ }
+ }
+ else {
+ USAGE_ERROR_ACTION(ms,ms);
+ }
+ return result;
+}
+
+#if !NO_MALLOC_STATS
+void mspace_malloc_stats(mspace msp) {
+ mstate ms = (mstate)msp;
+ if (ok_magic(ms)) {
+ internal_malloc_stats(ms);
+ }
+ else {
+ USAGE_ERROR_ACTION(ms,ms);
+ }
+}
+#endif /* NO_MALLOC_STATS */
+
+size_t mspace_footprint(mspace msp) {
+ size_t result = 0;
+ mstate ms = (mstate)msp;
+ if (ok_magic(ms)) {
+ result = ms->footprint;
+ }
+ else {
+ USAGE_ERROR_ACTION(ms,ms);
+ }
+ return result;
+}
+
+size_t mspace_max_footprint(mspace msp) {
+ size_t result = 0;
+ mstate ms = (mstate)msp;
+ if (ok_magic(ms)) {
+ result = ms->max_footprint;
+ }
+ else {
+ USAGE_ERROR_ACTION(ms,ms);
+ }
+ return result;
+}
+
+size_t mspace_footprint_limit(mspace msp) {
+ size_t result = 0;
+ mstate ms = (mstate)msp;
+ if (ok_magic(ms)) {
+ size_t maf = ms->footprint_limit;
+ result = (maf == 0) ? MAX_SIZE_T : maf;
+ }
+ else {
+ USAGE_ERROR_ACTION(ms,ms);
+ }
+ return result;
+}
+
+size_t mspace_set_footprint_limit(mspace msp, size_t bytes) {
+ size_t result = 0;
+ mstate ms = (mstate)msp;
+ if (ok_magic(ms)) {
+ if (bytes == 0)
+ result = granularity_align(1); /* Use minimal size */
+ if (bytes == MAX_SIZE_T)
+ result = 0; /* disable */
+ else
+ result = granularity_align(bytes);
+ ms->footprint_limit = result;
+ }
+ else {
+ USAGE_ERROR_ACTION(ms,ms);
+ }
+ return result;
+}
+
+#if !NO_MALLINFO
+struct mallinfo mspace_mallinfo(mspace msp) {
+ mstate ms = (mstate)msp;
+ if (!ok_magic(ms)) {
+ USAGE_ERROR_ACTION(ms,ms);
+ }
+ return internal_mallinfo(ms);
+}
+#endif /* NO_MALLINFO */
+
+size_t mspace_usable_size(const void* mem) {
+ if (mem != 0) {
+ mchunkptr p = mem2chunk(mem);
+ if (is_inuse(p))
+ return chunksize(p) - overhead_for(p);
+ }
+ return 0;
+}
+
+int mspace_mallopt(int param_number, int value) {
+ return change_mparam(param_number, value);
+}
+
+#endif /* MSPACES */
+
+
+/* -------------------- Alternative MORECORE functions ------------------- */
+
+/*
+ Guidelines for creating a custom version of MORECORE:
+
+ * For best performance, MORECORE should allocate in multiples of pagesize.
+ * MORECORE may allocate more memory than requested. (Or even less,
+ but this will usually result in a malloc failure.)
+ * MORECORE must not allocate memory when given argument zero, but
+ instead return one past the end address of memory from previous
+ nonzero call.
+ * For best performance, consecutive calls to MORECORE with positive
+ arguments should return increasing addresses, indicating that
+ space has been contiguously extended.
+ * Even though consecutive calls to MORECORE need not return contiguous
+ addresses, it must be OK for malloc'ed chunks to span multiple
+ regions in those cases where they do happen to be contiguous.
+ * MORECORE need not handle negative arguments -- it may instead
+ just return MFAIL when given negative arguments.
+ Negative arguments are always multiples of pagesize. MORECORE
+ must not misinterpret negative args as large positive unsigned
+ args. You can suppress all such calls from even occurring by defining
+ MORECORE_CANNOT_TRIM,
+
+ As an example alternative MORECORE, here is a custom allocator
+ kindly contributed for pre-OSX macOS. It uses virtually but not
+ necessarily physically contiguous non-paged memory (locked in,
+ present and won't get swapped out). You can use it by uncommenting
+ this section, adding some #includes, and setting up the appropriate
+ defines above:
+
+ #define MORECORE osMoreCore
+
+ There is also a shutdown routine that should somehow be called for
+ cleanup upon program exit.
+
+ #define MAX_POOL_ENTRIES 100
+ #define MINIMUM_MORECORE_SIZE (64 * 1024U)
+ static int next_os_pool;
+ void *our_os_pools[MAX_POOL_ENTRIES];
+
+ void *osMoreCore(int size)
+ {
+ void *ptr = 0;
+ static void *sbrk_top = 0;
+
+ if (size > 0)
+ {
+ if (size < MINIMUM_MORECORE_SIZE)
+ size = MINIMUM_MORECORE_SIZE;
+ if (CurrentExecutionLevel() == kTaskLevel)
+ ptr = PoolAllocateResident(size + RM_PAGE_SIZE, 0);
+ if (ptr == 0)
+ {
+ return (void *) MFAIL;
+ }
+ // save ptrs so they can be freed during cleanup
+ our_os_pools[next_os_pool] = ptr;
+ next_os_pool++;
+ ptr = (void *) ((((size_t) ptr) + RM_PAGE_MASK) & ~RM_PAGE_MASK);
+ sbrk_top = (char *) ptr + size;
+ return ptr;
+ }
+ else if (size < 0)
+ {
+ // we don't currently support shrink behavior
+ return (void *) MFAIL;
+ }
+ else
+ {
+ return sbrk_top;
+ }
+ }
+
+ // cleanup any allocated memory pools
+ // called as last thing before shutting down driver
+
+ void osCleanupMem(void)
+ {
+ void **ptr;
+
+ for (ptr = our_os_pools; ptr < &our_os_pools[MAX_POOL_ENTRIES]; ptr++)
+ if (*ptr)
+ {
+ PoolDeallocate(*ptr);
+ *ptr = 0;
+ }
+ }
+
+*/
+
+
+/* -----------------------------------------------------------------------
+History:
+ v2.8.6 Wed Aug 29 06:57:58 2012 Doug Lea
+ * fix bad comparison in dlposix_memalign
+ * don't reuse adjusted asize in sys_alloc
+ * add LOCK_AT_FORK -- thanks to Kirill Artamonov for the suggestion
+ * reduce compiler warnings -- thanks to all who reported/suggested these
+
+ v2.8.5 Sun May 22 10:26:02 2011 Doug Lea (dl at gee)
+ * Always perform unlink checks unless INSECURE
+ * Add posix_memalign.
+ * Improve realloc to expand in more cases; expose realloc_in_place.
+ Thanks to Peter Buhr for the suggestion.
+ * Add footprint_limit, inspect_all, bulk_free. Thanks
+ to Barry Hayes and others for the suggestions.
+ * Internal refactorings to avoid calls while holding locks
+ * Use non-reentrant locks by default. Thanks to Roland McGrath
+ for the suggestion.
+ * Small fixes to mspace_destroy, reset_on_error.
+ * Various configuration extensions/changes. Thanks
+ to all who contributed these.
+
+ V2.8.4a Thu Apr 28 14:39:43 2011 (dl at gee.cs.oswego.edu)
+ * Update Creative Commons URL
+
+ V2.8.4 Wed May 27 09:56:23 2009 Doug Lea (dl at gee)
+ * Use zeros instead of prev foot for is_mmapped
+ * Add mspace_track_large_chunks; thanks to Jean Brouwers
+ * Fix set_inuse in internal_realloc; thanks to Jean Brouwers
+ * Fix insufficient sys_alloc padding when using 16byte alignment
+ * Fix bad error check in mspace_footprint
+ * Adaptations for ptmalloc; thanks to Wolfram Gloger.
+ * Reentrant spin locks; thanks to Earl Chew and others
+ * Win32 improvements; thanks to Niall Douglas and Earl Chew
+ * Add NO_SEGMENT_TRAVERSAL and MAX_RELEASE_CHECK_RATE options
+ * Extension hook in malloc_state
+ * Various small adjustments to reduce warnings on some compilers
+ * Various configuration extensions/changes for more platforms. Thanks
+ to all who contributed these.
+
+ V2.8.3 Thu Sep 22 11:16:32 2005 Doug Lea (dl at gee)
+ * Add max_footprint functions
+ * Ensure all appropriate literals are size_t
+ * Fix conditional compilation problem for some #define settings
+ * Avoid concatenating segments with the one provided
+ in create_mspace_with_base
+ * Rename some variables to avoid compiler shadowing warnings
+ * Use explicit lock initialization.
+ * Better handling of sbrk interference.
+ * Simplify and fix segment insertion, trimming and mspace_destroy
+ * Reinstate REALLOC_ZERO_BYTES_FREES option from 2.7.x
+ * Thanks especially to Dennis Flanagan for help on these.
+
+ V2.8.2 Sun Jun 12 16:01:10 2005 Doug Lea (dl at gee)
+ * Fix memalign brace error.
+
+ V2.8.1 Wed Jun 8 16:11:46 2005 Doug Lea (dl at gee)
+ * Fix improper #endif nesting in C++
+ * Add explicit casts needed for C++
+
+ V2.8.0 Mon May 30 14:09:02 2005 Doug Lea (dl at gee)
+ * Use trees for large bins
+ * Support mspaces
+ * Use segments to unify sbrk-based and mmap-based system allocation,
+ removing need for emulation on most platforms without sbrk.
+ * Default safety checks
+ * Optional footer checks. Thanks to William Robertson for the idea.
+ * Internal code refactoring
+ * Incorporate suggestions and platform-specific changes.
+ Thanks to Dennis Flanagan, Colin Plumb, Niall Douglas,
+ Aaron Bachmann, Emery Berger, and others.
+ * Speed up non-fastbin processing enough to remove fastbins.
+ * Remove useless cfree() to avoid conflicts with other apps.
+ * Remove internal memcpy, memset. Compilers handle builtins better.
+ * Remove some options that no one ever used and rename others.
+
+ V2.7.2 Sat Aug 17 09:07:30 2002 Doug Lea (dl at gee)
+ * Fix malloc_state bitmap array misdeclaration
+
+ V2.7.1 Thu Jul 25 10:58:03 2002 Doug Lea (dl at gee)
+ * Allow tuning of FIRST_SORTED_BIN_SIZE
+ * Use PTR_UINT as type for all ptr->int casts. Thanks to John Belmonte.
+ * Better detection and support for non-contiguousness of MORECORE.
+ Thanks to Andreas Mueller, Conal Walsh, and Wolfram Gloger
+ * Bypass most of malloc if no frees. Thanks To Emery Berger.
+ * Fix freeing of old top non-contiguous chunk im sysmalloc.
+ * Raised default trim and map thresholds to 256K.
+ * Fix mmap-related #defines. Thanks to Lubos Lunak.
+ * Fix copy macros; added LACKS_FCNTL_H. Thanks to Neal Walfield.
+ * Branch-free bin calculation
+ * Default trim and mmap thresholds now 256K.
+
+ V2.7.0 Sun Mar 11 14:14:06 2001 Doug Lea (dl at gee)
+ * Introduce independent_comalloc and independent_calloc.
+ Thanks to Michael Pachos for motivation and help.
+ * Make optional .h file available
+ * Allow > 2GB requests on 32bit systems.
+ * new WIN32 sbrk, mmap, munmap, lock code from <Walter@GeNeSys-e.de>.
+ Thanks also to Andreas Mueller <a.mueller at paradatec.de>,
+ and Anonymous.
+ * Allow override of MALLOC_ALIGNMENT (Thanks to Ruud Waij for
+ helping test this.)
+ * memalign: check alignment arg
+ * realloc: don't try to shift chunks backwards, since this
+ leads to more fragmentation in some programs and doesn't
+ seem to help in any others.
+ * Collect all cases in malloc requiring system memory into sysmalloc
+ * Use mmap as backup to sbrk
+ * Place all internal state in malloc_state
+ * Introduce fastbins (although similar to 2.5.1)
+ * Many minor tunings and cosmetic improvements
+ * Introduce USE_PUBLIC_MALLOC_WRAPPERS, USE_MALLOC_LOCK
+ * Introduce MALLOC_FAILURE_ACTION, MORECORE_CONTIGUOUS
+ Thanks to Tony E. Bennett <tbennett@nvidia.com> and others.
+ * Include errno.h to support default failure action.
+
+ V2.6.6 Sun Dec 5 07:42:19 1999 Doug Lea (dl at gee)
+ * return null for negative arguments
+ * Added Several WIN32 cleanups from Martin C. Fong <mcfong at yahoo.com>
+ * Add 'LACKS_SYS_PARAM_H' for those systems without 'sys/param.h'
+ (e.g. WIN32 platforms)
+ * Cleanup header file inclusion for WIN32 platforms
+ * Cleanup code to avoid Microsoft Visual C++ compiler complaints
+ * Add 'USE_DL_PREFIX' to quickly allow co-existence with existing
+ memory allocation routines
+ * Set 'malloc_getpagesize' for WIN32 platforms (needs more work)
+ * Use 'assert' rather than 'ASSERT' in WIN32 code to conform to
+ usage of 'assert' in non-WIN32 code
+ * Improve WIN32 'sbrk()' emulation's 'findRegion()' routine to
+ avoid infinite loop
+ * Always call 'fREe()' rather than 'free()'
+
+ V2.6.5 Wed Jun 17 15:57:31 1998 Doug Lea (dl at gee)
+ * Fixed ordering problem with boundary-stamping
+
+ V2.6.3 Sun May 19 08:17:58 1996 Doug Lea (dl at gee)
+ * Added pvalloc, as recommended by H.J. Liu
+ * Added 64bit pointer support mainly from Wolfram Gloger
+ * Added anonymously donated WIN32 sbrk emulation
+ * Malloc, calloc, getpagesize: add optimizations from Raymond Nijssen
+ * malloc_extend_top: fix mask error that caused wastage after
+ foreign sbrks
+ * Add linux mremap support code from HJ Liu
+
+ V2.6.2 Tue Dec 5 06:52:55 1995 Doug Lea (dl at gee)
+ * Integrated most documentation with the code.
+ * Add support for mmap, with help from
+ Wolfram Gloger (Gloger@lrz.uni-muenchen.de).
+ * Use last_remainder in more cases.
+ * Pack bins using idea from colin@nyx10.cs.du.edu
+ * Use ordered bins instead of best-fit threshhold
+ * Eliminate block-local decls to simplify tracing and debugging.
+ * Support another case of realloc via move into top
+ * Fix error occuring when initial sbrk_base not word-aligned.
+ * Rely on page size for units instead of SBRK_UNIT to
+ avoid surprises about sbrk alignment conventions.
+ * Add mallinfo, mallopt. Thanks to Raymond Nijssen
+ (raymond@es.ele.tue.nl) for the suggestion.
+ * Add `pad' argument to malloc_trim and top_pad mallopt parameter.
+ * More precautions for cases where other routines call sbrk,
+ courtesy of Wolfram Gloger (Gloger@lrz.uni-muenchen.de).
+ * Added macros etc., allowing use in linux libc from
+ H.J. Lu (hjl@gnu.ai.mit.edu)
+ * Inverted this history list
+
+ V2.6.1 Sat Dec 2 14:10:57 1995 Doug Lea (dl at gee)
+ * Re-tuned and fixed to behave more nicely with V2.6.0 changes.
+ * Removed all preallocation code since under current scheme
+ the work required to undo bad preallocations exceeds
+ the work saved in good cases for most test programs.
+ * No longer use return list or unconsolidated bins since
+ no scheme using them consistently outperforms those that don't
+ given above changes.
+ * Use best fit for very large chunks to prevent some worst-cases.
+ * Added some support for debugging
+
+ V2.6.0 Sat Nov 4 07:05:23 1995 Doug Lea (dl at gee)
+ * Removed footers when chunks are in use. Thanks to
+ Paul Wilson (wilson@cs.texas.edu) for the suggestion.
+
+ V2.5.4 Wed Nov 1 07:54:51 1995 Doug Lea (dl at gee)
+ * Added malloc_trim, with help from Wolfram Gloger
+ (wmglo@Dent.MED.Uni-Muenchen.DE).
+
+ V2.5.3 Tue Apr 26 10:16:01 1994 Doug Lea (dl at g)
+
+ V2.5.2 Tue Apr 5 16:20:40 1994 Doug Lea (dl at g)
+ * realloc: try to expand in both directions
+ * malloc: swap order of clean-bin strategy;
+ * realloc: only conditionally expand backwards
+ * Try not to scavenge used bins
+ * Use bin counts as a guide to preallocation
+ * Occasionally bin return list chunks in first scan
+ * Add a few optimizations from colin@nyx10.cs.du.edu
+
+ V2.5.1 Sat Aug 14 15:40:43 1993 Doug Lea (dl at g)
+ * faster bin computation & slightly different binning
+ * merged all consolidations to one part of malloc proper
+ (eliminating old malloc_find_space & malloc_clean_bin)
+ * Scan 2 returns chunks (not just 1)
+ * Propagate failure in realloc if malloc returns 0
+ * Add stuff to allow compilation on non-ANSI compilers
+ from kpv@research.att.com
+
+ V2.5 Sat Aug 7 07:41:59 1993 Doug Lea (dl at g.oswego.edu)
+ * removed potential for odd address access in prev_chunk
+ * removed dependency on getpagesize.h
+ * misc cosmetics and a bit more internal documentation
+ * anticosmetics: mangled names in macros to evade debugger strangeness
+ * tested on sparc, hp-700, dec-mips, rs6000
+ with gcc & native cc (hp, dec only) allowing
+ Detlefs & Zorn comparison study (in SIGPLAN Notices.)
+
+ Trial version Fri Aug 28 13:14:29 1992 Doug Lea (dl at g.oswego.edu)
+ * Based loosely on libg++-1.2X malloc. (It retains some of the overall
+ structure of old version, but most details differ.)
+
+*/
diff --git a/tools/src/dlmalloc/malloc_config.h b/tools/src/dlmalloc/malloc_config.h
new file mode 100644
index 0000000..7616baf
--- /dev/null
+++ b/tools/src/dlmalloc/malloc_config.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: MIT */
+
+#include <string.h>
+
+#include "../heapblock.h"
+#include "../utils.h"
+
+#define HAVE_MORECORE 1
+#define HAVE_MMAP 0
+#define MORECORE sbrk
+// This is optimal; dlmalloc copes with other users of sbrk/MORECORE gracefully, and heapblock
+// guarantees contiguous returns if called consecutively.
+#define MORECORE_CONTIGUOUS 1
+#define MALLOC_ALIGNMENT 16
+#define ABORT panic("dlmalloc: internal error\n")
+#define NO_MALLINFO 1
+#define NO_MALLOC_STATS 1
+#define malloc_getpagesize 16384
+#define LACKS_FCNTL_H 1
+#define LACKS_SYS_MMAN_H 1
+#define LACKS_SYS_PARAM_H 1
+#define LACKS_SYS_TYPES_H 1
+#define LACKS_STRINGS_H 1
+#define LACKS_STRING_H 1
+#define LACKS_STDLIB_H 1
+#define LACKS_SCHED_H 1
+#define LACKS_TIME_H 1
+#define LACKS_UNISTD_H 1
+#define MALLOC_FAILURE_ACTION panic("dlmalloc: out of memory\n");
+
+static void *sbrk(intptr_t inc)
+{
+ if (inc < 0)
+ return (void *)-1;
+
+ return heapblock_alloc(inc);
+}
diff --git a/tools/src/exception.c b/tools/src/exception.c
new file mode 100644
index 0000000..e849456
--- /dev/null
+++ b/tools/src/exception.c
@@ -0,0 +1,388 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "exception.h"
+#include "aic.h"
+#include "aic_regs.h"
+#include "cpu_regs.h"
+#include "gxf.h"
+#include "iodev.h"
+#include "memory.h"
+#include "uart.h"
+#include "utils.h"
+
+#define EL0_STACK_SIZE 0x4000
+
+u8 el0_stack[EL0_STACK_SIZE] ALIGNED(64);
+void *el0_stack_base = (void *)(u64)(&el0_stack[EL0_STACK_SIZE]);
+
+extern char _vectors_start[0];
+extern char _el1_vectors_start[0];
+
+volatile enum exc_guard_t exc_guard = GUARD_OFF;
+volatile int exc_count = 0;
+
+void el0_ret(void);
+void el1_ret(void);
+
+static char *m_table[0x10] = {
+ [0x00] = "EL0t", //
+ [0x04] = "EL1t", //
+ [0x05] = "EL1h", //
+ [0x08] = "EL2t", //
+ [0x09] = "EL2h", //
+};
+
+static char *gl_m_table[0x10] = {
+ [0x00] = "GL0t", //
+ [0x04] = "GL1t", //
+ [0x05] = "GL1h", //
+ [0x08] = "GL2t", //
+ [0x09] = "GL2h", //
+};
+
+static char *ec_table[0x40] = {
+ [0x00] = "unknown",
+ [0x01] = "wf*",
+ [0x03] = "c15 mcr/mrc",
+ [0x04] = "c15 mcrr/mrrc",
+ [0x05] = "c14 mcr/mrc",
+ [0x06] = "ldc/stc",
+ [0x07] = "FP off",
+ [0x08] = "VMRS access",
+ [0x09] = "PAC off",
+ [0x0a] = "ld/st64b",
+ [0x0c] = "c14 mrrc",
+ [0x0d] = "branch target",
+ [0x0e] = "illegal state",
+ [0x11] = "svc in a32",
+ [0x12] = "hvc in a32",
+ [0x13] = "smc in a32",
+ [0x15] = "svc in a64",
+ [0x16] = "hvc in a64",
+ [0x17] = "smc in a64",
+ [0x18] = "other mcr/mrc/sys",
+ [0x19] = "SVE off",
+ [0x1a] = "eret",
+ [0x1c] = "PAC failure",
+ [0x20] = "instruction abort (lower)",
+ [0x21] = "instruction abort (current)",
+ [0x22] = "pc misaligned",
+ [0x24] = "data abort (lower)",
+ [0x25] = "data abort (current)",
+ [0x26] = "sp misaligned",
+ [0x28] = "FP exception (a32)",
+ [0x2c] = "FP exception (a64)",
+ [0x2f] = "SError",
+ [0x30] = "BP (lower)",
+ [0x31] = "BP (current)",
+ [0x32] = "step (lower)",
+ [0x33] = "step (current)",
+ [0x34] = "watchpoint (lower)",
+ [0x35] = "watchpoint (current)",
+ [0x38] = "bkpt (a32)",
+ [0x3a] = "vector catch (a32)",
+ [0x3c] = "brk (a64)",
+};
+
+static const char *get_exception_source(u64 spsr)
+{
+ u64 aspsr = in_gl12() ? mrs(SYS_IMP_APL_ASPSR_GL1) : 0;
+ const char *m_desc = NULL;
+
+ if (aspsr & 1)
+ m_desc = gl_m_table[spsr & 0xf];
+ else
+ m_desc = m_table[spsr & 0xf];
+
+ if (!m_desc)
+ m_desc = "?";
+
+ return m_desc;
+}
+
+static const char *get_exception_level(void)
+{
+ u64 lvl = mrs(CurrentEL);
+
+ if (in_gl12()) {
+ if (lvl == 0x04)
+ return "GL1";
+ else if (lvl == 0x08)
+ return "GL2";
+ } else {
+ if (lvl == 0x04)
+ return "EL1";
+ else if (lvl == 0x08)
+ return "EL2";
+ }
+
+ return "?";
+}
+
+void exception_initialize(void)
+{
+ msr(VBAR_EL1, _vectors_start);
+
+ // Clear FIQ sources
+ msr(CNTP_CTL_EL0, 7L);
+ msr(CNTV_CTL_EL0, 7L);
+ if (in_el2()) {
+ msr(CNTP_CTL_EL02, 7L);
+ msr(CNTV_CTL_EL02, 7L);
+ }
+ reg_clr(SYS_IMP_APL_PMCR0, PMCR0_IACT | PMCR0_IMODE_MASK);
+ reg_clr(SYS_IMP_APL_UPMCR0, UPMCR0_IMODE_MASK);
+ msr(SYS_IMP_APL_IPI_SR_EL1, IPI_SR_PENDING);
+
+ if (is_primary_core())
+ msr(DAIF, 0 << 6); // Enable SError, IRQ and FIQ
+ else
+ msr(DAIF, 3 << 6); // Disable IRQ and FIQ
+
+ if (in_el2()) {
+ // Set up a sane HCR_EL2
+ msr(HCR_EL2, (BIT(41) | // API
+ BIT(40) | // APK
+ BIT(37) | // TEA
+ BIT(34) | // E2H
+ BIT(31) | // RW
+ BIT(27) | // TGE
+ BIT(5) | // AMO
+ BIT(4) | // IMO
+ BIT(3)); // FMO
+ );
+ // Set up exception forwarding from EL1
+ msr(VBAR_EL12, _el1_vectors_start);
+ sysop("isb");
+ }
+}
+
+void exception_shutdown(void)
+{
+ msr(DAIF, 7 << 6); // Disable SError, IRQ and FIQ
+}
+
+void print_regs(u64 *regs, int el12)
+{
+ bool in_gl;
+ u64 sp = ((u64)(regs)) + 256;
+
+ in_gl = in_gl12();
+
+ u64 spsr = in_gl ? mrs(SYS_IMP_APL_SPSR_GL1) : (el12 ? mrs(SPSR_EL12) : mrs(SPSR_EL1));
+
+ printf("Exception taken from %s\n", get_exception_source(spsr));
+ printf("Running in %s\n", get_exception_level());
+ printf("MPIDR: 0x%lx\n", mrs(MPIDR_EL1));
+ printf("Registers: (@%p)\n", regs);
+ printf(" x0-x3: %016lx %016lx %016lx %016lx\n", regs[0], regs[1], regs[2], regs[3]);
+ printf(" x4-x7: %016lx %016lx %016lx %016lx\n", regs[4], regs[5], regs[6], regs[7]);
+ printf(" x8-x11: %016lx %016lx %016lx %016lx\n", regs[8], regs[9], regs[10], regs[11]);
+ printf("x12-x15: %016lx %016lx %016lx %016lx\n", regs[12], regs[13], regs[14], regs[15]);
+ printf("x16-x19: %016lx %016lx %016lx %016lx\n", regs[16], regs[17], regs[18], regs[19]);
+ printf("x20-x23: %016lx %016lx %016lx %016lx\n", regs[20], regs[21], regs[22], regs[23]);
+ printf("x24-x27: %016lx %016lx %016lx %016lx\n", regs[24], regs[25], regs[26], regs[27]);
+ printf("x28-x30: %016lx %016lx %016lx\n", regs[28], regs[29], regs[30]);
+
+ u64 elr = in_gl ? mrs(SYS_IMP_APL_ELR_GL1) : (el12 ? mrs(ELR_EL12) : mrs(ELR_EL1));
+ u64 esr = in_gl ? mrs(SYS_IMP_APL_ESR_GL1) : (el12 ? mrs(ESR_EL12) : mrs(ESR_EL1));
+ u64 far = in_gl ? mrs(SYS_IMP_APL_FAR_GL1) : (el12 ? mrs(FAR_EL12) : mrs(FAR_EL1));
+
+ printf("PC: 0x%lx (rel: 0x%lx)\n", elr, elr - (u64)_base);
+ printf("SP: 0x%lx\n", sp);
+ printf("SPSR: 0x%lx\n", spsr);
+ if (in_gl12()) {
+ printf("ASPSR: 0x%lx\n", mrs(SYS_IMP_APL_ASPSR_GL1));
+ }
+ printf("FAR: 0x%lx\n", far);
+
+ const char *ec_desc = ec_table[(esr >> 26) & 0x3f];
+ printf("ESR: 0x%lx (%s)\n", esr, ec_desc ? ec_desc : "?");
+
+ u64 sts = mrs(SYS_IMP_APL_L2C_ERR_STS);
+ printf("L2C_ERR_STS: 0x%lx\n", sts);
+ printf("L2C_ERR_ADR: 0x%lx\n", mrs(SYS_IMP_APL_L2C_ERR_ADR));
+ printf("L2C_ERR_INF: 0x%lx\n", mrs(SYS_IMP_APL_L2C_ERR_INF));
+ msr(SYS_IMP_APL_L2C_ERR_STS, sts);
+
+ if (is_ecore()) {
+ printf("E_LSU_ERR_STS: 0x%lx\n", mrs(SYS_IMP_APL_E_LSU_ERR_STS));
+ printf("E_FED_ERR_STS: 0x%lx\n", mrs(SYS_IMP_APL_E_FED_ERR_STS));
+ printf("E_MMU_ERR_STS: 0x%lx\n", mrs(SYS_IMP_APL_E_MMU_ERR_STS));
+ } else {
+ printf("LSU_ERR_STS: 0x%lx\n", mrs(SYS_IMP_APL_LSU_ERR_STS));
+ printf("FED_ERR_STS: 0x%lx\n", mrs(SYS_IMP_APL_FED_ERR_STS));
+ printf("MMU_ERR_STS: 0x%lx\n", mrs(SYS_IMP_APL_MMU_ERR_STS));
+ }
+}
+
+void exc_sync(u64 *regs)
+{
+ u32 insn;
+ int el12 = 0;
+ bool in_gl = in_gl12();
+
+ u64 spsr = in_gl ? mrs(SYS_IMP_APL_SPSR_GL1) : mrs(SPSR_EL1);
+ u64 esr = in_gl ? mrs(SYS_IMP_APL_ESR_GL1) : mrs(ESR_EL1);
+ u64 elr = in_gl ? mrs(SYS_IMP_APL_ELR_GL1) : mrs(ELR_EL1);
+
+ if ((spsr & 0xf) == 0 && ((esr >> 26) & 0x3f) == 0x3c) {
+ // On clean EL0 return, let the normal exception return
+ // path take us back to the return thunk.
+ msr(SPSR_EL1, 0x09); // EL2h
+ msr(ELR_EL1, el0_ret);
+ return;
+ }
+
+ if (in_el2() && !in_gl12() && (spsr & 0xf) == 5 && ((esr >> 26) & 0x3f) == 0x16) {
+ // Hypercall
+ u32 imm = mrs(ESR_EL2) & 0xffff;
+ switch (imm) {
+ case 0:
+ // On clean EL1 return, let the normal exception return
+ // path take us back to the return thunk.
+ msr(SPSR_EL2, 0x09); // EL2h
+ msr(ELR_EL2, el1_ret);
+ return;
+ case 0x10 ... 0x1f:
+ if (!(exc_guard & GUARD_SILENT))
+ printf("EL1 Exception: 0x%x\n", imm);
+ // Short-circuit the hypercall and handle the EL1 exception
+ el12 = 1;
+ msr(SPSR_EL2, mrs(SPSR_EL12));
+ msr(ELR_EL2, mrs(ELR_EL12));
+ break;
+ default:
+ printf("Unknown HVC: 0x%x\n", imm);
+ break;
+ }
+ } else {
+ if (!(exc_guard & GUARD_SILENT))
+ printf("Exception: SYNC\n");
+ }
+
+ sysop("isb");
+ sysop("dsb sy");
+
+ if (!(exc_guard & GUARD_SILENT))
+ print_regs(regs, el12);
+
+ u64 l2c_err_sts = mrs(SYS_IMP_APL_L2C_ERR_STS);
+ msr(SYS_IMP_APL_L2C_ERR_STS, l2c_err_sts); // Clear the L2C_ERR flag bits
+
+ switch (exc_guard & GUARD_TYPE_MASK) {
+ case GUARD_SKIP:
+ elr += 4;
+ break;
+ case GUARD_MARK:
+ // Assuming this is a load or store, dest reg is in low bits
+ insn = read32(elr);
+ regs[insn & 0x1f] = 0xacce5515abad1dea;
+ elr += 4;
+ break;
+ case GUARD_RETURN:
+ regs[0] = 0xacce5515abad1dea;
+ elr = regs[30];
+ exc_guard = GUARD_OFF;
+ break;
+ case GUARD_OFF:
+ default:
+ printf("Unhandled exception, rebooting...\n");
+ flush_and_reboot();
+ }
+
+ exc_count++;
+
+ if (!(exc_guard & GUARD_SILENT))
+ printf("Recovering from exception (ELR=0x%lx)\n", elr);
+ if (in_gl)
+ msr(SYS_IMP_APL_ELR_GL1, elr);
+ else
+ msr(ELR_EL1, elr);
+
+ sysop("isb");
+ sysop("dsb sy");
+}
+
+void exc_irq(u64 *regs)
+{
+ u32 reason = aic_ack();
+
+ printf("Exception: IRQ (from %s) die: %lu type: %lu num: %lu mpidr: %lx\n",
+ get_exception_source(0), FIELD_GET(AIC_EVENT_DIE, reason),
+ FIELD_GET(AIC_EVENT_TYPE, reason), FIELD_GET(AIC_EVENT_NUM, reason), mrs(MPIDR_EL1));
+
+ UNUSED(regs);
+ // print_regs(regs);
+}
+
+void exc_fiq(u64 *regs)
+{
+ printf("Exception: FIQ (from %s)\n", get_exception_source(0));
+
+ u64 reg = mrs(CNTP_CTL_EL0);
+ if (reg == 0x5) {
+ printf(" PHYS timer IRQ, masking\n");
+ msr(CNTP_CTL_EL0, 7L);
+ }
+
+ reg = mrs(CNTV_CTL_EL0);
+ if (reg == 0x5) {
+ printf(" VIRT timer IRQ, masking\n");
+ msr(CNTV_CTL_EL0, 7L);
+ }
+
+ if (in_el2()) {
+ reg = mrs(CNTP_CTL_EL02);
+ if (reg == 0x5) {
+ printf(" PHYS EL02 timer IRQ, masking\n");
+ msr(CNTP_CTL_EL02, 7L);
+ }
+ reg = mrs(CNTV_CTL_EL02);
+ if (reg == 0x5) {
+ printf(" VIRT EL02 timer IRQ, masking\n");
+ msr(CNTV_CTL_EL02, 7L);
+ }
+ }
+
+ reg = mrs(SYS_IMP_APL_PMCR0);
+ if ((reg & (PMCR0_IMODE_MASK | PMCR0_IACT)) == (PMCR0_IMODE_FIQ | PMCR0_IACT)) {
+ printf(" PMC IRQ, masking\n");
+ reg_clr(SYS_IMP_APL_PMCR0, PMCR0_IACT | PMCR0_IMODE_MASK);
+ }
+ reg = mrs(SYS_IMP_APL_UPMCR0);
+ if ((reg & UPMCR0_IMODE_MASK) == UPMCR0_IMODE_FIQ && (mrs(SYS_IMP_APL_UPMSR) & UPMSR_IACT)) {
+ printf(" UPMC IRQ, masking\n");
+ reg_clr(SYS_IMP_APL_UPMCR0, UPMCR0_IMODE_MASK);
+ }
+
+ if (mrs(SYS_IMP_APL_IPI_SR_EL1) & IPI_SR_PENDING) {
+ printf(" Fast IPI IRQ, clearing\n");
+ msr(SYS_IMP_APL_IPI_SR_EL1, IPI_SR_PENDING);
+ }
+
+ UNUSED(regs);
+ // print_regs(regs);
+}
+
+void exc_serr(u64 *regs)
+{
+ if (!(exc_guard & GUARD_SILENT))
+ printf("Exception: SError\n");
+
+ sysop("dsb sy");
+ sysop("isb");
+
+ if (!(exc_guard & GUARD_SILENT))
+ print_regs(regs, 0);
+
+ if ((exc_guard & GUARD_TYPE_MASK) == GUARD_OFF) {
+ printf("Unhandled exception, rebooting...\n");
+ flush_and_reboot();
+ }
+
+ exc_count++;
+
+ sysop("dsb sy");
+ sysop("isb");
+}
diff --git a/tools/src/exception.h b/tools/src/exception.h
new file mode 100644
index 0000000..786f38f
--- /dev/null
+++ b/tools/src/exception.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef __EXCEPTION_H__
+#define __EXCEPTION_H__
+
+#define SIZEOF_EXC_INFO (64 * 8)
+
+#ifndef __ASSEMBLER__
+
+#include <assert.h>
+#include <stdint.h>
+
+#include "types.h"
+
+enum exc_guard_t {
+ GUARD_OFF = 0,
+ GUARD_SKIP,
+ GUARD_MARK,
+ GUARD_RETURN,
+ GUARD_TYPE_MASK = 0xff,
+ GUARD_SILENT = 0x100,
+};
+
+struct exc_info {
+ u64 regs[32];
+ u64 spsr;
+ u64 elr;
+ u64 esr;
+ u64 far;
+ u64 afsr1;
+ u64 sp[3];
+ u64 cpu_id;
+ u64 mpidr;
+ u64 elr_phys;
+ u64 far_phys;
+ u64 sp_phys;
+ void *extra;
+};
+static_assert(sizeof(struct exc_info) <= SIZEOF_EXC_INFO, "Please increase SIZEOF_EXC_INFO");
+static_assert((sizeof(struct exc_info) & 15) == 0, "SIZEOF_EXC_INFO must be a multiple of 16");
+
+extern volatile enum exc_guard_t exc_guard;
+extern volatile int exc_count;
+
+void exception_initialize(void);
+void exception_shutdown(void);
+
+void print_regs(u64 *regs, int el12);
+
+uint64_t el0_call(void *func, uint64_t a, uint64_t b, uint64_t c, uint64_t d);
+uint64_t el1_call(void *func, uint64_t a, uint64_t b, uint64_t c, uint64_t d);
+
+#endif
+
+#endif
diff --git a/tools/src/exception_asm.S b/tools/src/exception_asm.S
new file mode 100644
index 0000000..8c8d01f
--- /dev/null
+++ b/tools/src/exception_asm.S
@@ -0,0 +1,231 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "exception.h"
+#include "memory.h"
+
+.globl exc_sync
+.globl exc_irq
+.globl exc_fiq
+.globl exc_serr
+.globl _vectors_start
+.globl el0_stack
+
+.globl _v_sp0_sync
+.type _v_sp0_sync, @function
+_v_sp0_sync:
+ msr pan, #0
+ sub sp, sp, #(SIZEOF_EXC_INFO - 32 * 8)
+ str x30, [sp, #-16]!
+ bl _exc_entry
+ bl exc_sync
+
+ b _exc_return
+
+.globl _v_sp0_irq
+.type _v_sp0_irq, @function
+_v_sp0_irq:
+ msr pan, #0
+ sub sp, sp, #(SIZEOF_EXC_INFO - 32 * 8)
+ str x30, [sp, #-16]!
+ bl _exc_entry
+ bl exc_irq
+
+ b _exc_return
+
+.globl _v_sp0_fiq
+.type _v_sp0_fiq, @function
+_v_sp0_fiq:
+ msr pan, #0
+ sub sp, sp, #(SIZEOF_EXC_INFO - 32 * 8)
+ str x30, [sp, #-16]!
+ bl _exc_entry
+ bl exc_fiq
+
+ b _exc_return
+
+.globl _v_sp0_serr
+.type _v_sp0_serr, @function
+_v_sp0_serr:
+ msr pan, #0
+ sub sp, sp, #(SIZEOF_EXC_INFO - 32 * 8)
+ str x30, [sp, #-16]!
+ bl _exc_entry
+ bl exc_serr
+
+ b _exc_return
+
+.globl _exc_entry
+.type _exc_entry, @function
+_exc_entry:
+ stp x28, x29, [sp, #-16]!
+ stp x26, x27, [sp, #-16]!
+ stp x24, x25, [sp, #-16]!
+ stp x22, x23, [sp, #-16]!
+ stp x20, x21, [sp, #-16]!
+ stp x18, x19, [sp, #-16]!
+ stp x16, x17, [sp, #-16]!
+ stp x14, x15, [sp, #-16]!
+ stp x12, x13, [sp, #-16]!
+ stp x10, x11, [sp, #-16]!
+ stp x8, x9, [sp, #-16]!
+ stp x6, x7, [sp, #-16]!
+ stp x4, x5, [sp, #-16]!
+ stp x2, x3, [sp, #-16]!
+ stp x0, x1, [sp, #-16]!
+
+ mov x0, sp
+ ret
+
+.globl _exc_return
+.type _exc_return, @function
+_exc_return:
+ ldp x0, x1, [sp], #16
+ ldp x2, x3, [sp], #16
+ ldp x4, x5, [sp], #16
+ ldp x6, x7, [sp], #16
+ ldp x8, x9, [sp], #16
+ ldp x10, x11, [sp], #16
+ ldp x12, x13, [sp], #16
+ ldp x14, x15, [sp], #16
+ ldp x16, x17, [sp], #16
+ ldr x18, [sp], #8
+ add sp, sp, #88
+ ldr x30, [sp], #16
+
+ add sp, sp, #(SIZEOF_EXC_INFO - 32 * 8)
+
+ eret
+
+.globl el0_call
+.type el0_call, @function
+el0_call:
+ str x30, [sp, #-16]!
+
+ // Disable EL1
+ mrs x5, hcr_el2
+ orr x5, x5, #(1 << 27)
+ msr hcr_el2, x5
+ isb
+
+ mrs x5, daif
+ msr daifclr, 3
+ msr spsr_el1, x5
+
+ ldr x5, =_el0_thunk
+ msr elr_el1, x5
+
+ mov x5, #REGION_RWX_EL0
+ orr x0, x0, x5
+
+ ldr x5, =el0_stack_base
+ ldr x5, [x5]
+ mov x6, #REGION_RW_EL0
+ orr x5, x5, x6
+ msr spsel, #0
+ mov sp, x5
+
+ eret
+
+_el0_thunk:
+ mov x5, x0
+ mov x0, x1
+ mov x1, x2
+ mov x2, x3
+ mov x3, x4
+
+ blr x5
+
+ brk 0
+ .long 0
+
+.globl el0_ret
+.type el0_ret, @function
+el0_ret:
+ ldr x30, [sp], #16
+ ret
+
+.globl el1_call
+.type el1_call, @function
+el1_call:
+ str x30, [sp, #-16]!
+
+ // Enable EL1, but only if not already done.
+ // this check is here because writes to hcr_el2 are only possible from GL2
+ // if that mode has been enabled
+ mrs x5, hcr_el2
+ bic x6, x5, #(1 << 27)
+ cmp x5, x6
+ beq 1f
+ msr hcr_el2, x6
+ isb
+
+ 1: mrs x5, daif
+ msr daifclr, 3
+ mov x6, #5
+ orr x5, x5, x6 // EL1h
+ msr spsr_el2, x5
+
+ ldr x5, =_el1_thunk
+ msr elr_el2, x5
+
+ ldr x5, =el0_stack_base
+ ldr x5, [x5]
+ msr sp_el1, x5
+
+ eret
+
+_el1_thunk:
+ mov x5, x0
+ mov x0, x1
+ mov x1, x2
+ mov x2, x3
+ mov x3, x4
+
+ blr x5
+
+ hvc 0
+ .long 0
+
+.globl el1_ret
+.type el1_ret, @function
+el1_ret:
+ ldr x30, [sp], #16
+ ret
+
+.align 11
+.globl _el1_vectors_start
+_el1_vectors_start:
+ hvc 0x10
+ .align 7
+ hvc 0x11
+ .align 7
+ hvc 0x12
+ .align 7
+ hvc 0x13
+ .align 7
+
+ hvc 0x14
+ .align 7
+ hvc 0x15
+ .align 7
+ hvc 0x16
+ .align 7
+ hvc 0x17
+ .align 7
+
+ hvc 0x18
+ .align 7
+ hvc 0x19
+ .align 7
+ hvc 0x1a
+ .align 7
+ hvc 0x1b
+ .align 7
+
+ hvc 0x1c
+ .align 7
+ hvc 0x1d
+ .align 7
+ hvc 0x1e
+ .align 7
+ hvc 0x1f
diff --git a/tools/src/fb.c b/tools/src/fb.c
new file mode 100644
index 0000000..4c3e8b5
--- /dev/null
+++ b/tools/src/fb.c
@@ -0,0 +1,415 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "fb.h"
+#include "assert.h"
+#include "iodev.h"
+#include "malloc.h"
+#include "memory.h"
+#include "string.h"
+#include "types.h"
+#include "utils.h"
+#include "xnuboot.h"
+
+#define FB_DEPTH_MASK 0xff
+
+fb_t fb;
+
+struct image {
+ u32 *ptr;
+ u32 width;
+ u32 height;
+};
+
+static struct {
+ struct {
+ u8 *ptr;
+ u32 width;
+ u32 height;
+ } font;
+
+ struct {
+ u32 row;
+ u32 col;
+
+ u32 max_row;
+ u32 max_col;
+ } cursor;
+
+ struct {
+ u32 rows;
+ u32 cols;
+ } margin;
+
+ bool initialized;
+ bool active;
+} console;
+
+extern u8 _binary_build_bootlogo_128_bin_start[];
+extern u8 _binary_build_bootlogo_256_bin_start[];
+
+extern u8 _binary_build_font_bin_start[];
+extern u8 _binary_build_font_retina_bin_start[];
+
+const struct image logo_128 = {
+ .ptr = (void *)_binary_build_bootlogo_128_bin_start,
+ .width = 128,
+ .height = 128,
+};
+
+const struct image logo_256 = {
+ .ptr = (void *)_binary_build_bootlogo_256_bin_start,
+ .width = 256,
+ .height = 256,
+};
+
+const struct image *logo;
+struct image orig_logo;
+
+void fb_update(void)
+{
+ memcpy128(fb.hwptr, fb.ptr, fb.size);
+}
+
+static void fb_clear_font_row(u32 row)
+{
+ const u32 row_size = (console.margin.cols + console.cursor.max_col) * console.font.width * 4;
+ const u32 ystart = (console.margin.rows + row) * console.font.height * fb.stride;
+
+ for (u32 y = 0; y < console.font.height; ++y)
+ memset32(fb.ptr + ystart + y * fb.stride, 0, row_size);
+}
+
+static void fb_move_font_row(u32 dst, u32 src)
+{
+ const u32 row_size = (console.margin.cols + console.cursor.max_col) * console.font.width * 4;
+ u32 ysrc = (console.margin.rows + src) * console.font.height;
+ u32 ydst = (console.margin.rows + dst) * console.font.height;
+
+ ysrc *= fb.stride;
+ ydst *= fb.stride;
+
+ for (u32 y = 0; y < console.font.height; ++y)
+ memcpy32(fb.ptr + ydst + y * fb.stride, fb.ptr + ysrc + y * fb.stride, row_size);
+
+ fb_clear_font_row(src);
+}
+
+static inline u32 rgb2pixel_30(rgb_t c)
+{
+ return (c.b << 2) | (c.g << 12) | (c.r << 22);
+}
+
+static inline rgb_t pixel2rgb_30(u32 c)
+{
+ return (rgb_t){(c >> 22) & 0xff, (c >> 12) & 0xff, c >> 2};
+}
+
+static inline void fb_set_pixel(u32 x, u32 y, rgb_t c)
+{
+ fb.ptr[x + y * fb.stride] = rgb2pixel_30(c);
+}
+
+static inline rgb_t fb_get_pixel(u32 x, u32 y)
+{
+ return pixel2rgb_30(fb.ptr[x + y * fb.stride]);
+}
+
+void fb_blit(u32 x, u32 y, u32 w, u32 h, void *data, u32 stride, pix_fmt_t pix_fmt)
+{
+ u8 *p = data;
+
+ for (u32 i = 0; i < h; i++) {
+ for (u32 j = 0; j < w; j++) {
+ rgb_t color;
+ switch (pix_fmt) {
+ default:
+ case PIX_FMT_XRGB:
+ color.r = p[(j + i * stride) * 4];
+ color.g = p[(j + i * stride) * 4 + 1];
+ color.b = p[(j + i * stride) * 4 + 2];
+ break;
+ case PIX_FMT_XBGR:
+ color.r = p[(j + i * stride) * 4 + 2];
+ color.g = p[(j + i * stride) * 4 + 1];
+ color.b = p[(j + i * stride) * 4];
+ break;
+ }
+ fb_set_pixel(x + j, y + i, color);
+ }
+ }
+ fb_update();
+}
+
+void fb_unblit(u32 x, u32 y, u32 w, u32 h, void *data, u32 stride)
+{
+ u8 *p = data;
+
+ for (u32 i = 0; i < h; i++) {
+ for (u32 j = 0; j < w; j++) {
+ rgb_t color = fb_get_pixel(x + j, y + i);
+ p[(j + i * stride) * 4] = color.r;
+ p[(j + i * stride) * 4 + 1] = color.g;
+ p[(j + i * stride) * 4 + 2] = color.b;
+ p[(j + i * stride) * 4 + 3] = 0xff;
+ }
+ }
+}
+
+void fb_fill(u32 x, u32 y, u32 w, u32 h, rgb_t color)
+{
+ u32 c = rgb2pixel_30(color);
+ for (u32 i = 0; i < h; i++)
+ memset32(&fb.ptr[x + (y + i) * fb.stride], c, w * 4);
+ fb_update();
+}
+
+void fb_clear(rgb_t color)
+{
+ u32 c = rgb2pixel_30(color);
+ memset32(fb.ptr, c, fb.stride * fb.height * 4);
+ fb_update();
+}
+
+void fb_blit_image(u32 x, u32 y, const struct image *img)
+{
+ fb_blit(x, y, img->width, img->height, img->ptr, img->width, PIX_FMT_XRGB);
+}
+
+void fb_unblit_image(u32 x, u32 y, struct image *img)
+{
+ fb_unblit(x, y, img->width, img->height, img->ptr, img->width);
+}
+
+void fb_blit_logo(const struct image *logo)
+{
+ fb_blit_image((fb.width - logo->width) / 2, (fb.height - logo->height) / 2, logo);
+}
+
+void fb_display_logo(void)
+{
+ printf("fb: display logo\n");
+ fb_blit_logo(logo);
+}
+
+void fb_restore_logo(void)
+{
+ if (!orig_logo.ptr)
+ return;
+ fb_blit_logo(&orig_logo);
+}
+
+void fb_improve_logo(void)
+{
+ const u8 magic[] = "BY;iX2gK0b89P9P*Qa";
+ u8 *p = (void *)orig_logo.ptr;
+
+ if (!p || p[orig_logo.width * (orig_logo.height + 1) * 2] <= 250)
+ return;
+
+ for (u32 i = 0; i < orig_logo.height; i++) {
+ const u8 *c = &magic[min((max(i * 128 / orig_logo.height, 41) - 41) / 11, 5) * 3];
+ for (u32 j = 0; j < (orig_logo.width * 4); j++, p++)
+ *p = (*p * (c[(j - (j >> 2)) % 3] - 42)) / 63;
+ }
+}
+
+static inline rgb_t font_get_pixel(u8 c, u32 x, u32 y)
+{
+ c -= 0x20;
+ u8 v =
+ console.font.ptr[c * console.font.width * console.font.height + y * console.font.width + x];
+
+ rgb_t col = {.r = v, .g = v, .b = v};
+ return col;
+}
+
+static void fb_putbyte(u8 c)
+{
+ u32 x = (console.margin.cols + console.cursor.col) * console.font.width;
+ u32 y = (console.margin.rows + console.cursor.row) * console.font.height;
+
+ for (u32 i = 0; i < console.font.height; i++)
+ for (u32 j = 0; j < console.font.width; j++)
+ fb_set_pixel(x + j, y + i, font_get_pixel(c, j, i));
+}
+
+static void fb_putchar(u8 c)
+{
+ if (c == '\r') {
+ console.cursor.col = 0;
+ } else if (c == '\n') {
+ console.cursor.row++;
+ console.cursor.col = 0;
+ } else if (c >= 0x20 && c < 0x7f) {
+ fb_putbyte(c);
+ console.cursor.col++;
+ } else {
+ fb_putbyte('?');
+ console.cursor.col++;
+ }
+
+ if (console.cursor.col == console.cursor.max_col) {
+ console.cursor.row++;
+ console.cursor.col = 0;
+ }
+
+ if (console.cursor.row == console.cursor.max_row)
+ fb_console_scroll(1);
+}
+
+void fb_console_scroll(u32 n)
+{
+ u32 row = 0;
+ n = min(n, console.cursor.row);
+ for (; row < console.cursor.max_row - n; ++row)
+ fb_move_font_row(row, row + n);
+ for (; row < console.cursor.max_row; ++row)
+ fb_clear_font_row(row);
+ console.cursor.row -= n;
+}
+
+void fb_console_reserve_lines(u32 n)
+{
+ if ((console.cursor.max_row - console.cursor.row) <= n)
+ fb_console_scroll(1 + n - (console.cursor.max_row - console.cursor.row));
+ fb_update();
+}
+
+ssize_t fb_console_write(const char *bfr, size_t len)
+{
+ ssize_t wrote = 0;
+
+ if (!console.initialized || !console.active)
+ return 0;
+
+ while (len--) {
+ fb_putchar(*bfr++);
+ wrote++;
+ }
+
+ fb_update();
+
+ return wrote;
+}
+
+static bool fb_console_iodev_can_write(void *opaque)
+{
+ UNUSED(opaque);
+ return console.initialized && console.active;
+}
+
+static ssize_t fb_console_iodev_write(void *opaque, const void *buf, size_t len)
+{
+ UNUSED(opaque);
+ return fb_console_write(buf, len);
+}
+
+const struct iodev_ops iodev_fb_ops = {
+ .can_write = fb_console_iodev_can_write,
+ .write = fb_console_iodev_write,
+};
+
+struct iodev iodev_fb = {
+ .ops = &iodev_fb_ops,
+ .usage = USAGE_CONSOLE,
+ .lock = SPINLOCK_INIT,
+};
+
+static void fb_clear_console(void)
+{
+ for (u32 row = 0; row < console.cursor.max_row; ++row)
+ fb_clear_font_row(row);
+
+ console.cursor.col = 0;
+ console.cursor.row = 0;
+ fb_update();
+}
+
+void fb_init(bool clear)
+{
+ fb.hwptr = (void *)cur_boot_args.video.base;
+ fb.stride = cur_boot_args.video.stride / 4;
+ fb.width = cur_boot_args.video.width;
+ fb.height = cur_boot_args.video.height;
+ fb.depth = cur_boot_args.video.depth & FB_DEPTH_MASK;
+ fb.size = cur_boot_args.video.stride * cur_boot_args.video.height;
+ printf("fb init: %dx%d (%d) [s=%d] @%p\n", fb.width, fb.height, fb.depth, fb.stride, fb.hwptr);
+
+ mmu_add_mapping(cur_boot_args.video.base, cur_boot_args.video.base, ALIGN_UP(fb.size, 0x4000),
+ MAIR_IDX_NORMAL_NC, PERM_RW);
+
+ fb.ptr = malloc(fb.size);
+ memcpy(fb.ptr, fb.hwptr, fb.size);
+
+ if (cur_boot_args.video.depth & FB_DEPTH_FLAG_RETINA) {
+ logo = &logo_256;
+ console.font.ptr = _binary_build_font_retina_bin_start;
+ console.font.width = 16;
+ console.font.height = 32;
+ } else {
+ logo = &logo_128;
+ console.font.ptr = _binary_build_font_bin_start;
+ console.font.width = 8;
+ console.font.height = 16;
+ }
+
+ if (!orig_logo.ptr) {
+ orig_logo = *logo;
+ orig_logo.ptr = malloc(orig_logo.width * orig_logo.height * 4);
+ fb_unblit_image((fb.width - orig_logo.width) / 2, (fb.height - orig_logo.height) / 2,
+ &orig_logo);
+ }
+
+ if (clear)
+ memset32(fb.ptr, 0, fb.size);
+
+ console.margin.rows = 2;
+ console.margin.cols = 4;
+ console.cursor.col = 0;
+ console.cursor.row = 0;
+
+ console.cursor.max_row = (fb.height / console.font.height) - 2 * console.margin.rows;
+ console.cursor.max_col =
+ ((fb.width - logo->width) / 2) / console.font.width - 2 * console.margin.cols;
+
+ console.initialized = true;
+ console.active = false;
+
+ fb_clear_console();
+
+ printf("fb console: max rows %d, max cols %d\n", console.cursor.max_row,
+ console.cursor.max_col);
+}
+
+void fb_set_active(bool active)
+{
+ console.active = active;
+ if (active)
+ iodev_console_kick();
+}
+
+void fb_shutdown(bool restore_logo)
+{
+ if (!console.initialized)
+ return;
+
+ console.active = false;
+ console.initialized = false;
+ fb_clear_console();
+ if (restore_logo) {
+ fb_restore_logo();
+ free(orig_logo.ptr);
+ orig_logo.ptr = NULL;
+ }
+ free(fb.ptr);
+}
+
+void fb_reinit(void)
+{
+ if (!console.initialized)
+ return;
+
+ fb_shutdown(false);
+ fb_init(true);
+ fb_display_logo();
+}
diff --git a/tools/src/fb.h b/tools/src/fb.h
new file mode 100644
index 0000000..2bfd406
--- /dev/null
+++ b/tools/src/fb.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef FB_H
+#define FB_H
+
+#include "types.h"
+
+#define FB_DEPTH_FLAG_RETINA 0x10000
+
+typedef struct {
+ u32 *ptr; /* pointer to the start of the framebuffer */
+ u32 *hwptr; /* pointer to the start of the real framebuffer */
+ u32 stride; /* framebuffer stride divided by four (i.e. stride in pixels) */
+ u32 depth; /* framebuffer depth (i.e. bits per pixel) */
+ u32 width; /* width of the framebuffer in pixels */
+ u32 height; /* height of the framebuffer in pixels */
+ u32 size; /* size of the framebuffer in bytes */
+} fb_t;
+
+typedef struct {
+ u8 r;
+ u8 g;
+ u8 b;
+} rgb_t;
+
+typedef enum {
+ PIX_FMT_XRGB,
+ PIX_FMT_XBGR,
+} pix_fmt_t;
+
+extern fb_t fb;
+
+static inline rgb_t int2rgb(u32 c)
+{
+ return (rgb_t){c >> 16, c >> 8, c};
+}
+
+void fb_init(bool clear);
+void fb_shutdown(bool restore_logo);
+void fb_reinit(void);
+void fb_update(void);
+void fb_set_active(bool active);
+
+void fb_blit(u32 x, u32 y, u32 w, u32 h, void *data, u32 stride, pix_fmt_t format);
+void fb_unblit(u32 x, u32 y, u32 w, u32 h, void *data, u32 stride);
+void fb_fill(u32 x, u32 y, u32 w, u32 h, rgb_t color);
+void fb_clear(rgb_t color);
+
+void fb_display_logo(void);
+void fb_restore_logo(void);
+void fb_improve_logo(void);
+
+void fb_console_scroll(u32 n);
+void fb_console_reserve_lines(u32 n);
+ssize_t fb_console_write(const char *bfr, size_t len);
+
+#endif
diff --git a/tools/src/firmware.c b/tools/src/firmware.c
new file mode 100644
index 0000000..ca5f108
--- /dev/null
+++ b/tools/src/firmware.c
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "firmware.h"
+#include "adt.h"
+#include "string.h"
+#include "types.h"
+#include "utils.h"
+
+#include "libfdt/libfdt.h"
+#include "libfdt/libfdt_env.h"
+
+struct fw_version_info os_firmware;
+struct fw_version_info system_firmware;
+
+const struct fw_version_info fw_versions[NUM_FW_VERSIONS] = {
+ [V_UNKNOWN] = {V_UNKNOWN, "unknown", {0}, 1, "unknown"},
+ [V12_1] = {V12_1, "12.1", {12, 1, 0}, 3, "iBoot-7429.61.2"},
+ [V12_2] = {V12_2, "12.2", {12, 2, 0}, 3, "iBoot-7429.81.3"},
+ [V12_3] = {V12_3, "12.3", {12, 3, 0}, 3, "iBoot-7459.101.2"},
+ [V12_3_1] = {V12_3_1, "12.3.1", {12, 3, 1}, 3, "iBoot-7459.101.3"},
+ [V12_4] = {V12_4, "12.4", {12, 4, 0}, 3, "iBoot-7459.121.3"},
+ [V12_5] = {V12_5, "12.5", {12, 5, 0}, 3, "iBoot-7459.141.1"},
+ // Same as 12.5
+ // {V12_6, "12.6", {12, 6, 0}, 3, "iBoot-7459.141.1"},
+ [V13_0B4] = {V13_0B4, "13.0 beta4", {12, 99, 4}, 3, "iBoot-8419.0.151.0.1"},
+ [V13_0] = {V13_0, "13.0", {13, 0, 0}, 3, "iBoot-8419.41.10"},
+};
+
+int firmware_set_fdt(void *fdt, int node, const char *prop, const struct fw_version_info *ver)
+{
+ fdt32_t data[ARRAY_SIZE(ver->num)];
+
+ for (size_t i = 0; i < ver->num_length; i++) {
+ data[i] = cpu_to_fdt32(ver->num[i]);
+ }
+
+ return fdt_setprop(fdt, node, prop, data, ver->num_length * sizeof(u32));
+}
+
+static void detect_firmware(struct fw_version_info *info, const char *ver)
+{
+ for (size_t i = 0; i < ARRAY_SIZE(fw_versions); i++) {
+ if (!strcmp(fw_versions[i].iboot, ver)) {
+ *info = fw_versions[i];
+ return;
+ }
+ }
+
+ *info = fw_versions[V_UNKNOWN];
+ info->iboot = ver;
+}
+
+int firmware_init(void)
+{
+ int node = adt_path_offset(adt, "/chosen");
+
+ if (node < 0) {
+ printf("ADT: no /chosen found\n");
+ return -1;
+ }
+
+ u32 len;
+ const char *p = adt_getprop(adt, node, "firmware-version", &len);
+ if (p && len && p[len - 1] == 0) {
+ detect_firmware(&os_firmware, p);
+ printf("OS FW version: %s (%s)\n", os_firmware.string, os_firmware.iboot);
+ } else {
+ printf("ADT: failed to find firmware-version\n");
+ return -1;
+ }
+
+ p = adt_getprop(adt, node, "system-firmware-version", &len);
+ if (p && len && p[len - 1] == 0) {
+ detect_firmware(&system_firmware, p);
+ printf("System FW version: %s (%s)\n", system_firmware.string, system_firmware.iboot);
+ } else {
+ printf("ADT: failed to find system-firmware-version\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/tools/src/firmware.h b/tools/src/firmware.h
new file mode 100644
index 0000000..1a3375b
--- /dev/null
+++ b/tools/src/firmware.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef __FIRMWARE_H__
+#define __FIRMWARE_H__
+
+#include "types.h"
+
+enum fw_version {
+ V_UNKNOWN,
+ V12_1,
+ V12_2,
+ V12_3,
+ V12_3_1,
+ V12_4,
+ V12_5,
+ // V12_6,
+ V13_0B4,
+ V13_0,
+ NUM_FW_VERSIONS,
+};
+
+struct fw_version_info {
+ enum fw_version version;
+ const char *string;
+ u32 num[4];
+ size_t num_length;
+ const char *iboot;
+};
+
+extern struct fw_version_info os_firmware;
+extern struct fw_version_info system_firmware;
+extern const struct fw_version_info fw_versions[NUM_FW_VERSIONS];
+
+int firmware_init(void);
+int firmware_set_fdt(void *fdt, int node, const char *prop, const struct fw_version_info *ver);
+
+#endif
diff --git a/tools/src/gxf.c b/tools/src/gxf.c
new file mode 100644
index 0000000..7b751c5
--- /dev/null
+++ b/tools/src/gxf.c
@@ -0,0 +1,114 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "cpu_regs.h"
+#include "exception.h"
+#include "gxf.h"
+#include "malloc.h"
+#include "memory.h"
+#include "smp.h"
+#include "uart.h"
+#include "utils.h"
+
+uint64_t gxf_enter(void *func, uint64_t a, uint64_t b, uint64_t c, uint64_t d);
+
+void _gxf_init(void *gl2_stack, void *gl1_stack);
+
+u8 *gl1_stack[MAX_CPUS];
+u8 *gl2_stack[MAX_CPUS];
+
+void gxf_init(void)
+{
+ int cpu = smp_id();
+
+ if (!gl2_stack[cpu])
+ gl2_stack[cpu] = memalign(0x4000, GL_STACK_SIZE);
+ if (in_el2() && !gl1_stack[cpu])
+ gl1_stack[cpu] = memalign(0x4000, GL_STACK_SIZE);
+
+ _gxf_init(gl2_stack[cpu], gl1_stack[cpu]);
+}
+
+bool gxf_enabled(void)
+{
+ if (!(mrs(SYS_IMP_APL_SPRR_CONFIG_EL1) & SPRR_CONFIG_EN))
+ return false;
+
+ return (mrs(SYS_IMP_APL_GXF_CONFIG_EL1) & GXF_CONFIG_EN);
+}
+
+bool in_gl12(void)
+{
+ if (!gxf_enabled())
+ return false;
+
+ return (mrs(SYS_IMP_APL_GXF_STATUS_EL1) & GXF_STATUS_GUARDED);
+}
+
+static uint64_t gl_call(void *func, uint64_t a, uint64_t b, uint64_t c, uint64_t d)
+{
+ // disable the MMU first since enabling SPRR will change the meaning of all
+ // pagetable permission bits and also prevent us from having rwx pages
+ u64 sprr_state = mrs(SYS_IMP_APL_SPRR_CONFIG_EL1);
+ if (!(sprr_state & SPRR_CONFIG_EN))
+ reg_set_sync(SYS_IMP_APL_SPRR_CONFIG_EL1, sprr_state | SPRR_CONFIG_EN);
+
+ u64 gxf_state = mrs(SYS_IMP_APL_GXF_CONFIG_EL1);
+ if (!(gxf_state & GXF_CONFIG_EN))
+ reg_set_sync(SYS_IMP_APL_GXF_CONFIG_EL1, gxf_state | GXF_CONFIG_EN);
+
+ uint64_t ret = gxf_enter(func, a, b, c, d);
+
+ if (!(gxf_state & GXF_CONFIG_EN))
+ msr_sync(SYS_IMP_APL_GXF_CONFIG_EL1, gxf_state);
+ if (!(sprr_state & SPRR_CONFIG_EN))
+ msr_sync(SYS_IMP_APL_SPRR_CONFIG_EL1, sprr_state);
+
+ return ret;
+}
+
+uint64_t gl2_call(void *func, uint64_t a, uint64_t b, uint64_t c, uint64_t d)
+{
+ if (mrs(CurrentEL) != 0x8)
+ return -1;
+ return gl_call(func, a, b, c, d);
+}
+
+struct gl_call_argv {
+ void *func;
+ uint64_t a, b, c, d;
+};
+
+static uint64_t gl_call_wrapper(struct gl_call_argv *args)
+{
+ return gl_call(args->func, args->a, args->b, args->c, args->d);
+}
+
+uint64_t gl1_call(void *func, uint64_t a, uint64_t b, uint64_t c, uint64_t d)
+{
+ if (mrs(CurrentEL) == 0x4)
+ return gl_call(func, a, b, c, d);
+
+ struct gl_call_argv args;
+ args.func = func;
+ args.a = a;
+ args.b = b;
+ args.c = c;
+ args.d = d;
+
+ // enable EL1 here since once GXF has been enabled HCR_EL2 writes are only possible from GL2
+ if (mrs(HCR_EL2) & HCR_TGE)
+ reg_clr(HCR_EL2, HCR_TGE);
+
+ u64 sprr_state = mrs(SYS_IMP_APL_SPRR_CONFIG_EL1) & SPRR_CONFIG_EN;
+ reg_set_sync(SYS_IMP_APL_SPRR_CONFIG_EL1, SPRR_CONFIG_EN);
+
+ u64 gxf_state = mrs(SYS_IMP_APL_GXF_CONFIG_EL1) & GXF_CONFIG_EN;
+ reg_set_sync(SYS_IMP_APL_GXF_CONFIG_EL1, GXF_CONFIG_EN);
+
+ uint64_t ret = el1_call(gl_call_wrapper, (uint64_t)&args, 0, 0, 0);
+
+ msr_sync(SYS_IMP_APL_GXF_CONFIG_EL1, gxf_state);
+ msr_sync(SYS_IMP_APL_SPRR_CONFIG_EL1, sprr_state);
+
+ return ret;
+}
diff --git a/tools/src/gxf.h b/tools/src/gxf.h
new file mode 100644
index 0000000..9d1f22b
--- /dev/null
+++ b/tools/src/gxf.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef __GXF_H__
+#define __GXF_H__
+
+#include "types.h"
+
+#define GL_STACK_SIZE 0x10000
+
+#ifndef __ASSEMBLER__
+
+bool gxf_enabled(void);
+bool in_gl12(void);
+
+void gxf_init(void);
+
+uint64_t gl1_call(void *func, uint64_t a, uint64_t b, uint64_t c, uint64_t d);
+uint64_t gl2_call(void *func, uint64_t a, uint64_t b, uint64_t c, uint64_t d);
+
+#endif
+
+#endif
diff --git a/tools/src/gxf_asm.S b/tools/src/gxf_asm.S
new file mode 100644
index 0000000..6b6405f
--- /dev/null
+++ b/tools/src/gxf_asm.S
@@ -0,0 +1,246 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "gxf.h"
+#include "cpu_regs.h"
+#include "exception.h"
+
+#define genter .long 0x00201420
+#define gexit .long 0x00201400
+
+.global _gxf_init
+.type _gxf_init, @function
+_gxf_init:
+ str x30, [sp, #-16]!
+ mov x5, x0
+ mov x6, x1
+ mov x0, 1
+ msr SYS_IMP_APL_SPRR_CONFIG_EL1, x0
+ isb
+ msr SYS_IMP_APL_GXF_CONFIG_EL1, x0
+ isb
+ ldr x0, =_gxf_setup
+ msr SYS_IMP_APL_GXF_ENTER_EL1, x0
+ isb
+ genter
+ msr SYS_IMP_APL_GXF_CONFIG_EL1, xzr
+ isb
+ msr SYS_IMP_APL_SPRR_CONFIG_EL1, xzr
+ isb
+ ldr x30, [sp], #16
+ ret
+
+.globl gxf_enter
+.type gxf_enter, @function
+gxf_enter:
+ genter
+ ret
+
+_gxf_setup:
+ mov sp, x5
+ ldr x1, =_gxf_vectors
+ ldr x2, =_gxf_exc_sync
+ ldr x3, =_gxf_entry
+ msr SYS_IMP_APL_VBAR_GL1, x1
+ msr SYS_IMP_APL_GXF_ABORT_EL1, x2
+ msr SYS_IMP_APL_GXF_ENTER_EL1, x3
+
+ mrs x4, CurrentEL
+ cmp x4, #8
+ bne 1f
+
+ msr SYS_IMP_APL_SP_GL12, x6
+ msr SYS_IMP_APL_VBAR_GL12, x1
+ msr SYS_IMP_APL_GXF_ABORT_EL12, x2
+ msr SYS_IMP_APL_GXF_ENTER_EL12, x3
+
+1:
+ isb
+ gexit
+
+_gxf_entry:
+ stp x29, x30, [sp, #-16]!
+ stp x23, x24, [sp, #-16]!
+ stp x21, x22, [sp, #-16]!
+ stp x19, x20, [sp, #-16]!
+
+ // these registers would be overwritten by each exception happening in GL1/2
+ // but we need them to gexit correctly again
+ mrs x20, SYS_IMP_APL_SPSR_GL1
+ mrs x21, SYS_IMP_APL_ASPSR_GL1
+ mrs x22, SYS_IMP_APL_ESR_GL1
+ mrs x23, SYS_IMP_APL_ELR_GL1
+ mrs x24, SYS_IMP_APL_FAR_GL1
+
+ mov x5, x0
+ mov x0, x1
+ mov x1, x2
+ mov x2, x3
+ mov x3, x4
+
+ blr x5
+
+ msr SYS_IMP_APL_SPSR_GL1, x20
+ msr SYS_IMP_APL_ASPSR_GL1, x21
+ msr SYS_IMP_APL_ESR_GL1, x22
+ msr SYS_IMP_APL_ELR_GL1, x23
+ msr SYS_IMP_APL_FAR_GL1, x24
+
+ ldp x19, x20, [sp], #16
+ ldp x21, x22, [sp], #16
+ ldp x23, x24, [sp], #16
+ ldp x29, x30, [sp], #16
+
+ isb
+ gexit
+
+.align 11
+_gxf_vectors:
+ mov x9, '0'
+ b _gxf_exc_unk
+ .align 7
+ mov x9, '1'
+ b _gxf_exc_unk
+ .align 7
+ mov x9, '2'
+ b _gxf_exc_unk
+ .align 7
+ mov x9, '3'
+ b _gxf_exc_unk
+ .align 7
+ b _gxf_exc_sync
+ .align 7
+ mov x9, '5'
+ b _gxf_exc_unk
+ .align 7
+ mov x9, '6'
+ b _gxf_exc_unk
+ .align 7
+ b _gxf_serr
+ .align 7
+ b _gxf_exc_sync
+ .align 7
+ mov x9, '9'
+ b _gxf_exc_unk
+ .align 7
+ mov x9, 'a'
+ b _gxf_exc_unk
+ .align 7
+ b _gxf_serr
+ .align 7
+ mov x9, 'c'
+ b _gxf_exc_unk
+ .align 7
+ mov x9, 'd'
+ b _gxf_exc_unk
+ .align 7
+ mov x9, 'e'
+ b _gxf_exc_unk
+ .align 7
+ mov x9, 'f'
+ b _gxf_exc_unk
+ .align 7
+
+_gxf_exc_sync:
+ msr pan, #0
+ sub sp, sp, #(SIZEOF_EXC_INFO - 32 * 8)
+ str x30, [sp, #-16]!
+ bl _gxf_exc_entry
+ bl exc_sync
+ b _gxf_exc_return
+
+_gxf_serr:
+ msr pan, #0
+ sub sp, sp, #(SIZEOF_EXC_INFO - 32 * 8)
+ str x30, [sp, #-16]!
+ bl _gxf_exc_entry
+ bl exc_serr
+ b _gxf_exc_return
+
+_gxf_exc_entry:
+ stp x28, x29, [sp, #-16]!
+ stp x26, x27, [sp, #-16]!
+ stp x24, x25, [sp, #-16]!
+ stp x22, x23, [sp, #-16]!
+ stp x20, x21, [sp, #-16]!
+ stp x18, x19, [sp, #-16]!
+ stp x16, x17, [sp, #-16]!
+ stp x14, x15, [sp, #-16]!
+ stp x12, x13, [sp, #-16]!
+ stp x10, x11, [sp, #-16]!
+ stp x8, x9, [sp, #-16]!
+ stp x6, x7, [sp, #-16]!
+ stp x4, x5, [sp, #-16]!
+ stp x2, x3, [sp, #-16]!
+ stp x0, x1, [sp, #-16]!
+
+ mov x0, sp
+
+ mrs x1, SYS_IMP_APL_SPSR_GL1
+ msr SPSR_EL1, x1
+ mrs x1, SYS_IMP_APL_ELR_GL1
+ msr ELR_EL1, x1
+ mrs x1, SYS_IMP_APL_ESR_GL1
+ msr ESR_EL1, x1
+ mrs x1, SYS_IMP_APL_FAR_GL1
+ msr FAR_EL1, x1
+
+ ret
+
+_gxf_exc_return:
+ mrs x0, SPSR_EL1
+ msr SYS_IMP_APL_SPSR_GL1, x0
+ mrs x0, ELR_EL1
+ msr SYS_IMP_APL_ELR_GL1, x0
+
+ ldp x0, x1, [sp], #16
+ ldp x2, x3, [sp], #16
+ ldp x4, x5, [sp], #16
+ ldp x6, x7, [sp], #16
+ ldp x8, x9, [sp], #16
+ ldp x10, x11, [sp], #16
+ ldp x12, x13, [sp], #16
+ ldp x14, x15, [sp], #16
+ ldp x16, x17, [sp], #16
+ ldp x18, x19, [sp], #16
+ ldp x20, x21, [sp], #16
+ ldp x22, x23, [sp], #16
+ ldp x24, x25, [sp], #16
+ ldp x26, x27, [sp], #16
+ ldp x28, x29, [sp], #16
+ ldr x30, [sp], #16
+
+ add sp, sp, #(SIZEOF_EXC_INFO - 32 * 8)
+
+ isb
+
+ gexit
+
+_gxf_exc_unk:
+ msr pan, #0
+ mov w0, 0xd /* '\r', clang compat */
+ bl debug_putc
+ mov w0, '\n'
+ bl debug_putc
+ mov w0, '!'
+ bl debug_putc
+ mov w0, 'G'
+ bl debug_putc
+ mov w0, 'L'
+ bl debug_putc
+ mov w0, 'E'
+ bl debug_putc
+ mov w0, 'X'
+ bl debug_putc
+ mov w0, 'C'
+ bl debug_putc
+ mov w0, ':'
+ bl debug_putc
+ mov w0, w9
+ bl debug_putc
+ mov w0, '!'
+ bl debug_putc
+ mov w0, 0xd /* '\r', clang compat */
+ bl debug_putc
+ mov w0, '\n'
+ bl debug_putc
+ b reboot
diff --git a/tools/src/heapblock.c b/tools/src/heapblock.c
new file mode 100644
index 0000000..5f07f44
--- /dev/null
+++ b/tools/src/heapblock.c
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "heapblock.h"
+#include "assert.h"
+#include "types.h"
+#include "utils.h"
+#include "xnuboot.h"
+
+/*
+ * This is a non-freeing allocator, used as a backend for malloc and for uncompressing data.
+ *
+ * Allocating 0 bytes is allowed, and guarantees "infinite" (until the end of RAM) space is
+ * available at the returned pointer as long as no other malloc/heapblock calls occur, which is
+ * useful as a buffer for unknown-length uncompressed data. A subsequent call with a size will then
+ * actually reserve the block.
+ */
+
+static void *heap_base;
+
+void heapblock_init(void)
+{
+ void *top_of_kernel_data = (void *)cur_boot_args.top_of_kernel_data;
+
+ heap_base = top_of_kernel_data;
+ heapblock_alloc(0); // align base
+
+ printf("Heap base: %p\n", heap_base);
+}
+
+void *heapblock_alloc(size_t size)
+{
+ return heapblock_alloc_aligned(size, 64);
+}
+
+void *heapblock_alloc_aligned(size_t size, size_t align)
+{
+ assert((align & (align - 1)) == 0);
+ assert(heap_base);
+
+ uintptr_t block = (((uintptr_t)heap_base) + align - 1) & ~(align - 1);
+ heap_base = (void *)(block + size);
+
+ return (void *)block;
+}
diff --git a/tools/src/heapblock.h b/tools/src/heapblock.h
new file mode 100644
index 0000000..d67411d
--- /dev/null
+++ b/tools/src/heapblock.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef HEAPBLOCK_H
+#define HEAPBLOCK_H
+
+#include "types.h"
+
+void heapblock_init(void);
+
+void *heapblock_alloc(size_t size);
+void *heapblock_alloc_aligned(size_t size, size_t align);
+
+#endif
diff --git a/tools/src/hv.c b/tools/src/hv.c
new file mode 100644
index 0000000..be01692
--- /dev/null
+++ b/tools/src/hv.c
@@ -0,0 +1,329 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "hv.h"
+#include "assert.h"
+#include "cpu_regs.h"
+#include "display.h"
+#include "gxf.h"
+#include "memory.h"
+#include "pcie.h"
+#include "smp.h"
+#include "string.h"
+#include "usb.h"
+#include "utils.h"
+
+#define HV_TICK_RATE 1000
+
+DECLARE_SPINLOCK(bhl);
+
+void hv_enter_guest(u64 x0, u64 x1, u64 x2, u64 x3, void *entry);
+void hv_exit_guest(void) __attribute__((noreturn));
+
+extern char _hv_vectors_start[0];
+
+u64 hv_tick_interval;
+
+int hv_pinned_cpu;
+int hv_want_cpu;
+
+static bool hv_should_exit;
+bool hv_started_cpus[MAX_CPUS];
+u32 hv_cpus_in_guest;
+u64 hv_saved_sp[MAX_CPUS];
+
+struct hv_secondary_info_t {
+ uint64_t hcr;
+ uint64_t hacr;
+ uint64_t vtcr, vttbr;
+ uint64_t mdcr;
+ uint64_t mdscr;
+ uint64_t amx_ctl;
+ uint64_t apvmkeylo, apvmkeyhi, apsts;
+ uint64_t actlr_el2;
+ uint64_t actlr_el1;
+ uint64_t cnthctl;
+ uint64_t sprr_config;
+ uint64_t gxf_config;
+};
+
+static struct hv_secondary_info_t hv_secondary_info;
+
+void hv_init(void)
+{
+ pcie_shutdown();
+ // Make sure we wake up DCP if we put it to sleep, just quiesce it to match ADT
+ if (display_is_external && display_start_dcp() >= 0)
+ display_shutdown(DCP_QUIESCED);
+ // reenable hpm interrupts for the guest for unused iodevs
+ usb_hpm_restore_irqs(0);
+ smp_start_secondaries();
+ smp_set_wfe_mode(true);
+ hv_wdt_init();
+
+ // Enable physical timer for EL1
+ msr(CNTHCTL_EL2, CNTHCTL_EL1PTEN | CNTHCTL_EL1PCTEN);
+
+ hv_pt_init();
+
+ // Configure hypervisor defaults
+ hv_write_hcr(HCR_API | // Allow PAuth instructions
+ HCR_APK | // Allow PAuth key registers
+ HCR_TEA | // Trap external aborts
+ HCR_E2H | // VHE mode (forced)
+ HCR_RW | // AArch64 guest
+ HCR_AMO | // Trap SError exceptions
+ HCR_VM); // Enable stage 2 translation
+
+ // No guest vectors initially
+ msr(VBAR_EL12, 0);
+
+ // Compute tick interval
+ hv_tick_interval = mrs(CNTFRQ_EL0) / HV_TICK_RATE;
+
+ sysop("dsb ishst");
+ sysop("tlbi alle1is");
+ sysop("dsb ish");
+ sysop("isb");
+}
+
+static void hv_set_gxf_vbar(void)
+{
+ msr(SYS_IMP_APL_VBAR_GL1, _hv_vectors_start);
+}
+
+void hv_start(void *entry, u64 regs[4])
+{
+ hv_should_exit = false;
+ memset(hv_started_cpus, 0, sizeof(hv_started_cpus));
+ hv_started_cpus[0] = 1;
+
+ msr(VBAR_EL1, _hv_vectors_start);
+
+ if (gxf_enabled())
+ gl2_call(hv_set_gxf_vbar, 0, 0, 0, 0);
+
+ hv_secondary_info.hcr = mrs(HCR_EL2);
+ hv_secondary_info.hacr = mrs(HACR_EL2);
+ hv_secondary_info.vtcr = mrs(VTCR_EL2);
+ hv_secondary_info.vttbr = mrs(VTTBR_EL2);
+ hv_secondary_info.mdcr = mrs(MDCR_EL2);
+ hv_secondary_info.mdscr = mrs(MDSCR_EL1);
+ hv_secondary_info.amx_ctl = mrs(SYS_IMP_APL_AMX_CTL_EL2);
+ hv_secondary_info.apvmkeylo = mrs(SYS_IMP_APL_APVMKEYLO_EL2);
+ hv_secondary_info.apvmkeyhi = mrs(SYS_IMP_APL_APVMKEYHI_EL2);
+ hv_secondary_info.apsts = mrs(SYS_IMP_APL_APSTS_EL12);
+ hv_secondary_info.actlr_el2 = mrs(ACTLR_EL2);
+ hv_secondary_info.actlr_el1 = mrs(SYS_IMP_APL_ACTLR_EL12);
+ hv_secondary_info.cnthctl = mrs(CNTHCTL_EL2);
+ hv_secondary_info.sprr_config = mrs(SYS_IMP_APL_SPRR_CONFIG_EL1);
+ hv_secondary_info.gxf_config = mrs(SYS_IMP_APL_GXF_CONFIG_EL1);
+
+ hv_arm_tick();
+ hv_pinned_cpu = -1;
+ hv_want_cpu = -1;
+ hv_cpus_in_guest = 1;
+
+ hv_enter_guest(regs[0], regs[1], regs[2], regs[3], entry);
+
+ __atomic_sub_fetch(&hv_cpus_in_guest, 1, __ATOMIC_ACQUIRE);
+ spin_lock(&bhl);
+
+ hv_wdt_stop();
+
+ hv_should_exit = true;
+ printf("HV: Exiting hypervisor (main CPU)\n");
+
+ for (int i = 0; i < MAX_CPUS; i++) {
+ if (hv_started_cpus[i]) {
+ printf("HV: Waiting for CPU %d to exit\n", i);
+ spin_unlock(&bhl);
+ smp_wait(i);
+ spin_lock(&bhl);
+ hv_started_cpus[i] = false;
+ }
+ }
+
+ printf("HV: All CPUs exited\n");
+ spin_unlock(&bhl);
+}
+
+static void hv_init_secondary(struct hv_secondary_info_t *info)
+{
+ gxf_init();
+
+ msr(VBAR_EL1, _hv_vectors_start);
+
+ msr(HCR_EL2, info->hcr);
+ msr(HACR_EL2, info->hacr);
+ msr(VTCR_EL2, info->vtcr);
+ msr(VTTBR_EL2, info->vttbr);
+ msr(MDCR_EL2, info->mdcr);
+ msr(MDSCR_EL1, info->mdscr);
+ msr(SYS_IMP_APL_AMX_CTL_EL2, info->amx_ctl);
+ msr(SYS_IMP_APL_APVMKEYLO_EL2, info->apvmkeylo);
+ msr(SYS_IMP_APL_APVMKEYHI_EL2, info->apvmkeyhi);
+ msr(SYS_IMP_APL_APSTS_EL12, info->apsts);
+ msr(ACTLR_EL2, info->actlr_el2);
+ msr(SYS_IMP_APL_ACTLR_EL12, info->actlr_el1);
+ msr(CNTHCTL_EL2, info->cnthctl);
+ msr(SYS_IMP_APL_SPRR_CONFIG_EL1, info->sprr_config);
+ msr(SYS_IMP_APL_GXF_CONFIG_EL1, info->gxf_config);
+
+ if (gxf_enabled())
+ gl2_call(hv_set_gxf_vbar, 0, 0, 0, 0);
+
+ hv_arm_tick();
+}
+
+static void hv_enter_secondary(void *entry, u64 regs[4])
+{
+ hv_enter_guest(regs[0], regs[1], regs[2], regs[3], entry);
+
+ spin_lock(&bhl);
+
+ hv_should_exit = true;
+ printf("HV: Exiting from CPU %d\n", smp_id());
+
+ __atomic_sub_fetch(&hv_cpus_in_guest, 1, __ATOMIC_ACQUIRE);
+
+ spin_unlock(&bhl);
+}
+
+void hv_start_secondary(int cpu, void *entry, u64 regs[4])
+{
+ printf("HV: Initializing secondary %d\n", cpu);
+ iodev_console_flush();
+
+ mmu_init_secondary(cpu);
+ iodev_console_flush();
+ smp_call4(cpu, hv_init_secondary, (u64)&hv_secondary_info, 0, 0, 0);
+ smp_wait(cpu);
+ iodev_console_flush();
+
+ printf("HV: Entering guest secondary %d at %p\n", cpu, entry);
+ hv_started_cpus[cpu] = true;
+ __atomic_add_fetch(&hv_cpus_in_guest, 1, __ATOMIC_ACQUIRE);
+
+ iodev_console_flush();
+ smp_call4(cpu, hv_enter_secondary, (u64)entry, (u64)regs, 0, 0);
+}
+
+void hv_rendezvous(void)
+{
+ if (!__atomic_load_n(&hv_cpus_in_guest, __ATOMIC_ACQUIRE))
+ return;
+
+ /* IPI all CPUs. This might result in spurious IPIs to the guest... */
+ for (int i = 0; i < MAX_CPUS; i++) {
+ if (i != smp_id() && hv_started_cpus[i]) {
+ smp_send_ipi(i);
+ }
+ }
+ while (__atomic_load_n(&hv_cpus_in_guest, __ATOMIC_ACQUIRE))
+ ;
+}
+
+bool hv_switch_cpu(int cpu)
+{
+ if (cpu > MAX_CPUS || cpu < 0 || !hv_started_cpus[cpu]) {
+ printf("HV: CPU #%d is inactive or invalid\n", cpu);
+ return false;
+ }
+ printf("HV: switching to CPU #%d\n", cpu);
+ hv_want_cpu = cpu;
+ hv_rendezvous();
+ return true;
+}
+
+void hv_pin_cpu(int cpu)
+{
+ hv_pinned_cpu = cpu;
+}
+
+void hv_write_hcr(u64 val)
+{
+ if (gxf_enabled() && !in_gl12())
+ gl2_call(hv_write_hcr, val, 0, 0, 0);
+ else
+ msr(HCR_EL2, val);
+}
+
+u64 hv_get_spsr(void)
+{
+ if (in_gl12())
+ return mrs(SYS_IMP_APL_SPSR_GL1);
+ else
+ return mrs(SPSR_EL2);
+}
+
+void hv_set_spsr(u64 val)
+{
+ if (in_gl12())
+ return msr(SYS_IMP_APL_SPSR_GL1, val);
+ else
+ return msr(SPSR_EL2, val);
+}
+
+u64 hv_get_esr(void)
+{
+ if (in_gl12())
+ return mrs(SYS_IMP_APL_ESR_GL1);
+ else
+ return mrs(ESR_EL2);
+}
+
+u64 hv_get_far(void)
+{
+ if (in_gl12())
+ return mrs(SYS_IMP_APL_FAR_GL1);
+ else
+ return mrs(FAR_EL2);
+}
+
+u64 hv_get_afsr1(void)
+{
+ if (in_gl12())
+ return mrs(SYS_IMP_APL_AFSR1_GL1);
+ else
+ return mrs(AFSR1_EL2);
+}
+
+u64 hv_get_elr(void)
+{
+ if (in_gl12())
+ return mrs(SYS_IMP_APL_ELR_GL1);
+ else
+ return mrs(ELR_EL2);
+}
+
+void hv_set_elr(u64 val)
+{
+ if (in_gl12())
+ return msr(SYS_IMP_APL_ELR_GL1, val);
+ else
+ return msr(ELR_EL2, val);
+}
+
+void hv_arm_tick(void)
+{
+ msr(CNTP_TVAL_EL0, hv_tick_interval);
+ msr(CNTP_CTL_EL0, CNTx_CTL_ENABLE);
+}
+
+void hv_maybe_exit(void)
+{
+ if (hv_should_exit) {
+ hv_exit_guest();
+ }
+}
+
+void hv_tick(struct exc_info *ctx)
+{
+ hv_wdt_pet();
+ iodev_handle_events(uartproxy_iodev);
+ if (iodev_can_read(uartproxy_iodev)) {
+ if (hv_pinned_cpu == -1 || hv_pinned_cpu == smp_id())
+ hv_exc_proxy(ctx, START_HV, HV_USER_INTERRUPT, NULL);
+ }
+ hv_vuart_poll();
+}
diff --git a/tools/src/hv.h b/tools/src/hv.h
new file mode 100644
index 0000000..c91a444
--- /dev/null
+++ b/tools/src/hv.h
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef HV_H
+#define HV_H
+
+#include "exception.h"
+#include "iodev.h"
+#include "types.h"
+#include "uartproxy.h"
+
+typedef bool(hv_hook_t)(struct exc_info *ctx, u64 addr, u64 *val, bool write, int width);
+
+#define MMIO_EVT_ATTR GENMASK(31, 24)
+#define MMIO_EVT_CPU GENMASK(23, 16)
+#define MMIO_EVT_SH GENMASK(15, 14)
+#define MMIO_EVT_MULTI BIT(6)
+#define MMIO_EVT_WRITE BIT(5)
+#define MMIO_EVT_WIDTH GENMASK(4, 0)
+
+struct hv_evt_mmiotrace {
+ u32 flags;
+ u32 reserved;
+ u64 pc;
+ u64 addr;
+ u64 data;
+};
+
+struct hv_evt_irqtrace {
+ u32 flags;
+ u16 type;
+ u16 num;
+};
+
+#define HV_MAX_RW_SIZE 64
+#define HV_MAX_RW_WORDS (HV_MAX_RW_SIZE >> 3)
+
+struct hv_vm_proxy_hook_data {
+ u32 flags;
+ u32 id;
+ u64 addr;
+ u64 data[HV_MAX_RW_WORDS];
+};
+
+typedef enum _hv_entry_type {
+ HV_HOOK_VM = 1,
+ HV_VTIMER,
+ HV_USER_INTERRUPT,
+ HV_WDT_BARK,
+ HV_CPU_SWITCH,
+ HV_VIRTIO,
+} hv_entry_type;
+
+/* VM */
+void hv_pt_init(void);
+int hv_map(u64 from, u64 to, u64 size, u64 incr);
+int hv_unmap(u64 from, u64 size);
+int hv_map_hw(u64 from, u64 to, u64 size);
+int hv_map_sw(u64 from, u64 to, u64 size);
+int hv_map_hook(u64 from, hv_hook_t *hook, u64 size);
+u64 hv_translate(u64 addr, bool s1only, bool w, u64 *par_out);
+u64 hv_pt_walk(u64 addr);
+bool hv_handle_dabort(struct exc_info *ctx);
+bool hv_pa_write(struct exc_info *ctx, u64 addr, u64 *val, int width);
+bool hv_pa_read(struct exc_info *ctx, u64 addr, u64 *val, int width);
+bool hv_pa_rw(struct exc_info *ctx, u64 addr, u64 *val, bool write, int width);
+
+/* AIC events through tracing the MMIO event address */
+bool hv_trace_irq(u32 type, u32 num, u32 count, u32 flags);
+
+/* Virtual peripherals */
+void hv_vuart_poll(void);
+void hv_map_vuart(u64 base, int irq, iodev_id_t iodev);
+struct virtio_conf;
+void hv_map_virtio(u64 base, struct virtio_conf *conf);
+void virtio_put_buffer(u64 base, int qu, u32 id, u32 len);
+
+/* Exceptions */
+void hv_exc_proxy(struct exc_info *ctx, uartproxy_boot_reason_t reason, u32 type, void *extra);
+void hv_set_time_stealing(bool enabled, bool reset);
+
+/* WDT */
+void hv_wdt_pet(void);
+void hv_wdt_suspend(void);
+void hv_wdt_resume(void);
+void hv_wdt_init(void);
+void hv_wdt_start(int cpu);
+void hv_wdt_stop(void);
+void hv_wdt_breadcrumb(char c);
+
+/* Utilities */
+void hv_write_hcr(u64 val);
+u64 hv_get_spsr(void);
+void hv_set_spsr(u64 val);
+u64 hv_get_esr(void);
+u64 hv_get_far(void);
+u64 hv_get_elr(void);
+u64 hv_get_afsr1(void);
+void hv_set_elr(u64 val);
+
+/* HV main */
+void hv_init(void);
+void hv_start(void *entry, u64 regs[4]);
+void hv_start_secondary(int cpu, void *entry, u64 regs[4]);
+void hv_rendezvous(void);
+bool hv_switch_cpu(int cpu);
+void hv_pin_cpu(int cpu);
+void hv_arm_tick(void);
+void hv_rearm(void);
+void hv_maybe_exit(void);
+void hv_tick(struct exc_info *ctx);
+
+#endif
diff --git a/tools/src/hv_aic.c b/tools/src/hv_aic.c
new file mode 100644
index 0000000..cc5406a
--- /dev/null
+++ b/tools/src/hv_aic.c
@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "adt.h"
+#include "aic.h"
+#include "aic_regs.h"
+#include "hv.h"
+#include "uartproxy.h"
+#include "utils.h"
+
+#define IRQTRACE_IRQ BIT(0)
+
+static u32 trace_hw_num[AIC_MAX_DIES][AIC_MAX_HW_NUM / 32];
+
+static void emit_irqtrace(u16 die, u16 type, u16 num)
+{
+ struct hv_evt_irqtrace evt = {
+ .flags = IRQTRACE_IRQ,
+ .type = type,
+ .num = die * aic->max_irq + num,
+ };
+
+ hv_wdt_suspend();
+ uartproxy_send_event(EVT_IRQTRACE, &evt, sizeof(evt));
+ hv_wdt_resume();
+}
+
+static bool trace_aic_event(struct exc_info *ctx, u64 addr, u64 *val, bool write, int width)
+{
+ if (!hv_pa_rw(ctx, addr, val, write, width))
+ return false;
+
+ if (addr != (aic->base + aic->regs.event) || write || width != 2) {
+ return true;
+ }
+
+ u16 die = FIELD_GET(AIC_EVENT_DIE, *val);
+ u16 type = FIELD_GET(AIC_EVENT_TYPE, *val);
+ u16 num = FIELD_GET(AIC_EVENT_NUM, *val);
+
+ if (die > AIC_MAX_DIES)
+ return true;
+
+ switch (type) {
+ case AIC_EVENT_TYPE_HW:
+ if (trace_hw_num[die][num / 32] & BIT(num & 31)) {
+ emit_irqtrace(die, type, num);
+ }
+ break;
+ default:
+ // ignore
+ break;
+ }
+
+ return true;
+}
+
+bool hv_trace_irq(u32 type, u32 num, u32 count, u32 flags)
+{
+ dprintf("HV: hv_trace_irq type: %u start: %u num: %u flags: 0x%x\n", type, num, count, flags);
+ if (type == AIC_EVENT_TYPE_HW) {
+ u32 die = num / aic->max_irq;
+ num %= AIC_MAX_HW_NUM;
+ if (die >= aic->max_irq || num >= AIC_MAX_HW_NUM || count > AIC_MAX_HW_NUM - num) {
+ printf("HV: invalid IRQ range: (%u, %u) for die %u\n", num, num + count, die);
+ return false;
+ }
+ for (u32 n = num; n < num + count; n++) {
+ switch (flags) {
+ case IRQTRACE_IRQ:
+ trace_hw_num[die][n / 32] |= BIT(n & 31);
+ break;
+ default:
+ trace_hw_num[die][n / 32] &= ~(BIT(n & 31));
+ break;
+ }
+ }
+ } else {
+ printf("HV: not handling AIC event type: 0x%02x num: %u\n", type, num);
+ return false;
+ }
+
+ if (!aic) {
+ printf("HV: AIC not initialized\n");
+ return false;
+ }
+
+ static bool hooked = false;
+
+ if (aic && !hooked) {
+ hv_map_hook(aic->base, trace_aic_event, aic->regs.reg_size);
+ hooked = true;
+ }
+
+ return true;
+}
diff --git a/tools/src/hv_asm.S b/tools/src/hv_asm.S
new file mode 100644
index 0000000..634eb09
--- /dev/null
+++ b/tools/src/hv_asm.S
@@ -0,0 +1,196 @@
+/* spDx-License-Identifier: MIT */
+
+#include "exception.h"
+
+.align 11
+.globl _hv_vectors_start
+_hv_vectors_start:
+
+ /* EL2 with SP_EL0 */
+ mov x9, '0'
+ b cpu_reset
+ .align 7
+ mov x9, '1'
+ b exc_unk
+ .align 7
+ mov x9, '2'
+ b exc_unk
+ .align 7
+ mov x9, '3'
+ b exc_unk
+ .align 7
+
+ /* EL2 with SP_EL2 */
+ b _v_sp0_sync
+ .align 7
+ b _v_sp0_irq
+ .align 7
+ b _v_sp0_fiq
+ .align 7
+ b _v_sp0_serr
+ .align 7
+
+ /* EL1/0 64-bit */
+ b _v_hv_sync
+ .align 7
+ b _v_hv_irq
+ .align 7
+ b _v_hv_fiq
+ .align 7
+ b _v_hv_serr
+ .align 7
+
+ /* EL1/0 32-bit */
+ mov x9, 'p'
+ b exc_unk
+ .align 7
+ mov x9, 'q'
+ b exc_unk
+ .align 7
+ mov x9, 'r'
+ b exc_unk
+ .align 7
+ mov x9, 's'
+ b exc_unk
+ .align 7
+
+.globl _hv_entry
+.type _hv_entry, @function
+_hv_entry:
+ stp x28, x29, [sp, #-16]!
+ stp x26, x27, [sp, #-16]!
+ stp x24, x25, [sp, #-16]!
+ stp x22, x23, [sp, #-16]!
+ stp x20, x21, [sp, #-16]!
+ stp x18, x19, [sp, #-16]!
+ stp x16, x17, [sp, #-16]!
+ stp x14, x15, [sp, #-16]!
+ stp x12, x13, [sp, #-16]!
+ stp x10, x11, [sp, #-16]!
+ stp x8, x9, [sp, #-16]!
+ stp x6, x7, [sp, #-16]!
+ stp x4, x5, [sp, #-16]!
+ stp x2, x3, [sp, #-16]!
+ stp x0, x1, [sp, #-16]!
+
+ dsb sy
+ isb
+
+ mov x0, sp
+ ret
+
+.globl _hv_return
+.type _hv_return, @function
+_hv_return:
+ ldp x0, x1, [sp], #16
+ ldp x2, x3, [sp], #16
+ ldp x4, x5, [sp], #16
+ ldp x6, x7, [sp], #16
+ ldp x8, x9, [sp], #16
+ ldp x10, x11, [sp], #16
+ ldp x12, x13, [sp], #16
+ ldp x14, x15, [sp], #16
+ ldp x16, x17, [sp], #16
+ ldp x18, x19, [sp], #16
+ ldp x20, x21, [sp], #16
+ ldp x22, x23, [sp], #16
+ ldp x24, x25, [sp], #16
+ ldp x26, x27, [sp], #16
+ ldp x28, x29, [sp], #16
+ ldr x30, [sp], #16
+
+ add sp, sp, #(SIZEOF_EXC_INFO - 32 * 8)
+
+ eret
+
+.globl _v_hv_sync
+.type _v_hv_sync, @function
+_v_hv_sync:
+ msr pan, #0
+ sub sp, sp, #(SIZEOF_EXC_INFO - 32 * 8)
+ str x30, [sp, #-16]!
+ bl _hv_entry
+ bl hv_exc_sync
+
+ b _hv_return
+
+.globl _v_hv_irq
+.type _v_hv_irq, @function
+_v_hv_irq:
+ msr pan, #0
+ sub sp, sp, #(SIZEOF_EXC_INFO - 32 * 8)
+ str x30, [sp, #-16]!
+ bl _hv_entry
+ bl hv_exc_irq
+
+ b _hv_return
+
+.globl _v_hv_fiq
+.type _v_hv_fiq, @function
+_v_hv_fiq:
+ msr pan, #0
+ sub sp, sp, #(SIZEOF_EXC_INFO - 32 * 8)
+ str x30, [sp, #-16]!
+ bl _hv_entry
+ bl hv_exc_fiq
+
+ b _hv_return
+
+.globl _v_hv_serr
+.type _v_hv_serr, @function
+_v_hv_serr:
+ msr pan, #0
+ sub sp, sp, #(SIZEOF_EXC_INFO - 32 * 8)
+ str x30, [sp, #-16]!
+ bl _hv_entry
+ bl hv_exc_serr
+
+ b _hv_return
+
+.extern hv_saved_sp
+
+.globl hv_enter_guest
+.type hv_enter_guest, @function
+hv_enter_guest:
+ stp x29, x30, [sp, #-16]!
+ stp x27, x28, [sp, #-16]!
+ stp x25, x26, [sp, #-16]!
+ stp x23, x24, [sp, #-16]!
+ stp x21, x22, [sp, #-16]!
+ stp x19, x20, [sp, #-16]!
+ str x18, [sp, #-16]!
+
+ mrs x7, tpidr_el2
+ ldr x6, =hv_saved_sp
+ mov x5, sp
+ str x5, [x6, x7, LSL #3]
+
+ mrs x5, daif
+ mov x6, #5
+ orr x5, x5, x6 // EL1h
+ msr spsr_el2, x5
+
+ msr elr_el2, x4
+ mov x5, #0
+ msr sp_el0, x5
+ msr sp_el1, x5
+
+ eret
+
+.globl hv_exit_guest
+.type hv_exit_guest, @function
+hv_exit_guest:
+ mrs x7, tpidr_el2
+ ldr x6, =hv_saved_sp
+ ldr x5, [x6, x7, LSL #3]
+ mov sp, x5
+
+ ldr x18, [sp], #16
+ ldp x19, x20, [sp], #16
+ ldp x21, x22, [sp], #16
+ ldp x23, x24, [sp], #16
+ ldp x25, x26, [sp], #16
+ ldp x27, x28, [sp], #16
+ ldp x29, x30, [sp], #16
+
+ ret
diff --git a/tools/src/hv_exc.c b/tools/src/hv_exc.c
new file mode 100644
index 0000000..fc750c1
--- /dev/null
+++ b/tools/src/hv_exc.c
@@ -0,0 +1,515 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "hv.h"
+#include "assert.h"
+#include "cpu_regs.h"
+#include "exception.h"
+#include "smp.h"
+#include "string.h"
+#include "uart.h"
+#include "uartproxy.h"
+
+#define TIME_ACCOUNTING
+
+extern spinlock_t bhl;
+
+#define _SYSREG_ISS(_1, _2, op0, op1, CRn, CRm, op2) \
+ (((op0) << ESR_ISS_MSR_OP0_SHIFT) | ((op1) << ESR_ISS_MSR_OP1_SHIFT) | \
+ ((CRn) << ESR_ISS_MSR_CRn_SHIFT) | ((CRm) << ESR_ISS_MSR_CRm_SHIFT) | \
+ ((op2) << ESR_ISS_MSR_OP2_SHIFT))
+#define SYSREG_ISS(...) _SYSREG_ISS(__VA_ARGS__)
+
+#define PERCPU(x) pcpu[mrs(TPIDR_EL2)].x
+
+struct hv_pcpu_data {
+ u32 ipi_queued;
+ u32 ipi_pending;
+ u32 pmc_pending;
+ u64 pmc_irq_mode;
+ u64 exc_entry_pmcr0_cnt;
+} ALIGNED(64);
+
+struct hv_pcpu_data pcpu[MAX_CPUS];
+
+void hv_exit_guest(void) __attribute__((noreturn));
+
+static u64 stolen_time = 0;
+static u64 exc_entry_time;
+
+extern u32 hv_cpus_in_guest;
+extern int hv_pinned_cpu;
+extern int hv_want_cpu;
+
+static bool time_stealing = true;
+
+static void _hv_exc_proxy(struct exc_info *ctx, uartproxy_boot_reason_t reason, u32 type,
+ void *extra)
+{
+ int from_el = FIELD_GET(SPSR_M, ctx->spsr) >> 2;
+
+ hv_wdt_breadcrumb('P');
+
+ /*
+ * Get all the CPUs into the HV before running the proxy, to make sure they all exit to
+ * the guest with a consistent time offset.
+ */
+ if (time_stealing)
+ hv_rendezvous();
+
+ u64 entry_time = mrs(CNTPCT_EL0);
+
+ ctx->elr_phys = hv_translate(ctx->elr, false, false, NULL);
+ ctx->far_phys = hv_translate(ctx->far, false, false, NULL);
+ ctx->sp_phys = hv_translate(from_el == 0 ? ctx->sp[0] : ctx->sp[1], false, false, NULL);
+ ctx->extra = extra;
+
+ struct uartproxy_msg_start start = {
+ .reason = reason,
+ .code = type,
+ .info = ctx,
+ };
+
+ hv_wdt_suspend();
+ int ret = uartproxy_run(&start);
+ hv_wdt_resume();
+
+ switch (ret) {
+ case EXC_RET_HANDLED:
+ hv_wdt_breadcrumb('p');
+ if (time_stealing) {
+ u64 lost = mrs(CNTPCT_EL0) - entry_time;
+ stolen_time += lost;
+ }
+ break;
+ case EXC_EXIT_GUEST:
+ hv_rendezvous();
+ spin_unlock(&bhl);
+ hv_exit_guest(); // does not return
+ default:
+ printf("Guest exception not handled, rebooting.\n");
+ print_regs(ctx->regs, 0);
+ flush_and_reboot(); // does not return
+ }
+}
+
+static void hv_maybe_switch_cpu(struct exc_info *ctx, uartproxy_boot_reason_t reason, u32 type,
+ void *extra)
+{
+ while (hv_want_cpu != -1) {
+ if (hv_want_cpu == smp_id()) {
+ hv_want_cpu = -1;
+ _hv_exc_proxy(ctx, reason, type, extra);
+ } else {
+ // Unlock the HV so the target CPU can get into the proxy
+ spin_unlock(&bhl);
+ while (hv_want_cpu != -1)
+ sysop("dmb sy");
+ spin_lock(&bhl);
+ }
+ }
+}
+
+void hv_exc_proxy(struct exc_info *ctx, uartproxy_boot_reason_t reason, u32 type, void *extra)
+{
+ /*
+ * Wait while another CPU is pinned or being switched to.
+ * If a CPU switch is requested, handle it before actually handling the
+ * exception. We still tell the host the real reason code, though.
+ */
+ while ((hv_pinned_cpu != -1 && hv_pinned_cpu != smp_id()) || hv_want_cpu != -1) {
+ if (hv_want_cpu == smp_id()) {
+ hv_want_cpu = -1;
+ _hv_exc_proxy(ctx, reason, type, extra);
+ } else {
+ // Unlock the HV so the target CPU can get into the proxy
+ spin_unlock(&bhl);
+ while ((hv_pinned_cpu != -1 && hv_pinned_cpu != smp_id()) || hv_want_cpu != -1)
+ sysop("dmb sy");
+ spin_lock(&bhl);
+ }
+ }
+
+ /* Handle the actual exception */
+ _hv_exc_proxy(ctx, reason, type, extra);
+
+ /*
+ * If as part of handling this exception we want to switch CPUs, handle it without returning
+ * to the guest.
+ */
+ hv_maybe_switch_cpu(ctx, reason, type, extra);
+}
+
+void hv_set_time_stealing(bool enabled, bool reset)
+{
+ time_stealing = enabled;
+ if (reset)
+ stolen_time = 0;
+}
+
+static void hv_update_fiq(void)
+{
+ u64 hcr = mrs(HCR_EL2);
+ bool fiq_pending = false;
+
+ if (mrs(CNTP_CTL_EL02) == (CNTx_CTL_ISTATUS | CNTx_CTL_ENABLE)) {
+ fiq_pending = true;
+ reg_clr(SYS_IMP_APL_VM_TMR_FIQ_ENA_EL2, VM_TMR_FIQ_ENA_ENA_P);
+ } else {
+ reg_set(SYS_IMP_APL_VM_TMR_FIQ_ENA_EL2, VM_TMR_FIQ_ENA_ENA_P);
+ }
+
+ if (mrs(CNTV_CTL_EL02) == (CNTx_CTL_ISTATUS | CNTx_CTL_ENABLE)) {
+ fiq_pending = true;
+ reg_clr(SYS_IMP_APL_VM_TMR_FIQ_ENA_EL2, VM_TMR_FIQ_ENA_ENA_V);
+ } else {
+ reg_set(SYS_IMP_APL_VM_TMR_FIQ_ENA_EL2, VM_TMR_FIQ_ENA_ENA_V);
+ }
+
+ fiq_pending |= PERCPU(ipi_pending) || PERCPU(pmc_pending);
+
+ sysop("isb");
+
+ if ((hcr & HCR_VF) && !fiq_pending) {
+ hv_write_hcr(hcr & ~HCR_VF);
+ } else if (!(hcr & HCR_VF) && fiq_pending) {
+ hv_write_hcr(hcr | HCR_VF);
+ }
+}
+
+#define SYSREG_MAP(sr, to) \
+ case SYSREG_ISS(sr): \
+ if (is_read) \
+ regs[rt] = _mrs(sr_tkn(to)); \
+ else \
+ _msr(sr_tkn(to), regs[rt]); \
+ return true;
+
+#define SYSREG_PASS(sr) \
+ case SYSREG_ISS(sr): \
+ if (is_read) \
+ regs[rt] = _mrs(sr_tkn(sr)); \
+ else \
+ _msr(sr_tkn(sr), regs[rt]); \
+ return true;
+
+static bool hv_handle_msr(struct exc_info *ctx, u64 iss)
+{
+ u64 reg = iss & (ESR_ISS_MSR_OP0 | ESR_ISS_MSR_OP2 | ESR_ISS_MSR_OP1 | ESR_ISS_MSR_CRn |
+ ESR_ISS_MSR_CRm);
+ u64 rt = FIELD_GET(ESR_ISS_MSR_Rt, iss);
+ bool is_read = iss & ESR_ISS_MSR_DIR;
+
+ u64 *regs = ctx->regs;
+
+ regs[31] = 0;
+
+ switch (reg) {
+ /* Some kind of timer */
+ SYSREG_PASS(sys_reg(3, 7, 15, 1, 1));
+ SYSREG_PASS(sys_reg(3, 7, 15, 3, 1));
+ /* Spammy stuff seen on t600x p-cores */
+ SYSREG_PASS(sys_reg(3, 2, 15, 12, 0));
+ SYSREG_PASS(sys_reg(3, 2, 15, 13, 0));
+ SYSREG_PASS(sys_reg(3, 2, 15, 14, 0));
+ SYSREG_PASS(sys_reg(3, 2, 15, 15, 0));
+ SYSREG_PASS(sys_reg(3, 1, 15, 7, 0));
+ SYSREG_PASS(sys_reg(3, 1, 15, 8, 0));
+ SYSREG_PASS(sys_reg(3, 1, 15, 9, 0));
+ SYSREG_PASS(sys_reg(3, 1, 15, 10, 0));
+ /* Noisy traps */
+ SYSREG_MAP(SYS_ACTLR_EL1, SYS_IMP_APL_ACTLR_EL12)
+ SYSREG_PASS(SYS_IMP_APL_HID4)
+ SYSREG_PASS(SYS_IMP_APL_EHID4)
+ /* We don't normally trap hese, but if we do, they're noisy */
+ SYSREG_PASS(SYS_IMP_APL_GXF_STATUS_EL1)
+ SYSREG_PASS(SYS_IMP_APL_CNTVCT_ALIAS_EL0)
+ SYSREG_PASS(SYS_IMP_APL_TPIDR_GL1)
+ SYSREG_MAP(SYS_IMP_APL_SPSR_GL1, SYS_IMP_APL_SPSR_GL12)
+ SYSREG_MAP(SYS_IMP_APL_ASPSR_GL1, SYS_IMP_APL_ASPSR_GL12)
+ SYSREG_MAP(SYS_IMP_APL_ELR_GL1, SYS_IMP_APL_ELR_GL12)
+ SYSREG_MAP(SYS_IMP_APL_ESR_GL1, SYS_IMP_APL_ESR_GL12)
+ SYSREG_MAP(SYS_IMP_APL_SPRR_PERM_EL1, SYS_IMP_APL_SPRR_PERM_EL12)
+ SYSREG_MAP(SYS_IMP_APL_APCTL_EL1, SYS_IMP_APL_APCTL_EL12)
+ SYSREG_MAP(SYS_IMP_APL_AMX_CTL_EL1, SYS_IMP_APL_AMX_CTL_EL12)
+ /* FIXME:Might be wrong */
+ SYSREG_PASS(sys_reg(3, 4, 15, 1, 3))
+ /* pass through PMU handling */
+ SYSREG_PASS(SYS_IMP_APL_PMCR1)
+ SYSREG_PASS(SYS_IMP_APL_PMCR2)
+ SYSREG_PASS(SYS_IMP_APL_PMCR3)
+ SYSREG_PASS(SYS_IMP_APL_PMCR4)
+ SYSREG_PASS(SYS_IMP_APL_PMESR0)
+ SYSREG_PASS(SYS_IMP_APL_PMESR1)
+ SYSREG_PASS(SYS_IMP_APL_PMSR)
+#ifndef DEBUG_PMU_IRQ
+ SYSREG_PASS(SYS_IMP_APL_PMC0)
+#endif
+ SYSREG_PASS(SYS_IMP_APL_PMC1)
+ SYSREG_PASS(SYS_IMP_APL_PMC2)
+ SYSREG_PASS(SYS_IMP_APL_PMC3)
+ SYSREG_PASS(SYS_IMP_APL_PMC4)
+ SYSREG_PASS(SYS_IMP_APL_PMC5)
+ SYSREG_PASS(SYS_IMP_APL_PMC6)
+ SYSREG_PASS(SYS_IMP_APL_PMC7)
+ SYSREG_PASS(SYS_IMP_APL_PMC8)
+ SYSREG_PASS(SYS_IMP_APL_PMC9)
+
+ /* Outer Sharable TLB maintenance instructions */
+ SYSREG_PASS(sys_reg(1, 0, 8, 1, 0)) // TLBI VMALLE1OS
+ SYSREG_PASS(sys_reg(1, 0, 8, 1, 1)) // TLBI VAE1OS
+ SYSREG_PASS(sys_reg(1, 0, 8, 1, 2)) // TLBI ASIDE1OS
+ SYSREG_PASS(sys_reg(1, 0, 8, 5, 1)) // TLBI RVAE1OS
+
+ /*
+ * Handle this one here because m1n1/Linux (will) use it for explicit cpuidle.
+ * We can pass it through; going into deep sleep doesn't break the HV since we
+ * don't do any wfis that assume otherwise in m1n1. However, don't het macOS
+ * disable WFI ret (when going into systemwide sleep), since that breaks things.
+ */
+ case SYSREG_ISS(SYS_IMP_APL_CYC_OVRD):
+ if (is_read) {
+ regs[rt] = mrs(SYS_IMP_APL_CYC_OVRD);
+ } else {
+ msr(SYS_IMP_APL_CYC_OVRD, regs[rt] & ~CYC_OVRD_DISABLE_WFI_RET);
+ if (regs[rt] & CYC_OVRD_DISABLE_WFI_RET)
+ printf("msr(SYS_IMP_APL_CYC_OVRD, 0x%08lx): Filtered WFI RET disable\n",
+ regs[rt]);
+ }
+ return true;
+ /* clang-format off */
+
+ /* IPI handling */
+ SYSREG_PASS(SYS_IMP_APL_IPI_CR_EL1)
+ /* clang-format on */
+ case SYSREG_ISS(SYS_IMP_APL_IPI_RR_LOCAL_EL1): {
+ assert(!is_read);
+ u64 mpidr = (regs[rt] & 0xff) | (mrs(MPIDR_EL1) & 0xffff00);
+ msr(SYS_IMP_APL_IPI_RR_LOCAL_EL1, regs[rt]);
+ for (int i = 0; i < MAX_CPUS; i++)
+ if (mpidr == smp_get_mpidr(i))
+ pcpu[i].ipi_queued = true;
+ return true;
+ }
+ case SYSREG_ISS(SYS_IMP_APL_IPI_RR_GLOBAL_EL1):
+ assert(!is_read);
+ u64 mpidr = (regs[rt] & 0xff) | ((regs[rt] & 0xff0000) >> 8);
+ msr(SYS_IMP_APL_IPI_RR_GLOBAL_EL1, regs[rt]);
+ for (int i = 0; i < MAX_CPUS; i++) {
+ if (mpidr == (smp_get_mpidr(i) & 0xffff))
+ pcpu[i].ipi_queued = true;
+ }
+ return true;
+ case SYSREG_ISS(SYS_IMP_APL_IPI_SR_EL1):
+ if (is_read)
+ regs[rt] = PERCPU(ipi_pending) ? IPI_SR_PENDING : 0;
+ else if (regs[rt] & IPI_SR_PENDING)
+ PERCPU(ipi_pending) = false;
+ return true;
+ /* shadow the interrupt mode and state flag */
+ case SYSREG_ISS(SYS_IMP_APL_PMCR0):
+ if (is_read) {
+ u64 val = (mrs(SYS_IMP_APL_PMCR0) & ~PMCR0_IMODE_MASK) | PERCPU(pmc_irq_mode);
+ regs[rt] =
+ val | (PERCPU(pmc_pending) ? PMCR0_IACT : 0) | PERCPU(exc_entry_pmcr0_cnt);
+ } else {
+ PERCPU(pmc_pending) = !!(regs[rt] & PMCR0_IACT);
+ PERCPU(pmc_irq_mode) = regs[rt] & PMCR0_IMODE_MASK;
+ PERCPU(exc_entry_pmcr0_cnt) = regs[rt] & PMCR0_CNT_MASK;
+ msr(SYS_IMP_APL_PMCR0, regs[rt] & ~PERCPU(exc_entry_pmcr0_cnt));
+ }
+ return true;
+#ifdef DEBUG_PMU_IRQ
+ case SYSREG_ISS(SYS_IMP_APL_PMC0):
+ if (is_read) {
+ regs[rt] = mrs(SYS_IMP_APL_PMC0);
+ } else {
+ msr(SYS_IMP_APL_PMC0, regs[rt]);
+ printf("msr(SYS_IMP_APL_PMC0, 0x%04lx_%08lx)\n", regs[rt] >> 32,
+ regs[rt] & 0xFFFFFFFF);
+ }
+ return true;
+#endif
+ /* M1RACLES reg, handle here due to silly 12.0 "mitigation" */
+ case SYSREG_ISS(sys_reg(3, 5, 15, 10, 1)):
+ if (is_read)
+ regs[rt] = 0;
+ return true;
+ }
+
+ return false;
+}
+
+static void hv_exc_entry(struct exc_info *ctx)
+{
+ ctx->spsr = hv_get_spsr();
+ ctx->elr = hv_get_elr();
+ ctx->esr = hv_get_esr();
+ ctx->far = hv_get_far();
+ ctx->afsr1 = hv_get_afsr1();
+ ctx->sp[0] = mrs(SP_EL0);
+ ctx->sp[1] = mrs(SP_EL1);
+ ctx->sp[2] = (u64)ctx;
+ ctx->cpu_id = smp_id();
+ ctx->mpidr = mrs(MPIDR_EL1);
+
+ sysop("isb");
+
+ // Enable SErrors in the HV, but only if not already pending
+ if (!(mrs(ISR_EL1) & 0x100))
+ sysop("msr daifclr, 4");
+
+ __atomic_sub_fetch(&hv_cpus_in_guest, 1, __ATOMIC_ACQUIRE);
+ spin_lock(&bhl);
+ hv_wdt_breadcrumb('X');
+ exc_entry_time = mrs(CNTPCT_EL0);
+ /* disable PMU counters in the hypervisor */
+ u64 pmcr0 = mrs(SYS_IMP_APL_PMCR0);
+ PERCPU(exc_entry_pmcr0_cnt) = pmcr0 & PMCR0_CNT_MASK;
+ msr(SYS_IMP_APL_PMCR0, pmcr0 & ~PMCR0_CNT_MASK);
+}
+
+static void hv_exc_exit(struct exc_info *ctx)
+{
+ hv_wdt_breadcrumb('x');
+ hv_update_fiq();
+ /* reenable PMU counters */
+ reg_set(SYS_IMP_APL_PMCR0, PERCPU(exc_entry_pmcr0_cnt));
+ msr(CNTVOFF_EL2, stolen_time);
+ spin_unlock(&bhl);
+ __atomic_add_fetch(&hv_cpus_in_guest, 1, __ATOMIC_ACQUIRE);
+
+ hv_set_spsr(ctx->spsr);
+ hv_set_elr(ctx->elr);
+ msr(SP_EL0, ctx->sp[0]);
+ msr(SP_EL1, ctx->sp[1]);
+}
+
+void hv_exc_sync(struct exc_info *ctx)
+{
+ hv_wdt_breadcrumb('S');
+ hv_exc_entry(ctx);
+ bool handled = false;
+ u32 ec = FIELD_GET(ESR_EC, ctx->esr);
+
+ switch (ec) {
+ case ESR_EC_DABORT_LOWER:
+ hv_wdt_breadcrumb('D');
+ handled = hv_handle_dabort(ctx);
+ break;
+ case ESR_EC_MSR:
+ hv_wdt_breadcrumb('M');
+ handled = hv_handle_msr(ctx, FIELD_GET(ESR_ISS, ctx->esr));
+ break;
+ case ESR_EC_IMPDEF:
+ hv_wdt_breadcrumb('A');
+ switch (FIELD_GET(ESR_ISS, ctx->esr)) {
+ case ESR_ISS_IMPDEF_MSR:
+ handled = hv_handle_msr(ctx, ctx->afsr1);
+ break;
+ }
+ break;
+ }
+
+ if (handled) {
+ hv_wdt_breadcrumb('+');
+ ctx->elr += 4;
+ } else {
+ hv_wdt_breadcrumb('-');
+ // VM code can forward a nested SError exception here
+ if (FIELD_GET(ESR_EC, ctx->esr) == ESR_EC_SERROR)
+ hv_exc_proxy(ctx, START_EXCEPTION_LOWER, EXC_SERROR, NULL);
+ else
+ hv_exc_proxy(ctx, START_EXCEPTION_LOWER, EXC_SYNC, NULL);
+ }
+
+ hv_exc_exit(ctx);
+ hv_wdt_breadcrumb('s');
+}
+
+void hv_exc_irq(struct exc_info *ctx)
+{
+ hv_wdt_breadcrumb('I');
+ hv_exc_entry(ctx);
+ hv_exc_proxy(ctx, START_EXCEPTION_LOWER, EXC_IRQ, NULL);
+ hv_exc_exit(ctx);
+ hv_wdt_breadcrumb('i');
+}
+
+void hv_exc_fiq(struct exc_info *ctx)
+{
+ bool tick = false;
+
+ hv_maybe_exit();
+
+ if (mrs(CNTP_CTL_EL0) == (CNTx_CTL_ISTATUS | CNTx_CTL_ENABLE)) {
+ msr(CNTP_CTL_EL0, CNTx_CTL_ISTATUS | CNTx_CTL_IMASK | CNTx_CTL_ENABLE);
+ tick = true;
+ }
+
+ int interruptible_cpu = hv_pinned_cpu;
+ if (interruptible_cpu == -1)
+ interruptible_cpu = 0;
+
+ if (smp_id() != interruptible_cpu && !(mrs(ISR_EL1) & 0x40) && hv_want_cpu == -1) {
+ // Non-interruptible CPU and it was just a timer tick (or spurious), so just update FIQs
+ hv_update_fiq();
+ hv_arm_tick();
+ return;
+ }
+
+ // Slow (single threaded) path
+ hv_wdt_breadcrumb('F');
+ hv_exc_entry(ctx);
+
+ // Only poll for HV events in the interruptible CPU
+ if (tick) {
+ if (smp_id() == interruptible_cpu)
+ hv_tick(ctx);
+ hv_arm_tick();
+ }
+
+ if (mrs(CNTV_CTL_EL0) == (CNTx_CTL_ISTATUS | CNTx_CTL_ENABLE)) {
+ msr(CNTV_CTL_EL0, CNTx_CTL_ISTATUS | CNTx_CTL_IMASK | CNTx_CTL_ENABLE);
+ hv_exc_proxy(ctx, START_HV, HV_VTIMER, NULL);
+ }
+
+ u64 reg = mrs(SYS_IMP_APL_PMCR0);
+ if ((reg & (PMCR0_IMODE_MASK | PMCR0_IACT)) == (PMCR0_IMODE_FIQ | PMCR0_IACT)) {
+#ifdef DEBUG_PMU_IRQ
+ printf("[FIQ] PMC IRQ, masking and delivering to the guest\n");
+#endif
+ reg_clr(SYS_IMP_APL_PMCR0, PMCR0_IACT | PMCR0_IMODE_MASK);
+ PERCPU(pmc_pending) = true;
+ }
+
+ reg = mrs(SYS_IMP_APL_UPMCR0);
+ if ((reg & UPMCR0_IMODE_MASK) == UPMCR0_IMODE_FIQ && (mrs(SYS_IMP_APL_UPMSR) & UPMSR_IACT)) {
+ printf("[FIQ] UPMC IRQ, masking");
+ reg_clr(SYS_IMP_APL_UPMCR0, UPMCR0_IMODE_MASK);
+ hv_exc_proxy(ctx, START_EXCEPTION_LOWER, EXC_FIQ, NULL);
+ }
+
+ if (mrs(SYS_IMP_APL_IPI_SR_EL1) & IPI_SR_PENDING) {
+ if (PERCPU(ipi_queued)) {
+ PERCPU(ipi_pending) = true;
+ PERCPU(ipi_queued) = false;
+ }
+ msr(SYS_IMP_APL_IPI_SR_EL1, IPI_SR_PENDING);
+ sysop("isb");
+ }
+
+ hv_maybe_switch_cpu(ctx, START_HV, HV_CPU_SWITCH, NULL);
+
+ // Handles guest timers
+ hv_exc_exit(ctx);
+ hv_wdt_breadcrumb('f');
+}
+
+void hv_exc_serr(struct exc_info *ctx)
+{
+ hv_wdt_breadcrumb('E');
+ hv_exc_entry(ctx);
+ hv_exc_proxy(ctx, START_EXCEPTION_LOWER, EXC_SERROR, NULL);
+ hv_exc_exit(ctx);
+ hv_wdt_breadcrumb('e');
+}
diff --git a/tools/src/hv_virtio.c b/tools/src/hv_virtio.c
new file mode 100644
index 0000000..abe4582
--- /dev/null
+++ b/tools/src/hv_virtio.c
@@ -0,0 +1,308 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "hv.h"
+#include "aic.h"
+#include "iodev.h"
+#include "malloc.h"
+
+#define MAGIC 0x000
+#define VERSION 0x004
+#define DEVID 0x008
+#define VENDID 0x00c
+#define FEAT_HOST 0x010
+#define FEAT_HOST_SEL 0x014
+#define FEAT_GUEST 0x020
+#define FEAT_GUEST_SEL 0x024
+
+#define QSEL 0x030
+#define QMAX 0x034
+#define QSIZE 0x038
+#define QREADY 0x044
+#define QNOTIFY 0x050
+
+#define QDESC 0x080
+#define QGUESTAREA 0x090
+#define QHOSTAREA 0x0a0
+
+#define IRQ_STATUS 0x060
+#define USED_BUFFER BIT(0)
+#define CFG_CHANGE BIT(1)
+#define IRQ_ACK 0x064
+#define DEV_STATUS 0x070
+
+#define DESC_NEXT BIT(0)
+#define DESC_WRITE BIT(1)
+
+struct availring {
+ u16 flags;
+ u16 idx;
+ u16 ring[];
+};
+
+struct usedring {
+ u16 flags;
+ u16 idx;
+ struct {
+ u32 id;
+ u32 len;
+ } ring[];
+};
+
+struct desc {
+ u64 addr;
+ u32 len;
+ u16 flags;
+ u16 id;
+};
+
+struct virtio_q {
+ struct virtio_dev *host;
+ int idx;
+ u32 max;
+ u32 size;
+ bool ready;
+ struct desc *desc;
+
+ u16 avail_seen;
+ struct availring *avail;
+ struct usedring *used;
+
+ u64 area_regs[(QHOSTAREA + 8 - QDESC) / 4];
+};
+
+struct virtio_conf {
+ s32 irq;
+ u32 devid;
+ u64 feats;
+ u32 num_qus;
+ void *config;
+ u64 config_len;
+ u8 verbose;
+} PACKED;
+
+struct virtio_dev {
+ struct virtio_dev *next;
+ u64 base;
+ int irq;
+ int num_qus;
+ u32 devid;
+ u64 feats;
+ uint8_t *config;
+ size_t config_len;
+ bool verbose;
+
+ u32 feat_host_sel;
+ u32 status;
+ u32 irqstatus;
+
+ struct virtio_q *currq;
+ struct virtio_q qs[];
+};
+
+static struct virtio_dev *devlist;
+
+static void notify_avail(struct exc_info *ctx, struct virtio_q *q, int idx)
+{
+ struct desc *d = &q->desc[idx];
+ struct {
+ u64 devbase;
+ u16 qu;
+ u16 idx;
+ u32 pad;
+ u64 descbase;
+ } PACKED info = {
+ q->host->base, q->idx, idx, 0, (u64)q->desc,
+ };
+
+ if (q->host->verbose)
+ printf("virtio @ %lx: available %s buffer at %lx, size %x, flags %x\n", q->host->base,
+ (d->flags & DESC_WRITE) ? "device" : "driver", d->addr, d->len, d->flags);
+
+ hv_exc_proxy(ctx, START_HV, HV_VIRTIO, &info);
+}
+
+static void notify_buffers(struct exc_info *ctx, struct virtio_dev *dev, u32 qidx)
+{
+ struct virtio_q *q = &dev->qs[qidx];
+ struct availring *avail = q->avail;
+
+ if (qidx >= (u32)dev->num_qus)
+ return;
+
+ for (; avail->idx != q->avail_seen; q->avail_seen++)
+ notify_avail(ctx, q, avail->ring[q->avail_seen % q->size]);
+}
+
+static struct virtio_dev *dev_by_base(u64 base)
+{
+ struct virtio_dev *dev;
+
+ for (dev = devlist; dev; dev = dev->next)
+ if (dev->base == base)
+ break;
+
+ return dev;
+}
+
+void virtio_put_buffer(u64 base, int qu, u32 id, u32 len)
+{
+ struct virtio_dev *dev = dev_by_base(base);
+ struct virtio_q *q;
+ struct usedring *used;
+
+ if (!dev) {
+ printf("virtio_put_buffer: no device at %lx\n", base);
+ return;
+ }
+
+ q = &dev->qs[qu];
+ used = q->used;
+
+ used->ring[used->idx % q->size].id = id;
+ used->ring[used->idx % q->size].len = len;
+ used->idx++;
+
+ dev->irqstatus |= USED_BUFFER;
+ aic_set_sw(dev->irq, true);
+}
+
+static bool handle_virtio(struct exc_info *ctx, u64 addr, u64 *val, bool write, int width)
+{
+ struct virtio_dev *dev;
+ struct virtio_q *q;
+ UNUSED(ctx);
+ UNUSED(width);
+
+ dev = dev_by_base(addr & ~0xfff);
+ if (!dev)
+ return false;
+
+ addr &= 0xfff;
+
+ if (write) {
+ if (dev->verbose)
+ printf("virtio @ %lx: W 0x%lx <- 0x%lx (%d)\n", dev->base, addr, *val, width);
+
+ switch (addr) {
+ case DEV_STATUS:
+ dev->status = *val;
+ break;
+ case QSEL:
+ if (((int)*val) <= dev->num_qus)
+ dev->currq = &dev->qs[*val];
+ else
+ dev->currq = NULL;
+ break;
+ case QNOTIFY:
+ notify_buffers(ctx, dev, *val);
+ break;
+ case FEAT_HOST_SEL:
+ dev->feat_host_sel = *val;
+ break;
+ case IRQ_ACK:
+ dev->irqstatus &= ~(*val);
+ if (!dev->irqstatus)
+ aic_set_sw(dev->irq, false);
+ break;
+ }
+
+ q = dev->currq;
+ if (!q)
+ return true;
+
+ switch (addr) {
+ case QSIZE:
+ q->size = *val;
+ break;
+ case QREADY:
+ q->ready = *val & 1;
+ break;
+ case QDESC ... QHOSTAREA + 4:
+ addr -= QDESC;
+ addr /= 4;
+ q->area_regs[addr] = *val;
+
+ q->desc = (void *)(q->area_regs[1] << 32 | q->area_regs[0]);
+ q->avail = (void *)(q->area_regs[5] << 32 | q->area_regs[4]);
+ q->used = (void *)(q->area_regs[9] << 32 | q->area_regs[8]);
+ break;
+ }
+ } else {
+ switch (addr) {
+ case MAGIC:
+ *val = 0x74726976;
+ break;
+ case VERSION:
+ *val = 2;
+ break;
+ case DEVID:
+ *val = dev->devid;
+ break;
+ case DEV_STATUS:
+ *val = dev->status;
+ break;
+ case FEAT_HOST:
+ *val = dev->feats >> (dev->feat_host_sel * 32);
+ break;
+ case IRQ_STATUS:
+ *val = dev->irqstatus;
+ break;
+ case 0x100 ... 0x1000:
+ if (addr - 0x100 < dev->config_len)
+ *val = dev->config[addr - 0x100];
+ else
+ *val = 0;
+ break;
+ default:
+ q = dev->currq;
+ if (!q) {
+ *val = 0;
+ goto rdone;
+ }
+ }
+
+ switch (addr) {
+ case QMAX:
+ *val = q->max;
+ break;
+ case QREADY:
+ *val = q->ready;
+ break;
+ }
+ rdone:
+ if (dev->verbose)
+ printf("virtio @ %lx: R 0x%lx -> 0x%lx (%d)\n", dev->base, addr, *val, width);
+ };
+
+ return true;
+}
+
+void hv_map_virtio(u64 base, struct virtio_conf *conf)
+{
+ struct virtio_dev *dev;
+ int i;
+
+ dev = malloc(sizeof(*dev) + sizeof(struct virtio_q) * conf->num_qus);
+ dev->num_qus = conf->num_qus;
+ dev->base = base;
+ dev->irq = conf->irq;
+ dev->devid = conf->devid;
+ dev->currq = NULL;
+ dev->feats = conf->feats | BIT(32); /* always set: VIRTIO_F_VERSION_1 */
+ dev->config = conf->config;
+ dev->config_len = conf->config_len;
+ dev->verbose = conf->verbose;
+ for (i = 0; i < dev->num_qus; i++) {
+ dev->qs[i].host = dev;
+ dev->qs[i].idx = i;
+ dev->qs[i].max = 256;
+ dev->qs[i].avail_seen = 0;
+ dev->qs[i].ready = 0;
+ }
+
+ if (devlist)
+ dev->next = devlist;
+ devlist = dev;
+
+ hv_map_hook(base, handle_virtio, 0x1000);
+}
diff --git a/tools/src/hv_vm.c b/tools/src/hv_vm.c
new file mode 100644
index 0000000..671ef70
--- /dev/null
+++ b/tools/src/hv_vm.c
@@ -0,0 +1,1278 @@
+/* SPDX-License-Identifier: MIT */
+
+// #define DEBUG
+
+#include "hv.h"
+#include "assert.h"
+#include "cpu_regs.h"
+#include "exception.h"
+#include "iodev.h"
+#include "malloc.h"
+#include "smp.h"
+#include "string.h"
+#include "types.h"
+#include "uartproxy.h"
+#include "utils.h"
+
+#define PAGE_SIZE 0x4000
+#define CACHE_LINE_SIZE 64
+#define CACHE_LINE_LOG2 6
+
+#define PTE_ACCESS BIT(10)
+#define PTE_SH_NS (0b11L << 8)
+#define PTE_S2AP_RW (0b11L << 6)
+#define PTE_MEMATTR_UNCHANGED (0b1111L << 2)
+
+#define PTE_ATTRIBUTES (PTE_ACCESS | PTE_SH_NS | PTE_S2AP_RW | PTE_MEMATTR_UNCHANGED)
+
+#define PTE_LOWER_ATTRIBUTES GENMASK(13, 2)
+
+#define PTE_VALID BIT(0)
+#define PTE_TYPE BIT(1)
+#define PTE_BLOCK 0
+#define PTE_TABLE 1
+#define PTE_PAGE 1
+
+#define VADDR_L4_INDEX_BITS 12
+#define VADDR_L3_INDEX_BITS 11
+#define VADDR_L2_INDEX_BITS 11
+#define VADDR_L1_INDEX_BITS 8
+
+#define VADDR_L4_OFFSET_BITS 2
+#define VADDR_L3_OFFSET_BITS 14
+#define VADDR_L2_OFFSET_BITS 25
+#define VADDR_L1_OFFSET_BITS 36
+
+#define VADDR_L2_ALIGN_MASK GENMASK(VADDR_L2_OFFSET_BITS - 1, VADDR_L3_OFFSET_BITS)
+#define VADDR_L3_ALIGN_MASK GENMASK(VADDR_L3_OFFSET_BITS - 1, VADDR_L4_OFFSET_BITS)
+#define PTE_TARGET_MASK GENMASK(49, VADDR_L3_OFFSET_BITS)
+#define PTE_TARGET_MASK_L4 GENMASK(49, VADDR_L4_OFFSET_BITS)
+
+#define ENTRIES_PER_L1_TABLE BIT(VADDR_L1_INDEX_BITS)
+#define ENTRIES_PER_L2_TABLE BIT(VADDR_L2_INDEX_BITS)
+#define ENTRIES_PER_L3_TABLE BIT(VADDR_L3_INDEX_BITS)
+#define ENTRIES_PER_L4_TABLE BIT(VADDR_L4_INDEX_BITS)
+
+#define SPTE_TRACE_READ BIT(63)
+#define SPTE_TRACE_WRITE BIT(62)
+#define SPTE_TRACE_UNBUF BIT(61)
+#define SPTE_TYPE GENMASK(52, 50)
+#define SPTE_MAP 0
+#define SPTE_HOOK 1
+#define SPTE_PROXY_HOOK_R 2
+#define SPTE_PROXY_HOOK_W 3
+#define SPTE_PROXY_HOOK_RW 4
+
+#define IS_HW(pte) ((pte) && pte & PTE_VALID)
+#define IS_SW(pte) ((pte) && !(pte & PTE_VALID))
+
+#define L1_IS_TABLE(pte) ((pte) && FIELD_GET(PTE_TYPE, pte) == PTE_TABLE)
+
+#define L2_IS_TABLE(pte) ((pte) && FIELD_GET(PTE_TYPE, pte) == PTE_TABLE)
+#define L2_IS_NOT_TABLE(pte) ((pte) && !L2_IS_TABLE(pte))
+#define L2_IS_HW_BLOCK(pte) (IS_HW(pte) && FIELD_GET(PTE_TYPE, pte) == PTE_BLOCK)
+#define L2_IS_SW_BLOCK(pte) \
+ (IS_SW(pte) && FIELD_GET(PTE_TYPE, pte) == PTE_BLOCK && FIELD_GET(SPTE_TYPE, pte) == SPTE_MAP)
+#define L3_IS_TABLE(pte) (IS_SW(pte) && FIELD_GET(PTE_TYPE, pte) == PTE_TABLE)
+#define L3_IS_NOT_TABLE(pte) ((pte) && !L3_IS_TABLE(pte))
+#define L3_IS_HW_BLOCK(pte) (IS_HW(pte) && FIELD_GET(PTE_TYPE, pte) == PTE_PAGE)
+#define L3_IS_SW_BLOCK(pte) \
+ (IS_SW(pte) && FIELD_GET(PTE_TYPE, pte) == PTE_BLOCK && FIELD_GET(SPTE_TYPE, pte) == SPTE_MAP)
+
+uint64_t vaddr_bits;
+
+/*
+ * We use 16KB page tables for stage 2 translation, and a 64GB (36-bit) guest
+ * PA size, which results in the following virtual address space:
+ *
+ * [L2 index] [L3 index] [page offset]
+ * 11 bits 11 bits 14 bits
+ *
+ * 32MB L2 mappings look like this:
+ * [L2 index] [page offset]
+ * 11 bits 25 bits
+ *
+ * We implement sub-page granularity mappings for software MMIO hooks, which behave
+ * as an additional page table level used only by software. This works like this:
+ *
+ * [L2 index] [L3 index] [L4 index] [Word offset]
+ * 11 bits 11 bits 12 bits 2 bits
+ *
+ * Thus, L4 sub-page tables are twice the size.
+ *
+ * We use invalid mappings (PTE_VALID == 0) to represent mmiotrace descriptors, but
+ * otherwise the page table format is the same. The PTE_TYPE bit is weird, as 0 means
+ * block but 1 means both table (at L<3) and page (at L3). For mmiotrace, this is
+ * pushed to L4.
+ *
+ * On SoCs with more than 36-bit PA sizes there is an additional L1 translation level,
+ * but no blocks or software mappings are allowed there. This level can have up to 8 bits
+ * at this time.
+ */
+
+static u64 *hv_Ltop;
+
+void hv_pt_init(void)
+{
+ const uint64_t pa_bits[] = {32, 36, 40, 42, 44, 48, 52};
+ uint64_t pa_range = FIELD_GET(ID_AA64MMFR0_PARange, mrs(ID_AA64MMFR0_EL1));
+
+ vaddr_bits = min(44, pa_bits[pa_range]);
+
+ printf("HV: Initializing for %ld-bit PA range\n", vaddr_bits);
+
+ hv_Ltop = memalign(PAGE_SIZE, sizeof(u64) * ENTRIES_PER_L2_TABLE);
+ memset(hv_Ltop, 0, sizeof(u64) * ENTRIES_PER_L2_TABLE);
+
+ u64 sl0 = vaddr_bits > 36 ? 2 : 1;
+
+ msr(VTCR_EL2, FIELD_PREP(VTCR_PS, pa_range) | // Full PA size
+ FIELD_PREP(VTCR_TG0, 2) | // 16KB page size
+ FIELD_PREP(VTCR_SH0, 3) | // PTWs Inner Sharable
+ FIELD_PREP(VTCR_ORGN0, 1) | // PTWs Cacheable
+ FIELD_PREP(VTCR_IRGN0, 1) | // PTWs Cacheable
+ FIELD_PREP(VTCR_SL0, sl0) | // Start level
+ FIELD_PREP(VTCR_T0SZ, 64 - vaddr_bits)); // Translation region == PA
+
+ msr(VTTBR_EL2, hv_Ltop);
+}
+
+static u64 *hv_pt_get_l2(u64 from)
+{
+ u64 l1idx = from >> VADDR_L1_OFFSET_BITS;
+
+ if (vaddr_bits <= 36) {
+ assert(l1idx == 0);
+ return hv_Ltop;
+ }
+
+ u64 l1d = hv_Ltop[l1idx];
+
+ if (L1_IS_TABLE(l1d))
+ return (u64 *)(l1d & PTE_TARGET_MASK);
+
+ u64 *l2 = (u64 *)memalign(PAGE_SIZE, ENTRIES_PER_L2_TABLE * sizeof(u64));
+ memset64(l2, 0, ENTRIES_PER_L2_TABLE * sizeof(u64));
+
+ l1d = ((u64)l2) | FIELD_PREP(PTE_TYPE, PTE_TABLE) | PTE_VALID;
+ hv_Ltop[l1idx] = l1d;
+ return l2;
+}
+
+static void hv_pt_free_l3(u64 *l3)
+{
+ if (!l3)
+ return;
+
+ for (u64 idx = 0; idx < ENTRIES_PER_L3_TABLE; idx++)
+ if (IS_SW(l3[idx]) && FIELD_GET(PTE_TYPE, l3[idx]) == PTE_TABLE)
+ free((void *)(l3[idx] & PTE_TARGET_MASK));
+ free(l3);
+}
+
+static void hv_pt_map_l2(u64 from, u64 to, u64 size, u64 incr)
+{
+ assert((from & MASK(VADDR_L2_OFFSET_BITS)) == 0);
+ assert(IS_SW(to) || (to & PTE_TARGET_MASK & MASK(VADDR_L2_OFFSET_BITS)) == 0);
+ assert((size & MASK(VADDR_L2_OFFSET_BITS)) == 0);
+
+ to |= FIELD_PREP(PTE_TYPE, PTE_BLOCK);
+
+ for (; size; size -= BIT(VADDR_L2_OFFSET_BITS)) {
+ u64 *l2 = hv_pt_get_l2(from);
+ u64 idx = (from >> VADDR_L2_OFFSET_BITS) & MASK(VADDR_L2_INDEX_BITS);
+
+ if (L2_IS_TABLE(l2[idx]))
+ hv_pt_free_l3((u64 *)(l2[idx] & PTE_TARGET_MASK));
+
+ l2[idx] = to;
+ from += BIT(VADDR_L2_OFFSET_BITS);
+ to += incr * BIT(VADDR_L2_OFFSET_BITS);
+ }
+}
+
+static u64 *hv_pt_get_l3(u64 from)
+{
+ u64 *l2 = hv_pt_get_l2(from);
+ u64 l2idx = (from >> VADDR_L2_OFFSET_BITS) & MASK(VADDR_L2_INDEX_BITS);
+ u64 l2d = l2[l2idx];
+
+ if (L2_IS_TABLE(l2d))
+ return (u64 *)(l2d & PTE_TARGET_MASK);
+
+ u64 *l3 = (u64 *)memalign(PAGE_SIZE, ENTRIES_PER_L3_TABLE * sizeof(u64));
+ if (l2d) {
+ u64 incr = 0;
+ u64 l3d = l2d;
+ if (IS_HW(l2d)) {
+ l3d &= ~PTE_TYPE;
+ l3d |= FIELD_PREP(PTE_TYPE, PTE_PAGE);
+ incr = BIT(VADDR_L3_OFFSET_BITS);
+ } else if (IS_SW(l2d) && FIELD_GET(SPTE_TYPE, l3d) == SPTE_MAP) {
+ incr = BIT(VADDR_L3_OFFSET_BITS);
+ }
+ for (u64 idx = 0; idx < ENTRIES_PER_L3_TABLE; idx++, l3d += incr)
+ l3[idx] = l3d;
+ } else {
+ memset64(l3, 0, ENTRIES_PER_L3_TABLE * sizeof(u64));
+ }
+
+ l2d = ((u64)l3) | FIELD_PREP(PTE_TYPE, PTE_TABLE) | PTE_VALID;
+ l2[l2idx] = l2d;
+ return l3;
+}
+
+static void hv_pt_map_l3(u64 from, u64 to, u64 size, u64 incr)
+{
+ assert((from & MASK(VADDR_L3_OFFSET_BITS)) == 0);
+ assert(IS_SW(to) || (to & PTE_TARGET_MASK & MASK(VADDR_L3_OFFSET_BITS)) == 0);
+ assert((size & MASK(VADDR_L3_OFFSET_BITS)) == 0);
+
+ if (IS_HW(to))
+ to |= FIELD_PREP(PTE_TYPE, PTE_PAGE);
+ else
+ to |= FIELD_PREP(PTE_TYPE, PTE_BLOCK);
+
+ for (; size; size -= BIT(VADDR_L3_OFFSET_BITS)) {
+ u64 idx = (from >> VADDR_L3_OFFSET_BITS) & MASK(VADDR_L3_INDEX_BITS);
+ u64 *l3 = hv_pt_get_l3(from);
+
+ if (L3_IS_TABLE(l3[idx]))
+ free((void *)(l3[idx] & PTE_TARGET_MASK));
+
+ l3[idx] = to;
+ from += BIT(VADDR_L3_OFFSET_BITS);
+ to += incr * BIT(VADDR_L3_OFFSET_BITS);
+ }
+}
+
+static u64 *hv_pt_get_l4(u64 from)
+{
+ u64 *l3 = hv_pt_get_l3(from);
+ u64 l3idx = (from >> VADDR_L3_OFFSET_BITS) & MASK(VADDR_L3_INDEX_BITS);
+ u64 l3d = l3[l3idx];
+
+ if (L3_IS_TABLE(l3d)) {
+ return (u64 *)(l3d & PTE_TARGET_MASK);
+ }
+
+ if (IS_HW(l3d)) {
+ assert(FIELD_GET(PTE_TYPE, l3d) == PTE_PAGE);
+ l3d &= PTE_TARGET_MASK;
+ l3d |= FIELD_PREP(PTE_TYPE, PTE_BLOCK) | FIELD_PREP(SPTE_TYPE, SPTE_MAP);
+ }
+
+ u64 *l4 = (u64 *)memalign(PAGE_SIZE, ENTRIES_PER_L4_TABLE * sizeof(u64));
+ if (l3d) {
+ u64 incr = 0;
+ u64 l4d = l3d;
+ l4d &= ~PTE_TYPE;
+ l4d |= FIELD_PREP(PTE_TYPE, PTE_PAGE);
+ if (FIELD_GET(SPTE_TYPE, l4d) == SPTE_MAP)
+ incr = BIT(VADDR_L4_OFFSET_BITS);
+ for (u64 idx = 0; idx < ENTRIES_PER_L4_TABLE; idx++, l4d += incr)
+ l4[idx] = l4d;
+ } else {
+ memset64(l4, 0, ENTRIES_PER_L4_TABLE * sizeof(u64));
+ }
+
+ l3d = ((u64)l4) | FIELD_PREP(PTE_TYPE, PTE_TABLE);
+ l3[l3idx] = l3d;
+ return l4;
+}
+
+static void hv_pt_map_l4(u64 from, u64 to, u64 size, u64 incr)
+{
+ assert((from & MASK(VADDR_L4_OFFSET_BITS)) == 0);
+ assert((size & MASK(VADDR_L4_OFFSET_BITS)) == 0);
+
+ assert(!IS_HW(to));
+
+ if (IS_SW(to))
+ to |= FIELD_PREP(PTE_TYPE, PTE_PAGE);
+
+ for (; size; size -= BIT(VADDR_L4_OFFSET_BITS)) {
+ u64 idx = (from >> VADDR_L4_OFFSET_BITS) & MASK(VADDR_L4_INDEX_BITS);
+ u64 *l4 = hv_pt_get_l4(from);
+
+ l4[idx] = to;
+ from += BIT(VADDR_L4_OFFSET_BITS);
+ to += incr * BIT(VADDR_L4_OFFSET_BITS);
+ }
+}
+
+int hv_map(u64 from, u64 to, u64 size, u64 incr)
+{
+ u64 chunk;
+ bool hw = IS_HW(to);
+
+ if (from & MASK(VADDR_L4_OFFSET_BITS) || size & MASK(VADDR_L4_OFFSET_BITS))
+ return -1;
+
+ if (hw && (from & MASK(VADDR_L3_OFFSET_BITS) || size & MASK(VADDR_L3_OFFSET_BITS))) {
+ printf("HV: cannot use L4 pages with HW mappings (0x%lx -> 0x%lx)\n", from, to);
+ return -1;
+ }
+
+ // L4 mappings to boundary
+ chunk = min(size, ALIGN_UP(from, BIT(VADDR_L3_OFFSET_BITS)) - from);
+ if (chunk) {
+ assert(!hw);
+ hv_pt_map_l4(from, to, chunk, incr);
+ from += chunk;
+ to += incr * chunk;
+ size -= chunk;
+ }
+
+ // L3 mappings to boundary
+ chunk = ALIGN_DOWN(min(size, ALIGN_UP(from, BIT(VADDR_L2_OFFSET_BITS)) - from),
+ BIT(VADDR_L3_OFFSET_BITS));
+ if (chunk) {
+ hv_pt_map_l3(from, to, chunk, incr);
+ from += chunk;
+ to += incr * chunk;
+ size -= chunk;
+ }
+
+ // L2 mappings
+ chunk = ALIGN_DOWN(size, BIT(VADDR_L2_OFFSET_BITS));
+ if (chunk && (!hw || (to & VADDR_L2_ALIGN_MASK) == 0)) {
+ hv_pt_map_l2(from, to, chunk, incr);
+ from += chunk;
+ to += incr * chunk;
+ size -= chunk;
+ }
+
+ // L3 mappings to end
+ chunk = ALIGN_DOWN(size, BIT(VADDR_L3_OFFSET_BITS));
+ if (chunk) {
+ hv_pt_map_l3(from, to, chunk, incr);
+ from += chunk;
+ to += incr * chunk;
+ size -= chunk;
+ }
+
+ // L4 mappings to end
+ if (size) {
+ assert(!hw);
+ hv_pt_map_l4(from, to, size, incr);
+ }
+
+ return 0;
+}
+
+int hv_unmap(u64 from, u64 size)
+{
+ return hv_map(from, 0, size, 0);
+}
+
+int hv_map_hw(u64 from, u64 to, u64 size)
+{
+ return hv_map(from, to | PTE_ATTRIBUTES | PTE_VALID, size, 1);
+}
+
+int hv_map_sw(u64 from, u64 to, u64 size)
+{
+ return hv_map(from, to | FIELD_PREP(SPTE_TYPE, SPTE_MAP), size, 1);
+}
+
+int hv_map_hook(u64 from, hv_hook_t *hook, u64 size)
+{
+ return hv_map(from, ((u64)hook) | FIELD_PREP(SPTE_TYPE, SPTE_HOOK), size, 0);
+}
+
+u64 hv_translate(u64 addr, bool s1, bool w, u64 *par_out)
+{
+ if (!(mrs(SCTLR_EL12) & SCTLR_M))
+ return addr; // MMU off
+
+ u64 el = FIELD_GET(SPSR_M, hv_get_spsr()) >> 2;
+ u64 save = mrs(PAR_EL1);
+
+ if (w) {
+ if (s1) {
+ if (el == 0)
+ asm("at s1e0w, %0" : : "r"(addr));
+ else
+ asm("at s1e1w, %0" : : "r"(addr));
+ } else {
+ if (el == 0)
+ asm("at s12e0w, %0" : : "r"(addr));
+ else
+ asm("at s12e1w, %0" : : "r"(addr));
+ }
+ } else {
+ if (s1) {
+ if (el == 0)
+ asm("at s1e0r, %0" : : "r"(addr));
+ else
+ asm("at s1e1r, %0" : : "r"(addr));
+ } else {
+ if (el == 0)
+ asm("at s12e0r, %0" : : "r"(addr));
+ else
+ asm("at s12e1r, %0" : : "r"(addr));
+ }
+ }
+
+ u64 par = mrs(PAR_EL1);
+ if (par_out)
+ *par_out = par;
+ msr(PAR_EL1, save);
+
+ if (par & PAR_F) {
+ dprintf("hv_translate(0x%lx, %d, %d): fault 0x%lx\n", addr, s1, w, par);
+ return 0; // fault
+ } else {
+ return (par & PAR_PA) | (addr & 0xfff);
+ }
+}
+
+u64 hv_pt_walk(u64 addr)
+{
+ dprintf("hv_pt_walk(0x%lx)\n", addr);
+
+ u64 idx = addr >> VADDR_L1_OFFSET_BITS;
+ u64 *l2;
+ if (vaddr_bits > 36) {
+ assert(idx < ENTRIES_PER_L1_TABLE);
+
+ u64 l1d = hv_Ltop[idx];
+
+ dprintf(" l1d = 0x%lx\n", l2d);
+
+ if (!L1_IS_TABLE(l1d)) {
+ dprintf(" result: 0x%lx\n", l1d);
+ return l1d;
+ }
+ l2 = (u64 *)(l1d & PTE_TARGET_MASK);
+ } else {
+ assert(idx == 0);
+ l2 = hv_Ltop;
+ }
+
+ idx = (addr >> VADDR_L2_OFFSET_BITS) & MASK(VADDR_L2_INDEX_BITS);
+ u64 l2d = l2[idx];
+ dprintf(" l2d = 0x%lx\n", l2d);
+
+ if (!L2_IS_TABLE(l2d)) {
+ if (L2_IS_SW_BLOCK(l2d))
+ l2d += addr & (VADDR_L2_ALIGN_MASK | VADDR_L3_ALIGN_MASK);
+ if (L2_IS_HW_BLOCK(l2d)) {
+ l2d &= ~PTE_LOWER_ATTRIBUTES;
+ l2d |= addr & (VADDR_L2_ALIGN_MASK | VADDR_L3_ALIGN_MASK);
+ }
+
+ dprintf(" result: 0x%lx\n", l2d);
+ return l2d;
+ }
+
+ idx = (addr >> VADDR_L3_OFFSET_BITS) & MASK(VADDR_L3_INDEX_BITS);
+ u64 l3d = ((u64 *)(l2d & PTE_TARGET_MASK))[idx];
+ dprintf(" l3d = 0x%lx\n", l3d);
+
+ if (!L3_IS_TABLE(l3d)) {
+ if (L3_IS_SW_BLOCK(l3d))
+ l3d += addr & VADDR_L3_ALIGN_MASK;
+ if (L3_IS_HW_BLOCK(l3d)) {
+ l3d &= ~PTE_LOWER_ATTRIBUTES;
+ l3d |= addr & VADDR_L3_ALIGN_MASK;
+ }
+ dprintf(" result: 0x%lx\n", l3d);
+ return l3d;
+ }
+
+ idx = (addr >> VADDR_L4_OFFSET_BITS) & MASK(VADDR_L4_INDEX_BITS);
+ dprintf(" l4 idx = 0x%lx\n", idx);
+ u64 l4d = ((u64 *)(l3d & PTE_TARGET_MASK))[idx];
+ dprintf(" l4d = 0x%lx\n", l4d);
+ return l4d;
+}
+
+#define CHECK_RN \
+ if (Rn == 31) \
+ return false
+#define DECODE_OK \
+ if (!val) \
+ return true
+
+#define EXT(n, b) (((s32)(((u32)(n)) << (32 - (b)))) >> (32 - (b)))
+
+union simd_reg {
+ u64 d[2];
+ u32 s[4];
+ u16 h[8];
+ u8 b[16];
+};
+
+static bool emulate_load(struct exc_info *ctx, u32 insn, u64 *val, u64 *width, u64 *vaddr)
+{
+ u64 Rt = insn & 0x1f;
+ u64 Rn = (insn >> 5) & 0x1f;
+ u64 imm12 = EXT((insn >> 10) & 0xfff, 12);
+ u64 imm9 = EXT((insn >> 12) & 0x1ff, 9);
+ u64 imm7 = EXT((insn >> 15) & 0x7f, 7);
+ u64 *regs = ctx->regs;
+
+ union simd_reg simd[32];
+
+ *width = insn >> 30;
+
+ if (val)
+ dprintf("emulate_load(%p, 0x%08x, 0x%08lx, %ld\n", regs, insn, *val, *width);
+
+ if ((insn & 0x3fe00400) == 0x38400400) {
+ // LDRx (immediate) Pre/Post-index
+ CHECK_RN;
+ DECODE_OK;
+ regs[Rn] += imm9;
+ regs[Rt] = *val;
+ } else if ((insn & 0x3fc00000) == 0x39400000) {
+ // LDRx (immediate) Unsigned offset
+ DECODE_OK;
+ regs[Rt] = *val;
+ } else if ((insn & 0x3fa00400) == 0x38800400) {
+ // LDRSx (immediate) Pre/Post-index
+ CHECK_RN;
+ DECODE_OK;
+ regs[Rn] += imm9;
+ regs[Rt] = (s64)EXT(*val, 8 << *width);
+ if (insn & (1 << 22))
+ regs[Rt] &= 0xffffffff;
+ } else if ((insn & 0x3fa00000) == 0x39800000) {
+ // LDRSx (immediate) Unsigned offset
+ DECODE_OK;
+ regs[Rt] = (s64)EXT(*val, 8 << *width);
+ if (insn & (1 << 22))
+ regs[Rt] &= 0xffffffff;
+ } else if ((insn & 0x3fe04c00) == 0x38604800) {
+ // LDRx (register)
+ DECODE_OK;
+ regs[Rt] = *val;
+ } else if ((insn & 0x3fa04c00) == 0x38a04800) {
+ // LDRSx (register)
+ DECODE_OK;
+ regs[Rt] = (s64)EXT(*val, 8 << *width);
+ if (insn & (1 << 22))
+ regs[Rt] &= 0xffffffff;
+ } else if ((insn & 0x3fe00c00) == 0x38400000) {
+ // LDURx (unscaled)
+ DECODE_OK;
+ regs[Rt] = *val;
+ } else if ((insn & 0x3fa00c00) == 0x38a00000) {
+ // LDURSx (unscaled)
+ DECODE_OK;
+ regs[Rt] = (s64)EXT(*val, (8 << *width));
+ if (insn & (1 << 22))
+ regs[Rt] &= 0xffffffff;
+ } else if ((insn & 0xffc00000) == 0x29400000) {
+ // LDP (Signed offset, 32-bit)
+ *width = 3;
+ *vaddr = regs[Rn] + (imm7 * 4);
+ DECODE_OK;
+ u64 Rt2 = (insn >> 10) & 0x1f;
+ regs[Rt] = val[0] & 0xffffffff;
+ regs[Rt2] = val[0] >> 32;
+ } else if ((insn & 0xffc00000) == 0xa9400000) {
+ // LDP (Signed offset, 64-bit)
+ *width = 4;
+ *vaddr = regs[Rn] + (imm7 * 8);
+ DECODE_OK;
+ u64 Rt2 = (insn >> 10) & 0x1f;
+ regs[Rt] = val[0];
+ regs[Rt2] = val[1];
+ } else if ((insn & 0xfec00000) == 0xa8c00000) {
+ // LDP (pre/post-increment, 64-bit)
+ *width = 4;
+ *vaddr = regs[Rn] + ((insn & BIT(24)) ? (imm7 * 8) : 0);
+ DECODE_OK;
+ regs[Rn] += imm7 * 8;
+ u64 Rt2 = (insn >> 10) & 0x1f;
+ regs[Rt] = val[0];
+ regs[Rt2] = val[1];
+ } else if ((insn & 0xfec00000) == 0xac400000) {
+ // LD[N]P (SIMD&FP, 128-bit) Signed offset
+ *width = 5;
+ *vaddr = regs[Rn] + (imm7 * 16);
+ DECODE_OK;
+ u64 Rt2 = (insn >> 10) & 0x1f;
+ get_simd_state(simd);
+ simd[Rt].d[0] = val[0];
+ simd[Rt].d[1] = val[1];
+ simd[Rt2].d[0] = val[2];
+ simd[Rt2].d[1] = val[3];
+ put_simd_state(simd);
+ } else if ((insn & 0x3fc00000) == 0x3d400000) {
+ // LDR (immediate, SIMD&FP) Unsigned offset
+ *vaddr = regs[Rn] + (imm12 << *width);
+ DECODE_OK;
+ get_simd_state(simd);
+ simd[Rt].d[0] = val[0];
+ simd[Rt].d[1] = 0;
+ put_simd_state(simd);
+ } else if ((insn & 0xffc00000) == 0x3dc00000) {
+ // LDR (immediate, SIMD&FP) Unsigned offset, 128-bit
+ *width = 4;
+ *vaddr = regs[Rn] + (imm12 << *width);
+ DECODE_OK;
+ get_simd_state(simd);
+ simd[Rt].d[0] = val[0];
+ simd[Rt].d[1] = val[1];
+ put_simd_state(simd);
+ } else if ((insn & 0xffe00c00) == 0x3cc00000) {
+ // LDURx (unscaled, SIMD&FP, 128-bit)
+ *width = 4;
+ *vaddr = regs[Rn] + (imm9 << *width);
+ DECODE_OK;
+ get_simd_state(simd);
+ simd[Rt].d[0] = val[0];
+ simd[Rt].d[1] = val[1];
+ put_simd_state(simd);
+ } else if ((insn & 0x3fe00400) == 0x3c400400) {
+ // LDR (immediate, SIMD&FP) Pre/Post-index
+ CHECK_RN;
+ DECODE_OK;
+ regs[Rn] += imm9;
+ get_simd_state(simd);
+ simd[Rt].d[0] = val[0];
+ simd[Rt].d[1] = 0;
+ put_simd_state(simd);
+ } else if ((insn & 0xffe00400) == 0x3cc00400) {
+ // LDR (immediate, SIMD&FP) Pre/Post-index, 128-bit
+ *width = 4;
+ CHECK_RN;
+ DECODE_OK;
+ regs[Rn] += imm9;
+ get_simd_state(simd);
+ simd[Rt].d[0] = val[0];
+ simd[Rt].d[1] = val[1];
+ put_simd_state(simd);
+ } else if ((insn & 0x3fe04c00) == 0x3c604800) {
+ // LDR (register, SIMD&FP)
+ DECODE_OK;
+ get_simd_state(simd);
+ simd[Rt].d[0] = val[0];
+ simd[Rt].d[1] = 0;
+ put_simd_state(simd);
+ } else if ((insn & 0xffe04c00) == 0x3ce04800) {
+ // LDR (register, SIMD&FP), 128-bit
+ *width = 4;
+ DECODE_OK;
+ get_simd_state(simd);
+ simd[Rt].d[0] = val[0];
+ simd[Rt].d[1] = val[1];
+ put_simd_state(simd);
+ } else if ((insn & 0xbffffc00) == 0x0d408400) {
+ // LD1 (single structure) No offset, 64-bit
+ *width = 3;
+ DECODE_OK;
+ u64 index = (insn >> 30) & 1;
+ get_simd_state(simd);
+ simd[Rt].d[index] = val[0];
+ put_simd_state(simd);
+ } else if ((insn & 0x3ffffc00) == 0x08dffc00) {
+ // LDAR*
+ DECODE_OK;
+ regs[Rt] = *val;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+static bool emulate_store(struct exc_info *ctx, u32 insn, u64 *val, u64 *width, u64 *vaddr)
+{
+ u64 Rt = insn & 0x1f;
+ u64 Rn = (insn >> 5) & 0x1f;
+ u64 imm9 = EXT((insn >> 12) & 0x1ff, 9);
+ u64 imm7 = EXT((insn >> 15) & 0x7f, 7);
+ u64 *regs = ctx->regs;
+
+ union simd_reg simd[32];
+
+ *width = insn >> 30;
+
+ dprintf("emulate_store(%p, 0x%08x, ..., %ld) = ", regs, insn, *width);
+
+ regs[31] = 0;
+
+ u64 mask = 0xffffffffffffffffUL;
+
+ if (*width < 3)
+ mask = (1UL << (8 << *width)) - 1;
+
+ if ((insn & 0x3fe00400) == 0x38000400) {
+ // STRx (immediate) Pre/Post-index
+ CHECK_RN;
+ regs[Rn] += imm9;
+ *val = regs[Rt] & mask;
+ } else if ((insn & 0x3fc00000) == 0x39000000) {
+ // STRx (immediate) Unsigned offset
+ *val = regs[Rt] & mask;
+ } else if ((insn & 0x3fe04c00) == 0x38204800) {
+ // STRx (register)
+ *val = regs[Rt] & mask;
+ } else if ((insn & 0xfec00000) == 0x28000000) {
+ // ST[N]P (Signed offset, 32-bit)
+ *vaddr = regs[Rn] + (imm7 * 4);
+ u64 Rt2 = (insn >> 10) & 0x1f;
+ val[0] = (regs[Rt] & 0xffffffff) | (regs[Rt2] << 32);
+ *width = 3;
+ } else if ((insn & 0xfec00000) == 0xa8000000) {
+ // ST[N]P (Signed offset, 64-bit)
+ *vaddr = regs[Rn] + (imm7 * 8);
+ u64 Rt2 = (insn >> 10) & 0x1f;
+ val[0] = regs[Rt];
+ val[1] = regs[Rt2];
+ *width = 4;
+ } else if ((insn & 0xfec00000) == 0xa8800000) {
+ // ST[N]P (immediate, 64-bit, pre/post-index)
+ CHECK_RN;
+ *vaddr = regs[Rn] + ((insn & BIT(24)) ? (imm7 * 8) : 0);
+ regs[Rn] += (imm7 * 8);
+ u64 Rt2 = (insn >> 10) & 0x1f;
+ val[0] = regs[Rt];
+ val[1] = regs[Rt2];
+ *width = 4;
+ } else if ((insn & 0x3fc00000) == 0x3d000000) {
+ // STR (immediate, SIMD&FP) Unsigned offset, 8..64-bit
+ get_simd_state(simd);
+ *val = simd[Rt].d[0];
+ } else if ((insn & 0x3fe04c00) == 0x3c204800) {
+ // STR (register, SIMD&FP) 8..64-bit
+ get_simd_state(simd);
+ *val = simd[Rt].d[0];
+ } else if ((insn & 0xffe04c00) == 0x3ca04800) {
+ // STR (register, SIMD&FP) 128-bit
+ get_simd_state(simd);
+ val[0] = simd[Rt].d[0];
+ val[1] = simd[Rt].d[1];
+ *width = 4;
+ } else if ((insn & 0xffc00000) == 0x3d800000) {
+ // STR (immediate, SIMD&FP) Unsigned offset, 128-bit
+ get_simd_state(simd);
+ val[0] = simd[Rt].d[0];
+ val[1] = simd[Rt].d[1];
+ *width = 4;
+ } else if ((insn & 0xffe00000) == 0xbc000000) {
+ // STUR (immediate, SIMD&FP) 32-bit
+ get_simd_state(simd);
+ val[0] = simd[Rt].s[0];
+ *width = 2;
+ } else if ((insn & 0xffe00000) == 0xfc000000) {
+ // STUR (immediate, SIMD&FP) 64-bit
+ get_simd_state(simd);
+ val[0] = simd[Rt].d[0];
+ *width = 3;
+ } else if ((insn & 0xffe00000) == 0x3c800000) {
+ // STUR (immediate, SIMD&FP) 128-bit
+ get_simd_state(simd);
+ val[0] = simd[Rt].d[0];
+ val[1] = simd[Rt].d[1];
+ *width = 4;
+ } else if ((insn & 0xffc00000) == 0x2d000000) {
+ // STP (SIMD&FP, 128-bit) Signed offset
+ *vaddr = regs[Rn] + (imm7 * 4);
+ u64 Rt2 = (insn >> 10) & 0x1f;
+ get_simd_state(simd);
+ val[0] = simd[Rt].s[0] | (((u64)simd[Rt2].s[0]) << 32);
+ *width = 3;
+ } else if ((insn & 0xffc00000) == 0xad000000) {
+ // STP (SIMD&FP, 128-bit) Signed offset
+ *vaddr = regs[Rn] + (imm7 * 16);
+ u64 Rt2 = (insn >> 10) & 0x1f;
+ get_simd_state(simd);
+ val[0] = simd[Rt].d[0];
+ val[1] = simd[Rt].d[1];
+ val[2] = simd[Rt2].d[0];
+ val[3] = simd[Rt2].d[1];
+ *width = 5;
+ } else if ((insn & 0x3fe00c00) == 0x38000000) {
+ // STURx (unscaled)
+ *val = regs[Rt] & mask;
+ } else if ((insn & 0xffffffe0) == 0xd50b7420) {
+ // DC ZVA
+ *vaddr = regs[Rt];
+ memset(val, 0, CACHE_LINE_SIZE);
+ *width = CACHE_LINE_LOG2;
+ } else if ((insn & 0x3ffffc00) == 0x089ffc00) {
+ // STL qR*
+ *val = regs[Rt] & mask;
+ } else {
+ return false;
+ }
+
+ dprintf("0x%lx\n", *width);
+
+ return true;
+}
+
+static void emit_mmiotrace(u64 pc, u64 addr, u64 *data, u64 width, u64 flags, bool sync)
+{
+ struct hv_evt_mmiotrace evt = {
+ .flags = flags | FIELD_PREP(MMIO_EVT_CPU, smp_id()),
+ .pc = pc,
+ .addr = addr,
+ };
+
+ if (width > 3)
+ evt.flags |= FIELD_PREP(MMIO_EVT_WIDTH, 3) | MMIO_EVT_MULTI;
+ else
+ evt.flags |= FIELD_PREP(MMIO_EVT_WIDTH, width);
+
+ for (int i = 0; i < (1 << width); i += 8) {
+ evt.data = *data++;
+ hv_wdt_suspend();
+ uartproxy_send_event(EVT_MMIOTRACE, &evt, sizeof(evt));
+ if (sync) {
+ iodev_flush(uartproxy_iodev);
+ }
+ hv_wdt_resume();
+ evt.addr += 8;
+ }
+}
+
+bool hv_pa_write(struct exc_info *ctx, u64 addr, u64 *val, int width)
+{
+ sysop("dsb sy");
+ exc_count = 0;
+ exc_guard = GUARD_SKIP;
+ switch (width) {
+ case 0:
+ write8(addr, val[0]);
+ break;
+ case 1:
+ write16(addr, val[0]);
+ break;
+ case 2:
+ write32(addr, val[0]);
+ break;
+ case 3:
+ write64(addr, val[0]);
+ break;
+ case 4:
+ case 5:
+ case 6:
+ for (u64 i = 0; i < (1UL << (width - 3)); i++)
+ write64(addr + 8 * i, val[i]);
+ break;
+ default:
+ dprintf("HV: unsupported write width %ld\n", width);
+ exc_guard = GUARD_OFF;
+ return false;
+ }
+ // Make sure we catch SErrors here
+ sysop("dsb sy");
+ sysop("isb");
+ exc_guard = GUARD_OFF;
+ if (exc_count) {
+ printf("HV: Exception during write to 0x%lx (width: %d)\n", addr, width);
+ // Update exception info with "real" cause
+ ctx->esr = hv_get_esr();
+ ctx->far = hv_get_far();
+ return false;
+ }
+ return true;
+}
+
+bool hv_pa_read(struct exc_info *ctx, u64 addr, u64 *val, int width)
+{
+ sysop("dsb sy");
+ exc_count = 0;
+ exc_guard = GUARD_SKIP;
+ switch (width) {
+ case 0:
+ val[0] = read8(addr);
+ break;
+ case 1:
+ val[0] = read16(addr);
+ break;
+ case 2:
+ val[0] = read32(addr);
+ break;
+ case 3:
+ val[0] = read64(addr);
+ break;
+ case 4:
+ val[0] = read64(addr);
+ val[1] = read64(addr + 8);
+ break;
+ case 5:
+ val[0] = read64(addr);
+ val[1] = read64(addr + 8);
+ val[2] = read64(addr + 16);
+ val[3] = read64(addr + 24);
+ break;
+ default:
+ dprintf("HV: unsupported read width %ld\n", width);
+ exc_guard = GUARD_OFF;
+ return false;
+ }
+ sysop("dsb sy");
+ exc_guard = GUARD_OFF;
+ if (exc_count) {
+ dprintf("HV: Exception during read from 0x%lx (width: %d)\n", addr, width);
+ // Update exception info with "real" cause
+ ctx->esr = hv_get_esr();
+ ctx->far = hv_get_far();
+ return false;
+ }
+ return true;
+}
+
+bool hv_pa_rw(struct exc_info *ctx, u64 addr, u64 *val, bool write, int width)
+{
+ if (write)
+ return hv_pa_write(ctx, addr, val, width);
+ else
+ return hv_pa_read(ctx, addr, val, width);
+}
+
+static bool hv_emulate_rw_aligned(struct exc_info *ctx, u64 pte, u64 vaddr, u64 ipa, u64 *val,
+ bool is_write, u64 width, u64 elr, u64 par)
+{
+ assert(pte);
+ assert(((ipa & 0x3fff) + (1 << width)) <= 0x4000);
+
+ u64 target = pte & PTE_TARGET_MASK_L4;
+ u64 paddr = target | (vaddr & MASK(VADDR_L4_OFFSET_BITS));
+ u64 flags = FIELD_PREP(MMIO_EVT_ATTR, FIELD_GET(PAR_ATTR, par)) |
+ FIELD_PREP(MMIO_EVT_SH, FIELD_GET(PAR_SH, par));
+
+ // For split ops, treat hardware mapped pages as SPTE_MAP
+ if (IS_HW(pte))
+ pte = target | FIELD_PREP(PTE_TYPE, PTE_BLOCK) | FIELD_PREP(SPTE_TYPE, SPTE_MAP);
+
+ if (is_write) {
+ // Write
+ hv_wdt_breadcrumb('3');
+
+ if (pte & SPTE_TRACE_WRITE)
+ emit_mmiotrace(elr, ipa, val, width, flags | MMIO_EVT_WRITE, pte & SPTE_TRACE_UNBUF);
+
+ hv_wdt_breadcrumb('4');
+
+ switch (FIELD_GET(SPTE_TYPE, pte)) {
+ case SPTE_PROXY_HOOK_R:
+ paddr = ipa;
+ // fallthrough
+ case SPTE_MAP:
+ hv_wdt_breadcrumb('5');
+ dprintf("HV: SPTE_MAP[W] @0x%lx 0x%lx -> 0x%lx (w=%d): 0x%lx\n", elr, ipa, paddr,
+ 1 << width, val[0]);
+ if (!hv_pa_write(ctx, paddr, val, width))
+ return false;
+ break;
+ case SPTE_HOOK: {
+ hv_wdt_breadcrumb('6');
+ hv_hook_t *hook = (hv_hook_t *)target;
+ if (!hook(ctx, ipa, val, true, width))
+ return false;
+ dprintf("HV: SPTE_HOOK[W] @0x%lx 0x%lx -> 0x%lx (w=%d) @%p: 0x%lx\n", elr, far, ipa,
+ 1 << width, hook, wval);
+ break;
+ }
+ case SPTE_PROXY_HOOK_RW:
+ case SPTE_PROXY_HOOK_W: {
+ hv_wdt_breadcrumb('7');
+ struct hv_vm_proxy_hook_data hook = {
+ .flags = FIELD_PREP(MMIO_EVT_WIDTH, width) | MMIO_EVT_WRITE | flags,
+ .id = FIELD_GET(PTE_TARGET_MASK_L4, pte),
+ .addr = ipa,
+ .data = {0},
+ };
+ memcpy(hook.data, val, 1 << width);
+ hv_exc_proxy(ctx, START_HV, HV_HOOK_VM, &hook);
+ break;
+ }
+ default:
+ printf("HV: invalid SPTE 0x%016lx for IPA 0x%lx\n", pte, ipa);
+ return false;
+ }
+ } else {
+ hv_wdt_breadcrumb('3');
+ switch (FIELD_GET(SPTE_TYPE, pte)) {
+ case SPTE_PROXY_HOOK_W:
+ paddr = ipa;
+ // fallthrough
+ case SPTE_MAP:
+ hv_wdt_breadcrumb('4');
+ if (!hv_pa_read(ctx, paddr, val, width))
+ return false;
+ dprintf("HV: SPTE_MAP[R] @0x%lx 0x%lx -> 0x%lx (w=%d): 0x%lx\n", elr, ipa, paddr,
+ 1 << width, val[0]);
+ break;
+ case SPTE_HOOK: {
+ hv_wdt_breadcrumb('5');
+ hv_hook_t *hook = (hv_hook_t *)target;
+ if (!hook(ctx, ipa, val, false, width))
+ return false;
+ dprintf("HV: SPTE_HOOK[R] @0x%lx 0x%lx -> 0x%lx (w=%d) @%p: 0x%lx\n", elr, far, ipa,
+ 1 << width, hook, val);
+ break;
+ }
+ case SPTE_PROXY_HOOK_RW:
+ case SPTE_PROXY_HOOK_R: {
+ hv_wdt_breadcrumb('6');
+ struct hv_vm_proxy_hook_data hook = {
+ .flags = FIELD_PREP(MMIO_EVT_WIDTH, width) | flags,
+ .id = FIELD_GET(PTE_TARGET_MASK_L4, pte),
+ .addr = ipa,
+ };
+ hv_exc_proxy(ctx, START_HV, HV_HOOK_VM, &hook);
+ memcpy(val, hook.data, 1 << width);
+ break;
+ }
+ default:
+ printf("HV: invalid SPTE 0x%016lx for IPA 0x%lx\n", pte, ipa);
+ return false;
+ }
+
+ hv_wdt_breadcrumb('7');
+ if (pte & SPTE_TRACE_READ)
+ emit_mmiotrace(elr, ipa, val, width, flags, pte & SPTE_TRACE_UNBUF);
+ }
+
+ hv_wdt_breadcrumb('*');
+
+ return true;
+}
+
+static bool hv_emulate_rw(struct exc_info *ctx, u64 pte, u64 vaddr, u64 ipa, u8 *val, bool is_write,
+ u64 bytes, u64 elr, u64 par)
+{
+ u64 aval[HV_MAX_RW_WORDS];
+
+ bool advance = (IS_HW(pte) || (IS_SW(pte) && FIELD_GET(SPTE_TYPE, pte) == SPTE_MAP)) ? 1 : 0;
+ u64 off = 0;
+ u64 width;
+
+ bool first = true;
+
+ u64 left = bytes;
+ u64 paddr = (pte & PTE_TARGET_MASK_L4) | (vaddr & MASK(VADDR_L4_OFFSET_BITS));
+
+ while (left > 0) {
+ memset(aval, 0, sizeof(aval));
+
+ if (left >= 64 && (ipa & 63) == 0)
+ width = 6;
+ else if (left >= 32 && (ipa & 31) == 0)
+ width = 5;
+ else if (left >= 16 && (ipa & 15) == 0)
+ width = 4;
+ else if (left >= 8 && (ipa & 7) == 0)
+ width = 3;
+ else if (left >= 4 && (ipa & 3) == 0)
+ width = 2;
+ else if (left >= 2 && (ipa & 1) == 0)
+ width = 1;
+ else
+ width = 0;
+
+ u64 chunk = 1 << width;
+
+ /*
+ if (chunk != bytes)
+ printf("HV: Splitting unaligned %ld-byte %s: %ld bytes @ 0x%lx\n", bytes,
+ is_write ? "write" : "read", chunk, vaddr);
+ */
+
+ if (is_write)
+ memcpy(aval, val + off, chunk);
+
+ if (advance)
+ pte = (paddr & PTE_TARGET_MASK_L4) | (pte & ~PTE_TARGET_MASK_L4);
+
+ if (!hv_emulate_rw_aligned(ctx, pte, vaddr, ipa, aval, is_write, width, elr, par)) {
+ if (!first)
+ printf("HV: WARNING: Failed to emulate split op but part of it did commit!\n");
+ return false;
+ }
+
+ if (!is_write)
+ memcpy(val + off, aval, chunk);
+
+ left -= chunk;
+ off += chunk;
+
+ ipa += chunk;
+ vaddr += chunk;
+ if (advance)
+ paddr += chunk;
+
+ first = 0;
+ }
+
+ return true;
+}
+
+bool hv_handle_dabort(struct exc_info *ctx)
+{
+ hv_wdt_breadcrumb('0');
+ u64 esr = hv_get_esr();
+ bool is_write = esr & ESR_ISS_DABORT_WnR;
+
+ u64 far = hv_get_far();
+ u64 par;
+ u64 ipa = hv_translate(far, true, is_write, &par);
+
+ dprintf("hv_handle_abort(): stage 1 0x%0lx -> 0x%lx\n", far, ipa);
+
+ if (!ipa) {
+ printf("HV: stage 1 translation failed at VA 0x%0lx\n", far);
+ return false;
+ }
+
+ if (ipa >= BIT(vaddr_bits)) {
+ printf("hv_handle_abort(): IPA out of bounds: 0x%0lx -> 0x%lx\n", far, ipa);
+ return false;
+ }
+
+ u64 pte = hv_pt_walk(ipa);
+
+ if (!pte) {
+ printf("HV: Unmapped IPA 0x%lx\n", ipa);
+ return false;
+ }
+
+ if (IS_HW(pte)) {
+ printf("HV: Data abort on mapped page (0x%lx -> 0x%lx)\n", far, pte);
+ // Try again, this is usually a race
+ ctx->elr -= 4;
+ return true;
+ }
+
+ hv_wdt_breadcrumb('1');
+
+ assert(IS_SW(pte));
+
+ u64 elr = ctx->elr;
+ u64 elr_pa = hv_translate(elr, false, false, NULL);
+ if (!elr_pa) {
+ printf("HV: Failed to fetch instruction for data abort at 0x%lx\n", elr);
+ return false;
+ }
+
+ u32 insn = read32(elr_pa);
+ u64 width;
+
+ hv_wdt_breadcrumb('2');
+
+ u64 vaddr = far;
+
+ u8 val[HV_MAX_RW_SIZE] ALIGNED(HV_MAX_RW_SIZE);
+ memset(val, 0, sizeof(val));
+
+ if (is_write) {
+ hv_wdt_breadcrumb('W');
+
+ if (!emulate_store(ctx, insn, (u64 *)val, &width, &vaddr)) {
+ printf("HV: store not emulated: 0x%08x at 0x%lx\n", insn, ipa);
+ return false;
+ }
+ } else {
+ hv_wdt_breadcrumb('R');
+
+ if (!emulate_load(ctx, insn, NULL, &width, &vaddr)) {
+ printf("HV: load not emulated: 0x%08x at 0x%lx\n", insn, ipa);
+ return false;
+ }
+ }
+
+ /*
+ Check for HW page-straddling conditions
+ Right now we only support the case where the page boundary is exactly halfway
+ through the read/write.
+ */
+ u64 bytes = 1 << width;
+ u64 vaddrp0 = vaddr & ~MASK(VADDR_L3_OFFSET_BITS);
+ u64 vaddrp1 = (vaddr + bytes - 1) & ~MASK(VADDR_L3_OFFSET_BITS);
+
+ if (vaddrp0 == vaddrp1) {
+ // Easy case, no page straddle
+ if (far != vaddr) {
+ printf("HV: faulted at 0x%lx, but expecting 0x%lx\n", far, vaddr);
+ return false;
+ }
+
+ if (!hv_emulate_rw(ctx, pte, vaddr, ipa, val, is_write, bytes, elr, par))
+ return false;
+ } else {
+ // Oops, we're straddling a page boundary
+ // Treat it as two separate loads or stores
+
+ assert(bytes > 1);
+ hv_wdt_breadcrumb('s');
+
+ u64 off = vaddrp1 - vaddr;
+
+ u64 vaddr2;
+ const char *other;
+ if (far == vaddr) {
+ other = "upper";
+ vaddr2 = vaddrp1;
+ } else {
+ if (far != vaddrp1) {
+ printf("HV: faulted at 0x%lx, but expecting 0x%lx\n", far, vaddrp1);
+ return false;
+ }
+ other = "lower";
+ vaddr2 = vaddr;
+ }
+
+ u64 par2;
+ u64 ipa2 = hv_translate(vaddr2, true, esr & ESR_ISS_DABORT_WnR, &par2);
+ if (!ipa2) {
+ printf("HV: %s half stage 1 translation failed at VA 0x%0lx\n", other, vaddr2);
+ return false;
+ }
+ if (ipa2 >= BIT(vaddr_bits)) {
+ printf("hv_handle_abort(): %s half IPA out of bounds: 0x%0lx -> 0x%lx\n", other, vaddr2,
+ ipa2);
+ return false;
+ }
+
+ u64 pte2 = hv_pt_walk(ipa2);
+ if (!pte2) {
+ printf("HV: Unmapped %s half IPA 0x%lx\n", other, ipa2);
+ return false;
+ }
+
+ hv_wdt_breadcrumb('S');
+
+ printf("HV: Emulating %s straddling page boundary as two ops @ 0x%lx (%ld bytes)\n",
+ is_write ? "write" : "read", vaddr, bytes);
+
+ bool upper_ret;
+ if (far == vaddr) {
+ if (!hv_emulate_rw(ctx, pte, vaddr, ipa, val, is_write, off, elr, par))
+ return false;
+ upper_ret =
+ hv_emulate_rw(ctx, pte2, vaddr2, ipa2, val + off, is_write, bytes - off, elr, par2);
+ } else {
+ if (!hv_emulate_rw(ctx, pte2, vaddr2, ipa2, val, is_write, off, elr, par2))
+ return false;
+ upper_ret =
+ hv_emulate_rw(ctx, pte, vaddrp1, ipa, val + off, is_write, bytes - off, elr, par);
+ }
+
+ if (!upper_ret) {
+ printf("HV: WARNING: Failed to emulate upper half but lower half did commit!\n");
+ return false;
+ }
+ }
+
+ if (vaddrp0 != vaddrp1) {
+ printf("HV: Straddled r/w data:\n");
+ hexdump(val, bytes);
+ }
+
+ hv_wdt_breadcrumb('8');
+ if (!is_write && !emulate_load(ctx, insn, (u64 *)val, &width, &vaddr))
+ return false;
+
+ hv_wdt_breadcrumb('9');
+
+ return true;
+}
diff --git a/tools/src/hv_vuart.c b/tools/src/hv_vuart.c
new file mode 100644
index 0000000..595c031
--- /dev/null
+++ b/tools/src/hv_vuart.c
@@ -0,0 +1,125 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "hv.h"
+#include "aic.h"
+#include "iodev.h"
+#include "uart.h"
+#include "uart_regs.h"
+#include "usb.h"
+
+bool active = false;
+
+u32 ucon = 0;
+u32 utrstat = 0;
+u32 ufstat = 0;
+
+int vuart_irq = 0;
+
+static void update_irq(void)
+{
+ ssize_t rx_queued;
+
+ iodev_handle_events(IODEV_USB_VUART);
+
+ utrstat |= UTRSTAT_TXBE | UTRSTAT_TXE;
+ utrstat &= ~UTRSTAT_RXD;
+
+ ufstat = 0;
+ if ((rx_queued = iodev_can_read(IODEV_USB_VUART))) {
+ utrstat |= UTRSTAT_RXD;
+ if (rx_queued > 15)
+ ufstat = FIELD_PREP(UFSTAT_RXCNT, 15) | UFSTAT_RXFULL;
+ else
+ ufstat = FIELD_PREP(UFSTAT_RXCNT, rx_queued);
+
+ if (FIELD_GET(UCON_RXMODE, ucon) == UCON_MODE_IRQ && ucon & UCON_RXTO_ENA) {
+ utrstat |= UTRSTAT_RXTO;
+ }
+ }
+
+ if (FIELD_GET(UCON_TXMODE, ucon) == UCON_MODE_IRQ && ucon & UCON_TXTHRESH_ENA) {
+ utrstat |= UTRSTAT_TXTHRESH;
+ }
+
+ if (vuart_irq) {
+ uart_clear_irqs();
+ if (utrstat & (UTRSTAT_TXTHRESH | UTRSTAT_RXTHRESH | UTRSTAT_RXTO)) {
+ aic_set_sw(vuart_irq, true);
+ } else {
+ aic_set_sw(vuart_irq, false);
+ }
+ }
+
+ // printf("HV: vuart UTRSTAT=0x%x UFSTAT=0x%x UCON=0x%x\n", utrstat, ufstat, ucon);
+}
+
+static bool handle_vuart(struct exc_info *ctx, u64 addr, u64 *val, bool write, int width)
+{
+ UNUSED(ctx);
+ UNUSED(width);
+
+ addr &= 0xfff;
+
+ update_irq();
+
+ if (write) {
+ // printf("HV: vuart W 0x%lx <- 0x%lx (%d)\n", addr, *val, width);
+ switch (addr) {
+ case UCON:
+ ucon = *val;
+ break;
+ case UTXH: {
+ uint8_t b = *val;
+ if (iodev_can_write(IODEV_USB_VUART))
+ iodev_write(IODEV_USB_VUART, &b, 1);
+ break;
+ }
+ case UTRSTAT:
+ utrstat &= ~(*val & (UTRSTAT_TXTHRESH | UTRSTAT_RXTHRESH | UTRSTAT_RXTO));
+ break;
+ }
+ } else {
+ switch (addr) {
+ case UCON:
+ *val = ucon;
+ break;
+ case URXH:
+ if (iodev_can_read(IODEV_USB_VUART)) {
+ uint8_t c;
+ iodev_read(IODEV_USB_VUART, &c, 1);
+ *val = c;
+ } else {
+ *val = 0;
+ }
+ break;
+ case UTRSTAT:
+ *val = utrstat;
+ break;
+ case UFSTAT:
+ *val = ufstat;
+ break;
+ default:
+ *val = 0;
+ break;
+ }
+ // printf("HV: vuart R 0x%lx -> 0x%lx (%d)\n", addr, *val, width);
+ }
+
+ return true;
+}
+
+void hv_vuart_poll(void)
+{
+ if (!active)
+ return;
+
+ update_irq();
+}
+
+void hv_map_vuart(u64 base, int irq, iodev_id_t iodev)
+{
+ hv_map_hook(base, handle_vuart, 0x1000);
+ usb_iodev_vuart_setup(iodev);
+ vuart_irq = irq;
+ active = true;
+}
diff --git a/tools/src/hv_wdt.c b/tools/src/hv_wdt.c
new file mode 100644
index 0000000..6010412
--- /dev/null
+++ b/tools/src/hv_wdt.c
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "hv.h"
+#include "adt.h"
+#include "smp.h"
+#include "uart.h"
+#include "utils.h"
+
+#define WDT_TIMEOUT 1
+
+static bool hv_wdt_active = false;
+static bool hv_wdt_enabled = false;
+static volatile u64 hv_wdt_timestamp = 0;
+static u64 hv_wdt_timeout = 0;
+static volatile u64 hv_wdt_breadcrumbs;
+
+static int hv_wdt_cpu;
+
+static u64 cpu_dbg_base = 0;
+
+void hv_wdt_bark(void)
+{
+ u64 tmp = hv_wdt_breadcrumbs;
+ uart_puts("HV watchdog: bark!");
+
+ uart_printf("Breadcrumbs: ");
+ for (int i = 56; i >= 0; i -= 8) {
+ char c = (tmp >> i) & 0xff;
+ if (c)
+ uart_putchar(c);
+ }
+ uart_putchar('\n');
+
+ uart_puts("Attempting to enter proxy");
+
+ struct uartproxy_msg_start start = {
+ .reason = START_HV,
+ .code = HV_WDT_BARK,
+ };
+
+ uartproxy_run(&start);
+ reboot();
+}
+
+void hv_wdt_main(void)
+{
+ while (hv_wdt_active) {
+ if (hv_wdt_enabled) {
+ sysop("dmb ish");
+ u64 timestamp = hv_wdt_timestamp;
+ sysop("isb");
+ u64 now = mrs(CNTPCT_EL0);
+ sysop("isb");
+ if ((now - timestamp) > hv_wdt_timeout)
+ hv_wdt_bark();
+ }
+
+ udelay(1000);
+
+ sysop("dmb ish");
+ }
+}
+
+void hv_wdt_pet(void)
+{
+ hv_wdt_timestamp = mrs(CNTPCT_EL0);
+ sysop("dmb ish");
+}
+
+void hv_wdt_suspend(void)
+{
+ hv_wdt_enabled = false;
+ sysop("dsb ish");
+}
+
+void hv_wdt_resume(void)
+{
+ hv_wdt_pet();
+ hv_wdt_enabled = true;
+ sysop("dsb ish");
+}
+
+void hv_wdt_breadcrumb(char c)
+{
+ u64 tmp = hv_wdt_breadcrumbs;
+ tmp <<= 8;
+ tmp |= c;
+ hv_wdt_breadcrumbs = tmp;
+ sysop("dmb ish");
+}
+
+void hv_wdt_init(void)
+{
+ int node = adt_path_offset(adt, "/cpus/cpu0");
+ if (node < 0) {
+ printf("Error getting /cpus/cpu0 node\n");
+ return;
+ }
+
+ u64 reg[2];
+ if (ADT_GETPROP_ARRAY(adt, node, "cpu-uttdbg-reg", reg) < 0) {
+ printf("Error getting cpu-uttdbg-reg property\n");
+ return;
+ }
+
+ cpu_dbg_base = reg[0];
+}
+
+void hv_wdt_start(int cpu)
+{
+ if (hv_wdt_active)
+ return;
+
+ hv_wdt_cpu = cpu;
+ hv_wdt_breadcrumbs = 0;
+ hv_wdt_timeout = mrs(CNTFRQ_EL0) * WDT_TIMEOUT;
+ hv_wdt_pet();
+ hv_wdt_active = true;
+ hv_wdt_enabled = true;
+ smp_call4(hv_wdt_cpu, hv_wdt_main, 0, 0, 0, 0);
+}
+
+void hv_wdt_stop(void)
+{
+ if (!hv_wdt_active)
+ return;
+
+ hv_wdt_active = false;
+ smp_wait(hv_wdt_cpu);
+}
diff --git a/tools/src/i2c.c b/tools/src/i2c.c
new file mode 100644
index 0000000..942ef1e
--- /dev/null
+++ b/tools/src/i2c.c
@@ -0,0 +1,216 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "adt.h"
+#include "i2c.h"
+#include "malloc.h"
+#include "pmgr.h"
+#include "types.h"
+#include "utils.h"
+
+#define PASEMI_FIFO_TX 0x00
+#define PASEMI_TX_FLAG_READ BIT(10)
+#define PASEMI_TX_FLAG_STOP BIT(9)
+#define PASEMI_TX_FLAG_START BIT(8)
+
+#define PASEMI_FIFO_RX 0x04
+#define PASEMI_RX_FLAG_EMPTY BIT(8)
+
+#define PASEMI_STATUS 0x14
+#define PASEMI_STATUS_XFER_BUSY BIT(28)
+#define PASEMI_STATUS_XFER_ENDED BIT(27)
+
+#define PASEMI_CONTROL 0x1c
+#define PASEMI_CONTROL_CLEAR_RX BIT(10)
+#define PASEMI_CONTROL_CLEAR_TX BIT(9)
+
+struct i2c_dev {
+ uintptr_t base;
+};
+
+i2c_dev_t *i2c_init(const char *adt_node)
+{
+ int adt_path[8];
+ int adt_offset;
+ adt_offset = adt_path_offset_trace(adt, adt_node, adt_path);
+ if (adt_offset < 0) {
+ printf("i2c: Error getting %s node\n", adt_node);
+ return NULL;
+ }
+
+ u64 base;
+ if (adt_get_reg(adt, adt_path, "reg", 0, &base, NULL) < 0) {
+ printf("i2c: Error getting %s regs\n", adt_node);
+ return NULL;
+ }
+
+ if (pmgr_adt_power_enable(adt_node)) {
+ printf("i2c: Error enabling power for %s\n", adt_node);
+ return NULL;
+ }
+
+ i2c_dev_t *dev = malloc(sizeof(*dev));
+ if (!dev)
+ return NULL;
+
+ dev->base = base;
+ return dev;
+}
+
+void i2c_shutdown(i2c_dev_t *dev)
+{
+ free(dev);
+}
+
+static void i2c_clear_fifos(i2c_dev_t *dev)
+{
+ set32(dev->base + PASEMI_CONTROL, PASEMI_CONTROL_CLEAR_TX | PASEMI_CONTROL_CLEAR_RX);
+}
+
+static void i2c_clear_status(i2c_dev_t *dev)
+{
+ write32(dev->base + PASEMI_STATUS, 0xffffffff);
+}
+
+static void i2c_xfer_start_read(i2c_dev_t *dev, u8 addr, size_t len)
+{
+ write32(dev->base + PASEMI_FIFO_TX, PASEMI_TX_FLAG_START | (addr << 1) | 1);
+ write32(dev->base + PASEMI_FIFO_TX, PASEMI_TX_FLAG_READ | PASEMI_TX_FLAG_STOP | len);
+}
+
+static size_t i2c_xfer_read(i2c_dev_t *dev, u8 *bfr, size_t len)
+{
+ for (size_t i = 0; i < len; ++i) {
+ u32 timeout = 5000;
+ u32 val;
+
+ do {
+ val = read32(dev->base + PASEMI_FIFO_RX);
+ if (!(val & PASEMI_RX_FLAG_EMPTY))
+ break;
+ udelay(10);
+ } while (--timeout);
+
+ if (val & PASEMI_RX_FLAG_EMPTY) {
+ printf("i2c: timeout while reading (got %lu, expected %lu bytes)\n", i, len);
+ return i;
+ }
+
+ bfr[i] = val;
+ }
+
+ return len;
+}
+
+static int i2c_xfer_write(i2c_dev_t *dev, u8 addr, u32 start, u32 stop, const u8 *bfr, size_t len)
+{
+ if (start)
+ write32(dev->base + PASEMI_FIFO_TX, PASEMI_TX_FLAG_START | (addr << 1));
+
+ for (size_t i = 0; i < len; ++i) {
+ u32 data = bfr[i];
+ if (i == (len - 1) && stop)
+ data |= PASEMI_TX_FLAG_STOP;
+
+ write32(dev->base + PASEMI_FIFO_TX, data);
+ }
+
+ if (!stop)
+ return 0;
+
+ if (poll32(dev->base + PASEMI_STATUS, PASEMI_STATUS_XFER_BUSY, 0, 50000)) {
+ printf(
+ "i2c: timeout while waiting for PASEMI_STATUS_XFER_BUSY to clear after write xfer\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int i2c_smbus_read(i2c_dev_t *dev, u8 addr, u8 reg, u8 *bfr, size_t len)
+{
+ int ret = -1;
+
+ i2c_clear_fifos(dev);
+ i2c_clear_status(dev);
+
+ if (i2c_xfer_write(dev, addr, 1, 0, &reg, 1))
+ goto err;
+
+ i2c_xfer_start_read(dev, addr, len + 1);
+ u8 len_reply;
+ if (i2c_xfer_read(dev, &len_reply, 1) != 1)
+ goto err;
+
+ if (len_reply < len)
+ printf("i2c: want to read %ld bytes from addr %d but can only read %d\n", len, addr,
+ len_reply);
+ if (len_reply > len)
+ printf("i2c: want to read %ld bytes from addr %d but device wants to send %d\n", len, addr,
+ len_reply);
+
+ ret = i2c_xfer_read(dev, bfr, min(len, len_reply));
+
+err:
+ if (poll32(dev->base + PASEMI_STATUS, PASEMI_STATUS_XFER_BUSY, 0, 50000)) {
+ printf("i2c: timeout while waiting for PASEMI_STATUS_XFER_BUSY to clear after read xfer\n");
+ return -1;
+ }
+
+ return ret;
+}
+
+int i2c_smbus_write(i2c_dev_t *dev, u8 addr, u8 reg, const u8 *bfr, size_t len)
+{
+ i2c_clear_fifos(dev);
+ i2c_clear_status(dev);
+
+ if (i2c_xfer_write(dev, addr, 1, 0, &reg, 1))
+ return -1;
+
+ u8 len_send = len;
+ if (i2c_xfer_write(dev, addr, 0, 0, &len_send, 1))
+ return -1;
+ if (i2c_xfer_write(dev, addr, 0, 1, bfr, len))
+ return -1;
+
+ return len_send;
+}
+
+int i2c_smbus_read32(i2c_dev_t *dev, u8 addr, u8 reg, u32 *val)
+{
+ u8 bfr[4];
+ if (i2c_smbus_read(dev, addr, reg, bfr, 4) != 4)
+ return -1;
+
+ *val = (bfr[0]) | (bfr[1] << 8) | (bfr[2] << 16) | (bfr[3] << 24);
+ return 0;
+}
+
+int i2c_smbus_read16(i2c_dev_t *dev, u8 addr, u8 reg, u16 *val)
+{
+ u8 bfr[2];
+ if (i2c_smbus_read(dev, addr, reg, bfr, 2) != 2)
+ return -1;
+
+ *val = (bfr[0]) | (bfr[1] << 8);
+ return 0;
+}
+
+int i2c_smbus_write32(i2c_dev_t *dev, u8 addr, u8 reg, u32 val)
+{
+ u8 bfr[4];
+
+ bfr[0] = val;
+ bfr[1] = val >> 8;
+ bfr[2] = val >> 16;
+ bfr[3] = val >> 24;
+
+ return i2c_smbus_write(dev, addr, reg, bfr, 4);
+}
+
+int i2c_smbus_read8(i2c_dev_t *dev, u8 addr, u8 reg, u8 *val)
+{
+ if (i2c_smbus_read(dev, addr, reg, val, 1) != 1)
+ return -1;
+ return 0;
+}
diff --git a/tools/src/i2c.h b/tools/src/i2c.h
new file mode 100644
index 0000000..cbfc119
--- /dev/null
+++ b/tools/src/i2c.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef I2C_H
+#define I2C_H
+
+#include "types.h"
+
+typedef struct i2c_dev i2c_dev_t;
+
+i2c_dev_t *i2c_init(const char *adt_node);
+void i2c_shutdown(i2c_dev_t *dev);
+
+int i2c_smbus_read(i2c_dev_t *dev, u8 addr, u8 reg, u8 *bfr, size_t len);
+int i2c_smbus_write(i2c_dev_t *dev, u8 addr, u8 reg, const u8 *bfr, size_t len);
+
+int i2c_smbus_read32(i2c_dev_t *dev, u8 addr, u8 reg, u32 *val);
+int i2c_smbus_write32(i2c_dev_t *dev, u8 addr, u8 reg, u32 val);
+
+int i2c_smbus_read16(i2c_dev_t *dev, u8 addr, u8 reg, u16 *val);
+int i2c_smbus_read8(i2c_dev_t *dev, u8 addr, u8 reg, u8 *val);
+
+#endif
diff --git a/tools/src/iodev.c b/tools/src/iodev.c
new file mode 100644
index 0000000..32b5831
--- /dev/null
+++ b/tools/src/iodev.c
@@ -0,0 +1,319 @@
+/* SPDX-License-Identifier: MIT */
+
+// #define DEBUG_IODEV
+
+#include "iodev.h"
+#include "memory.h"
+#include "string.h"
+
+#ifdef DEBUG_IODEV
+#define dprintf printf
+#else
+#define dprintf(...) \
+ do { \
+ } while (0)
+#endif
+
+#define CONSOLE_BUFFER_SIZE 8192
+
+extern struct iodev iodev_uart;
+extern struct iodev iodev_fb;
+extern struct iodev iodev_usb_vuart;
+
+struct iodev *iodevs[IODEV_MAX] = {
+ [IODEV_UART] = &iodev_uart,
+ [IODEV_FB] = &iodev_fb,
+ [IODEV_USB_VUART] = &iodev_usb_vuart,
+};
+
+char con_buf[CONSOLE_BUFFER_SIZE];
+size_t con_wp;
+size_t con_rp[IODEV_MAX];
+
+void iodev_register_device(iodev_id_t id, struct iodev *dev)
+{
+ if (id >= IODEV_MAX)
+ return;
+ iodevs[id] = dev;
+}
+
+struct iodev *iodev_unregister_device(iodev_id_t id)
+{
+ if (id < IODEV_USB0 || id >= IODEV_MAX)
+ return NULL;
+
+ struct iodev *dev = iodevs[id];
+ iodevs[id] = NULL;
+ return dev;
+}
+
+ssize_t iodev_can_read(iodev_id_t id)
+{
+ if (!iodevs[id] || !iodevs[id]->ops->can_read)
+ return 0;
+
+ if (mmu_active())
+ spin_lock(&iodevs[id]->lock);
+ ssize_t ret = iodevs[id]->ops->can_read(iodevs[id]->opaque);
+ if (mmu_active())
+ spin_unlock(&iodevs[id]->lock);
+ return ret;
+}
+
+bool iodev_can_write(iodev_id_t id)
+{
+ if (!iodevs[id] || !iodevs[id]->ops->can_write)
+ return false;
+
+ if (mmu_active())
+ spin_lock(&iodevs[id]->lock);
+ bool ret = iodevs[id]->ops->can_write(iodevs[id]->opaque);
+ if (mmu_active())
+ spin_unlock(&iodevs[id]->lock);
+ return ret;
+}
+
+ssize_t iodev_read(iodev_id_t id, void *buf, size_t length)
+{
+ if (!iodevs[id] || !iodevs[id]->ops->read)
+ return -1;
+
+ if (mmu_active())
+ spin_lock(&iodevs[id]->lock);
+ ssize_t ret = iodevs[id]->ops->read(iodevs[id]->opaque, buf, length);
+ if (mmu_active())
+ spin_unlock(&iodevs[id]->lock);
+ return ret;
+}
+
+ssize_t iodev_write(iodev_id_t id, const void *buf, size_t length)
+{
+ if (!iodevs[id] || !iodevs[id]->ops->write)
+ return -1;
+
+ if (mmu_active())
+ spin_lock(&iodevs[id]->lock);
+ ssize_t ret = iodevs[id]->ops->write(iodevs[id]->opaque, buf, length);
+ if (mmu_active())
+ spin_unlock(&iodevs[id]->lock);
+ return ret;
+}
+
+ssize_t iodev_queue(iodev_id_t id, const void *buf, size_t length)
+{
+ if (!iodevs[id] || !iodevs[id]->ops->queue)
+ return iodev_write(id, buf, length);
+
+ if (mmu_active())
+ spin_lock(&iodevs[id]->lock);
+ ssize_t ret = iodevs[id]->ops->queue(iodevs[id]->opaque, buf, length);
+ if (mmu_active())
+ spin_unlock(&iodevs[id]->lock);
+ return ret;
+}
+
+void iodev_flush(iodev_id_t id)
+{
+ if (!iodevs[id] || !iodevs[id]->ops->flush)
+ return;
+
+ if (mmu_active())
+ spin_lock(&iodevs[id]->lock);
+ iodevs[id]->ops->flush(iodevs[id]->opaque);
+ if (mmu_active())
+ spin_unlock(&iodevs[id]->lock);
+}
+
+void iodev_lock(iodev_id_t id)
+{
+ if (!iodevs[id])
+ return;
+
+ if (mmu_active())
+ spin_lock(&iodevs[id]->lock);
+}
+
+void iodev_unlock(iodev_id_t id)
+{
+ if (!iodevs[id])
+ return;
+
+ if (mmu_active())
+ spin_unlock(&iodevs[id]->lock);
+}
+
+int in_iodev = 0;
+
+static DECLARE_SPINLOCK(console_lock);
+
+void iodev_console_write(const void *buf, size_t length)
+{
+ bool do_lock = mmu_active();
+
+ if (!do_lock && !is_primary_core()) {
+ if (length && iodevs[IODEV_UART]->usage & USAGE_CONSOLE) {
+ iodevs[IODEV_UART]->ops->write(iodevs[IODEV_UART]->opaque, "*", 1);
+ iodevs[IODEV_UART]->ops->write(iodevs[IODEV_UART]->opaque, buf, length);
+ }
+ return;
+ }
+
+ if (do_lock)
+ spin_lock(&console_lock);
+
+ if (in_iodev) {
+ if (length && iodevs[IODEV_UART]->usage & USAGE_CONSOLE) {
+ iodevs[IODEV_UART]->ops->write(iodevs[IODEV_UART]->opaque, "+", 1);
+ iodevs[IODEV_UART]->ops->write(iodevs[IODEV_UART]->opaque, buf, length);
+ }
+ if (do_lock)
+ spin_unlock(&console_lock);
+ return;
+ }
+ in_iodev++;
+
+ dprintf(" iodev_console_write() wp=%d\n", con_wp);
+ for (iodev_id_t id = 0; id < IODEV_MAX; id++) {
+ if (!iodevs[id])
+ continue;
+
+ if (!(iodevs[id]->usage & USAGE_CONSOLE)) {
+ /* Drop buffer */
+ con_rp[id] = con_wp + length;
+ continue;
+ }
+
+ if (!iodev_can_write(id))
+ continue;
+
+ if (con_wp > CONSOLE_BUFFER_SIZE)
+ con_rp[id] = max(con_wp - CONSOLE_BUFFER_SIZE, con_rp[id]);
+
+ dprintf(" rp=%d\n", con_rp[id]);
+ // Flush existing buffer to device if possible
+ while (con_rp[id] < con_wp) {
+ size_t buf_rp = con_rp[id] % CONSOLE_BUFFER_SIZE;
+ size_t block = min(con_wp - con_rp[id], CONSOLE_BUFFER_SIZE - buf_rp);
+
+ dprintf(" write buf %d\n", block);
+ ssize_t ret = iodev_write(id, &con_buf[buf_rp], block);
+
+ if (ret <= 0)
+ goto next_dev;
+
+ con_rp[id] += ret;
+ }
+
+ const u8 *p = buf;
+ size_t wrote = 0;
+
+ // Write the current buffer
+ while (wrote < length) {
+ ssize_t ret = iodev_write(id, p, length - wrote);
+
+ if (ret <= 0)
+ goto next_dev;
+
+ con_rp[id] += ret;
+ wrote += ret;
+ p += ret;
+ }
+
+ next_dev:;
+ }
+
+ // Update console buffer
+
+ if (length > CONSOLE_BUFFER_SIZE) {
+ buf += (length - CONSOLE_BUFFER_SIZE);
+ con_wp += (length - CONSOLE_BUFFER_SIZE);
+ length = CONSOLE_BUFFER_SIZE;
+ }
+
+ while (length) {
+ size_t buf_wp = con_wp % CONSOLE_BUFFER_SIZE;
+ size_t block = min(length, CONSOLE_BUFFER_SIZE - buf_wp);
+ memcpy(&con_buf[buf_wp], buf, block);
+ buf += block;
+ con_wp += block;
+ length -= block;
+ }
+
+ in_iodev--;
+ if (do_lock)
+ spin_unlock(&console_lock);
+}
+
+void iodev_handle_events(iodev_id_t id)
+{
+ bool do_lock = mmu_active();
+
+ if (do_lock)
+ spin_lock(&console_lock);
+
+ if (in_iodev) {
+ if (do_lock)
+ spin_unlock(&console_lock);
+ return;
+ }
+
+ in_iodev++;
+
+ if (iodevs[id]->ops->handle_events)
+ iodevs[id]->ops->handle_events(iodevs[id]->opaque);
+
+ in_iodev--;
+
+ if (iodev_can_write(id))
+ iodev_console_write(NULL, 0);
+
+ if (do_lock)
+ spin_unlock(&console_lock);
+}
+
+void iodev_console_kick(void)
+{
+ iodev_console_write(NULL, 0);
+
+ for (iodev_id_t id = 0; id < IODEV_MAX; id++) {
+ if (!iodevs[id])
+ continue;
+ if (!(iodevs[id]->usage & USAGE_CONSOLE))
+ continue;
+
+ iodev_handle_events(id);
+ }
+}
+
+void iodev_console_flush(void)
+{
+ for (iodev_id_t id = 0; id < IODEV_MAX; id++) {
+ if (!iodevs[id])
+ continue;
+ if (!(iodevs[id]->usage & USAGE_CONSOLE))
+ continue;
+
+ iodev_flush(id);
+ }
+}
+
+void iodev_set_usage(iodev_id_t id, iodev_usage_t usage)
+{
+ if (iodevs[id])
+ iodevs[id]->usage = usage;
+}
+
+iodev_usage_t iodev_get_usage(iodev_id_t id)
+{
+ if (iodevs[id])
+ return iodevs[id]->usage;
+ return 0;
+}
+
+void *iodev_get_opaque(iodev_id_t id)
+{
+ if (id >= IODEV_MAX || !iodevs[id])
+ return NULL;
+
+ return iodevs[id]->opaque;
+}
diff --git a/tools/src/iodev.h b/tools/src/iodev.h
new file mode 100644
index 0000000..24187c7
--- /dev/null
+++ b/tools/src/iodev.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef IODEV_H
+#define IODEV_H
+
+#include "types.h"
+#include "utils.h"
+
+#define USB_IODEV_COUNT 8
+
+typedef enum _iodev_id_t {
+ IODEV_UART,
+ IODEV_FB,
+ IODEV_USB_VUART,
+ IODEV_USB0,
+ IODEV_MAX = IODEV_USB0 + USB_IODEV_COUNT,
+} iodev_id_t;
+
+typedef enum _iodev_usage_t {
+ USAGE_CONSOLE = BIT(0),
+ USAGE_UARTPROXY = BIT(1),
+} iodev_usage_t;
+
+struct iodev_ops {
+ ssize_t (*can_read)(void *opaque);
+ bool (*can_write)(void *opaque);
+ ssize_t (*read)(void *opaque, void *buf, size_t length);
+ ssize_t (*write)(void *opaque, const void *buf, size_t length);
+ ssize_t (*queue)(void *opaque, const void *buf, size_t length);
+ void (*flush)(void *opaque);
+ void (*handle_events)(void *opaque);
+};
+
+struct iodev {
+ const struct iodev_ops *ops;
+
+ spinlock_t lock;
+ iodev_usage_t usage;
+ void *opaque;
+};
+
+void iodev_register_device(iodev_id_t id, struct iodev *dev);
+struct iodev *iodev_unregister_device(iodev_id_t id);
+
+ssize_t iodev_can_read(iodev_id_t id);
+bool iodev_can_write(iodev_id_t id);
+ssize_t iodev_read(iodev_id_t id, void *buf, size_t length);
+ssize_t iodev_write(iodev_id_t id, const void *buf, size_t length);
+ssize_t iodev_queue(iodev_id_t id, const void *buf, size_t length);
+void iodev_flush(iodev_id_t id);
+void iodev_handle_events(iodev_id_t id);
+void iodev_lock(iodev_id_t id);
+void iodev_unlock(iodev_id_t id);
+
+void iodev_console_write(const void *buf, size_t length);
+void iodev_console_kick(void);
+void iodev_console_flush(void);
+
+iodev_usage_t iodev_get_usage(iodev_id_t id);
+void iodev_set_usage(iodev_id_t id, iodev_usage_t usage);
+void *iodev_get_opaque(iodev_id_t id);
+
+#endif
diff --git a/tools/src/iova.c b/tools/src/iova.c
new file mode 100644
index 0000000..c3146cd
--- /dev/null
+++ b/tools/src/iova.c
@@ -0,0 +1,233 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "iova.h"
+#include "malloc.h"
+#include "string.h"
+#include "utils.h"
+
+struct iova_block {
+ u64 iova;
+ size_t sz;
+ struct iova_block *next;
+};
+
+struct iova_domain {
+ u64 base;
+ u64 limit;
+ struct iova_block *free_list;
+};
+
+iova_domain_t *iovad_init(u64 base, u64 limit)
+{
+ if (base != ALIGN_UP(base, SZ_32M)) {
+ printf("iovad_init: base it not is not aligned to SZ_32M\n");
+ return NULL;
+ }
+
+ iova_domain_t *iovad = malloc(sizeof(*iovad));
+ if (!iovad)
+ return NULL;
+
+ memset(iovad, 0, sizeof(*iovad));
+
+ struct iova_block *blk = malloc(sizeof(*blk));
+ if (!blk) {
+ free(iovad);
+ return NULL;
+ }
+
+ /* don't hand out NULL pointers */
+ blk->iova = base;
+ blk->sz = limit - SZ_16K;
+ blk->next = NULL;
+ iovad->base = base;
+ iovad->limit = limit;
+ iovad->free_list = blk;
+
+ return iovad;
+}
+
+void iovad_shutdown(iova_domain_t *iovad, dart_dev_t *dart)
+{
+ struct iova_block *blk = iovad->free_list;
+
+ while (blk != NULL) {
+ struct iova_block *blk_free = blk;
+ blk = blk->next;
+
+ free(blk_free);
+ }
+
+ if (dart)
+ for (u64 addr = iovad->base; addr < iovad->limit; addr += SZ_32M)
+ dart_free_l2(dart, addr);
+
+ free(iovad);
+}
+
+bool iova_reserve(iova_domain_t *iovad, u64 iova, size_t sz)
+{
+ iova = ALIGN_DOWN(iova, SZ_16K);
+ sz = ALIGN_UP(sz, SZ_16K);
+
+ if (iova == 0) {
+ iova += SZ_16K;
+ sz -= SZ_16K;
+ }
+ if (sz == 0)
+ return true;
+
+ if (!iovad->free_list) {
+ printf("iova_reserve: trying to reserve iova range but empty free list\n");
+ return false;
+ }
+
+ struct iova_block *blk = iovad->free_list;
+ struct iova_block *blk_prev = NULL;
+ while (blk != NULL) {
+ if (iova >= blk->iova && iova < (blk->iova + blk->sz)) {
+ if (iova + sz >= (blk->iova + blk->sz)) {
+ printf("iova_reserve: tried to reserve [%lx; +%lx] but block in free list has "
+ "range [%lx; +%lx]\n",
+ iova, sz, blk->iova, blk->sz);
+ return false;
+ }
+
+ if (iova == blk->iova && sz == blk->sz) {
+ /* if the to-be-reserved range is present as a single block in the free list we just
+ * need to remove it */
+ if (blk_prev)
+ blk_prev->next = blk->next;
+ else
+ iovad->free_list = NULL;
+
+ free(blk);
+ return true;
+ } else if (iova == blk->iova) {
+ /* cut off the reserved range from the beginning */
+ blk->iova += sz;
+ blk->sz -= sz;
+ return true;
+ } else if (iova + sz == blk->iova + blk->sz) {
+ /* cut off the reserved range from the end */
+ blk->sz -= sz;
+ return true;
+ } else {
+ /* the to-be-reserved range is in the middle and we'll have to split this block */
+ struct iova_block *blk_new = malloc(sizeof(*blk_new));
+ if (!blk_new) {
+ printf("iova_reserve: out of memory.\n");
+ return false;
+ }
+
+ blk_new->iova = iova + sz;
+ blk_new->sz = blk->iova + blk->sz - blk_new->iova;
+ blk_new->next = blk->next;
+ blk->next = blk_new;
+ blk->sz = iova - blk->iova;
+ return true;
+ }
+ }
+
+ blk_prev = blk;
+ blk = blk->next;
+ }
+
+ printf("iova_reserve: tried to reserve [%lx; +%lx] but range is already used.\n", iova, sz);
+ return false;
+}
+
+u64 iova_alloc(iova_domain_t *iovad, size_t sz)
+{
+ sz = ALIGN_UP(sz, SZ_16K);
+
+ struct iova_block *blk_prev = NULL;
+ struct iova_block *blk = iovad->free_list;
+ while (blk != NULL) {
+ if (blk->sz == sz) {
+ u64 iova = blk->iova;
+
+ if (blk_prev)
+ blk_prev->next = blk->next;
+ else
+ iovad->free_list = blk->next;
+
+ free(blk);
+ return iova;
+ } else if (blk->sz > sz) {
+ u64 iova = blk->iova;
+
+ blk->iova += sz;
+ blk->sz -= sz;
+
+ return iova;
+ }
+
+ blk_prev = blk;
+ blk = blk->next;
+ }
+
+ return 0;
+}
+
+void iova_free(iova_domain_t *iovad, u64 iova, size_t sz)
+{
+ sz = ALIGN_UP(sz, SZ_16K);
+
+ struct iova_block *blk_prev = NULL;
+ struct iova_block *blk = iovad->free_list;
+
+ /* create a new free list if it's empty */
+ if (!blk) {
+ blk = malloc(sizeof(*blk));
+ if (!blk)
+ panic("out of memory in iovad_free");
+ blk->iova = iova;
+ blk->sz = sz;
+ blk->next = NULL;
+ iovad->free_list = blk;
+ return;
+ }
+
+ while (blk != NULL) {
+ if ((iova + sz) == blk->iova) {
+ /* extend the block at the beginning */
+ blk->iova -= sz;
+ blk->sz += sz;
+
+ /* if we have just extended the start of the free list we're already done */
+ if (!blk_prev)
+ return;
+
+ /* check if we can merge two blocks otherwise */
+ if ((blk_prev->iova + blk_prev->sz) == blk->iova) {
+ blk_prev->sz += blk->sz;
+ blk_prev->next = blk->next;
+ free(blk);
+ }
+
+ return;
+ } else if ((iova + sz) < blk->iova) {
+ /* create a new block */
+ struct iova_block *blk_new = malloc(sizeof(*blk_new));
+ if (!blk_new)
+ panic("iova_free: out of memory\n");
+
+ blk_new->iova = iova;
+ blk_new->sz = sz;
+ blk_new->next = blk;
+
+ if (blk_prev)
+ blk_prev->next = blk_new;
+ else
+ iovad->free_list = blk_new;
+
+ return;
+ }
+
+ blk_prev = blk;
+ blk = blk->next;
+ }
+
+ panic("iovad_free: corruption detected, unable to insert freed range\n");
+}
diff --git a/tools/src/iova.h b/tools/src/iova.h
new file mode 100644
index 0000000..1637be4
--- /dev/null
+++ b/tools/src/iova.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef IOVA_H
+#define IOVA_H
+
+#include "dart.h"
+#include "types.h"
+
+typedef struct iova_domain iova_domain_t;
+
+iova_domain_t *iovad_init(u64 base, u64 limit);
+void iovad_shutdown(iova_domain_t *iovad, dart_dev_t *dart);
+
+bool iova_reserve(iova_domain_t *iovad, u64 iova, size_t sz);
+u64 iova_alloc(iova_domain_t *iovad, size_t sz);
+void iova_free(iova_domain_t *iovad, u64 iova, size_t sz);
+
+#endif
diff --git a/tools/src/kboot.c b/tools/src/kboot.c
new file mode 100644
index 0000000..c56c0e7
--- /dev/null
+++ b/tools/src/kboot.c
@@ -0,0 +1,1937 @@
+/* SPDX-License-Identifier: MIT */
+
+#include <stdint.h>
+
+#include "kboot.h"
+#include "adt.h"
+#include "assert.h"
+#include "dapf.h"
+#include "devicetree.h"
+#include "exception.h"
+#include "firmware.h"
+#include "malloc.h"
+#include "memory.h"
+#include "pcie.h"
+#include "pmgr.h"
+#include "sep.h"
+#include "smp.h"
+#include "types.h"
+#include "usb.h"
+#include "utils.h"
+#include "xnuboot.h"
+
+#include "libfdt/libfdt.h"
+
+#define MAX_CHOSEN_PARAMS 16
+
+#define MAX_ATC_DEVS 8
+#define MAX_CIO_DEVS 8
+
+#define MAX_DISP_MAPPINGS 8
+
+static void *dt = NULL;
+static int dt_bufsize = 0;
+static void *initrd_start = NULL;
+static size_t initrd_size = 0;
+static char *chosen_params[MAX_CHOSEN_PARAMS][2];
+
+extern const char *const m1n1_version;
+
+int dt_set_gpu(void *dt);
+
+#define DT_ALIGN 16384
+
+#define bail(...) \
+ do { \
+ printf(__VA_ARGS__); \
+ return -1; \
+ } while (0)
+
+#define bail_cleanup(...) \
+ do { \
+ printf(__VA_ARGS__); \
+ ret = -1; \
+ goto err; \
+ } while (0)
+
+void get_notchless_fb(u64 *fb_base, u64 *fb_height)
+{
+ *fb_base = cur_boot_args.video.base;
+ *fb_height = cur_boot_args.video.height;
+
+ int node = adt_path_offset(adt, "/product");
+
+ if (node < 0) {
+ printf("FDT: /product node not found\n");
+ return;
+ }
+
+ u32 val;
+
+ if (ADT_GETPROP(adt, node, "partially-occluded-display", &val) < 0 || !val) {
+ printf("FDT: No notch detected\n");
+ return;
+ }
+
+ u64 hfrac = cur_boot_args.video.height * 16 / cur_boot_args.video.width;
+ u64 new_height = cur_boot_args.video.width * hfrac / 16;
+
+ if (new_height == cur_boot_args.video.height) {
+ printf("FDT: Notch detected, but display aspect is already 16:%lu?\n", hfrac);
+ return;
+ }
+
+ u64 offset = cur_boot_args.video.height - new_height;
+
+ printf("display: Hiding notch, %lux%lu -> %lux%lu (+%lu, 16:%lu)\n", cur_boot_args.video.width,
+ cur_boot_args.video.height, cur_boot_args.video.width, new_height, offset, hfrac);
+
+ *fb_base += cur_boot_args.video.stride * offset;
+ *fb_height = new_height;
+}
+
+static int dt_set_rng_seed_sep(int node)
+{
+ u64 kaslr_seed;
+ uint8_t rng_seed[128]; // same size used by Linux for kexec
+
+ if (sep_get_random(&kaslr_seed, sizeof(kaslr_seed)) != sizeof(kaslr_seed))
+ bail("SEP: couldn't get enough random bytes for KASLR seed");
+ if (sep_get_random(rng_seed, sizeof(rng_seed)) != sizeof(rng_seed))
+ bail("SEP: couldn't get enough random bytes for RNG seed");
+
+ if (fdt_setprop_u64(dt, node, "kaslr-seed", kaslr_seed))
+ bail("FDT: couldn't set kaslr-seed\n");
+ if (fdt_setprop(dt, node, "rng-seed", rng_seed, sizeof(rng_seed)))
+ bail("FDT: couldn't set rng-seed\n");
+
+ printf("FDT: Passing %ld bytes of KASLR seed and %ld bytes of random seed\n",
+ sizeof(kaslr_seed), sizeof(rng_seed));
+
+ return 0;
+}
+
+static int dt_set_rng_seed_adt(int node)
+{
+ int anode = adt_path_offset(adt, "/chosen");
+
+ if (anode < 0)
+ bail("ADT: /chosen not found\n");
+
+ const uint8_t *random_seed;
+ u32 seed_length;
+
+ random_seed = adt_getprop(adt, anode, "random-seed", &seed_length);
+ if (random_seed) {
+ printf("ADT: %d bytes of random seed available\n", seed_length);
+
+ if (seed_length >= sizeof(u64)) {
+ u64 kaslr_seed;
+
+ memcpy(&kaslr_seed, random_seed, sizeof(kaslr_seed));
+
+ // Ideally we would throw away the kaslr_seed part of random_seed
+ // and avoid reusing it. However, Linux wants 64 bytes of bootloader
+ // random seed to consider its CRNG initialized, which is exactly
+ // how much iBoot gives us. This probably doesn't matter, since
+ // that entropy is going to get shuffled together and Linux makes
+ // sure to clear the FDT randomness after using it anyway, but just
+ // in case let's mix in a few bits from our own KASLR base to make
+ // kaslr_seed unique.
+
+ kaslr_seed ^= (u64)cur_boot_args.virt_base;
+
+ if (fdt_setprop_u64(dt, node, "kaslr-seed", kaslr_seed))
+ bail("FDT: couldn't set kaslr-seed\n");
+
+ printf("FDT: KASLR seed initialized\n");
+ } else {
+ printf("ADT: not enough random data for kaslr-seed\n");
+ }
+
+ if (seed_length) {
+ if (fdt_setprop(dt, node, "rng-seed", random_seed, seed_length))
+ bail("FDT: couldn't set rng-seed\n");
+
+ printf("FDT: Passing %d bytes of random seed\n", seed_length);
+ }
+ } else {
+ printf("ADT: no random-seed available!\n");
+ }
+
+ return 0;
+}
+
+static int dt_set_chosen(void)
+{
+
+ int node = fdt_path_offset(dt, "/chosen");
+ if (node < 0)
+ bail("FDT: /chosen node not found in devtree\n");
+
+ for (int i = 0; i < MAX_CHOSEN_PARAMS; i++) {
+ if (!chosen_params[i][0])
+ break;
+
+ const char *name = chosen_params[i][0];
+ const char *value = chosen_params[i][1];
+ if (fdt_setprop(dt, node, name, value, strlen(value) + 1) < 0)
+ bail("FDT: couldn't set chosen.%s property\n", name);
+ printf("FDT: %s = '%s'\n", name, value);
+ }
+
+ if (initrd_start && initrd_size) {
+ if (fdt_setprop_u64(dt, node, "linux,initrd-start", (u64)initrd_start))
+ bail("FDT: couldn't set chosen.linux,initrd-start property\n");
+
+ u64 end = ((u64)initrd_start) + initrd_size;
+ if (fdt_setprop_u64(dt, node, "linux,initrd-end", end))
+ bail("FDT: couldn't set chosen.linux,initrd-end property\n");
+
+ if (fdt_add_mem_rsv(dt, (u64)initrd_start, initrd_size))
+ bail("FDT: couldn't add reservation for the initrd\n");
+
+ printf("FDT: initrd at %p size 0x%lx\n", initrd_start, initrd_size);
+ }
+
+ if (cur_boot_args.video.base) {
+ int fb = fdt_path_offset(dt, "/chosen/framebuffer");
+ if (fb < 0)
+ bail("FDT: /chosen node not found in devtree\n");
+
+ u64 fb_base, fb_height;
+ get_notchless_fb(&fb_base, &fb_height);
+ u64 fb_size = cur_boot_args.video.stride * fb_height;
+ u64 fbreg[2] = {cpu_to_fdt64(fb_base), cpu_to_fdt64(fb_size)};
+ char fbname[32];
+
+ snprintf(fbname, sizeof(fbname), "framebuffer@%lx", fb_base);
+
+ if (fdt_setprop(dt, fb, "reg", fbreg, sizeof(fbreg)))
+ bail("FDT: couldn't set framebuffer.reg property\n");
+
+ if (fdt_set_name(dt, fb, fbname))
+ bail("FDT: couldn't set framebuffer name\n");
+
+ if (fdt_setprop_u32(dt, fb, "width", cur_boot_args.video.width))
+ bail("FDT: couldn't set framebuffer width\n");
+
+ if (fdt_setprop_u32(dt, fb, "height", fb_height))
+ bail("FDT: couldn't set framebuffer height\n");
+
+ if (fdt_setprop_u32(dt, fb, "stride", cur_boot_args.video.stride))
+ bail("FDT: couldn't set framebuffer stride\n");
+
+ const char *format = NULL;
+
+ switch (cur_boot_args.video.depth & 0xff) {
+ case 32:
+ format = "x8r8g8b8";
+ break;
+ case 30:
+ format = "x2r10g10b10";
+ break;
+ case 16:
+ format = "r5g6b5";
+ break;
+ default:
+ printf("FDT: unsupported fb depth %lu, not enabling\n", cur_boot_args.video.depth);
+ return 0; // Do not error out, but don't set the FB
+ }
+
+ if (fdt_setprop_string(dt, fb, "format", format))
+ bail("FDT: couldn't set framebuffer format\n");
+
+ fdt_delprop(dt, fb, "status"); // may fail if it does not exist
+
+ printf("FDT: %s base 0x%lx size 0x%lx\n", fbname, fb_base, fb_size);
+
+ // We do not need to reserve the framebuffer, as it will be excluded from the usable RAM
+ // range already.
+
+ // save notch height in the dcp node if present
+ if (cur_boot_args.video.height - fb_height) {
+ int dcp = fdt_path_offset(dt, "dcp");
+ if (dcp >= 0)
+ if (fdt_appendprop_u32(dt, dcp, "apple,notch-height",
+ cur_boot_args.video.height - fb_height))
+ printf("FDT: couldn't set apple,notch-height\n");
+ }
+ }
+ node = fdt_path_offset(dt, "/chosen");
+ if (node < 0)
+ bail("FDT: /chosen node not found in devtree\n");
+
+ int ipd = adt_path_offset(adt, "/arm-io/spi3/ipd");
+ if (ipd < 0)
+ ipd = adt_path_offset(adt, "/arm-io/dockchannel-mtp/mtp-transport/keyboard");
+
+ if (ipd < 0) {
+ printf("ADT: no keyboard found\n");
+ } else {
+ u32 len;
+ const u8 *kblang = adt_getprop(adt, ipd, "kblang-calibration", &len);
+ if (kblang && len >= 2) {
+ if (fdt_setprop_u32(dt, node, "asahi,kblang-code", kblang[1]))
+ bail("FDT: couldn't set asahi,kblang-code");
+ } else {
+ printf("ADT: kblang-calibration not found, no keyboard layout\n");
+ }
+ }
+
+ if (fdt_setprop(dt, node, "asahi,iboot1-version", system_firmware.iboot,
+ strlen(system_firmware.iboot) + 1))
+ bail("FDT: couldn't set asahi,iboot1-version");
+
+ if (fdt_setprop(dt, node, "asahi,system-fw-version", system_firmware.string,
+ strlen(system_firmware.string) + 1))
+ bail("FDT: couldn't set asahi,system-fw-version");
+
+ if (fdt_setprop(dt, node, "asahi,iboot2-version", os_firmware.iboot,
+ strlen(os_firmware.iboot) + 1))
+ bail("FDT: couldn't set asahi,iboot2-version");
+
+ if (fdt_setprop(dt, node, "asahi,os-fw-version", os_firmware.string,
+ strlen(os_firmware.string) + 1))
+ bail("FDT: couldn't set asahi,os-fw-version");
+
+ if (fdt_setprop(dt, node, "asahi,m1n1-stage2-version", m1n1_version, strlen(m1n1_version) + 1))
+ bail("FDT: couldn't set asahi,m1n1-stage2-version");
+
+ if (dt_set_rng_seed_sep(node))
+ return dt_set_rng_seed_adt(node);
+
+ return 0;
+}
+
+static int dt_set_memory(void)
+{
+ int anode = adt_path_offset(adt, "/chosen");
+
+ if (anode < 0)
+ bail("ADT: /chosen not found\n");
+
+ u64 dram_base, dram_size;
+
+ if (ADT_GETPROP(adt, anode, "dram-base", &dram_base) < 0)
+ bail("ADT: Failed to get dram-base\n");
+ if (ADT_GETPROP(adt, anode, "dram-size", &dram_size) < 0)
+ bail("ADT: Failed to get dram-size\n");
+
+ // Tell the kernel our usable memory range. We cannot declare all of DRAM, and just reserve the
+ // bottom and top, because the kernel would still map it (and just not use it), which breaks
+ // ioremap (e.g. simplefb).
+
+ u64 dram_min = cur_boot_args.phys_base;
+ u64 dram_max = cur_boot_args.phys_base + cur_boot_args.mem_size;
+
+ printf("FDT: DRAM at 0x%lx size 0x%lx\n", dram_base, dram_size);
+ printf("FDT: Usable memory is 0x%lx..0x%lx (0x%lx)\n", dram_min, dram_max, dram_max - dram_min);
+
+ u64 memreg[2] = {cpu_to_fdt64(dram_min), cpu_to_fdt64(dram_max - dram_min)};
+
+ int node = fdt_path_offset(dt, "/memory");
+ if (node < 0)
+ bail("FDT: /memory node not found in devtree\n");
+
+ if (fdt_setprop(dt, node, "reg", memreg, sizeof(memreg)))
+ bail("FDT: couldn't set memory.reg property\n");
+
+ return 0;
+}
+
+static int dt_set_serial_number(void)
+{
+
+ int fdt_root = fdt_path_offset(dt, "/");
+ int adt_root = adt_path_offset(adt, "/");
+
+ if (fdt_root < 0)
+ bail("FDT: could not open a handle to FDT root.\n");
+ if (adt_root < 0)
+ bail("ADT: could not open a handle to ADT root.\n");
+
+ u32 sn_len;
+ const char *serial_number = adt_getprop(adt, adt_root, "serial-number", &sn_len);
+ if (fdt_setprop_string(dt, fdt_root, "serial-number", serial_number))
+ bail("FDT: unable to set device serial number!\n");
+ printf("FDT: reporting device serial number: %s\n", serial_number);
+
+ return 0;
+}
+
+static int dt_set_cpus(void)
+{
+ int ret = 0;
+
+ int cpus = fdt_path_offset(dt, "/cpus");
+ if (cpus < 0)
+ bail("FDT: /cpus node not found in devtree\n");
+
+ uint32_t *pruned_phandles = malloc(MAX_CPUS * sizeof(uint32_t));
+ size_t pruned = 0;
+ if (!pruned_phandles)
+ bail("FDT: out of memory\n");
+
+ /* Prune CPU nodes */
+ int node, cpu = 0;
+ for (node = fdt_first_subnode(dt, cpus); node >= 0;) {
+ const char *name = fdt_get_name(dt, node, NULL);
+ if (strncmp(name, "cpu@", 4))
+ goto next_node;
+
+ if (cpu > MAX_CPUS)
+ bail_cleanup("Maximum number of CPUs exceeded, consider increasing MAX_CPUS\n");
+
+ const fdt64_t *prop = fdt_getprop(dt, node, "reg", NULL);
+ if (!prop)
+ bail_cleanup("FDT: failed to get reg property of CPU\n");
+
+ u64 dt_mpidr = fdt64_ld(prop);
+
+ if (dt_mpidr == (mrs(MPIDR_EL1) & 0xFFFFFF))
+ goto next_cpu;
+
+ if (!smp_is_alive(cpu)) {
+ printf("FDT: CPU %d is not alive, disabling...\n", cpu);
+ pruned_phandles[pruned++] = fdt_get_phandle(dt, node);
+
+ int next = fdt_next_subnode(dt, node);
+ fdt_nop_node(dt, node);
+ cpu++;
+ node = next;
+ continue;
+ }
+
+ u64 mpidr = smp_get_mpidr(cpu);
+
+ if (dt_mpidr != mpidr)
+ bail_cleanup("FDT: DT CPU %d MPIDR mismatch: 0x%lx != 0x%lx\n", cpu, dt_mpidr, mpidr);
+
+ u64 release_addr = smp_get_release_addr(cpu);
+ if (fdt_setprop_inplace_u64(dt, node, "cpu-release-addr", release_addr))
+ bail_cleanup("FDT: couldn't set cpu-release-addr property\n");
+
+ printf("FDT: CPU %d MPIDR=0x%lx release-addr=0x%lx\n", cpu, mpidr, release_addr);
+
+ next_cpu:
+ cpu++;
+ next_node:
+ node = fdt_next_subnode(dt, node);
+ }
+
+ if ((node < 0) && (node != -FDT_ERR_NOTFOUND)) {
+ bail_cleanup("FDT: error iterating through CPUs\n");
+ }
+
+ /* Prune AIC PMU affinities */
+ int aic = fdt_node_offset_by_compatible(dt, -1, "apple,aic");
+ if (aic == -FDT_ERR_NOTFOUND)
+ aic = fdt_node_offset_by_compatible(dt, -1, "apple,aic2");
+ if (aic < 0)
+ bail_cleanup("FDT: Failed to find AIC node\n");
+
+ int affinities = fdt_subnode_offset(dt, aic, "affinities");
+ if (affinities < 0) {
+ printf("FDT: Failed to find AIC affinities node, ignoring...\n");
+ } else {
+ int node;
+ for (node = fdt_first_subnode(dt, affinities); node >= 0;
+ node = fdt_next_subnode(dt, node)) {
+ int len;
+ const fdt32_t *phs = fdt_getprop(dt, node, "cpus", &len);
+ if (!phs)
+ bail_cleanup("FDT: Failed to find cpus property under AIC affinity\n");
+
+ fdt32_t *new_phs = malloc(len);
+ size_t index = 0;
+ size_t count = len / sizeof(fdt32_t);
+
+ for (size_t i = 0; i < count; i++) {
+ uint32_t phandle = fdt32_ld(&phs[i]);
+ bool prune = false;
+
+ for (size_t j = 0; j < pruned; j++) {
+ if (pruned_phandles[j] == phandle) {
+ prune = true;
+ break;
+ }
+ }
+ if (!prune)
+ new_phs[index++] = phs[i];
+ }
+
+ ret = fdt_setprop(dt, node, "cpus", new_phs, sizeof(fdt32_t) * index);
+ free(new_phs);
+
+ if (ret < 0)
+ bail_cleanup("FDT: Failed to set cpus property under AIC affinity\n");
+
+ const char *name = fdt_get_name(dt, node, NULL);
+ printf("FDT: Pruned %ld/%ld CPU references in [AIC]/affinities/%s\n", count - index,
+ count, name);
+ }
+
+ if ((node < 0) && (node != -FDT_ERR_NOTFOUND))
+ bail_cleanup("FDT: Error iterating through affinity nodes\n");
+ }
+
+ /* Prune CPU-map */
+ int cpu_map = fdt_path_offset(dt, "/cpus/cpu-map");
+ if (cpu_map < 0) {
+ printf("FDT: /cpus/cpu-map node not found in devtree, ignoring...\n");
+ free(pruned_phandles);
+ return 0;
+ }
+
+ int cluster_idx = 0;
+ int cluster_node;
+ for (cluster_node = fdt_first_subnode(dt, cpu_map); cluster_node >= 0;) {
+ const char *name = fdt_get_name(dt, cluster_node, NULL);
+ int cpu_idx = 0;
+
+ if (strncmp(name, "cluster", 7))
+ goto next_cluster;
+
+ int cpu_node;
+ for (cpu_node = fdt_first_subnode(dt, cluster_node); cpu_node >= 0;) {
+ const char *cpu_name = fdt_get_name(dt, cpu_node, NULL);
+
+ if (strncmp(cpu_name, "core", 4))
+ goto next_map_cpu;
+
+ int len;
+ const fdt32_t *cpu_ph = fdt_getprop(dt, cpu_node, "cpu", &len);
+
+ if (!cpu_ph || len != sizeof(*cpu_ph))
+ bail_cleanup("FDT: Failed to get cpu prop for /cpus/cpu-map/%s/%s\n", name,
+ cpu_name);
+
+ uint32_t phandle = fdt32_ld(cpu_ph);
+ bool prune = false;
+ for (size_t i = 0; i < pruned; i++) {
+ if (pruned_phandles[i] == phandle) {
+ prune = true;
+ break;
+ }
+ }
+
+ if (prune) {
+ printf("FDT: Pruning /cpus/cpu-map/%s/%s\n", name, cpu_name);
+
+ int next = fdt_next_subnode(dt, cpu_node);
+ fdt_nop_node(dt, cpu_node);
+ cpu_node = next;
+ continue;
+ } else {
+ char new_name[16];
+
+ snprintf(new_name, 16, "core%d", cpu_idx++);
+ fdt_set_name(dt, cpu_node, new_name);
+ }
+ next_map_cpu:
+ cpu_node = fdt_next_subnode(dt, cpu_node);
+ }
+
+ if ((cpu_node < 0) && (cpu_node != -FDT_ERR_NOTFOUND))
+ bail_cleanup("FDT: Error iterating through CPU nodes\n");
+
+ if (cpu_idx == 0) {
+ printf("FDT: Pruning /cpus/cpu-map/%s\n", name);
+
+ int next = fdt_next_subnode(dt, cluster_node);
+ fdt_nop_node(dt, cluster_node);
+ cluster_node = next;
+ continue;
+ } else {
+ char new_name[16];
+
+ snprintf(new_name, 16, "cluster%d", cluster_idx++);
+ fdt_set_name(dt, cluster_node, new_name);
+ }
+ next_cluster:
+ cluster_node = fdt_next_subnode(dt, cluster_node);
+ }
+
+ if ((cluster_node < 0) && (cluster_node != -FDT_ERR_NOTFOUND))
+ bail_cleanup("FDT: Error iterating through CPU clusters\n");
+
+ return 0;
+
+err:
+ free(pruned_phandles);
+ return ret;
+}
+
+static struct {
+ const char *alias;
+ const char *fdt_property;
+ bool swap;
+} mac_address_devices[] = {
+ {
+ .alias = "bluetooth0",
+ .fdt_property = "local-bd-address",
+ .swap = true,
+ },
+ {
+ .alias = "ethernet0",
+ .fdt_property = "local-mac-address",
+ },
+ {
+ .alias = "wifi0",
+ .fdt_property = "local-mac-address",
+ },
+};
+
+static int dt_set_mac_addresses(void)
+{
+ int anode = adt_path_offset(adt, "/chosen");
+
+ if (anode < 0)
+ bail("ADT: /chosen not found\n");
+
+ for (size_t i = 0; i < sizeof(mac_address_devices) / sizeof(*mac_address_devices); i++) {
+ char propname[32];
+ snprintf(propname, sizeof(propname), "mac-address-%s", mac_address_devices[i].alias);
+
+ uint8_t addr[6];
+ if (ADT_GETPROP_ARRAY(adt, anode, propname, addr) < 0)
+ continue;
+
+ if (mac_address_devices[i].swap) {
+ for (size_t i = 0; i < sizeof(addr) / 2; ++i) {
+ uint8_t tmp = addr[i];
+ addr[i] = addr[sizeof(addr) - i - 1];
+ addr[sizeof(addr) - i - 1] = tmp;
+ }
+ }
+
+ const char *path = fdt_get_alias(dt, mac_address_devices[i].alias);
+ if (path == NULL)
+ continue;
+
+ int node = fdt_path_offset(dt, path);
+ if (node < 0)
+ continue;
+
+ fdt_setprop(dt, node, mac_address_devices[i].fdt_property, addr, sizeof(addr));
+ }
+
+ return 0;
+}
+
+static int dt_set_bluetooth_cal(int anode, int node, const char *adt_name, const char *fdt_name)
+{
+ u32 len;
+ const u8 *cal_blob = adt_getprop(adt, anode, adt_name, &len);
+
+ if (!cal_blob || !len)
+ bail("ADT: Failed to get %s", adt_name);
+
+ fdt_setprop(dt, node, fdt_name, cal_blob, len);
+ return 0;
+}
+
+static int dt_set_bluetooth(void)
+{
+ int ret;
+ int anode = adt_path_offset(adt, "/arm-io/bluetooth");
+
+ if (anode < 0)
+ bail("ADT: /arm-io/bluetooth not found\n");
+
+ const char *path = fdt_get_alias(dt, "bluetooth0");
+ if (path == NULL)
+ return 0;
+
+ int node = fdt_path_offset(dt, path);
+ if (node < 0)
+ return 0;
+
+ ret = dt_set_bluetooth_cal(anode, node, "bluetooth-taurus-calibration-bf",
+ "brcm,taurus-bf-cal-blob");
+ if (ret)
+ return ret;
+
+ ret = dt_set_bluetooth_cal(anode, node, "bluetooth-taurus-calibration", "brcm,taurus-cal-blob");
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int dt_set_multitouch(void)
+{
+ const char *path = fdt_get_alias(dt, "touchbar0");
+ if (path == NULL)
+ return 0;
+
+ int node = fdt_path_offset(dt, path);
+ if (node < 0)
+ bail("FDT: alias points at nonexistent node");
+
+ int anode = adt_path_offset(adt, "/arm-io/spi0/multi-touch");
+ if (anode < 0)
+ bail("ADT /arm-io/spi0/multi-touch not found\n");
+
+ u32 len;
+ const u8 *cal_blob = adt_getprop(adt, anode, "multi-touch-calibration", &len);
+ if (!cal_blob || !len)
+ bail("ADT: Failed to get multi-touch-calibration");
+
+ fdt_setprop(dt, node, "apple,z2-cal-blob", cal_blob, len);
+ return 0;
+}
+
+static int dt_set_wifi(void)
+{
+ int anode = adt_path_offset(adt, "/arm-io/wlan");
+
+ if (anode < 0)
+ bail("ADT: /arm-io/wlan not found\n");
+
+ uint8_t info[16];
+ if (ADT_GETPROP_ARRAY(adt, anode, "wifi-antenna-sku-info", info) < 0)
+ bail("ADT: Failed to get wifi-antenna-sku-info");
+
+ const char *path = fdt_get_alias(dt, "wifi0");
+ if (path == NULL)
+ return 0;
+
+ int node = fdt_path_offset(dt, path);
+ if (node < 0)
+ return 0;
+
+ char antenna[8];
+ memcpy(antenna, &info[8], sizeof(antenna));
+ fdt_setprop_string(dt, node, "apple,antenna-sku", antenna);
+
+ u32 len;
+ const u8 *cal_blob = adt_getprop(adt, anode, "wifi-calibration-msf", &len);
+
+ if (!cal_blob || !len)
+ bail("ADT: Failed to get wifi-calibration-msf");
+
+ fdt_setprop(dt, node, "brcm,cal-blob", cal_blob, len);
+
+ return 0;
+}
+
+static void dt_set_uboot_dm_preloc(int node)
+{
+ // Tell U-Boot to bind this node early
+ fdt_setprop_empty(dt, node, "u-boot,dm-pre-reloc");
+ fdt_setprop_empty(dt, node, "bootph-all");
+
+ // Make sure the power domains are bound early as well
+ int pds_size;
+ const fdt32_t *pds = fdt_getprop(dt, node, "power-domains", &pds_size);
+ if (!pds)
+ return;
+
+ fdt32_t *phandles = malloc(pds_size);
+ if (!phandles) {
+ printf("FDT: out of memory\n");
+ return;
+ }
+ memcpy(phandles, pds, pds_size);
+
+ for (int i = 0; i < pds_size / 4; i++) {
+ node = fdt_node_offset_by_phandle(dt, fdt32_ld(&phandles[i]));
+ if (node < 0)
+ continue;
+ dt_set_uboot_dm_preloc(node);
+
+ // restore node offset after DT update
+ node = fdt_node_offset_by_phandle(dt, fdt32_ld(&phandles[i]));
+ if (node < 0)
+ continue;
+
+ // And make sure the PMGR node is bound early too
+ node = fdt_parent_offset(dt, node);
+ if (node < 0)
+ continue;
+ dt_set_uboot_dm_preloc(node);
+ }
+
+ free(phandles);
+}
+
+static int dt_set_uboot(void)
+{
+ // Make sure that U-Boot can initialize the serial port in its
+ // pre-relocation phase by marking its node and the nodes of the
+ // power domains it depends on with a "u-boot,dm-pre-reloc"
+ // property.
+
+ const char *path = fdt_get_alias(dt, "serial0");
+ if (path == NULL)
+ return 0;
+
+ int node = fdt_path_offset(dt, path);
+ if (node < 0)
+ return 0;
+
+ dt_set_uboot_dm_preloc(node);
+ return 0;
+}
+
+struct atc_tunable {
+ u32 offset : 24;
+ u32 size : 8;
+ u32 mask;
+ u32 value;
+} PACKED;
+static_assert(sizeof(struct atc_tunable) == 12, "Invalid atc_tunable size");
+
+struct adt_tunable_info {
+ const char *adt_name;
+ const char *fdt_name;
+ size_t reg_offset;
+ size_t reg_size;
+ bool required;
+};
+
+static const struct adt_tunable_info atc_tunables[] = {
+ /* global tunables applied after power on or reset */
+ {"tunable_ATC0AXI2AF", "apple,tunable-axi2af", 0x0, 0x4000, true},
+ {"tunable_ATC_FABRIC", "apple,tunable-common", 0x45000, 0x4000, true},
+ {"tunable_AUS_CMN_TOP", "apple,tunable-common", 0x800, 0x4000, true},
+ {"tunable_AUS_CMN_SHM", "apple,tunable-common", 0xa00, 0x4000, true},
+ {"tunable_AUSPLL_CORE", "apple,tunable-common", 0x2200, 0x4000, true},
+ {"tunable_AUSPLL_TOP", "apple,tunable-common", 0x2000, 0x4000, true},
+ {"tunable_CIO3PLL_CORE", "apple,tunable-common", 0x2a00, 0x4000, true},
+ {"tunable_CIO3PLL_TOP", "apple,tunable-common", 0x2800, 0x4000, true},
+ {"tunable_CIO_CIO3PLL_TOP", "apple,tunable-common", 0x2800, 0x4000, false},
+ {"tunable_USB_ACIOPHY_TOP", "apple,tunable-common", 0x0, 0x4000, true},
+ /* lane-specific tunables applied after a cable is connected */
+ {"tunable_DP_LN0_AUSPMA_TX_TOP", "apple,tunable-lane0-dp", 0xc000, 0x1000, true},
+ {"tunable_DP_LN1_AUSPMA_TX_TOP", "apple,tunable-lane1-dp", 0x13000, 0x1000, true},
+ {"tunable_USB_LN0_AUSPMA_RX_TOP", "apple,tunable-lane0-usb", 0x9000, 0x1000, true},
+ {"tunable_USB_LN0_AUSPMA_RX_EQ", "apple,tunable-lane0-usb", 0xa000, 0x1000, true},
+ {"tunable_USB_LN0_AUSPMA_RX_SHM", "apple,tunable-lane0-usb", 0xb000, 0x1000, true},
+ {"tunable_USB_LN0_AUSPMA_TX_TOP", "apple,tunable-lane0-usb", 0xc000, 0x1000, true},
+ {"tunable_USB_LN1_AUSPMA_RX_TOP", "apple,tunable-lane1-usb", 0x10000, 0x1000, true},
+ {"tunable_USB_LN1_AUSPMA_RX_EQ", "apple,tunable-lane1-usb", 0x11000, 0x1000, true},
+ {"tunable_USB_LN1_AUSPMA_RX_SHM", "apple,tunable-lane1-usb", 0x12000, 0x1000, true},
+ {"tunable_USB_LN1_AUSPMA_TX_TOP", "apple,tunable-lane1-usb", 0x13000, 0x1000, true},
+ {"tunable_CIO_LN0_AUSPMA_RX_TOP", "apple,tunable-lane0-cio", 0x9000, 0x1000, true},
+ {"tunable_CIO_LN0_AUSPMA_RX_EQ", "apple,tunable-lane0-cio", 0xa000, 0x1000, true},
+ {"tunable_CIO_LN0_AUSPMA_RX_SHM", "apple,tunable-lane0-cio", 0xb000, 0x1000, true},
+ {"tunable_CIO_LN0_AUSPMA_TX_TOP", "apple,tunable-lane0-cio", 0xc000, 0x1000, true},
+ {"tunable_CIO_LN1_AUSPMA_RX_TOP", "apple,tunable-lane1-cio", 0x10000, 0x1000, true},
+ {"tunable_CIO_LN1_AUSPMA_RX_EQ", "apple,tunable-lane1-cio", 0x11000, 0x1000, true},
+ {"tunable_CIO_LN1_AUSPMA_RX_SHM", "apple,tunable-lane1-cio", 0x12000, 0x1000, true},
+ {"tunable_CIO_LN1_AUSPMA_TX_TOP", "apple,tunable-lane1-cio", 0x13000, 0x1000, true},
+};
+
+static int dt_append_atc_tunable(int adt_node, int fdt_node,
+ const struct adt_tunable_info *tunable_info)
+{
+ u32 tunables_len;
+ const struct atc_tunable *tunable_adt =
+ adt_getprop(adt, adt_node, tunable_info->adt_name, &tunables_len);
+
+ if (!tunable_adt) {
+ printf("ADT: tunable %s not found\n", tunable_info->adt_name);
+
+ if (tunable_info->required)
+ return -1;
+ else
+ return 0;
+ }
+
+ if (tunables_len % sizeof(*tunable_adt)) {
+ printf("ADT: tunable %s with invalid length %d\n", tunable_info->adt_name, tunables_len);
+ return -1;
+ }
+
+ u32 n_tunables = tunables_len / sizeof(*tunable_adt);
+ for (size_t j = 0; j < n_tunables; j++) {
+ const struct atc_tunable *tunable = &tunable_adt[j];
+
+ if (tunable->size != 32) {
+ printf("kboot: ATC tunable has invalid size %d\n", tunable->size);
+ return -1;
+ }
+
+ if (tunable->offset % (tunable->size / 8)) {
+ printf("kboot: ATC tunable has unaligned offset %x\n", tunable->offset);
+ return -1;
+ }
+
+ if (tunable->offset + (tunable->size / 8) > tunable_info->reg_size) {
+ printf("kboot: ATC tunable has invalid offset %x\n", tunable->offset);
+ return -1;
+ }
+
+ if (fdt_appendprop_u32(dt, fdt_node, tunable_info->fdt_name,
+ tunable->offset + tunable_info->reg_offset) < 0)
+ return -1;
+ if (fdt_appendprop_u32(dt, fdt_node, tunable_info->fdt_name, tunable->mask) < 0)
+ return -1;
+ if (fdt_appendprop_u32(dt, fdt_node, tunable_info->fdt_name, tunable->value) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static void dt_copy_atc_tunables(const char *adt_path, const char *dt_alias)
+{
+ int ret;
+
+ int adt_node = adt_path_offset(adt, adt_path);
+ if (adt_node < 0)
+ return;
+
+ const char *fdt_path = fdt_get_alias(dt, dt_alias);
+ if (fdt_path == NULL) {
+ printf("FDT: Unable to find alias %s\n", dt_alias);
+ return;
+ }
+
+ int fdt_node = fdt_path_offset(dt, fdt_path);
+ if (fdt_node < 0) {
+ printf("FDT: Unable to find path %s for alias %s\n", fdt_path, dt_alias);
+ return;
+ }
+
+ for (size_t i = 0; i < sizeof(atc_tunables) / sizeof(*atc_tunables); ++i) {
+ ret = dt_append_atc_tunable(adt_node, fdt_node, &atc_tunables[i]);
+ if (ret)
+ goto cleanup;
+ }
+
+ return;
+
+cleanup:
+ /*
+ * USB3 and Thunderbolt won't work if something went wrong. Clean up to make
+ * sure we don't leave half-filled properties around so that we can at least
+ * try to boot with USB2 support only.
+ */
+ for (size_t i = 0; i < sizeof(atc_tunables) / sizeof(*atc_tunables); ++i)
+ fdt_delprop(dt, fdt_node, atc_tunables[i].fdt_name);
+
+ printf("FDT: Unable to setup ATC tunables for %s - USB3/Thunderbolt will not work\n", adt_path);
+}
+
+static int dt_set_atc_tunables(void)
+{
+ char adt_path[32];
+ char fdt_alias[32];
+
+ for (int i = 0; i < MAX_ATC_DEVS; ++i) {
+ memset(adt_path, 0, sizeof(adt_path));
+ snprintf(adt_path, sizeof(adt_path), "/arm-io/atc-phy%d", i);
+
+ memset(fdt_alias, 0, sizeof(adt_path));
+ snprintf(fdt_alias, sizeof(fdt_alias), "atcphy%d", i);
+
+ dt_copy_atc_tunables(adt_path, fdt_alias);
+ }
+
+ return 0;
+}
+
+static const struct adt_tunable_info acio_tunables[] = {
+ /* NHI tunables */
+ {"hi_up_tx_desc_fabric_tunables", "apple,tunable-nhi", 0xf0000, 0x4000, true},
+ {"hi_up_tx_data_fabric_tunables", "apple,tunable-nhi", 0xec000, 0x4000, true},
+ {"hi_up_rx_desc_fabric_tunables", "apple,tunable-nhi", 0xe8000, 0x4000, true},
+ {"hi_up_wr_fabric_tunables", "apple,tunable-nhi", 0xf4000, 0x4000, true},
+ {"hi_up_merge_fabric_tunables", "apple,tunable-nhi", 0xf8000, 0x4000, true},
+ {"hi_dn_merge_fabric_tunables", "apple,tunable-nhi", 0xfc000, 0x4000, true},
+ {"fw_int_ctl_management_tunables", "apple,tunable-nhi", 0x4000, 0x4000, true},
+ /* M3 tunables */
+ {"top_tunables", "apple,tunable-m3", 0x0, 0x4000, true},
+ {"hbw_fabric_tunables", "apple,tunable-m3", 0x4000, 0x4000, true},
+ {"lbw_fabric_tunables", "apple,tunable-m3", 0x8000, 0x4000, true},
+ /* PCIe adapter tunables */
+ {"pcie_adapter_regs_tunables", "apple,tunable-pcie-adapter", 0x0, 0x4000, true},
+};
+
+struct acio_tunable {
+ u32 offset;
+ u32 size;
+ u64 mask;
+ u64 value;
+} PACKED;
+static_assert(sizeof(struct acio_tunable) == 24, "Invalid acio_tunable size");
+
+/*
+ * This is *almost* identical to dt_append_atc_tunable except for the different
+ * tunable struct and that tunable->size is in bytes instead of bits.
+ * If only C had generics that aren't macros :-(
+ */
+static int dt_append_acio_tunable(int adt_node, int fdt_node,
+ const struct adt_tunable_info *tunable_info)
+{
+ u32 tunables_len;
+ const struct acio_tunable *tunable_adt =
+ adt_getprop(adt, adt_node, tunable_info->adt_name, &tunables_len);
+
+ if (!tunable_adt) {
+ printf("ADT: tunable %s not found\n", tunable_info->adt_name);
+
+ if (tunable_info->required)
+ return -1;
+ else
+ return 0;
+ }
+
+ if (tunables_len % sizeof(*tunable_adt)) {
+ printf("ADT: tunable %s with invalid length %d\n", tunable_info->adt_name, tunables_len);
+ return -1;
+ }
+
+ u32 n_tunables = tunables_len / sizeof(*tunable_adt);
+ for (size_t j = 0; j < n_tunables; j++) {
+ const struct acio_tunable *tunable = &tunable_adt[j];
+
+ if (tunable->size != 4) {
+ printf("kboot: ACIO tunable has invalid size %d\n", tunable->size);
+ return -1;
+ }
+
+ if (tunable->offset % tunable->size) {
+ printf("kboot: ACIO tunable has unaligned offset %x\n", tunable->offset);
+ return -1;
+ }
+
+ if (tunable->offset + tunable->size > tunable_info->reg_size) {
+ printf("kboot: ACIO tunable has invalid offset %x\n", tunable->offset);
+ return -1;
+ }
+
+ if (fdt_appendprop_u32(dt, fdt_node, tunable_info->fdt_name,
+ tunable->offset + tunable_info->reg_offset) < 0)
+ return -1;
+ if (fdt_appendprop_u32(dt, fdt_node, tunable_info->fdt_name, tunable->mask) < 0)
+ return -1;
+ if (fdt_appendprop_u32(dt, fdt_node, tunable_info->fdt_name, tunable->value) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int dt_copy_acio_tunables(const char *adt_path, const char *dt_alias)
+{
+ int ret;
+ int adt_node = adt_path_offset(adt, adt_path);
+ if (adt_node < 0)
+ return -1;
+
+ const char *fdt_path = fdt_get_alias(dt, dt_alias);
+ if (fdt_path == NULL)
+ bail("FDT: Unable to find alias %s\n", dt_alias);
+
+ int fdt_node = fdt_path_offset(dt, fdt_path);
+ if (fdt_node < 0)
+ bail("FDT: Unable to find path %s for alias %s\n", fdt_path, dt_alias);
+
+ u32 drom_len;
+ const u8 *drom_blob = adt_getprop(adt, adt_node, "thunderbolt-drom", &drom_len);
+ if (!drom_blob || !drom_len)
+ bail("ADT: Failed to get thunderbolt-drom");
+
+ fdt_setprop(dt, fdt_node, "apple,thunderbolt-drom", drom_blob, drom_len);
+ for (size_t i = 0; i < sizeof(acio_tunables) / sizeof(*acio_tunables); ++i) {
+ ret = dt_append_acio_tunable(adt_node, fdt_node, &acio_tunables[i]);
+ if (ret)
+ bail_cleanup("ADT: unable to convert '%s' tunable", acio_tunables[i].adt_name);
+ }
+
+ return 0;
+
+err:
+ fdt_delprop(dt, fdt_node, "apple,thunderbolt-drom");
+ fdt_delprop(dt, fdt_node, "apple,tunable-nhi");
+ fdt_delprop(dt, fdt_node, "apple,tunable-m3");
+ fdt_delprop(dt, fdt_node, "apple,tunable-pcie-adapter");
+
+ return -1;
+}
+
+static int dt_set_acio_tunables(void)
+{
+ char adt_path[32];
+ char fdt_alias[32];
+
+ for (int i = 0; i < MAX_CIO_DEVS; ++i) {
+ memset(adt_path, 0, sizeof(adt_path));
+ snprintf(adt_path, sizeof(adt_path), "/arm-io/acio%d", i);
+
+ memset(fdt_alias, 0, sizeof(adt_path));
+ snprintf(fdt_alias, sizeof(fdt_alias), "acio%d", i);
+
+ dt_copy_acio_tunables(adt_path, fdt_alias);
+ }
+
+ return 0;
+}
+
+static int dt_get_iommu_node(int node, u32 num)
+{
+ int len;
+ assert(num < 32);
+ const void *prop = fdt_getprop(dt, node, "iommus", &len);
+ if (!prop || len < 0 || (u32)len < 8 * (num + 1)) {
+ printf("FDT: unexpected 'iommus' prop / len %d\n", len);
+ return -FDT_ERR_NOTFOUND;
+ }
+
+ const fdt32_t *iommus = prop;
+ uint32_t phandle = fdt32_ld(&iommus[num * 2]);
+
+ return fdt_node_offset_by_phandle(dt, phandle);
+}
+
+static dart_dev_t *dt_init_dart_by_node(int node, u32 num)
+{
+ int len;
+ assert(num < 32);
+ const void *prop = fdt_getprop(dt, node, "iommus", &len);
+ if (!prop || len < 0 || (u32)len < 8 * (num + 1)) {
+ printf("FDT: unexpected 'iommus' prop / len %d\n", len);
+ return NULL;
+ }
+
+ const fdt32_t *iommus = prop;
+ u32 iommu_phandle = fdt32_ld(&iommus[num * 2]);
+ u32 iommu_stream = fdt32_ld(&iommus[num * 2 + 1]);
+
+ printf("FDT: iommu phande:%u stream:%u\n", iommu_phandle, iommu_stream);
+
+ return dart_init_fdt(dt, iommu_phandle, iommu_stream, true);
+}
+
+static u64 dart_get_mapping(dart_dev_t *dart, const char *path, u64 paddr, size_t size)
+{
+ u64 iova = dart_search(dart, (void *)paddr);
+ if (DART_IS_ERR(iova)) {
+ printf("ADT: %s paddr: 0x%lx is not mapped\n", path, paddr);
+ return iova;
+ }
+
+ u64 pend = (u64)dart_translate(dart, iova + size - 1);
+ if (pend != (paddr + size - 1)) {
+ printf("ADT: %s is not continuously mapped: 0x%lx\n", path, pend);
+ return DART_PTR_ERR;
+ }
+
+ return iova;
+}
+
+static int dt_device_set_reserved_mem(int node, dart_dev_t *dart, const char *name,
+ uint32_t phandle, u64 paddr, u64 size)
+{
+ int ret;
+
+ u64 iova = dart_get_mapping(dart, name, paddr, size);
+ if (DART_IS_ERR(iova))
+ bail("ADT: no mapping found for '%s' 0x%012lx iova:0x%08lx)\n", name, paddr, iova);
+
+ ret = fdt_appendprop_u32(dt, node, "iommu-addresses", phandle);
+ if (ret != 0)
+ bail("DT: could not append phandle '%s.compatible' property: %d\n", name, ret);
+
+ ret = fdt_appendprop_u64(dt, node, "iommu-addresses", iova);
+ if (ret != 0)
+ bail("DT: could not append iova to '%s.iommu-addresses' property: %d\n", name, ret);
+
+ ret = fdt_appendprop_u64(dt, node, "iommu-addresses", size);
+ if (ret != 0)
+ bail("DT: could not append size to '%s.iommu-addresses' property: %d\n", name, ret);
+
+ return 0;
+}
+
+static int dt_get_or_add_reserved_mem(const char *node_name, const char *compat, u64 paddr,
+ size_t size)
+{
+ int ret;
+ int resv_node = fdt_path_offset(dt, "/reserved-memory");
+ if (resv_node < 0)
+ bail("DT: '/reserved-memory' not found\n");
+
+ int node = fdt_subnode_offset(dt, resv_node, node_name);
+ if (node >= 0)
+ return node;
+
+ node = fdt_add_subnode(dt, resv_node, node_name);
+ if (node < 0)
+ bail("DT: failed to add node '%s' to '/reserved-memory'\n", node_name);
+
+ uint32_t phandle;
+ ret = fdt_generate_phandle(dt, &phandle);
+ if (ret)
+ bail("DT: failed to generate phandle: %d\n", ret);
+
+ ret = fdt_setprop_u32(dt, node, "phandle", phandle);
+ if (ret != 0)
+ bail("DT: couldn't set '%s.phandle' property: %d\n", node_name, ret);
+
+ u64 reg[2] = {cpu_to_fdt64(paddr), cpu_to_fdt64(size)};
+ ret = fdt_setprop(dt, node, "reg", reg, sizeof(reg));
+ if (ret != 0)
+ bail("DT: couldn't set '%s.reg' property: %d\n", node_name, ret);
+
+ ret = fdt_setprop_string(dt, node, "compatible", compat);
+ if (ret != 0)
+ bail("DT: couldn't set '%s.compatible' property: %d\n", node_name, ret);
+
+ ret = fdt_setprop_empty(dt, node, "no-map");
+ if (ret != 0)
+ bail("DT: couldn't set '%s.no-map' property: %d\n", node_name, ret);
+
+ return node;
+}
+
+static int dt_device_add_mem_region(const char *alias, uint32_t phandle, const char *name)
+{
+ int ret;
+ int dev_node = fdt_path_offset(dt, alias);
+ if (dev_node < 0)
+ bail("DT: failed to get node for alias '%s'\n", alias);
+
+ ret = fdt_appendprop_u32(dt, dev_node, "memory-region", phandle);
+ if (ret != 0)
+ bail("DT: failed to append to 'memory-region' property\n");
+
+ dev_node = fdt_path_offset(dt, alias);
+ if (dev_node < 0)
+ bail("DT: failed to update node for alias '%s'\n", alias);
+
+ ret = fdt_appendprop_string(dt, dev_node, "memory-region-names", name);
+ if (ret != 0)
+ bail("DT: failed to append to 'memory-region-names' property\n");
+
+ return 0;
+}
+
+static int dt_set_dcp_firmware(const char *alias)
+{
+ const char *path = fdt_get_alias(dt, alias);
+
+ if (!path)
+ return 0;
+
+ int node = fdt_path_offset(dt, path);
+ if (node < 0)
+ return 0;
+
+ if (firmware_set_fdt(dt, node, "apple,firmware-version", &os_firmware) < 0)
+ bail("FDT: Could not set apple,firmware-version for %s\n", path);
+
+ const struct fw_version_info *compat;
+
+ switch (os_firmware.version) {
+ case V12_3_1:
+ case V12_4:
+ compat = &fw_versions[V12_3];
+ break;
+ default:
+ compat = &os_firmware;
+ break;
+ }
+
+ if (firmware_set_fdt(dt, node, "apple,firmware-compat", compat) < 0)
+ bail("FDT: Could not set apple,firmware-compat for %s\n", path);
+
+ return 0;
+}
+
+struct disp_mapping {
+ char region_adt[24];
+ char mem_fdt[24];
+ bool map_dcp;
+ bool map_disp;
+ bool map_piodma;
+};
+
+struct mem_region {
+ u64 paddr;
+ u64 size;
+};
+
+static int dt_add_reserved_regions(const char *dcp_alias, const char *disp_alias,
+ const char *piodma_alias, const char *compat,
+ struct disp_mapping *maps, struct mem_region *region,
+ u32 num_maps)
+{
+ int ret = 0;
+ dart_dev_t *dart_dcp = NULL, *dart_disp = NULL, *dart_piodma = NULL;
+ uint32_t dcp_phandle = 0, disp_phandle = 0, piodma_phandle = 0;
+
+ /* Check for display device aliases, if one is missing assume it is an old DT
+ * without display nodes and return without error.
+ * Otherwise init each dart and retrieve the node's phandle.
+ */
+ if (dcp_alias) {
+ int dcp_node = fdt_path_offset(dt, dcp_alias);
+ if (dcp_node < 0) {
+ printf("DT: could not resolve '%s' alias\n", dcp_alias);
+ goto err; // cleanup
+ }
+ dart_dcp = dt_init_dart_by_node(dcp_node, 0);
+ if (!dart_dcp)
+ bail_cleanup("DT: failed to init DART for '%s'\n", dcp_alias);
+ dcp_phandle = fdt_get_phandle(dt, dcp_node);
+ }
+
+ if (disp_alias) {
+ int disp_node = fdt_path_offset(dt, disp_alias);
+ if (disp_node < 0) {
+ printf("DT: could not resolve '%s' alias\n", disp_alias);
+ goto err; // cleanup
+ }
+ dart_disp = dt_init_dart_by_node(disp_node, 0);
+ if (!dart_disp)
+ bail_cleanup("DT: failed to init DART for '%s'\n", disp_alias);
+ disp_phandle = fdt_get_phandle(dt, disp_node);
+ }
+
+ if (piodma_alias) {
+ int piodma_node = fdt_path_offset(dt, piodma_alias);
+ if (piodma_node < 0) {
+ printf("DT: could not resolve '%s' alias\n", piodma_alias);
+ goto err; // cleanup
+ }
+
+ dart_piodma = dt_init_dart_by_node(piodma_node, 0);
+ if (!dart_piodma)
+ bail_cleanup("DT: failed to init DART for '%s'\n", piodma_alias);
+ piodma_phandle = fdt_get_phandle(dt, piodma_node);
+ }
+
+ for (unsigned i = 0; i < num_maps; i++) {
+ const char *name = maps[i].mem_fdt;
+ char node_name[64];
+
+ snprintf(node_name, sizeof(node_name), "%s@%lx", name, region[i].paddr);
+ int mem_node =
+ dt_get_or_add_reserved_mem(node_name, compat, region[i].paddr, region[i].size);
+ if (mem_node < 0)
+ goto err;
+
+ uint32_t mem_phandle = fdt_get_phandle(dt, mem_node);
+
+ if (maps[i].map_dcp && dart_dcp) {
+ ret = dt_device_set_reserved_mem(mem_node, dart_dcp, node_name, dcp_phandle,
+ region[i].paddr, region[i].size);
+ if (ret != 0)
+ goto err;
+ }
+ if (maps[i].map_disp && dart_disp) {
+ ret = dt_device_set_reserved_mem(mem_node, dart_disp, node_name, disp_phandle,
+ region[i].paddr, region[i].size);
+ if (ret != 0)
+ goto err;
+ }
+ if (maps[i].map_piodma && dart_piodma) {
+ ret = dt_device_set_reserved_mem(mem_node, dart_piodma, node_name, piodma_phandle,
+ region[i].paddr, region[i].size);
+ if (ret != 0)
+ goto err;
+ }
+
+ /* modify device nodes after filling /reserved-memory to avoid
+ * reloading mem_node's offset */
+ if (maps[i].map_dcp && dcp_alias) {
+ ret = dt_device_add_mem_region(dcp_alias, mem_phandle, maps[i].mem_fdt);
+ if (ret < 0)
+ goto err;
+ }
+ if (maps[i].map_disp && disp_alias) {
+ ret = dt_device_add_mem_region(disp_alias, mem_phandle, maps[i].mem_fdt);
+ if (ret < 0)
+ goto err;
+ }
+ if (maps[i].map_piodma && piodma_alias) {
+ ret = dt_device_add_mem_region(piodma_alias, mem_phandle, maps[i].mem_fdt);
+ if (ret < 0)
+ goto err;
+ }
+ }
+
+ /* enable dart-disp0, it is disabled in device tree to avoid resetting
+ * it and breaking display scanout when booting with old m1n1 which
+ * does not lock dart-disp0.
+ */
+ if (disp_alias) {
+ int disp_node = fdt_path_offset(dt, disp_alias);
+
+ int dart_disp0 = dt_get_iommu_node(disp_node, 0);
+ if (dart_disp0 < 0)
+ bail_cleanup("DT: failed to find 'dart-disp0'\n");
+
+ if (fdt_setprop_string(dt, dart_disp0, "status", "okay") < 0)
+ bail_cleanup("DT: failed to enable 'dart-disp0'\n");
+ }
+err:
+ if (dart_dcp)
+ dart_shutdown(dart_dcp);
+ if (dart_disp)
+ dart_shutdown(dart_disp);
+ if (dart_piodma)
+ dart_shutdown(dart_piodma);
+
+ return ret;
+}
+
+static int dt_carveout_reserved_regions(const char *dcp_alias, const char *disp_alias,
+ const char *piodma_alias, struct disp_mapping *maps,
+ u32 num_maps)
+{
+ int ret = 0;
+
+ struct mem_region region[MAX_DISP_MAPPINGS];
+
+ assert(num_maps <= MAX_DISP_MAPPINGS);
+
+ // return early if dcp_alias does not exists
+ if (!fdt_get_alias(dt, dcp_alias))
+ return 0;
+
+ ret = dt_set_dcp_firmware(dcp_alias);
+ if (ret)
+ return ret;
+
+ int node = adt_path_offset(adt, "/chosen/carveout-memory-map");
+ if (node < 0)
+ bail("ADT: '/chosen/carveout-memory-map' not found\n");
+
+ /* read physical addresses of reserved memory regions */
+ /* do this up front to avoid errors after modifying the DT */
+ for (unsigned i = 0; i < num_maps; i++) {
+
+ int ret;
+ u64 phys_map[2];
+ struct disp_mapping *map = &maps[i];
+ const char *name = map->region_adt;
+
+ ret = ADT_GETPROP_ARRAY(adt, node, name, phys_map);
+ if (ret != sizeof(phys_map))
+ bail("ADT: could not get carveout memory '%s'\n", name);
+ if (!phys_map[0] || !phys_map[1])
+ bail("ADT: carveout memory '%s'\n", name);
+
+ region[i].paddr = phys_map[0];
+ region[i].size = phys_map[1];
+ }
+
+ return dt_add_reserved_regions(dcp_alias, disp_alias, piodma_alias, "apple,asc-mem", maps,
+ region, num_maps);
+}
+
+static struct disp_mapping disp_reserved_regions_vram[] = {
+ // boot framebuffer, mapped to dart-disp0 sid 0 and dart-dcp sid 0/5
+ {"vram", "framebuffer", true, true, false},
+};
+
+static int dt_vram_reserved_region(const char *dcp_alias, const char *disp_alias)
+{
+ int ret = 0;
+ int adt_path[4];
+ struct mem_region region;
+
+ // return early if dcp_alias does not exists
+ if (!fdt_get_alias(dt, dcp_alias))
+ return 0;
+
+ int node = adt_path_offset_trace(adt, "/vram", adt_path);
+
+ if (node < 0)
+ bail("ADT: '/vram' not found\n");
+
+ int pp = 0;
+ while (adt_path[pp])
+ pp++;
+ adt_path[pp + 1] = 0;
+
+ ret = adt_get_reg(adt, adt_path, "reg", 0, &region.paddr, &region.size);
+ if (ret < 0)
+ bail("ADT: failed to read /vram/reg\n");
+
+ return dt_add_reserved_regions(dcp_alias, disp_alias, NULL, "framebuffer",
+ disp_reserved_regions_vram, &region, 1);
+}
+
+static struct disp_mapping disp_reserved_regions_t8103[] = {
+ {"region-id-50", "dcp_data", true, false, false},
+ {"region-id-57", "region57", true, false, false},
+ // The 2 following regions are mapped in dart-dcp sid 0 and dart-disp0 sid 0 and 4
+ {"region-id-94", "region94", true, true, false},
+ {"region-id-95", "region95", true, false, true},
+};
+
+static struct disp_mapping dcpext_reserved_regions_t8103[] = {
+ {"region-id-73", "dcpext_data", true, false, false},
+ {"region-id-74", "region74", true, false, false},
+};
+
+static struct disp_mapping disp_reserved_regions_t8112[] = {
+ {"region-id-49", "dcp_txt", true, false, false},
+ {"region-id-50", "dcp_data", true, false, false},
+ {"region-id-57", "region57", true, false, false},
+ // The 2 following regions are mapped in dart-dcp sid 5 and dart-disp0 sid 0 and 4
+ {"region-id-94", "region94", true, true, false},
+ {"region-id-95", "region95", true, false, true},
+};
+
+static struct disp_mapping dcpext_reserved_regions_t8112[] = {
+ {"region-id-49", "dcp_txt", true, false, false},
+ {"region-id-73", "dcpext_data", true, false, false},
+ {"region-id-74", "region74", true, false, false},
+};
+
+static struct disp_mapping disp_reserved_regions_t600x[] = {
+ {"region-id-50", "dcp_data", true, false, false},
+ {"region-id-57", "region57", true, false, false},
+ // The 2 following regions are mapped in dart-dcp sid 0 and dart-disp0 sid 0 and 4
+ {"region-id-94", "region94", true, true, false},
+ {"region-id-95", "region95", true, false, true},
+ // used on M1 Pro/Max/Ultra, mapped to dcp and disp0
+ {"region-id-157", "region157", true, true, false},
+};
+
+#define MAX_DCPEXT 8
+
+static struct disp_mapping dcpext_reserved_regions_t600x[MAX_DCPEXT][2] = {
+ {
+ {"region-id-73", "dcpext0_data", true, false, false},
+ {"region-id-74", "", true, false, false},
+ },
+ {
+ {"region-id-88", "dcpext1_data", true, false, false},
+ {"region-id-89", "region89", true, false, false},
+ },
+ {
+ {"region-id-111", "dcpext2_data", true, false, false},
+ {"region-id-112", "region112", true, false, false},
+ },
+ {
+ {"region-id-119", "dcpext3_data", true, false, false},
+ {"region-id-120", "region120", true, false, false},
+ },
+ {
+ {"region-id-127", "dcpext4_data", true, false, false},
+ {"region-id-128", "region128", true, false, false},
+ },
+ {
+ {"region-id-135", "dcpext5_data", true, false, false},
+ {"region-id-136", "region136", true, false, false},
+ },
+ {
+ {"region-id-143", "dcpext6_data", true, false, false},
+ {"region-id-144", "region144", true, false, false},
+ },
+ {
+ {"region-id-151", "dcpext7_data", true, false, false},
+ {"region-id-152", "region152", true, false, false},
+ },
+};
+
+#define ARRAY_SIZE(s) (sizeof(s) / sizeof((s)[0]))
+
+static int dt_set_display(void)
+{
+ /* lock dart-disp0 to prevent old software from resetting it */
+ dart_lock_adt("/arm-io/dart-disp0", 0);
+
+ /* Add "/reserved-memory" nodes with iommu mapping and link them to their
+ * devices. The memory is already excluded from useable RAM so these nodes
+ * are only required to inform the OS about the existing mappings.
+ * Required for disp0, dcp and all dcpext.
+ * Checks for dcp* / disp*_piodma / disp* aliases and fails silently if
+ * they are missing. */
+
+ int ret = 0;
+
+ if (!fdt_node_check_compatible(dt, 0, "apple,t8103")) {
+ ret = dt_carveout_reserved_regions("dcp", "disp0", "disp0_piodma",
+ disp_reserved_regions_t8103,
+ ARRAY_SIZE(disp_reserved_regions_t8103));
+ if (ret)
+ return ret;
+
+ ret = dt_carveout_reserved_regions("dcpext", NULL, NULL, dcpext_reserved_regions_t8103,
+ ARRAY_SIZE(dcpext_reserved_regions_t8103));
+ } else if (!fdt_node_check_compatible(dt, 0, "apple,t8112")) {
+ ret = dt_carveout_reserved_regions("dcp", "disp0", "disp0_piodma",
+ disp_reserved_regions_t8112,
+ ARRAY_SIZE(disp_reserved_regions_t8112));
+ if (ret)
+ return ret;
+
+ ret = dt_carveout_reserved_regions("dcpext", NULL, NULL, dcpext_reserved_regions_t8112,
+ ARRAY_SIZE(dcpext_reserved_regions_t8112));
+ } else if (!fdt_node_check_compatible(dt, 0, "apple,t6000") ||
+ !fdt_node_check_compatible(dt, 0, "apple,t6001") ||
+ !fdt_node_check_compatible(dt, 0, "apple,t6002")) {
+ ret = dt_carveout_reserved_regions("dcp", "disp0", "disp0_piodma",
+ disp_reserved_regions_t600x,
+ ARRAY_SIZE(disp_reserved_regions_t600x));
+ if (ret)
+ return ret;
+
+ for (int n = 0; n < MAX_DCPEXT && ret == 0; n++) {
+ char dcpext_alias[16];
+
+ snprintf(dcpext_alias, sizeof(dcpext_alias), "dcpext%d", n);
+ ret = dt_carveout_reserved_regions(dcpext_alias, NULL, NULL,
+ dcpext_reserved_regions_t600x[n],
+ ARRAY_SIZE(dcpext_reserved_regions_t600x[n]));
+ }
+ } else {
+ printf("DT: unknown compatible, skip display reserved-memory setup\n");
+ return 0;
+ }
+ if (ret)
+ return ret;
+
+ return dt_vram_reserved_region("dcp", "disp0");
+}
+
+static int dt_disable_missing_devs(const char *adt_prefix, const char *dt_prefix, int max_devs)
+{
+ int ret = -1;
+ int adt_prefix_len = strlen(adt_prefix);
+ int dt_prefix_len = strlen(dt_prefix);
+
+ int acnt = 0, phcnt = 0;
+ u64 *addrs = malloc(max_devs * sizeof(u64));
+ u32 *phandles = malloc(max_devs * sizeof(u32) * 4); // Allow up to 4 extra nodes per device
+ if (!addrs || !phandles)
+ bail_cleanup("FDT: out of memory\n");
+
+ int path[8];
+ int node = adt_path_offset_trace(adt, "/arm-io", path);
+ if (node < 0)
+ bail_cleanup("ADT: /arm-io not found\n");
+
+ int pp = 0;
+ while (path[pp])
+ pp++;
+ path[pp + 1] = 0;
+
+ u32 die_count;
+ if (ADT_GETPROP(adt, node, "die-count", &die_count) < 0) {
+ die_count = 1;
+ }
+ if (die_count > 8) {
+ printf("ADT: limiting die-count from %u to 8\n", die_count);
+ die_count = 8;
+ }
+
+ /* Find ADT registers */
+ ADT_FOREACH_CHILD(adt, node)
+ {
+ const char *name = adt_get_name(adt, node);
+ if (strncmp(name, adt_prefix, adt_prefix_len))
+ continue;
+
+ path[pp] = node;
+ if (adt_get_reg(adt, path, "reg", 0, &addrs[acnt++], NULL) < 0)
+ bail_cleanup("Error getting /arm-io/%s regs\n", name);
+ }
+
+ for (u32 die = 0; die < die_count; ++die) {
+ char path[32] = "/soc";
+
+ if (die_count > 1) {
+ // pre-linux submission multi-die path
+ // can probably removed the next time someone read this comment.
+ snprintf(path, sizeof(path), "/soc/die%u", die);
+ int die_node = fdt_path_offset(dt, path);
+ if (die_node < 0) {
+ /* this should use aliases for the soc nodes */
+ u64 die_unit_addr = die * PMGR_DIE_OFFSET + 0x200000000;
+ snprintf(path, sizeof(path), "/soc@%lx", die_unit_addr);
+ }
+ }
+
+ int soc = fdt_path_offset(dt, path);
+ if (soc < 0)
+ bail("FDT: %s node not found in devtree\n", path);
+
+ // parse ranges for address translation
+ struct dt_ranges_tbl ranges[DT_MAX_RANGES] = {0};
+ dt_parse_ranges(dt, soc, ranges);
+
+ /* Disable primary devices */
+ fdt_for_each_subnode(node, dt, soc)
+ {
+ const char *name = fdt_get_name(dt, node, NULL);
+ if (strncmp(name, dt_prefix, dt_prefix_len))
+ continue;
+
+ const fdt64_t *reg = fdt_getprop(dt, node, "reg", NULL);
+ if (!reg)
+ bail_cleanup("FDT: failed to get reg property of %s\n", name);
+
+ u64 addr = dt_translate(ranges, reg);
+
+ int i;
+ for (i = 0; i < acnt; i++)
+ if (addrs[i] == addr)
+ break;
+ if (i < acnt)
+ continue;
+
+ int iommus_size;
+ const fdt32_t *iommus = fdt_getprop(dt, node, "iommus", &iommus_size);
+ if (iommus) {
+ if (iommus_size & 7 || iommus_size > 4 * 8) {
+ printf("FDT: bad iommus property for %s/%s\n", path, name);
+ } else {
+ for (int i = 0; i < iommus_size / 8; i++)
+ phandles[phcnt++] = fdt32_ld(&iommus[i * 2]);
+ }
+ }
+
+ int phys_size;
+ const fdt32_t *phys = fdt_getprop(dt, node, "phys", &phys_size);
+ if (phys) {
+ if (phys_size & 7 || phys_size > 4 * 8) {
+ printf("FDT: bad phys property for %s/%s\n", path, name);
+ } else {
+ for (int i = 0; i < phys_size / 8; i++)
+ phandles[phcnt++] = fdt32_ld(&phys[i * 2]);
+ }
+ }
+
+ const char *status = fdt_getprop(dt, node, "status", NULL);
+ if (!status || strcmp(status, "disabled")) {
+ printf("FDT: Disabling missing device %s/%s\n", path, name);
+
+ if (fdt_setprop_string(dt, node, "status", "disabled") < 0)
+ bail_cleanup("FDT: failed to set status property of %s/%s\n", path, name);
+ }
+ }
+
+ /* Disable secondary devices */
+ fdt_for_each_subnode(node, dt, soc)
+ {
+ const char *name = fdt_get_name(dt, node, NULL);
+ u32 phandle = fdt_get_phandle(dt, node);
+
+ for (int i = 0; i < phcnt; i++) {
+ if (phandles[i] != phandle)
+ continue;
+
+ const char *status = fdt_getprop(dt, node, "status", NULL);
+ if (status && !strcmp(status, "disabled"))
+ continue;
+
+ printf("FDT: Disabling secondary device %s/%s\n", path, name);
+
+ if (fdt_setprop_string(dt, node, "status", "disabled") < 0)
+ bail_cleanup("FDT: failed to set status property of %s/%s\n", path, name);
+ break;
+ }
+ }
+ }
+
+ ret = 0;
+err:
+ free(phandles);
+ free(addrs);
+
+ return ret;
+}
+
+static int dt_transfer_virtios(void)
+{
+ int path[3];
+ path[0] = adt_path_offset(adt, "/arm-io/");
+ if (path[0] < 0)
+ bail("ADT: /arm-io not found\n");
+
+ int aic = fdt_node_offset_by_compatible(dt, -1, "apple,aic");
+ if (aic == -FDT_ERR_NOTFOUND)
+ aic = fdt_node_offset_by_compatible(dt, -1, "apple,aic2");
+ if (aic < 0)
+ bail("FDT: failed to find AIC node\n");
+
+ u32 aic_phandle = fdt_get_phandle(dt, aic);
+ const fdt32_t *ic_prop = fdt_getprop(dt, aic, "#interrupt-cells", NULL);
+ u32 intcells = 0;
+ if (ic_prop)
+ intcells = fdt32_ld(ic_prop);
+ if (intcells < 3 || intcells > 4)
+ bail("FDT: bad '#interrupt-cells' on AIC node (%d)\n", intcells);
+
+ for (u32 i = 0; i < 16; i++) {
+ char name[16], fullname[32];
+ snprintf(name, sizeof(name) - 1, "virtio%d", i);
+
+ path[1] = adt_subnode_offset(adt, path[0], name);
+ if (path[1] < 0)
+ break;
+ path[2] = 0;
+
+ u64 addr, size;
+ if (adt_get_reg(adt, path, "reg", 0, &addr, &size) < 0)
+ bail("ADT: error getting /arm-io/%s regs\n", name);
+
+ u32 irq;
+ ADT_GETPROP(adt, path[1], "interrupts", &irq);
+
+ snprintf(fullname, sizeof(fullname) - 1, "virtio@%lx", addr);
+ printf("FDT: Adding %s found in ADT\n", name);
+
+ int fnode = fdt_add_subnode(dt, 0, fullname);
+ if (fnode < 0)
+ bail("FDT: failed to create %s\n", fullname);
+
+ if (fdt_setprop_string(dt, fnode, "compatible", "virtio,mmio"))
+ bail("FDT: couldn't set %s.compatible\n", fullname);
+
+ fdt64_t reg[2];
+ fdt64_st(reg + 0, addr);
+ fdt64_st(reg + 1, size);
+ if (fdt_setprop(dt, fnode, "reg", reg, sizeof(reg)))
+ bail("FDT: couldn't set %s.reg\n", fullname);
+
+ if (fdt_setprop_u32(dt, fnode, "interrupt-parent", aic_phandle))
+ bail("FDT: couldn't set %s.interrupt-parent\n", fullname);
+
+ fdt32_t intprop[4];
+ fdt32_st(intprop + 0, 0); // AIC_IRQ
+ fdt32_st(intprop + 1, 0);
+ fdt32_st(intprop + intcells - 2, irq);
+ fdt32_st(intprop + intcells - 1, 4); // IRQ_TYPE_LEVEL_HIGH
+ if (fdt_setprop(dt, fnode, "interrupts", intprop, 4 * intcells))
+ bail("FDT: couldn't set %s.interrupts\n", fullname);
+ }
+
+ return 0;
+}
+
+void kboot_set_initrd(void *start, size_t size)
+{
+ initrd_start = start;
+ initrd_size = size;
+}
+
+int kboot_set_chosen(const char *name, const char *value)
+{
+ int i = 0;
+
+ if (!name)
+ return -1;
+
+ for (i = 0; i < MAX_CHOSEN_PARAMS; i++) {
+ if (!chosen_params[i][0]) {
+ chosen_params[i][0] = malloc(strlen(name) + 1);
+ strcpy(chosen_params[i][0], name);
+ break;
+ }
+
+ if (!strcmp(name, chosen_params[i][0])) {
+ free(chosen_params[i][1]);
+ chosen_params[i][1] = NULL;
+ break;
+ }
+ }
+
+ if (i >= MAX_CHOSEN_PARAMS)
+ return -1;
+
+ if (value) {
+ chosen_params[i][1] = malloc(strlen(value) + 1);
+ strcpy(chosen_params[i][1], value);
+ }
+
+ return i;
+}
+
+int kboot_prepare_dt(void *fdt)
+{
+ if (dt) {
+ free(dt);
+ dt = NULL;
+ }
+
+ dt_bufsize = fdt_totalsize(fdt);
+ assert(dt_bufsize);
+
+ dt_bufsize += 64 * 1024; // Add 64K of buffer for modifications
+ dt = memalign(DT_ALIGN, dt_bufsize);
+
+ if (fdt_open_into(fdt, dt, dt_bufsize) < 0)
+ bail("FDT: fdt_open_into() failed\n");
+
+ if (fdt_add_mem_rsv(dt, (u64)dt, dt_bufsize))
+ bail("FDT: couldn't add reservation for the devtree\n");
+
+ if (fdt_add_mem_rsv(dt, (u64)_base, ((u64)_end) - ((u64)_base)))
+ bail("FDT: couldn't add reservation for m1n1\n");
+
+ if (dt_set_chosen())
+ return -1;
+ if (dt_set_serial_number())
+ return -1;
+ if (dt_set_memory())
+ return -1;
+ if (dt_set_cpus())
+ return -1;
+ if (dt_set_mac_addresses())
+ return -1;
+ if (dt_set_wifi())
+ return -1;
+ if (dt_set_bluetooth())
+ return -1;
+ if (dt_set_uboot())
+ return -1;
+ if (dt_set_atc_tunables())
+ return -1;
+ if (dt_set_acio_tunables())
+ return -1;
+ if (dt_set_display())
+ return -1;
+ if (dt_set_gpu(dt))
+ return -1;
+ if (dt_set_multitouch())
+ return -1;
+ if (dt_disable_missing_devs("usb-drd", "usb@", 8))
+ return -1;
+ if (dt_disable_missing_devs("i2c", "i2c@", 8))
+ return -1;
+#ifndef RELEASE
+ if (dt_transfer_virtios())
+ return 1;
+#endif
+
+ if (fdt_pack(dt))
+ bail("FDT: fdt_pack() failed\n");
+
+ printf("FDT prepared at %p\n", dt);
+
+ return 0;
+}
+
+int kboot_boot(void *kernel)
+{
+ usb_init();
+ pcie_init();
+ dapf_init_all();
+
+ printf("Setting SMP mode to WFE...\n");
+ smp_set_wfe_mode(true);
+ printf("Preparing to boot kernel at %p with fdt at %p\n", kernel, dt);
+
+ next_stage.entry = kernel;
+ next_stage.args[0] = (u64)dt;
+ next_stage.args[1] = 0;
+ next_stage.args[2] = 0;
+ next_stage.args[3] = 0;
+ next_stage.args[4] = 0;
+ next_stage.restore_logo = false;
+
+ return 0;
+}
diff --git a/tools/src/kboot.h b/tools/src/kboot.h
new file mode 100644
index 0000000..44a8740
--- /dev/null
+++ b/tools/src/kboot.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef KBOOT_H
+#define KBOOT_H
+
+#include "types.h"
+
+struct kernel_header {
+ u32 code[2]; /* Executable code */
+ u64 text_offset; /* Image load offset, little endian */
+ u64 image_size; /* Effective Image size, little endian */
+ u64 flags; /* kernel flags, little endian */
+ u64 res2; /* reserved */
+ u64 res3; /* reserved */
+ u64 res4; /* reserved */
+ u32 magic; /* Magic number, little endian, "ARM\x64" */
+ u32 res5; /* reserved (used for PE COFF offset) */
+};
+
+void kboot_set_initrd(void *start, size_t size);
+int kboot_set_chosen(const char *name, const char *value);
+int kboot_prepare_dt(void *fdt);
+int kboot_boot(void *kernel);
+
+#endif
diff --git a/tools/src/kboot_gpu.c b/tools/src/kboot_gpu.c
new file mode 100644
index 0000000..54e6d03
--- /dev/null
+++ b/tools/src/kboot_gpu.c
@@ -0,0 +1,452 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "kboot.h"
+#include "adt.h"
+#include "assert.h"
+#include "firmware.h"
+#include "math.h"
+#include "pmgr.h"
+#include "soc.h"
+#include "utils.h"
+
+#include "libfdt/libfdt.h"
+
+#define bail(...) \
+ do { \
+ printf(__VA_ARGS__); \
+ return -1; \
+ } while (0)
+
+#define MAX_PSTATES 16
+#define MAX_CLUSTERS 8
+
+struct perf_state {
+ u32 freq;
+ u32 volt;
+};
+
+static int get_core_counts(u32 *count, u32 nclusters, u32 ncores)
+{
+ u64 base;
+ pmgr_adt_power_enable("/arm-io/sgx");
+
+ int adt_sgx_path[8];
+ if (adt_path_offset_trace(adt, "/arm-io/sgx", adt_sgx_path) < 0)
+ bail("ADT: GPU: Failed to get sgx\n");
+
+ if (adt_get_reg(adt, adt_sgx_path, "reg", 0, &base, NULL) < 0)
+ bail("ADT: GPU: Failed to get sgx reg 0\n");
+
+ u32 cores_lo = read32(base + 0xd01500);
+ u32 cores_hi = read32(base + 0xd01514);
+
+ u64 cores = (((u64)cores_hi) << 32) | cores_lo;
+
+ for (u32 i = 0; i < nclusters; i++) {
+ count[i] = __builtin_popcount(cores & MASK(ncores));
+ cores >>= ncores;
+ }
+
+ return 0;
+}
+
+static void adjust_leakage(float *val, u32 clusters, u32 *cores, u32 max, float uncore_fraction)
+{
+ for (u32 i = 0; i < clusters; i++) {
+ float uncore = val[i] * uncore_fraction;
+ float core = val[i] - uncore;
+
+ val[i] = uncore + (cores[i] / (float)max) * core;
+ }
+}
+
+static void load_fuses(float *out, u32 count, u64 base, u32 start, u32 width, float scale,
+ float offset, bool flip)
+{
+ for (u32 i = 0; i < count; i++) {
+ base += (start / 32) * 4;
+ start &= 31;
+
+ u32 low = read32(base);
+ u32 high = read32(base + 4);
+ u32 val = (((((u64)high) << 32) | low) >> start) & MASK(width);
+
+ float fval = (float)val * scale + offset;
+
+ if (flip)
+ out[count - i - 1] = fval;
+ else
+ out[i] = fval;
+
+ start += width;
+ }
+}
+
+static u32 t8103_pwr_scale[] = {0, 63, 80, 108, 150, 198, 210};
+
+static int calc_power_t8103(u32 count, u32 table_count, const struct perf_state *core,
+ const struct perf_state *sram, u32 *max_pwr, float *core_leak,
+ float *sram_leak)
+{
+ UNUSED(sram);
+ UNUSED(core_leak);
+ UNUSED(sram_leak);
+ u32 *pwr_scale;
+ u32 pwr_scale_count;
+ u32 core_count;
+ u32 max_cores;
+
+ switch (chip_id) {
+ case T8103:
+ pwr_scale = t8103_pwr_scale;
+ pwr_scale_count = ARRAY_SIZE(t8103_pwr_scale);
+ max_cores = 8;
+ break;
+ default:
+ bail("ADT: GPU: Unsupported chip\n");
+ }
+
+ if (get_core_counts(&core_count, 1, max_cores))
+ return -1;
+
+ if (table_count != 1)
+ bail("ADT: GPU: expected 1 perf state table but got %d\n", table_count);
+
+ if (count != pwr_scale_count)
+ bail("ADT: GPU: expected %d perf states but got %d\n", pwr_scale_count, count);
+
+ for (u32 i = 0; i < pwr_scale_count; i++)
+ max_pwr[i] = (u32)core[i].volt * (u32)pwr_scale[i] * 100;
+
+ core_leak[0] = 1000.0;
+ sram_leak[0] = 45.0;
+
+ adjust_leakage(core_leak, 1, &core_count, max_cores, 0.12);
+ adjust_leakage(sram_leak, 1, &core_count, max_cores, 0.2);
+
+ return 0;
+}
+
+static int calc_power_t600x(u32 count, u32 table_count, const struct perf_state *core,
+ const struct perf_state *sram, u32 *max_pwr, float *core_leak,
+ float *sram_leak)
+{
+ float s_sram, k_sram, s_core, k_core;
+ float dk_core, dk_sram;
+ float imax = 1000;
+
+ u32 nclusters = 0;
+ u32 ncores = 0;
+ u32 core_count[MAX_CLUSTERS];
+
+ bool simple_exps = false;
+ bool adjust_leakages = true;
+
+ switch (chip_id) {
+ case T6002:
+ nclusters += 4;
+ load_fuses(core_leak + 4, 4, 0x22922bc1b8, 25, 13, 2, 2, true);
+ load_fuses(sram_leak + 4, 4, 0x22922bc1cc, 4, 9, 1, 1, true);
+ // fallthrough
+ case T6001:
+ nclusters += 2;
+ case T6000:
+ nclusters += 2;
+ load_fuses(core_leak + 0, min(4, nclusters), 0x2922bc1b8, 25, 13, 2, 2, false);
+ load_fuses(sram_leak + 0, min(4, nclusters), 0x2922bc1cc, 4, 9, 1, 1, false);
+
+ s_sram = 4.3547606;
+ k_sram = 0.024927923;
+ // macOS difference: macOS uses a misbehaved piecewise function here
+ // Since it's obviously wrong, let's just use only the first component
+ s_core = 1.48461742;
+ k_core = 0.39013552;
+ dk_core = 8.558;
+ dk_sram = 0.05;
+
+ ncores = 8;
+ adjust_leakages = true;
+ imax = 26.0;
+ break;
+ case T8112:
+ nclusters = 1;
+ load_fuses(core_leak, 1, 0x23d2c84dc, 30, 13, 2, 2, false);
+ load_fuses(sram_leak, 1, 0x23d2c84b0, 15, 9, 1, 1, false);
+
+ s_sram = 3.61619841;
+ k_sram = 0.0529281;
+ // macOS difference: macOS uses a misbehaved piecewise function here
+ // Since it's obviously wrong, let's just use only the first component
+ s_core = 1.21356187;
+ k_core = 0.43328839;
+ dk_core = 9.83196;
+ dk_sram = 0.07828;
+
+ simple_exps = true;
+ ncores = 10;
+ adjust_leakages = false; // pre-adjusted?
+ imax = 24.0;
+ break;
+ }
+
+ if (get_core_counts(core_count, nclusters, ncores))
+ return -1;
+
+ printf("FDT: GPU: Core counts: ");
+ for (u32 i = 0; i < nclusters; i++) {
+ printf("%d ", core_count[i]);
+ }
+ printf("\n");
+
+ if (adjust_leakages) {
+ adjust_leakage(core_leak, nclusters, core_count, ncores, 0.0825);
+ adjust_leakage(sram_leak, nclusters, core_count, ncores, 0.2247);
+ }
+
+ if (table_count != nclusters)
+ bail("ADT: GPU: expected %d perf state tables but got %d\n", nclusters, table_count);
+
+ max_pwr[0] = 0;
+
+ for (u32 i = 1; i < count; i++) {
+ u32 total_mw = 0;
+
+ for (u32 j = 0; j < nclusters; j++) {
+ // macOS difference: macOS truncates Hz to integer MHz before doing this math.
+ // That's probably wrong, so let's not do that.
+
+ float mw = 0;
+ size_t idx = j * count + i;
+
+ mw += sram[idx].volt / 1000.f * sram_leak[j] * k_sram *
+ expf(sram[idx].volt / 1000.f * s_sram);
+ mw += core[idx].volt / 1000.f * core_leak[j] * k_core *
+ expf(core[idx].volt / 1000.f * s_core);
+
+ float sbase = sram[idx].volt / 750.f;
+ float sram_v_p;
+ if (simple_exps)
+ sram_v_p = sbase * sbase; // v ^ 2
+ else
+ sram_v_p = sbase * sbase * sbase; // v ^ 3
+ mw += dk_sram * (sram[idx].freq / 1000000.f) * sram_v_p;
+
+ float cbase = core[idx].volt / 750.f;
+ float core_v_p;
+ if (simple_exps || core[idx].volt < 750)
+ core_v_p = cbase * cbase; // v ^ 2
+ else
+ core_v_p = cbase * cbase * cbase; // v ^ 3
+ mw += dk_core * (core[idx].freq / 1000000.f) * core_v_p;
+
+ if (mw > imax * core[idx].volt)
+ mw = imax * core[idx].volt;
+
+ total_mw += mw;
+ }
+
+ max_pwr[i] = total_mw * 1000;
+ }
+
+ return 0;
+}
+
+static int dt_set_region(void *dt, int sgx, const char *name, const char *path)
+{
+ u64 base, size;
+ char prop[64];
+
+ snprintf(prop, sizeof(prop), "%s-base", name);
+ if (ADT_GETPROP(adt, sgx, prop, &base) < 0 || !base)
+ bail("ADT: GPU: failed to find %s property\n", prop);
+
+ snprintf(prop, sizeof(prop), "%s-size", name);
+ if (ADT_GETPROP(adt, sgx, prop, &size) < 0 || !base)
+ bail("ADT: GPU: failed to find %s property\n", prop);
+
+ int node = fdt_path_offset(dt, path);
+ if (node < 0)
+ bail("FDT: GPU: failed to find %s node\n", path);
+
+ fdt64_t reg[2];
+
+ fdt64_st(&reg[0], base);
+ fdt64_st(&reg[1], size);
+
+ if (fdt_setprop_inplace(dt, node, "reg", reg, sizeof(reg)))
+ bail("FDT: GPU: failed to set reg prop for %s\n", path);
+
+ return 0;
+}
+
+int fdt_set_float_array(void *dt, int node, const char *name, float *val, int count)
+{
+ fdt32_t data[MAX_CLUSTERS];
+
+ if (count > MAX_CLUSTERS)
+ bail("FDT: GPU: fdt_set_float_array() with too many values\n");
+
+ memcpy(data, val, sizeof(float) * count);
+ for (int i = 0; i < count; i++) {
+ data[i] = cpu_to_fdt32(data[i]);
+ }
+
+ if (fdt_setprop_inplace(dt, node, name, data, sizeof(u32) * count))
+ bail("FDT: GPU: Failed to set %s\n", name);
+
+ return 0;
+}
+
+int dt_set_gpu(void *dt)
+{
+ int (*calc_power)(u32 count, u32 table_count, const struct perf_state *perf,
+ const struct perf_state *sram, u32 *max_pwr, float *core_leak,
+ float *sram_leak);
+
+ printf("FDT: GPU: Initializing GPU info\n");
+
+ switch (chip_id) {
+ case T8103:
+ calc_power = calc_power_t8103;
+ break;
+ case T6000:
+ case T6001:
+ case T6002:
+ case T8112:
+ calc_power = calc_power_t600x;
+ break;
+ default:
+ printf("ADT: GPU: unsupported chip!\n");
+ return 0;
+ }
+
+ int gpu = fdt_path_offset(dt, "gpu");
+ if (gpu < 0) {
+ printf("FDT: GPU: gpu alias not found in device tree\n");
+ return 0;
+ }
+
+ int len;
+ const fdt32_t *opps_ph = fdt_getprop(dt, gpu, "operating-points-v2", &len);
+ if (!opps_ph || len != 4)
+ bail("FDT: GPU: operating-points-v2 not found\n");
+
+ int opps = fdt_node_offset_by_phandle(dt, fdt32_ld(opps_ph));
+ if (opps < 0)
+ bail("FDT: GPU: node for phandle %u not found\n", fdt32_ld(opps_ph));
+
+ int sgx = adt_path_offset(adt, "/arm-io/sgx");
+ if (sgx < 0)
+ bail("ADT: GPU: /arm-io/sgx node not found\n");
+
+ u32 perf_state_count;
+ if (ADT_GETPROP(adt, sgx, "perf-state-count", &perf_state_count) < 0 || !perf_state_count)
+ bail("ADT: GPU: missing perf-state-count\n");
+
+ u32 perf_state_table_count;
+ if (ADT_GETPROP(adt, sgx, "perf-state-table-count", &perf_state_table_count) < 0 ||
+ !perf_state_table_count)
+ bail("ADT: GPU: missing perf-state-table-count\n");
+
+ if (perf_state_count > MAX_PSTATES)
+ bail("ADT: GPU: perf-state-count too large\n");
+
+ if (perf_state_table_count > MAX_CLUSTERS)
+ bail("ADT: GPU: perf-state-table-count too large\n");
+
+ u32 perf_states_len;
+ const struct perf_state *perf_states, *perf_states_sram;
+
+ perf_states = adt_getprop(adt, sgx, "perf-states", &perf_states_len);
+ if (!perf_states ||
+ perf_states_len != sizeof(*perf_states) * perf_state_count * perf_state_table_count)
+ bail("ADT: GPU: invalid perf-states length\n");
+
+ perf_states_sram = adt_getprop(adt, sgx, "perf-states-sram", &perf_states_len);
+ if (perf_states_sram &&
+ perf_states_len != sizeof(*perf_states) * perf_state_count * perf_state_table_count)
+ bail("ADT: GPU: invalid perf-states-sram length\n");
+
+ u32 max_pwr[MAX_PSTATES];
+ float core_leak[MAX_CLUSTERS];
+ float sram_leak[MAX_CLUSTERS];
+
+ if (calc_power(perf_state_count, perf_state_table_count, perf_states, perf_states_sram, max_pwr,
+ core_leak, sram_leak))
+ return -1;
+
+ printf("FDT: GPU: Max power table: ");
+ for (u32 i = 0; i < perf_state_count; i++) {
+ printf("%d ", max_pwr[i]);
+ }
+ printf("\nFDT: GPU: Core leakage table: ");
+ for (u32 i = 0; i < perf_state_table_count; i++) {
+ printf("%d.%03d ", (int)core_leak[i], ((int)(core_leak[i] * 1000) % 1000));
+ }
+ printf("\nFDT: GPU: SRAM leakage table: ");
+ for (u32 i = 0; i < perf_state_table_count; i++) {
+ printf("%d.%03d ", (int)sram_leak[i], ((int)(sram_leak[i] * 1000) % 1000));
+ }
+ printf("\n");
+
+ if (fdt_set_float_array(dt, gpu, "apple,core-leak-coef", core_leak, perf_state_table_count))
+ return -1;
+
+ if (fdt_set_float_array(dt, gpu, "apple,sram-leak-coef", sram_leak, perf_state_table_count))
+ return -1;
+
+ if (firmware_set_fdt(dt, gpu, "apple,firmware-version", &os_firmware))
+ return -1;
+
+ const struct fw_version_info *compat;
+
+ switch (os_firmware.version) {
+ case V12_3_1:
+ compat = &fw_versions[V12_3];
+ break;
+ default:
+ compat = &os_firmware;
+ break;
+ }
+
+ if (firmware_set_fdt(dt, gpu, "apple,firmware-compat", compat))
+ return -1;
+
+ u32 i = 0;
+ int opp;
+ fdt_for_each_subnode(opp, dt, opps)
+ {
+ fdt32_t volts[MAX_CLUSTERS];
+
+ for (u32 j = 0; j < perf_state_table_count; j++) {
+ volts[j] = cpu_to_fdt32(perf_states[i + j * perf_state_count].volt * 1000);
+ }
+
+ if (i >= perf_state_count)
+ bail("FDT: GPU: Expected %d operating points, but found more\n", perf_state_count);
+
+ if (fdt_setprop_inplace(dt, opp, "opp-microvolt", &volts,
+ sizeof(u32) * perf_state_table_count))
+ bail("FDT: GPU: Failed to set opp-microvolt for PS %d\n", i);
+
+ if (fdt_setprop_inplace_u64(dt, opp, "opp-hz", perf_states[i].freq))
+ bail("FDT: GPU: Failed to set opp-hz for PS %d\n", i);
+
+ if (fdt_setprop_inplace_u32(dt, opp, "opp-microwatt", max_pwr[i]))
+ bail("FDT: GPU: Failed to set opp-microwatt for PS %d\n", i);
+
+ i++;
+ }
+
+ if (i != perf_state_count)
+ bail("FDT: GPU: Expected %d operating points, but found %d\n", perf_state_count, i);
+
+ if (dt_set_region(dt, sgx, "gfx-handoff", "/reserved-memory/uat-handoff"))
+ return -1;
+ if (dt_set_region(dt, sgx, "gfx-shared-region", "/reserved-memory/uat-pagetables"))
+ return -1;
+ if (dt_set_region(dt, sgx, "gpu-region", "/reserved-memory/uat-ttbs"))
+ return -1;
+
+ return 0;
+}
diff --git a/tools/src/libfdt/fdt.c b/tools/src/libfdt/fdt.c
new file mode 100644
index 0000000..ebd163a
--- /dev/null
+++ b/tools/src/libfdt/fdt.c
@@ -0,0 +1,327 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include "libfdt_env.h"
+
+#include "fdt.h"
+#include "libfdt.h"
+
+#include "libfdt_internal.h"
+
+/*
+ * Minimal sanity check for a read-only tree. fdt_ro_probe_() checks
+ * that the given buffer contains what appears to be a flattened
+ * device tree with sane information in its header.
+ */
+int32_t fdt_ro_probe_(const void *fdt)
+{
+ uint32_t totalsize = fdt_totalsize(fdt);
+
+ if (can_assume(VALID_DTB))
+ return totalsize;
+
+ if (fdt_magic(fdt) == FDT_MAGIC) {
+ /* Complete tree */
+ if (!can_assume(LATEST)) {
+ if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
+ return -FDT_ERR_BADVERSION;
+ if (fdt_last_comp_version(fdt) >
+ FDT_LAST_SUPPORTED_VERSION)
+ return -FDT_ERR_BADVERSION;
+ }
+ } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
+ /* Unfinished sequential-write blob */
+ if (!can_assume(VALID_INPUT) && fdt_size_dt_struct(fdt) == 0)
+ return -FDT_ERR_BADSTATE;
+ } else {
+ return -FDT_ERR_BADMAGIC;
+ }
+
+ if (totalsize < INT32_MAX)
+ return totalsize;
+ else
+ return -FDT_ERR_TRUNCATED;
+}
+
+static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off)
+{
+ return (off >= hdrsize) && (off <= totalsize);
+}
+
+static int check_block_(uint32_t hdrsize, uint32_t totalsize,
+ uint32_t base, uint32_t size)
+{
+ if (!check_off_(hdrsize, totalsize, base))
+ return 0; /* block start out of bounds */
+ if ((base + size) < base)
+ return 0; /* overflow */
+ if (!check_off_(hdrsize, totalsize, base + size))
+ return 0; /* block end out of bounds */
+ return 1;
+}
+
+size_t fdt_header_size_(uint32_t version)
+{
+ if (version <= 1)
+ return FDT_V1_SIZE;
+ else if (version <= 2)
+ return FDT_V2_SIZE;
+ else if (version <= 3)
+ return FDT_V3_SIZE;
+ else if (version <= 16)
+ return FDT_V16_SIZE;
+ else
+ return FDT_V17_SIZE;
+}
+
+size_t fdt_header_size(const void *fdt)
+{
+ return can_assume(LATEST) ? FDT_V17_SIZE :
+ fdt_header_size_(fdt_version(fdt));
+}
+
+int fdt_check_header(const void *fdt)
+{
+ size_t hdrsize;
+
+ if (fdt_magic(fdt) != FDT_MAGIC)
+ return -FDT_ERR_BADMAGIC;
+ if (!can_assume(LATEST)) {
+ if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
+ || (fdt_last_comp_version(fdt) >
+ FDT_LAST_SUPPORTED_VERSION))
+ return -FDT_ERR_BADVERSION;
+ if (fdt_version(fdt) < fdt_last_comp_version(fdt))
+ return -FDT_ERR_BADVERSION;
+ }
+ hdrsize = fdt_header_size(fdt);
+ if (!can_assume(VALID_DTB)) {
+
+ if ((fdt_totalsize(fdt) < hdrsize)
+ || (fdt_totalsize(fdt) > INT_MAX))
+ return -FDT_ERR_TRUNCATED;
+
+ /* Bounds check memrsv block */
+ if (!check_off_(hdrsize, fdt_totalsize(fdt),
+ fdt_off_mem_rsvmap(fdt)))
+ return -FDT_ERR_TRUNCATED;
+ }
+
+ if (!can_assume(VALID_DTB)) {
+ /* Bounds check structure block */
+ if (!can_assume(LATEST) && fdt_version(fdt) < 17) {
+ if (!check_off_(hdrsize, fdt_totalsize(fdt),
+ fdt_off_dt_struct(fdt)))
+ return -FDT_ERR_TRUNCATED;
+ } else {
+ if (!check_block_(hdrsize, fdt_totalsize(fdt),
+ fdt_off_dt_struct(fdt),
+ fdt_size_dt_struct(fdt)))
+ return -FDT_ERR_TRUNCATED;
+ }
+
+ /* Bounds check strings block */
+ if (!check_block_(hdrsize, fdt_totalsize(fdt),
+ fdt_off_dt_strings(fdt),
+ fdt_size_dt_strings(fdt)))
+ return -FDT_ERR_TRUNCATED;
+ }
+
+ return 0;
+}
+
+const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
+{
+ unsigned int uoffset = offset;
+ unsigned int absoffset = offset + fdt_off_dt_struct(fdt);
+
+ if (offset < 0)
+ return NULL;
+
+ if (!can_assume(VALID_INPUT))
+ if ((absoffset < uoffset)
+ || ((absoffset + len) < absoffset)
+ || (absoffset + len) > fdt_totalsize(fdt))
+ return NULL;
+
+ if (can_assume(LATEST) || fdt_version(fdt) >= 0x11)
+ if (((uoffset + len) < uoffset)
+ || ((offset + len) > fdt_size_dt_struct(fdt)))
+ return NULL;
+
+ return fdt_offset_ptr_(fdt, offset);
+}
+
+uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
+{
+ const fdt32_t *tagp, *lenp;
+ uint32_t tag;
+ int offset = startoffset;
+ const char *p;
+
+ *nextoffset = -FDT_ERR_TRUNCATED;
+ tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
+ if (!can_assume(VALID_DTB) && !tagp)
+ return FDT_END; /* premature end */
+ tag = fdt32_to_cpu(*tagp);
+ offset += FDT_TAGSIZE;
+
+ *nextoffset = -FDT_ERR_BADSTRUCTURE;
+ switch (tag) {
+ case FDT_BEGIN_NODE:
+ /* skip name */
+ do {
+ p = fdt_offset_ptr(fdt, offset++, 1);
+ } while (p && (*p != '\0'));
+ if (!can_assume(VALID_DTB) && !p)
+ return FDT_END; /* premature end */
+ break;
+
+ case FDT_PROP:
+ lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
+ if (!can_assume(VALID_DTB) && !lenp)
+ return FDT_END; /* premature end */
+ /* skip-name offset, length and value */
+ offset += sizeof(struct fdt_property) - FDT_TAGSIZE
+ + fdt32_to_cpu(*lenp);
+ if (!can_assume(LATEST) &&
+ fdt_version(fdt) < 0x10 && fdt32_to_cpu(*lenp) >= 8 &&
+ ((offset - fdt32_to_cpu(*lenp)) % 8) != 0)
+ offset += 4;
+ break;
+
+ case FDT_END:
+ case FDT_END_NODE:
+ case FDT_NOP:
+ break;
+
+ default:
+ return FDT_END;
+ }
+
+ if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
+ return FDT_END; /* premature end */
+
+ *nextoffset = FDT_TAGALIGN(offset);
+ return tag;
+}
+
+int fdt_check_node_offset_(const void *fdt, int offset)
+{
+ if (!can_assume(VALID_INPUT)
+ && ((offset < 0) || (offset % FDT_TAGSIZE)))
+ return -FDT_ERR_BADOFFSET;
+
+ if (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)
+ return -FDT_ERR_BADOFFSET;
+
+ return offset;
+}
+
+int fdt_check_prop_offset_(const void *fdt, int offset)
+{
+ if (!can_assume(VALID_INPUT)
+ && ((offset < 0) || (offset % FDT_TAGSIZE)))
+ return -FDT_ERR_BADOFFSET;
+
+ if (fdt_next_tag(fdt, offset, &offset) != FDT_PROP)
+ return -FDT_ERR_BADOFFSET;
+
+ return offset;
+}
+
+int fdt_next_node(const void *fdt, int offset, int *depth)
+{
+ int nextoffset = 0;
+ uint32_t tag;
+
+ if (offset >= 0)
+ if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0)
+ return nextoffset;
+
+ do {
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+
+ switch (tag) {
+ case FDT_PROP:
+ case FDT_NOP:
+ break;
+
+ case FDT_BEGIN_NODE:
+ if (depth)
+ (*depth)++;
+ break;
+
+ case FDT_END_NODE:
+ if (depth && ((--(*depth)) < 0))
+ return nextoffset;
+ break;
+
+ case FDT_END:
+ if ((nextoffset >= 0)
+ || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
+ return -FDT_ERR_NOTFOUND;
+ else
+ return nextoffset;
+ }
+ } while (tag != FDT_BEGIN_NODE);
+
+ return offset;
+}
+
+int fdt_first_subnode(const void *fdt, int offset)
+{
+ int depth = 0;
+
+ offset = fdt_next_node(fdt, offset, &depth);
+ if (offset < 0 || depth != 1)
+ return -FDT_ERR_NOTFOUND;
+
+ return offset;
+}
+
+int fdt_next_subnode(const void *fdt, int offset)
+{
+ int depth = 1;
+
+ /*
+ * With respect to the parent, the depth of the next subnode will be
+ * the same as the last.
+ */
+ do {
+ offset = fdt_next_node(fdt, offset, &depth);
+ if (offset < 0 || depth < 1)
+ return -FDT_ERR_NOTFOUND;
+ } while (depth > 1);
+
+ return offset;
+}
+
+const char *fdt_find_string_(const char *strtab, int tabsize, const char *s)
+{
+ int len = strlen(s) + 1;
+ const char *last = strtab + tabsize - len;
+ const char *p;
+
+ for (p = strtab; p <= last; p++)
+ if (memcmp(p, s, len) == 0)
+ return p;
+ return NULL;
+}
+
+int fdt_move(const void *fdt, void *buf, int bufsize)
+{
+ if (!can_assume(VALID_INPUT) && bufsize < 0)
+ return -FDT_ERR_NOSPACE;
+
+ FDT_RO_PROBE(fdt);
+
+ if (fdt_totalsize(fdt) > (unsigned int)bufsize)
+ return -FDT_ERR_NOSPACE;
+
+ memmove(buf, fdt, fdt_totalsize(fdt));
+ return 0;
+}
diff --git a/tools/src/libfdt/fdt.h b/tools/src/libfdt/fdt.h
new file mode 100644
index 0000000..f2e6880
--- /dev/null
+++ b/tools/src/libfdt/fdt.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */
+#ifndef FDT_H
+#define FDT_H
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ * Copyright 2012 Kim Phillips, Freescale Semiconductor.
+ */
+
+#ifndef __ASSEMBLY__
+
+struct fdt_header {
+ fdt32_t magic; /* magic word FDT_MAGIC */
+ fdt32_t totalsize; /* total size of DT block */
+ fdt32_t off_dt_struct; /* offset to structure */
+ fdt32_t off_dt_strings; /* offset to strings */
+ fdt32_t off_mem_rsvmap; /* offset to memory reserve map */
+ fdt32_t version; /* format version */
+ fdt32_t last_comp_version; /* last compatible version */
+
+ /* version 2 fields below */
+ fdt32_t boot_cpuid_phys; /* Which physical CPU id we're
+ booting on */
+ /* version 3 fields below */
+ fdt32_t size_dt_strings; /* size of the strings block */
+
+ /* version 17 fields below */
+ fdt32_t size_dt_struct; /* size of the structure block */
+};
+
+struct fdt_reserve_entry {
+ fdt64_t address;
+ fdt64_t size;
+};
+
+struct fdt_node_header {
+ fdt32_t tag;
+ char name[0];
+};
+
+struct fdt_property {
+ fdt32_t tag;
+ fdt32_t len;
+ fdt32_t nameoff;
+ char data[0];
+};
+
+#endif /* !__ASSEMBLY */
+
+#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */
+#define FDT_TAGSIZE sizeof(fdt32_t)
+
+#define FDT_BEGIN_NODE 0x1 /* Start node: full name */
+#define FDT_END_NODE 0x2 /* End node */
+#define FDT_PROP 0x3 /* Property: name off,
+ size, content */
+#define FDT_NOP 0x4 /* nop */
+#define FDT_END 0x9
+
+#define FDT_V1_SIZE (7*sizeof(fdt32_t))
+#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t))
+#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t))
+#define FDT_V16_SIZE FDT_V3_SIZE
+#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(fdt32_t))
+
+#endif /* FDT_H */
diff --git a/tools/src/libfdt/fdt_addresses.c b/tools/src/libfdt/fdt_addresses.c
new file mode 100644
index 0000000..6357859
--- /dev/null
+++ b/tools/src/libfdt/fdt_addresses.c
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2014 David Gibson <david@gibson.dropbear.id.au>
+ * Copyright (C) 2018 embedded brains GmbH
+ */
+#include "libfdt_env.h"
+
+#include "fdt.h"
+#include "libfdt.h"
+
+#include "libfdt_internal.h"
+
+static int fdt_cells(const void *fdt, int nodeoffset, const char *name)
+{
+ const fdt32_t *c;
+ uint32_t val;
+ int len;
+
+ c = fdt_getprop(fdt, nodeoffset, name, &len);
+ if (!c)
+ return len;
+
+ if (len != sizeof(*c))
+ return -FDT_ERR_BADNCELLS;
+
+ val = fdt32_to_cpu(*c);
+ if (val > FDT_MAX_NCELLS)
+ return -FDT_ERR_BADNCELLS;
+
+ return (int)val;
+}
+
+int fdt_address_cells(const void *fdt, int nodeoffset)
+{
+ int val;
+
+ val = fdt_cells(fdt, nodeoffset, "#address-cells");
+ if (val == 0)
+ return -FDT_ERR_BADNCELLS;
+ if (val == -FDT_ERR_NOTFOUND)
+ return 2;
+ return val;
+}
+
+int fdt_size_cells(const void *fdt, int nodeoffset)
+{
+ int val;
+
+ val = fdt_cells(fdt, nodeoffset, "#size-cells");
+ if (val == -FDT_ERR_NOTFOUND)
+ return 1;
+ return val;
+}
+
+/* This function assumes that [address|size]_cells is 1 or 2 */
+int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset,
+ const char *name, uint64_t addr, uint64_t size)
+{
+ int addr_cells, size_cells, ret;
+ uint8_t data[sizeof(fdt64_t) * 2], *prop;
+
+ ret = fdt_address_cells(fdt, parent);
+ if (ret < 0)
+ return ret;
+ addr_cells = ret;
+
+ ret = fdt_size_cells(fdt, parent);
+ if (ret < 0)
+ return ret;
+ size_cells = ret;
+
+ /* check validity of address */
+ prop = data;
+ if (addr_cells == 1) {
+ if ((addr > UINT32_MAX) || ((UINT32_MAX + 1 - addr) < size))
+ return -FDT_ERR_BADVALUE;
+
+ fdt32_st(prop, (uint32_t)addr);
+ } else if (addr_cells == 2) {
+ fdt64_st(prop, addr);
+ } else {
+ return -FDT_ERR_BADNCELLS;
+ }
+
+ /* check validity of size */
+ prop += addr_cells * sizeof(fdt32_t);
+ if (size_cells == 1) {
+ if (size > UINT32_MAX)
+ return -FDT_ERR_BADVALUE;
+
+ fdt32_st(prop, (uint32_t)size);
+ } else if (size_cells == 2) {
+ fdt64_st(prop, size);
+ } else {
+ return -FDT_ERR_BADNCELLS;
+ }
+
+ return fdt_appendprop(fdt, nodeoffset, name, data,
+ (addr_cells + size_cells) * sizeof(fdt32_t));
+}
diff --git a/tools/src/libfdt/fdt_empty_tree.c b/tools/src/libfdt/fdt_empty_tree.c
new file mode 100644
index 0000000..15f0cd7
--- /dev/null
+++ b/tools/src/libfdt/fdt_empty_tree.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2012 David Gibson, IBM Corporation.
+ */
+#include "libfdt_env.h"
+
+#include "fdt.h"
+#include "libfdt.h"
+
+#include "libfdt_internal.h"
+
+int fdt_create_empty_tree(void *buf, int bufsize)
+{
+ int err;
+
+ err = fdt_create(buf, bufsize);
+ if (err)
+ return err;
+
+ err = fdt_finish_reservemap(buf);
+ if (err)
+ return err;
+
+ err = fdt_begin_node(buf, "");
+ if (err)
+ return err;
+
+ err = fdt_end_node(buf);
+ if (err)
+ return err;
+
+ err = fdt_finish(buf);
+ if (err)
+ return err;
+
+ return fdt_open_into(buf, buf, bufsize);
+}
diff --git a/tools/src/libfdt/fdt_overlay.c b/tools/src/libfdt/fdt_overlay.c
new file mode 100644
index 0000000..1fc78d4
--- /dev/null
+++ b/tools/src/libfdt/fdt_overlay.c
@@ -0,0 +1,882 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2016 Free Electrons
+ * Copyright (C) 2016 NextThing Co.
+ */
+#include "libfdt_env.h"
+
+#include "fdt.h"
+#include "libfdt.h"
+
+#include "libfdt_internal.h"
+
+/**
+ * overlay_get_target_phandle - retrieves the target phandle of a fragment
+ * @fdto: pointer to the device tree overlay blob
+ * @fragment: node offset of the fragment in the overlay
+ *
+ * overlay_get_target_phandle() retrieves the target phandle of an
+ * overlay fragment when that fragment uses a phandle (target
+ * property) instead of a path (target-path property).
+ *
+ * returns:
+ * the phandle pointed by the target property
+ * 0, if the phandle was not found
+ * -1, if the phandle was malformed
+ */
+static uint32_t overlay_get_target_phandle(const void *fdto, int fragment)
+{
+ const fdt32_t *val;
+ int len;
+
+ val = fdt_getprop(fdto, fragment, "target", &len);
+ if (!val)
+ return 0;
+
+ if ((len != sizeof(*val)) || (fdt32_to_cpu(*val) == (uint32_t)-1))
+ return (uint32_t)-1;
+
+ return fdt32_to_cpu(*val);
+}
+
+/**
+ * overlay_get_target - retrieves the offset of a fragment's target
+ * @fdt: Base device tree blob
+ * @fdto: Device tree overlay blob
+ * @fragment: node offset of the fragment in the overlay
+ * @pathp: pointer which receives the path of the target (or NULL)
+ *
+ * overlay_get_target() retrieves the target offset in the base
+ * device tree of a fragment, no matter how the actual targeting is
+ * done (through a phandle or a path)
+ *
+ * returns:
+ * the targeted node offset in the base device tree
+ * Negative error code on error
+ */
+static int overlay_get_target(const void *fdt, const void *fdto,
+ int fragment, char const **pathp)
+{
+ uint32_t phandle;
+ const char *path = NULL;
+ int path_len = 0, ret;
+
+ /* Try first to do a phandle based lookup */
+ phandle = overlay_get_target_phandle(fdto, fragment);
+ if (phandle == (uint32_t)-1)
+ return -FDT_ERR_BADPHANDLE;
+
+ /* no phandle, try path */
+ if (!phandle) {
+ /* And then a path based lookup */
+ path = fdt_getprop(fdto, fragment, "target-path", &path_len);
+ if (path)
+ ret = fdt_path_offset(fdt, path);
+ else
+ ret = path_len;
+ } else
+ ret = fdt_node_offset_by_phandle(fdt, phandle);
+
+ /*
+ * If we haven't found either a target or a
+ * target-path property in a node that contains a
+ * __overlay__ subnode (we wouldn't be called
+ * otherwise), consider it a improperly written
+ * overlay
+ */
+ if (ret < 0 && path_len == -FDT_ERR_NOTFOUND)
+ ret = -FDT_ERR_BADOVERLAY;
+
+ /* return on error */
+ if (ret < 0)
+ return ret;
+
+ /* return pointer to path (if available) */
+ if (pathp)
+ *pathp = path ? path : NULL;
+
+ return ret;
+}
+
+/**
+ * overlay_phandle_add_offset - Increases a phandle by an offset
+ * @fdt: Base device tree blob
+ * @node: Device tree overlay blob
+ * @name: Name of the property to modify (phandle or linux,phandle)
+ * @delta: offset to apply
+ *
+ * overlay_phandle_add_offset() increments a node phandle by a given
+ * offset.
+ *
+ * returns:
+ * 0 on success.
+ * Negative error code on error
+ */
+static int overlay_phandle_add_offset(void *fdt, int node,
+ const char *name, uint32_t delta)
+{
+ const fdt32_t *val;
+ uint32_t adj_val;
+ int len;
+
+ val = fdt_getprop(fdt, node, name, &len);
+ if (!val)
+ return len;
+
+ if (len != sizeof(*val))
+ return -FDT_ERR_BADPHANDLE;
+
+ adj_val = fdt32_to_cpu(*val);
+ if ((adj_val + delta) < adj_val)
+ return -FDT_ERR_NOPHANDLES;
+
+ adj_val += delta;
+ if (adj_val == (uint32_t)-1)
+ return -FDT_ERR_NOPHANDLES;
+
+ return fdt_setprop_inplace_u32(fdt, node, name, adj_val);
+}
+
+/**
+ * overlay_adjust_node_phandles - Offsets the phandles of a node
+ * @fdto: Device tree overlay blob
+ * @node: Offset of the node we want to adjust
+ * @delta: Offset to shift the phandles of
+ *
+ * overlay_adjust_node_phandles() adds a constant to all the phandles
+ * of a given node. This is mainly use as part of the overlay
+ * application process, when we want to update all the overlay
+ * phandles to not conflict with the overlays of the base device tree.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_adjust_node_phandles(void *fdto, int node,
+ uint32_t delta)
+{
+ int child;
+ int ret;
+
+ ret = overlay_phandle_add_offset(fdto, node, "phandle", delta);
+ if (ret && ret != -FDT_ERR_NOTFOUND)
+ return ret;
+
+ ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta);
+ if (ret && ret != -FDT_ERR_NOTFOUND)
+ return ret;
+
+ fdt_for_each_subnode(child, fdto, node) {
+ ret = overlay_adjust_node_phandles(fdto, child, delta);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * overlay_adjust_local_phandles - Adjust the phandles of a whole overlay
+ * @fdto: Device tree overlay blob
+ * @delta: Offset to shift the phandles of
+ *
+ * overlay_adjust_local_phandles() adds a constant to all the
+ * phandles of an overlay. This is mainly use as part of the overlay
+ * application process, when we want to update all the overlay
+ * phandles to not conflict with the overlays of the base device tree.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_adjust_local_phandles(void *fdto, uint32_t delta)
+{
+ /*
+ * Start adjusting the phandles from the overlay root
+ */
+ return overlay_adjust_node_phandles(fdto, 0, delta);
+}
+
+/**
+ * overlay_update_local_node_references - Adjust the overlay references
+ * @fdto: Device tree overlay blob
+ * @tree_node: Node offset of the node to operate on
+ * @fixup_node: Node offset of the matching local fixups node
+ * @delta: Offset to shift the phandles of
+ *
+ * overlay_update_local_nodes_references() update the phandles
+ * pointing to a node within the device tree overlay by adding a
+ * constant delta.
+ *
+ * This is mainly used as part of a device tree application process,
+ * where you want the device tree overlays phandles to not conflict
+ * with the ones from the base device tree before merging them.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_update_local_node_references(void *fdto,
+ int tree_node,
+ int fixup_node,
+ uint32_t delta)
+{
+ int fixup_prop;
+ int fixup_child;
+ int ret;
+
+ fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) {
+ const fdt32_t *fixup_val;
+ const char *tree_val;
+ const char *name;
+ int fixup_len;
+ int tree_len;
+ int i;
+
+ fixup_val = fdt_getprop_by_offset(fdto, fixup_prop,
+ &name, &fixup_len);
+ if (!fixup_val)
+ return fixup_len;
+
+ if (fixup_len % sizeof(uint32_t))
+ return -FDT_ERR_BADOVERLAY;
+ fixup_len /= sizeof(uint32_t);
+
+ tree_val = fdt_getprop(fdto, tree_node, name, &tree_len);
+ if (!tree_val) {
+ if (tree_len == -FDT_ERR_NOTFOUND)
+ return -FDT_ERR_BADOVERLAY;
+
+ return tree_len;
+ }
+
+ for (i = 0; i < fixup_len; i++) {
+ fdt32_t adj_val;
+ uint32_t poffset;
+
+ poffset = fdt32_to_cpu(fixup_val[i]);
+
+ /*
+ * phandles to fixup can be unaligned.
+ *
+ * Use a memcpy for the architectures that do
+ * not support unaligned accesses.
+ */
+ memcpy(&adj_val, tree_val + poffset, sizeof(adj_val));
+
+ adj_val = cpu_to_fdt32(fdt32_to_cpu(adj_val) + delta);
+
+ ret = fdt_setprop_inplace_namelen_partial(fdto,
+ tree_node,
+ name,
+ strlen(name),
+ poffset,
+ &adj_val,
+ sizeof(adj_val));
+ if (ret == -FDT_ERR_NOSPACE)
+ return -FDT_ERR_BADOVERLAY;
+
+ if (ret)
+ return ret;
+ }
+ }
+
+ fdt_for_each_subnode(fixup_child, fdto, fixup_node) {
+ const char *fixup_child_name = fdt_get_name(fdto, fixup_child,
+ NULL);
+ int tree_child;
+
+ tree_child = fdt_subnode_offset(fdto, tree_node,
+ fixup_child_name);
+ if (tree_child == -FDT_ERR_NOTFOUND)
+ return -FDT_ERR_BADOVERLAY;
+ if (tree_child < 0)
+ return tree_child;
+
+ ret = overlay_update_local_node_references(fdto,
+ tree_child,
+ fixup_child,
+ delta);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * overlay_update_local_references - Adjust the overlay references
+ * @fdto: Device tree overlay blob
+ * @delta: Offset to shift the phandles of
+ *
+ * overlay_update_local_references() update all the phandles pointing
+ * to a node within the device tree overlay by adding a constant
+ * delta to not conflict with the base overlay.
+ *
+ * This is mainly used as part of a device tree application process,
+ * where you want the device tree overlays phandles to not conflict
+ * with the ones from the base device tree before merging them.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_update_local_references(void *fdto, uint32_t delta)
+{
+ int fixups;
+
+ fixups = fdt_path_offset(fdto, "/__local_fixups__");
+ if (fixups < 0) {
+ /* There's no local phandles to adjust, bail out */
+ if (fixups == -FDT_ERR_NOTFOUND)
+ return 0;
+
+ return fixups;
+ }
+
+ /*
+ * Update our local references from the root of the tree
+ */
+ return overlay_update_local_node_references(fdto, 0, fixups,
+ delta);
+}
+
+/**
+ * overlay_fixup_one_phandle - Set an overlay phandle to the base one
+ * @fdt: Base Device Tree blob
+ * @fdto: Device tree overlay blob
+ * @symbols_off: Node offset of the symbols node in the base device tree
+ * @path: Path to a node holding a phandle in the overlay
+ * @path_len: number of path characters to consider
+ * @name: Name of the property holding the phandle reference in the overlay
+ * @name_len: number of name characters to consider
+ * @poffset: Offset within the overlay property where the phandle is stored
+ * @label: Label of the node referenced by the phandle
+ *
+ * overlay_fixup_one_phandle() resolves an overlay phandle pointing to
+ * a node in the base device tree.
+ *
+ * This is part of the device tree overlay application process, when
+ * you want all the phandles in the overlay to point to the actual
+ * base dt nodes.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_fixup_one_phandle(void *fdt, void *fdto,
+ int symbols_off,
+ const char *path, uint32_t path_len,
+ const char *name, uint32_t name_len,
+ int poffset, const char *label)
+{
+ const char *symbol_path;
+ uint32_t phandle;
+ fdt32_t phandle_prop;
+ int symbol_off, fixup_off;
+ int prop_len;
+
+ if (symbols_off < 0)
+ return symbols_off;
+
+ symbol_path = fdt_getprop(fdt, symbols_off, label,
+ &prop_len);
+ if (!symbol_path)
+ return prop_len;
+
+ symbol_off = fdt_path_offset(fdt, symbol_path);
+ if (symbol_off < 0)
+ return symbol_off;
+
+ phandle = fdt_get_phandle(fdt, symbol_off);
+ if (!phandle)
+ return -FDT_ERR_NOTFOUND;
+
+ fixup_off = fdt_path_offset_namelen(fdto, path, path_len);
+ if (fixup_off == -FDT_ERR_NOTFOUND)
+ return -FDT_ERR_BADOVERLAY;
+ if (fixup_off < 0)
+ return fixup_off;
+
+ phandle_prop = cpu_to_fdt32(phandle);
+ return fdt_setprop_inplace_namelen_partial(fdto, fixup_off,
+ name, name_len, poffset,
+ &phandle_prop,
+ sizeof(phandle_prop));
+};
+
+/**
+ * overlay_fixup_phandle - Set an overlay phandle to the base one
+ * @fdt: Base Device Tree blob
+ * @fdto: Device tree overlay blob
+ * @symbols_off: Node offset of the symbols node in the base device tree
+ * @property: Property offset in the overlay holding the list of fixups
+ *
+ * overlay_fixup_phandle() resolves all the overlay phandles pointed
+ * to in a __fixups__ property, and updates them to match the phandles
+ * in use in the base device tree.
+ *
+ * This is part of the device tree overlay application process, when
+ * you want all the phandles in the overlay to point to the actual
+ * base dt nodes.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off,
+ int property)
+{
+ const char *value;
+ const char *label;
+ int len;
+
+ value = fdt_getprop_by_offset(fdto, property,
+ &label, &len);
+ if (!value) {
+ if (len == -FDT_ERR_NOTFOUND)
+ return -FDT_ERR_INTERNAL;
+
+ return len;
+ }
+
+ do {
+ const char *path, *name, *fixup_end;
+ const char *fixup_str = value;
+ uint32_t path_len, name_len;
+ uint32_t fixup_len;
+ char *sep, *endptr;
+ int poffset, ret;
+
+ fixup_end = memchr(value, '\0', len);
+ if (!fixup_end)
+ return -FDT_ERR_BADOVERLAY;
+ fixup_len = fixup_end - fixup_str;
+
+ len -= fixup_len + 1;
+ value += fixup_len + 1;
+
+ path = fixup_str;
+ sep = memchr(fixup_str, ':', fixup_len);
+ if (!sep || *sep != ':')
+ return -FDT_ERR_BADOVERLAY;
+
+ path_len = sep - path;
+ if (path_len == (fixup_len - 1))
+ return -FDT_ERR_BADOVERLAY;
+
+ fixup_len -= path_len + 1;
+ name = sep + 1;
+ sep = memchr(name, ':', fixup_len);
+ if (!sep || *sep != ':')
+ return -FDT_ERR_BADOVERLAY;
+
+ name_len = sep - name;
+ if (!name_len)
+ return -FDT_ERR_BADOVERLAY;
+
+ poffset = strtoul(sep + 1, &endptr, 10);
+ if ((*endptr != '\0') || (endptr <= (sep + 1)))
+ return -FDT_ERR_BADOVERLAY;
+
+ ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off,
+ path, path_len, name, name_len,
+ poffset, label);
+ if (ret)
+ return ret;
+ } while (len > 0);
+
+ return 0;
+}
+
+/**
+ * overlay_fixup_phandles - Resolve the overlay phandles to the base
+ * device tree
+ * @fdt: Base Device Tree blob
+ * @fdto: Device tree overlay blob
+ *
+ * overlay_fixup_phandles() resolves all the overlay phandles pointing
+ * to nodes in the base device tree.
+ *
+ * This is one of the steps of the device tree overlay application
+ * process, when you want all the phandles in the overlay to point to
+ * the actual base dt nodes.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_fixup_phandles(void *fdt, void *fdto)
+{
+ int fixups_off, symbols_off;
+ int property;
+
+ /* We can have overlays without any fixups */
+ fixups_off = fdt_path_offset(fdto, "/__fixups__");
+ if (fixups_off == -FDT_ERR_NOTFOUND)
+ return 0; /* nothing to do */
+ if (fixups_off < 0)
+ return fixups_off;
+
+ /* And base DTs without symbols */
+ symbols_off = fdt_path_offset(fdt, "/__symbols__");
+ if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND)))
+ return symbols_off;
+
+ fdt_for_each_property_offset(property, fdto, fixups_off) {
+ int ret;
+
+ ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * overlay_apply_node - Merges a node into the base device tree
+ * @fdt: Base Device Tree blob
+ * @target: Node offset in the base device tree to apply the fragment to
+ * @fdto: Device tree overlay blob
+ * @node: Node offset in the overlay holding the changes to merge
+ *
+ * overlay_apply_node() merges a node into a target base device tree
+ * node pointed.
+ *
+ * This is part of the final step in the device tree overlay
+ * application process, when all the phandles have been adjusted and
+ * resolved and you just have to merge overlay into the base device
+ * tree.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_apply_node(void *fdt, int target,
+ void *fdto, int node)
+{
+ int property;
+ int subnode;
+
+ fdt_for_each_property_offset(property, fdto, node) {
+ const char *name;
+ const void *prop;
+ int prop_len;
+ int ret;
+
+ prop = fdt_getprop_by_offset(fdto, property, &name,
+ &prop_len);
+ if (prop_len == -FDT_ERR_NOTFOUND)
+ return -FDT_ERR_INTERNAL;
+ if (prop_len < 0)
+ return prop_len;
+
+ ret = fdt_setprop(fdt, target, name, prop, prop_len);
+ if (ret)
+ return ret;
+ }
+
+ fdt_for_each_subnode(subnode, fdto, node) {
+ const char *name = fdt_get_name(fdto, subnode, NULL);
+ int nnode;
+ int ret;
+
+ nnode = fdt_add_subnode(fdt, target, name);
+ if (nnode == -FDT_ERR_EXISTS) {
+ nnode = fdt_subnode_offset(fdt, target, name);
+ if (nnode == -FDT_ERR_NOTFOUND)
+ return -FDT_ERR_INTERNAL;
+ }
+
+ if (nnode < 0)
+ return nnode;
+
+ ret = overlay_apply_node(fdt, nnode, fdto, subnode);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * overlay_merge - Merge an overlay into its base device tree
+ * @fdt: Base Device Tree blob
+ * @fdto: Device tree overlay blob
+ *
+ * overlay_merge() merges an overlay into its base device tree.
+ *
+ * This is the next to last step in the device tree overlay application
+ * process, when all the phandles have been adjusted and resolved and
+ * you just have to merge overlay into the base device tree.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_merge(void *fdt, void *fdto)
+{
+ int fragment;
+
+ fdt_for_each_subnode(fragment, fdto, 0) {
+ int overlay;
+ int target;
+ int ret;
+
+ /*
+ * Each fragments will have an __overlay__ node. If
+ * they don't, it's not supposed to be merged
+ */
+ overlay = fdt_subnode_offset(fdto, fragment, "__overlay__");
+ if (overlay == -FDT_ERR_NOTFOUND)
+ continue;
+
+ if (overlay < 0)
+ return overlay;
+
+ target = overlay_get_target(fdt, fdto, fragment, NULL);
+ if (target < 0)
+ return target;
+
+ ret = overlay_apply_node(fdt, target, fdto, overlay);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int get_path_len(const void *fdt, int nodeoffset)
+{
+ int len = 0, namelen;
+ const char *name;
+
+ FDT_RO_PROBE(fdt);
+
+ for (;;) {
+ name = fdt_get_name(fdt, nodeoffset, &namelen);
+ if (!name)
+ return namelen;
+
+ /* root? we're done */
+ if (namelen == 0)
+ break;
+
+ nodeoffset = fdt_parent_offset(fdt, nodeoffset);
+ if (nodeoffset < 0)
+ return nodeoffset;
+ len += namelen + 1;
+ }
+
+ /* in case of root pretend it's "/" */
+ if (len == 0)
+ len++;
+ return len;
+}
+
+/**
+ * overlay_symbol_update - Update the symbols of base tree after a merge
+ * @fdt: Base Device Tree blob
+ * @fdto: Device tree overlay blob
+ *
+ * overlay_symbol_update() updates the symbols of the base tree with the
+ * symbols of the applied overlay
+ *
+ * This is the last step in the device tree overlay application
+ * process, allowing the reference of overlay symbols by subsequent
+ * overlay operations.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int overlay_symbol_update(void *fdt, void *fdto)
+{
+ int root_sym, ov_sym, prop, path_len, fragment, target;
+ int len, frag_name_len, ret, rel_path_len;
+ const char *s, *e;
+ const char *path;
+ const char *name;
+ const char *frag_name;
+ const char *rel_path;
+ const char *target_path;
+ char *buf;
+ void *p;
+
+ ov_sym = fdt_subnode_offset(fdto, 0, "__symbols__");
+
+ /* if no overlay symbols exist no problem */
+ if (ov_sym < 0)
+ return 0;
+
+ root_sym = fdt_subnode_offset(fdt, 0, "__symbols__");
+
+ /* it no root symbols exist we should create them */
+ if (root_sym == -FDT_ERR_NOTFOUND)
+ root_sym = fdt_add_subnode(fdt, 0, "__symbols__");
+
+ /* any error is fatal now */
+ if (root_sym < 0)
+ return root_sym;
+
+ /* iterate over each overlay symbol */
+ fdt_for_each_property_offset(prop, fdto, ov_sym) {
+ path = fdt_getprop_by_offset(fdto, prop, &name, &path_len);
+ if (!path)
+ return path_len;
+
+ /* verify it's a string property (terminated by a single \0) */
+ if (path_len < 1 || memchr(path, '\0', path_len) != &path[path_len - 1])
+ return -FDT_ERR_BADVALUE;
+
+ /* keep end marker to avoid strlen() */
+ e = path + path_len;
+
+ if (*path != '/')
+ return -FDT_ERR_BADVALUE;
+
+ /* get fragment name first */
+ s = strchr(path + 1, '/');
+ if (!s) {
+ /* Symbol refers to something that won't end
+ * up in the target tree */
+ continue;
+ }
+
+ frag_name = path + 1;
+ frag_name_len = s - path - 1;
+
+ /* verify format; safe since "s" lies in \0 terminated prop */
+ len = sizeof("/__overlay__/") - 1;
+ if ((e - s) > len && (memcmp(s, "/__overlay__/", len) == 0)) {
+ /* /<fragment-name>/__overlay__/<relative-subnode-path> */
+ rel_path = s + len;
+ rel_path_len = e - rel_path - 1;
+ } else if ((e - s) == len
+ && (memcmp(s, "/__overlay__", len - 1) == 0)) {
+ /* /<fragment-name>/__overlay__ */
+ rel_path = "";
+ rel_path_len = 0;
+ } else {
+ /* Symbol refers to something that won't end
+ * up in the target tree */
+ continue;
+ }
+
+ /* find the fragment index in which the symbol lies */
+ ret = fdt_subnode_offset_namelen(fdto, 0, frag_name,
+ frag_name_len);
+ /* not found? */
+ if (ret < 0)
+ return -FDT_ERR_BADOVERLAY;
+ fragment = ret;
+
+ /* an __overlay__ subnode must exist */
+ ret = fdt_subnode_offset(fdto, fragment, "__overlay__");
+ if (ret < 0)
+ return -FDT_ERR_BADOVERLAY;
+
+ /* get the target of the fragment */
+ ret = overlay_get_target(fdt, fdto, fragment, &target_path);
+ if (ret < 0)
+ return ret;
+ target = ret;
+
+ /* if we have a target path use */
+ if (!target_path) {
+ ret = get_path_len(fdt, target);
+ if (ret < 0)
+ return ret;
+ len = ret;
+ } else {
+ len = strlen(target_path);
+ }
+
+ ret = fdt_setprop_placeholder(fdt, root_sym, name,
+ len + (len > 1) + rel_path_len + 1, &p);
+ if (ret < 0)
+ return ret;
+
+ if (!target_path) {
+ /* again in case setprop_placeholder changed it */
+ ret = overlay_get_target(fdt, fdto, fragment, &target_path);
+ if (ret < 0)
+ return ret;
+ target = ret;
+ }
+
+ buf = p;
+ if (len > 1) { /* target is not root */
+ if (!target_path) {
+ ret = fdt_get_path(fdt, target, buf, len + 1);
+ if (ret < 0)
+ return ret;
+ } else
+ memcpy(buf, target_path, len + 1);
+
+ } else
+ len--;
+
+ buf[len] = '/';
+ memcpy(buf + len + 1, rel_path, rel_path_len);
+ buf[len + 1 + rel_path_len] = '\0';
+ }
+
+ return 0;
+}
+
+int fdt_overlay_apply(void *fdt, void *fdto)
+{
+ uint32_t delta;
+ int ret;
+
+ FDT_RO_PROBE(fdt);
+ FDT_RO_PROBE(fdto);
+
+ ret = fdt_find_max_phandle(fdt, &delta);
+ if (ret)
+ goto err;
+
+ ret = overlay_adjust_local_phandles(fdto, delta);
+ if (ret)
+ goto err;
+
+ ret = overlay_update_local_references(fdto, delta);
+ if (ret)
+ goto err;
+
+ ret = overlay_fixup_phandles(fdt, fdto);
+ if (ret)
+ goto err;
+
+ ret = overlay_merge(fdt, fdto);
+ if (ret)
+ goto err;
+
+ ret = overlay_symbol_update(fdt, fdto);
+ if (ret)
+ goto err;
+
+ /*
+ * The overlay has been damaged, erase its magic.
+ */
+ fdt_set_magic(fdto, ~0);
+
+ return 0;
+
+err:
+ /*
+ * The overlay might have been damaged, erase its magic.
+ */
+ fdt_set_magic(fdto, ~0);
+
+ /*
+ * The base device tree might have been damaged, erase its
+ * magic.
+ */
+ fdt_set_magic(fdt, ~0);
+
+ return ret;
+}
diff --git a/tools/src/libfdt/fdt_ro.c b/tools/src/libfdt/fdt_ro.c
new file mode 100644
index 0000000..e7f8b62
--- /dev/null
+++ b/tools/src/libfdt/fdt_ro.c
@@ -0,0 +1,859 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include "libfdt_env.h"
+
+#include "fdt.h"
+#include "libfdt.h"
+
+#include "libfdt_internal.h"
+
+static int fdt_nodename_eq_(const void *fdt, int offset,
+ const char *s, int len)
+{
+ int olen;
+ const char *p = fdt_get_name(fdt, offset, &olen);
+
+ if (!p || olen < len)
+ /* short match */
+ return 0;
+
+ if (memcmp(p, s, len) != 0)
+ return 0;
+
+ if (p[len] == '\0')
+ return 1;
+ else if (!memchr(s, '@', len) && (p[len] == '@'))
+ return 1;
+ else
+ return 0;
+}
+
+const char *fdt_get_string(const void *fdt, int stroffset, int *lenp)
+{
+ int32_t totalsize;
+ uint32_t absoffset;
+ size_t len;
+ int err;
+ const char *s, *n;
+
+ if (can_assume(VALID_INPUT)) {
+ s = (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
+
+ if (lenp)
+ *lenp = strlen(s);
+ return s;
+ }
+ totalsize = fdt_ro_probe_(fdt);
+ err = totalsize;
+ if (totalsize < 0)
+ goto fail;
+
+ err = -FDT_ERR_BADOFFSET;
+ absoffset = stroffset + fdt_off_dt_strings(fdt);
+ if (absoffset >= (unsigned)totalsize)
+ goto fail;
+ len = totalsize - absoffset;
+
+ if (fdt_magic(fdt) == FDT_MAGIC) {
+ if (stroffset < 0)
+ goto fail;
+ if (can_assume(LATEST) || fdt_version(fdt) >= 17) {
+ if ((unsigned)stroffset >= fdt_size_dt_strings(fdt))
+ goto fail;
+ if ((fdt_size_dt_strings(fdt) - stroffset) < len)
+ len = fdt_size_dt_strings(fdt) - stroffset;
+ }
+ } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
+ unsigned int sw_stroffset = -stroffset;
+
+ if ((stroffset >= 0) ||
+ (sw_stroffset > fdt_size_dt_strings(fdt)))
+ goto fail;
+ if (sw_stroffset < len)
+ len = sw_stroffset;
+ } else {
+ err = -FDT_ERR_INTERNAL;
+ goto fail;
+ }
+
+ s = (const char *)fdt + absoffset;
+ n = memchr(s, '\0', len);
+ if (!n) {
+ /* missing terminating NULL */
+ err = -FDT_ERR_TRUNCATED;
+ goto fail;
+ }
+
+ if (lenp)
+ *lenp = n - s;
+ return s;
+
+fail:
+ if (lenp)
+ *lenp = err;
+ return NULL;
+}
+
+const char *fdt_string(const void *fdt, int stroffset)
+{
+ return fdt_get_string(fdt, stroffset, NULL);
+}
+
+static int fdt_string_eq_(const void *fdt, int stroffset,
+ const char *s, int len)
+{
+ int slen;
+ const char *p = fdt_get_string(fdt, stroffset, &slen);
+
+ return p && (slen == len) && (memcmp(p, s, len) == 0);
+}
+
+int fdt_find_max_phandle(const void *fdt, uint32_t *phandle)
+{
+ uint32_t max = 0;
+ int offset = -1;
+
+ while (true) {
+ uint32_t value;
+
+ offset = fdt_next_node(fdt, offset, NULL);
+ if (offset < 0) {
+ if (offset == -FDT_ERR_NOTFOUND)
+ break;
+
+ return offset;
+ }
+
+ value = fdt_get_phandle(fdt, offset);
+
+ if (value > max)
+ max = value;
+ }
+
+ if (phandle)
+ *phandle = max;
+
+ return 0;
+}
+
+int fdt_generate_phandle(const void *fdt, uint32_t *phandle)
+{
+ uint32_t max;
+ int err;
+
+ err = fdt_find_max_phandle(fdt, &max);
+ if (err < 0)
+ return err;
+
+ if (max == FDT_MAX_PHANDLE)
+ return -FDT_ERR_NOPHANDLES;
+
+ if (phandle)
+ *phandle = max + 1;
+
+ return 0;
+}
+
+static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n)
+{
+ unsigned int offset = n * sizeof(struct fdt_reserve_entry);
+ unsigned int absoffset = fdt_off_mem_rsvmap(fdt) + offset;
+
+ if (!can_assume(VALID_INPUT)) {
+ if (absoffset < fdt_off_mem_rsvmap(fdt))
+ return NULL;
+ if (absoffset > fdt_totalsize(fdt) -
+ sizeof(struct fdt_reserve_entry))
+ return NULL;
+ }
+ return fdt_mem_rsv_(fdt, n);
+}
+
+int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
+{
+ const struct fdt_reserve_entry *re;
+
+ FDT_RO_PROBE(fdt);
+ re = fdt_mem_rsv(fdt, n);
+ if (!can_assume(VALID_INPUT) && !re)
+ return -FDT_ERR_BADOFFSET;
+
+ *address = fdt64_ld(&re->address);
+ *size = fdt64_ld(&re->size);
+ return 0;
+}
+
+int fdt_num_mem_rsv(const void *fdt)
+{
+ int i;
+ const struct fdt_reserve_entry *re;
+
+ for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) {
+ if (fdt64_ld(&re->size) == 0)
+ return i;
+ }
+ return -FDT_ERR_TRUNCATED;
+}
+
+static int nextprop_(const void *fdt, int offset)
+{
+ uint32_t tag;
+ int nextoffset;
+
+ do {
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+
+ switch (tag) {
+ case FDT_END:
+ if (nextoffset >= 0)
+ return -FDT_ERR_BADSTRUCTURE;
+ else
+ return nextoffset;
+
+ case FDT_PROP:
+ return offset;
+ }
+ offset = nextoffset;
+ } while (tag == FDT_NOP);
+
+ return -FDT_ERR_NOTFOUND;
+}
+
+int fdt_subnode_offset_namelen(const void *fdt, int offset,
+ const char *name, int namelen)
+{
+ int depth;
+
+ FDT_RO_PROBE(fdt);
+
+ for (depth = 0;
+ (offset >= 0) && (depth >= 0);
+ offset = fdt_next_node(fdt, offset, &depth))
+ if ((depth == 1)
+ && fdt_nodename_eq_(fdt, offset, name, namelen))
+ return offset;
+
+ if (depth < 0)
+ return -FDT_ERR_NOTFOUND;
+ return offset; /* error */
+}
+
+int fdt_subnode_offset(const void *fdt, int parentoffset,
+ const char *name)
+{
+ return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
+}
+
+int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
+{
+ const char *end = path + namelen;
+ const char *p = path;
+ int offset = 0;
+
+ FDT_RO_PROBE(fdt);
+
+ /* see if we have an alias */
+ if (*path != '/') {
+ const char *q = memchr(path, '/', end - p);
+
+ if (!q)
+ q = end;
+
+ p = fdt_get_alias_namelen(fdt, p, q - p);
+ if (!p)
+ return -FDT_ERR_BADPATH;
+ offset = fdt_path_offset(fdt, p);
+
+ p = q;
+ }
+
+ while (p < end) {
+ const char *q;
+
+ while (*p == '/') {
+ p++;
+ if (p == end)
+ return offset;
+ }
+ q = memchr(p, '/', end - p);
+ if (! q)
+ q = end;
+
+ offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
+ if (offset < 0)
+ return offset;
+
+ p = q;
+ }
+
+ return offset;
+}
+
+int fdt_path_offset(const void *fdt, const char *path)
+{
+ return fdt_path_offset_namelen(fdt, path, strlen(path));
+}
+
+const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
+{
+ const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
+ const char *nameptr;
+ int err;
+
+ if (((err = fdt_ro_probe_(fdt)) < 0)
+ || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0))
+ goto fail;
+
+ nameptr = nh->name;
+
+ if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
+ /*
+ * For old FDT versions, match the naming conventions of V16:
+ * give only the leaf name (after all /). The actual tree
+ * contents are loosely checked.
+ */
+ const char *leaf;
+ leaf = strrchr(nameptr, '/');
+ if (leaf == NULL) {
+ err = -FDT_ERR_BADSTRUCTURE;
+ goto fail;
+ }
+ nameptr = leaf+1;
+ }
+
+ if (len)
+ *len = strlen(nameptr);
+
+ return nameptr;
+
+ fail:
+ if (len)
+ *len = err;
+ return NULL;
+}
+
+int fdt_first_property_offset(const void *fdt, int nodeoffset)
+{
+ int offset;
+
+ if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
+ return offset;
+
+ return nextprop_(fdt, offset);
+}
+
+int fdt_next_property_offset(const void *fdt, int offset)
+{
+ if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)
+ return offset;
+
+ return nextprop_(fdt, offset);
+}
+
+static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt,
+ int offset,
+ int *lenp)
+{
+ int err;
+ const struct fdt_property *prop;
+
+ if (!can_assume(VALID_INPUT) &&
+ (err = fdt_check_prop_offset_(fdt, offset)) < 0) {
+ if (lenp)
+ *lenp = err;
+ return NULL;
+ }
+
+ prop = fdt_offset_ptr_(fdt, offset);
+
+ if (lenp)
+ *lenp = fdt32_ld(&prop->len);
+
+ return prop;
+}
+
+const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
+ int offset,
+ int *lenp)
+{
+ /* Prior to version 16, properties may need realignment
+ * and this API does not work. fdt_getprop_*() will, however. */
+
+ if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
+ if (lenp)
+ *lenp = -FDT_ERR_BADVERSION;
+ return NULL;
+ }
+
+ return fdt_get_property_by_offset_(fdt, offset, lenp);
+}
+
+static const struct fdt_property *fdt_get_property_namelen_(const void *fdt,
+ int offset,
+ const char *name,
+ int namelen,
+ int *lenp,
+ int *poffset)
+{
+ for (offset = fdt_first_property_offset(fdt, offset);
+ (offset >= 0);
+ (offset = fdt_next_property_offset(fdt, offset))) {
+ const struct fdt_property *prop;
+
+ prop = fdt_get_property_by_offset_(fdt, offset, lenp);
+ if (!can_assume(LIBFDT_FLAWLESS) && !prop) {
+ offset = -FDT_ERR_INTERNAL;
+ break;
+ }
+ if (fdt_string_eq_(fdt, fdt32_ld(&prop->nameoff),
+ name, namelen)) {
+ if (poffset)
+ *poffset = offset;
+ return prop;
+ }
+ }
+
+ if (lenp)
+ *lenp = offset;
+ return NULL;
+}
+
+
+const struct fdt_property *fdt_get_property_namelen(const void *fdt,
+ int offset,
+ const char *name,
+ int namelen, int *lenp)
+{
+ /* Prior to version 16, properties may need realignment
+ * and this API does not work. fdt_getprop_*() will, however. */
+ if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
+ if (lenp)
+ *lenp = -FDT_ERR_BADVERSION;
+ return NULL;
+ }
+
+ return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp,
+ NULL);
+}
+
+
+const struct fdt_property *fdt_get_property(const void *fdt,
+ int nodeoffset,
+ const char *name, int *lenp)
+{
+ return fdt_get_property_namelen(fdt, nodeoffset, name,
+ strlen(name), lenp);
+}
+
+const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
+ const char *name, int namelen, int *lenp)
+{
+ int poffset;
+ const struct fdt_property *prop;
+
+ prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp,
+ &poffset);
+ if (!prop)
+ return NULL;
+
+ /* Handle realignment */
+ if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 &&
+ (poffset + sizeof(*prop)) % 8 && fdt32_ld(&prop->len) >= 8)
+ return prop->data + 4;
+ return prop->data;
+}
+
+const void *fdt_getprop_by_offset(const void *fdt, int offset,
+ const char **namep, int *lenp)
+{
+ const struct fdt_property *prop;
+
+ prop = fdt_get_property_by_offset_(fdt, offset, lenp);
+ if (!prop)
+ return NULL;
+ if (namep) {
+ const char *name;
+ int namelen;
+
+ if (!can_assume(VALID_INPUT)) {
+ name = fdt_get_string(fdt, fdt32_ld(&prop->nameoff),
+ &namelen);
+ if (!name) {
+ if (lenp)
+ *lenp = namelen;
+ return NULL;
+ }
+ *namep = name;
+ } else {
+ *namep = fdt_string(fdt, fdt32_ld(&prop->nameoff));
+ }
+ }
+
+ /* Handle realignment */
+ if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 &&
+ (offset + sizeof(*prop)) % 8 && fdt32_ld(&prop->len) >= 8)
+ return prop->data + 4;
+ return prop->data;
+}
+
+const void *fdt_getprop(const void *fdt, int nodeoffset,
+ const char *name, int *lenp)
+{
+ return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
+}
+
+uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
+{
+ const fdt32_t *php;
+ int len;
+
+ /* FIXME: This is a bit sub-optimal, since we potentially scan
+ * over all the properties twice. */
+ php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
+ if (!php || (len != sizeof(*php))) {
+ php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
+ if (!php || (len != sizeof(*php)))
+ return 0;
+ }
+
+ return fdt32_ld(php);
+}
+
+const char *fdt_get_alias_namelen(const void *fdt,
+ const char *name, int namelen)
+{
+ int aliasoffset;
+
+ aliasoffset = fdt_path_offset(fdt, "/aliases");
+ if (aliasoffset < 0)
+ return NULL;
+
+ return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
+}
+
+const char *fdt_get_alias(const void *fdt, const char *name)
+{
+ return fdt_get_alias_namelen(fdt, name, strlen(name));
+}
+
+int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
+{
+ int pdepth = 0, p = 0;
+ int offset, depth, namelen;
+ const char *name;
+
+ FDT_RO_PROBE(fdt);
+
+ if (buflen < 2)
+ return -FDT_ERR_NOSPACE;
+
+ for (offset = 0, depth = 0;
+ (offset >= 0) && (offset <= nodeoffset);
+ offset = fdt_next_node(fdt, offset, &depth)) {
+ while (pdepth > depth) {
+ do {
+ p--;
+ } while (buf[p-1] != '/');
+ pdepth--;
+ }
+
+ if (pdepth >= depth) {
+ name = fdt_get_name(fdt, offset, &namelen);
+ if (!name)
+ return namelen;
+ if ((p + namelen + 1) <= buflen) {
+ memcpy(buf + p, name, namelen);
+ p += namelen;
+ buf[p++] = '/';
+ pdepth++;
+ }
+ }
+
+ if (offset == nodeoffset) {
+ if (pdepth < (depth + 1))
+ return -FDT_ERR_NOSPACE;
+
+ if (p > 1) /* special case so that root path is "/", not "" */
+ p--;
+ buf[p] = '\0';
+ return 0;
+ }
+ }
+
+ if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
+ return -FDT_ERR_BADOFFSET;
+ else if (offset == -FDT_ERR_BADOFFSET)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ return offset; /* error from fdt_next_node() */
+}
+
+int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
+ int supernodedepth, int *nodedepth)
+{
+ int offset, depth;
+ int supernodeoffset = -FDT_ERR_INTERNAL;
+
+ FDT_RO_PROBE(fdt);
+
+ if (supernodedepth < 0)
+ return -FDT_ERR_NOTFOUND;
+
+ for (offset = 0, depth = 0;
+ (offset >= 0) && (offset <= nodeoffset);
+ offset = fdt_next_node(fdt, offset, &depth)) {
+ if (depth == supernodedepth)
+ supernodeoffset = offset;
+
+ if (offset == nodeoffset) {
+ if (nodedepth)
+ *nodedepth = depth;
+
+ if (supernodedepth > depth)
+ return -FDT_ERR_NOTFOUND;
+ else
+ return supernodeoffset;
+ }
+ }
+
+ if (!can_assume(VALID_INPUT)) {
+ if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
+ return -FDT_ERR_BADOFFSET;
+ else if (offset == -FDT_ERR_BADOFFSET)
+ return -FDT_ERR_BADSTRUCTURE;
+ }
+
+ return offset; /* error from fdt_next_node() */
+}
+
+int fdt_node_depth(const void *fdt, int nodeoffset)
+{
+ int nodedepth;
+ int err;
+
+ err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
+ if (err)
+ return (can_assume(LIBFDT_FLAWLESS) || err < 0) ? err :
+ -FDT_ERR_INTERNAL;
+ return nodedepth;
+}
+
+int fdt_parent_offset(const void *fdt, int nodeoffset)
+{
+ int nodedepth = fdt_node_depth(fdt, nodeoffset);
+
+ if (nodedepth < 0)
+ return nodedepth;
+ return fdt_supernode_atdepth_offset(fdt, nodeoffset,
+ nodedepth - 1, NULL);
+}
+
+int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
+ const char *propname,
+ const void *propval, int proplen)
+{
+ int offset;
+ const void *val;
+ int len;
+
+ FDT_RO_PROBE(fdt);
+
+ /* FIXME: The algorithm here is pretty horrible: we scan each
+ * property of a node in fdt_getprop(), then if that didn't
+ * find what we want, we scan over them again making our way
+ * to the next node. Still it's the easiest to implement
+ * approach; performance can come later. */
+ for (offset = fdt_next_node(fdt, startoffset, NULL);
+ offset >= 0;
+ offset = fdt_next_node(fdt, offset, NULL)) {
+ val = fdt_getprop(fdt, offset, propname, &len);
+ if (val && (len == proplen)
+ && (memcmp(val, propval, len) == 0))
+ return offset;
+ }
+
+ return offset; /* error from fdt_next_node() */
+}
+
+int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
+{
+ int offset;
+
+ if ((phandle == 0) || (phandle == ~0U))
+ return -FDT_ERR_BADPHANDLE;
+
+ FDT_RO_PROBE(fdt);
+
+ /* FIXME: The algorithm here is pretty horrible: we
+ * potentially scan each property of a node in
+ * fdt_get_phandle(), then if that didn't find what
+ * we want, we scan over them again making our way to the next
+ * node. Still it's the easiest to implement approach;
+ * performance can come later. */
+ for (offset = fdt_next_node(fdt, -1, NULL);
+ offset >= 0;
+ offset = fdt_next_node(fdt, offset, NULL)) {
+ if (fdt_get_phandle(fdt, offset) == phandle)
+ return offset;
+ }
+
+ return offset; /* error from fdt_next_node() */
+}
+
+int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
+{
+ int len = strlen(str);
+ const char *p;
+
+ while (listlen >= len) {
+ if (memcmp(str, strlist, len+1) == 0)
+ return 1;
+ p = memchr(strlist, '\0', listlen);
+ if (!p)
+ return 0; /* malformed strlist.. */
+ listlen -= (p-strlist) + 1;
+ strlist = p + 1;
+ }
+ return 0;
+}
+
+int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
+{
+ const char *list, *end;
+ int length, count = 0;
+
+ list = fdt_getprop(fdt, nodeoffset, property, &length);
+ if (!list)
+ return length;
+
+ end = list + length;
+
+ while (list < end) {
+ length = strnlen(list, end - list) + 1;
+
+ /* Abort if the last string isn't properly NUL-terminated. */
+ if (list + length > end)
+ return -FDT_ERR_BADVALUE;
+
+ list += length;
+ count++;
+ }
+
+ return count;
+}
+
+int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
+ const char *string)
+{
+ int length, len, idx = 0;
+ const char *list, *end;
+
+ list = fdt_getprop(fdt, nodeoffset, property, &length);
+ if (!list)
+ return length;
+
+ len = strlen(string) + 1;
+ end = list + length;
+
+ while (list < end) {
+ length = strnlen(list, end - list) + 1;
+
+ /* Abort if the last string isn't properly NUL-terminated. */
+ if (list + length > end)
+ return -FDT_ERR_BADVALUE;
+
+ if (length == len && memcmp(list, string, length) == 0)
+ return idx;
+
+ list += length;
+ idx++;
+ }
+
+ return -FDT_ERR_NOTFOUND;
+}
+
+const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
+ const char *property, int idx,
+ int *lenp)
+{
+ const char *list, *end;
+ int length;
+
+ list = fdt_getprop(fdt, nodeoffset, property, &length);
+ if (!list) {
+ if (lenp)
+ *lenp = length;
+
+ return NULL;
+ }
+
+ end = list + length;
+
+ while (list < end) {
+ length = strnlen(list, end - list) + 1;
+
+ /* Abort if the last string isn't properly NUL-terminated. */
+ if (list + length > end) {
+ if (lenp)
+ *lenp = -FDT_ERR_BADVALUE;
+
+ return NULL;
+ }
+
+ if (idx == 0) {
+ if (lenp)
+ *lenp = length - 1;
+
+ return list;
+ }
+
+ list += length;
+ idx--;
+ }
+
+ if (lenp)
+ *lenp = -FDT_ERR_NOTFOUND;
+
+ return NULL;
+}
+
+int fdt_node_check_compatible(const void *fdt, int nodeoffset,
+ const char *compatible)
+{
+ const void *prop;
+ int len;
+
+ prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
+ if (!prop)
+ return len;
+
+ return !fdt_stringlist_contains(prop, len, compatible);
+}
+
+int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
+ const char *compatible)
+{
+ int offset, err;
+
+ FDT_RO_PROBE(fdt);
+
+ /* FIXME: The algorithm here is pretty horrible: we scan each
+ * property of a node in fdt_node_check_compatible(), then if
+ * that didn't find what we want, we scan over them again
+ * making our way to the next node. Still it's the easiest to
+ * implement approach; performance can come later. */
+ for (offset = fdt_next_node(fdt, startoffset, NULL);
+ offset >= 0;
+ offset = fdt_next_node(fdt, offset, NULL)) {
+ err = fdt_node_check_compatible(fdt, offset, compatible);
+ if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
+ return err;
+ else if (err == 0)
+ return offset;
+ }
+
+ return offset; /* error from fdt_next_node() */
+}
diff --git a/tools/src/libfdt/fdt_rw.c b/tools/src/libfdt/fdt_rw.c
new file mode 100644
index 0000000..dd5c93e
--- /dev/null
+++ b/tools/src/libfdt/fdt_rw.c
@@ -0,0 +1,492 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include "libfdt_env.h"
+
+#include "fdt.h"
+#include "libfdt.h"
+
+#include "libfdt_internal.h"
+
+static int fdt_blocks_misordered_(const void *fdt,
+ int mem_rsv_size, int struct_size)
+{
+ return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8))
+ || (fdt_off_dt_struct(fdt) <
+ (fdt_off_mem_rsvmap(fdt) + mem_rsv_size))
+ || (fdt_off_dt_strings(fdt) <
+ (fdt_off_dt_struct(fdt) + struct_size))
+ || (fdt_totalsize(fdt) <
+ (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt)));
+}
+
+static int fdt_rw_probe_(void *fdt)
+{
+ if (can_assume(VALID_DTB))
+ return 0;
+ FDT_RO_PROBE(fdt);
+
+ if (!can_assume(LATEST) && fdt_version(fdt) < 17)
+ return -FDT_ERR_BADVERSION;
+ if (fdt_blocks_misordered_(fdt, sizeof(struct fdt_reserve_entry),
+ fdt_size_dt_struct(fdt)))
+ return -FDT_ERR_BADLAYOUT;
+ if (!can_assume(LATEST) && fdt_version(fdt) > 17)
+ fdt_set_version(fdt, 17);
+
+ return 0;
+}
+
+#define FDT_RW_PROBE(fdt) \
+ { \
+ int err_; \
+ if ((err_ = fdt_rw_probe_(fdt)) != 0) \
+ return err_; \
+ }
+
+static inline unsigned int fdt_data_size_(void *fdt)
+{
+ return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
+}
+
+static int fdt_splice_(void *fdt, void *splicepoint, int oldlen, int newlen)
+{
+ char *p = splicepoint;
+ unsigned int dsize = fdt_data_size_(fdt);
+ size_t soff = p - (char *)fdt;
+
+ if ((oldlen < 0) || (soff + oldlen < soff) || (soff + oldlen > dsize))
+ return -FDT_ERR_BADOFFSET;
+ if ((p < (char *)fdt) || (dsize + newlen < (unsigned)oldlen))
+ return -FDT_ERR_BADOFFSET;
+ if (dsize - oldlen + newlen > fdt_totalsize(fdt))
+ return -FDT_ERR_NOSPACE;
+ memmove(p + newlen, p + oldlen, ((char *)fdt + dsize) - (p + oldlen));
+ return 0;
+}
+
+static int fdt_splice_mem_rsv_(void *fdt, struct fdt_reserve_entry *p,
+ int oldn, int newn)
+{
+ int delta = (newn - oldn) * sizeof(*p);
+ int err;
+ err = fdt_splice_(fdt, p, oldn * sizeof(*p), newn * sizeof(*p));
+ if (err)
+ return err;
+ fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta);
+ fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
+ return 0;
+}
+
+static int fdt_splice_struct_(void *fdt, void *p,
+ int oldlen, int newlen)
+{
+ int delta = newlen - oldlen;
+ int err;
+
+ if ((err = fdt_splice_(fdt, p, oldlen, newlen)))
+ return err;
+
+ fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta);
+ fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
+ return 0;
+}
+
+/* Must only be used to roll back in case of error */
+static void fdt_del_last_string_(void *fdt, const char *s)
+{
+ int newlen = strlen(s) + 1;
+
+ fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) - newlen);
+}
+
+static int fdt_splice_string_(void *fdt, int newlen)
+{
+ void *p = (char *)fdt
+ + fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
+ int err;
+
+ if ((err = fdt_splice_(fdt, p, 0, newlen)))
+ return err;
+
+ fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen);
+ return 0;
+}
+
+/**
+ * fdt_find_add_string_() - Find or allocate a string
+ *
+ * @fdt: pointer to the device tree to check/adjust
+ * @s: string to find/add
+ * @allocated: Set to 0 if the string was found, 1 if not found and so
+ * allocated. Ignored if can_assume(NO_ROLLBACK)
+ * @return offset of string in the string table (whether found or added)
+ */
+static int fdt_find_add_string_(void *fdt, const char *s, int *allocated)
+{
+ char *strtab = (char *)fdt + fdt_off_dt_strings(fdt);
+ const char *p;
+ char *new;
+ int len = strlen(s) + 1;
+ int err;
+
+ if (!can_assume(NO_ROLLBACK))
+ *allocated = 0;
+
+ p = fdt_find_string_(strtab, fdt_size_dt_strings(fdt), s);
+ if (p)
+ /* found it */
+ return (p - strtab);
+
+ new = strtab + fdt_size_dt_strings(fdt);
+ err = fdt_splice_string_(fdt, len);
+ if (err)
+ return err;
+
+ if (!can_assume(NO_ROLLBACK))
+ *allocated = 1;
+
+ memcpy(new, s, len);
+ return (new - strtab);
+}
+
+int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size)
+{
+ struct fdt_reserve_entry *re;
+ int err;
+
+ FDT_RW_PROBE(fdt);
+
+ re = fdt_mem_rsv_w_(fdt, fdt_num_mem_rsv(fdt));
+ err = fdt_splice_mem_rsv_(fdt, re, 0, 1);
+ if (err)
+ return err;
+
+ re->address = cpu_to_fdt64(address);
+ re->size = cpu_to_fdt64(size);
+ return 0;
+}
+
+int fdt_del_mem_rsv(void *fdt, int n)
+{
+ struct fdt_reserve_entry *re = fdt_mem_rsv_w_(fdt, n);
+
+ FDT_RW_PROBE(fdt);
+
+ if (n >= fdt_num_mem_rsv(fdt))
+ return -FDT_ERR_NOTFOUND;
+
+ return fdt_splice_mem_rsv_(fdt, re, 1, 0);
+}
+
+static int fdt_resize_property_(void *fdt, int nodeoffset, const char *name,
+ int len, struct fdt_property **prop)
+{
+ int oldlen;
+ int err;
+
+ *prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
+ if (!*prop)
+ return oldlen;
+
+ if ((err = fdt_splice_struct_(fdt, (*prop)->data, FDT_TAGALIGN(oldlen),
+ FDT_TAGALIGN(len))))
+ return err;
+
+ (*prop)->len = cpu_to_fdt32(len);
+ return 0;
+}
+
+static int fdt_add_property_(void *fdt, int nodeoffset, const char *name,
+ int len, struct fdt_property **prop)
+{
+ int proplen;
+ int nextoffset;
+ int namestroff;
+ int err;
+ int allocated;
+
+ if ((nextoffset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
+ return nextoffset;
+
+ namestroff = fdt_find_add_string_(fdt, name, &allocated);
+ if (namestroff < 0)
+ return namestroff;
+
+ *prop = fdt_offset_ptr_w_(fdt, nextoffset);
+ proplen = sizeof(**prop) + FDT_TAGALIGN(len);
+
+ err = fdt_splice_struct_(fdt, *prop, 0, proplen);
+ if (err) {
+ /* Delete the string if we failed to add it */
+ if (!can_assume(NO_ROLLBACK) && allocated)
+ fdt_del_last_string_(fdt, name);
+ return err;
+ }
+
+ (*prop)->tag = cpu_to_fdt32(FDT_PROP);
+ (*prop)->nameoff = cpu_to_fdt32(namestroff);
+ (*prop)->len = cpu_to_fdt32(len);
+ return 0;
+}
+
+int fdt_set_name(void *fdt, int nodeoffset, const char *name)
+{
+ char *namep;
+ int oldlen, newlen;
+ int err;
+
+ FDT_RW_PROBE(fdt);
+
+ namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen);
+ if (!namep)
+ return oldlen;
+
+ newlen = strlen(name);
+
+ err = fdt_splice_struct_(fdt, namep, FDT_TAGALIGN(oldlen+1),
+ FDT_TAGALIGN(newlen+1));
+ if (err)
+ return err;
+
+ memcpy(namep, name, newlen+1);
+ return 0;
+}
+
+int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name,
+ int len, void **prop_data)
+{
+ struct fdt_property *prop;
+ int err;
+
+ FDT_RW_PROBE(fdt);
+
+ err = fdt_resize_property_(fdt, nodeoffset, name, len, &prop);
+ if (err == -FDT_ERR_NOTFOUND)
+ err = fdt_add_property_(fdt, nodeoffset, name, len, &prop);
+ if (err)
+ return err;
+
+ *prop_data = prop->data;
+ return 0;
+}
+
+int fdt_setprop(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len)
+{
+ void *prop_data;
+ int err;
+
+ err = fdt_setprop_placeholder(fdt, nodeoffset, name, len, &prop_data);
+ if (err)
+ return err;
+
+ if (len)
+ memcpy(prop_data, val, len);
+ return 0;
+}
+
+int fdt_appendprop(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len)
+{
+ struct fdt_property *prop;
+ int err, oldlen, newlen;
+
+ FDT_RW_PROBE(fdt);
+
+ prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
+ if (prop) {
+ newlen = len + oldlen;
+ err = fdt_splice_struct_(fdt, prop->data,
+ FDT_TAGALIGN(oldlen),
+ FDT_TAGALIGN(newlen));
+ if (err)
+ return err;
+ prop->len = cpu_to_fdt32(newlen);
+ memcpy(prop->data + oldlen, val, len);
+ } else {
+ err = fdt_add_property_(fdt, nodeoffset, name, len, &prop);
+ if (err)
+ return err;
+ memcpy(prop->data, val, len);
+ }
+ return 0;
+}
+
+int fdt_delprop(void *fdt, int nodeoffset, const char *name)
+{
+ struct fdt_property *prop;
+ int len, proplen;
+
+ FDT_RW_PROBE(fdt);
+
+ prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
+ if (!prop)
+ return len;
+
+ proplen = sizeof(*prop) + FDT_TAGALIGN(len);
+ return fdt_splice_struct_(fdt, prop, proplen, 0);
+}
+
+int fdt_add_subnode_namelen(void *fdt, int parentoffset,
+ const char *name, int namelen)
+{
+ struct fdt_node_header *nh;
+ int offset, nextoffset;
+ int nodelen;
+ int err;
+ uint32_t tag;
+ fdt32_t *endtag;
+
+ FDT_RW_PROBE(fdt);
+
+ offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen);
+ if (offset >= 0)
+ return -FDT_ERR_EXISTS;
+ else if (offset != -FDT_ERR_NOTFOUND)
+ return offset;
+
+ /* Try to place the new node after the parent's properties */
+ fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */
+ do {
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+ } while ((tag == FDT_PROP) || (tag == FDT_NOP));
+
+ nh = fdt_offset_ptr_w_(fdt, offset);
+ nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE;
+
+ err = fdt_splice_struct_(fdt, nh, 0, nodelen);
+ if (err)
+ return err;
+
+ nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
+ memset(nh->name, 0, FDT_TAGALIGN(namelen+1));
+ memcpy(nh->name, name, namelen);
+ endtag = (fdt32_t *)((char *)nh + nodelen - FDT_TAGSIZE);
+ *endtag = cpu_to_fdt32(FDT_END_NODE);
+
+ return offset;
+}
+
+int fdt_add_subnode(void *fdt, int parentoffset, const char *name)
+{
+ return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name));
+}
+
+int fdt_del_node(void *fdt, int nodeoffset)
+{
+ int endoffset;
+
+ FDT_RW_PROBE(fdt);
+
+ endoffset = fdt_node_end_offset_(fdt, nodeoffset);
+ if (endoffset < 0)
+ return endoffset;
+
+ return fdt_splice_struct_(fdt, fdt_offset_ptr_w_(fdt, nodeoffset),
+ endoffset - nodeoffset, 0);
+}
+
+static void fdt_packblocks_(const char *old, char *new,
+ int mem_rsv_size, int struct_size)
+{
+ int mem_rsv_off, struct_off, strings_off;
+
+ mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8);
+ struct_off = mem_rsv_off + mem_rsv_size;
+ strings_off = struct_off + struct_size;
+
+ memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size);
+ fdt_set_off_mem_rsvmap(new, mem_rsv_off);
+
+ memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size);
+ fdt_set_off_dt_struct(new, struct_off);
+ fdt_set_size_dt_struct(new, struct_size);
+
+ memmove(new + strings_off, old + fdt_off_dt_strings(old),
+ fdt_size_dt_strings(old));
+ fdt_set_off_dt_strings(new, strings_off);
+ fdt_set_size_dt_strings(new, fdt_size_dt_strings(old));
+}
+
+int fdt_open_into(const void *fdt, void *buf, int bufsize)
+{
+ int err;
+ int mem_rsv_size, struct_size;
+ int newsize;
+ const char *fdtstart = fdt;
+ const char *fdtend = fdtstart + fdt_totalsize(fdt);
+ char *tmp;
+
+ FDT_RO_PROBE(fdt);
+
+ mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
+ * sizeof(struct fdt_reserve_entry);
+
+ if (can_assume(LATEST) || fdt_version(fdt) >= 17) {
+ struct_size = fdt_size_dt_struct(fdt);
+ } else {
+ struct_size = 0;
+ while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END)
+ ;
+ if (struct_size < 0)
+ return struct_size;
+ }
+
+ if (can_assume(LIBFDT_ORDER) ||
+ !fdt_blocks_misordered_(fdt, mem_rsv_size, struct_size)) {
+ /* no further work necessary */
+ err = fdt_move(fdt, buf, bufsize);
+ if (err)
+ return err;
+ fdt_set_version(buf, 17);
+ fdt_set_size_dt_struct(buf, struct_size);
+ fdt_set_totalsize(buf, bufsize);
+ return 0;
+ }
+
+ /* Need to reorder */
+ newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size
+ + struct_size + fdt_size_dt_strings(fdt);
+
+ if (bufsize < newsize)
+ return -FDT_ERR_NOSPACE;
+
+ /* First attempt to build converted tree at beginning of buffer */
+ tmp = buf;
+ /* But if that overlaps with the old tree... */
+ if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) {
+ /* Try right after the old tree instead */
+ tmp = (char *)(uintptr_t)fdtend;
+ if ((tmp + newsize) > ((char *)buf + bufsize))
+ return -FDT_ERR_NOSPACE;
+ }
+
+ fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size);
+ memmove(buf, tmp, newsize);
+
+ fdt_set_magic(buf, FDT_MAGIC);
+ fdt_set_totalsize(buf, bufsize);
+ fdt_set_version(buf, 17);
+ fdt_set_last_comp_version(buf, 16);
+ fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt));
+
+ return 0;
+}
+
+int fdt_pack(void *fdt)
+{
+ int mem_rsv_size;
+
+ FDT_RW_PROBE(fdt);
+
+ mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
+ * sizeof(struct fdt_reserve_entry);
+ fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt));
+ fdt_set_totalsize(fdt, fdt_data_size_(fdt));
+
+ return 0;
+}
diff --git a/tools/src/libfdt/fdt_strerror.c b/tools/src/libfdt/fdt_strerror.c
new file mode 100644
index 0000000..218b323
--- /dev/null
+++ b/tools/src/libfdt/fdt_strerror.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+
+#include "fdt.h"
+#include "libfdt.h"
+
+#include "libfdt_internal.h"
+
+struct fdt_errtabent {
+ const char *str;
+};
+
+#define FDT_ERRTABENT(val) \
+ [(val)] = { .str = #val, }
+
+static struct fdt_errtabent fdt_errtable[] = {
+ FDT_ERRTABENT(FDT_ERR_NOTFOUND),
+ FDT_ERRTABENT(FDT_ERR_EXISTS),
+ FDT_ERRTABENT(FDT_ERR_NOSPACE),
+
+ FDT_ERRTABENT(FDT_ERR_BADOFFSET),
+ FDT_ERRTABENT(FDT_ERR_BADPATH),
+ FDT_ERRTABENT(FDT_ERR_BADPHANDLE),
+ FDT_ERRTABENT(FDT_ERR_BADSTATE),
+
+ FDT_ERRTABENT(FDT_ERR_TRUNCATED),
+ FDT_ERRTABENT(FDT_ERR_BADMAGIC),
+ FDT_ERRTABENT(FDT_ERR_BADVERSION),
+ FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE),
+ FDT_ERRTABENT(FDT_ERR_BADLAYOUT),
+ FDT_ERRTABENT(FDT_ERR_INTERNAL),
+ FDT_ERRTABENT(FDT_ERR_BADNCELLS),
+ FDT_ERRTABENT(FDT_ERR_BADVALUE),
+ FDT_ERRTABENT(FDT_ERR_BADOVERLAY),
+ FDT_ERRTABENT(FDT_ERR_NOPHANDLES),
+ FDT_ERRTABENT(FDT_ERR_BADFLAGS),
+};
+#define FDT_ERRTABSIZE ((int)(sizeof(fdt_errtable) / sizeof(fdt_errtable[0])))
+
+const char *fdt_strerror(int errval)
+{
+ if (errval > 0)
+ return "<valid offset/length>";
+ else if (errval == 0)
+ return "<no error>";
+ else if (-errval < FDT_ERRTABSIZE) {
+ const char *s = fdt_errtable[-errval].str;
+
+ if (s)
+ return s;
+ }
+
+ return "<unknown error>";
+}
diff --git a/tools/src/libfdt/fdt_sw.c b/tools/src/libfdt/fdt_sw.c
new file mode 100644
index 0000000..c0d9cd0
--- /dev/null
+++ b/tools/src/libfdt/fdt_sw.c
@@ -0,0 +1,384 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include "libfdt_env.h"
+
+#include "fdt.h"
+#include "libfdt.h"
+
+#include "libfdt_internal.h"
+
+static int fdt_sw_probe_(void *fdt)
+{
+ if (!can_assume(VALID_INPUT)) {
+ if (fdt_magic(fdt) == FDT_MAGIC)
+ return -FDT_ERR_BADSTATE;
+ else if (fdt_magic(fdt) != FDT_SW_MAGIC)
+ return -FDT_ERR_BADMAGIC;
+ }
+
+ return 0;
+}
+
+#define FDT_SW_PROBE(fdt) \
+ { \
+ int err; \
+ if ((err = fdt_sw_probe_(fdt)) != 0) \
+ return err; \
+ }
+
+/* 'memrsv' state: Initial state after fdt_create()
+ *
+ * Allowed functions:
+ * fdt_add_reservemap_entry()
+ * fdt_finish_reservemap() [moves to 'struct' state]
+ */
+static int fdt_sw_probe_memrsv_(void *fdt)
+{
+ int err = fdt_sw_probe_(fdt);
+ if (err)
+ return err;
+
+ if (!can_assume(VALID_INPUT) && fdt_off_dt_strings(fdt) != 0)
+ return -FDT_ERR_BADSTATE;
+ return 0;
+}
+
+#define FDT_SW_PROBE_MEMRSV(fdt) \
+ { \
+ int err; \
+ if ((err = fdt_sw_probe_memrsv_(fdt)) != 0) \
+ return err; \
+ }
+
+/* 'struct' state: Enter this state after fdt_finish_reservemap()
+ *
+ * Allowed functions:
+ * fdt_begin_node()
+ * fdt_end_node()
+ * fdt_property*()
+ * fdt_finish() [moves to 'complete' state]
+ */
+static int fdt_sw_probe_struct_(void *fdt)
+{
+ int err = fdt_sw_probe_(fdt);
+ if (err)
+ return err;
+
+ if (!can_assume(VALID_INPUT) &&
+ fdt_off_dt_strings(fdt) != fdt_totalsize(fdt))
+ return -FDT_ERR_BADSTATE;
+ return 0;
+}
+
+#define FDT_SW_PROBE_STRUCT(fdt) \
+ { \
+ int err; \
+ if ((err = fdt_sw_probe_struct_(fdt)) != 0) \
+ return err; \
+ }
+
+static inline uint32_t sw_flags(void *fdt)
+{
+ /* assert: (fdt_magic(fdt) == FDT_SW_MAGIC) */
+ return fdt_last_comp_version(fdt);
+}
+
+/* 'complete' state: Enter this state after fdt_finish()
+ *
+ * Allowed functions: none
+ */
+
+static void *fdt_grab_space_(void *fdt, size_t len)
+{
+ unsigned int offset = fdt_size_dt_struct(fdt);
+ unsigned int spaceleft;
+
+ spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt)
+ - fdt_size_dt_strings(fdt);
+
+ if ((offset + len < offset) || (offset + len > spaceleft))
+ return NULL;
+
+ fdt_set_size_dt_struct(fdt, offset + len);
+ return fdt_offset_ptr_w_(fdt, offset);
+}
+
+int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags)
+{
+ const int hdrsize = FDT_ALIGN(sizeof(struct fdt_header),
+ sizeof(struct fdt_reserve_entry));
+ void *fdt = buf;
+
+ if (bufsize < hdrsize)
+ return -FDT_ERR_NOSPACE;
+
+ if (flags & ~FDT_CREATE_FLAGS_ALL)
+ return -FDT_ERR_BADFLAGS;
+
+ memset(buf, 0, bufsize);
+
+ /*
+ * magic and last_comp_version keep intermediate state during the fdt
+ * creation process, which is replaced with the proper FDT format by
+ * fdt_finish().
+ *
+ * flags should be accessed with sw_flags().
+ */
+ fdt_set_magic(fdt, FDT_SW_MAGIC);
+ fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION);
+ fdt_set_last_comp_version(fdt, flags);
+
+ fdt_set_totalsize(fdt, bufsize);
+
+ fdt_set_off_mem_rsvmap(fdt, hdrsize);
+ fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt));
+ fdt_set_off_dt_strings(fdt, 0);
+
+ return 0;
+}
+
+int fdt_create(void *buf, int bufsize)
+{
+ return fdt_create_with_flags(buf, bufsize, 0);
+}
+
+int fdt_resize(void *fdt, void *buf, int bufsize)
+{
+ size_t headsize, tailsize;
+ char *oldtail, *newtail;
+
+ FDT_SW_PROBE(fdt);
+
+ if (bufsize < 0)
+ return -FDT_ERR_NOSPACE;
+
+ headsize = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
+ tailsize = fdt_size_dt_strings(fdt);
+
+ if (!can_assume(VALID_DTB) &&
+ headsize + tailsize > fdt_totalsize(fdt))
+ return -FDT_ERR_INTERNAL;
+
+ if ((headsize + tailsize) > (unsigned)bufsize)
+ return -FDT_ERR_NOSPACE;
+
+ oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize;
+ newtail = (char *)buf + bufsize - tailsize;
+
+ /* Two cases to avoid clobbering data if the old and new
+ * buffers partially overlap */
+ if (buf <= fdt) {
+ memmove(buf, fdt, headsize);
+ memmove(newtail, oldtail, tailsize);
+ } else {
+ memmove(newtail, oldtail, tailsize);
+ memmove(buf, fdt, headsize);
+ }
+
+ fdt_set_totalsize(buf, bufsize);
+ if (fdt_off_dt_strings(buf))
+ fdt_set_off_dt_strings(buf, bufsize);
+
+ return 0;
+}
+
+int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size)
+{
+ struct fdt_reserve_entry *re;
+ int offset;
+
+ FDT_SW_PROBE_MEMRSV(fdt);
+
+ offset = fdt_off_dt_struct(fdt);
+ if ((offset + sizeof(*re)) > fdt_totalsize(fdt))
+ return -FDT_ERR_NOSPACE;
+
+ re = (struct fdt_reserve_entry *)((char *)fdt + offset);
+ re->address = cpu_to_fdt64(addr);
+ re->size = cpu_to_fdt64(size);
+
+ fdt_set_off_dt_struct(fdt, offset + sizeof(*re));
+
+ return 0;
+}
+
+int fdt_finish_reservemap(void *fdt)
+{
+ int err = fdt_add_reservemap_entry(fdt, 0, 0);
+
+ if (err)
+ return err;
+
+ fdt_set_off_dt_strings(fdt, fdt_totalsize(fdt));
+ return 0;
+}
+
+int fdt_begin_node(void *fdt, const char *name)
+{
+ struct fdt_node_header *nh;
+ int namelen;
+
+ FDT_SW_PROBE_STRUCT(fdt);
+
+ namelen = strlen(name) + 1;
+ nh = fdt_grab_space_(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen));
+ if (! nh)
+ return -FDT_ERR_NOSPACE;
+
+ nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
+ memcpy(nh->name, name, namelen);
+ return 0;
+}
+
+int fdt_end_node(void *fdt)
+{
+ fdt32_t *en;
+
+ FDT_SW_PROBE_STRUCT(fdt);
+
+ en = fdt_grab_space_(fdt, FDT_TAGSIZE);
+ if (! en)
+ return -FDT_ERR_NOSPACE;
+
+ *en = cpu_to_fdt32(FDT_END_NODE);
+ return 0;
+}
+
+static int fdt_add_string_(void *fdt, const char *s)
+{
+ char *strtab = (char *)fdt + fdt_totalsize(fdt);
+ unsigned int strtabsize = fdt_size_dt_strings(fdt);
+ unsigned int len = strlen(s) + 1;
+ unsigned int struct_top, offset;
+
+ offset = strtabsize + len;
+ struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
+ if (fdt_totalsize(fdt) - offset < struct_top)
+ return 0; /* no more room :( */
+
+ memcpy(strtab - offset, s, len);
+ fdt_set_size_dt_strings(fdt, strtabsize + len);
+ return -offset;
+}
+
+/* Must only be used to roll back in case of error */
+static void fdt_del_last_string_(void *fdt, const char *s)
+{
+ int strtabsize = fdt_size_dt_strings(fdt);
+ int len = strlen(s) + 1;
+
+ fdt_set_size_dt_strings(fdt, strtabsize - len);
+}
+
+static int fdt_find_add_string_(void *fdt, const char *s, int *allocated)
+{
+ char *strtab = (char *)fdt + fdt_totalsize(fdt);
+ int strtabsize = fdt_size_dt_strings(fdt);
+ const char *p;
+
+ *allocated = 0;
+
+ p = fdt_find_string_(strtab - strtabsize, strtabsize, s);
+ if (p)
+ return p - strtab;
+
+ *allocated = 1;
+
+ return fdt_add_string_(fdt, s);
+}
+
+int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp)
+{
+ struct fdt_property *prop;
+ int nameoff;
+ int allocated;
+
+ FDT_SW_PROBE_STRUCT(fdt);
+
+ /* String de-duplication can be slow, _NO_NAME_DEDUP skips it */
+ if (sw_flags(fdt) & FDT_CREATE_FLAG_NO_NAME_DEDUP) {
+ allocated = 1;
+ nameoff = fdt_add_string_(fdt, name);
+ } else {
+ nameoff = fdt_find_add_string_(fdt, name, &allocated);
+ }
+ if (nameoff == 0)
+ return -FDT_ERR_NOSPACE;
+
+ prop = fdt_grab_space_(fdt, sizeof(*prop) + FDT_TAGALIGN(len));
+ if (! prop) {
+ if (allocated)
+ fdt_del_last_string_(fdt, name);
+ return -FDT_ERR_NOSPACE;
+ }
+
+ prop->tag = cpu_to_fdt32(FDT_PROP);
+ prop->nameoff = cpu_to_fdt32(nameoff);
+ prop->len = cpu_to_fdt32(len);
+ *valp = prop->data;
+ return 0;
+}
+
+int fdt_property(void *fdt, const char *name, const void *val, int len)
+{
+ void *ptr;
+ int ret;
+
+ ret = fdt_property_placeholder(fdt, name, len, &ptr);
+ if (ret)
+ return ret;
+ memcpy(ptr, val, len);
+ return 0;
+}
+
+int fdt_finish(void *fdt)
+{
+ char *p = (char *)fdt;
+ fdt32_t *end;
+ int oldstroffset, newstroffset;
+ uint32_t tag;
+ int offset, nextoffset;
+
+ FDT_SW_PROBE_STRUCT(fdt);
+
+ /* Add terminator */
+ end = fdt_grab_space_(fdt, sizeof(*end));
+ if (! end)
+ return -FDT_ERR_NOSPACE;
+ *end = cpu_to_fdt32(FDT_END);
+
+ /* Relocate the string table */
+ oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt);
+ newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
+ memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt));
+ fdt_set_off_dt_strings(fdt, newstroffset);
+
+ /* Walk the structure, correcting string offsets */
+ offset = 0;
+ while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) {
+ if (tag == FDT_PROP) {
+ struct fdt_property *prop =
+ fdt_offset_ptr_w_(fdt, offset);
+ int nameoff;
+
+ nameoff = fdt32_to_cpu(prop->nameoff);
+ nameoff += fdt_size_dt_strings(fdt);
+ prop->nameoff = cpu_to_fdt32(nameoff);
+ }
+ offset = nextoffset;
+ }
+ if (nextoffset < 0)
+ return nextoffset;
+
+ /* Finally, adjust the header */
+ fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt));
+
+ /* And fix up fields that were keeping intermediate state. */
+ fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION);
+ fdt_set_magic(fdt, FDT_MAGIC);
+
+ return 0;
+}
diff --git a/tools/src/libfdt/fdt_wip.c b/tools/src/libfdt/fdt_wip.c
new file mode 100644
index 0000000..44aed08
--- /dev/null
+++ b/tools/src/libfdt/fdt_wip.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include "libfdt_env.h"
+
+#include "fdt.h"
+#include "libfdt.h"
+
+#include "libfdt_internal.h"
+
+int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset,
+ const char *name, int namelen,
+ uint32_t idx, const void *val,
+ int len)
+{
+ void *propval;
+ int proplen;
+
+ propval = fdt_getprop_namelen_w(fdt, nodeoffset, name, namelen,
+ &proplen);
+ if (!propval)
+ return proplen;
+
+ if ((unsigned)proplen < (len + idx))
+ return -FDT_ERR_NOSPACE;
+
+ memcpy((char *)propval + idx, val, len);
+ return 0;
+}
+
+int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len)
+{
+ const void *propval;
+ int proplen;
+
+ propval = fdt_getprop(fdt, nodeoffset, name, &proplen);
+ if (!propval)
+ return proplen;
+
+ if (proplen != len)
+ return -FDT_ERR_NOSPACE;
+
+ return fdt_setprop_inplace_namelen_partial(fdt, nodeoffset, name,
+ strlen(name), 0,
+ val, len);
+}
+
+static void fdt_nop_region_(void *start, int len)
+{
+ fdt32_t *p;
+
+ for (p = start; (char *)p < ((char *)start + len); p++)
+ *p = cpu_to_fdt32(FDT_NOP);
+}
+
+int fdt_nop_property(void *fdt, int nodeoffset, const char *name)
+{
+ struct fdt_property *prop;
+ int len;
+
+ prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
+ if (!prop)
+ return len;
+
+ fdt_nop_region_(prop, len + sizeof(*prop));
+
+ return 0;
+}
+
+int fdt_node_end_offset_(void *fdt, int offset)
+{
+ int depth = 0;
+
+ while ((offset >= 0) && (depth >= 0))
+ offset = fdt_next_node(fdt, offset, &depth);
+
+ return offset;
+}
+
+int fdt_nop_node(void *fdt, int nodeoffset)
+{
+ int endoffset;
+
+ endoffset = fdt_node_end_offset_(fdt, nodeoffset);
+ if (endoffset < 0)
+ return endoffset;
+
+ fdt_nop_region_(fdt_offset_ptr_w(fdt, nodeoffset, 0),
+ endoffset - nodeoffset);
+ return 0;
+}
diff --git a/tools/src/libfdt/libfdt.h b/tools/src/libfdt/libfdt.h
new file mode 100644
index 0000000..fe49b5d
--- /dev/null
+++ b/tools/src/libfdt/libfdt.h
@@ -0,0 +1,2080 @@
+/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */
+#ifndef LIBFDT_H
+#define LIBFDT_H
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+
+#include "libfdt_env.h"
+#include "fdt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FDT_FIRST_SUPPORTED_VERSION 0x02
+#define FDT_LAST_SUPPORTED_VERSION 0x11
+
+/* Error codes: informative error codes */
+#define FDT_ERR_NOTFOUND 1
+ /* FDT_ERR_NOTFOUND: The requested node or property does not exist */
+#define FDT_ERR_EXISTS 2
+ /* FDT_ERR_EXISTS: Attempted to create a node or property which
+ * already exists */
+#define FDT_ERR_NOSPACE 3
+ /* FDT_ERR_NOSPACE: Operation needed to expand the device
+ * tree, but its buffer did not have sufficient space to
+ * contain the expanded tree. Use fdt_open_into() to move the
+ * device tree to a buffer with more space. */
+
+/* Error codes: codes for bad parameters */
+#define FDT_ERR_BADOFFSET 4
+ /* FDT_ERR_BADOFFSET: Function was passed a structure block
+ * offset which is out-of-bounds, or which points to an
+ * unsuitable part of the structure for the operation. */
+#define FDT_ERR_BADPATH 5
+ /* FDT_ERR_BADPATH: Function was passed a badly formatted path
+ * (e.g. missing a leading / for a function which requires an
+ * absolute path) */
+#define FDT_ERR_BADPHANDLE 6
+ /* FDT_ERR_BADPHANDLE: Function was passed an invalid phandle.
+ * This can be caused either by an invalid phandle property
+ * length, or the phandle value was either 0 or -1, which are
+ * not permitted. */
+#define FDT_ERR_BADSTATE 7
+ /* FDT_ERR_BADSTATE: Function was passed an incomplete device
+ * tree created by the sequential-write functions, which is
+ * not sufficiently complete for the requested operation. */
+
+/* Error codes: codes for bad device tree blobs */
+#define FDT_ERR_TRUNCATED 8
+ /* FDT_ERR_TRUNCATED: FDT or a sub-block is improperly
+ * terminated (overflows, goes outside allowed bounds, or
+ * isn't properly terminated). */
+#define FDT_ERR_BADMAGIC 9
+ /* FDT_ERR_BADMAGIC: Given "device tree" appears not to be a
+ * device tree at all - it is missing the flattened device
+ * tree magic number. */
+#define FDT_ERR_BADVERSION 10
+ /* FDT_ERR_BADVERSION: Given device tree has a version which
+ * can't be handled by the requested operation. For
+ * read-write functions, this may mean that fdt_open_into() is
+ * required to convert the tree to the expected version. */
+#define FDT_ERR_BADSTRUCTURE 11
+ /* FDT_ERR_BADSTRUCTURE: Given device tree has a corrupt
+ * structure block or other serious error (e.g. misnested
+ * nodes, or subnodes preceding properties). */
+#define FDT_ERR_BADLAYOUT 12
+ /* FDT_ERR_BADLAYOUT: For read-write functions, the given
+ * device tree has it's sub-blocks in an order that the
+ * function can't handle (memory reserve map, then structure,
+ * then strings). Use fdt_open_into() to reorganize the tree
+ * into a form suitable for the read-write operations. */
+
+/* "Can't happen" error indicating a bug in libfdt */
+#define FDT_ERR_INTERNAL 13
+ /* FDT_ERR_INTERNAL: libfdt has failed an internal assertion.
+ * Should never be returned, if it is, it indicates a bug in
+ * libfdt itself. */
+
+/* Errors in device tree content */
+#define FDT_ERR_BADNCELLS 14
+ /* FDT_ERR_BADNCELLS: Device tree has a #address-cells, #size-cells
+ * or similar property with a bad format or value */
+
+#define FDT_ERR_BADVALUE 15
+ /* FDT_ERR_BADVALUE: Device tree has a property with an unexpected
+ * value. For example: a property expected to contain a string list
+ * is not NUL-terminated within the length of its value. */
+
+#define FDT_ERR_BADOVERLAY 16
+ /* FDT_ERR_BADOVERLAY: The device tree overlay, while
+ * correctly structured, cannot be applied due to some
+ * unexpected or missing value, property or node. */
+
+#define FDT_ERR_NOPHANDLES 17
+ /* FDT_ERR_NOPHANDLES: The device tree doesn't have any
+ * phandle available anymore without causing an overflow */
+
+#define FDT_ERR_BADFLAGS 18
+ /* FDT_ERR_BADFLAGS: The function was passed a flags field that
+ * contains invalid flags or an invalid combination of flags. */
+
+#define FDT_ERR_MAX 18
+
+/* constants */
+#define FDT_MAX_PHANDLE 0xfffffffe
+ /* Valid values for phandles range from 1 to 2^32-2. */
+
+/**********************************************************************/
+/* Low-level functions (you probably don't need these) */
+/**********************************************************************/
+
+#ifndef SWIG /* This function is not useful in Python */
+const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int checklen);
+#endif
+static inline void *fdt_offset_ptr_w(void *fdt, int offset, int checklen)
+{
+ return (void *)(uintptr_t)fdt_offset_ptr(fdt, offset, checklen);
+}
+
+uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset);
+
+/*
+ * Alignment helpers:
+ * These helpers access words from a device tree blob. They're
+ * built to work even with unaligned pointers on platforms (ike
+ * ARM) that don't like unaligned loads and stores
+ */
+
+static inline uint32_t fdt32_ld(const fdt32_t *p)
+{
+ const uint8_t *bp = (const uint8_t *)p;
+
+ return ((uint32_t)bp[0] << 24)
+ | ((uint32_t)bp[1] << 16)
+ | ((uint32_t)bp[2] << 8)
+ | bp[3];
+}
+
+static inline void fdt32_st(void *property, uint32_t value)
+{
+ uint8_t *bp = (uint8_t *)property;
+
+ bp[0] = value >> 24;
+ bp[1] = (value >> 16) & 0xff;
+ bp[2] = (value >> 8) & 0xff;
+ bp[3] = value & 0xff;
+}
+
+static inline uint64_t fdt64_ld(const fdt64_t *p)
+{
+ const uint8_t *bp = (const uint8_t *)p;
+
+ return ((uint64_t)bp[0] << 56)
+ | ((uint64_t)bp[1] << 48)
+ | ((uint64_t)bp[2] << 40)
+ | ((uint64_t)bp[3] << 32)
+ | ((uint64_t)bp[4] << 24)
+ | ((uint64_t)bp[5] << 16)
+ | ((uint64_t)bp[6] << 8)
+ | bp[7];
+}
+
+static inline void fdt64_st(void *property, uint64_t value)
+{
+ uint8_t *bp = (uint8_t *)property;
+
+ bp[0] = value >> 56;
+ bp[1] = (value >> 48) & 0xff;
+ bp[2] = (value >> 40) & 0xff;
+ bp[3] = (value >> 32) & 0xff;
+ bp[4] = (value >> 24) & 0xff;
+ bp[5] = (value >> 16) & 0xff;
+ bp[6] = (value >> 8) & 0xff;
+ bp[7] = value & 0xff;
+}
+
+/**********************************************************************/
+/* Traversal functions */
+/**********************************************************************/
+
+int fdt_next_node(const void *fdt, int offset, int *depth);
+
+/**
+ * fdt_first_subnode() - get offset of first direct subnode
+ *
+ * @fdt: FDT blob
+ * @offset: Offset of node to check
+ * @return offset of first subnode, or -FDT_ERR_NOTFOUND if there is none
+ */
+int fdt_first_subnode(const void *fdt, int offset);
+
+/**
+ * fdt_next_subnode() - get offset of next direct subnode
+ *
+ * After first calling fdt_first_subnode(), call this function repeatedly to
+ * get direct subnodes of a parent node.
+ *
+ * @fdt: FDT blob
+ * @offset: Offset of previous subnode
+ * @return offset of next subnode, or -FDT_ERR_NOTFOUND if there are no more
+ * subnodes
+ */
+int fdt_next_subnode(const void *fdt, int offset);
+
+/**
+ * fdt_for_each_subnode - iterate over all subnodes of a parent
+ *
+ * @node: child node (int, lvalue)
+ * @fdt: FDT blob (const void *)
+ * @parent: parent node (int)
+ *
+ * This is actually a wrapper around a for loop and would be used like so:
+ *
+ * fdt_for_each_subnode(node, fdt, parent) {
+ * Use node
+ * ...
+ * }
+ *
+ * if ((node < 0) && (node != -FDT_ERR_NOTFOUND)) {
+ * Error handling
+ * }
+ *
+ * Note that this is implemented as a macro and @node is used as
+ * iterator in the loop. The parent variable be constant or even a
+ * literal.
+ *
+ */
+#define fdt_for_each_subnode(node, fdt, parent) \
+ for (node = fdt_first_subnode(fdt, parent); \
+ node >= 0; \
+ node = fdt_next_subnode(fdt, node))
+
+/**********************************************************************/
+/* General functions */
+/**********************************************************************/
+#define fdt_get_header(fdt, field) \
+ (fdt32_ld(&((const struct fdt_header *)(fdt))->field))
+#define fdt_magic(fdt) (fdt_get_header(fdt, magic))
+#define fdt_totalsize(fdt) (fdt_get_header(fdt, totalsize))
+#define fdt_off_dt_struct(fdt) (fdt_get_header(fdt, off_dt_struct))
+#define fdt_off_dt_strings(fdt) (fdt_get_header(fdt, off_dt_strings))
+#define fdt_off_mem_rsvmap(fdt) (fdt_get_header(fdt, off_mem_rsvmap))
+#define fdt_version(fdt) (fdt_get_header(fdt, version))
+#define fdt_last_comp_version(fdt) (fdt_get_header(fdt, last_comp_version))
+#define fdt_boot_cpuid_phys(fdt) (fdt_get_header(fdt, boot_cpuid_phys))
+#define fdt_size_dt_strings(fdt) (fdt_get_header(fdt, size_dt_strings))
+#define fdt_size_dt_struct(fdt) (fdt_get_header(fdt, size_dt_struct))
+
+#define fdt_set_hdr_(name) \
+ static inline void fdt_set_##name(void *fdt, uint32_t val) \
+ { \
+ struct fdt_header *fdth = (struct fdt_header *)fdt; \
+ fdth->name = cpu_to_fdt32(val); \
+ }
+fdt_set_hdr_(magic);
+fdt_set_hdr_(totalsize);
+fdt_set_hdr_(off_dt_struct);
+fdt_set_hdr_(off_dt_strings);
+fdt_set_hdr_(off_mem_rsvmap);
+fdt_set_hdr_(version);
+fdt_set_hdr_(last_comp_version);
+fdt_set_hdr_(boot_cpuid_phys);
+fdt_set_hdr_(size_dt_strings);
+fdt_set_hdr_(size_dt_struct);
+#undef fdt_set_hdr_
+
+/**
+ * fdt_header_size - return the size of the tree's header
+ * @fdt: pointer to a flattened device tree
+ */
+size_t fdt_header_size(const void *fdt);
+
+/**
+ * fdt_header_size_ - internal function which takes a version number
+ */
+size_t fdt_header_size_(uint32_t version);
+
+/**
+ * fdt_check_header - sanity check a device tree header
+
+ * @fdt: pointer to data which might be a flattened device tree
+ *
+ * fdt_check_header() checks that the given buffer contains what
+ * appears to be a flattened device tree, and that the header contains
+ * valid information (to the extent that can be determined from the
+ * header alone).
+ *
+ * returns:
+ * 0, if the buffer appears to contain a valid device tree
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_TRUNCATED, standard meanings, as above
+ */
+int fdt_check_header(const void *fdt);
+
+/**
+ * fdt_move - move a device tree around in memory
+ * @fdt: pointer to the device tree to move
+ * @buf: pointer to memory where the device is to be moved
+ * @bufsize: size of the memory space at buf
+ *
+ * fdt_move() relocates, if possible, the device tree blob located at
+ * fdt to the buffer at buf of size bufsize. The buffer may overlap
+ * with the existing device tree blob at fdt. Therefore,
+ * fdt_move(fdt, fdt, fdt_totalsize(fdt))
+ * should always succeed.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, bufsize is insufficient to contain the device tree
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE, standard meanings
+ */
+int fdt_move(const void *fdt, void *buf, int bufsize);
+
+/**********************************************************************/
+/* Read-only functions */
+/**********************************************************************/
+
+int fdt_check_full(const void *fdt, size_t bufsize);
+
+/**
+ * fdt_get_string - retrieve a string from the strings block of a device tree
+ * @fdt: pointer to the device tree blob
+ * @stroffset: offset of the string within the strings block (native endian)
+ * @lenp: optional pointer to return the string's length
+ *
+ * fdt_get_string() retrieves a pointer to a single string from the
+ * strings block of the device tree blob at fdt, and optionally also
+ * returns the string's length in *lenp.
+ *
+ * returns:
+ * a pointer to the string, on success
+ * NULL, if stroffset is out of bounds, or doesn't point to a valid string
+ */
+const char *fdt_get_string(const void *fdt, int stroffset, int *lenp);
+
+/**
+ * fdt_string - retrieve a string from the strings block of a device tree
+ * @fdt: pointer to the device tree blob
+ * @stroffset: offset of the string within the strings block (native endian)
+ *
+ * fdt_string() retrieves a pointer to a single string from the
+ * strings block of the device tree blob at fdt.
+ *
+ * returns:
+ * a pointer to the string, on success
+ * NULL, if stroffset is out of bounds, or doesn't point to a valid string
+ */
+const char *fdt_string(const void *fdt, int stroffset);
+
+/**
+ * fdt_find_max_phandle - find and return the highest phandle in a tree
+ * @fdt: pointer to the device tree blob
+ * @phandle: return location for the highest phandle value found in the tree
+ *
+ * fdt_find_max_phandle() finds the highest phandle value in the given device
+ * tree. The value returned in @phandle is only valid if the function returns
+ * success.
+ *
+ * returns:
+ * 0 on success or a negative error code on failure
+ */
+int fdt_find_max_phandle(const void *fdt, uint32_t *phandle);
+
+/**
+ * fdt_get_max_phandle - retrieves the highest phandle in a tree
+ * @fdt: pointer to the device tree blob
+ *
+ * fdt_get_max_phandle retrieves the highest phandle in the given
+ * device tree. This will ignore badly formatted phandles, or phandles
+ * with a value of 0 or -1.
+ *
+ * This function is deprecated in favour of fdt_find_max_phandle().
+ *
+ * returns:
+ * the highest phandle on success
+ * 0, if no phandle was found in the device tree
+ * -1, if an error occurred
+ */
+static inline uint32_t fdt_get_max_phandle(const void *fdt)
+{
+ uint32_t phandle;
+ int err;
+
+ err = fdt_find_max_phandle(fdt, &phandle);
+ if (err < 0)
+ return (uint32_t)-1;
+
+ return phandle;
+}
+
+/**
+ * fdt_generate_phandle - return a new, unused phandle for a device tree blob
+ * @fdt: pointer to the device tree blob
+ * @phandle: return location for the new phandle
+ *
+ * Walks the device tree blob and looks for the highest phandle value. On
+ * success, the new, unused phandle value (one higher than the previously
+ * highest phandle value in the device tree blob) will be returned in the
+ * @phandle parameter.
+ *
+ * Returns:
+ * 0 on success or a negative error-code on failure
+ */
+int fdt_generate_phandle(const void *fdt, uint32_t *phandle);
+
+/**
+ * fdt_num_mem_rsv - retrieve the number of memory reserve map entries
+ * @fdt: pointer to the device tree blob
+ *
+ * Returns the number of entries in the device tree blob's memory
+ * reservation map. This does not include the terminating 0,0 entry
+ * or any other (0,0) entries reserved for expansion.
+ *
+ * returns:
+ * the number of entries
+ */
+int fdt_num_mem_rsv(const void *fdt);
+
+/**
+ * fdt_get_mem_rsv - retrieve one memory reserve map entry
+ * @fdt: pointer to the device tree blob
+ * @address, @size: pointers to 64-bit variables
+ *
+ * On success, *address and *size will contain the address and size of
+ * the n-th reserve map entry from the device tree blob, in
+ * native-endian format.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE, standard meanings
+ */
+int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size);
+
+/**
+ * fdt_subnode_offset_namelen - find a subnode based on substring
+ * @fdt: pointer to the device tree blob
+ * @parentoffset: structure block offset of a node
+ * @name: name of the subnode to locate
+ * @namelen: number of characters of name to consider
+ *
+ * Identical to fdt_subnode_offset(), but only examine the first
+ * namelen characters of name for matching the subnode name. This is
+ * useful for finding subnodes based on a portion of a larger string,
+ * such as a full path.
+ */
+#ifndef SWIG /* Not available in Python */
+int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
+ const char *name, int namelen);
+#endif
+/**
+ * fdt_subnode_offset - find a subnode of a given node
+ * @fdt: pointer to the device tree blob
+ * @parentoffset: structure block offset of a node
+ * @name: name of the subnode to locate
+ *
+ * fdt_subnode_offset() finds a subnode of the node at structure block
+ * offset parentoffset with the given name. name may include a unit
+ * address, in which case fdt_subnode_offset() will find the subnode
+ * with that unit address, or the unit address may be omitted, in
+ * which case fdt_subnode_offset() will find an arbitrary subnode
+ * whose name excluding unit address matches the given name.
+ *
+ * returns:
+ * structure block offset of the requested subnode (>=0), on success
+ * -FDT_ERR_NOTFOUND, if the requested subnode does not exist
+ * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE
+ * tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name);
+
+/**
+ * fdt_path_offset_namelen - find a tree node by its full path
+ * @fdt: pointer to the device tree blob
+ * @path: full path of the node to locate
+ * @namelen: number of characters of path to consider
+ *
+ * Identical to fdt_path_offset(), but only consider the first namelen
+ * characters of path as the path name.
+ */
+#ifndef SWIG /* Not available in Python */
+int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen);
+#endif
+
+/**
+ * fdt_path_offset - find a tree node by its full path
+ * @fdt: pointer to the device tree blob
+ * @path: full path of the node to locate
+ *
+ * fdt_path_offset() finds a node of a given path in the device tree.
+ * Each path component may omit the unit address portion, but the
+ * results of this are undefined if any such path component is
+ * ambiguous (that is if there are multiple nodes at the relevant
+ * level matching the given component, differentiated only by unit
+ * address).
+ *
+ * returns:
+ * structure block offset of the node with the requested path (>=0), on
+ * success
+ * -FDT_ERR_BADPATH, given path does not begin with '/' or is invalid
+ * -FDT_ERR_NOTFOUND, if the requested node does not exist
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_path_offset(const void *fdt, const char *path);
+
+/**
+ * fdt_get_name - retrieve the name of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: structure block offset of the starting node
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_get_name() retrieves the name (including unit address) of the
+ * device tree node at structure block offset nodeoffset. If lenp is
+ * non-NULL, the length of this name is also returned, in the integer
+ * pointed to by lenp.
+ *
+ * returns:
+ * pointer to the node's name, on success
+ * If lenp is non-NULL, *lenp contains the length of that name
+ * (>=0)
+ * NULL, on error
+ * if lenp is non-NULL *lenp contains an error code (<0):
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE
+ * tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE, standard meanings
+ */
+const char *fdt_get_name(const void *fdt, int nodeoffset, int *lenp);
+
+/**
+ * fdt_first_property_offset - find the offset of a node's first property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: structure block offset of a node
+ *
+ * fdt_first_property_offset() finds the first property of the node at
+ * the given structure block offset.
+ *
+ * returns:
+ * structure block offset of the property (>=0), on success
+ * -FDT_ERR_NOTFOUND, if the requested node has no properties
+ * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_first_property_offset(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_next_property_offset - step through a node's properties
+ * @fdt: pointer to the device tree blob
+ * @offset: structure block offset of a property
+ *
+ * fdt_next_property_offset() finds the property immediately after the
+ * one at the given structure block offset. This will be a property
+ * of the same node as the given property.
+ *
+ * returns:
+ * structure block offset of the next property (>=0), on success
+ * -FDT_ERR_NOTFOUND, if the given property is the last in its node
+ * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_PROP tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_next_property_offset(const void *fdt, int offset);
+
+/**
+ * fdt_for_each_property_offset - iterate over all properties of a node
+ *
+ * @property_offset: property offset (int, lvalue)
+ * @fdt: FDT blob (const void *)
+ * @node: node offset (int)
+ *
+ * This is actually a wrapper around a for loop and would be used like so:
+ *
+ * fdt_for_each_property_offset(property, fdt, node) {
+ * Use property
+ * ...
+ * }
+ *
+ * if ((property < 0) && (property != -FDT_ERR_NOTFOUND)) {
+ * Error handling
+ * }
+ *
+ * Note that this is implemented as a macro and property is used as
+ * iterator in the loop. The node variable can be constant or even a
+ * literal.
+ */
+#define fdt_for_each_property_offset(property, fdt, node) \
+ for (property = fdt_first_property_offset(fdt, node); \
+ property >= 0; \
+ property = fdt_next_property_offset(fdt, property))
+
+/**
+ * fdt_get_property_by_offset - retrieve the property at a given offset
+ * @fdt: pointer to the device tree blob
+ * @offset: offset of the property to retrieve
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_get_property_by_offset() retrieves a pointer to the
+ * fdt_property structure within the device tree blob at the given
+ * offset. If lenp is non-NULL, the length of the property value is
+ * also returned, in the integer pointed to by lenp.
+ *
+ * Note that this code only works on device tree versions >= 16. fdt_getprop()
+ * works on all versions.
+ *
+ * returns:
+ * pointer to the structure representing the property
+ * if lenp is non-NULL, *lenp contains the length of the property
+ * value (>=0)
+ * NULL, on error
+ * if lenp is non-NULL, *lenp contains an error code (<0):
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
+ int offset,
+ int *lenp);
+
+/**
+ * fdt_get_property_namelen - find a property based on substring
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to find
+ * @name: name of the property to find
+ * @namelen: number of characters of name to consider
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * Identical to fdt_get_property(), but only examine the first namelen
+ * characters of name for matching the property name.
+ */
+#ifndef SWIG /* Not available in Python */
+const struct fdt_property *fdt_get_property_namelen(const void *fdt,
+ int nodeoffset,
+ const char *name,
+ int namelen, int *lenp);
+#endif
+
+/**
+ * fdt_get_property - find a given property in a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to find
+ * @name: name of the property to find
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_get_property() retrieves a pointer to the fdt_property
+ * structure within the device tree blob corresponding to the property
+ * named 'name' of the node at offset nodeoffset. If lenp is
+ * non-NULL, the length of the property value is also returned, in the
+ * integer pointed to by lenp.
+ *
+ * returns:
+ * pointer to the structure representing the property
+ * if lenp is non-NULL, *lenp contains the length of the property
+ * value (>=0)
+ * NULL, on error
+ * if lenp is non-NULL, *lenp contains an error code (<0):
+ * -FDT_ERR_NOTFOUND, node does not have named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE
+ * tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+const struct fdt_property *fdt_get_property(const void *fdt, int nodeoffset,
+ const char *name, int *lenp);
+static inline struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset,
+ const char *name,
+ int *lenp)
+{
+ return (struct fdt_property *)(uintptr_t)
+ fdt_get_property(fdt, nodeoffset, name, lenp);
+}
+
+/**
+ * fdt_getprop_by_offset - retrieve the value of a property at a given offset
+ * @fdt: pointer to the device tree blob
+ * @offset: offset of the property to read
+ * @namep: pointer to a string variable (will be overwritten) or NULL
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_getprop_by_offset() retrieves a pointer to the value of the
+ * property at structure block offset 'offset' (this will be a pointer
+ * to within the device blob itself, not a copy of the value). If
+ * lenp is non-NULL, the length of the property value is also
+ * returned, in the integer pointed to by lenp. If namep is non-NULL,
+ * the property's namne will also be returned in the char * pointed to
+ * by namep (this will be a pointer to within the device tree's string
+ * block, not a new copy of the name).
+ *
+ * returns:
+ * pointer to the property's value
+ * if lenp is non-NULL, *lenp contains the length of the property
+ * value (>=0)
+ * if namep is non-NULL *namep contiains a pointer to the property
+ * name.
+ * NULL, on error
+ * if lenp is non-NULL, *lenp contains an error code (<0):
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+#ifndef SWIG /* This function is not useful in Python */
+const void *fdt_getprop_by_offset(const void *fdt, int offset,
+ const char **namep, int *lenp);
+#endif
+
+/**
+ * fdt_getprop_namelen - get property value based on substring
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to find
+ * @name: name of the property to find
+ * @namelen: number of characters of name to consider
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * Identical to fdt_getprop(), but only examine the first namelen
+ * characters of name for matching the property name.
+ */
+#ifndef SWIG /* Not available in Python */
+const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
+ const char *name, int namelen, int *lenp);
+static inline void *fdt_getprop_namelen_w(void *fdt, int nodeoffset,
+ const char *name, int namelen,
+ int *lenp)
+{
+ return (void *)(uintptr_t)fdt_getprop_namelen(fdt, nodeoffset, name,
+ namelen, lenp);
+}
+#endif
+
+/**
+ * fdt_getprop - retrieve the value of a given property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to find
+ * @name: name of the property to find
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_getprop() retrieves a pointer to the value of the property
+ * named 'name' of the node at offset nodeoffset (this will be a
+ * pointer to within the device blob itself, not a copy of the value).
+ * If lenp is non-NULL, the length of the property value is also
+ * returned, in the integer pointed to by lenp.
+ *
+ * returns:
+ * pointer to the property's value
+ * if lenp is non-NULL, *lenp contains the length of the property
+ * value (>=0)
+ * NULL, on error
+ * if lenp is non-NULL, *lenp contains an error code (<0):
+ * -FDT_ERR_NOTFOUND, node does not have named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE
+ * tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+const void *fdt_getprop(const void *fdt, int nodeoffset,
+ const char *name, int *lenp);
+static inline void *fdt_getprop_w(void *fdt, int nodeoffset,
+ const char *name, int *lenp)
+{
+ return (void *)(uintptr_t)fdt_getprop(fdt, nodeoffset, name, lenp);
+}
+
+/**
+ * fdt_get_phandle - retrieve the phandle of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: structure block offset of the node
+ *
+ * fdt_get_phandle() retrieves the phandle of the device tree node at
+ * structure block offset nodeoffset.
+ *
+ * returns:
+ * the phandle of the node at nodeoffset, on success (!= 0, != -1)
+ * 0, if the node has no phandle, or another error occurs
+ */
+uint32_t fdt_get_phandle(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_get_alias_namelen - get alias based on substring
+ * @fdt: pointer to the device tree blob
+ * @name: name of the alias th look up
+ * @namelen: number of characters of name to consider
+ *
+ * Identical to fdt_get_alias(), but only examine the first namelen
+ * characters of name for matching the alias name.
+ */
+#ifndef SWIG /* Not available in Python */
+const char *fdt_get_alias_namelen(const void *fdt,
+ const char *name, int namelen);
+#endif
+
+/**
+ * fdt_get_alias - retrieve the path referenced by a given alias
+ * @fdt: pointer to the device tree blob
+ * @name: name of the alias th look up
+ *
+ * fdt_get_alias() retrieves the value of a given alias. That is, the
+ * value of the property named 'name' in the node /aliases.
+ *
+ * returns:
+ * a pointer to the expansion of the alias named 'name', if it exists
+ * NULL, if the given alias or the /aliases node does not exist
+ */
+const char *fdt_get_alias(const void *fdt, const char *name);
+
+/**
+ * fdt_get_path - determine the full path of a node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose path to find
+ * @buf: character buffer to contain the returned path (will be overwritten)
+ * @buflen: size of the character buffer at buf
+ *
+ * fdt_get_path() computes the full path of the node at offset
+ * nodeoffset, and records that path in the buffer at buf.
+ *
+ * NOTE: This function is expensive, as it must scan the device tree
+ * structure from the start to nodeoffset.
+ *
+ * returns:
+ * 0, on success
+ * buf contains the absolute path of the node at
+ * nodeoffset, as a NUL-terminated string.
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_NOSPACE, the path of the given node is longer than (bufsize-1)
+ * characters and will not fit in the given buffer.
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen);
+
+/**
+ * fdt_supernode_atdepth_offset - find a specific ancestor of a node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose parent to find
+ * @supernodedepth: depth of the ancestor to find
+ * @nodedepth: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_supernode_atdepth_offset() finds an ancestor of the given node
+ * at a specific depth from the root (where the root itself has depth
+ * 0, its immediate subnodes depth 1 and so forth). So
+ * fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, NULL);
+ * will always return 0, the offset of the root node. If the node at
+ * nodeoffset has depth D, then:
+ * fdt_supernode_atdepth_offset(fdt, nodeoffset, D, NULL);
+ * will return nodeoffset itself.
+ *
+ * NOTE: This function is expensive, as it must scan the device tree
+ * structure from the start to nodeoffset.
+ *
+ * returns:
+ * structure block offset of the node at node offset's ancestor
+ * of depth supernodedepth (>=0), on success
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_NOTFOUND, supernodedepth was greater than the depth of
+ * nodeoffset
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
+ int supernodedepth, int *nodedepth);
+
+/**
+ * fdt_node_depth - find the depth of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose parent to find
+ *
+ * fdt_node_depth() finds the depth of a given node. The root node
+ * has depth 0, its immediate subnodes depth 1 and so forth.
+ *
+ * NOTE: This function is expensive, as it must scan the device tree
+ * structure from the start to nodeoffset.
+ *
+ * returns:
+ * depth of the node at nodeoffset (>=0), on success
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_depth(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_parent_offset - find the parent of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose parent to find
+ *
+ * fdt_parent_offset() locates the parent node of a given node (that
+ * is, it finds the offset of the node which contains the node at
+ * nodeoffset as a subnode).
+ *
+ * NOTE: This function is expensive, as it must scan the device tree
+ * structure from the start to nodeoffset, *twice*.
+ *
+ * returns:
+ * structure block offset of the parent of the node at nodeoffset
+ * (>=0), on success
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_parent_offset(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_node_offset_by_prop_value - find nodes with a given property value
+ * @fdt: pointer to the device tree blob
+ * @startoffset: only find nodes after this offset
+ * @propname: property name to check
+ * @propval: property value to search for
+ * @proplen: length of the value in propval
+ *
+ * fdt_node_offset_by_prop_value() returns the offset of the first
+ * node after startoffset, which has a property named propname whose
+ * value is of length proplen and has value equal to propval; or if
+ * startoffset is -1, the very first such node in the tree.
+ *
+ * To iterate through all nodes matching the criterion, the following
+ * idiom can be used:
+ * offset = fdt_node_offset_by_prop_value(fdt, -1, propname,
+ * propval, proplen);
+ * while (offset != -FDT_ERR_NOTFOUND) {
+ * // other code here
+ * offset = fdt_node_offset_by_prop_value(fdt, offset, propname,
+ * propval, proplen);
+ * }
+ *
+ * Note the -1 in the first call to the function, if 0 is used here
+ * instead, the function will never locate the root node, even if it
+ * matches the criterion.
+ *
+ * returns:
+ * structure block offset of the located node (>= 0, >startoffset),
+ * on success
+ * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the
+ * tree after startoffset
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
+ const char *propname,
+ const void *propval, int proplen);
+
+/**
+ * fdt_node_offset_by_phandle - find the node with a given phandle
+ * @fdt: pointer to the device tree blob
+ * @phandle: phandle value
+ *
+ * fdt_node_offset_by_phandle() returns the offset of the node
+ * which has the given phandle value. If there is more than one node
+ * in the tree with the given phandle (an invalid tree), results are
+ * undefined.
+ *
+ * returns:
+ * structure block offset of the located node (>= 0), on success
+ * -FDT_ERR_NOTFOUND, no node with that phandle exists
+ * -FDT_ERR_BADPHANDLE, given phandle value was invalid (0 or -1)
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle);
+
+/**
+ * fdt_node_check_compatible: check a node's compatible property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of a tree node
+ * @compatible: string to match against
+ *
+ *
+ * fdt_node_check_compatible() returns 0 if the given node contains a
+ * 'compatible' property with the given string as one of its elements,
+ * it returns non-zero otherwise, or on error.
+ *
+ * returns:
+ * 0, if the node has a 'compatible' property listing the given string
+ * 1, if the node has a 'compatible' property, but it does not list
+ * the given string
+ * -FDT_ERR_NOTFOUND, if the given node has no 'compatible' property
+ * -FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_check_compatible(const void *fdt, int nodeoffset,
+ const char *compatible);
+
+/**
+ * fdt_node_offset_by_compatible - find nodes with a given 'compatible' value
+ * @fdt: pointer to the device tree blob
+ * @startoffset: only find nodes after this offset
+ * @compatible: 'compatible' string to match against
+ *
+ * fdt_node_offset_by_compatible() returns the offset of the first
+ * node after startoffset, which has a 'compatible' property which
+ * lists the given compatible string; or if startoffset is -1, the
+ * very first such node in the tree.
+ *
+ * To iterate through all nodes matching the criterion, the following
+ * idiom can be used:
+ * offset = fdt_node_offset_by_compatible(fdt, -1, compatible);
+ * while (offset != -FDT_ERR_NOTFOUND) {
+ * // other code here
+ * offset = fdt_node_offset_by_compatible(fdt, offset, compatible);
+ * }
+ *
+ * Note the -1 in the first call to the function, if 0 is used here
+ * instead, the function will never locate the root node, even if it
+ * matches the criterion.
+ *
+ * returns:
+ * structure block offset of the located node (>= 0, >startoffset),
+ * on success
+ * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the
+ * tree after startoffset
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
+ const char *compatible);
+
+/**
+ * fdt_stringlist_contains - check a string list property for a string
+ * @strlist: Property containing a list of strings to check
+ * @listlen: Length of property
+ * @str: String to search for
+ *
+ * This is a utility function provided for convenience. The list contains
+ * one or more strings, each terminated by \0, as is found in a device tree
+ * "compatible" property.
+ *
+ * @return: 1 if the string is found in the list, 0 not found, or invalid list
+ */
+int fdt_stringlist_contains(const char *strlist, int listlen, const char *str);
+
+/**
+ * fdt_stringlist_count - count the number of strings in a string list
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of a tree node
+ * @property: name of the property containing the string list
+ * @return:
+ * the number of strings in the given property
+ * -FDT_ERR_BADVALUE if the property value is not NUL-terminated
+ * -FDT_ERR_NOTFOUND if the property does not exist
+ */
+int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property);
+
+/**
+ * fdt_stringlist_search - find a string in a string list and return its index
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of a tree node
+ * @property: name of the property containing the string list
+ * @string: string to look up in the string list
+ *
+ * Note that it is possible for this function to succeed on property values
+ * that are not NUL-terminated. That's because the function will stop after
+ * finding the first occurrence of @string. This can for example happen with
+ * small-valued cell properties, such as #address-cells, when searching for
+ * the empty string.
+ *
+ * @return:
+ * the index of the string in the list of strings
+ * -FDT_ERR_BADVALUE if the property value is not NUL-terminated
+ * -FDT_ERR_NOTFOUND if the property does not exist or does not contain
+ * the given string
+ */
+int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
+ const char *string);
+
+/**
+ * fdt_stringlist_get() - obtain the string at a given index in a string list
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of a tree node
+ * @property: name of the property containing the string list
+ * @index: index of the string to return
+ * @lenp: return location for the string length or an error code on failure
+ *
+ * Note that this will successfully extract strings from properties with
+ * non-NUL-terminated values. For example on small-valued cell properties
+ * this function will return the empty string.
+ *
+ * If non-NULL, the length of the string (on success) or a negative error-code
+ * (on failure) will be stored in the integer pointer to by lenp.
+ *
+ * @return:
+ * A pointer to the string at the given index in the string list or NULL on
+ * failure. On success the length of the string will be stored in the memory
+ * location pointed to by the lenp parameter, if non-NULL. On failure one of
+ * the following negative error codes will be returned in the lenp parameter
+ * (if non-NULL):
+ * -FDT_ERR_BADVALUE if the property value is not NUL-terminated
+ * -FDT_ERR_NOTFOUND if the property does not exist
+ */
+const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
+ const char *property, int index,
+ int *lenp);
+
+/**********************************************************************/
+/* Read-only functions (addressing related) */
+/**********************************************************************/
+
+/**
+ * FDT_MAX_NCELLS - maximum value for #address-cells and #size-cells
+ *
+ * This is the maximum value for #address-cells, #size-cells and
+ * similar properties that will be processed by libfdt. IEE1275
+ * requires that OF implementations handle values up to 4.
+ * Implementations may support larger values, but in practice higher
+ * values aren't used.
+ */
+#define FDT_MAX_NCELLS 4
+
+/**
+ * fdt_address_cells - retrieve address size for a bus represented in the tree
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node to find the address size for
+ *
+ * When the node has a valid #address-cells property, returns its value.
+ *
+ * returns:
+ * 0 <= n < FDT_MAX_NCELLS, on success
+ * 2, if the node has no #address-cells property
+ * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid
+ * #address-cells property
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_address_cells(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_size_cells - retrieve address range size for a bus represented in the
+ * tree
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node to find the address range size for
+ *
+ * When the node has a valid #size-cells property, returns its value.
+ *
+ * returns:
+ * 0 <= n < FDT_MAX_NCELLS, on success
+ * 1, if the node has no #size-cells property
+ * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid
+ * #size-cells property
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_size_cells(const void *fdt, int nodeoffset);
+
+
+/**********************************************************************/
+/* Write-in-place functions */
+/**********************************************************************/
+
+/**
+ * fdt_setprop_inplace_namelen_partial - change a property's value,
+ * but not its size
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @namelen: number of characters of name to consider
+ * @idx: index of the property to change in the array
+ * @val: pointer to data to replace the property value with
+ * @len: length of the property value
+ *
+ * Identical to fdt_setprop_inplace(), but modifies the given property
+ * starting from the given index, and using only the first characters
+ * of the name. It is useful when you want to manipulate only one value of
+ * an array and you have a string that doesn't end with \0.
+ */
+#ifndef SWIG /* Not available in Python */
+int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset,
+ const char *name, int namelen,
+ uint32_t idx, const void *val,
+ int len);
+#endif
+
+/**
+ * fdt_setprop_inplace - change a property's value, but not its size
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: pointer to data to replace the property value with
+ * @len: length of the property value
+ *
+ * fdt_setprop_inplace() replaces the value of a given property with
+ * the data in val, of length len. This function cannot change the
+ * size of a property, and so will only work if len is equal to the
+ * current length of the property.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the given property value, and will not alter or move any other part
+ * of the tree.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, if len is not equal to the property's current length
+ * -FDT_ERR_NOTFOUND, node does not have the named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+#ifndef SWIG /* Not available in Python */
+int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len);
+#endif
+
+/**
+ * fdt_setprop_inplace_u32 - change the value of a 32-bit integer property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: 32-bit integer value to replace the property with
+ *
+ * fdt_setprop_inplace_u32() replaces the value of a given property
+ * with the 32-bit integer value in val, converting val to big-endian
+ * if necessary. This function cannot change the size of a property,
+ * and so will only work if the property already exists and has length
+ * 4.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the given property value, and will not alter or move any other part
+ * of the tree.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, if the property's length is not equal to 4
+ * -FDT_ERR_NOTFOUND, node does not have the named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_setprop_inplace_u32(void *fdt, int nodeoffset,
+ const char *name, uint32_t val)
+{
+ fdt32_t tmp = cpu_to_fdt32(val);
+ return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp));
+}
+
+/**
+ * fdt_setprop_inplace_u64 - change the value of a 64-bit integer property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: 64-bit integer value to replace the property with
+ *
+ * fdt_setprop_inplace_u64() replaces the value of a given property
+ * with the 64-bit integer value in val, converting val to big-endian
+ * if necessary. This function cannot change the size of a property,
+ * and so will only work if the property already exists and has length
+ * 8.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the given property value, and will not alter or move any other part
+ * of the tree.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, if the property's length is not equal to 8
+ * -FDT_ERR_NOTFOUND, node does not have the named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_setprop_inplace_u64(void *fdt, int nodeoffset,
+ const char *name, uint64_t val)
+{
+ fdt64_t tmp = cpu_to_fdt64(val);
+ return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp));
+}
+
+/**
+ * fdt_setprop_inplace_cell - change the value of a single-cell property
+ *
+ * This is an alternative name for fdt_setprop_inplace_u32()
+ */
+static inline int fdt_setprop_inplace_cell(void *fdt, int nodeoffset,
+ const char *name, uint32_t val)
+{
+ return fdt_setprop_inplace_u32(fdt, nodeoffset, name, val);
+}
+
+/**
+ * fdt_nop_property - replace a property with nop tags
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to nop
+ * @name: name of the property to nop
+ *
+ * fdt_nop_property() will replace a given property's representation
+ * in the blob with FDT_NOP tags, effectively removing it from the
+ * tree.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the property, and will not alter or move any other part of the
+ * tree.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOTFOUND, node does not have the named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_nop_property(void *fdt, int nodeoffset, const char *name);
+
+/**
+ * fdt_nop_node - replace a node (subtree) with nop tags
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node to nop
+ *
+ * fdt_nop_node() will replace a given node's representation in the
+ * blob, including all its subnodes, if any, with FDT_NOP tags,
+ * effectively removing it from the tree.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the node and its properties and subnodes, and will not alter or
+ * move any other part of the tree.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_nop_node(void *fdt, int nodeoffset);
+
+/**********************************************************************/
+/* Sequential write functions */
+/**********************************************************************/
+
+/* fdt_create_with_flags flags */
+#define FDT_CREATE_FLAG_NO_NAME_DEDUP 0x1
+ /* FDT_CREATE_FLAG_NO_NAME_DEDUP: Do not try to de-duplicate property
+ * names in the fdt. This can result in faster creation times, but
+ * a larger fdt. */
+
+#define FDT_CREATE_FLAGS_ALL (FDT_CREATE_FLAG_NO_NAME_DEDUP)
+
+/**
+ * fdt_create_with_flags - begin creation of a new fdt
+ * @fdt: pointer to memory allocated where fdt will be created
+ * @bufsize: size of the memory space at fdt
+ * @flags: a valid combination of FDT_CREATE_FLAG_ flags, or 0.
+ *
+ * fdt_create_with_flags() begins the process of creating a new fdt with
+ * the sequential write interface.
+ *
+ * fdt creation process must end with fdt_finished() to produce a valid fdt.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, bufsize is insufficient for a minimal fdt
+ * -FDT_ERR_BADFLAGS, flags is not valid
+ */
+int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags);
+
+/**
+ * fdt_create - begin creation of a new fdt
+ * @fdt: pointer to memory allocated where fdt will be created
+ * @bufsize: size of the memory space at fdt
+ *
+ * fdt_create() is equivalent to fdt_create_with_flags() with flags=0.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, bufsize is insufficient for a minimal fdt
+ */
+int fdt_create(void *buf, int bufsize);
+
+int fdt_resize(void *fdt, void *buf, int bufsize);
+int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size);
+int fdt_finish_reservemap(void *fdt);
+int fdt_begin_node(void *fdt, const char *name);
+int fdt_property(void *fdt, const char *name, const void *val, int len);
+static inline int fdt_property_u32(void *fdt, const char *name, uint32_t val)
+{
+ fdt32_t tmp = cpu_to_fdt32(val);
+ return fdt_property(fdt, name, &tmp, sizeof(tmp));
+}
+static inline int fdt_property_u64(void *fdt, const char *name, uint64_t val)
+{
+ fdt64_t tmp = cpu_to_fdt64(val);
+ return fdt_property(fdt, name, &tmp, sizeof(tmp));
+}
+
+#ifndef SWIG /* Not available in Python */
+static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val)
+{
+ return fdt_property_u32(fdt, name, val);
+}
+#endif
+
+/**
+ * fdt_property_placeholder - add a new property and return a ptr to its value
+ *
+ * @fdt: pointer to the device tree blob
+ * @name: name of property to add
+ * @len: length of property value in bytes
+ * @valp: returns a pointer to where where the value should be placed
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_NOSPACE, standard meanings
+ */
+int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp);
+
+#define fdt_property_string(fdt, name, str) \
+ fdt_property(fdt, name, str, strlen(str)+1)
+int fdt_end_node(void *fdt);
+int fdt_finish(void *fdt);
+
+/**********************************************************************/
+/* Read-write functions */
+/**********************************************************************/
+
+int fdt_create_empty_tree(void *buf, int bufsize);
+int fdt_open_into(const void *fdt, void *buf, int bufsize);
+int fdt_pack(void *fdt);
+
+/**
+ * fdt_add_mem_rsv - add one memory reserve map entry
+ * @fdt: pointer to the device tree blob
+ * @address, @size: 64-bit values (native endian)
+ *
+ * Adds a reserve map entry to the given blob reserving a region at
+ * address address of length size.
+ *
+ * This function will insert data into the reserve map and will
+ * therefore change the indexes of some entries in the table.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new reservation entry
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size);
+
+/**
+ * fdt_del_mem_rsv - remove a memory reserve map entry
+ * @fdt: pointer to the device tree blob
+ * @n: entry to remove
+ *
+ * fdt_del_mem_rsv() removes the n-th memory reserve map entry from
+ * the blob.
+ *
+ * This function will delete data from the reservation table and will
+ * therefore change the indexes of some entries in the table.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOTFOUND, there is no entry of the given index (i.e. there
+ * are less than n+1 reserve map entries)
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_del_mem_rsv(void *fdt, int n);
+
+/**
+ * fdt_set_name - change the name of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: structure block offset of a node
+ * @name: name to give the node
+ *
+ * fdt_set_name() replaces the name (including unit address, if any)
+ * of the given node with the given string. NOTE: this function can't
+ * efficiently check if the new name is unique amongst the given
+ * node's siblings; results are undefined if this function is invoked
+ * with a name equal to one of the given node's siblings.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob
+ * to contain the new name
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE, standard meanings
+ */
+int fdt_set_name(void *fdt, int nodeoffset, const char *name);
+
+/**
+ * fdt_setprop - create or change a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: pointer to data to set the property value to
+ * @len: length of the property value
+ *
+ * fdt_setprop() sets the value of the named property in the given
+ * node to the given value and length, creating the property if it
+ * does not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_setprop(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len);
+
+/**
+ * fdt_setprop_placeholder - allocate space for a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @len: length of the property value
+ * @prop_data: return pointer to property data
+ *
+ * fdt_setprop_placeholer() allocates the named property in the given node.
+ * If the property exists it is resized. In either case a pointer to the
+ * property data is returned.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name,
+ int len, void **prop_data);
+
+/**
+ * fdt_setprop_u32 - set a property to a 32-bit integer
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: 32-bit integer value for the property (native endian)
+ *
+ * fdt_setprop_u32() sets the value of the named property in the given
+ * node to the given 32-bit integer value (converting to big-endian if
+ * necessary), or creates a new property with that value if it does
+ * not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_setprop_u32(void *fdt, int nodeoffset, const char *name,
+ uint32_t val)
+{
+ fdt32_t tmp = cpu_to_fdt32(val);
+ return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp));
+}
+
+/**
+ * fdt_setprop_u64 - set a property to a 64-bit integer
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: 64-bit integer value for the property (native endian)
+ *
+ * fdt_setprop_u64() sets the value of the named property in the given
+ * node to the given 64-bit integer value (converting to big-endian if
+ * necessary), or creates a new property with that value if it does
+ * not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_setprop_u64(void *fdt, int nodeoffset, const char *name,
+ uint64_t val)
+{
+ fdt64_t tmp = cpu_to_fdt64(val);
+ return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp));
+}
+
+/**
+ * fdt_setprop_cell - set a property to a single cell value
+ *
+ * This is an alternative name for fdt_setprop_u32()
+ */
+static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name,
+ uint32_t val)
+{
+ return fdt_setprop_u32(fdt, nodeoffset, name, val);
+}
+
+/**
+ * fdt_setprop_string - set a property to a string value
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @str: string value for the property
+ *
+ * fdt_setprop_string() sets the value of the named property in the
+ * given node to the given string value (using the length of the
+ * string to determine the new length of the property), or creates a
+ * new property with that value if it does not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+#define fdt_setprop_string(fdt, nodeoffset, name, str) \
+ fdt_setprop((fdt), (nodeoffset), (name), (str), strlen(str)+1)
+
+
+/**
+ * fdt_setprop_empty - set a property to an empty value
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ *
+ * fdt_setprop_empty() sets the value of the named property in the
+ * given node to an empty (zero length) value, or creates a new empty
+ * property if it does not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+#define fdt_setprop_empty(fdt, nodeoffset, name) \
+ fdt_setprop((fdt), (nodeoffset), (name), NULL, 0)
+
+/**
+ * fdt_appendprop - append to or create a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to append to
+ * @val: pointer to data to append to the property value
+ * @len: length of the data to append to the property value
+ *
+ * fdt_appendprop() appends the value to the named property in the
+ * given node, creating the property if it does not already exist.
+ *
+ * This function may insert data into the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_appendprop(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len);
+
+/**
+ * fdt_appendprop_u32 - append a 32-bit integer value to a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: 32-bit integer value to append to the property (native endian)
+ *
+ * fdt_appendprop_u32() appends the given 32-bit integer value
+ * (converting to big-endian if necessary) to the value of the named
+ * property in the given node, or creates a new property with that
+ * value if it does not already exist.
+ *
+ * This function may insert data into the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_appendprop_u32(void *fdt, int nodeoffset,
+ const char *name, uint32_t val)
+{
+ fdt32_t tmp = cpu_to_fdt32(val);
+ return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp));
+}
+
+/**
+ * fdt_appendprop_u64 - append a 64-bit integer value to a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: 64-bit integer value to append to the property (native endian)
+ *
+ * fdt_appendprop_u64() appends the given 64-bit integer value
+ * (converting to big-endian if necessary) to the value of the named
+ * property in the given node, or creates a new property with that
+ * value if it does not already exist.
+ *
+ * This function may insert data into the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_appendprop_u64(void *fdt, int nodeoffset,
+ const char *name, uint64_t val)
+{
+ fdt64_t tmp = cpu_to_fdt64(val);
+ return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp));
+}
+
+/**
+ * fdt_appendprop_cell - append a single cell value to a property
+ *
+ * This is an alternative name for fdt_appendprop_u32()
+ */
+static inline int fdt_appendprop_cell(void *fdt, int nodeoffset,
+ const char *name, uint32_t val)
+{
+ return fdt_appendprop_u32(fdt, nodeoffset, name, val);
+}
+
+/**
+ * fdt_appendprop_string - append a string to a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @str: string value to append to the property
+ *
+ * fdt_appendprop_string() appends the given string to the value of
+ * the named property in the given node, or creates a new property
+ * with that value if it does not already exist.
+ *
+ * This function may insert data into the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+#define fdt_appendprop_string(fdt, nodeoffset, name, str) \
+ fdt_appendprop((fdt), (nodeoffset), (name), (str), strlen(str)+1)
+
+/**
+ * fdt_appendprop_addrrange - append a address range property
+ * @fdt: pointer to the device tree blob
+ * @parent: offset of the parent node
+ * @nodeoffset: offset of the node to add a property at
+ * @name: name of property
+ * @addr: start address of a given range
+ * @size: size of a given range
+ *
+ * fdt_appendprop_addrrange() appends an address range value (start
+ * address and size) to the value of the named property in the given
+ * node, or creates a new property with that value if it does not
+ * already exist.
+ * If "name" is not specified, a default "reg" is used.
+ * Cell sizes are determined by parent's #address-cells and #size-cells.
+ *
+ * This function may insert data into the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid
+ * #address-cells property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADVALUE, addr or size doesn't fit to respective cells size
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain a new property
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset,
+ const char *name, uint64_t addr, uint64_t size);
+
+/**
+ * fdt_delprop - delete a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to nop
+ * @name: name of the property to nop
+ *
+ * fdt_del_property() will delete the given property.
+ *
+ * This function will delete data from the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOTFOUND, node does not have the named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_delprop(void *fdt, int nodeoffset, const char *name);
+
+/**
+ * fdt_add_subnode_namelen - creates a new node based on substring
+ * @fdt: pointer to the device tree blob
+ * @parentoffset: structure block offset of a node
+ * @name: name of the subnode to locate
+ * @namelen: number of characters of name to consider
+ *
+ * Identical to fdt_add_subnode(), but use only the first namelen
+ * characters of name as the name of the new node. This is useful for
+ * creating subnodes based on a portion of a larger string, such as a
+ * full path.
+ */
+#ifndef SWIG /* Not available in Python */
+int fdt_add_subnode_namelen(void *fdt, int parentoffset,
+ const char *name, int namelen);
+#endif
+
+/**
+ * fdt_add_subnode - creates a new node
+ * @fdt: pointer to the device tree blob
+ * @parentoffset: structure block offset of a node
+ * @name: name of the subnode to locate
+ *
+ * fdt_add_subnode() creates a new node as a subnode of the node at
+ * structure block offset parentoffset, with the given name (which
+ * should include the unit address, if any).
+ *
+ * This function will insert data into the blob, and will therefore
+ * change the offsets of some existing nodes.
+
+ * returns:
+ * structure block offset of the created nodeequested subnode (>=0), on
+ * success
+ * -FDT_ERR_NOTFOUND, if the requested subnode does not exist
+ * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE
+ * tag
+ * -FDT_ERR_EXISTS, if the node at parentoffset already has a subnode of
+ * the given name
+ * -FDT_ERR_NOSPACE, if there is insufficient free space in the
+ * blob to contain the new node
+ * -FDT_ERR_NOSPACE
+ * -FDT_ERR_BADLAYOUT
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_add_subnode(void *fdt, int parentoffset, const char *name);
+
+/**
+ * fdt_del_node - delete a node (subtree)
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node to nop
+ *
+ * fdt_del_node() will remove the given node, including all its
+ * subnodes if any, from the blob.
+ *
+ * This function will delete data from the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_del_node(void *fdt, int nodeoffset);
+
+/**
+ * fdt_overlay_apply - Applies a DT overlay on a base DT
+ * @fdt: pointer to the base device tree blob
+ * @fdto: pointer to the device tree overlay blob
+ *
+ * fdt_overlay_apply() will apply the given device tree overlay on the
+ * given base device tree.
+ *
+ * Expect the base device tree to be modified, even if the function
+ * returns an error.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there's not enough space in the base device tree
+ * -FDT_ERR_NOTFOUND, the overlay points to some inexistant nodes or
+ * properties in the base DT
+ * -FDT_ERR_BADPHANDLE,
+ * -FDT_ERR_BADOVERLAY,
+ * -FDT_ERR_NOPHANDLES,
+ * -FDT_ERR_INTERNAL,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADOFFSET,
+ * -FDT_ERR_BADPATH,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_overlay_apply(void *fdt, void *fdto);
+
+/**********************************************************************/
+/* Debugging / informational functions */
+/**********************************************************************/
+
+const char *fdt_strerror(int errval);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBFDT_H */
diff --git a/tools/src/libfdt/libfdt_env.h b/tools/src/libfdt/libfdt_env.h
new file mode 100644
index 0000000..6d028a4
--- /dev/null
+++ b/tools/src/libfdt/libfdt_env.h
@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */
+#ifndef LIBFDT_ENV_H
+#define LIBFDT_ENV_H
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ * Copyright 2012 Kim Phillips, Freescale Semiconductor.
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <limits.h>
+
+#ifdef __CHECKER__
+#define FDT_FORCE __attribute__((force))
+#define FDT_BITWISE __attribute__((bitwise))
+#else
+#define FDT_FORCE
+#define FDT_BITWISE
+#endif
+
+typedef uint16_t FDT_BITWISE fdt16_t;
+typedef uint32_t FDT_BITWISE fdt32_t;
+typedef uint64_t FDT_BITWISE fdt64_t;
+
+#define EXTRACT_BYTE(x, n) ((unsigned long long)((uint8_t *)&x)[n])
+#define CPU_TO_FDT16(x) ((EXTRACT_BYTE(x, 0) << 8) | EXTRACT_BYTE(x, 1))
+#define CPU_TO_FDT32(x) ((EXTRACT_BYTE(x, 0) << 24) | (EXTRACT_BYTE(x, 1) << 16) | \
+ (EXTRACT_BYTE(x, 2) << 8) | EXTRACT_BYTE(x, 3))
+#define CPU_TO_FDT64(x) ((EXTRACT_BYTE(x, 0) << 56) | (EXTRACT_BYTE(x, 1) << 48) | \
+ (EXTRACT_BYTE(x, 2) << 40) | (EXTRACT_BYTE(x, 3) << 32) | \
+ (EXTRACT_BYTE(x, 4) << 24) | (EXTRACT_BYTE(x, 5) << 16) | \
+ (EXTRACT_BYTE(x, 6) << 8) | EXTRACT_BYTE(x, 7))
+
+static inline uint16_t fdt16_to_cpu(fdt16_t x)
+{
+ return (FDT_FORCE uint16_t)CPU_TO_FDT16(x);
+}
+static inline fdt16_t cpu_to_fdt16(uint16_t x)
+{
+ return (FDT_FORCE fdt16_t)CPU_TO_FDT16(x);
+}
+
+static inline uint32_t fdt32_to_cpu(fdt32_t x)
+{
+ return (FDT_FORCE uint32_t)CPU_TO_FDT32(x);
+}
+static inline fdt32_t cpu_to_fdt32(uint32_t x)
+{
+ return (FDT_FORCE fdt32_t)CPU_TO_FDT32(x);
+}
+
+static inline uint64_t fdt64_to_cpu(fdt64_t x)
+{
+ return (FDT_FORCE uint64_t)CPU_TO_FDT64(x);
+}
+static inline fdt64_t cpu_to_fdt64(uint64_t x)
+{
+ return (FDT_FORCE fdt64_t)CPU_TO_FDT64(x);
+}
+#undef CPU_TO_FDT64
+#undef CPU_TO_FDT32
+#undef CPU_TO_FDT16
+#undef EXTRACT_BYTE
+
+#ifdef __APPLE__
+#include <AvailabilityMacros.h>
+
+/* strnlen() is not available on Mac OS < 10.7 */
+# if !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED < \
+ MAC_OS_X_VERSION_10_7)
+
+#define strnlen fdt_strnlen
+
+/*
+ * fdt_strnlen: returns the length of a string or max_count - which ever is
+ * smallest.
+ * Input 1 string: the string whose size is to be determined
+ * Input 2 max_count: the maximum value returned by this function
+ * Output: length of the string or max_count (the smallest of the two)
+ */
+static inline size_t fdt_strnlen(const char *string, size_t max_count)
+{
+ const char *p = memchr(string, 0, max_count);
+ return p ? p - string : max_count;
+}
+
+#endif /* !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED <
+ MAC_OS_X_VERSION_10_7) */
+
+#endif /* __APPLE__ */
+
+#endif /* LIBFDT_ENV_H */
diff --git a/tools/src/libfdt/libfdt_internal.h b/tools/src/libfdt/libfdt_internal.h
new file mode 100644
index 0000000..1a393d0
--- /dev/null
+++ b/tools/src/libfdt/libfdt_internal.h
@@ -0,0 +1,173 @@
+/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */
+#ifndef LIBFDT_INTERNAL_H
+#define LIBFDT_INTERNAL_H
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ */
+#include "fdt.h"
+
+#define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
+#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE))
+
+int32_t fdt_ro_probe_(const void *fdt);
+#define FDT_RO_PROBE(fdt) \
+ { \
+ int32_t totalsize_; \
+ if ((totalsize_ = fdt_ro_probe_(fdt)) < 0) \
+ return totalsize_; \
+ }
+
+int fdt_check_node_offset_(const void *fdt, int offset);
+int fdt_check_prop_offset_(const void *fdt, int offset);
+const char *fdt_find_string_(const char *strtab, int tabsize, const char *s);
+int fdt_node_end_offset_(void *fdt, int nodeoffset);
+
+static inline const void *fdt_offset_ptr_(const void *fdt, int offset)
+{
+ return (const char *)fdt + fdt_off_dt_struct(fdt) + offset;
+}
+
+static inline void *fdt_offset_ptr_w_(void *fdt, int offset)
+{
+ return (void *)(uintptr_t)fdt_offset_ptr_(fdt, offset);
+}
+
+static inline const struct fdt_reserve_entry *fdt_mem_rsv_(const void *fdt, int n)
+{
+ const struct fdt_reserve_entry *rsv_table =
+ (const struct fdt_reserve_entry *)
+ ((const char *)fdt + fdt_off_mem_rsvmap(fdt));
+
+ return rsv_table + n;
+}
+static inline struct fdt_reserve_entry *fdt_mem_rsv_w_(void *fdt, int n)
+{
+ return (void *)(uintptr_t)fdt_mem_rsv_(fdt, n);
+}
+
+#define FDT_SW_MAGIC (~FDT_MAGIC)
+
+/**********************************************************************/
+/* Checking controls */
+/**********************************************************************/
+
+#ifndef FDT_ASSUME_MASK
+#define FDT_ASSUME_MASK 0
+#endif
+
+/*
+ * Defines assumptions which can be enabled. Each of these can be enabled
+ * individually. For maximum safety, don't enable any assumptions!
+ *
+ * For minimal code size and no safety, use ASSUME_PERFECT at your own risk.
+ * You should have another method of validating the device tree, such as a
+ * signature or hash check before using libfdt.
+ *
+ * For situations where security is not a concern it may be safe to enable
+ * ASSUME_SANE.
+ */
+enum {
+ /*
+ * This does essentially no checks. Only the latest device-tree
+ * version is correctly handled. Inconsistencies or errors in the device
+ * tree may cause undefined behaviour or crashes. Invalid parameters
+ * passed to libfdt may do the same.
+ *
+ * If an error occurs when modifying the tree it may leave the tree in
+ * an intermediate (but valid) state. As an example, adding a property
+ * where there is insufficient space may result in the property name
+ * being added to the string table even though the property itself is
+ * not added to the struct section.
+ *
+ * Only use this if you have a fully validated device tree with
+ * the latest supported version and wish to minimise code size.
+ */
+ ASSUME_PERFECT = 0xff,
+
+ /*
+ * This assumes that the device tree is sane. i.e. header metadata
+ * and basic hierarchy are correct.
+ *
+ * With this assumption enabled, normal device trees produced by libfdt
+ * and the compiler should be handled safely. Malicious device trees and
+ * complete garbage may cause libfdt to behave badly or crash. Truncated
+ * device trees (e.g. those only partially loaded) can also cause
+ * problems.
+ *
+ * Note: Only checks that relate exclusively to the device tree itself
+ * (not the parameters passed to libfdt) are disabled by this
+ * assumption. This includes checking headers, tags and the like.
+ */
+ ASSUME_VALID_DTB = 1 << 0,
+
+ /*
+ * This builds on ASSUME_VALID_DTB and further assumes that libfdt
+ * functions are called with valid parameters, i.e. not trigger
+ * FDT_ERR_BADOFFSET or offsets that are out of bounds. It disables any
+ * extensive checking of parameters and the device tree, making various
+ * assumptions about correctness.
+ *
+ * It doesn't make sense to enable this assumption unless
+ * ASSUME_VALID_DTB is also enabled.
+ */
+ ASSUME_VALID_INPUT = 1 << 1,
+
+ /*
+ * This disables checks for device-tree version and removes all code
+ * which handles older versions.
+ *
+ * Only enable this if you know you have a device tree with the latest
+ * version.
+ */
+ ASSUME_LATEST = 1 << 2,
+
+ /*
+ * This assumes that it is OK for a failed addition to the device tree,
+ * due to lack of space or some other problem, to skip any rollback
+ * steps (such as dropping the property name from the string table).
+ * This is safe to enable in most circumstances, even though it may
+ * leave the tree in a sub-optimal state.
+ */
+ ASSUME_NO_ROLLBACK = 1 << 3,
+
+ /*
+ * This assumes that the device tree components appear in a 'convenient'
+ * order, i.e. the memory reservation block first, then the structure
+ * block and finally the string block.
+ *
+ * This order is not specified by the device-tree specification,
+ * but is expected by libfdt. The device-tree compiler always created
+ * device trees with this order.
+ *
+ * This assumption disables a check in fdt_open_into() and removes the
+ * ability to fix the problem there. This is safe if you know that the
+ * device tree is correctly ordered. See fdt_blocks_misordered_().
+ */
+ ASSUME_LIBFDT_ORDER = 1 << 4,
+
+ /*
+ * This assumes that libfdt itself does not have any internal bugs. It
+ * drops certain checks that should never be needed unless libfdt has an
+ * undiscovered bug.
+ *
+ * This can generally be considered safe to enable.
+ */
+ ASSUME_LIBFDT_FLAWLESS = 1 << 5,
+};
+
+/**
+ * can_assume_() - check if a particular assumption is enabled
+ *
+ * @mask: Mask to check (ASSUME_...)
+ * @return true if that assumption is enabled, else false
+ */
+static inline bool can_assume_(int mask)
+{
+ return FDT_ASSUME_MASK & mask;
+}
+
+/** helper macros for checking assumptions */
+#define can_assume(_assume) can_assume_(ASSUME_ ## _assume)
+
+#endif /* LIBFDT_INTERNAL_H */
diff --git a/tools/src/main.c b/tools/src/main.c
new file mode 100644
index 0000000..8d69e03
--- /dev/null
+++ b/tools/src/main.c
@@ -0,0 +1,205 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "../build/build_cfg.h"
+#include "../build/build_tag.h"
+
+#include "../config.h"
+
+#include "adt.h"
+#include "aic.h"
+#include "clk.h"
+#include "cpufreq.h"
+#include "display.h"
+#include "exception.h"
+#include "fb.h"
+#include "firmware.h"
+#include "gxf.h"
+#include "heapblock.h"
+#include "mcc.h"
+#include "memory.h"
+#include "nvme.h"
+#include "payload.h"
+#include "pcie.h"
+#include "pmgr.h"
+#include "sep.h"
+#include "smp.h"
+#include "string.h"
+#include "tunables.h"
+#include "uart.h"
+#include "uartproxy.h"
+#include "usb.h"
+#include "utils.h"
+#include "wdt.h"
+#include "xnuboot.h"
+
+struct vector_args next_stage;
+
+const char version_tag[] = "##m1n1_ver##" BUILD_TAG;
+const char *const m1n1_version = version_tag + 12;
+
+u32 board_id = ~0, chip_id = ~0;
+
+void get_device_info(void)
+{
+ printf("Device info:\n");
+ printf(" Model: %s\n", (const char *)adt_getprop(adt, 0, "model", NULL));
+ printf(" Target: %s\n", (const char *)adt_getprop(adt, 0, "target-type", NULL));
+
+ int chosen = adt_path_offset(adt, "/chosen");
+ if (chosen > 0) {
+ if (ADT_GETPROP(adt, chosen, "board-id", &board_id) < 0)
+ printf("Failed to find board-id\n");
+ if (ADT_GETPROP(adt, chosen, "chip-id", &chip_id) < 0)
+ printf("Failed to find chip-id\n");
+
+ printf(" Board-ID: 0x%x\n", board_id);
+ printf(" Chip-ID: 0x%x\n", chip_id);
+ } else {
+ printf("No chosen node!\n");
+ }
+
+ printf("\n");
+}
+
+void run_actions(void)
+{
+ bool usb_up = false;
+
+#ifndef BRINGUP
+#ifdef EARLY_PROXY_TIMEOUT
+ int node = adt_path_offset(adt, "/chosen/asmb");
+ u64 lp_sip0 = 0;
+
+ if (node >= 0) {
+ ADT_GETPROP(adt, node, "lp-sip0", &lp_sip0);
+ printf("Boot policy: sip0 = %ld\n", lp_sip0);
+ }
+
+ if (!cur_boot_args.video.display && lp_sip0 == 127) {
+ printf("Bringing up USB for early debug...\n");
+
+ usb_init();
+ usb_iodev_init();
+
+ usb_up = true;
+
+ printf("Waiting for proxy connection... ");
+ for (int i = 0; i < EARLY_PROXY_TIMEOUT * 100; i++) {
+ for (int j = 0; j < USB_IODEV_COUNT; j++) {
+ iodev_id_t iodev = IODEV_USB0 + j;
+
+ if (!(iodev_get_usage(iodev) & USAGE_UARTPROXY))
+ continue;
+
+ usb_iodev_vuart_setup(iodev);
+ iodev_handle_events(iodev);
+ if (iodev_can_write(iodev) || iodev_can_write(IODEV_USB_VUART)) {
+ printf(" Connected!\n");
+ uartproxy_run(NULL);
+ return;
+ }
+ }
+
+ mdelay(10);
+ if (i % 100 == 99)
+ printf(".");
+ }
+ printf(" Timed out\n");
+ }
+#endif
+#endif
+
+ printf("Checking for payloads...\n");
+
+ if (payload_run() == 0) {
+ printf("Valid payload found\n");
+ return;
+ }
+
+ fb_set_active(true);
+
+ printf("No valid payload found\n");
+
+#ifndef BRINGUP
+ if (!usb_up) {
+ usb_init();
+ usb_iodev_init();
+ }
+#endif
+
+ printf("Running proxy...\n");
+
+ uartproxy_run(NULL);
+}
+
+void m1n1_main(void)
+{
+ printf("\n\nm1n1 %s\n", m1n1_version);
+ printf("Copyright The Asahi Linux Contributors\n");
+ printf("Licensed under the MIT license\n\n");
+
+ printf("Running in EL%lu\n\n", mrs(CurrentEL) >> 2);
+
+ get_device_info();
+ firmware_init();
+
+ heapblock_init();
+
+#ifndef BRINGUP
+ gxf_init();
+ mcc_init();
+ mmu_init();
+ aic_init();
+#endif
+ wdt_disable();
+#ifndef BRINGUP
+ pmgr_init();
+ tunables_apply_static();
+
+#ifdef USE_FB
+ display_init();
+ // Kick DCP to sleep, so dodgy monitors which cause reconnect cycles don't cause us to lose the
+ // framebuffer.
+ display_shutdown(DCP_SLEEP_IF_EXTERNAL);
+ fb_init(false);
+ fb_display_logo();
+#ifdef FB_SILENT_MODE
+ fb_set_active(!cur_boot_args.video.display);
+#else
+ fb_set_active(true);
+#endif
+#endif
+
+ clk_init();
+ cpufreq_init();
+ sep_init();
+#endif
+
+ printf("Initialization complete.\n");
+
+ run_actions();
+
+ if (!next_stage.entry) {
+ panic("Nothing to do!\n");
+ }
+
+ printf("Preparing to run next stage at %p...\n", next_stage.entry);
+
+ nvme_shutdown();
+ exception_shutdown();
+#ifndef BRINGUP
+ usb_iodev_shutdown();
+ display_shutdown(DCP_SLEEP_IF_EXTERNAL);
+#ifdef USE_FB
+ fb_shutdown(next_stage.restore_logo);
+#endif
+ mmu_shutdown();
+#endif
+
+ printf("Vectoring to next stage...\n");
+
+ next_stage.entry(next_stage.args[0], next_stage.args[1], next_stage.args[2], next_stage.args[3],
+ next_stage.args[4]);
+
+ panic("Next stage returned!\n");
+}
diff --git a/tools/src/math/exp2f_data.c b/tools/src/math/exp2f_data.c
new file mode 100644
index 0000000..38c9333
--- /dev/null
+++ b/tools/src/math/exp2f_data.c
@@ -0,0 +1,42 @@
+/*
+ * Shared data between expf, exp2f and powf.
+ *
+ * Copyright (c) 2017-2018, Arm Limited.
+ * SPDX-License-Identifier: MIT
+ */
+
+#include "exp2f_data.h"
+
+#define N (1 << EXP2F_TABLE_BITS)
+
+const struct exp2f_data __exp2f_data = {
+ /* tab[i] = uint(2^(i/N)) - (i << 52-BITS)
+ used for computing 2^(k/N) for an int |k| < 150 N as
+ double(tab[k%N] + (k << 52-BITS)) */
+ .tab =
+ {
+ 0x3ff0000000000000, 0x3fefd9b0d3158574, 0x3fefb5586cf9890f, 0x3fef9301d0125b51,
+ 0x3fef72b83c7d517b, 0x3fef54873168b9aa, 0x3fef387a6e756238, 0x3fef1e9df51fdee1,
+ 0x3fef06fe0a31b715, 0x3feef1a7373aa9cb, 0x3feedea64c123422, 0x3feece086061892d,
+ 0x3feebfdad5362a27, 0x3feeb42b569d4f82, 0x3feeab07dd485429, 0x3feea47eb03a5585,
+ 0x3feea09e667f3bcd, 0x3fee9f75e8ec5f74, 0x3feea11473eb0187, 0x3feea589994cce13,
+ 0x3feeace5422aa0db, 0x3feeb737b0cdc5e5, 0x3feec49182a3f090, 0x3feed503b23e255d,
+ 0x3feee89f995ad3ad, 0x3feeff76f2fb5e47, 0x3fef199bdd85529c, 0x3fef3720dcef9069,
+ 0x3fef5818dcfba487, 0x3fef7c97337b9b5f, 0x3fefa4afa2a490da, 0x3fefd0765b6e4540,
+ },
+ .shift_scaled = 0x1.8p+52 / N,
+ .poly =
+ {
+ 0x1.c6af84b912394p-5,
+ 0x1.ebfce50fac4f3p-3,
+ 0x1.62e42ff0c52d6p-1,
+ },
+ .shift = 0x1.8p+52,
+ .invln2_scaled = 0x1.71547652b82fep+0 * N,
+ .poly_scaled =
+ {
+ 0x1.c6af84b912394p-5 / N / N / N,
+ 0x1.ebfce50fac4f3p-3 / N / N,
+ 0x1.62e42ff0c52d6p-1 / N,
+ },
+};
diff --git a/tools/src/math/exp2f_data.h b/tools/src/math/exp2f_data.h
new file mode 100644
index 0000000..a88c6ce
--- /dev/null
+++ b/tools/src/math/exp2f_data.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2017-2018, Arm Limited.
+ * SPDX-License-Identifier: MIT
+ */
+#ifndef _EXP2F_DATA_H
+#define _EXP2F_DATA_H
+
+#include <stdint.h>
+
+/* Shared between expf, exp2f and powf. */
+#define EXP2F_TABLE_BITS 5
+#define EXP2F_POLY_ORDER 3
+extern const struct exp2f_data {
+ uint64_t tab[1 << EXP2F_TABLE_BITS];
+ double shift_scaled;
+ double poly[EXP2F_POLY_ORDER];
+ double shift;
+ double invln2_scaled;
+ double poly_scaled[EXP2F_POLY_ORDER];
+} __exp2f_data;
+
+#endif
diff --git a/tools/src/math/expf.c b/tools/src/math/expf.c
new file mode 100644
index 0000000..c9f1b3f
--- /dev/null
+++ b/tools/src/math/expf.c
@@ -0,0 +1,83 @@
+/*
+ * Single-precision e^x function.
+ *
+ * Copyright (c) 2017-2018, Arm Limited.
+ * SPDX-License-Identifier: MIT
+ */
+
+#include <math.h>
+#include <stdint.h>
+
+#include "exp2f_data.h"
+#include "libm.h"
+
+#define double_t double
+
+/*
+EXP2F_TABLE_BITS = 5
+EXP2F_POLY_ORDER = 3
+
+ULP error: 0.502 (nearest rounding.)
+Relative error: 1.69 * 2^-34 in [-ln2/64, ln2/64] (before rounding.)
+Wrong count: 170635 (all nearest rounding wrong results with fma.)
+Non-nearest ULP error: 1 (rounded ULP error)
+*/
+
+#define N (1 << EXP2F_TABLE_BITS)
+#define InvLn2N __exp2f_data.invln2_scaled
+#define T __exp2f_data.tab
+#define C __exp2f_data.poly_scaled
+
+static inline uint32_t top12(float x)
+{
+ return asuint(x) >> 20;
+}
+
+float expf(float x)
+{
+ uint32_t abstop;
+ uint64_t ki, t;
+ double_t kd, xd, z, r, r2, y, s;
+
+ xd = (double_t)x;
+ abstop = top12(x) & 0x7ff;
+ if (predict_false(abstop >= top12(88.0f))) {
+ /* |x| >= 88 or x is nan. */
+ if (asuint(x) == asuint(-INFINITY))
+ return 0.0f;
+ if (abstop >= top12(INFINITY))
+ return x + x;
+ if (x > 0x1.62e42ep6f) /* x > log(0x1p128) ~= 88.72 */
+ return __math_oflowf(0);
+ if (x < -0x1.9fe368p6f) /* x < log(0x1p-150) ~= -103.97 */
+ return __math_uflowf(0);
+ }
+
+ /* x*N/Ln2 = k + r with r in [-1/2, 1/2] and int k. */
+ z = InvLn2N * xd;
+
+ /* Round and convert z to int, the result is in [-150*N, 128*N] and
+ ideally ties-to-even rule is used, otherwise the magnitude of r
+ can be bigger which gives larger approximation error. */
+#if TOINT_INTRINSICS
+ kd = roundtoint(z);
+ ki = converttoint(z);
+#else
+#define SHIFT __exp2f_data.shift
+ kd = eval_as_double(z + SHIFT);
+ ki = asuint64(kd);
+ kd -= SHIFT;
+#endif
+ r = z - kd;
+
+ /* exp(x) = 2^(k/N) * 2^(r/N) ~= s * (C0*r^3 + C1*r^2 + C2*r + 1) */
+ t = T[ki % N];
+ t += ki << (52 - EXP2F_TABLE_BITS);
+ s = asdouble(t);
+ z = C[0] * r + C[1];
+ r2 = r * r;
+ y = C[2] * r + 1;
+ y = z * r2 + y;
+ y = y * s;
+ return eval_as_float(y);
+}
diff --git a/tools/src/math/libm.h b/tools/src/math/libm.h
new file mode 100644
index 0000000..1616c74
--- /dev/null
+++ b/tools/src/math/libm.h
@@ -0,0 +1,271 @@
+#ifndef _LIBM_H
+#define _LIBM_H
+
+#include <endian.h>
+#include <float.h>
+#include <math.h>
+#include <stdint.h>
+
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER == __LITTLE_ENDIAN
+union ldshape {
+ long double f;
+ struct {
+ uint64_t m;
+ uint16_t se;
+ } i;
+};
+#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER == __BIG_ENDIAN
+/* This is the m68k variant of 80-bit long double, and this definition only works
+ * on archs where the alignment requirement of uint64_t is <= 4. */
+union ldshape {
+ long double f;
+ struct {
+ uint16_t se;
+ uint16_t pad;
+ uint64_t m;
+ } i;
+};
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER == __LITTLE_ENDIAN
+union ldshape {
+ long double f;
+ struct {
+ uint64_t lo;
+ uint32_t mid;
+ uint16_t top;
+ uint16_t se;
+ } i;
+ struct {
+ uint64_t lo;
+ uint64_t hi;
+ } i2;
+};
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER == __BIG_ENDIAN
+union ldshape {
+ long double f;
+ struct {
+ uint16_t se;
+ uint16_t top;
+ uint32_t mid;
+ uint64_t lo;
+ } i;
+ struct {
+ uint64_t hi;
+ uint64_t lo;
+ } i2;
+};
+#else
+#error Unsupported long double representation
+#endif
+
+/* Support non-nearest rounding mode. */
+#define WANT_ROUNDING 1
+/* Support signaling NaNs. */
+#define WANT_SNAN 0
+
+#if WANT_SNAN
+#error SNaN is unsupported
+#else
+#define issignalingf_inline(x) 0
+#define issignaling_inline(x) 0
+#endif
+
+#ifndef TOINT_INTRINSICS
+#define TOINT_INTRINSICS 0
+#endif
+
+#if TOINT_INTRINSICS
+/* Round x to nearest int in all rounding modes, ties have to be rounded
+ consistently with converttoint so the results match. If the result
+ would be outside of [-2^31, 2^31-1] then the semantics is unspecified. */
+static double_t roundtoint(double_t);
+
+/* Convert x to nearest int in all rounding modes, ties have to be rounded
+ consistently with roundtoint. If the result is not representible in an
+ int32_t then the semantics is unspecified. */
+static int32_t converttoint(double_t);
+#endif
+
+/* Helps static branch prediction so hot path can be better optimized. */
+#ifdef __GNUC__
+#define predict_true(x) __builtin_expect(!!(x), 1)
+#define predict_false(x) __builtin_expect(x, 0)
+#else
+#define predict_true(x) (x)
+#define predict_false(x) (x)
+#endif
+
+/* Evaluate an expression as the specified type. With standard excess
+ precision handling a type cast or assignment is enough (with
+ -ffloat-store an assignment is required, in old compilers argument
+ passing and return statement may not drop excess precision). */
+
+static inline float eval_as_float(float x)
+{
+ float y = x;
+ return y;
+}
+
+static inline double eval_as_double(double x)
+{
+ double y = x;
+ return y;
+}
+
+/* fp_barrier returns its input, but limits code transformations
+ as if it had a side-effect (e.g. observable io) and returned
+ an arbitrary value. */
+
+#ifndef fp_barrierf
+#define fp_barrierf fp_barrierf
+static inline float fp_barrierf(float x)
+{
+ volatile float y = x;
+ return y;
+}
+#endif
+
+#ifndef fp_barrier
+#define fp_barrier fp_barrier
+static inline double fp_barrier(double x)
+{
+ volatile double y = x;
+ return y;
+}
+#endif
+
+#ifndef fp_barrierl
+#define fp_barrierl fp_barrierl
+static inline long double fp_barrierl(long double x)
+{
+ volatile long double y = x;
+ return y;
+}
+#endif
+
+/* fp_force_eval ensures that the input value is computed when that's
+ otherwise unused. To prevent the constant folding of the input
+ expression, an additional fp_barrier may be needed or a compilation
+ mode that does so (e.g. -frounding-math in gcc). Then it can be
+ used to evaluate an expression for its fenv side-effects only. */
+
+#ifndef fp_force_evalf
+#define fp_force_evalf fp_force_evalf
+static inline void fp_force_evalf(float x)
+{
+ volatile float y;
+ y = x;
+ (void)y;
+}
+#endif
+
+#ifndef fp_force_eval
+#define fp_force_eval fp_force_eval
+static inline void fp_force_eval(double x)
+{
+ volatile double y;
+ y = x;
+ (void)y;
+}
+#endif
+
+#ifndef fp_force_evall
+#define fp_force_evall fp_force_evall
+static inline void fp_force_evall(long double x)
+{
+ volatile long double y;
+ y = x;
+ (void)y;
+}
+#endif
+
+#define FORCE_EVAL(x) \
+ do { \
+ if (sizeof(x) == sizeof(float)) { \
+ fp_force_evalf(x); \
+ } else if (sizeof(x) == sizeof(double)) { \
+ fp_force_eval(x); \
+ } else { \
+ fp_force_evall(x); \
+ } \
+ } while (0)
+
+#define asuint(f) \
+ ((union { \
+ float _f; \
+ uint32_t _i; \
+ }){f}) \
+ ._i
+#define asfloat(i) \
+ ((union { \
+ uint32_t _i; \
+ float _f; \
+ }){i}) \
+ ._f
+#define asuint64(f) \
+ ((union { \
+ double _f; \
+ uint64_t _i; \
+ }){f}) \
+ ._i
+#define asdouble(i) \
+ ((union { \
+ uint64_t _i; \
+ double _f; \
+ }){i}) \
+ ._f
+
+#define EXTRACT_WORDS(hi, lo, d) \
+ do { \
+ uint64_t __u = asuint64(d); \
+ (hi) = __u >> 32; \
+ (lo) = (uint32_t)__u; \
+ } while (0)
+
+#define GET_HIGH_WORD(hi, d) \
+ do { \
+ (hi) = asuint64(d) >> 32; \
+ } while (0)
+
+#define GET_LOW_WORD(lo, d) \
+ do { \
+ (lo) = (uint32_t)asuint64(d); \
+ } while (0)
+
+#define INSERT_WORDS(d, hi, lo) \
+ do { \
+ (d) = asdouble(((uint64_t)(hi) << 32) | (uint32_t)(lo)); \
+ } while (0)
+
+#define SET_HIGH_WORD(d, hi) INSERT_WORDS(d, hi, (uint32_t)asuint64(d))
+
+#define SET_LOW_WORD(d, lo) INSERT_WORDS(d, asuint64(d) >> 32, lo)
+
+#define GET_FLOAT_WORD(w, d) \
+ do { \
+ (w) = asuint(d); \
+ } while (0)
+
+#define SET_FLOAT_WORD(d, w) \
+ do { \
+ (d) = asfloat(w); \
+ } while (0)
+
+/* error handling functions */
+
+static inline float __math_xflowf(uint32_t sign, float y)
+{
+ return eval_as_float(fp_barrierf(sign ? -y : y) * y);
+}
+
+static inline float __math_oflowf(uint32_t sign)
+{
+ return __math_xflowf(sign, 0x1p97f);
+}
+
+float __math_uflowf(uint32_t sign)
+{
+ return __math_xflowf(sign, 0x1p-95f);
+}
+
+#endif
diff --git a/tools/src/mcc.c b/tools/src/mcc.c
new file mode 100644
index 0000000..d687d73
--- /dev/null
+++ b/tools/src/mcc.c
@@ -0,0 +1,271 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "mcc.h"
+#include "adt.h"
+#include "hv.h"
+#include "memory.h"
+#include "string.h"
+#include "utils.h"
+
+static bool mcc_initialized = false;
+
+#define MAX_MCC_INSTANCES 16
+
+#define T8103_PLANES 4
+#define T8103_PLANE_STRIDE 0x40000
+#define T8103_DCS_STRIDE 0x40000
+
+#define T6000_PLANES 4
+#define T6000_PLANE_OFFSET 0
+#define T6000_PLANE_STRIDE 0x40000
+#define T6000_GLOBAL_OFFSET 0x100000
+#define T6000_DCS_OFFSET 0x200000
+#define T6000_DCS_STRIDE 0x100000
+#define T6000_DCS_COUNT 4
+
+#define PLANE_TZ_START(i) (0x6a0 + i * 0x10)
+#define PLANE_TZ_END(i) (0x6a4 + i * 0x10)
+#define PLANE_TZ_ENABLE(i) (0x6a8 + i * 0x10)
+#define PLANE_TZ_REGS 4
+
+#define PLANE_CACHE_ENABLE 0x1c00
+#define PLANE_CACHE_STATUS 0x1c04
+
+#define T8103_CACHE_STATUS_DATA_COUNT GENMASK(14, 10)
+#define T8103_CACHE_STATUS_TAG_COUNT GENMASK(9, 5)
+
+#define T6000_CACHE_STATUS_DATA_COUNT GENMASK(13, 9)
+#define T6000_CACHE_STATUS_TAG_COUNT GENMASK(8, 4)
+
+#define T6000_CACHE_WAYS 12
+#define T6000_CACHE_STATUS_MASK (T6000_CACHE_STATUS_DATA_COUNT | T6000_CACHE_STATUS_TAG_COUNT)
+#define T6000_CACHE_STATUS_VAL \
+ (FIELD_PREP(T6000_CACHE_STATUS_DATA_COUNT, T6000_CACHE_WAYS) | \
+ FIELD_PREP(T6000_CACHE_STATUS_TAG_COUNT, T6000_CACHE_WAYS))
+
+#define T8103_CACHE_WAYS 16
+#define T8103_CACHE_STATUS_MASK (T8103_CACHE_STATUS_DATA_COUNT | T8103_CACHE_STATUS_TAG_COUNT)
+#define T8103_CACHE_STATUS_VAL \
+ (FIELD_PREP(T8103_CACHE_STATUS_DATA_COUNT, T8103_CACHE_WAYS) | \
+ FIELD_PREP(T8103_CACHE_STATUS_TAG_COUNT, T8103_CACHE_WAYS))
+
+#define CACHE_ENABLE_TIMEOUT 10000
+
+#define T8103_DCC_DRAMCFG0 0xdc4
+#define T8103_DCC_DRAMCFG1 0xdbc
+#define T8103_DCC_DRAMCFG0_DEFAULT 0x813057f
+#define T8103_DCC_DRAMCFG1_DEFAULT 0x1800180
+#define T8103_DCC_DRAMCFG0_FAST 0x133
+#define T8103_DCC_DRAMCFG1_FAST 0x55555340
+
+#define T6000_DCC_DRAMCFG 0x13cc
+#define T6000_DCC_DRAMCFG_DEFAULT 0x55551555
+#define T6000_DCC_DRAMCFG_FAST 0xffff0000
+
+size_t mcc_carveout_count;
+struct mcc_carveout mcc_carveouts[PLANE_TZ_REGS + 1];
+
+struct mcc_regs {
+ u64 plane_base;
+ u64 plane_stride;
+ int plane_count;
+
+ u64 global_base;
+
+ u64 dcs_base;
+ u64 dcs_stride;
+ int dcs_count;
+
+ int cache_ways;
+ u32 cache_status_mask;
+ u32 cache_status_val;
+};
+
+static int mcc_count;
+static struct mcc_regs mcc_regs[MAX_MCC_INSTANCES];
+
+static u32 plane_read32(int mcc, int plane, u64 offset)
+{
+ return read32(mcc_regs[mcc].plane_base + plane * mcc_regs[mcc].plane_stride + offset);
+}
+
+static void plane_write32(int mcc, int plane, u64 offset, u32 value)
+{
+ write32(mcc_regs[mcc].plane_base + plane * mcc_regs[mcc].plane_stride + offset, value);
+}
+
+static int plane_poll32(int mcc, int plane, u64 offset, u32 mask, u32 target, u32 timeout)
+{
+ return poll32(mcc_regs[mcc].plane_base + plane * mcc_regs[mcc].plane_stride + offset, mask,
+ target, timeout);
+}
+
+static void mcc_enable_cache(void)
+{
+ if (!mcc_initialized)
+ return;
+
+ for (int mcc = 0; mcc < mcc_count; mcc++) {
+ for (int plane = 0; plane < mcc_regs[mcc].plane_count; plane++) {
+ plane_write32(mcc, plane, PLANE_CACHE_ENABLE, mcc_regs[mcc].cache_ways);
+ if (plane_poll32(mcc, plane, PLANE_CACHE_STATUS, mcc_regs[mcc].cache_status_mask,
+ mcc_regs[mcc].cache_status_val, CACHE_ENABLE_TIMEOUT))
+ printf("MCC: timeout while enabling cache for MCC %d plane %d: 0x%x\n", mcc, plane,
+ plane_read32(mcc, plane, PLANE_CACHE_STATUS));
+ }
+ }
+}
+
+int mcc_unmap_carveouts(void)
+{
+ if (!mcc_initialized)
+ return -1;
+
+ mcc_carveout_count = 0;
+ memset(mcc_carveouts, 0, sizeof mcc_carveouts);
+ // All MCCs and planes should have identical configs
+ for (int i = 0; i < PLANE_TZ_REGS; i++) {
+ uint64_t start = plane_read32(0, 0, PLANE_TZ_START(i));
+ uint64_t end = plane_read32(0, 0, PLANE_TZ_END(i));
+ bool enabled = plane_read32(0, 0, PLANE_TZ_ENABLE(i));
+
+ if (enabled) {
+ if (!start || start == end) {
+ printf("MMU: TZ%d region has bad bounds 0x%lx..0x%lx (iBoot bug?)\n", i, start,
+ end);
+ continue;
+ }
+
+ start = start << 12;
+ end = (end + 1) << 12;
+ start |= ram_base;
+ end |= ram_base;
+ printf("MMU: Unmapping TZ%d region at 0x%lx..0x%lx\n", i, start, end);
+ mmu_rm_mapping(start, end - start);
+ mmu_rm_mapping(start | REGION_RWX_EL0, end - start);
+ mmu_rm_mapping(start | REGION_RW_EL0, end - start);
+ mmu_rm_mapping(start | REGION_RX_EL1, end - start);
+ mcc_carveouts[mcc_carveout_count].base = start;
+ mcc_carveouts[mcc_carveout_count].size = end - start;
+ mcc_carveout_count++;
+ }
+ }
+
+ return 0;
+}
+
+int mcc_init_t8103(int node, int *path)
+{
+ printf("MCC: Initializing T8103 MCC...\n");
+
+ mcc_count = 1;
+ mcc_regs[0].plane_stride = T8103_PLANE_STRIDE;
+ mcc_regs[0].plane_count = T8103_PLANES;
+ mcc_regs[0].dcs_stride = T8103_DCS_STRIDE;
+
+ if (adt_get_reg(adt, path, "reg", 0, &mcc_regs[0].global_base, NULL)) {
+ printf("MCC: Failed to get reg property 0!\n");
+ return -1;
+ }
+
+ if (adt_get_reg(adt, path, "reg", 1, &mcc_regs[0].plane_base, NULL)) {
+ printf("MCC: Failed to get reg property 1!\n");
+ return -1;
+ }
+
+ if (adt_get_reg(adt, path, "reg", 2, &mcc_regs[0].dcs_base, NULL)) {
+ printf("MCC: Failed to get reg property 2!\n");
+ return -1;
+ }
+
+ u32 val;
+ if (ADT_GETPROP(adt, node, "dcs_num_channels", &val) < 0) {
+ printf("MCC: Failed to get dcs_num_channels property!\n");
+ return -1;
+ }
+
+ mcc_regs[0].dcs_count = val;
+ mcc_regs[0].cache_ways = T8103_CACHE_WAYS;
+ mcc_regs[0].cache_status_mask = T8103_CACHE_STATUS_MASK;
+ mcc_regs[0].cache_status_val = T8103_CACHE_STATUS_VAL;
+
+ mcc_enable_cache();
+
+ printf("MCC: Initialized T8103 MCC (%d channels)\n", val);
+
+ mcc_initialized = true;
+
+ return 0;
+}
+
+int mcc_init_t6000(int node, int *path)
+{
+ u32 reg_len;
+
+ if (!adt_getprop(adt, node, "reg", &reg_len)) {
+ printf("MCC: Failed to get reg property!\n");
+ return -1;
+ }
+
+ mcc_count = reg_len / 16;
+
+ printf("MCC: Initializing T6000 MCCs (%d instances)...\n", mcc_count);
+
+ if (mcc_count > MAX_MCC_INSTANCES) {
+ printf("MCC: Too many instances, increase MAX_MCC_INSTANCES!\n");
+ mcc_count = MAX_MCC_INSTANCES;
+ }
+
+ for (int i = 0; i < mcc_count; i++) {
+ u64 base;
+ if (adt_get_reg(adt, path, "reg", 0, &base, NULL)) {
+ printf("MCC: Failed to get reg index %d!\n", i);
+ return -1;
+ }
+
+ mcc_regs[i].plane_base = base + T6000_PLANE_OFFSET;
+ mcc_regs[i].plane_stride = T6000_PLANE_STRIDE;
+ mcc_regs[i].plane_count = T6000_PLANES;
+
+ mcc_regs[i].global_base = base + T6000_GLOBAL_OFFSET;
+
+ mcc_regs[i].dcs_base = base + T6000_DCS_OFFSET;
+ mcc_regs[i].dcs_stride = T6000_DCS_STRIDE;
+ mcc_regs[i].dcs_count = T6000_DCS_COUNT;
+
+ mcc_regs[i].cache_ways = T6000_CACHE_WAYS;
+ mcc_regs[i].cache_status_mask = T6000_CACHE_STATUS_MASK;
+ mcc_regs[i].cache_status_val = T6000_CACHE_STATUS_VAL;
+ }
+
+ mcc_enable_cache();
+
+ printf("MCC: Initialized T6000 MCCs (%d instances, %d planes, %d channels)\n", mcc_count,
+ mcc_regs[0].plane_count, mcc_regs[0].dcs_count);
+
+ mcc_initialized = true;
+
+ return 0;
+}
+
+int mcc_init(void)
+{
+ int path[8];
+ int node = adt_path_offset_trace(adt, "/arm-io/mcc", path);
+
+ if (node < 0) {
+ printf("MCC: MCC node not found!\n");
+ return -1;
+ }
+
+ if (adt_is_compatible(adt, node, "mcc,t8103")) {
+ return mcc_init_t8103(node, path);
+ } else if (adt_is_compatible(adt, node, "mcc,t8112")) {
+ return mcc_init_t8103(node, path);
+ } else if (adt_is_compatible(adt, node, "mcc,t6000")) {
+ return mcc_init_t6000(node, path);
+ } else {
+ printf("MCC: Unsupported version\n");
+ return -1;
+ }
+}
diff --git a/tools/src/mcc.h b/tools/src/mcc.h
new file mode 100644
index 0000000..b059d47
--- /dev/null
+++ b/tools/src/mcc.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef MCC_H
+#define MCC_H
+
+#include "types.h"
+
+struct mcc_carveout {
+ u64 base;
+ u64 size;
+};
+
+extern size_t mcc_carveout_count;
+extern struct mcc_carveout mcc_carveouts[];
+
+int mcc_init(void);
+int mcc_unmap_carveouts(void);
+
+#endif
diff --git a/tools/src/memory.c b/tools/src/memory.c
new file mode 100644
index 0000000..aec6782
--- /dev/null
+++ b/tools/src/memory.c
@@ -0,0 +1,566 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "memory.h"
+#include "adt.h"
+#include "assert.h"
+#include "cpu_regs.h"
+#include "fb.h"
+#include "gxf.h"
+#include "malloc.h"
+#include "mcc.h"
+#include "smp.h"
+#include "string.h"
+#include "utils.h"
+#include "xnuboot.h"
+
+#define PAGE_SIZE 0x4000
+#define CACHE_LINE_SIZE 64
+
+#define CACHE_RANGE_OP(func, op) \
+ void func(void *addr, size_t length) \
+ { \
+ u64 p = (u64)addr; \
+ u64 end = p + length; \
+ while (p < end) { \
+ cacheop(op, p); \
+ p += CACHE_LINE_SIZE; \
+ } \
+ }
+
+CACHE_RANGE_OP(ic_ivau_range, "ic ivau")
+CACHE_RANGE_OP(dc_ivac_range, "dc ivac")
+CACHE_RANGE_OP(dc_zva_range, "dc zva")
+CACHE_RANGE_OP(dc_cvac_range, "dc cvac")
+CACHE_RANGE_OP(dc_cvau_range, "dc cvau")
+CACHE_RANGE_OP(dc_civac_range, "dc civac")
+
+extern u8 _stack_top[];
+
+uint64_t ram_base = 0;
+
+static inline u64 read_sctlr(void)
+{
+ sysop("isb");
+ return mrs(SCTLR_EL1);
+}
+
+static inline void write_sctlr(u64 val)
+{
+ msr(SCTLR_EL1, val);
+ sysop("isb");
+}
+
+#define VADDR_L3_INDEX_BITS 11
+#define VADDR_L2_INDEX_BITS 11
+// We treat two concatenated L1 page tables as one
+#define VADDR_L1_INDEX_BITS 12
+
+#define VADDR_L3_OFFSET_BITS 14
+#define VADDR_L2_OFFSET_BITS 25
+#define VADDR_L1_OFFSET_BITS 36
+
+#define VADDR_L1_ALIGN_MASK GENMASK(VADDR_L1_OFFSET_BITS - 1, VADDR_L2_OFFSET_BITS)
+#define VADDR_L2_ALIGN_MASK GENMASK(VADDR_L2_OFFSET_BITS - 1, VADDR_L3_OFFSET_BITS)
+#define PTE_TARGET_MASK GENMASK(49, VADDR_L3_OFFSET_BITS)
+
+#define ENTRIES_PER_L1_TABLE BIT(VADDR_L1_INDEX_BITS)
+#define ENTRIES_PER_L2_TABLE BIT(VADDR_L2_INDEX_BITS)
+#define ENTRIES_PER_L3_TABLE BIT(VADDR_L3_INDEX_BITS)
+
+#define IS_PTE(pte) ((pte) && pte & PTE_VALID)
+
+#define L1_IS_TABLE(pte) (IS_PTE(pte) && FIELD_GET(PTE_TYPE, pte) == PTE_TABLE)
+#define L1_IS_BLOCK(pte) (IS_PTE(pte) && FIELD_GET(PTE_TYPE, pte) == PTE_BLOCK)
+#define L2_IS_TABLE(pte) (IS_PTE(pte) && FIELD_GET(PTE_TYPE, pte) == PTE_TABLE)
+#define L2_IS_BLOCK(pte) (IS_PTE(pte) && FIELD_GET(PTE_TYPE, pte) == PTE_BLOCK)
+#define L3_IS_BLOCK(pte) (IS_PTE(pte) && FIELD_GET(PTE_TYPE, pte) == PTE_PAGE)
+
+/*
+ * We use 16KB pages which results in the following virtual address space:
+ *
+ * [L0 index] [L1 index] [L2 index] [L3 index] [page offset]
+ * 1 bit 11 bits 11 bits 11 bits 14 bits
+ *
+ * To simplify things we treat the L1 page table as a concatenated table,
+ * which results in the following layout:
+ *
+ * [L1 index] [L2 index] [L3 index] [page offset]
+ * 12 bits 11 bits 11 bits 14 bits
+ *
+ * We initalize one double-size L1 table which covers the entire virtual memory space,
+ * point to the two halves in the single L0 table and then create L2/L3 tables on demand.
+ */
+
+/*
+ * SPRR mappings interpret these bits as a 4-bit index as follows
+ * [AP1][AP0][PXN][UXN]
+ */
+#define SPRR_INDEX(perm) \
+ (((PTE_AP_RO & (perm)) ? 0b1000 : 0) | ((PTE_AP_EL0 & (perm)) ? 0b0100 : 0) | \
+ ((PTE_UXN & (perm)) ? 0b0010 : 0) | ((PTE_PXN & (perm)) ? 0b0001 : 0))
+
+enum SPRR_val_t {
+ EL0_GL0,
+ ELrx_GL0,
+ ELr_GL0,
+ ELrw_GL0,
+ EL0_GLrx,
+ ELrx_GLrx,
+ ELr_GLrx,
+ EL0_GLrx_ALT,
+ EL0_GLr,
+ ELx_GLr,
+ ELr_GLr,
+ ELrw_GLr,
+ EL0_GLrw,
+ ELrx_GLrw,
+ ELr_GLrw,
+ ELrw_GLrw,
+};
+
+/*
+ * With SPRR enabled, RWX mappings get downgraded to RW.
+ */
+
+#define SPRR_PERM(ap, val) (((u64)val) << (4 * SPRR_INDEX(ap)))
+
+#define SPRR_DEFAULT_PERM_EL1 \
+ SPRR_PERM(PERM_RO_EL0, ELrw_GLrw) | SPRR_PERM(PERM_RW_EL0, ELrw_GLrw) | \
+ SPRR_PERM(PERM_RX_EL0, ELrx_GLrx) | SPRR_PERM(PERM_RWX_EL0, ELrw_GLrw) | \
+ SPRR_PERM(PERM_RO, ELr_GLr) | SPRR_PERM(PERM_RW, ELrw_GLrw) | \
+ SPRR_PERM(PERM_RX, ELrx_GLrx) | SPRR_PERM(PERM_RWX, ELrw_GLrw)
+
+#define SPRR_DEFAULT_PERM_EL0 \
+ SPRR_PERM(PERM_RO_EL0, ELr_GLr) | SPRR_PERM(PERM_RW_EL0, ELrw_GLrw) | \
+ SPRR_PERM(PERM_RX_EL0, ELrx_GLrx) | SPRR_PERM(PERM_RWX_EL0, ELrx_GLrx) | \
+ SPRR_PERM(PERM_RO, ELr_GLr) | SPRR_PERM(PERM_RW, ELrw_GLrw) | \
+ SPRR_PERM(PERM_RX, ELrx_GLrx) | SPRR_PERM(PERM_RWX, ELrw_GLrw)
+
+/*
+ * aarch64 allows to configure attribute sets for up to eight different memory
+ * types. we need normal memory and two types of device memory (nGnRnE and
+ * nGnRE) in m1n1.
+ * The indexes here are selected arbitrarily: A page table entry
+ * contains a field to select one of these which will then be used
+ * to select the corresponding memory access flags from MAIR.
+ */
+
+#define MAIR_SHIFT_NORMAL (MAIR_IDX_NORMAL * 8)
+#define MAIR_SHIFT_NORMAL_NC (MAIR_IDX_NORMAL_NC * 8)
+#define MAIR_SHIFT_DEVICE_nGnRnE (MAIR_IDX_DEVICE_nGnRnE * 8)
+#define MAIR_SHIFT_DEVICE_nGnRE (MAIR_IDX_DEVICE_nGnRE * 8)
+#define MAIR_SHIFT_DEVICE_nGRE (MAIR_IDX_DEVICE_nGRE * 8)
+#define MAIR_SHIFT_DEVICE_GRE (MAIR_IDX_DEVICE_GRE * 8)
+
+/*
+ * https://developer.arm.com/documentation/ddi0500/e/system-control/aarch64-register-descriptions/memory-attribute-indirection-register--el1
+ *
+ * MAIR_ATTR_NORMAL_DEFAULT sets Normal Memory, Outer Write-back non-transient,
+ * Inner Write-back non-transient, R=1, W=1
+ * MAIR_ATTR_DEVICE_nGnRnE sets Device-nGnRnE memory
+ * MAIR_ATTR_DEVICE_nGnRE sets Device-nGnRE memory
+ */
+#define MAIR_ATTR_NORMAL_DEFAULT 0xffUL
+#define MAIR_ATTR_NORMAL_NC 0x44UL
+#define MAIR_ATTR_DEVICE_nGnRnE 0x00UL
+#define MAIR_ATTR_DEVICE_nGnRE 0x04UL
+#define MAIR_ATTR_DEVICE_nGRE 0x08UL
+#define MAIR_ATTR_DEVICE_GRE 0x0cUL
+
+static u64 *mmu_pt_L0;
+static u64 *mmu_pt_L1;
+
+static u64 *mmu_pt_get_l2(u64 from)
+{
+ u64 l1idx = from >> VADDR_L1_OFFSET_BITS;
+ assert(l1idx < ENTRIES_PER_L1_TABLE);
+ u64 l1d = mmu_pt_L1[l1idx];
+
+ if (L1_IS_TABLE(l1d))
+ return (u64 *)(l1d & PTE_TARGET_MASK);
+
+ u64 *l2 = (u64 *)memalign(PAGE_SIZE, ENTRIES_PER_L2_TABLE * sizeof(u64));
+ assert(!IS_PTE(l1d));
+ memset64(l2, 0, ENTRIES_PER_L2_TABLE * sizeof(u64));
+
+ l1d = ((u64)l2) | FIELD_PREP(PTE_TYPE, PTE_TABLE) | PTE_VALID;
+ mmu_pt_L1[l1idx] = l1d;
+ return l2;
+}
+
+static void mmu_pt_map_l2(u64 from, u64 to, u64 size)
+{
+ assert((from & MASK(VADDR_L2_OFFSET_BITS)) == 0);
+ assert((to & PTE_TARGET_MASK & MASK(VADDR_L2_OFFSET_BITS)) == 0);
+ assert((size & MASK(VADDR_L2_OFFSET_BITS)) == 0);
+
+ to |= FIELD_PREP(PTE_TYPE, PTE_BLOCK);
+
+ for (; size; size -= BIT(VADDR_L2_OFFSET_BITS)) {
+ u64 idx = (from >> VADDR_L2_OFFSET_BITS) & MASK(VADDR_L2_INDEX_BITS);
+ u64 *l2 = mmu_pt_get_l2(from);
+
+ if (L2_IS_TABLE(l2[idx]))
+ free((void *)(l2[idx] & PTE_TARGET_MASK));
+
+ l2[idx] = to;
+ from += BIT(VADDR_L2_OFFSET_BITS);
+ to += BIT(VADDR_L2_OFFSET_BITS);
+ }
+}
+
+static u64 *mmu_pt_get_l3(u64 from)
+{
+ u64 *l2 = mmu_pt_get_l2(from);
+ u64 l2idx = (from >> VADDR_L2_OFFSET_BITS) & MASK(VADDR_L2_INDEX_BITS);
+ assert(l2idx < ENTRIES_PER_L2_TABLE);
+ u64 l2d = l2[l2idx];
+
+ if (L2_IS_TABLE(l2d))
+ return (u64 *)(l2d & PTE_TARGET_MASK);
+
+ u64 *l3 = (u64 *)memalign(PAGE_SIZE, ENTRIES_PER_L3_TABLE * sizeof(u64));
+ if (IS_PTE(l2d)) {
+ u64 l3d = l2d;
+ l3d &= ~PTE_TYPE;
+ l3d |= FIELD_PREP(PTE_TYPE, PTE_PAGE);
+ for (u64 idx = 0; idx < ENTRIES_PER_L3_TABLE; idx++, l3d += BIT(VADDR_L3_OFFSET_BITS))
+ l3[idx] = l3d;
+ } else {
+ memset64(l3, 0, ENTRIES_PER_L3_TABLE * sizeof(u64));
+ }
+
+ l2d = ((u64)l3) | FIELD_PREP(PTE_TYPE, PTE_TABLE) | PTE_VALID;
+ l2[l2idx] = l2d;
+ return l3;
+}
+
+static void mmu_pt_map_l3(u64 from, u64 to, u64 size)
+{
+ assert((from & MASK(VADDR_L3_OFFSET_BITS)) == 0);
+ assert((to & PTE_TARGET_MASK & MASK(VADDR_L3_OFFSET_BITS)) == 0);
+ assert((size & MASK(VADDR_L3_OFFSET_BITS)) == 0);
+
+ to |= FIELD_PREP(PTE_TYPE, PTE_PAGE);
+
+ for (; size; size -= BIT(VADDR_L3_OFFSET_BITS)) {
+ u64 idx = (from >> VADDR_L3_OFFSET_BITS) & MASK(VADDR_L3_INDEX_BITS);
+ u64 *l3 = mmu_pt_get_l3(from);
+
+ l3[idx] = to;
+ from += BIT(VADDR_L3_OFFSET_BITS);
+ to += BIT(VADDR_L3_OFFSET_BITS);
+ }
+}
+
+int mmu_map(u64 from, u64 to, u64 size)
+{
+ u64 chunk;
+ if (from & MASK(VADDR_L3_OFFSET_BITS) || size & MASK(VADDR_L3_OFFSET_BITS))
+ return -1;
+
+ // L3 mappings to boundary
+ u64 boundary = ALIGN_UP(from, MASK(VADDR_L2_OFFSET_BITS));
+ // CPU CTRR doesn't like L2 mappings crossing CTRR boundaries!
+ // Map everything below the m1n1 base as L3
+ if (boundary >= ram_base && boundary < (u64)_base)
+ boundary = ALIGN_UP((u64)_base, MASK(VADDR_L2_OFFSET_BITS));
+
+ chunk = min(size, boundary - from);
+ if (chunk) {
+ mmu_pt_map_l3(from, to, chunk);
+ from += chunk;
+ to += chunk;
+ size -= chunk;
+ }
+
+ // L2 mappings
+ chunk = ALIGN_DOWN(size, MASK(VADDR_L2_OFFSET_BITS));
+ if (chunk && (to & VADDR_L2_ALIGN_MASK) == 0) {
+ mmu_pt_map_l2(from, to, chunk);
+ from += chunk;
+ to += chunk;
+ size -= chunk;
+ }
+
+ // L3 mappings to end
+ if (size) {
+ mmu_pt_map_l3(from, to, size);
+ }
+
+ return 0;
+}
+
+static u64 mmu_make_table_pte(u64 *addr)
+{
+ u64 pte = FIELD_PREP(PTE_TYPE, PTE_TABLE) | PTE_VALID;
+ pte |= (uintptr_t)addr;
+ pte |= PTE_ACCESS;
+ return pte;
+}
+
+static void mmu_init_pagetables(void)
+{
+ mmu_pt_L0 = memalign(PAGE_SIZE, sizeof(u64) * 2);
+ mmu_pt_L1 = memalign(PAGE_SIZE, sizeof(u64) * ENTRIES_PER_L1_TABLE);
+
+ memset64(mmu_pt_L0, 0, sizeof(u64) * 2);
+ memset64(mmu_pt_L1, 0, sizeof(u64) * ENTRIES_PER_L1_TABLE);
+
+ mmu_pt_L0[0] = mmu_make_table_pte(&mmu_pt_L1[0]);
+ mmu_pt_L0[1] = mmu_make_table_pte(&mmu_pt_L1[ENTRIES_PER_L1_TABLE >> 1]);
+}
+
+void mmu_add_mapping(u64 from, u64 to, size_t size, u8 attribute_index, u64 perms)
+{
+ if (mmu_map(from,
+ to | PTE_MAIR_IDX(attribute_index) | PTE_ACCESS | PTE_VALID | PTE_SH_OS | perms,
+ size) < 0)
+ panic("Failed to add MMU mapping 0x%lx -> 0x%lx (0x%lx)\n", from, to, size);
+
+ sysop("dsb ishst");
+ sysop("tlbi vmalle1is");
+ sysop("dsb ish");
+ sysop("isb");
+}
+
+void mmu_rm_mapping(u64 from, size_t size)
+{
+ if (mmu_map(from, 0, size) < 0)
+ panic("Failed to rm MMU mapping at 0x%lx (0x%lx)\n", from, size);
+}
+
+static void mmu_map_mmio(void)
+{
+ int node = adt_path_offset(adt, "/arm-io");
+ if (node < 0) {
+ printf("MMU: ARM-IO node not found!\n");
+ return;
+ }
+ u32 ranges_len;
+ const u32 *ranges = adt_getprop(adt, node, "ranges", &ranges_len);
+ if (!ranges) {
+ printf("MMU: Failed to get ranges property!\n");
+ return;
+ }
+ // Assume all cell counts are 2 (64bit)
+ int range_cnt = ranges_len / 24;
+ while (range_cnt--) {
+ u64 bus = ranges[2] | ((u64)ranges[3] << 32);
+ u64 size = ranges[4] | ((u64)ranges[5] << 32);
+
+ mmu_add_mapping(bus, bus, size, MAIR_IDX_DEVICE_nGnRnE, PERM_RW_EL0);
+
+ ranges += 6;
+ }
+}
+
+static void mmu_remap_ranges(void)
+{
+
+ int node = adt_path_offset(adt, "/defaults");
+ if (node < 0) {
+ printf("MMU: defaults node not found!\n");
+ return;
+ }
+ u32 ranges_len;
+ const u32 *ranges = adt_getprop(adt, node, "pmap-io-ranges", &ranges_len);
+ if (!ranges) {
+ printf("MMU: Failed to get pmap-io-ranges property!\n");
+ return;
+ }
+ int range_cnt = ranges_len / 24;
+ while (range_cnt--) {
+ u64 addr = ranges[0] | ((u64)ranges[1] << 32);
+ u64 size = ranges[2] | ((u64)ranges[3] << 32);
+ u32 flags = ranges[4];
+
+ // TODO: is this the right logic?
+ if ((flags >> 28) == 8) {
+ printf("MMU: Adding Device-nGnRE mapping at 0x%lx (0x%lx)\n", addr, size);
+ mmu_add_mapping(addr, addr, size, MAIR_IDX_DEVICE_nGnRE, PERM_RW_EL0);
+ } else if (flags == 0x60004016) {
+ printf("MMU: Adding Normal-NC mapping at 0x%lx (0x%lx)\n", addr, size);
+ mmu_add_mapping(addr, addr, size, MAIR_IDX_NORMAL_NC, PERM_RW_EL0);
+ }
+
+ ranges += 6;
+ }
+}
+
+void mmu_map_framebuffer(u64 addr, size_t size)
+{
+ printf("MMU: Adding Normal-NC mapping at 0x%lx (0x%zx) for framebuffer\n", addr, size);
+ dc_civac_range((void *)addr, size);
+ mmu_add_mapping(addr, addr, size, MAIR_IDX_NORMAL_NC, PERM_RW_EL0);
+}
+
+static void mmu_add_default_mappings(void)
+{
+ ram_base = ALIGN_DOWN(cur_boot_args.phys_base, BIT(32));
+ uint64_t ram_size = cur_boot_args.mem_size + cur_boot_args.phys_base - ram_base;
+ ram_size = ALIGN_DOWN(ram_size, 0x4000);
+
+ printf("MMU: RAM base: 0x%lx\n", ram_base);
+ printf("MMU: Top of normal RAM: 0x%lx\n", ram_base + ram_size);
+
+ mmu_map_mmio();
+
+ /*
+ * Create identity mapping for RAM from 0x08_0000_0000
+ * With SPRR enabled, this becomes RW.
+ * This range includes all real RAM, including carveouts
+ */
+ mmu_add_mapping(ram_base, ram_base, cur_boot_args.mem_size_actual, MAIR_IDX_NORMAL, PERM_RWX);
+
+ /* Unmap carveout regions */
+ mcc_unmap_carveouts();
+
+ /*
+ * Remap m1n1 executable code as RX.
+ */
+ mmu_add_mapping((u64)_base, (u64)_base, (u64)_rodata_end - (u64)_base, MAIR_IDX_NORMAL,
+ PERM_RX_EL0);
+
+ /*
+ * Make guard page at the end of the main stack
+ */
+ mmu_rm_mapping((u64)_stack_top, PAGE_SIZE);
+
+ /*
+ * Create mapping for RAM from 0x88_0000_0000,
+ * read/writable/exec by EL0 (but not executable by EL1)
+ * With SPRR enabled, this becomes RX_EL0.
+ */
+ mmu_add_mapping(ram_base | REGION_RWX_EL0, ram_base, ram_size, MAIR_IDX_NORMAL, PERM_RWX_EL0);
+ /*
+ * Create mapping for RAM from 0x98_0000_0000,
+ * read/writable by EL0 (but not executable by EL1)
+ * With SPRR enabled, this becomes RW_EL0.
+ */
+ mmu_add_mapping(ram_base | REGION_RW_EL0, ram_base, ram_size, MAIR_IDX_NORMAL, PERM_RW_EL0);
+ /*
+ * Create mapping for RAM from 0xa8_0000_0000,
+ * read/executable by EL1
+ * This allows executing from dynamic regions in EL1
+ */
+ mmu_add_mapping(ram_base | REGION_RX_EL1, ram_base, ram_size, MAIR_IDX_NORMAL, PERM_RX_EL0);
+
+ /*
+ * Create four seperate full mappings of MMIO space, with different access types
+ */
+ mmu_add_mapping(0xc000000000, 0x0000000000, 0x0800000000, MAIR_IDX_DEVICE_GRE, PERM_RW_EL0);
+ mmu_add_mapping(0xd000000000, 0x0000000000, 0x0800000000, MAIR_IDX_DEVICE_nGRE, PERM_RW_EL0);
+ mmu_add_mapping(0xe000000000, 0x0000000000, 0x0800000000, MAIR_IDX_DEVICE_nGnRnE, PERM_RW_EL0);
+ mmu_add_mapping(0xf000000000, 0x0000000000, 0x0800000000, MAIR_IDX_DEVICE_nGnRE, PERM_RW_EL0);
+
+ /*
+ * Handle pmap-ranges
+ */
+ mmu_remap_ranges();
+}
+
+static void mmu_configure(void)
+{
+ msr(MAIR_EL1, (MAIR_ATTR_NORMAL_DEFAULT << MAIR_SHIFT_NORMAL) |
+ (MAIR_ATTR_DEVICE_nGnRnE << MAIR_SHIFT_DEVICE_nGnRnE) |
+ (MAIR_ATTR_DEVICE_nGnRE << MAIR_SHIFT_DEVICE_nGnRE) |
+ (MAIR_ATTR_NORMAL_NC << MAIR_SHIFT_NORMAL_NC));
+ msr(TCR_EL1, FIELD_PREP(TCR_IPS, TCR_IPS_4TB) | FIELD_PREP(TCR_TG1, TCR_TG1_16K) |
+ FIELD_PREP(TCR_SH1, TCR_SH1_IS) | FIELD_PREP(TCR_ORGN1, TCR_ORGN1_WBWA) |
+ FIELD_PREP(TCR_IRGN1, TCR_IRGN1_WBWA) | FIELD_PREP(TCR_T1SZ, TCR_T1SZ_48BIT) |
+ FIELD_PREP(TCR_TG0, TCR_TG0_16K) | FIELD_PREP(TCR_SH0, TCR_SH0_IS) |
+ FIELD_PREP(TCR_ORGN0, TCR_ORGN0_WBWA) | FIELD_PREP(TCR_IRGN0, TCR_IRGN0_WBWA) |
+ FIELD_PREP(TCR_T0SZ, TCR_T0SZ_48BIT));
+
+ msr(TTBR0_EL1, (uintptr_t)mmu_pt_L0);
+ msr(TTBR1_EL1, (uintptr_t)mmu_pt_L0);
+
+ // Armv8-A Address Translation, 100940_0101_en, page 28
+ sysop("dsb ishst");
+ sysop("tlbi vmalle1is");
+ sysop("dsb ish");
+ sysop("isb");
+}
+
+static void mmu_init_sprr(void)
+{
+ msr_sync(SYS_IMP_APL_SPRR_CONFIG_EL1, 1);
+ msr_sync(SYS_IMP_APL_SPRR_PERM_EL0, SPRR_DEFAULT_PERM_EL0);
+ msr_sync(SYS_IMP_APL_SPRR_PERM_EL1, SPRR_DEFAULT_PERM_EL1);
+ msr_sync(SYS_IMP_APL_SPRR_CONFIG_EL1, 0);
+}
+
+void mmu_init(void)
+{
+ printf("MMU: Initializing...\n");
+
+ if (read_sctlr() & SCTLR_M) {
+ printf("MMU: already intialized.\n");
+ return;
+ }
+
+ mmu_init_pagetables();
+ mmu_add_default_mappings();
+ mmu_configure();
+ mmu_init_sprr();
+
+ // Enable EL0 memory access by EL1
+ msr(PAN, 0);
+
+ // RES1 bits
+ u64 sctlr = SCTLR_LSMAOE | SCTLR_nTLSMD | SCTLR_TSCXT | SCTLR_ITD;
+ // Configure translation
+ sctlr |= SCTLR_I | SCTLR_C | SCTLR_M | SCTLR_SPAN;
+
+ printf("MMU: SCTLR_EL1: %lx -> %lx\n", mrs(SCTLR_EL1), sctlr);
+ write_sctlr(sctlr);
+ printf("MMU: running with MMU and caches enabled!\n");
+}
+
+static void mmu_secondary_setup(void)
+{
+ mmu_configure();
+ mmu_init_sprr();
+
+ // Enable EL0 memory access by EL1
+ msr(PAN, 0);
+
+ // RES1 bits
+ u64 sctlr = SCTLR_LSMAOE | SCTLR_nTLSMD | SCTLR_TSCXT | SCTLR_ITD;
+ // Configure translation
+ sctlr |= SCTLR_I | SCTLR_C | SCTLR_M | SCTLR_SPAN;
+ write_sctlr(sctlr);
+}
+
+void mmu_init_secondary(int cpu)
+{
+ smp_call4(cpu, mmu_secondary_setup, 0, 0, 0, 0);
+ smp_wait(cpu);
+}
+
+void mmu_shutdown(void)
+{
+ fb_console_reserve_lines(3);
+ printf("MMU: shutting down...\n");
+ write_sctlr(read_sctlr() & ~(SCTLR_I | SCTLR_C | SCTLR_M));
+ printf("MMU: shutdown successful, clearing caches\n");
+ dcsw_op_all(DCSW_OP_DCCISW);
+}
+
+u64 mmu_disable(void)
+{
+ u64 sctlr_old = read_sctlr();
+ if (!(sctlr_old & SCTLR_M))
+ return sctlr_old;
+
+ write_sctlr(sctlr_old & ~(SCTLR_I | SCTLR_C | SCTLR_M));
+ dcsw_op_all(DCSW_OP_DCCISW);
+
+ return sctlr_old;
+}
+
+void mmu_restore(u64 state)
+{
+ write_sctlr(state);
+}
diff --git a/tools/src/memory.h b/tools/src/memory.h
new file mode 100644
index 0000000..247a5d3
--- /dev/null
+++ b/tools/src/memory.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef MEMORY_H
+#define MEMORY_H
+
+#include "cpu_regs.h"
+#include "types.h"
+
+#define REGION_RWX_EL0 0x80000000000
+#define REGION_RW_EL0 0xa0000000000
+#define REGION_RX_EL1 0xc0000000000
+
+/*
+ * https://armv8-ref.codingbelief.com/en/chapter_d4/d43_2_armv8_translation_table_level_3_descriptor_formats.html
+ * PTE_TYPE:PTE_BLOCK indicates that the page table entry (PTE) points to a physical memory block
+ * PTE_TYPE:PTE_TABLE indicates that the PTE points to another PTE
+ * PTE_TYPE:PTE_PAGE indicates that the PTE points to a single page
+ * PTE_FLAG_ACCESS is required to allow access to the memory region
+ * PTE_MAIR_IDX sets the MAIR index to be used for this PTE
+ */
+#define PTE_VALID BIT(0)
+#define PTE_TYPE BIT(1)
+#define PTE_BLOCK 0
+#define PTE_TABLE 1
+#define PTE_PAGE 1
+#define PTE_ACCESS BIT(10)
+#define PTE_MAIR_IDX(i) ((i & 7) << 2)
+#define PTE_PXN BIT(53)
+#define PTE_UXN BIT(54)
+#define PTE_AP_RO BIT(7)
+#define PTE_AP_EL0 BIT(6)
+#define PTE_SH_NS (0b00 << 8)
+#define PTE_SH_OS (0b10 << 8)
+#define PTE_SH_IS (0b11 << 8)
+
+#define PERM_RO_EL0 PTE_AP_EL0 | PTE_AP_RO | PTE_PXN | PTE_UXN
+#define PERM_RW_EL0 PTE_AP_EL0 | PTE_PXN | PTE_UXN
+#define PERM_RX_EL0 PTE_AP_EL0 | PTE_AP_RO
+#define PERM_RWX_EL0 PTE_AP_EL0
+
+#define PERM_RO PTE_AP_RO | PTE_PXN | PTE_UXN
+#define PERM_RW PTE_PXN | PTE_UXN
+#define PERM_RX PTE_AP_RO | PTE_UXN
+#define PERM_RWX 0
+
+#define MAIR_IDX_NORMAL 0
+#define MAIR_IDX_NORMAL_NC 1
+#define MAIR_IDX_DEVICE_nGnRnE 2
+#define MAIR_IDX_DEVICE_nGnRE 3
+#define MAIR_IDX_DEVICE_nGRE 4
+#define MAIR_IDX_DEVICE_GRE 5
+
+#ifndef __ASSEMBLER__
+
+#include "utils.h"
+
+extern uint64_t ram_base;
+
+void ic_ivau_range(void *addr, size_t length);
+void dc_ivac_range(void *addr, size_t length);
+void dc_zva_range(void *addr, size_t length);
+void dc_cvac_range(void *addr, size_t length);
+void dc_cvau_range(void *addr, size_t length);
+void dc_civac_range(void *addr, size_t length);
+
+#define DCSW_OP_DCISW 0x0
+#define DCSW_OP_DCCISW 0x1
+#define DCSW_OP_DCCSW 0x2
+void dcsw_op_all(u64 op_type);
+
+void mmu_init(void);
+void mmu_init_secondary(int cpu);
+void mmu_shutdown(void);
+void mmu_add_mapping(u64 from, u64 to, size_t size, u8 attribute_index, u64 perms);
+void mmu_rm_mapping(u64 from, size_t size);
+void mmu_map_framebuffer(u64 addr, size_t size);
+
+u64 mmu_disable(void);
+void mmu_restore(u64 state);
+
+static inline bool mmu_active(void)
+{
+ return mrs(SCTLR_EL1) & SCTLR_M;
+}
+
+#endif
+
+#endif
diff --git a/tools/src/memory_asm.S b/tools/src/memory_asm.S
new file mode 100644
index 0000000..2c2c778
--- /dev/null
+++ b/tools/src/memory_asm.S
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#define LOC_SHIFT 24
+#define CLIDR_FIELD_WIDTH 3
+#define LEVEL_SHIFT 1
+
+.macro func, name
+.globl \name
+.type \name, @function
+\name:
+.endm
+
+ .globl dcsw_op_all
+
+/*
+ * This macro can be used for implementing various data cache operations `op`
+ */
+.macro do_dcache_maintenance_by_mva op
+ /* Exit early if size is zero */
+ cbz x1, exit_loop_\op
+ dcache_line_size x2, x3
+ add x1, x0, x1
+ sub x3, x2, #1
+ bic x0, x0, x3
+loop_\op:
+ dc \op, x0
+ add x0, x0, x2
+ cmp x0, x1
+ b.lo loop_\op
+ dsb sy
+exit_loop_\op:
+ ret
+.endm
+
+ /* ---------------------------------------------------------------
+ * Data cache operations by set/way to the level specified
+ *
+ * The main function, do_dcsw_op requires:
+ * x0: The operation type (0-2), as defined in arch.h
+ * x3: The last cache level to operate on
+ * x9: clidr_el1
+ * x10: The cache level to begin operation from
+ * and will carry out the operation on each data cache from level 0
+ * to the level in x3 in sequence
+ *
+ * The dcsw_op macro sets up the x3 and x9 parameters based on
+ * clidr_el1 cache information before invoking the main function
+ * ---------------------------------------------------------------
+ */
+
+ .macro dcsw_op shift, fw, ls
+ mrs x9, clidr_el1
+ ubfx x3, x9, \shift, \fw
+ lsl x3, x3, \ls
+ mov x10, xzr
+ b do_dcsw_op
+ .endm
+
+func do_dcsw_op
+ cbz x3, exit
+ adr x14, dcsw_loop_table // compute inner loop address
+ add x14, x14, x0, lsl #5 // inner loop is 8x32-bit instructions
+ mov x0, x9
+ mov w8, #1
+loop1:
+ add x2, x10, x10, lsr #1 // work out 3x current cache level
+ lsr x1, x0, x2 // extract cache type bits from clidr
+ and x1, x1, #7 // mask the bits for current cache only
+ cmp x1, #2 // see what cache we have at this level
+ b.lo level_done // nothing to do if no cache or icache
+
+ msr csselr_el1, x10 // select current cache level in csselr
+ isb // isb to sych the new cssr&csidr
+ mrs x1, ccsidr_el1 // read the new ccsidr
+ and x2, x1, #7 // extract the length of the cache lines
+ add x2, x2, #4 // add 4 (line length offset)
+ ubfx x4, x1, #3, #10 // maximum way number
+ clz w5, w4 // bit position of way size increment
+ lsl w9, w4, w5 // w9 = aligned max way number
+ lsl w16, w8, w5 // w16 = way number loop decrement
+ orr w9, w10, w9 // w9 = combine way and cache number
+ ubfx w6, w1, #13, #15 // w6 = max set number
+ lsl w17, w8, w2 // w17 = set number loop decrement
+ dsb sy // barrier before we start this level
+ br x14 // jump to DC operation specific loop
+
+ .macro dcsw_loop _op
+loop2_\_op:
+ lsl w7, w6, w2 // w7 = aligned max set number
+
+loop3_\_op:
+ orr w11, w9, w7 // combine cache, way and set number
+ dc \_op, x11
+ subs w7, w7, w17 // decrement set number
+ b.hs loop3_\_op
+
+ subs x9, x9, x16 // decrement way number
+ b.hs loop2_\_op
+
+ b level_done
+ .endm
+
+level_done:
+ add x10, x10, #2 // increment cache number
+ cmp x3, x10
+ b.hi loop1
+ msr csselr_el1, xzr // select cache level 0 in csselr
+ dsb sy // barrier to complete final cache operation
+ isb
+exit:
+ ret
+
+dcsw_loop_table:
+ dcsw_loop isw
+ dcsw_loop cisw
+ dcsw_loop csw
+
+
+func dcsw_op_all
+ dcsw_op #LOC_SHIFT, #CLIDR_FIELD_WIDTH, #LEVEL_SHIFT
+
+ /* ---------------------------------------------------------------
+ * Helper macro for data cache operations by set/way for the
+ * level specified
+ * ---------------------------------------------------------------
+ */
+ .macro dcsw_op_level level
+ mrs x9, clidr_el1
+ mov x3, \level
+ sub x10, x3, #2
+ b do_dcsw_op
+ .endm
+
+ /* ---------------------------------------------------------------
+ * Data cache operations by set/way for level 1 cache
+ *
+ * The main function, do_dcsw_op requires:
+ * x0: The operation type (0-2), as defined in arch.h
+ * ---------------------------------------------------------------
+ */
+func dcsw_op_level1
+ dcsw_op_level #(1 << LEVEL_SHIFT)
+
+ /* ---------------------------------------------------------------
+ * Data cache operations by set/way for level 2 cache
+ *
+ * The main function, do_dcsw_op requires:
+ * x0: The operation type (0-2), as defined in arch.h
+ * ---------------------------------------------------------------
+ */
+func dcsw_op_level2
+ dcsw_op_level #(2 << LEVEL_SHIFT)
+
+ /* ---------------------------------------------------------------
+ * Data cache operations by set/way for level 3 cache
+ *
+ * The main function, do_dcsw_op requires:
+ * x0: The operation type (0-2), as defined in arch.h
+ * ---------------------------------------------------------------
+ */
+func dcsw_op_level3
+ dcsw_op_level #(3 << LEVEL_SHIFT)
diff --git a/tools/src/minilzlib/dictbuf.c b/tools/src/minilzlib/dictbuf.c
new file mode 100644
index 0000000..02875dc
--- /dev/null
+++ b/tools/src/minilzlib/dictbuf.c
@@ -0,0 +1,155 @@
+/*++
+
+Copyright (c) Alex Ionescu. All rights reserved.
+
+Module Name:
+
+ dictbuf.c
+
+Abstract:
+
+ This module implements the management of the LZMA "history buffer" which is
+ often called the "dictionary". Routines for writing into the history buffer
+ as well as for reading back from it are implemented, as well as mechanisms
+ for repeating previous symbols forward into the dictionary. This forms the
+ basis for LZMA match distance-length pairs that are found and decompressed.
+ Note that for simplicity's sake, the dictionary is stored directly in the
+ output buffer, such that no "flushing" or copying is needed back and forth.
+
+Author:
+
+ Alex Ionescu (@aionescu) 15-Apr-2020 - Initial version
+
+Environment:
+
+ Windows & Linux, user mode and kernel mode.
+
+--*/
+
+#include "minlzlib.h"
+
+//
+// State used for the history buffer (dictionary)
+//
+typedef struct _DICTIONARY_STATE
+{
+ //
+ // Buffer, start position, current position, and offset limit in the buffer
+ //
+ uint8_t* Buffer;
+ uint32_t BufferSize;
+ uint32_t Start;
+ uint32_t Offset;
+ uint32_t Limit;
+} DICTIONARY_STATE, *PDICTIONARY_STATE;
+DICTIONARY_STATE Dictionary;
+
+void
+DtInitialize (
+ uint8_t* HistoryBuffer,
+ uint32_t Size
+ )
+{
+ //
+ // Initialize the buffer and reset the position
+ //
+ Dictionary.Buffer = HistoryBuffer;
+ Dictionary.Offset = 0;
+ Dictionary.BufferSize = Size;
+}
+
+bool
+DtSetLimit (
+ uint32_t Limit
+ )
+{
+ //
+ // Make sure that the passed in dictionary limit fits within the size, and
+ // then set this as the new limit. Save the starting point (current offset)
+ //
+ if ((Dictionary.Offset + Limit) > Dictionary.BufferSize)
+ {
+ return false;
+ }
+ Dictionary.Limit = Dictionary.Offset + Limit;
+ Dictionary.Start = Dictionary.Offset;
+ return true;
+}
+
+bool
+DtIsComplete (
+ uint32_t* BytesProcessed
+ )
+{
+ //
+ // Return bytes processed and if the dictionary has been fully written to
+ //
+ *BytesProcessed = Dictionary.Offset - Dictionary.Start;
+ return (Dictionary.Offset == Dictionary.Limit);
+}
+
+bool
+DtCanWrite (
+ uint32_t* Position
+ )
+{
+ //
+ // Return our position and make sure it's not beyond the uncompressed size
+ //
+ *Position = Dictionary.Offset;
+ return (Dictionary.Offset < Dictionary.Limit);
+}
+
+uint8_t
+DtGetSymbol (
+ uint32_t Distance
+ )
+{
+ //
+ // If the dictionary is still empty, just return 0, otherwise, return the
+ // symbol that is Distance bytes backward.
+ //
+ if (Distance > Dictionary.Offset)
+ {
+ return 0;
+ }
+ return Dictionary.Buffer[Dictionary.Offset - Distance];
+}
+
+void
+DtPutSymbol (
+ uint8_t Symbol
+ )
+{
+ //
+ // Write the symbol and advance our position
+ //
+ Dictionary.Buffer[Dictionary.Offset++] = Symbol;
+}
+
+bool
+DtRepeatSymbol (
+ uint32_t Length,
+ uint32_t Distance
+ )
+{
+ //
+ // Make sure we never get asked to write past the end of the dictionary. We
+ // should also not allow the distance to go beyond the current offset since
+ // DtGetSymbol will return 0 thinking the dictionary is empty.
+ //
+ if (((Length + Dictionary.Offset) > Dictionary.Limit) ||
+ (Distance > Dictionary.Offset))
+ {
+ return false;
+ }
+
+ //
+ // Now rewrite the stream of past symbols forward into the dictionary.
+ //
+ do
+ {
+ DtPutSymbol(DtGetSymbol(Distance));
+ } while (--Length > 0);
+ return true;
+}
diff --git a/tools/src/minilzlib/inputbuf.c b/tools/src/minilzlib/inputbuf.c
new file mode 100644
index 0000000..67d652c
--- /dev/null
+++ b/tools/src/minilzlib/inputbuf.c
@@ -0,0 +1,144 @@
+/*++
+
+Copyright (c) Alex Ionescu. All rights reserved.
+
+Module Name:
+
+ inputbuf.c
+
+Abstract:
+
+ This module implements helper functions for managing the input buffer that
+ contains arithmetic-coded LZ77 match distance-length pairs and raw literals
+ Both seeking (such that an external reader can refer to multiple bytes) and
+ reading (capturing) an individual byte are supported. Support for aligning
+ input data to 4 bytes (which is a requirement for XZ-encoded files) is also
+ implemented.
+
+Author:
+
+ Alex Ionescu (@aionescu) 15-Apr-2020 - Initial version
+
+Environment:
+
+ Windows & Linux, user mode and kernel mode.
+
+--*/
+
+#include "minlzlib.h"
+
+//
+// Input Buffer State
+//
+typedef struct _BUFFER_STATE
+{
+ //
+ // Start of the buffer, current offset, current packet end, and total input size
+ //
+ uint8_t* Buffer;
+ uint32_t Offset;
+ uint32_t SoftLimit;
+ uint32_t Size;
+} BUFFER_STATE, * PBUFFER_STATE;
+BUFFER_STATE In;
+
+bool
+BfAlign (
+ void
+ )
+{
+ uint8_t padByte;
+ //
+ // Keep reading until we reach 32-bit alignment. All bytes must be zero.
+ //
+ while (In.Offset & 3)
+ {
+ if (!BfRead(&padByte) || (padByte != 0))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool
+BfSetSoftLimit (
+ uint32_t Remaining
+ )
+{
+ if ((In.Size - In.Offset) < Remaining)
+ {
+ return false;
+ }
+ In.SoftLimit = In.Offset + Remaining;
+ return true;
+}
+
+void
+BfResetSoftLimit (
+ void
+ )
+{
+ In.SoftLimit = In.Size;
+}
+
+bool
+BfSeek (
+ uint32_t Length,
+ uint8_t** Bytes
+ )
+{
+ //
+ // Make sure the input buffer has enough space to seek the desired size, if
+ // it does, return the current position and then seek past the desired size
+ //
+ if ((In.Offset + Length) > In.SoftLimit)
+ {
+ *Bytes = 0;
+ return false;
+ }
+ *Bytes = &In.Buffer[In.Offset];
+ In.Offset += Length;
+ return true;
+}
+
+uint32_t
+BfTell (
+ void
+ )
+{
+ return In.Offset;
+}
+
+bool
+BfRead (
+ uint8_t* Byte
+ )
+{
+ uint8_t* pByte;
+ //
+ // Seek past the byte and read it
+ //
+ if (!BfSeek(sizeof(*Byte), &pByte))
+ {
+ *Byte = 0;
+ return false;
+ }
+ *Byte = *pByte;
+ return true;
+}
+
+void
+BfInitialize (
+ uint8_t* InputBuffer,
+ uint32_t InputSize
+ )
+{
+ //
+ // Save all the data in the context buffer state
+ //
+ In.Buffer = InputBuffer;
+ In.Size = InputSize;
+ In.SoftLimit = InputSize;
+ In.Offset = 0;
+}
diff --git a/tools/src/minilzlib/lzma2dec.c b/tools/src/minilzlib/lzma2dec.c
new file mode 100644
index 0000000..7a15513
--- /dev/null
+++ b/tools/src/minilzlib/lzma2dec.c
@@ -0,0 +1,228 @@
+/*++
+
+Copyright (c) Alex Ionescu. All rights reserved.
+
+Module Name:
+
+ lzma2dec.c
+
+Abstract:
+
+ This module implements the LZMA2 decoding logic responsible for parsing the
+ LZMA2 Control Byte, the Information Bytes (Compressed & Uncompressed Stream
+ Size), and the Property Byte during the initial Dictionary Reset. Note that
+ this module only implements support for a single such reset (i.e.: archives
+ in "solid" mode).
+
+Author:
+
+ Alex Ionescu (@aionescu) 15-Apr-2020 - Initial version
+
+Environment:
+
+ Windows & Linux, user mode and kernel mode.
+
+--*/
+
+#include "minlzlib.h"
+#include "lzma2dec.h"
+
+bool
+Lz2DecodeChunk (
+ uint32_t* BytesProcessed,
+ uint32_t RawSize,
+ uint16_t CompressedSize
+ )
+{
+ uint32_t bytesProcessed;
+
+ //
+ // Go and decode this chunk, sequence by sequence
+ //
+ if (!LzDecode())
+ {
+ return false;
+ }
+
+ //
+ // In a correctly formatted stream, the last arithmetic-coded sequence must
+ // be zero once we finished with the last chunk. Make sure the stream ended
+ // exactly where we expected it to.
+ //
+ if (!RcIsComplete(&bytesProcessed) || (bytesProcessed != CompressedSize))
+ {
+ return false;
+ }
+
+ //
+ // The entire output stream must have been written to, and the dictionary
+ // must be full now.
+ //
+ if (!DtIsComplete(&bytesProcessed) || (bytesProcessed != RawSize))
+ {
+ return false;
+ }
+ *BytesProcessed += bytesProcessed;
+ return true;
+}
+
+bool
+Lz2DecodeStream (
+ uint32_t* BytesProcessed,
+ bool GetSizeOnly
+ )
+{
+ uint8_t* inBytes;
+ LZMA2_CONTROL_BYTE controlByte;
+ uint8_t propertyByte;
+ uint32_t rawSize;
+ uint16_t compressedSize;
+
+ //
+ // Read the first control byte
+ //
+ *BytesProcessed = 0;
+ while (BfRead(&controlByte.Value))
+ {
+ //
+ // When the LZMA2 control byte is 0, the entire stream is decoded. This
+ // is the only success path out of this function.
+ //
+ if (controlByte.Value == 0)
+ {
+ return true;
+ }
+
+ //
+ // Read the appropriate number of info bytes based on the stream type.
+ //
+ if (!BfSeek((controlByte.u.Common.IsLzma == 1 ) ? 4 : 2, &inBytes))
+ {
+ break;
+ }
+
+ //
+ // For LZMA streams calculate both the uncompressed and compressed size
+ // from the info bytes. Uncompressed streams only have the former.
+ //
+ if (controlByte.u.Common.IsLzma == 1)
+ {
+ rawSize = controlByte.u.Lzma.RawSize << 16;
+ compressedSize = inBytes[2] << 8;
+ compressedSize += inBytes[3] + 1;
+ }
+ else
+ {
+ rawSize = 0;
+ compressedSize = 0;
+ }
+
+ //
+ // Make sure that the output buffer that was supplied is big enough to
+ // fit the uncompressed chunk, unless we're just calculating the size.
+ //
+ rawSize += inBytes[0] << 8;
+ rawSize += inBytes[1] + 1;
+ if (!GetSizeOnly && !DtSetLimit(rawSize))
+ {
+ break;
+ }
+
+ //
+ // Check if the full LZMA state needs to be reset, which must happen at
+ // the start of stream. Also check for a property reset, which occurs
+ // when an LZMA stream follows an uncompressed stream. Separately,
+ // check for a state reset without a property byte (happens rarely,
+ // but does happen in a few compressed streams).
+ //
+ if ((controlByte.u.Lzma.ResetState == Lzma2FullReset) ||
+ (controlByte.u.Lzma.ResetState == Lzma2PropertyReset))
+ {
+ //
+ // Read the LZMA properties and then initialize the decoder.
+ //
+ if (!BfRead(&propertyByte) || !LzInitialize(propertyByte))
+ {
+ break;
+ }
+ }
+ else if (controlByte.u.Lzma.ResetState == Lzma2SimpleReset)
+ {
+ LzResetState();
+ }
+ //
+ // else controlByte.u.Lzma.ResetState == Lzma2NoReset, since a two-bit
+ // field only has four possible values
+ //
+
+ //
+ // Don't do any decompression if the caller only wants to know the size
+ //
+ if (GetSizeOnly)
+ {
+ *BytesProcessed += rawSize;
+ BfSeek((controlByte.u.Common.IsLzma == 1) ? compressedSize : rawSize,
+ &inBytes);
+ continue;
+ }
+ else if (controlByte.u.Common.IsLzma == 0)
+ {
+ //
+ // Seek to the requested size in the input buffer
+ //
+ if (!BfSeek(rawSize, &inBytes))
+ {
+ return false;
+ }
+
+ //
+ // Copy the data into the dictionary as-is
+ //
+ for (uint32_t i = 0; i < rawSize; i++)
+ {
+ DtPutSymbol(inBytes[i]);
+ }
+
+ //
+ // Update bytes and keep going to the next chunk
+ //
+ *BytesProcessed += rawSize;
+ continue;
+ }
+
+ //
+ // Record how many bytes are left in this sequence as our SoftLimit for
+ // the other operations. This allows us to omit most range checking
+ // logic in rangedec.c. This soft limit lasts until reset below.
+ //
+ if (!BfSetSoftLimit(compressedSize))
+ {
+ break;
+ }
+
+ //
+ // Read the initial range and code bytes to initialize the arithmetic
+ // coding decoder, and let it know how much input data exists. We've
+ // already validated that this much space exists in the input buffer.
+ //
+ if (!RcInitialize(&compressedSize))
+ {
+ break;
+ }
+
+ //
+ // Start decoding the LZMA sequences in this chunk
+ //
+ if (!Lz2DecodeChunk(BytesProcessed, rawSize, compressedSize))
+ {
+ break;
+ }
+
+ //
+ // Having decoded that chunk, reset our soft limit (to the full
+ // input stream) so we can read the next chunk.
+ //
+ BfResetSoftLimit();
+ }
+ return false;
+}
diff --git a/tools/src/minilzlib/lzma2dec.h b/tools/src/minilzlib/lzma2dec.h
new file mode 100644
index 0000000..0b31440
--- /dev/null
+++ b/tools/src/minilzlib/lzma2dec.h
@@ -0,0 +1,91 @@
+/*++
+
+Copyright (c) Alex Ionescu. All rights reserved.
+
+Module Name:
+
+ lzma2dec.h
+
+Abstract:
+
+ This header file contains C-style data structures and enumerations that map
+ back to the LZMA2 standard. This includes the encoding of the LZMA2 Control
+ Byte and the possible LZMA2 Reset States.
+
+Author:
+
+ Alex Ionescu (@aionescu) 15-Apr-2020 - Initial version
+
+Environment:
+
+ Windows & Linux, user mode and kernel mode.
+
+--*/
+
+#pragma once
+
+//
+// The most complex LZMA sequence possible is a "match" sequence where the
+// the length is > 127 bytes, and the distance is > 127 bytes. This type of
+// sequence starts with {1,1} for "match", followed by {1,1,nnnnnnnn} for
+// "8-bit encoded length", followed by {1,1,1,1,1,1} to select the distance
+// slot (63). That's 18 bits so far, which all come from arithmetic-coded
+// bit trees with various probabilities. The next 26 bits are going to be
+// fixed-probability, meaning that the bit tree is mathematically hardcoded
+// at 50%. Finally, there are the last 4 "align" distance bits which also
+// come from an arithmetic-coded bit tree, bringing the total such bits to
+// 22.
+//
+// Each time we have to "normalize" the arithmetic coder, it consumes an
+// additional byte. Normalization is done whenever we consume more than 8
+// of the high bits of the coder's range (i.e.: below 2^24), so exactly
+// every 8 direct bits (which always halve the range due to their 50%).
+// The other bits can have arbitrary probabilities, but in the worst case
+// we need to normalize the range every n bits. As such, this is a total of
+// 20 worst-case normalization per LZMA sequence. Finally, we do one last
+// normalization at the end of LzDecode, to make sure that the decoder is
+// always in a normalized state. This means that a compressed chunk should
+// be at least 21 bytes if we want to guarantee that LzDecode can never
+// read past the current input stream, and avoid range checking.
+//
+#define LZMA_MAX_SEQUENCE_SIZE 21
+
+//
+// This describes the different ways an LZMA2 control byte can request a reset
+//
+typedef enum _LZMA2_COMPRESSED_RESET_STATE
+{
+ Lzma2NoReset = 0,
+ Lzma2SimpleReset = 1,
+ Lzma2PropertyReset = 2,
+ Lzma2FullReset = 3
+} LZMA2_COMPRESSED_RESET_STATE;
+
+//
+// This describes how an LZMA2 control byte can be parsed
+//
+typedef union _LZMA2_CONTROL_BYTE
+{
+ union
+ {
+ struct
+ {
+ uint8_t ResetState : 2;
+ uint8_t Reserved : 5;
+ uint8_t IsLzma : 1;
+ } Raw;
+ struct
+ {
+ uint8_t RawSize : 5;
+ uint8_t ResetState : 2;
+ uint8_t IsLzma : 1;
+ } Lzma;
+ struct
+ {
+ uint8_t : 7;
+ uint8_t IsLzma : 1;
+ } Common;
+ } u;
+ uint8_t Value;
+} LZMA2_CONTROL_BYTE;
+static_assert(sizeof(LZMA2_CONTROL_BYTE) == 1, "Invalid control byte size");
diff --git a/tools/src/minilzlib/lzmadec.c b/tools/src/minilzlib/lzmadec.c
new file mode 100644
index 0000000..1a3c420
--- /dev/null
+++ b/tools/src/minilzlib/lzmadec.c
@@ -0,0 +1,627 @@
+/*++
+
+Copyright (c) Alex Ionescu. All rights reserved.
+
+Module Name:
+
+ lzmadec.c
+
+Abstract:
+
+ This module implements the LZMA Decoding Logic responsible for decoding the
+ three possible types of LZMA "packets": matches, repetitions (short \& long)
+ and literals. The probability model for each type of packet is also stored
+ in this file, along with the management of the previously seen packet types
+ (which is tracked as the "sequence").
+
+Author:
+
+ Alex Ionescu (@aionescu) 15-Apr-2020 - Initial version
+
+Environment:
+
+ Windows & Linux, user mode and kernel mode.
+
+--*/
+
+#include "minlzlib.h"
+#include "lzmadec.h"
+
+//
+// Probability Bit Model for Lenghts in Rep and in Match sequences
+//
+typedef struct _LENGTH_DECODER_STATE
+{
+ //
+ // Bit Model for the choosing the type of length encoding
+ //
+ uint16_t Choice;
+ uint16_t Choice2;
+ //
+ // Bit Model for each of the length encodings
+ //
+ uint16_t Low[LZMA_POSITION_COUNT][LZMA_MAX_LOW_LENGTH];
+ uint16_t Mid[LZMA_POSITION_COUNT][LZMA_MAX_MID_LENGTH];
+ uint16_t High[LZMA_MAX_HIGH_LENGTH];
+} LENGTH_DECODER_STATE, * PLENGTH_DECODER_STATE;
+
+//
+// State used for LZMA decoding
+//
+typedef struct _DECODER_STATE
+{
+ //
+ // Current type of sequence last decoded
+ //
+ LZMA_SEQUENCE_STATE Sequence;
+ //
+ // History of last 4 decoded distances
+ //
+ uint32_t Rep0;
+ uint32_t Rep1;
+ uint32_t Rep2;
+ uint32_t Rep3;
+ //
+ // Pending length to repeat from dictionary
+ //
+ uint32_t Len;
+ //
+ // Probability Bit Models for all sequence types
+ //
+ union
+ {
+ struct
+ {
+ //
+ // Literal model
+ //
+ uint16_t Literal[LZMA_LITERAL_CODERS][LZMA_LC_MODEL_SIZE];
+ //
+ // Last-used-distance based models
+ //
+ uint16_t Rep[LzmaMaxState];
+ uint16_t Rep0[LzmaMaxState];
+ uint16_t Rep0Long[LzmaMaxState][LZMA_POSITION_COUNT];
+ uint16_t Rep1[LzmaMaxState];
+ uint16_t Rep2[LzmaMaxState];
+ LENGTH_DECODER_STATE RepLen;
+ //
+ // Explicit distance match based models
+ //
+ uint16_t Match[LzmaMaxState][LZMA_POSITION_COUNT];
+ uint16_t DistSlot[LZMA_FIRST_CONTEXT_DISTANCE_SLOT][LZMA_DISTANCE_SLOTS];
+ uint16_t Dist[(1 << 7) - LZMA_FIRST_FIXED_DISTANCE_SLOT];
+ uint16_t Align[LZMA_DISTANCE_ALIGN_SLOTS];
+ LENGTH_DECODER_STATE MatchLen;
+ } BitModel;
+ uint16_t RawProbabilities[LZMA_BIT_MODEL_SLOTS];
+ } u;
+} DECODER_STATE, *PDECODER_STATE;
+DECODER_STATE Decoder;
+
+//
+// LZMA decoding uses 3 "properties" which determine how the probability
+// bit model will be laid out. These store the number of bits that are used
+// to pick the correct Literal Coder ("lc"), the number of Position bits to
+// select the Literal coder ("lp"), and the number of Position Bits used to
+// select various lengths ("pb"). In LZMA2, these properties are encoded in
+// a single byte with the formula: ((pb * 45) + lp * 9) + lc).
+//
+// We only support the default {lc = 3, lp = 0, pb = 2} properties, which
+// are what the main encoders out there use. This means that a total of 2
+// bits will be used for arithmetic-coded bit trees that are dependent on
+// the current position, and that a total of 3 bits will be used when we
+// pick the arithmetic-coded bit tree used for literal coding. The 0 means
+// this selection will _not_ be dependent on the position in the buffer.
+//
+const uint8_t k_LzSupportedProperties =
+ (LZMA_PB * 45) + (LZMA_LP * 9) + (LZMA_LC);
+
+void
+LzSetLiteral (
+ PLZMA_SEQUENCE_STATE State
+ )
+{
+ if (*State <= LzmaLitShortrepLitLitState)
+ {
+ //
+ // States 0-3 represent packets with at least 2 back-to-back literals,
+ // so another literal now takes us to state 0 (3 back-to-back literals)
+ //
+ *State = LzmaLitLitLitState;
+ }
+ else if (*State <= LzmaLitShortrepState)
+ {
+ //
+ // States 4-6 represent packets with a literal at the end, so seeing
+ // another literal now takes us to 2 back-to-back literals, which are
+ // state packets 1-3.
+ //
+ // States 7-9 represent packets with a literal at the start, followed
+ // by a match/rep/shortrep. Seeing another literal now drops this first
+ // literal and takes us to having a literal at the end, which are state
+ // packets 4-6 that we just described in the paragraph above.
+ //
+ *State = (LZMA_SEQUENCE_STATE)(*State - 3);
+ }
+ else
+ {
+ //
+ // Finally, state 10 and 11 represent cases without a single literal in
+ // the last 2 sequence packets, so seeing a literal now takes us to a
+ // "literal at the end" state, either following a match or a rep.
+ //
+ *State = (LZMA_SEQUENCE_STATE)(*State - 6);
+ }
+}
+
+bool
+LzIsLiteral (
+ LZMA_SEQUENCE_STATE State
+ )
+{
+ //
+ // States 0-6 describe literal packet sequences
+ //
+ return State < LzmaMaxLitState;
+}
+
+void
+LzSetMatch (
+ PLZMA_SEQUENCE_STATE State
+ )
+{
+ //
+ // Move to the appropriate "match" state based on current literal state
+ //
+ *State = LzIsLiteral(*State) ? LzmaLitMatchState : LzmaNonlitMatchState;
+}
+
+void
+LzSetLongRep (
+ PLZMA_SEQUENCE_STATE State
+ )
+{
+ //
+ // Move to the appropriate "long rep" state based on current literal state
+ //
+ *State = LzIsLiteral(*State) ? LzmaLitRepState : LzmaNonlitRepState;
+}
+
+void
+LzSetShortRep (
+ PLZMA_SEQUENCE_STATE State
+ )
+{
+ //
+ // Move to the appropriate "short rep" state based on current literal state
+ //
+ *State = LzIsLiteral(*State) ? LzmaLitShortrepState : LzmaNonlitRepState;
+}
+
+uint16_t*
+LzGetLiteralSlot (
+ void
+ )
+{
+ uint8_t symbol;
+
+ //
+ // To pick the correct literal coder arithmetic-coded bit tree, LZMA uses
+ // the "lc" parameter to choose the number of high bits from the previous
+ // symbol (in the normal case, 3). It then combines that with the "lp"
+ // parameter to choose the number of low bits from the current position in
+ // the dictionary. However, since "lp" is normally 0, we can omit this.
+ //
+ symbol = DtGetSymbol(1);
+ return Decoder.u.BitModel.Literal[symbol >> (8 - LZMA_LC)];
+}
+
+uint16_t*
+LzGetDistSlot (
+ void
+ )
+{
+ uint8_t slotIndex;
+
+ //
+ // There are 4 different arithmetic-coded bit trees which are used to pick
+ // the correct "distance slot" when doing match distance decoding. Each of
+ // them is used based on the length of the symbol that is being repeated.
+ // For lengths of 2, 3, 4 bytes, a dedicated set of distance slots is used.
+ // For lengths of 5 bytes or above, a shared set of distance slots is used.
+ //
+ if (Decoder.Len < (LZMA_FIRST_CONTEXT_DISTANCE_SLOT + LZMA_MIN_LENGTH))
+ {
+ slotIndex = (uint8_t)(Decoder.Len - LZMA_MIN_LENGTH);
+ }
+ else
+ {
+ slotIndex = LZMA_FIRST_CONTEXT_DISTANCE_SLOT - 1;
+ }
+ return Decoder.u.BitModel.DistSlot[slotIndex];
+}
+
+void
+LzDecodeLiteral (
+ void
+ )
+{
+ uint16_t* probArray;
+ uint8_t symbol, matchByte;
+
+ //
+ // First, choose the correct arithmetic-coded bit tree (which is based on
+ // the last symbol we just decoded), then see if we last decoded a literal.
+ //
+ // If so, simply get the symbol from the bit tree as normal. However, if
+ // we didn't last see a literal, we need to read the "match byte" that is
+ // "n" bytes away from the last decoded match. We previously stored this in
+ // rep0.
+ //
+ // Based on this match byte, we'll then use 2 other potential bit trees,
+ // see LzDecodeMatched for more information.
+ //
+ probArray = LzGetLiteralSlot();
+ if (LzIsLiteral(Decoder.Sequence))
+ {
+
+ symbol = RcGetBitTree(probArray, (1 << 8));
+ }
+ else
+ {
+ matchByte = DtGetSymbol(Decoder.Rep0 + 1);
+ symbol = RcDecodeMatchedBitTree(probArray, matchByte);
+ }
+
+ //
+ // Write the symbol and indicate that the last sequence was a literal
+ //
+ DtPutSymbol(symbol);
+ LzSetLiteral(&Decoder.Sequence);
+}
+
+void
+LzDecodeLen (
+ PLENGTH_DECODER_STATE LenState,
+ uint8_t PosBit
+ )
+{
+ uint16_t* probArray;
+ uint16_t limit;
+
+ //
+ // Lenghts of 2 and higher are encoded in 3 possible types of arithmetic-
+ // coded bit trees, depending on the size of the length.
+ //
+ // Lengths 2-9 are encoded in trees called "Low" using 3 bits of data.
+ // Lengths 10-17 are encoded in trees called "Mid" using 3 bits of data.
+ // Lengths 18-273 are encoded in a tree called "high" using 8 bits of data.
+ //
+ // The appropriate "Low" or "Mid" tree is selected based on the bottom 2
+ // position bits (0-3) (in the LZMA standard, this is based on the "pb",
+ // while the "High" tree is shared for all positions.
+ //
+ // Two arithmetic-coded bit trees, called "Choice" and "Choice2" tell us
+ // the type of Length, so we can choose the right tree. {0, n} tells us
+ // to use the Low trees, while {1, 0} tells us to use the Mid trees. Lastly
+ // {1, 1} tells us to use the High tree.
+ //
+ Decoder.Len = LZMA_MIN_LENGTH;
+ if (RcIsBitSet(&LenState->Choice))
+ {
+ if (RcIsBitSet(&LenState->Choice2))
+ {
+ probArray = LenState->High;
+ limit = LZMA_MAX_HIGH_LENGTH;
+ Decoder.Len += LZMA_MAX_LOW_LENGTH + LZMA_MAX_MID_LENGTH;
+ }
+ else
+ {
+ probArray = LenState->Mid[PosBit];
+ limit = LZMA_MAX_MID_LENGTH;
+ Decoder.Len += LZMA_MAX_LOW_LENGTH;
+ }
+ }
+ else
+ {
+ probArray = LenState->Low[PosBit];
+ limit = LZMA_MAX_LOW_LENGTH;
+ }
+ Decoder.Len += RcGetBitTree(probArray, limit);
+}
+
+void
+LzDecodeMatch (
+ uint8_t PosBit
+ )
+{
+ uint16_t* probArray;
+ uint8_t distSlot, distBits;
+
+ //
+ // Decode the length component of the "match" sequence. Then, since we're
+ // about to decode a new distance, update our history by one level.
+ //
+ LzDecodeLen(&Decoder.u.BitModel.MatchLen, PosBit);
+ Decoder.Rep3 = Decoder.Rep2;
+ Decoder.Rep2 = Decoder.Rep1;
+ Decoder.Rep1 = Decoder.Rep0;
+
+ //
+ // Read the first 6 bits, which make up the "distance slot"
+ //
+ probArray = LzGetDistSlot();
+ distSlot = RcGetBitTree(probArray, LZMA_DISTANCE_SLOTS);
+ if (distSlot < LZMA_FIRST_CONTEXT_DISTANCE_SLOT)
+ {
+ //
+ // Slots 0-3 directly encode the distance as a literal number
+ //
+ Decoder.Rep0 = distSlot;
+ }
+ else
+ {
+ //
+ // For slots 4-13, figure out how many "context encoded bits" are used
+ // to encode this distance. The math works out such that slots 4-5 use
+ // 1 bit, 6-7 use 2 bits, 8-9 use 3 bits, and so on and so forth until
+ // slots 12-13 which use 5 bits.
+ //
+ // This gives us anywhere from 1-5 bits, plus the two upper bits which
+ // can either be 0b10 or 0b11 (based on the bottom bit of the distance
+ // slot). Thus, with the context encoded bits, we can represent lengths
+ // anywhere from 0b10[0] to 0b11[11111] (i.e.: 4-127).
+ //
+ // For slots 14-63, we use "fixed 50% probability bits" which are also
+ // called "direct bits". The formula below also tells us how many such
+ // direct bits to use in this scenario. In other words, distBits can
+ // either be the number of "context encoded bits" for slots 4-13, or it
+ // can be the the number of "direct bits" for slots 14-63. This gives
+ // us a range of of 2 to 26 bits, which are then used as middle bits.
+ // Finally, the last 4 bits are called the "align" bits. The smallest
+ // possible number we can encode is now going to be 0b10[00][0000] and
+ // the highest is 0b11[1111111111111111111111111][1111], in other words
+ // 128 to (2^31)-1.
+ //
+ distBits = (distSlot >> 1) - 1;
+ Decoder.Rep0 = (0b10 | (distSlot & 1)) << distBits;
+
+ //
+ // Slots 4-13 have their own arithmetic-coded reverse bit trees. Slots
+ // 14-63 encode the middle "direct bits" with fixed 50% probability and
+ // the bottom 4 "align bits" with a shared arithmetic-coded reverse bit
+ // tree.
+ //
+ if (distSlot < LZMA_FIRST_FIXED_DISTANCE_SLOT)
+ {
+ probArray = &Decoder.u.BitModel.Dist[Decoder.Rep0 - distSlot];
+ }
+ else
+ {
+ Decoder.Rep0 |= RcGetFixed(distBits - LZMA_DISTANCE_ALIGN_BITS) <<
+ LZMA_DISTANCE_ALIGN_BITS;
+ distBits = LZMA_DISTANCE_ALIGN_BITS;
+ probArray = Decoder.u.BitModel.Align;
+ }
+ Decoder.Rep0 |= RcGetReverseBitTree(probArray, distBits);
+ }
+
+ //
+ // Indicate that the last sequence was a "match"
+ //
+ LzSetMatch(&Decoder.Sequence);
+}
+
+void
+LzDecodeRepLen (
+ uint8_t PosBit,
+ bool IsLongRep
+ )
+{
+ //
+ // Decode the length byte and indicate the last sequence was a "rep".
+ // If this is a short rep, then the length is always hard-coded to 1.
+ //
+ if (IsLongRep)
+ {
+ LzDecodeLen(&Decoder.u.BitModel.RepLen, PosBit);
+ LzSetLongRep(&Decoder.Sequence);
+ }
+ else
+ {
+ Decoder.Len = 1;
+ LzSetShortRep(&Decoder.Sequence);
+ }
+}
+
+void
+LzDecodeRep0(
+ uint8_t PosBit
+ )
+{
+ uint8_t bit;
+
+ //
+ // This could be a "short rep" with a length of 1, or a "long rep0" with
+ // a length that we have to decode. The next bit tells us this, using the
+ // arithmetic-coded bit trees stored in "Rep0Long", with 1 tree for each
+ // position bit (0-3).
+ //
+ bit = RcIsBitSet(&Decoder.u.BitModel.Rep0Long[Decoder.Sequence][PosBit]);
+ LzDecodeRepLen(PosBit, bit);
+}
+
+void
+LzDecodeLongRep (
+ uint8_t PosBit
+ )
+{
+ uint32_t newRep;
+
+ //
+ // Read the next 2 bits to figure out which of the recently used distances
+ // we should use for this match. The following three states are possible :
+ //
+ // {0,n} - "Long rep1", where the length is stored in an arithmetic-coded
+ // bit tree, and the distance is the 2nd most recently used distance (Rep1)
+ //
+ // {1,0} - "Long rep2", where the length is stored in an arithmetic-coded
+ // bit tree, and the distance is the 3rd most recently used distance (Rep2)
+ //
+ // {1,1} - "Long rep3", where the length is stored in an arithmetic-coded
+ // bit tree, and the distance is the 4th most recently used distance (Rep3)
+ //
+ // Once we have the right one, we must slide down each previously recently
+ // used distance, so that the distance we're now using (Rep1, Rep2 or Rep3)
+ // becomes "Rep0" again.
+ //
+ if (RcIsBitSet(&Decoder.u.BitModel.Rep1[Decoder.Sequence]))
+ {
+ if (RcIsBitSet(&Decoder.u.BitModel.Rep2[Decoder.Sequence]))
+ {
+ newRep = Decoder.Rep3;
+ Decoder.Rep3 = Decoder.Rep2;
+ }
+ else
+ {
+ newRep = Decoder.Rep2;
+ }
+ Decoder.Rep2 = Decoder.Rep1;
+ }
+ else
+ {
+ newRep = Decoder.Rep1;
+ }
+ Decoder.Rep1 = Decoder.Rep0;
+ Decoder.Rep0 = newRep;
+ LzDecodeRepLen(PosBit, true);
+}
+
+void
+LzDecodeRep (
+ uint8_t PosBit
+ )
+{
+ //
+ // We know this is an LZ77 distance-length pair where the distance is based
+ // on a history of up to 4 previously used distance (Rep0-3). To know which
+ // distance to use, the following 5 bit positions are possible (keeping in
+ // mind that we've already decoded the first 2 bits {1,1} in LzDecode which
+ // got us here in the first place):
+ //
+ // {0,0} - "Short rep", where the length is always 1 and distance is always
+ // the most recently used distance (Rep0).
+ //
+ // {0,1} - "Long rep0", where the length is stored in an arithmetic-coded
+ // bit tree, and the distance is the most recently used distance (Rep0).
+ //
+ // Because both of these possibilities just use Rep0, LzDecodeRep0 handles
+ // these two cases. Otherwise, we use LzDecodeLongRep to read up to two
+ // additional bits to figure out which recently used distance (1, 2, or 3)
+ // to use.
+ //
+ if (RcIsBitSet(&Decoder.u.BitModel.Rep0[Decoder.Sequence]))
+ {
+ LzDecodeLongRep(PosBit);
+ }
+ else
+ {
+ LzDecodeRep0(PosBit);
+ }
+}
+
+bool
+LzDecode (
+ void
+ )
+{
+ uint32_t position;
+ uint8_t posBit;
+
+ //
+ // Get the current position in dictionary, making sure we have input bytes.
+ // Once we run out of bytes, normalize the last arithmetic coded byte and
+ // ensure there's no pending lengths that we haven't yet repeated.
+ //
+ while (DtCanWrite(&position) && RcCanRead())
+ {
+ //
+ // An LZMA packet begins here, which can have 3 possible initial bit
+ // sequences that correspond to the type of encoding that was chosen
+ // to represent the next stream of symbols.
+ //
+ // {0, n} represents a "literal", which LzDecodeLiteral decodes.
+ // Literals are a single byte encoded with arithmetic-coded bit trees
+ //
+ // {1, 0} represents a "match", which LzDecodeMatch decodes.
+ // Matches are typical LZ77 sequences with explicit length and distance
+ //
+ // {1, 1} represents a "rep", which LzDecodeRep decodes.
+ // Reps are LZ77 sequences where the distance is encoded as a reference
+ // to a previously used distance (up to 4 -- called "Rep0-3").
+ //
+ // Once we've decoded either the "match" or the "rep', we now have the
+ // distance in "Rep0" (the most recently used distance) and the length
+ // in "Len", so we will use DtRepeatSymbol to go back in the dictionary
+ // buffer "Rep0" bytes and repeat that character "Len" times.
+ //
+ posBit = position & (LZMA_POSITION_COUNT - 1);
+ if (RcIsBitSet(&Decoder.u.BitModel.Match[Decoder.Sequence][posBit]))
+ {
+ if (RcIsBitSet(&Decoder.u.BitModel.Rep[Decoder.Sequence]))
+ {
+ LzDecodeRep(posBit);
+ }
+ else
+ {
+ LzDecodeMatch(posBit);
+ }
+
+ if (!DtRepeatSymbol(Decoder.Len, Decoder.Rep0 + 1))
+ {
+ return false;
+ }
+ Decoder.Len = 0;
+ }
+ else
+ {
+ LzDecodeLiteral();
+ }
+ }
+ RcNormalize();
+ return (Decoder.Len == 0);
+}
+
+void
+LzResetState (
+ void
+ )
+{
+ //
+ // Initialize decoder to default state in case we're called more than once.
+ // The LZMA "Bit Model" is an adaptive arithmetic-coded probability-based
+ // bit tree which encodes either a "0" or a "1".
+ //
+ Decoder.Sequence = LzmaLitLitLitState;
+ Decoder.Rep0 = Decoder.Rep1 = Decoder.Rep2 = Decoder.Rep3 = 0;
+ static_assert((LZMA_BIT_MODEL_SLOTS * 2) == sizeof(Decoder.u.BitModel),
+ "Invalid size");
+ for (int i = 0; i < LZMA_BIT_MODEL_SLOTS; i++)
+ {
+ RcSetDefaultProbability(&Decoder.u.RawProbabilities[i]);
+ }
+}
+
+bool
+LzInitialize (
+ uint8_t Properties
+ )
+{
+ if (Properties != k_LzSupportedProperties)
+ {
+ return false;
+ }
+ LzResetState();
+ return true;
+}
diff --git a/tools/src/minilzlib/lzmadec.h b/tools/src/minilzlib/lzmadec.h
new file mode 100644
index 0000000..652165d
--- /dev/null
+++ b/tools/src/minilzlib/lzmadec.h
@@ -0,0 +1,114 @@
+/*++
+
+Copyright (c) Alex Ionescu. All rights reserved.
+
+Module Name:
+
+ lzmadec.h
+
+Abstract:
+
+ This header file contains C-style definitions, constants, and enumerations
+ that map back to the LZMA Standard, specifically the probability model that
+ is used for encoding probabilities.
+
+Author:
+
+ Alex Ionescu (@aionescu) 15-Apr-2020 - Initial version
+
+Environment:
+
+ Windows & Linux, user mode and kernel mode.
+
+--*/
+
+#pragma once
+
+//
+// Literals can be 0-255 and are encoded in 3 different types of slots based on
+// the previous literal decoded and the "match byte" used.
+//
+#define LZMA_LITERALS 256
+#define LZMA_LC_TYPES 3
+#define LZMA_LC_MODEL_SIZE (LZMA_LC_TYPES * LZMA_LITERALS)
+
+//
+// These are the hardcoded LZMA properties we support for position and coders
+//
+#define LZMA_LC 3
+#define LZMA_PB 2
+#define LZMA_LP 0
+#define LZMA_LITERAL_CODERS (1 << LZMA_LC)
+#define LZMA_POSITION_COUNT (1 << LZMA_PB)
+
+//
+// Lengths are described in three different ways using "low", "mid", and "high"
+// bit trees. The first two trees encode 3 bits, the last encodes 8. We never
+// encode a length less than 2 bytes, since that's wasteful.
+//
+#define LZMA_MAX_LOW_LENGTH (1 << 3)
+#define LZMA_MAX_MID_LENGTH (1 << 3)
+#define LZMA_MAX_HIGH_LENGTH (1 << 8)
+#define LZMA_MIN_LENGTH 2
+
+//
+// Distances can be encoded in different ways, based on the distance slot.
+// Lengths of 2, 3, 4 bytes are directly encoded with their own slot. Lengths
+// over 5 share a slot, which is then further subdivded into 3 different ways
+// of encoding them, which are described in the source.
+//
+#define LZMA_DISTANCE_SLOTS 64
+#define LZMA_FIRST_CONTEXT_DISTANCE_SLOT 4
+#define LZMA_FIRST_FIXED_DISTANCE_SLOT 14
+#define LZMA_DISTANCE_ALIGN_BITS 4
+#define LZMA_DISTANCE_ALIGN_SLOTS (1 << LZMA_DISTANCE_ALIGN_BITS)
+
+//
+// Total number of probabilities that we need to store
+//
+#define LZMA_BIT_MODEL_SLOTS (1174 + \
+ (LZMA_LITERAL_CODERS * \
+ LZMA_LC_MODEL_SIZE))
+
+//
+// The LZMA probability bit model is typically based on the last LZMA sequences
+// that were decoded. There are 11 such possibilities that are tracked.
+//
+typedef enum _LZMA_SEQUENCE_STATE
+{
+ //
+ // State where we last saw three literals
+ //
+ LzmaLitLitLitState,
+ //
+ // States where we last saw two literals preceeded by a non-literal
+ //
+ LzmaMatchLitLitState,
+ LzmaRepLitLitState,
+ LzmaLitShortrepLitLitState,
+ //
+ // States where we last saw one literal preceeded by a non-literal
+ //
+ LzmaMatchLitState,
+ LzmaRepLitState,
+ LzmaLitShortrepLitState,
+ //
+ // Separator between states where we last saw at least one literal
+ //
+ LzmaMaxLitState,
+ //
+ // States where we last saw a non-literal preceeded by a literal
+ //
+ LzmaLitMatchState = 7,
+ LzmaLitRepState,
+ LzmaLitShortrepState,
+ //
+ // States where we last saw two non-literals
+ //
+ LzmaNonlitMatchState,
+ LzmaNonlitRepState,
+ //
+ // Separator for number of total states
+ //
+ LzmaMaxState
+} LZMA_SEQUENCE_STATE, * PLZMA_SEQUENCE_STATE;
diff --git a/tools/src/minilzlib/minlzlib.h b/tools/src/minilzlib/minlzlib.h
new file mode 100644
index 0000000..c5276ae
--- /dev/null
+++ b/tools/src/minilzlib/minlzlib.h
@@ -0,0 +1,88 @@
+/*++
+
+Copyright (c) Alex Ionescu. All rights reserved.
+
+Module Name:
+
+ minlzlib.h
+
+Abstract:
+
+ This header file is the main include for the minlz library. It contains the
+ internal function definitions for the history \& input buffers, the LZMA and
+ LZMA2 decoders, and the arithmetic (de)coder.
+
+Author:
+
+ Alex Ionescu (@aionescu) 15-Apr-2020 - Initial version
+
+Environment:
+
+ Windows & Linux, user mode and kernel mode.
+
+--*/
+
+#pragma once
+
+//
+// C Standard Headers
+//
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <assert.h>
+
+//
+// Input Buffer Management
+//
+bool BfRead(uint8_t* Byte);
+bool BfSeek(uint32_t Length, uint8_t** Bytes);
+uint32_t BfTell(void);
+bool BfAlign(void);
+void BfInitialize(uint8_t* InputBuffer, uint32_t InputSize);
+bool BfSetSoftLimit(uint32_t Remaining);
+void BfResetSoftLimit(void);
+
+//
+// Dictionary (History Buffer) Management
+//
+bool DtRepeatSymbol(uint32_t Length, uint32_t Distance);
+void DtInitialize(uint8_t* HistoryBuffer, uint32_t Position);
+bool DtSetLimit(uint32_t Limit);
+void DtPutSymbol(uint8_t Symbol);
+uint8_t DtGetSymbol(uint32_t Distance);
+bool DtCanWrite(uint32_t* Position);
+bool DtIsComplete(uint32_t* BytesProcessed);
+
+//
+// Range Decoder
+//
+uint8_t RcGetBitTree(uint16_t* BitModel, uint16_t Limit);
+uint8_t RcGetReverseBitTree(uint16_t* BitModel, uint8_t HighestBit);
+uint8_t RcDecodeMatchedBitTree(uint16_t* BitModel, uint8_t MatchByte);
+uint32_t RcGetFixed(uint8_t HighestBit);
+bool RcInitialize(uint16_t* ChunkSize);
+uint8_t RcIsBitSet(uint16_t* Probability);
+void RcNormalize(void);
+bool RcCanRead(void);
+bool RcIsComplete(uint32_t* Offset);
+void RcSetDefaultProbability(uint16_t* Probability);
+
+//
+// LZMA Decoder
+//
+bool LzDecode(void);
+bool LzInitialize(uint8_t Properties);
+void LzResetState(void);
+
+//
+// LZMA2 Decoder
+//
+bool Lz2DecodeStream(uint32_t* BytesProcessed, bool GetSizeOnly);
+#ifdef MINLZ_INTEGRITY_CHECKS
+//
+// Checksum Management
+//
+uint32_t OsComputeCrc32(uint32_t Initial, const uint8_t* Data, uint32_t Length);
+#define Crc32(Buffer, Length) OsComputeCrc32(0, (const uint8_t*)Buffer, Length)
+#endif
diff --git a/tools/src/minilzlib/minlzma.h b/tools/src/minilzlib/minlzma.h
new file mode 100644
index 0000000..f7ca4bd
--- /dev/null
+++ b/tools/src/minilzlib/minlzma.h
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <stdbool.h>
+
+/*!
+ * @brief Decompresses an XZ stream from InputBuffer into OutputBuffer.
+ *
+ * @detail The XZ stream must contain a single block with an LZMA2 filter
+ * and no BJC2 filters, using default LZMA properties, and using
+ * either CRC32 or None as the checksum type.
+ *
+ * @param[in] InputBuffer - A fully formed buffer containing the XZ stream.
+ * @param[in,out] InputSize - The size of the input buffer. On output, the size
+ * consumed from the input buffer.
+ * @param[in] OutputBuffer - A fully allocated buffer to receive the output.
+ * Callers can pass in NULL if they do not intend to decompress,
+ * in combination with setting OutputSize to 0, in order to query
+ * the final expected size of the decompressed buffer.
+ * @param[in,out] OutputSize - On input, the size of the buffer. On output, the
+ * size of the decompressed result.
+ *
+ * @return true - The input buffer was fully decompressed in OutputBuffer,
+ * or no decompression was requested, the size of the decompressed
+ * buffer was returned in OutputSIze.
+ * false - A failure occurred during the decompression process.
+ */
+bool
+XzDecode (
+ uint8_t* InputBuffer,
+ uint32_t* InputSize,
+ uint8_t* OutputBuffer,
+ uint32_t* OutputSize
+ );
diff --git a/tools/src/minilzlib/rangedec.c b/tools/src/minilzlib/rangedec.c
new file mode 100644
index 0000000..6a9f84f
--- /dev/null
+++ b/tools/src/minilzlib/rangedec.c
@@ -0,0 +1,395 @@
+/*++
+
+Copyright (c) Alex Ionescu. All rights reserved.
+
+Module Name:
+
+ rangedec.c
+
+Abstract:
+
+ This module implements the Range Decoder, which is how LZMA describes the
+ arithmetic coder that it uses to represent the binary representation of the
+ LZ77 match length-distance pairs after the initial compression pass. At the
+ implementation level, this coder works with an alphabet of only 2 symbols:
+ the bit "0", and the bit "1", so there are only ever two probability ranges
+ that need to be checked each pass. In LZMA, a probability of 100% encodes a
+ "0", while 0% encodes a "1". Initially, all probabilities are assumed to be
+ 50%. Probabilities are stored using 11-bits (2048 \=\= 100%), and thus use 16
+ bits of storage. Finally, the range decoder is adaptive, meaning that each
+ time a bit is decoded, the probabilities are updated: each 0 increases the
+ probability of another 0, and each 1 decrases it. The algorithm adapts the
+ probabilities using an exponential moving average with a shift ratio of 5.
+
+Author:
+
+ Alex Ionescu (@aionescu) 15-Apr-2020 - Initial version
+
+Environment:
+
+ Windows & Linux, user mode and kernel mode.
+
+--*/
+
+#include "minlzlib.h"
+
+//
+// The range decoder uses 11 probability bits, where 2048 is 100% chance of a 0
+//
+#define LZMA_RC_PROBABILITY_BITS 11
+#define LZMA_RC_MAX_PROBABILITY (1 << LZMA_RC_PROBABILITY_BITS)
+const uint16_t k_LzmaRcHalfProbability = LZMA_RC_MAX_PROBABILITY / 2;
+
+//
+// The range decoder uses an exponential moving average of the last probability
+// hit (match or miss) with an adaptation rate of 5 bits (which falls in the
+// middle of its 11 bits used to encode a probability.
+//
+#define LZMA_RC_ADAPTATION_RATE_SHIFT 5
+
+//
+// The range decoder has enough precision for the range only as long as the top
+// 8 bits are still set. Once it falls below, it needs a renormalization step.
+//
+#define LZMA_RC_MIN_RANGE (1 << 24)
+
+//
+// The range decoder must be initialized with 5 bytes, the first of which is
+// ignored
+//
+#define LZMA_RC_INIT_BYTES 5
+
+//
+// State used for the binary adaptive arithmetic coder (LZMA Range Decoder)
+//
+typedef struct _RANGE_DECODER_STATE
+{
+ //
+ // Start and end location of the current stream's range encoder buffer
+ //
+ uint8_t* Start;
+ uint8_t* Limit;
+ //
+ // Current probability range and 32-bit arithmetic encoded sequence code
+ //
+ uint32_t Range;
+ uint32_t Code;
+} RANGE_DECODER_STATE, *PRANGE_DECODER_STATE;
+RANGE_DECODER_STATE RcState;
+
+bool
+RcInitialize (
+ uint16_t* ChunkSize
+ )
+{
+ uint8_t i, rcByte;
+ uint8_t* chunkEnd;
+
+ //
+ // Make sure that the input buffer has enough space for the requirements of
+ // the range encoder. We (temporarily) seek forward to validate this.
+ //
+ if (!BfSeek(*ChunkSize, &chunkEnd))
+ {
+ return false;
+ }
+ BfSeek(-*ChunkSize, &chunkEnd);
+
+ //
+ // The initial probability range is set to its highest value, after which
+ // the next 5 bytes are used to initialize the initial code. Note that the
+ // first byte outputted by the encoder is always going to be zero, so it is
+ // ignored here.
+ //
+ RcState.Range = (uint32_t)-1;
+ RcState.Code = 0;
+ for (i = 0; i < LZMA_RC_INIT_BYTES; i++)
+ {
+ BfRead(&rcByte);
+ RcState.Code = (RcState.Code << 8) | rcByte;
+ }
+
+ //
+ // Store our current location in the buffer now, and how far we can go on
+ // reading. Then decrease the total chunk size by the count of init bytes,
+ // so that the caller can check, once done (RcIsComplete), if the code has
+ // become 0 exactly when the compressed chunk size has been fully consumed
+ // by the decoder.
+ //
+ BfSeek(0, &RcState.Start);
+ RcState.Limit = RcState.Start + *ChunkSize;
+ *ChunkSize -= LZMA_RC_INIT_BYTES;
+ return true;
+}
+
+bool
+RcCanRead (
+ void
+ )
+{
+ uint8_t* pos;
+ //
+ // We can keep reading symbols as long as we haven't reached the end of the
+ // input buffer yet.
+ //
+ BfSeek(0, &pos);
+ return pos <= RcState.Limit;
+}
+
+bool
+RcIsComplete (
+ uint32_t* BytesProcessed
+ )
+{
+ uint8_t* pos;
+ //
+ // When the last symbol has been decoded, the last code should be zero as
+ // there is nothing left to describe. Return the offset in the buffer where
+ // this occurred (which should be equal to the compressed size).
+ //
+ BfSeek(0, &pos);
+ *BytesProcessed = (uint32_t)(pos - RcState.Start);
+ return (RcState.Code == 0);
+}
+
+void
+RcNormalize (
+ void
+ )
+{
+ uint8_t rcByte;
+ //
+ // Whenever we drop below 24 bits, there is no longer enough precision in
+ // the probability range not to avoid a "stuck" state where we cannot tell
+ // apart the two branches (above/below the probability range) because the
+ // two options appear identical with the number of precision bits that we
+ // have. In this case, shift the state by a byte (8 bits) and read another.
+ //
+ if (RcState.Range < LZMA_RC_MIN_RANGE)
+ {
+ RcState.Range <<= 8;
+ RcState.Code <<= 8;
+ BfRead(&rcByte);
+ RcState.Code |= rcByte;
+ }
+}
+
+void
+RcAdapt (
+ bool Miss,
+ uint16_t* Probability
+ )
+{
+ //
+ // In the canonical range encoders out there (including this one used by
+ // LZMA, we want the probability to adapt (change) as we read more or less
+ // bits that match our expectation. In order to quickly adapt to change,
+ // use an exponential moving average. The standard way of doing this is to
+ // use an integer based adaptation with a shift that's somewhere between
+ // {1, bits-1}. Since LZMA uses 11 bits for its model, 5 is a nice number
+ // that lands exactly between 1 and 10.
+ //
+ if (Miss)
+ {
+ *Probability -= *Probability >> LZMA_RC_ADAPTATION_RATE_SHIFT;
+ }
+ else
+ {
+ *Probability += (LZMA_RC_MAX_PROBABILITY - *Probability) >>
+ LZMA_RC_ADAPTATION_RATE_SHIFT;
+ }
+}
+
+uint8_t
+RcIsBitSet (
+ uint16_t* Probability
+ )
+{
+ uint32_t bound;
+ uint8_t bit;
+
+ //
+ // Always begin by making sure the range has been normalized for precision
+ //
+ RcNormalize();
+
+ //
+ // Check if the current arithmetic code is descried by the next calculated
+ // proportionally-divided probability range. Recall that the probabilities
+ // encode the chance of the symbol (bit) being a 0 -- not a 1!
+ //
+ // Therefore, if the next chunk of the code lies outside of this new range,
+ // we are still on the path to our 0. Otherwise, if the code is now part of
+ // the newly defined range (inclusive), then we produce a 1 and limit the
+ // range to produce a new range and code for the next decoding pass.
+ //
+ bound = (RcState.Range >> LZMA_RC_PROBABILITY_BITS) * *Probability;
+ if (RcState.Code < bound)
+ {
+ RcState.Range = bound;
+ bit = 0;
+ }
+ else
+ {
+ RcState.Range -= bound;
+ RcState.Code -= bound;
+ bit = 1;
+ }
+
+ //
+ // Always finish by adapt the probabilities based on the bit value
+ //
+ RcAdapt(bit, Probability);
+ return bit;
+}
+
+uint8_t
+RcIsFixedBitSet(
+ void
+ )
+{
+ uint8_t bit;
+
+ //
+ // This is a specialized version of RcIsBitSet with two differences:
+ //
+ // First, there is no adaptive probability -- it is hardcoded to 50%.
+ //
+ // Second, because there are 11 bits per probability, and 50% is 1<<10,
+ // "(LZMA_RC_PROBABILITY_BITS) * Probability" is essentially 1. As such,
+ // we can just shift by 1 (in other words, halving the range).
+ //
+ RcNormalize();
+ RcState.Range >>= 1;
+ if (RcState.Code < RcState.Range)
+ {
+ bit = 0;
+ }
+ else
+ {
+ RcState.Code -= RcState.Range;
+ bit = 1;
+ }
+ return bit;
+}
+
+uint8_t
+RcGetBitTree (
+ uint16_t* BitModel,
+ uint16_t Limit
+ )
+{
+ uint16_t symbol;
+
+ //
+ // Context probability bit trees always begin at index 1. Iterate over each
+ // decoded bit and just keep shifting it in place, until we reach the total
+ // expected number of bits, which should never be over 8 (limit is 0x100).
+ //
+ // Once decoded, always subtract the limit back from the symbol since we go
+ // one bit "past" the limit in the loop, as a side effect of the tree being
+ // off-by-one.
+ //
+ for (symbol = 1; symbol < Limit; )
+ {
+ symbol = (symbol << 1) | RcIsBitSet(&BitModel[symbol]);
+ }
+ return (symbol - Limit) & 0xFF;
+}
+
+uint8_t
+RcGetReverseBitTree (
+ uint16_t* BitModel,
+ uint8_t HighestBit
+ )
+{
+ uint16_t symbol;
+ uint8_t i, bit, result;
+
+ //
+ // This is the same logic as in RcGetBitTree, but with the bits actually
+ // encoded in reverse order. We keep track of the probability index as the
+ // "symbol" just like RcGetBitTree, but actually decode the result in the
+ // opposite order.
+ //
+ for (i = 0, symbol = 1, result = 0; i < HighestBit; i++)
+ {
+ bit = RcIsBitSet(&BitModel[symbol]);
+ symbol = (symbol << 1) | bit;
+ result |= bit << i;
+ }
+ return result;
+}
+
+uint8_t
+RcDecodeMatchedBitTree (
+ uint16_t* BitModel,
+ uint8_t MatchByte
+ )
+{
+ uint16_t symbol, bytePos, matchBit;
+ uint8_t bit;
+
+ //
+ // Parse each bit in the "match byte" (see LzDecodeLiteral), which we call
+ // a "match bit".
+ //
+ // Then, treat this as a special bit tree decoding where two possible trees
+ // are used: one for when the "match bit" is set, and a separate one for
+ // when the "match bit" is not set. Since each tree can encode up to 256
+ // symbols, each one has 0x100 slots.
+ //
+ // Finally, we have the original bit tree which we'll revert back to once
+ // the match bits are no longer in play, which we parse for the remainder
+ // of the symbol.
+ //
+ for (bytePos = MatchByte, symbol = 1; symbol < 0x100; bytePos <<= 1)
+ {
+ matchBit = (bytePos >> 7) & 1;
+
+ bit = RcIsBitSet(&BitModel[symbol + (0x100 * (matchBit + 1))]);
+ symbol = (symbol << 1) | bit;
+
+ if (matchBit != bit)
+ {
+ while (symbol < 0x100)
+ {
+ symbol = (symbol << 1) | RcIsBitSet(&BitModel[symbol]);
+ }
+ break;
+ }
+ }
+ return symbol & 0xFF;
+}
+
+uint32_t
+RcGetFixed (
+ uint8_t HighestBit
+ )
+{
+ uint32_t symbol;
+
+ //
+ // Fixed probability bit trees always begin at index 0. Iterate over each
+ // decoded bit and just keep shifting it in place, until we reach the total
+ // expected number of bits (typically never higher than 26 -- the maximum
+ // number of "direct bits" that the distance of a "match" can encode).
+ //
+ symbol = 0;
+ do
+ {
+ symbol = (symbol << 1) | RcIsFixedBitSet();
+ } while (--HighestBit > 0);
+ return symbol;
+}
+
+void
+RcSetDefaultProbability (
+ uint16_t* Probability
+ )
+{
+ //
+ // By default, we initialize the probabilities to 0.5 (50% chance).
+ //
+ *Probability = k_LzmaRcHalfProbability;
+}
diff --git a/tools/src/minilzlib/xzstream.c b/tools/src/minilzlib/xzstream.c
new file mode 100644
index 0000000..dd5078c
--- /dev/null
+++ b/tools/src/minilzlib/xzstream.c
@@ -0,0 +1,547 @@
+/*++
+
+Copyright (c) Alex Ionescu. All rights reserved.
+
+Module Name:
+
+ xzstream.c
+
+Abstract:
+
+ This module implements the XZ stream format decoding, including support for
+ parsing the stream header and block header, and then handing off the block
+ decoding to the LZMA2 decoder. Finally, if "meta checking" is enabled, then
+ the index and stream footer are also parsed and validated. Optionally, each
+ of these component structures can be checked against its CRC32 checksum, if
+ "integrity checking" has been enabled. Note that this library only supports
+ single-stream, single-block XZ files that have CRC32 (or None) set as their
+ block checking algorithm. Finally, no BJC filters are supported, and files
+ with a compressed/uncompressed size metadata indicator are not handled.
+
+Author:
+
+ Alex Ionescu (@aionescu) 15-Apr-2020 - Initial version
+
+Environment:
+
+ Windows & Linux, user mode and kernel mode.
+
+--*/
+
+#define MINLZ_META_CHECKS
+
+#include "minlzlib.h"
+#include "xzstream.h"
+#include "../utils.h"
+
+//
+// XzDecodeBlockHeader can return "I successfully found a block",
+// "I failed/bad block header", or "there was no block header".
+// Though minlzlib explicitly only claims to handle files with a
+// single block, it needs to also handle files with no blocks at all.
+// (Produced by "xz" when compressing an empty input file)
+//
+typedef enum _XZ_DECODE_BLOCK_HEADER_RESULT {
+ XzBlockHeaderFail = 0,
+ XzBlockHeaderSuccess = 1,
+ XzBlockHeaderNoBlock = 2
+} XZ_DECODE_BLOCK_HEADER_RESULT;
+
+const uint8_t k_XzLzma2FilterIdentifier = 0x21;
+
+#ifdef _WIN32
+void __security_check_cookie(_In_ uintptr_t _StackCookie) { (void)(_StackCookie); }
+#endif
+
+#ifdef MINLZ_META_CHECKS
+//
+// XZ Stream Container State
+//
+typedef struct _CONTAINER_STATE
+{
+ //
+ // Size of the XZ header and the index, used to validate against footer
+ //
+ uint32_t HeaderSize;
+ uint32_t IndexSize;
+ //
+ // Size of the compressed block and its checksum
+ //
+ uint32_t UncompressedBlockSize;
+ uint32_t UnpaddedBlockSize;
+ uint32_t ChecksumSize;
+} CONTAINER_STATE, * PCONTAINER_STATE;
+CONTAINER_STATE Container;
+#endif
+
+#ifdef MINLZ_META_CHECKS
+bool
+XzDecodeVli (
+ vli_type* Vli
+ )
+{
+ uint8_t vliByte;
+ uint32_t bitPos;
+
+ //
+ // Read the initial VLI byte (might be the value itself)
+ //
+ if (!BfRead(&vliByte))
+ {
+ return false;
+ }
+ *Vli = vliByte & 0x7F;
+
+ //
+ // Check if this was a complex VLI (and we have space for it)
+ //
+ bitPos = 7;
+ while ((vliByte & 0x80) != 0)
+ {
+ //
+ // Read the next byte
+ //
+ if (!BfRead(&vliByte))
+ {
+ return false;
+ }
+
+ //
+ // Make sure we're not decoding an invalid VLI
+ //
+ if ((bitPos == (7 * VLI_BYTES_MAX)) || (vliByte == 0))
+ {
+ return false;
+ }
+
+ //
+ // Decode it and move to the next 7 bits
+ //
+ *Vli |= (vli_type)((vliByte & 0x7F) << bitPos);
+ bitPos += 7;
+ }
+ return true;
+}
+
+bool
+XzDecodeIndex (
+ void
+ )
+{
+ uint32_t vli;
+ uint8_t* indexStart;
+ uint8_t* indexEnd;
+ uint32_t* pCrc32;
+ uint8_t indexByte;
+
+ //
+ // Remember where the index started so we can compute its size
+ //
+ BfSeek(0, &indexStart);
+
+ //
+ // The index always starts out with an empty byte
+ //
+ if (!BfRead(&indexByte) || (indexByte != 0))
+ {
+ return false;
+ }
+
+ //
+ // Then the count of blocks, which we expect to be 1
+ //
+ if (!XzDecodeVli(&vli) || (vli != 1))
+ {
+ return false;
+ }
+
+ //
+ // Then the unpadded block size, which should match
+ //
+ if (!XzDecodeVli(&vli) || (Container.UnpaddedBlockSize != vli))
+ {
+ return false;
+ }
+
+ //
+ // Then the uncompressed block size, which should match
+ //
+ if (!XzDecodeVli(&vli) || (Container.UncompressedBlockSize != vli))
+ {
+ return false;
+ }
+
+ //
+ // Then we pad to the next multiple of 4
+ //
+ if (!BfAlign())
+ {
+ return false;
+ }
+
+ //
+ // Store the index size with padding to validate the footer later
+ //
+ BfSeek(0, &indexEnd);
+ Container.IndexSize = (uint32_t)(indexEnd - indexStart);
+
+ //
+ // Read the CRC32, which is not part of the index size
+ //
+ if (!BfSeek(sizeof(*pCrc32), (uint8_t**)&pCrc32))
+ {
+ return false;
+ }
+#ifdef MINLZ_INTEGRITY_CHECKS
+ //
+ // Make sure the index is not corrupt
+ //
+ if (Crc32(indexStart, Container.IndexSize) != *pCrc32)
+ {
+ return false;
+ }
+#endif
+ return true;
+}
+
+bool
+XzDecodeStreamFooter (
+ void
+ )
+{
+ PXZ_STREAM_FOOTER streamFooter;
+
+ //
+ // Seek past the footer, making sure we have space in the input stream
+ //
+ if (!BfSeek(sizeof(*streamFooter), (uint8_t**)&streamFooter))
+ {
+ return false;
+ }
+
+ //
+ // Validate the footer magic
+ //
+ if (streamFooter->Magic != 'ZY')
+ {
+ return false;
+ }
+
+ //
+ // Validate no flags other than checksum type are set
+ //
+ if ((streamFooter->u.Flags != 0) &&
+ ((streamFooter->u.s.CheckType != XzCheckTypeCrc32) &&
+ (streamFooter->u.s.CheckType != XzCheckTypeCrc64) &&
+ (streamFooter->u.s.CheckType != XzCheckTypeSha2) &&
+ (streamFooter->u.s.CheckType != XzCheckTypeNone)))
+ {
+ return false;
+ }
+
+ //
+ // Validate if the footer accurately describes the size of the index
+ //
+ if (Container.IndexSize != (streamFooter->BackwardSize * 4))
+ {
+ return false;
+ }
+#ifdef MINLZ_INTEGRITY_CHECKS
+ //
+ // Compute the footer's CRC32 and make sure it's not corrupted
+ //
+ if (Crc32(&streamFooter->BackwardSize,
+ sizeof(streamFooter->BackwardSize) +
+ sizeof(streamFooter->u.Flags)) !=
+ streamFooter->Crc32)
+ {
+ return false;
+ }
+#endif
+ return true;
+}
+#endif
+
+bool
+XzDecodeBlock (
+ uint8_t* OutputBuffer,
+ uint32_t* BlockSize
+ )
+{
+#ifdef MINLZ_META_CHECKS
+ uint8_t *inputStart, *inputEnd;
+#endif
+ //
+ // Decode the LZMA2 stream. If full integrity checking is enabled, also
+ // save the offset before and after decoding, so we can save the block
+ // sizes and compare them against the footer and index after decoding.
+ //
+#ifdef MINLZ_META_CHECKS
+ BfSeek(0, &inputStart);
+#endif
+ if (!Lz2DecodeStream(BlockSize, OutputBuffer == NULL))
+ {
+ return false;
+ }
+#ifdef MINLZ_META_CHECKS
+ BfSeek(0, &inputEnd);
+ Container.UnpaddedBlockSize = Container.HeaderSize +
+ (uint32_t)(inputEnd - inputStart);
+ Container.UncompressedBlockSize = *BlockSize;
+#endif
+ //
+ // After the block data, we need to pad to 32-bit alignment
+ //
+ if (!BfAlign())
+ {
+ return false;
+ }
+#if defined(MINLZ_INTEGRITY_CHECKS) || defined(MINLZ_META_CHECKS)
+ //
+ // Finally, move past the size of the checksum if any, then compare it with
+ // with the actual CRC32 of the block, if integrity checks are enabled. If
+ // meta checks are enabled, update the block size so the index checking can
+ // validate it.
+ //
+ if (!BfSeek(Container.ChecksumSize, &inputEnd))
+ {
+ return false;
+ }
+#endif
+ (void)(OutputBuffer);
+#ifdef MINLZ_INTEGRITY_CHECKS
+ if ((OutputBuffer != NULL) &&
+ (Crc32(OutputBuffer, *BlockSize) != *(uint32_t*)inputEnd))
+ {
+ return false;
+ }
+#endif
+#ifdef MINLZ_META_CHECKS
+ Container.UnpaddedBlockSize += Container.ChecksumSize;
+#endif
+ return true;
+}
+
+bool
+XzDecodeStreamHeader (
+ void
+ )
+{
+ PXZ_STREAM_HEADER streamHeader;
+
+ //
+ // Seek past the header, making sure we have space in the input stream
+ //
+ if (!BfSeek(sizeof(*streamHeader), (uint8_t**)&streamHeader))
+ {
+ return false;
+ }
+#ifdef MINLZ_META_CHECKS
+ //
+ // Validate the header magic
+ //
+ if ((*(uint32_t*)&streamHeader->Magic[1] != 'ZXz7') ||
+ (streamHeader->Magic[0] != 0xFD) ||
+ (streamHeader->Magic[5] != 0x00))
+ {
+ return false;
+ }
+
+ //
+ // Validate no flags other than checksum type are set
+ //
+ if ((streamHeader->u.Flags != 0) &&
+ ((streamHeader->u.s.CheckType != XzCheckTypeCrc32) &&
+ (streamHeader->u.s.CheckType != XzCheckTypeCrc64) &&
+ (streamHeader->u.s.CheckType != XzCheckTypeSha2) &&
+ (streamHeader->u.s.CheckType != XzCheckTypeNone)))
+ {
+ return false;
+ }
+
+ //
+ // Remember that a checksum might come at the end of the block later
+ //
+ if (streamHeader->u.s.CheckType == 0)
+ {
+ Container.ChecksumSize = 0;
+ } else {
+ Container.ChecksumSize = 4 << ((streamHeader->u.s.CheckType - 1) / 3);
+ }
+
+#endif
+#ifdef MINLZ_INTEGRITY_CHECKS
+ //
+ // Compute the header's CRC32 and make sure it's not corrupted
+ //
+ if (Crc32(&streamHeader->u.Flags, sizeof(streamHeader->u.Flags)) !=
+ streamHeader->Crc32)
+ {
+ return false;
+ }
+#endif
+ return true;
+}
+
+XZ_DECODE_BLOCK_HEADER_RESULT
+XzDecodeBlockHeader (
+ void
+ )
+{
+ PXZ_BLOCK_HEADER blockHeader;
+#ifdef MINLZ_META_CHECKS
+ uint32_t size;
+#endif
+ //
+ // Seek past the header, making sure we have space in the input stream
+ //
+ if (!BfSeek(sizeof(*blockHeader), (uint8_t**)&blockHeader))
+ {
+ return XzBlockHeaderFail;
+ }
+ if (blockHeader->Size == 0)
+ {
+ //
+ // That's no block! That's an index!
+ //
+ BfSeek((uint32_t)(-(uint16_t)sizeof(*blockHeader)),
+ (uint8_t**)&blockHeader);
+ return XzBlockHeaderNoBlock;
+ }
+#ifdef MINLZ_META_CHECKS
+ //
+ // Validate that the size of the header is what we expect
+ //
+ Container.HeaderSize = (blockHeader->Size + 1) * 4;
+ if (Container.HeaderSize != sizeof(*blockHeader))
+ {
+ return XzBlockHeaderFail;
+ }
+
+ //
+ // Validate that no additional flags or filters are enabled
+ //
+ if (blockHeader->u.Flags != 0)
+ {
+ return XzBlockHeaderFail;
+ }
+
+ //
+ // Validate that the only filter is the LZMA2 filter
+ //
+ if (blockHeader->LzmaFlags.Id != k_XzLzma2FilterIdentifier)
+ {
+ return XzBlockHeaderFail;
+ }
+
+ //
+ // With the expected number of property bytes
+ //
+ if (blockHeader->LzmaFlags.Size
+ != sizeof(blockHeader->LzmaFlags.u.Properties))
+ {
+ return XzBlockHeaderFail;
+ }
+
+ //
+ // The only property is the dictionary size, make sure it is valid.
+ //
+ // We don't actually need to store or compare the size with anything since
+ // the library expects the caller to always put in a buffer that's large
+ // enough to contain the full uncompressed file (or calling it in "get size
+ // only" mode to get this information).
+ //
+ // This output buffer can thus be smaller than the size of the dictionary
+ // which is absolutely OK as long as that's actually the size of the output
+ // file. If callers pass in a buffer size that's too small, decoding will
+ // fail at later stages anyway, and that's incorrect use of minlzlib.
+ //
+ size = blockHeader->LzmaFlags.u.s.DictionarySize;
+ if (size > 39)
+ {
+ return XzBlockHeaderFail;
+ }
+#ifdef MINLZ_INTEGRITY_CHECKS
+ //
+ // Compute the header's CRC32 and make sure it's not corrupted
+ //
+ if (Crc32(blockHeader,
+ Container.HeaderSize - sizeof(blockHeader->Crc32)) !=
+ blockHeader->Crc32)
+ {
+ return XzBlockHeaderFail;
+ }
+#endif
+#endif
+ return XzBlockHeaderSuccess;
+}
+
+bool
+XzDecode (
+ uint8_t* InputBuffer,
+ uint32_t* InputSize,
+ uint8_t* OutputBuffer,
+ uint32_t* OutputSize
+ )
+{
+
+ //
+ // Initialize the input buffer descriptor and history buffer (dictionary)
+ //
+ BfInitialize(InputBuffer, *InputSize ? *InputSize : UINT32_MAX);
+ DtInitialize(OutputBuffer, *OutputSize);
+
+ //
+ // Decode the stream header for check for validity
+ //
+ if (!XzDecodeStreamHeader())
+ {
+ printf("header decode failed\n");
+ return false;
+ }
+
+ //
+ // Decode the block header for check for validity
+ //
+ switch (XzDecodeBlockHeader())
+ {
+ case XzBlockHeaderFail:
+ printf("block header failed\n");
+ return false;
+ case XzBlockHeaderNoBlock:
+ *OutputSize = 0;
+ break;
+ case XzBlockHeaderSuccess:
+ //
+ // Decode the actual block
+ //
+ if (!XzDecodeBlock(OutputBuffer, OutputSize))
+ {
+ printf("block decode failed\n");
+ return false;
+ }
+ break;
+ }
+
+#ifdef MINLZ_META_CHECKS
+ //
+ // Decode the index for validity checks
+ //
+ if (!XzDecodeIndex())
+ {
+ return false;
+ }
+
+ //
+ // And finally decode the footer as a final set of checks
+ //
+ if (!XzDecodeStreamFooter())
+ {
+ return false;
+ }
+
+ if (!*InputSize)
+ *InputSize = BfTell();
+#endif
+ return true;
+}
diff --git a/tools/src/minilzlib/xzstream.h b/tools/src/minilzlib/xzstream.h
new file mode 100644
index 0000000..f227879
--- /dev/null
+++ b/tools/src/minilzlib/xzstream.h
@@ -0,0 +1,123 @@
+/*++
+
+Copyright (c) Alex Ionescu. All rights reserved.
+
+Module Name:
+
+ xzstream.h
+
+Abstract:
+
+ This header file contains C-style data structures and enumerations that map
+ back to the XZ stream and file format standard, including for the decoding
+ of Variable Length Integers (VLI). This includes definitions for the stream
+ header, block header, index and stream footer, and associated check types.
+
+Author:
+
+ Alex Ionescu (@aionescu) 15-Apr-2020 - Initial version
+
+Environment:
+
+ Windows & Linux, user mode and kernel mode.
+
+--*/
+
+#pragma once
+
+//
+// XZ streams encode certain numbers as "variable length integers", with 7 bits
+// for the data, and a high bit to encode that another byte must be consumed.
+//
+typedef uint32_t vli_type;
+#define VLI_BYTES_MAX (sizeof(vli_type) * 8 / 7)
+
+//
+// These are the possible supported types for integrity checking in an XZ file
+//
+typedef enum _XZ_CHECK_TYPES
+{
+ XzCheckTypeNone = 0,
+ XzCheckTypeCrc32 = 1,
+ XzCheckTypeCrc64 = 4,
+ XzCheckTypeSha2 = 10
+} XZ_CHECK_TYPES;
+
+//
+// This describes the first 12 bytes of any XZ container file / stream
+//
+typedef struct _XZ_STREAM_HEADER
+{
+ uint8_t Magic[6];
+ union
+ {
+ struct
+ {
+ uint8_t ReservedFlags;
+ uint8_t CheckType : 4;
+ uint8_t ReservedType : 4;
+ } s;
+ uint16_t Flags;
+ } u;
+ uint32_t Crc32;
+} XZ_STREAM_HEADER, * PXZ_STREAM_HEADER;
+static_assert(sizeof(XZ_STREAM_HEADER) == 12, "Invalid Stream Header Size");
+
+//
+// This describes the last 12 bytes of any XZ container file / stream
+//
+typedef struct _XZ_STREAM_FOOTER
+{
+ uint32_t Crc32;
+ uint32_t BackwardSize;
+ union
+ {
+ struct
+ {
+ uint8_t ReservedFlags;
+ uint8_t CheckType : 4;
+ uint8_t ReservedType : 4;
+ } s;
+ uint16_t Flags;
+ } u;
+ uint16_t Magic;
+} XZ_STREAM_FOOTER, * PXZ_STREAM_FOOTER;
+static_assert(sizeof(XZ_STREAM_FOOTER) == 12, "Invalid Stream Footer Size");
+
+//
+// This describes the beginning of a compressed payload stored in an XZ stream,
+// with hardcoded expectations for an LZMA2-compressed payload that has 0 extra
+// filters (such as BCJ2).
+//
+typedef struct _XZ_BLOCK_HEADER
+{
+ uint8_t Size;
+ union
+ {
+ struct
+ {
+ uint8_t FilterCount : 2;
+ uint8_t Reserved : 4;
+ uint8_t HasCompressedSize : 1;
+ uint8_t HasUncompressedSize : 1;
+ } s;
+ uint8_t Flags;
+ } u;
+ struct
+ {
+ uint8_t Id;
+ uint8_t Size;
+ union
+ {
+ struct
+ {
+ uint8_t DictionarySize : 6;
+ uint8_t Reserved : 2;
+ } s;
+ uint8_t Properties;
+ } u;
+ } LzmaFlags;
+ uint8_t Padding[3];
+ uint32_t Crc32;
+} XZ_BLOCK_HEADER, * PXZ_BLOCK_HEADER;
+static_assert(sizeof(XZ_BLOCK_HEADER) == 12, "Invalid Block Header Size");
diff --git a/tools/src/nvme.c b/tools/src/nvme.c
new file mode 100644
index 0000000..e6741eb
--- /dev/null
+++ b/tools/src/nvme.c
@@ -0,0 +1,505 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "adt.h"
+#include "assert.h"
+#include "malloc.h"
+#include "nvme.h"
+#include "pmgr.h"
+#include "rtkit.h"
+#include "sart.h"
+#include "string.h"
+#include "utils.h"
+
+#define NVME_TIMEOUT 1000000
+#define NVME_ENABLE_TIMEOUT 5000000
+#define NVME_SHUTDOWN_TIMEOUT 5000000
+#define NVME_QUEUE_SIZE 64
+
+#define NVME_CC 0x14
+#define NVME_CC_SHN GENMASK(15, 14)
+#define NVME_CC_SHN_NONE 0
+#define NVME_CC_SHN_NORMAL 1
+#define NVME_CC_SHN_ABRUPT 2
+#define NVME_CC_EN BIT(0)
+
+#define NVME_CSTS 0x1c
+#define NVME_CSTS_SHST GENMASK(3, 2)
+#define NVME_CSTS_SHST_NORMAL 0
+#define NVME_CSTS_SHST_BUSY 1
+#define NVME_CSTS_SHST_DONE 2
+#define NVME_CSTS_RDY BIT(0)
+
+#define NVME_AQA 0x24
+#define NVME_ASQ 0x28
+#define NVME_ACQ 0x30
+
+#define NVME_DB_ACQ 0x1004
+#define NVME_DB_IOCQ 0x100c
+
+#define NVME_BOOT_STATUS 0x1300
+#define NVME_BOOT_STATUS_OK 0xde71ce55
+
+#define NVME_LINEAR_SQ_CTRL 0x24908
+#define NVME_LINEAR_SQ_CTRL_EN BIT(0)
+
+#define NVME_UNKNONW_CTRL 0x24008
+#define NVME_UNKNONW_CTRL_PRP_NULL_CHECK BIT(11)
+
+#define NVME_MAX_PEND_CMDS_CTRL 0x1210
+#define NVME_DB_LINEAR_ASQ 0x2490c
+#define NVME_DB_LINEAR_IOSQ 0x24910
+
+#define NVMMU_NUM 0x28100
+#define NVMMU_ASQ_BASE 0x28108
+#define NVMMU_IOSQ_BASE 0x28110
+#define NVMMU_TCB_INVAL 0x28118
+#define NVMMU_TCB_STAT 0x29120
+
+#define NVME_ADMIN_CMD_DELETE_SQ 0x00
+#define NVME_ADMIN_CMD_CREATE_SQ 0x01
+#define NVME_ADMIN_CMD_DELETE_CQ 0x04
+#define NVME_ADMIN_CMD_CREATE_CQ 0x05
+#define NVME_QUEUE_CONTIGUOUS BIT(0)
+
+#define NVME_CMD_FLUSH 0x00
+#define NVME_CMD_WRITE 0x01
+#define NVME_CMD_READ 0x02
+
+struct nvme_command {
+ u8 opcode;
+ u8 flags;
+ u8 tag;
+ u8 rsvd; // normal NVMe has tag as u16
+ u32 nsid;
+ u32 cdw2;
+ u32 cdw3;
+ u64 metadata;
+ u64 prp1;
+ u64 prp2;
+ u32 cdw10;
+ u32 cdw11;
+ u32 cdw12;
+ u32 cdw13;
+ u32 cdw14;
+ u32 cdw15;
+};
+
+struct nvme_completion {
+ u64 result;
+ u32 rsvd; // normal NVMe has the sq_head and sq_id here
+ u16 tag;
+ u16 status;
+};
+
+struct apple_nvmmu_tcb {
+ u8 opcode;
+ u8 dma_flags;
+ u8 slot_id;
+ u8 unk0;
+ u32 len;
+ u64 unk1[2];
+ u64 prp1;
+ u64 prp2;
+ u64 unk2[2];
+ u8 aes_iv[8];
+ u8 _aes_unk[64];
+};
+
+struct nvme_queue {
+ struct apple_nvmmu_tcb *tcbs;
+ struct nvme_command *cmds;
+ struct nvme_completion *cqes;
+
+ u8 cq_head;
+ u8 cq_phase;
+
+ bool adminq;
+};
+
+static_assert(sizeof(struct nvme_command) == 64, "invalid nvme_command size");
+static_assert(sizeof(struct nvme_completion) == 16, "invalid nvme_completion size");
+static_assert(sizeof(struct apple_nvmmu_tcb) == 128, "invalid apple_nvmmu_tcb size");
+
+static bool nvme_initialized = false;
+static u8 nvme_die;
+
+static asc_dev_t *nvme_asc = NULL;
+static rtkit_dev_t *nvme_rtkit = NULL;
+static sart_dev_t *nvme_sart = NULL;
+
+static u64 nvme_base;
+
+static struct nvme_queue adminq, ioq;
+
+static bool alloc_queue(struct nvme_queue *q)
+{
+ memset(q, 0, sizeof(*q));
+
+ q->tcbs = memalign(SZ_16K, NVME_QUEUE_SIZE * sizeof(*q->tcbs));
+ if (!q->tcbs)
+ return false;
+
+ q->cmds = memalign(SZ_16K, NVME_QUEUE_SIZE * sizeof(*q->cmds));
+ if (!q->cmds)
+ goto free_tcbs;
+
+ q->cqes = memalign(SZ_16K, NVME_QUEUE_SIZE * sizeof(*q->cqes));
+ if (!q->cqes)
+ goto free_cmds;
+
+ memset(q->tcbs, 0, NVME_QUEUE_SIZE * sizeof(*q->tcbs));
+ memset(q->cmds, 0, NVME_QUEUE_SIZE * sizeof(*q->cmds));
+ memset(q->cqes, 0, NVME_QUEUE_SIZE * sizeof(*q->cqes));
+ q->cq_head = 0;
+ q->cq_phase = 1;
+ return true;
+
+free_cmds:
+ free(q->cmds);
+free_tcbs:
+ free(q->tcbs);
+ return false;
+}
+
+static void free_queue(struct nvme_queue *q)
+{
+ free(q->cmds);
+ free(q->tcbs);
+ free(q->cqes);
+}
+
+static void nvme_poll_syslog(void)
+{
+ struct rtkit_message msg;
+ rtkit_recv(nvme_rtkit, &msg);
+}
+
+static bool nvme_ctrl_disable(void)
+{
+ u64 timeout = timeout_calculate(NVME_TIMEOUT);
+
+ clear32(nvme_base + NVME_CC, NVME_CC_EN);
+ while (read32(nvme_base + NVME_CSTS) & NVME_CSTS_RDY && !timeout_expired(timeout))
+ nvme_poll_syslog();
+
+ return !(read32(nvme_base + NVME_CSTS) & NVME_CSTS_RDY);
+}
+
+static bool nvme_ctrl_enable(void)
+{
+ u64 timeout = timeout_calculate(NVME_ENABLE_TIMEOUT);
+
+ mask32(nvme_base + NVME_CC, NVME_CC_SHN, NVME_CC_EN);
+ while (!(read32(nvme_base + NVME_CSTS) & NVME_CSTS_RDY) && !timeout_expired(timeout))
+ nvme_poll_syslog();
+
+ return read32(nvme_base + NVME_CSTS) & NVME_CSTS_RDY;
+}
+
+static bool nvme_ctrl_shutdown(void)
+{
+ u64 timeout = timeout_calculate(NVME_SHUTDOWN_TIMEOUT);
+
+ mask32(nvme_base + NVME_CC, NVME_CC_SHN, FIELD_PREP(NVME_CC_SHN, NVME_CC_SHN_NORMAL));
+ while (FIELD_GET(NVME_CSTS_SHST, read32(nvme_base + NVME_CSTS)) != NVME_CSTS_SHST_DONE &&
+ !timeout_expired(timeout))
+ nvme_poll_syslog();
+
+ return FIELD_GET(NVME_CSTS_SHST, read32(nvme_base + NVME_CSTS)) == NVME_CSTS_SHST_DONE;
+}
+
+static bool nvme_exec_command(struct nvme_queue *q, struct nvme_command *cmd, u64 *result)
+{
+ bool found = false;
+ u64 timeout;
+ u8 tag = 0;
+ struct nvme_command *queue_cmd = &q->cmds[tag];
+ struct apple_nvmmu_tcb *tcb = &q->tcbs[tag];
+
+ memcpy(queue_cmd, cmd, sizeof(*cmd));
+ queue_cmd->tag = tag;
+
+ memset(tcb, 0, sizeof(*tcb));
+ tcb->opcode = queue_cmd->opcode;
+ tcb->dma_flags = 3; // always allow read+write to the PRP pages
+ tcb->slot_id = tag;
+ tcb->len = queue_cmd->cdw12;
+ tcb->prp1 = queue_cmd->prp1;
+ tcb->prp2 = queue_cmd->prp2;
+
+ /* make sure ANS2 can see the command and tcb before triggering it */
+ dma_wmb();
+
+ nvme_poll_syslog();
+ if (q->adminq)
+ write32(nvme_base + NVME_DB_LINEAR_ASQ, tag);
+ else
+ write32(nvme_base + NVME_DB_LINEAR_IOSQ, tag);
+ nvme_poll_syslog();
+
+ timeout = timeout_calculate(NVME_TIMEOUT);
+ struct nvme_completion cqe;
+ while (!timeout_expired(timeout)) {
+ nvme_poll_syslog();
+
+ /* we need a DMA read barrier here since the CQ will be updated using DMA */
+ dma_rmb();
+ memcpy(&cqe, &q->cqes[q->cq_head], sizeof(cqe));
+ if ((cqe.status & 1) != q->cq_phase)
+ continue;
+
+ if (cqe.tag == tag) {
+ found = true;
+ if (result)
+ *result = cqe.result;
+ } else {
+ printf("nvme: invalid tag in CQ: expected %d but got %d\n", tag, cqe.tag);
+ }
+
+ write32(nvme_base + NVMMU_TCB_INVAL, cqe.tag);
+ if (read32(nvme_base + NVMMU_TCB_STAT))
+ printf("nvme: NVMMU invalidation for tag %d failed\n", cqe.tag);
+
+ /* increment head and switch phase once the end of the queue has been reached */
+ q->cq_head += 1;
+ if (q->cq_head == NVME_QUEUE_SIZE) {
+ q->cq_head = 0;
+ q->cq_phase ^= 1;
+ }
+
+ if (q->adminq)
+ write32(nvme_base + NVME_DB_ACQ, q->cq_head);
+ else
+ write32(nvme_base + NVME_DB_IOCQ, q->cq_head);
+ break;
+ }
+
+ if (!found) {
+ printf("nvme: could not find command completion in CQ\n");
+ return false;
+ }
+
+ cqe.status >>= 1;
+ if (cqe.status) {
+ printf("nvme: command failed with status %d\n", cqe.status);
+ return false;
+ }
+
+ return true;
+}
+
+bool nvme_init(void)
+{
+ if (nvme_initialized) {
+ printf("nvme: already initialized\n");
+ return true;
+ }
+
+ int adt_path[8];
+ int node = adt_path_offset_trace(adt, "/arm-io/ans", adt_path);
+ if (node < 0) {
+ printf("nvme: Error getting NVMe node /arm-io/ans\n");
+ return NULL;
+ }
+
+ u32 cg;
+ if (ADT_GETPROP(adt, node, "clock-gates", &cg) < 0) {
+ printf("nvme: Error getting NVMe clock-gates\n");
+ return NULL;
+ }
+ nvme_die = FIELD_GET(PMGR_DIE_ID, cg);
+ printf("nvme: ANS is on die %d\n", nvme_die);
+
+ if (adt_get_reg(adt, adt_path, "reg", 3, &nvme_base, NULL) < 0) {
+ printf("nvme: Error getting NVMe base address.\n");
+ return NULL;
+ }
+
+ if (!alloc_queue(&adminq)) {
+ printf("nvme: Error allocating admin queue\n");
+ return NULL;
+ }
+ if (!alloc_queue(&ioq)) {
+ printf("nvme: Error allocating admin queue\n");
+ goto out_adminq;
+ }
+
+ ioq.adminq = false;
+ adminq.adminq = true;
+
+ nvme_asc = asc_init("/arm-io/ans");
+ if (!nvme_asc)
+ goto out_ioq;
+
+ nvme_sart = sart_init("/arm-io/sart-ans");
+ if (!nvme_sart)
+ goto out_asc;
+
+ nvme_rtkit = rtkit_init("nvme", nvme_asc, NULL, NULL, nvme_sart);
+ if (!nvme_rtkit)
+ goto out_sart;
+
+ if (!rtkit_boot(nvme_rtkit))
+ goto out_rtkit;
+
+ if (poll32(nvme_base + NVME_BOOT_STATUS, 0xffffffff, NVME_BOOT_STATUS_OK, USEC_PER_SEC) < 0) {
+ printf("nvme: ANS did not boot correctly.\n");
+ goto out_shutdown;
+ }
+
+ /* setup controller and NVMMU for linear submission queue */
+ set32(nvme_base + NVME_LINEAR_SQ_CTRL, NVME_LINEAR_SQ_CTRL_EN);
+ clear32(nvme_base + NVME_UNKNONW_CTRL, NVME_UNKNONW_CTRL_PRP_NULL_CHECK);
+ write32(nvme_base + NVME_MAX_PEND_CMDS_CTRL,
+ ((NVME_QUEUE_SIZE - 1) << 16) | (NVME_QUEUE_SIZE - 1));
+ write32(nvme_base + NVMMU_NUM, NVME_QUEUE_SIZE - 1);
+ write64_lo_hi(nvme_base + NVMMU_ASQ_BASE, (u64)adminq.tcbs);
+ write64_lo_hi(nvme_base + NVMMU_IOSQ_BASE, (u64)ioq.tcbs);
+
+ /* setup admin queue */
+ if (!nvme_ctrl_disable()) {
+ printf("nvme: timeout while waiting for CSTS.RDY to clear\n");
+ goto out_shutdown;
+ }
+ write64_lo_hi(nvme_base + NVME_ASQ, (u64)adminq.cmds);
+ write64_lo_hi(nvme_base + NVME_ACQ, (u64)adminq.cqes);
+ write32(nvme_base + NVME_AQA, ((NVME_QUEUE_SIZE - 1) << 16) | (NVME_QUEUE_SIZE - 1));
+ if (!nvme_ctrl_enable()) {
+ printf("nvme: timeout while waiting for CSTS.RDY to be set\n");
+ goto out_disable_ctrl;
+ }
+
+ /* setup IO queue */
+ struct nvme_command cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.opcode = NVME_ADMIN_CMD_CREATE_CQ;
+ cmd.prp1 = (u64)ioq.cqes;
+ cmd.cdw10 = 1; // cq id
+ cmd.cdw10 |= (NVME_QUEUE_SIZE - 1) << 16;
+ cmd.cdw11 = NVME_QUEUE_CONTIGUOUS;
+ if (!nvme_exec_command(&adminq, &cmd, NULL)) {
+ printf("nvme: create cq command failed\n");
+ goto out_disable_ctrl;
+ }
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.opcode = NVME_ADMIN_CMD_CREATE_SQ;
+ cmd.prp1 = (u64)ioq.cmds;
+ cmd.cdw10 = 1; // sq id
+ cmd.cdw10 |= (NVME_QUEUE_SIZE - 1) << 16;
+ cmd.cdw11 = NVME_QUEUE_CONTIGUOUS;
+ cmd.cdw11 |= 1 << 16; // cq id for this sq
+ if (!nvme_exec_command(&adminq, &cmd, NULL)) {
+ printf("nvme: create sq command failed\n");
+ goto out_delete_cq;
+ }
+
+ nvme_initialized = true;
+ printf("nvme: initialized at 0x%lx\n", nvme_base);
+ return true;
+
+out_delete_cq:
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.opcode = NVME_ADMIN_CMD_DELETE_CQ;
+ cmd.cdw10 = 1; // cq id
+ if (!nvme_exec_command(&adminq, &cmd, NULL))
+ printf("nvme: delete cq command failed\n");
+out_disable_ctrl:
+ nvme_ctrl_shutdown();
+ nvme_ctrl_disable();
+ nvme_poll_syslog();
+out_shutdown:
+ rtkit_sleep(nvme_rtkit);
+ // Some machines call this ANS, some ANS2...
+ pmgr_reset(nvme_die, "ANS");
+ pmgr_reset(nvme_die, "ANS2");
+out_rtkit:
+ rtkit_free(nvme_rtkit);
+out_sart:
+ sart_free(nvme_sart);
+out_asc:
+ asc_free(nvme_asc);
+out_ioq:
+ free_queue(&ioq);
+out_adminq:
+ free_queue(&adminq);
+ return false;
+}
+
+void nvme_shutdown(void)
+{
+ if (!nvme_initialized) {
+ printf("nvme: trying to shut down but not initialized\n");
+ return;
+ }
+
+ struct nvme_command cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.opcode = NVME_ADMIN_CMD_DELETE_SQ;
+ cmd.cdw10 = 1; // sq id
+ if (!nvme_exec_command(&adminq, &cmd, NULL))
+ printf("nvme: delete sq command failed\n");
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.opcode = NVME_ADMIN_CMD_DELETE_CQ;
+ cmd.cdw10 = 1; // cq id
+ if (!nvme_exec_command(&adminq, &cmd, NULL))
+ printf("nvme: delete cq command failed\n");
+
+ if (!nvme_ctrl_shutdown())
+ printf("nvme: timeout while waiting for controller shutdown\n");
+ if (!nvme_ctrl_disable())
+ printf("nvme: timeout while waiting for CSTS.RDY to clear\n");
+
+ rtkit_sleep(nvme_rtkit);
+ // Some machines call this ANS, some ANS2...
+ pmgr_reset(nvme_die, "ANS");
+ pmgr_reset(nvme_die, "ANS2");
+ rtkit_free(nvme_rtkit);
+ sart_free(nvme_sart);
+ asc_free(nvme_asc);
+ free_queue(&ioq);
+ free_queue(&adminq);
+ nvme_initialized = false;
+
+ printf("nvme: shutdown done\n");
+}
+
+bool nvme_flush(u32 nsid)
+{
+ struct nvme_command cmd;
+
+ if (!nvme_initialized)
+ return false;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.opcode = NVME_CMD_FLUSH;
+ cmd.nsid = nsid;
+
+ return nvme_exec_command(&ioq, &cmd, NULL);
+}
+
+bool nvme_read(u32 nsid, u64 lba, void *buffer)
+{
+ struct nvme_command cmd;
+ u64 buffer_addr = (u64)buffer;
+
+ if (!nvme_initialized)
+ return false;
+
+ /* no need for 16K alignment here since the NVME page size is 4k */
+ if (buffer_addr & (SZ_4K - 1))
+ return false;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.opcode = NVME_CMD_READ;
+ cmd.nsid = nsid;
+ cmd.prp1 = (u64)buffer_addr;
+ cmd.cdw10 = lba;
+ cmd.cdw11 = lba >> 32;
+ cmd.cdw12 = 1; // 4096 bytes
+
+ return nvme_exec_command(&ioq, &cmd, NULL);
+}
diff --git a/tools/src/nvme.h b/tools/src/nvme.h
new file mode 100644
index 0000000..8989a60
--- /dev/null
+++ b/tools/src/nvme.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef NVME_H
+#define NVME_H
+
+#include "types.h"
+
+bool nvme_init(void);
+void nvme_shutdown(void);
+
+bool nvme_flush(u32 nsid);
+bool nvme_read(u32 nsid, u64 lba, void *buffer);
+
+#endif
diff --git a/tools/src/payload.c b/tools/src/payload.c
new file mode 100644
index 0000000..69c9129
--- /dev/null
+++ b/tools/src/payload.c
@@ -0,0 +1,281 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "payload.h"
+#include "adt.h"
+#include "assert.h"
+#include "chainload.h"
+#include "display.h"
+#include "heapblock.h"
+#include "kboot.h"
+#include "smp.h"
+#include "utils.h"
+
+#include "libfdt/libfdt.h"
+#include "minilzlib/minlzma.h"
+#include "tinf/tinf.h"
+
+// Kernels must be 2MB aligned
+#define KERNEL_ALIGN (2 << 20)
+
+static const u8 gz_magic[] = {0x1f, 0x8b};
+static const u8 xz_magic[] = {0xfd, '7', 'z', 'X', 'Z', 0x00};
+static const u8 fdt_magic[] = {0xd0, 0x0d, 0xfe, 0xed};
+static const u8 kernel_magic[] = {'A', 'R', 'M', 0x64}; // at 0x38
+static const u8 cpio_magic[] = {'0', '7', '0', '7', '0'}; // '1' or '2' next
+static const u8 img4_magic[] = {0x16, 0x04, 'I', 'M', 'G', '4'}; // IA5String 'IMG4'
+static const u8 sig_magic[] = {'m', '1', 'n', '1', '_', 's', 'i', 'g'};
+static const u8 empty[] = {0, 0, 0, 0};
+
+static char expect_compatible[256];
+static struct kernel_header *kernel = NULL;
+static void *fdt = NULL;
+static char *chainload_spec = NULL;
+
+static void *load_one_payload(void *start, size_t size);
+
+static void finalize_uncompression(void *dest, size_t dest_len)
+{
+ // Actually reserve the space. malloc is safe after this, but...
+ assert(dest == heapblock_alloc_aligned(dest_len, KERNEL_ALIGN));
+
+ void *end = ((u8 *)dest) + dest_len;
+ void *next = load_one_payload(dest, dest_len);
+ assert(!next || next >= dest);
+
+ // If the payload needs padding, we need to reserve more, so it better have not used
+ // malloc either.
+ if (next > end) {
+ // Explicitly *un*aligned or it'll fail this assert, since 64b alignment is the default
+ assert(end == heapblock_alloc_aligned((u8 *)next - (u8 *)end, 1));
+ }
+}
+
+static void *decompress_gz(void *p, size_t size)
+{
+ unsigned int source_len = size, dest_len = 1 << 30; // 1 GiB should be enough hopefully
+
+ // Start at the end of the heap area, no allocation yet. The following code must not use
+ // malloc or heapblock, until finalize_uncompression is called.
+ void *dest = heapblock_alloc_aligned(0, KERNEL_ALIGN);
+
+ printf("Uncompressing... ");
+ int ret = tinf_gzip_uncompress(dest, &dest_len, p, &source_len);
+
+ if (ret != TINF_OK) {
+ printf("Error %d\n", ret);
+ return NULL;
+ }
+
+ printf("%d bytes uncompressed to %d bytes\n", source_len, dest_len);
+
+ finalize_uncompression(dest, dest_len);
+
+ return ((u8 *)p) + source_len;
+}
+
+static void *decompress_xz(void *p, size_t size)
+{
+ uint32_t source_len = size, dest_len = 1 << 30; // 1 GiB should be enough hopefully
+
+ // Start at the end of the heap area, no allocation yet. The following code must not use
+ // malloc or heapblock, until finalize_uncompression is called.
+ void *dest = heapblock_alloc_aligned(0, KERNEL_ALIGN);
+
+ printf("Uncompressing... ");
+ int ret = XzDecode(p, &source_len, dest, &dest_len);
+
+ if (!ret) {
+ printf("XZ decode failed\n");
+ return NULL;
+ }
+
+ printf("%d bytes uncompressed to %d bytes\n", source_len, dest_len);
+
+ finalize_uncompression(dest, dest_len);
+
+ return ((u8 *)p) + source_len;
+}
+
+static void *load_fdt(void *p, size_t size)
+{
+ if (fdt_node_check_compatible(p, 0, expect_compatible) == 0) {
+ printf("Found a devicetree for %s at %p\n", expect_compatible, p);
+ fdt = p;
+ }
+ assert(!size || size == fdt_totalsize(p));
+ return ((u8 *)p) + fdt_totalsize(p);
+}
+
+static void *load_cpio(void *p, size_t size)
+{
+ if (!size) {
+ // We could handle this, but who uses uncompressed initramfs?
+ printf("Uncompressed cpio archives not supported\n");
+ return NULL;
+ }
+
+ kboot_set_initrd(p, size);
+ return ((u8 *)p) + size;
+}
+
+static void *load_kernel(void *p, size_t size)
+{
+ kernel = p;
+
+ assert(size <= kernel->image_size);
+
+ // If this is an in-line kernel, it's probably not aligned, so we need to make a copy
+ if (((u64)kernel) & (KERNEL_ALIGN - 1)) {
+ void *new_addr = heapblock_alloc_aligned(kernel->image_size, KERNEL_ALIGN);
+ memcpy(new_addr, kernel, size ? size : kernel->image_size);
+ kernel = new_addr;
+ }
+
+ /*
+ * Kernel blobs unfortunately do not have an accurate file size header, so
+ * this will fail for in-line payloads. However, conversely, this is required for
+ * compressed payloads, in order to allocate padding that the kernel needs, which will be
+ * beyond the end of the compressed data. So if we know the input size, tell the caller
+ * about the true image size; otherwise don't.
+ */
+ if (size) {
+ return ((u8 *)p) + kernel->image_size;
+ } else {
+ return NULL;
+ }
+}
+
+#define MAX_VAR_NAME 64
+#define MAX_VAR_SIZE 1024
+
+#define IS_VAR(x) !strncmp((char *)*p, x, strlen(x))
+
+#define MAX_CHOSEN_VARS 16
+
+static size_t chosen_cnt = 0;
+static char *chosen[MAX_CHOSEN_VARS];
+
+static bool check_var(u8 **p)
+{
+ char *val = memchr(*p, '=', strnlen((char *)*p, MAX_VAR_NAME + 1));
+ if (!val)
+ return false;
+
+ val++;
+
+ char *end = memchr(val, '\n', strnlen(val, MAX_VAR_SIZE + 1));
+ if (!end)
+ return false;
+
+ *end = 0;
+ printf("Found a variable at %p: %s\n", *p, (char *)*p);
+
+ if (IS_VAR("chosen.")) {
+ if (chosen_cnt >= MAX_CHOSEN_VARS)
+ printf("Too many chosen vars, ignoring %s\n", *p);
+ else
+ chosen[chosen_cnt++] = (char *)*p;
+ } else if (IS_VAR("chainload=")) {
+ chainload_spec = val;
+ } else if (IS_VAR("display=")) {
+ display_configure(val);
+ } else {
+ printf("Unknown variable %s\n", *p);
+ }
+
+ *p = (u8 *)(end + 1);
+ return true;
+}
+
+static void *load_one_payload(void *start, size_t size)
+{
+ u8 *p = start;
+
+ if (!start)
+ return NULL;
+
+ if (!memcmp(p, gz_magic, sizeof gz_magic)) {
+ printf("Found a gzip compressed payload at %p\n", p);
+ return decompress_gz(p, size);
+ } else if (!memcmp(p, xz_magic, sizeof xz_magic)) {
+ printf("Found an XZ compressed payload at %p\n", p);
+ return decompress_xz(p, size);
+ } else if (!memcmp(p, fdt_magic, sizeof fdt_magic)) {
+ return load_fdt(p, size);
+ } else if (!memcmp(p, cpio_magic, sizeof cpio_magic)) {
+ printf("Found a cpio initramfs at %p\n", p);
+ return load_cpio(p, size);
+ } else if (!memcmp(p + 0x38, kernel_magic, sizeof kernel_magic)) {
+ printf("Found a kernel at %p\n", p);
+ return load_kernel(p, size);
+ } else if (!memcmp(p, sig_magic, sizeof sig_magic)) {
+ u32 size;
+ memcpy(&size, p + 8, 4);
+
+ printf("Found a m1n1 signature at %p, skipping 0x%x bytes\n", p, size);
+ return p + size;
+ } else if (check_var(&p)) {
+ return p;
+ } else if (!memcmp(p, empty, sizeof empty) ||
+ !memcmp(p + 0x05, img4_magic, sizeof img4_magic)) { // SEPFW after m1n1
+ printf("No more payloads at %p\n", p);
+ return NULL;
+ } else {
+ printf("Unknown payload at %p (magic: %02x%02x%02x%02x)\n", p, p[0], p[1], p[2], p[3]);
+ return NULL;
+ }
+}
+
+int payload_run(void)
+{
+ const char *target = adt_getprop(adt, 0, "target-type", NULL);
+ if (target) {
+ strcpy(expect_compatible, "apple,");
+ char *p = expect_compatible + strlen(expect_compatible);
+ while (*target && p != expect_compatible + sizeof(expect_compatible) - 1) {
+ *p++ = tolower(*target++);
+ }
+ *p = 0;
+ printf("Devicetree compatible value: %s\n", expect_compatible);
+ } else {
+ printf("Cannot find target type! %p %p\n", target, adt);
+ return -1;
+ }
+
+ chosen_cnt = 0;
+
+ void *p = _payload_start;
+
+ while (p)
+ p = load_one_payload(p, 0);
+
+ if (chainload_spec) {
+ return chainload_load(chainload_spec, chosen, chosen_cnt);
+ }
+
+ if (kernel && fdt) {
+ smp_start_secondaries();
+
+ for (size_t i = 0; i < chosen_cnt; i++) {
+ char *val = memchr(chosen[i], '=', MAX_VAR_NAME + 1);
+
+ assert(val);
+ val[0] = 0; // Terminate var name
+ if (kboot_set_chosen(chosen[i] + 7, val + 1) < 0)
+ printf("Failed to kboot set %s='%s'\n", chosen[i], val);
+ }
+
+ if (kboot_prepare_dt(fdt)) {
+ printf("Failed to prepare FDT!\n");
+ return -1;
+ }
+
+ return kboot_boot(kernel);
+ } else if (kernel && !fdt) {
+ printf("ERROR: Kernel found but no devicetree for %s available.\n", expect_compatible);
+ } else if (!kernel && fdt) {
+ printf("ERROR: Devicetree found but no kernel.\n");
+ }
+
+ return -1;
+}
diff --git a/tools/src/payload.h b/tools/src/payload.h
new file mode 100644
index 0000000..8e6aa72
--- /dev/null
+++ b/tools/src/payload.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef __PAYLOAD_H__
+#define __PAYLOAD_H__
+
+int payload_run(void);
+
+#endif
diff --git a/tools/src/pcie.c b/tools/src/pcie.c
new file mode 100644
index 0000000..39d6a23
--- /dev/null
+++ b/tools/src/pcie.c
@@ -0,0 +1,388 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "adt.h"
+#include "pcie.h"
+#include "pmgr.h"
+#include "tunables.h"
+#include "utils.h"
+
+/*
+ * The ADT uses 17 register sets:
+ *
+ * 0: 90000000 00000006 10000000 00000000 ECAM
+ * 1: 80000000 00000006 00040000 00000000 RC
+ * 2: 80080000 00000006 00090000 00000000 PHY
+ * 3: 800c0000 00000006 00020000 00000000 PHY IP
+ * 4: 8c000000 00000006 00004000 00000000 AXI
+ * 5: 3d2bc000 00000000 00001000 00000000 fuses
+ * 6: 81000000 00000006 00008000 00000000 port 0 config
+ * 7: 81010000 00000006 00001000 00000000 port 0 LTSSM debug
+ * 8: 80084000 00000006 00004000 00000000 port 0 PHY
+ * 9: 800c8000 00000006 00016610 00000000 port 0 PHY IP
+ <macOS 12.0 RC and later add a per-port Intr2AXI reg here>
+ * 10: 82000000 00000006 00008000 00000000 port 1 config
+ * 11: 82010000 00000006 00001000 00000000 port 1 LTSSM debug
+ * 12: 80088000 00000006 00004000 00000000 port 1 PHY
+ * 13: 800d0000 00000006 00006000 00000000 port 1 PHY IP
+ <...>
+ * 14: 83000000 00000006 00008000 00000000 port 2 config
+ * 15: 83010000 00000006 00001000 00000000 port 2 LTSSM debug
+ * 16: 8008c000 00000006 00004000 00000000 port 2 PHY
+ * 17: 800d8000 00000006 00006000 00000000 port 2 PHY IP
+ <...>
+ */
+
+/* PHY registers */
+
+#define APCIE_PHY_CTRL 0x000
+#define APCIE_PHY_CTRL_CLK0REQ BIT(0)
+#define APCIE_PHY_CTRL_CLK1REQ BIT(1)
+#define APCIE_PHY_CTRL_CLK0ACK BIT(2)
+#define APCIE_PHY_CTRL_CLK1ACK BIT(3)
+#define APCIE_PHY_CTRL_RESET BIT(7)
+
+#define APCIE_PHYIF_CTRL 0x024
+#define APCIE_PHYIF_CTRL_RUN BIT(0)
+
+/* Port registers */
+
+#define APCIE_PORT_LINKSTS 0x208
+#define APCIE_PORT_LINKSTS_BUSY BIT(2)
+
+#define APCIE_PORT_APPCLK 0x800
+#define APCIE_PORT_APPCLK_EN BIT(0)
+
+#define APCIE_PORT_STATUS 0x804
+#define APCIE_PORT_STATUS_RUN BIT(0)
+
+#define APCIE_PORT_RESET 0x814
+#define APCIE_PORT_RESET_DIS BIT(0)
+
+/* PCIe capability registers */
+#define PCIE_CAP_BASE 0x70
+#define PCIE_LNKCAP 0x0c
+#define PCIE_LNKCAP_SLS GENMASK(3, 0)
+#define PCIE_LNKCAP2 0x2c
+#define PCIE_LNKCAP2_SLS GENMASK(6, 1)
+#define PCIE_LNKCTL2 0x30
+#define PCIE_LNKCTL2_TLS GENMASK(3, 0)
+
+/* DesignWare PCIe Core registers */
+
+#define DWC_DBI_RO_WR 0x8bc
+#define DWC_DBI_RO_WR_EN BIT(0)
+
+#define DWC_DBI_LINK_WIDTH_SPEED_CONTROL 0x80c
+#define DWC_DBI_SPEED_CHANGE BIT(17)
+
+struct fuse_bits {
+ u16 src_reg;
+ u16 tgt_reg;
+ u8 src_bit;
+ u8 tgt_bit;
+ u8 width;
+};
+
+const struct fuse_bits pcie_fuse_bits_t8103[] = {
+ {0x0084, 0x6238, 4, 0, 6}, {0x0084, 0x6220, 10, 14, 3}, {0x0084, 0x62a4, 13, 17, 2},
+ {0x0418, 0x522c, 27, 9, 2}, {0x0418, 0x522c, 13, 12, 3}, {0x0418, 0x5220, 18, 14, 3},
+ {0x0418, 0x52a4, 21, 17, 2}, {0x0418, 0x522c, 23, 16, 5}, {0x0418, 0x5278, 23, 20, 3},
+ {0x0418, 0x5018, 31, 2, 1}, {0x041c, 0x1204, 0, 2, 5}, {},
+};
+
+const struct fuse_bits pcie_fuse_bits_t6000[] = {
+ {0x004c, 0x1004, 3, 2, 5}, {0x0048, 0x522c, 26, 16, 5}, {0x0048, 0x522c, 29, 9, 2},
+ {0x0048, 0x522c, 26, 12, 3}, {0x0048, 0x522c, 26, 16, 5}, {0x0048, 0x52a4, 24, 17, 2},
+ {0x004c, 0x5018, 2, 3, 1}, {0x0048, 0x50a4, 14, 17, 2}, {0x0048, 0x62a4, 14, 17, 2},
+ {0x0048, 0x6220, 8, 14, 3}, {0x0048, 0x6238, 2, 0, 6}, {},
+};
+
+/* clang-format off */
+const struct fuse_bits pcie_fuse_bits_t8112[] = {
+ {0x0490, 0x6238, 0, 0, 6}, {0x0490, 0x6220, 6, 14, 3}, {0x0490, 0x62a4, 12, 17, 2},
+ {0x0490, 0x5018, 14, 2, 1}, {0x0490, 0x5220, 15, 14, 3}, {0x0490, 0x52a4, 18, 17, 2},
+ {0x0490, 0x5278, 20, 20, 3}, {0x0490, 0x522c, 23, 12, 3}, {0x0490, 0x522c, 26, 9, 2},
+ {0x0490, 0x522c, 28, 16, 4}, {0x0494, 0x522c, 0, 20, 1}, {0x0494, 0x1204, 5, 2, 5},
+ {},
+};
+/* clang-format on */
+
+static bool pcie_initialized = false;
+static u64 rc_base;
+static u64 phy_base;
+static u64 phy_ip_base;
+static u64 fuse_base;
+static u32 port_count;
+static u64 port_base[8];
+
+#define SHARED_REG_COUNT 6
+
+int pcie_init(void)
+{
+ const char *path = "/arm-io/apcie";
+ int adt_path[8];
+ int adt_offset;
+ const struct fuse_bits *fuse_bits;
+
+ if (pcie_initialized)
+ return 0;
+
+ adt_offset = adt_path_offset_trace(adt, path, adt_path);
+ if (adt_offset < 0) {
+ printf("pcie: Error getting node %s\n", path);
+ return -1;
+ }
+
+ if (adt_is_compatible(adt, adt_offset, "apcie,t8103")) {
+ fuse_bits = pcie_fuse_bits_t8103;
+ printf("pcie: Initializing t8103 PCIe controller\n");
+ } else if (adt_is_compatible(adt, adt_offset, "apcie,t6000")) {
+ fuse_bits = pcie_fuse_bits_t6000;
+ printf("pcie: Initializing t6000 PCIe controller\n");
+ } else if (adt_is_compatible(adt, adt_offset, "apcie,t8112")) {
+ fuse_bits = pcie_fuse_bits_t8112;
+ printf("pcie: Initializing t8112 PCIe controller\n");
+ } else {
+ printf("pcie: Unsupported compatible\n");
+ return -1;
+ }
+
+ if (ADT_GETPROP(adt, adt_offset, "#ports", &port_count) < 0) {
+ printf("pcie: Error getting port count for %s\n", path);
+ return -1;
+ }
+
+ u64 config_base;
+ if (adt_get_reg(adt, adt_path, "reg", 0, &config_base, NULL)) {
+ printf("pcie: Error getting reg with index %d for %s\n", 0, path);
+ return -1;
+ }
+
+ if (adt_get_reg(adt, adt_path, "reg", 1, &rc_base, NULL)) {
+ printf("pcie: Error getting reg with index %d for %s\n", 1, path);
+ return -1;
+ }
+
+ if (adt_get_reg(adt, adt_path, "reg", 2, &phy_base, NULL)) {
+ printf("pcie: Error getting reg with index %d for %s\n", 2, path);
+ return -1;
+ }
+
+ if (adt_get_reg(adt, adt_path, "reg", 3, &phy_ip_base, NULL)) {
+ printf("pcie: Error getting reg with index %d for %s\n", 3, path);
+ return -1;
+ }
+
+ if (adt_get_reg(adt, adt_path, "reg", 5, &fuse_base, NULL)) {
+ printf("pcie: Error getting reg with index %d for %s\n", 5, path);
+ return -1;
+ }
+
+ u32 reg_len;
+ if (!adt_getprop(adt, adt_offset, "reg", &reg_len)) {
+ printf("pcie: Error getting reg length for %s\n", path);
+ return -1;
+ }
+
+ int port_regs = (reg_len / 16) - SHARED_REG_COUNT;
+
+ if (port_regs % port_count) {
+ printf("pcie: %d port registers do not evenly divide into %d ports\n", port_regs,
+ port_count);
+ return -1;
+ }
+
+ int port_reg_cnt = port_regs / port_count;
+ printf("pcie: ADT uses %d reg entries per port\n", port_reg_cnt);
+
+ if (pmgr_adt_power_enable(path)) {
+ printf("pcie: Error enabling power for %s\n", path);
+ return -1;
+ }
+
+ if (tunables_apply_local(path, "apcie-axi2af-tunables", 4)) {
+ printf("pcie: Error applying %s for %s\n", "apcie-axi2af-tunables", path);
+ return -1;
+ }
+
+ /* ??? */
+ write32(rc_base + 0x4, 0);
+
+ if (tunables_apply_local(path, "apcie-common-tunables", 1)) {
+ printf("pcie: Error applying %s for %s\n", "apcie-common-tunables", path);
+ return -1;
+ }
+
+ /*
+ * Initialize PHY.
+ */
+
+ if (tunables_apply_local(path, "apcie-phy-tunables", 2)) {
+ printf("pcie: Error applying %s for %s\n", "apcie-phy-tunables", path);
+ return -1;
+ }
+
+ set32(phy_base + APCIE_PHY_CTRL, APCIE_PHY_CTRL_CLK0REQ);
+ if (poll32(phy_base + APCIE_PHY_CTRL, APCIE_PHY_CTRL_CLK0ACK, APCIE_PHY_CTRL_CLK0ACK, 50000)) {
+ printf("pcie: Timeout enabling PHY CLK0\n");
+ return -1;
+ }
+
+ set32(phy_base + APCIE_PHY_CTRL, APCIE_PHY_CTRL_CLK1REQ);
+ if (poll32(phy_base + APCIE_PHY_CTRL, APCIE_PHY_CTRL_CLK1ACK, APCIE_PHY_CTRL_CLK1ACK, 50000)) {
+ printf("pcie: Timeout enabling PHY CLK1\n");
+ return -1;
+ }
+
+ clear32(phy_base + APCIE_PHY_CTRL, APCIE_PHY_CTRL_RESET);
+ udelay(1);
+
+ /* ??? */
+ set32(rc_base + APCIE_PHYIF_CTRL, APCIE_PHYIF_CTRL_RUN);
+ udelay(1);
+
+ /* Apply "fuses". */
+ for (int i = 0; fuse_bits[i].width; i++) {
+ u32 fuse;
+ fuse = (read32(fuse_base + fuse_bits[i].src_reg) >> fuse_bits[i].src_bit);
+ fuse &= (1 << fuse_bits[i].width) - 1;
+ mask32(phy_ip_base + fuse_bits[i].tgt_reg,
+ ((1 << fuse_bits[i].width) - 1) << fuse_bits[i].tgt_bit,
+ fuse << fuse_bits[i].tgt_bit);
+ }
+
+ if (tunables_apply_local(path, "apcie-phy-ip-pll-tunables", 3)) {
+ printf("pcie: Error applying %s for %s\n", "apcie-phy-ip-pll-tunables", path);
+ return -1;
+ }
+ if (tunables_apply_local(path, "apcie-phy-ip-auspma-tunables", 3)) {
+ printf("pcie: Error applying %s for %s\n", "apcie-phy-ip-auspma-tunables", path);
+ return -1;
+ }
+
+ for (u32 port = 0; port < port_count; port++) {
+ char bridge[64];
+ int bridge_offset;
+
+ /*
+ * Initialize RC port.
+ */
+
+ snprintf(bridge, sizeof(bridge), "/arm-io/apcie/pci-bridge%d", port);
+
+ if ((bridge_offset = adt_path_offset(adt, bridge)) < 0)
+ continue;
+
+ printf("pcie: Initializing port %d\n", port);
+
+ if (adt_get_reg(adt, adt_path, "reg", port * port_reg_cnt + SHARED_REG_COUNT,
+ &port_base[port], NULL)) {
+ printf("pcie: Error getting reg with index %d for %s\n",
+ port * port_reg_cnt + SHARED_REG_COUNT, path);
+ return -1;
+ }
+
+ if (tunables_apply_local_addr(bridge, "apcie-config-tunables", port_base[port])) {
+ printf("pcie: Error applying %s for %s\n", "apcie-config-tunables", bridge);
+ return -1;
+ }
+
+ set32(port_base[port] + APCIE_PORT_APPCLK, APCIE_PORT_APPCLK_EN);
+
+ /* PERSTN */
+ set32(port_base[port] + APCIE_PORT_RESET, APCIE_PORT_RESET_DIS);
+
+ if (poll32(port_base[port] + APCIE_PORT_STATUS, APCIE_PORT_STATUS_RUN,
+ APCIE_PORT_STATUS_RUN, 250000)) {
+ printf("pcie: Port failed to come up on %s\n", bridge);
+ return -1;
+ }
+
+ if (poll32(port_base[port] + APCIE_PORT_LINKSTS, APCIE_PORT_LINKSTS_BUSY, 0, 250000)) {
+ printf("pcie: Port failed to become idle on %s\n", bridge);
+ return -1;
+ }
+
+ /* Make Designware PCIe Core registers writable. */
+ set32(config_base + DWC_DBI_RO_WR, DWC_DBI_RO_WR_EN);
+
+ if (tunables_apply_local_addr(bridge, "pcie-rc-tunables", config_base)) {
+ printf("pcie: Error applying %s for %s\n", "pcie-rc-tunables", bridge);
+ return -1;
+ }
+ if (tunables_apply_local_addr(bridge, "pcie-rc-gen3-shadow-tunables", config_base)) {
+ printf("pcie: Error applying %s for %s\n", "pcie-rc-gen3-shadow-tunables", bridge);
+ return -1;
+ }
+ if (tunables_apply_local_addr(bridge, "pcie-rc-gen4-shadow-tunables", config_base)) {
+ printf("pcie: Error applying %s for %s\n", "pcie-rc-gen4-shadow-tunables", bridge);
+ return -1;
+ }
+
+ u32 max_speed;
+ if (ADT_GETPROP(adt, bridge_offset, "maximum-link-speed", &max_speed) >= 0) {
+ /* Apple changed how they announce the link speed for the 10gb nic
+ * at the latest in MacOS 12.3. The "lan-10gb" subnode has now a
+ * "target-link-speed" property and "maximum-link-speed" remains
+ * at 1.
+ */
+ int lan_10gb = adt_subnode_offset(adt, bridge_offset, "lan-10gb");
+ if (lan_10gb > 0) {
+ int target_speed;
+ if (ADT_GETPROP(adt, lan_10gb, "target-link-speed", &target_speed) >= 0) {
+ if (target_speed > 0)
+ max_speed = target_speed;
+ }
+ }
+
+ printf("pcie: Port %d max speed = %d\n", port, max_speed);
+
+ if (max_speed == 0) {
+ printf("pcie: Invalid max-speed\n");
+ return -1;
+ }
+
+ mask32(config_base + PCIE_CAP_BASE + PCIE_LNKCAP, PCIE_LNKCAP_SLS,
+ FIELD_PREP(PCIE_LNKCAP_SLS, max_speed));
+
+ mask32(config_base + PCIE_CAP_BASE + PCIE_LNKCAP2, PCIE_LNKCAP2_SLS,
+ FIELD_PREP(PCIE_LNKCAP2_SLS, (1 << max_speed) - 1));
+
+ mask16(config_base + PCIE_CAP_BASE + PCIE_LNKCTL2, PCIE_LNKCTL2_TLS,
+ FIELD_PREP(PCIE_LNKCTL2_TLS, max_speed));
+ }
+
+ set32(config_base + DWC_DBI_LINK_WIDTH_SPEED_CONTROL, DWC_DBI_SPEED_CHANGE);
+
+ /* Make Designware PCIe Core registers readonly. */
+ clear32(config_base + DWC_DBI_RO_WR, DWC_DBI_RO_WR_EN);
+
+ /* Move to the next PCIe device on this bus. */
+ config_base += (1 << 15);
+ }
+
+ pcie_initialized = true;
+ printf("pcie: Initialized.\n");
+
+ return 0;
+}
+
+int pcie_shutdown(void)
+{
+ if (!pcie_initialized)
+ return 0;
+
+ for (u32 port = 0; port < port_count; port++) {
+ clear32(port_base[port] + APCIE_PORT_RESET, APCIE_PORT_RESET_DIS);
+ clear32(port_base[port] + APCIE_PORT_APPCLK, APCIE_PORT_APPCLK_EN);
+ }
+
+ clear32(phy_base + APCIE_PHY_CTRL, APCIE_PHY_CTRL_RESET);
+ clear32(phy_base + APCIE_PHY_CTRL, APCIE_PHY_CTRL_CLK1REQ);
+ clear32(phy_base + APCIE_PHY_CTRL, APCIE_PHY_CTRL_CLK0REQ);
+
+ pcie_initialized = false;
+ printf("pcie: Shutdown.\n");
+
+ return 0;
+}
diff --git a/tools/src/pcie.h b/tools/src/pcie.h
new file mode 100644
index 0000000..e33d59d
--- /dev/null
+++ b/tools/src/pcie.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef PCIE_H
+#define PCIE_H
+
+int pcie_init(void);
+int pcie_shutdown(void);
+
+#endif
diff --git a/tools/src/pmgr.c b/tools/src/pmgr.c
new file mode 100644
index 0000000..0ae3888
--- /dev/null
+++ b/tools/src/pmgr.c
@@ -0,0 +1,358 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "pmgr.h"
+#include "adt.h"
+#include "string.h"
+#include "types.h"
+#include "utils.h"
+
+#define PMGR_RESET BIT(31)
+#define PMGR_AUTO_ENABLE BIT(28)
+#define PMGR_PS_AUTO GENMASK(27, 24)
+#define PMGR_PARENT_OFF BIT(11)
+#define PMGR_DEV_DISABLE BIT(10)
+#define PMGR_WAS_CLKGATED BIT(9)
+#define PMGR_WAS_PWRGATED BIT(8)
+#define PMGR_PS_ACTUAL GENMASK(7, 4)
+#define PMGR_PS_TARGET GENMASK(3, 0)
+
+#define PMGR_PS_ACTIVE 0xf
+#define PMGR_PS_CLKGATE 0x4
+#define PMGR_PS_PWRGATE 0x0
+
+#define PMGR_POLL_TIMEOUT 10000
+
+#define PMGR_FLAG_VIRTUAL 0x10
+
+struct pmgr_device {
+ u32 flags;
+ u16 parent[2];
+ u8 unk1[2];
+ u8 addr_offset;
+ u8 psreg_idx;
+ u8 unk2[14];
+ u16 id;
+ u8 unk3[4];
+ const char name[0x10];
+} PACKED;
+
+static int pmgr_initialized = 0;
+
+static int pmgr_path[8];
+static int pmgr_offset;
+static int pmgr_dies;
+
+static const u32 *pmgr_ps_regs = NULL;
+static u32 pmgr_ps_regs_len = 0;
+
+static const struct pmgr_device *pmgr_devices = NULL;
+static u32 pmgr_devices_len = 0;
+
+static uintptr_t pmgr_get_psreg(u8 idx)
+{
+ if (idx * 12 >= pmgr_ps_regs_len) {
+ printf("pmgr: Index %d is out of bounds for ps-regs\n", idx);
+ return 0;
+ }
+
+ u32 reg_idx = pmgr_ps_regs[3 * idx];
+ u32 reg_offset = pmgr_ps_regs[3 * idx + 1];
+
+ u64 pmgr_reg;
+ if (adt_get_reg(adt, pmgr_path, "reg", reg_idx, &pmgr_reg, NULL) < 0) {
+ printf("pmgr: Error getting /arm-io/pmgr regs\n");
+ return 0;
+ }
+
+ return pmgr_reg + reg_offset;
+}
+
+static int pmgr_set_mode(uintptr_t addr, u8 target_mode)
+{
+ mask32(addr, PMGR_PS_TARGET, FIELD_PREP(PMGR_PS_TARGET, target_mode));
+ if (poll32(addr, PMGR_PS_ACTUAL, FIELD_PREP(PMGR_PS_ACTUAL, target_mode), PMGR_POLL_TIMEOUT) <
+ 0) {
+ printf("pmgr: timeout while trying to set mode %x for device at 0x%lx: %x\n", target_mode,
+ addr, read32(addr));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int pmgr_find_device(u16 id, const struct pmgr_device **device)
+{
+ for (size_t i = 0; i < pmgr_devices_len; ++i) {
+ const struct pmgr_device *i_device = &pmgr_devices[i];
+ if (i_device->id != id)
+ continue;
+
+ *device = i_device;
+ return 0;
+ }
+
+ return -1;
+}
+
+static uintptr_t pmgr_device_get_addr(u8 die, const struct pmgr_device *device)
+{
+ uintptr_t addr = pmgr_get_psreg(device->psreg_idx);
+ if (addr == 0)
+ return 0;
+
+ addr += PMGR_DIE_OFFSET * die;
+
+ addr += (device->addr_offset << 3);
+ return addr;
+}
+
+static int pmgr_set_mode_recursive(u8 die, u16 id, u8 target_mode, bool recurse)
+{
+ if (!pmgr_initialized) {
+ printf("pmgr: pmgr_set_mode_recursive() called before successful pmgr_init()\n");
+ return -1;
+ }
+
+ if (id == 0)
+ return -1;
+
+ const struct pmgr_device *device;
+
+ if (pmgr_find_device(id, &device))
+ return -1;
+
+ if (!(device->flags & PMGR_FLAG_VIRTUAL)) {
+ uintptr_t addr = pmgr_device_get_addr(die, device);
+ if (!addr)
+ return -1;
+ if (pmgr_set_mode(addr, target_mode))
+ return -1;
+ }
+ if (!recurse)
+ return 0;
+
+ for (int i = 0; i < 2; i++) {
+ if (device->parent[i]) {
+ u16 parent = FIELD_GET(PMGR_DEVICE_ID, device->parent[i]);
+ int ret = pmgr_set_mode_recursive(die, parent, target_mode, true);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int pmgr_power_enable(u32 id)
+{
+ u16 device = FIELD_GET(PMGR_DEVICE_ID, id);
+ u8 die = FIELD_GET(PMGR_DIE_ID, id);
+ return pmgr_set_mode_recursive(die, device, PMGR_PS_ACTIVE, true);
+}
+
+int pmgr_power_disable(u32 id)
+{
+ u16 device = FIELD_GET(PMGR_DEVICE_ID, id);
+ u8 die = FIELD_GET(PMGR_DIE_ID, id);
+ return pmgr_set_mode_recursive(die, device, PMGR_PS_PWRGATE, false);
+}
+
+static int pmgr_adt_find_devices(const char *path, const u32 **devices, u32 *n_devices)
+{
+ int node_offset = adt_path_offset(adt, path);
+ if (node_offset < 0) {
+ printf("pmgr: Error getting node %s\n", path);
+ return -1;
+ }
+
+ *devices = adt_getprop(adt, node_offset, "clock-gates", n_devices);
+ if (*devices == NULL || *n_devices == 0) {
+ printf("pmgr: Error getting %s clock-gates.\n", path);
+ return -1;
+ }
+
+ *n_devices /= 4;
+
+ return 0;
+}
+
+static int pmgr_adt_devices_set_mode(const char *path, u8 target_mode, int recurse)
+{
+ const u32 *devices;
+ u32 n_devices;
+ int ret = 0;
+
+ if (pmgr_adt_find_devices(path, &devices, &n_devices) < 0)
+ return -1;
+
+ for (u32 i = 0; i < n_devices; ++i) {
+ u16 device = FIELD_GET(PMGR_DEVICE_ID, devices[i]);
+ u8 die = FIELD_GET(PMGR_DIE_ID, devices[i]);
+ if (pmgr_set_mode_recursive(die, device, target_mode, recurse))
+ ret = -1;
+ }
+
+ return ret;
+}
+
+int pmgr_adt_power_enable(const char *path)
+{
+ int ret = pmgr_adt_devices_set_mode(path, PMGR_PS_ACTIVE, true);
+ return ret;
+}
+
+int pmgr_adt_power_disable(const char *path)
+{
+ return pmgr_adt_devices_set_mode(path, PMGR_PS_PWRGATE, false);
+}
+
+static int pmgr_reset_device(int die, const struct pmgr_device *dev)
+{
+ if (die < 0 || die > 16) {
+ printf("pmgr: invalid die id %d for device %s\n", die, dev->name);
+ return -1;
+ }
+
+ uintptr_t addr = pmgr_device_get_addr(die, dev);
+
+ u32 reg = read32(addr);
+ if (FIELD_GET(PMGR_PS_ACTUAL, reg) != PMGR_PS_ACTIVE) {
+ printf("pmgr: will not reset disabled device %d.%s\n", die, dev->name);
+ return -1;
+ }
+
+ printf("pmgr: resetting device %d.%s\n", die, dev->name);
+
+ set32(addr, PMGR_DEV_DISABLE);
+ set32(addr, PMGR_RESET);
+ udelay(10);
+ clear32(addr, PMGR_RESET);
+ clear32(addr, PMGR_DEV_DISABLE);
+
+ return 0;
+}
+
+int pmgr_adt_reset(const char *path)
+{
+ const u32 *devices;
+ u32 n_devices;
+ int ret = 0;
+
+ if (pmgr_adt_find_devices(path, &devices, &n_devices) < 0)
+ return -1;
+
+ for (u32 i = 0; i < n_devices; ++i) {
+ const struct pmgr_device *device;
+ u16 id = FIELD_GET(PMGR_DEVICE_ID, devices[i]);
+ u8 die = FIELD_GET(PMGR_DIE_ID, devices[i]);
+
+ if (pmgr_find_device(id, &device)) {
+ ret = -1;
+ continue;
+ }
+
+ if (pmgr_reset_device(die, device))
+ ret = -1;
+ }
+
+ return ret;
+}
+
+int pmgr_reset(int die, const char *name)
+{
+ const struct pmgr_device *dev = NULL;
+
+ for (unsigned int i = 0; i < pmgr_devices_len; ++i) {
+ if (strncmp(pmgr_devices[i].name, name, 0x10) == 0) {
+ dev = &pmgr_devices[i];
+ break;
+ }
+ }
+
+ if (!dev)
+ return -1;
+
+ return pmgr_reset_device(die, dev);
+}
+
+int pmgr_init(void)
+{
+ int node = adt_path_offset(adt, "/arm-io");
+ if (node < 0) {
+ printf("pmgr: Error getting /arm-io node\n");
+ return -1;
+ }
+ if (ADT_GETPROP(adt, node, "die-count", &pmgr_dies) < 0)
+ pmgr_dies = 1;
+
+ pmgr_offset = adt_path_offset_trace(adt, "/arm-io/pmgr", pmgr_path);
+ if (pmgr_offset < 0) {
+ printf("pmgr: Error getting /arm-io/pmgr node\n");
+ return -1;
+ }
+
+ pmgr_ps_regs = adt_getprop(adt, pmgr_offset, "ps-regs", &pmgr_ps_regs_len);
+ if (pmgr_ps_regs == NULL || pmgr_ps_regs_len == 0) {
+ printf("pmgr: Error getting /arm-io/pmgr ps-regs\n.");
+ return -1;
+ }
+
+ pmgr_devices = adt_getprop(adt, pmgr_offset, "devices", &pmgr_devices_len);
+ if (pmgr_devices == NULL || pmgr_devices_len == 0) {
+ printf("pmgr: Error getting /arm-io/pmgr devices.\n");
+ return -1;
+ }
+
+ pmgr_devices_len /= sizeof(*pmgr_devices);
+ pmgr_initialized = 1;
+
+ printf("pmgr: Cleaning up device states...\n");
+
+ for (u8 die = 0; die < pmgr_dies; ++die) {
+ for (size_t i = 0; i < pmgr_devices_len; ++i) {
+ const struct pmgr_device *device = &pmgr_devices[i];
+
+ if ((device->flags & PMGR_FLAG_VIRTUAL))
+ continue;
+
+ uintptr_t addr = pmgr_device_get_addr(die, device);
+ if (!addr)
+ continue;
+
+ u32 reg = read32(addr);
+
+ if (reg & PMGR_AUTO_ENABLE || FIELD_GET(PMGR_PS_TARGET, reg) == PMGR_PS_ACTIVE) {
+ for (int j = 0; j < 2; j++) {
+ if (device->parent[j]) {
+ const struct pmgr_device *pdevice;
+ if (pmgr_find_device(device->parent[j], &pdevice)) {
+ printf("pmgr: Failed to find parent #%d for %s\n", device->parent[j],
+ device->name);
+ continue;
+ }
+
+ if ((pdevice->flags & PMGR_FLAG_VIRTUAL))
+ continue;
+
+ addr = pmgr_device_get_addr(die, pdevice);
+ if (!addr)
+ continue;
+
+ reg = read32(addr);
+
+ if (!(reg & PMGR_AUTO_ENABLE) &&
+ FIELD_GET(PMGR_PS_TARGET, reg) != PMGR_PS_ACTIVE) {
+ printf("pmgr: Enabling %d.%s, parent of active device %s\n", die,
+ pdevice->name, device->name);
+ pmgr_set_mode(addr, PMGR_PS_ACTIVE);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ printf("pmgr: initialized, %d devices on %u dies found.\n", pmgr_devices_len, pmgr_dies);
+
+ return 0;
+}
diff --git a/tools/src/pmgr.h b/tools/src/pmgr.h
new file mode 100644
index 0000000..5dcc939
--- /dev/null
+++ b/tools/src/pmgr.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef PMGR_H
+#define PMGR_H
+
+#include "types.h"
+
+#define PMGR_DIE_OFFSET 0x2000000000
+
+#define PMGR_DEVICE_ID GENMASK(15, 0)
+#define PMGR_DIE_ID GENMASK(31, 28)
+
+int pmgr_init(void);
+
+int pmgr_power_enable(u32 id);
+int pmgr_power_disable(u32 id);
+
+int pmgr_adt_power_enable(const char *path);
+int pmgr_adt_power_disable(const char *path);
+int pmgr_adt_reset(const char *path);
+
+int pmgr_reset(int die, const char *name);
+
+#endif
diff --git a/tools/src/proxy.c b/tools/src/proxy.c
new file mode 100644
index 0000000..3925d7e
--- /dev/null
+++ b/tools/src/proxy.c
@@ -0,0 +1,575 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "proxy.h"
+#include "dapf.h"
+#include "dart.h"
+#include "display.h"
+#include "exception.h"
+#include "fb.h"
+#include "gxf.h"
+#include "heapblock.h"
+#include "hv.h"
+#include "iodev.h"
+#include "kboot.h"
+#include "malloc.h"
+#include "mcc.h"
+#include "memory.h"
+#include "nvme.h"
+#include "pcie.h"
+#include "pmgr.h"
+#include "smp.h"
+#include "string.h"
+#include "tunables.h"
+#include "types.h"
+#include "uart.h"
+#include "uartproxy.h"
+#include "usb.h"
+#include "utils.h"
+#include "xnuboot.h"
+
+#include "minilzlib/minlzma.h"
+#include "tinf/tinf.h"
+
+int proxy_process(ProxyRequest *request, ProxyReply *reply)
+{
+ enum exc_guard_t guard_save = exc_guard;
+
+ reply->opcode = request->opcode;
+ reply->status = S_OK;
+ reply->retval = 0;
+ switch (request->opcode) {
+ case P_NOP:
+ break;
+ case P_EXIT:
+ if (request->args[0])
+ return request->args[0];
+ return 1;
+ case P_CALL: {
+ generic_func *f = (generic_func *)request->args[0];
+ reply->retval = f(request->args[1], request->args[2], request->args[3],
+ request->args[4], request->args[5]);
+ break;
+ }
+ case P_GET_BOOTARGS:
+ reply->retval = boot_args_addr;
+ break;
+ case P_GET_BASE:
+ reply->retval = (u64)_base;
+ break;
+ case P_SET_BAUD: {
+ int cnt = request->args[1];
+ printf("Changing baud rate to %lu...\n", request->args[0]);
+ uart_setbaud(request->args[0]);
+ while (cnt--) {
+ uart_putbyte(request->args[2]);
+ uart_putbyte(request->args[2] >> 8);
+ uart_putbyte(request->args[2] >> 16);
+ uart_putbyte(request->args[2] >> 24);
+ }
+ break;
+ }
+ case P_UDELAY:
+ udelay(request->args[0]);
+ break;
+ case P_SET_EXC_GUARD:
+ exc_count = 0;
+ guard_save = request->args[0];
+ break;
+ case P_GET_EXC_COUNT:
+ reply->retval = exc_count;
+ exc_count = 0;
+ break;
+ case P_EL0_CALL:
+ reply->retval = el0_call((void *)request->args[0], request->args[1], request->args[2],
+ request->args[3], request->args[4]);
+ break;
+ case P_EL1_CALL:
+ reply->retval = el1_call((void *)request->args[0], request->args[1], request->args[2],
+ request->args[3], request->args[4]);
+ break;
+ case P_VECTOR:
+ // forcefully restore tps6598x IRQs
+ usb_hpm_restore_irqs(1);
+ iodev_console_flush();
+ next_stage.entry = (generic_func *)request->args[0];
+ memcpy(next_stage.args, &request->args[1], 5 * sizeof(u64));
+ next_stage.restore_logo = true;
+ return 1;
+ case P_GL1_CALL:
+ reply->retval = gl1_call((void *)request->args[0], request->args[1], request->args[2],
+ request->args[3], request->args[4]);
+ break;
+ case P_GL2_CALL:
+ reply->retval = gl2_call((void *)request->args[0], request->args[1], request->args[2],
+ request->args[3], request->args[4]);
+ break;
+ case P_GET_SIMD_STATE:
+ get_simd_state((void *)request->args[0]);
+ break;
+ case P_PUT_SIMD_STATE:
+ put_simd_state((void *)request->args[0]);
+ break;
+ case P_REBOOT:
+ reboot();
+ break;
+
+ case P_WRITE64:
+ exc_guard = GUARD_SKIP;
+ write64(request->args[0], request->args[1]);
+ break;
+ case P_WRITE32:
+ exc_guard = GUARD_SKIP;
+ write32(request->args[0], request->args[1]);
+ break;
+ case P_WRITE16:
+ exc_guard = GUARD_SKIP;
+ write16(request->args[0], request->args[1]);
+ break;
+ case P_WRITE8:
+ exc_guard = GUARD_SKIP;
+ write8(request->args[0], request->args[1]);
+ break;
+
+ case P_READ64:
+ exc_guard = GUARD_MARK;
+ reply->retval = read64(request->args[0]);
+ break;
+ case P_READ32:
+ exc_guard = GUARD_MARK;
+ reply->retval = read32(request->args[0]);
+ break;
+ case P_READ16:
+ exc_guard = GUARD_MARK;
+ reply->retval = read16(request->args[0]);
+ break;
+ case P_READ8:
+ exc_guard = GUARD_MARK;
+ reply->retval = read8(request->args[0]);
+ break;
+
+ case P_SET64:
+ exc_guard = GUARD_MARK;
+ reply->retval = set64(request->args[0], request->args[1]);
+ break;
+ case P_SET32:
+ exc_guard = GUARD_MARK;
+ reply->retval = set32(request->args[0], request->args[1]);
+ break;
+ case P_SET16:
+ exc_guard = GUARD_MARK;
+ reply->retval = set16(request->args[0], request->args[1]);
+ break;
+ case P_SET8:
+ exc_guard = GUARD_MARK;
+ reply->retval = set8(request->args[0], request->args[1]);
+ break;
+
+ case P_CLEAR64:
+ exc_guard = GUARD_MARK;
+ reply->retval = clear64(request->args[0], request->args[1]);
+ break;
+ case P_CLEAR32:
+ exc_guard = GUARD_MARK;
+ reply->retval = clear32(request->args[0], request->args[1]);
+ break;
+ case P_CLEAR16:
+ exc_guard = GUARD_MARK;
+ reply->retval = clear16(request->args[0], request->args[1]);
+ break;
+ case P_CLEAR8:
+ exc_guard = GUARD_MARK;
+ reply->retval = clear8(request->args[0], request->args[1]);
+ break;
+
+ case P_MASK64:
+ exc_guard = GUARD_MARK;
+ reply->retval = mask64(request->args[0], request->args[1], request->args[2]);
+ break;
+ case P_MASK32:
+ exc_guard = GUARD_MARK;
+ reply->retval = mask32(request->args[0], request->args[1], request->args[2]);
+ break;
+ case P_MASK16:
+ exc_guard = GUARD_MARK;
+ reply->retval = mask16(request->args[0], request->args[1], request->args[2]);
+ break;
+ case P_MASK8:
+ exc_guard = GUARD_MARK;
+ reply->retval = mask8(request->args[0], request->args[1], request->args[2]);
+ break;
+
+ case P_WRITEREAD64:
+ exc_guard = GUARD_MARK;
+ reply->retval = writeread64(request->args[0], request->args[1]);
+ break;
+ case P_WRITEREAD32:
+ exc_guard = GUARD_MARK;
+ reply->retval = writeread32(request->args[0], request->args[1]);
+ break;
+ case P_WRITEREAD16:
+ exc_guard = GUARD_MARK;
+ reply->retval = writeread16(request->args[0], request->args[1]);
+ break;
+ case P_WRITEREAD8:
+ exc_guard = GUARD_MARK;
+ reply->retval = writeread8(request->args[0], request->args[1]);
+ break;
+
+ case P_MEMCPY64:
+ exc_guard = GUARD_RETURN;
+ memcpy64((void *)request->args[0], (void *)request->args[1], request->args[2]);
+ break;
+ case P_MEMCPY32:
+ exc_guard = GUARD_RETURN;
+ memcpy32((void *)request->args[0], (void *)request->args[1], request->args[2]);
+ break;
+ case P_MEMCPY16:
+ exc_guard = GUARD_RETURN;
+ memcpy16((void *)request->args[0], (void *)request->args[1], request->args[2]);
+ break;
+ case P_MEMCPY8:
+ exc_guard = GUARD_RETURN;
+ memcpy8((void *)request->args[0], (void *)request->args[1], request->args[2]);
+ break;
+
+ case P_MEMSET64:
+ exc_guard = GUARD_RETURN;
+ memset64((void *)request->args[0], request->args[1], request->args[2]);
+ break;
+ case P_MEMSET32:
+ exc_guard = GUARD_RETURN;
+ memset32((void *)request->args[0], request->args[1], request->args[2]);
+ break;
+ case P_MEMSET16:
+ exc_guard = GUARD_RETURN;
+ memset16((void *)request->args[0], request->args[1], request->args[2]);
+ break;
+ case P_MEMSET8:
+ exc_guard = GUARD_RETURN;
+ memset8((void *)request->args[0], request->args[1], request->args[2]);
+ break;
+
+ case P_IC_IALLUIS:
+ ic_ialluis();
+ break;
+ case P_IC_IALLU:
+ ic_iallu();
+ break;
+ case P_IC_IVAU:
+ ic_ivau_range((void *)request->args[0], request->args[1]);
+ break;
+ case P_DC_IVAC:
+ dc_ivac_range((void *)request->args[0], request->args[1]);
+ break;
+ case P_DC_ISW:
+ dc_isw((void *)request->args[0]);
+ break;
+ case P_DC_CSW:
+ dc_csw((void *)request->args[0]);
+ break;
+ case P_DC_CISW:
+ dc_cisw((void *)request->args[0]);
+ break;
+ case P_DC_ZVA:
+ dc_zva_range((void *)request->args[0], request->args[1]);
+ break;
+ case P_DC_CVAC:
+ dc_cvac_range((void *)request->args[0], request->args[1]);
+ break;
+ case P_DC_CVAU:
+ dc_cvau_range((void *)request->args[0], request->args[1]);
+ break;
+ case P_DC_CIVAC:
+ dc_civac_range((void *)request->args[0], request->args[1]);
+ break;
+ case P_MMU_SHUTDOWN:
+ mmu_shutdown();
+ break;
+ case P_MMU_INIT:
+ mmu_init();
+ break;
+ case P_MMU_DISABLE:
+ reply->retval = mmu_disable();
+ break;
+ case P_MMU_RESTORE:
+ mmu_restore(request->args[0]);
+ break;
+ case P_MMU_INIT_SECONDARY:
+ mmu_init_secondary(request->args[0]);
+ break;
+
+ case P_XZDEC: {
+ uint32_t destlen, srclen;
+ destlen = request->args[3];
+ srclen = request->args[1];
+ if (XzDecode((void *)request->args[0], &srclen, (void *)request->args[2], &destlen))
+ reply->retval = destlen;
+ else
+ reply->retval = ~0L;
+ break;
+ }
+ case P_GZDEC: {
+ unsigned int destlen, srclen;
+ destlen = request->args[3];
+ srclen = request->args[1];
+ size_t ret = tinf_gzip_uncompress((void *)request->args[2], &destlen,
+ (void *)request->args[0], &srclen);
+ if (ret != TINF_OK)
+ reply->retval = ret;
+ else
+ reply->retval = destlen;
+ break;
+ }
+
+ case P_SMP_START_SECONDARIES:
+ smp_start_secondaries();
+ break;
+ case P_SMP_CALL:
+ smp_call4(request->args[0], (void *)request->args[1], request->args[2],
+ request->args[3], request->args[4], request->args[5]);
+ break;
+ case P_SMP_CALL_SYNC:
+ smp_call4(request->args[0], (void *)request->args[1], request->args[2],
+ request->args[3], request->args[4], request->args[5]);
+ reply->retval = smp_wait(request->args[0]);
+ break;
+ case P_SMP_WAIT:
+ reply->retval = smp_wait(request->args[0]);
+ break;
+ case P_SMP_SET_WFE_MODE:
+ smp_set_wfe_mode(request->args[0]);
+ break;
+
+ case P_HEAPBLOCK_ALLOC:
+ reply->retval = (u64)heapblock_alloc(request->args[0]);
+ break;
+ case P_MALLOC:
+ reply->retval = (u64)malloc(request->args[0]);
+ break;
+ case P_MEMALIGN:
+ reply->retval = (u64)memalign(request->args[0], request->args[1]);
+ break;
+ case P_FREE:
+ free((void *)request->args[0]);
+ break;
+
+ case P_KBOOT_BOOT:
+ if (kboot_boot((void *)request->args[0]) == 0)
+ return 1;
+ break;
+ case P_KBOOT_SET_CHOSEN:
+ reply->retval = kboot_set_chosen((void *)request->args[0], (void *)request->args[1]);
+ break;
+ case P_KBOOT_SET_INITRD:
+ kboot_set_initrd((void *)request->args[0], request->args[1]);
+ break;
+ case P_KBOOT_PREPARE_DT:
+ reply->retval = kboot_prepare_dt((void *)request->args[0]);
+ break;
+
+ case P_PMGR_POWER_ENABLE:
+ reply->retval = pmgr_power_enable(request->args[0]);
+ break;
+ case P_PMGR_POWER_DISABLE:
+ reply->retval = pmgr_power_enable(request->args[0]);
+ break;
+ case P_PMGR_ADT_POWER_ENABLE:
+ reply->retval = pmgr_adt_power_enable((const char *)request->args[0]);
+ break;
+ case P_PMGR_ADT_POWER_DISABLE:
+ reply->retval = pmgr_adt_power_disable((const char *)request->args[0]);
+ break;
+ case P_PMGR_RESET:
+ reply->retval = pmgr_reset(request->args[0], (const char *)request->args[1]);
+ break;
+
+ case P_IODEV_SET_USAGE:
+ iodev_set_usage(request->args[0], request->args[1]);
+ break;
+ case P_IODEV_CAN_READ:
+ reply->retval = iodev_can_read(request->args[0]);
+ break;
+ case P_IODEV_CAN_WRITE:
+ reply->retval = iodev_can_write(request->args[0]);
+ break;
+ case P_IODEV_READ:
+ reply->retval =
+ iodev_read(request->args[0], (void *)request->args[1], request->args[2]);
+ break;
+ case P_IODEV_WRITE:
+ reply->retval =
+ iodev_write(request->args[0], (void *)request->args[1], request->args[2]);
+ break;
+ case P_IODEV_WHOAMI:
+ reply->retval = uartproxy_iodev;
+ break;
+
+ case P_USB_IODEV_VUART_SETUP:
+ usb_iodev_vuart_setup(request->args[0]);
+ break;
+
+ case P_TUNABLES_APPLY_GLOBAL:
+ reply->retval = tunables_apply_global((const char *)request->args[0],
+ (const char *)request->args[1]);
+ break;
+ case P_TUNABLES_APPLY_LOCAL:
+ reply->retval = tunables_apply_local((const char *)request->args[0],
+ (const char *)request->args[1], request->args[2]);
+ break;
+ case P_TUNABLES_APPLY_LOCAL_ADDR:
+ reply->retval = tunables_apply_local_addr(
+ (const char *)request->args[0], (const char *)request->args[1], request->args[2]);
+ break;
+
+ case P_DART_INIT:
+ reply->retval = (u64)dart_init(request->args[0], request->args[1], request->args[2],
+ request->args[3]);
+ break;
+ case P_DART_SHUTDOWN:
+ dart_shutdown((dart_dev_t *)request->args[0]);
+ break;
+ case P_DART_MAP:
+ reply->retval = dart_map((dart_dev_t *)request->args[0], request->args[1],
+ (void *)request->args[2], request->args[3]);
+ break;
+ case P_DART_UNMAP:
+ dart_unmap((dart_dev_t *)request->args[0], request->args[1], request->args[2]);
+ break;
+
+ case P_HV_INIT:
+ hv_init();
+ break;
+ case P_HV_MAP:
+ hv_map(request->args[0], request->args[1], request->args[2], request->args[3]);
+ break;
+ case P_HV_START:
+ hv_start((void *)request->args[0], &request->args[1]);
+ break;
+ case P_HV_TRANSLATE:
+ reply->retval = hv_translate(request->args[0], request->args[1], request->args[2],
+ (void *)request->args[3]);
+ break;
+ case P_HV_PT_WALK:
+ reply->retval = hv_pt_walk(request->args[0]);
+ break;
+ case P_HV_MAP_VUART:
+ hv_map_vuart(request->args[0], request->args[1], request->args[2]);
+ break;
+ case P_HV_MAP_VIRTIO:
+ hv_map_virtio(request->args[0], (void *)request->args[1]);
+ break;
+ case P_VIRTIO_PUT_BUFFER:
+ virtio_put_buffer(request->args[0], request->args[1], request->args[2],
+ request->args[3]);
+ break;
+ case P_HV_TRACE_IRQ:
+ reply->retval = hv_trace_irq(request->args[0], request->args[1], request->args[2],
+ request->args[3]);
+ break;
+ case P_HV_WDT_START:
+ hv_wdt_start(request->args[0]);
+ break;
+ case P_HV_START_SECONDARY:
+ hv_start_secondary(request->args[0], (void *)request->args[1], &request->args[2]);
+ break;
+ case P_HV_SWITCH_CPU:
+ reply->retval = hv_switch_cpu(request->args[0]);
+ break;
+ case P_HV_SET_TIME_STEALING:
+ hv_set_time_stealing(request->args[0], request->args[1]);
+ break;
+ case P_HV_PIN_CPU:
+ hv_pin_cpu(request->args[0]);
+ break;
+ case P_HV_WRITE_HCR:
+ hv_write_hcr(request->args[0]);
+ break;
+
+ case P_FB_INIT:
+ fb_init(request->args[0]);
+ break;
+ case P_FB_SHUTDOWN:
+ fb_shutdown(request->args[0]);
+ break;
+ case P_FB_BLIT:
+ // HACK: Running out of args, stash pix fmt in high bits of stride...
+ fb_blit(request->args[0], request->args[1], request->args[2], request->args[3],
+ (void *)request->args[4], (u32)request->args[5], request->args[5] >> 32);
+ break;
+ case P_FB_UNBLIT:
+ fb_unblit(request->args[0], request->args[1], request->args[2], request->args[3],
+ (void *)request->args[4], request->args[5]);
+ break;
+ case P_FB_FILL:
+ fb_fill(request->args[0], request->args[1], request->args[2], request->args[3],
+ int2rgb(request->args[4]));
+ break;
+ case P_FB_CLEAR:
+ fb_clear(int2rgb(request->args[0]));
+ break;
+ case P_FB_DISPLAY_LOGO:
+ fb_display_logo();
+ break;
+ case P_FB_RESTORE_LOGO:
+ fb_restore_logo();
+ break;
+ case P_FB_IMPROVE_LOGO:
+ fb_improve_logo();
+ break;
+
+ case P_PCIE_INIT:
+ pcie_init();
+ break;
+ case P_PCIE_SHUTDOWN:
+ pcie_shutdown();
+ break;
+
+ case P_NVME_INIT:
+ reply->retval = nvme_init();
+ break;
+ case P_NVME_SHUTDOWN:
+ nvme_shutdown();
+ break;
+ case P_NVME_READ:
+ reply->retval = nvme_read(request->args[0], request->args[1], (void *)request->args[2]);
+ break;
+ case P_NVME_FLUSH:
+ reply->retval = nvme_flush(request->args[0]);
+ break;
+
+ case P_MCC_GET_CARVEOUTS:
+ reply->retval = (u64)mcc_carveouts;
+ break;
+
+ case P_DISPLAY_INIT:
+ reply->retval = display_init();
+ break;
+ case P_DISPLAY_CONFIGURE:
+ reply->retval = display_configure((char *)request->args[0]);
+ break;
+ case P_DISPLAY_SHUTDOWN:
+ display_shutdown(request->args[0]);
+ break;
+ case P_DISPLAY_START_DCP:
+ display_start_dcp();
+ break;
+ case P_DISPLAY_IS_EXTERNAL:
+ reply->retval = display_is_external;
+ break;
+
+ case P_DAPF_INIT_ALL:
+ reply->retval = dapf_init_all();
+ break;
+ case P_DAPF_INIT:
+ reply->retval = dapf_init((const char *)request->args[0]);
+ break;
+
+ default:
+ reply->status = S_BADCMD;
+ break;
+ }
+ sysop("dsb sy");
+ sysop("isb");
+ exc_guard = guard_save;
+ return 0;
+}
diff --git a/tools/src/proxy.h b/tools/src/proxy.h
new file mode 100644
index 0000000..27a3f8e
--- /dev/null
+++ b/tools/src/proxy.h
@@ -0,0 +1,183 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef __PROXY_H__
+#define __PROXY_H__
+
+#include "types.h"
+
+typedef enum {
+ P_NOP = 0x000, // System functions
+ P_EXIT,
+ P_CALL,
+ P_GET_BOOTARGS,
+ P_GET_BASE,
+ P_SET_BAUD,
+ P_UDELAY,
+ P_SET_EXC_GUARD,
+ P_GET_EXC_COUNT,
+ P_EL0_CALL,
+ P_EL1_CALL,
+ P_VECTOR,
+ P_GL1_CALL,
+ P_GL2_CALL,
+ P_GET_SIMD_STATE,
+ P_PUT_SIMD_STATE,
+ P_REBOOT,
+
+ P_WRITE64 = 0x100, // Generic register functions
+ P_WRITE32,
+ P_WRITE16,
+ P_WRITE8,
+ P_READ64,
+ P_READ32,
+ P_READ16,
+ P_READ8,
+ P_SET64,
+ P_SET32,
+ P_SET16,
+ P_SET8,
+ P_CLEAR64,
+ P_CLEAR32,
+ P_CLEAR16,
+ P_CLEAR8,
+ P_MASK64,
+ P_MASK32,
+ P_MASK16,
+ P_MASK8,
+ P_WRITEREAD64,
+ P_WRITEREAD32,
+ P_WRITEREAD16,
+ P_WRITEREAD8,
+
+ P_MEMCPY64 = 0x200, // Memory block transfer functions
+ P_MEMCPY32,
+ P_MEMCPY16,
+ P_MEMCPY8,
+ P_MEMSET64,
+ P_MEMSET32,
+ P_MEMSET16,
+ P_MEMSET8,
+
+ P_IC_IALLUIS = 0x300, // Cache and memory ops
+ P_IC_IALLU,
+ P_IC_IVAU,
+ P_DC_IVAC,
+ P_DC_ISW,
+ P_DC_CSW,
+ P_DC_CISW,
+ P_DC_ZVA,
+ P_DC_CVAC,
+ P_DC_CVAU,
+ P_DC_CIVAC,
+ P_MMU_SHUTDOWN,
+ P_MMU_INIT,
+ P_MMU_DISABLE,
+ P_MMU_RESTORE,
+ P_MMU_INIT_SECONDARY,
+
+ P_XZDEC = 0x400, // Decompression and data processing ops
+ P_GZDEC,
+
+ P_SMP_START_SECONDARIES = 0x500, // SMP and system management ops
+ P_SMP_CALL,
+ P_SMP_CALL_SYNC,
+ P_SMP_WAIT,
+ P_SMP_SET_WFE_MODE,
+
+ P_HEAPBLOCK_ALLOC = 0x600, // Heap and memory management ops
+ P_MALLOC,
+ P_MEMALIGN,
+ P_FREE,
+
+ P_KBOOT_BOOT = 0x700, // Kernel boot ops
+ P_KBOOT_SET_CHOSEN,
+ P_KBOOT_SET_INITRD,
+ P_KBOOT_PREPARE_DT,
+
+ P_PMGR_POWER_ENABLE = 0x800, // power/clock management ops
+ P_PMGR_POWER_DISABLE,
+ P_PMGR_ADT_POWER_ENABLE,
+ P_PMGR_ADT_POWER_DISABLE,
+ P_PMGR_RESET,
+
+ P_IODEV_SET_USAGE = 0x900,
+ P_IODEV_CAN_READ,
+ P_IODEV_CAN_WRITE,
+ P_IODEV_READ,
+ P_IODEV_WRITE,
+ P_IODEV_WHOAMI,
+ P_USB_IODEV_VUART_SETUP,
+
+ P_TUNABLES_APPLY_GLOBAL = 0xa00,
+ P_TUNABLES_APPLY_LOCAL,
+ P_TUNABLES_APPLY_LOCAL_ADDR,
+
+ P_DART_INIT = 0xb00,
+ P_DART_SHUTDOWN,
+ P_DART_MAP,
+ P_DART_UNMAP,
+
+ P_HV_INIT = 0xc00,
+ P_HV_MAP,
+ P_HV_START,
+ P_HV_TRANSLATE,
+ P_HV_PT_WALK,
+ P_HV_MAP_VUART,
+ P_HV_TRACE_IRQ,
+ P_HV_WDT_START,
+ P_HV_START_SECONDARY,
+ P_HV_SWITCH_CPU,
+ P_HV_SET_TIME_STEALING,
+ P_HV_PIN_CPU,
+ P_HV_WRITE_HCR,
+ P_HV_MAP_VIRTIO,
+ P_VIRTIO_PUT_BUFFER,
+
+ P_FB_INIT = 0xd00,
+ P_FB_SHUTDOWN,
+ P_FB_BLIT,
+ P_FB_UNBLIT,
+ P_FB_FILL,
+ P_FB_CLEAR,
+ P_FB_DISPLAY_LOGO,
+ P_FB_RESTORE_LOGO,
+ P_FB_IMPROVE_LOGO,
+
+ P_PCIE_INIT = 0xe00,
+ P_PCIE_SHUTDOWN,
+
+ P_NVME_INIT = 0xf00,
+ P_NVME_SHUTDOWN,
+ P_NVME_READ,
+ P_NVME_FLUSH,
+
+ P_MCC_GET_CARVEOUTS = 0x1000,
+
+ P_DISPLAY_INIT = 0x1100,
+ P_DISPLAY_CONFIGURE,
+ P_DISPLAY_SHUTDOWN,
+ P_DISPLAY_START_DCP,
+ P_DISPLAY_IS_EXTERNAL,
+
+ P_DAPF_INIT_ALL = 0x1200,
+ P_DAPF_INIT,
+
+} ProxyOp;
+
+#define S_OK 0
+#define S_BADCMD -1
+
+typedef struct {
+ u64 opcode;
+ u64 args[6];
+} ProxyRequest;
+
+typedef struct {
+ u64 opcode;
+ s64 status;
+ u64 retval;
+} ProxyReply;
+
+int proxy_process(ProxyRequest *request, ProxyReply *reply);
+
+#endif
diff --git a/tools/src/ringbuffer.c b/tools/src/ringbuffer.c
new file mode 100644
index 0000000..36b89d9
--- /dev/null
+++ b/tools/src/ringbuffer.c
@@ -0,0 +1,81 @@
+#include "ringbuffer.h"
+#include "malloc.h"
+#include "types.h"
+
+ringbuffer_t *ringbuffer_alloc(size_t len)
+{
+ ringbuffer_t *bfr = malloc(sizeof(*bfr));
+ if (!bfr)
+ return NULL;
+
+ bfr->buffer = malloc(len);
+ if (!bfr->buffer) {
+ free(bfr);
+ return NULL;
+ }
+
+ bfr->read = 0;
+ bfr->write = 0;
+ bfr->len = len;
+
+ return bfr;
+}
+
+void ringbuffer_free(ringbuffer_t *bfr)
+{
+ if (bfr)
+ free(bfr->buffer);
+ free(bfr);
+}
+
+size_t ringbuffer_read(u8 *target, size_t len, ringbuffer_t *bfr)
+{
+ size_t read;
+
+ for (read = 0; read < len; ++read) {
+ if (bfr->read == bfr->write)
+ break;
+
+ *target = bfr->buffer[bfr->read];
+ target++;
+
+ bfr->read++;
+ bfr->read %= bfr->len;
+ }
+
+ return read;
+}
+
+size_t ringbuffer_write(const u8 *src, size_t len, ringbuffer_t *bfr)
+{
+ size_t written;
+
+ for (written = 0; written < len; ++written) {
+ if (((bfr->write + 1) % bfr->len) == bfr->read)
+ break;
+
+ bfr->buffer[bfr->write] = *src;
+ src++;
+
+ bfr->write++;
+ bfr->write %= bfr->len;
+ }
+
+ return written;
+}
+
+size_t ringbuffer_get_used(ringbuffer_t *bfr)
+{
+ size_t read = bfr->read;
+ size_t write = bfr->write;
+
+ if (write < read)
+ write += bfr->len;
+
+ return write - read;
+}
+
+size_t ringbuffer_get_free(ringbuffer_t *bfr)
+{
+ return bfr->len - ringbuffer_get_used(bfr);
+}
diff --git a/tools/src/ringbuffer.h b/tools/src/ringbuffer.h
new file mode 100644
index 0000000..553ae76
--- /dev/null
+++ b/tools/src/ringbuffer.h
@@ -0,0 +1,22 @@
+#ifndef RINGBUFFER_H
+#define RINGBUFFER_H
+
+#include "types.h"
+
+typedef struct {
+ u8 *buffer;
+ size_t len;
+ size_t read;
+ size_t write;
+} ringbuffer_t;
+
+ringbuffer_t *ringbuffer_alloc(size_t len);
+void ringbuffer_free(ringbuffer_t *bfr);
+
+size_t ringbuffer_read(u8 *target, size_t len, ringbuffer_t *bfr);
+size_t ringbuffer_write(const u8 *src, size_t len, ringbuffer_t *bfr);
+
+size_t ringbuffer_get_used(ringbuffer_t *bfr);
+size_t ringbuffer_get_free(ringbuffer_t *bfr);
+
+#endif
diff --git a/tools/src/rtkit.c b/tools/src/rtkit.c
new file mode 100644
index 0000000..db80258
--- /dev/null
+++ b/tools/src/rtkit.c
@@ -0,0 +1,710 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "../config.h"
+
+#include "rtkit.h"
+#include "adt.h"
+#include "asc.h"
+#include "dart.h"
+#include "iova.h"
+#include "malloc.h"
+#include "sart.h"
+#include "string.h"
+#include "types.h"
+#include "utils.h"
+
+#define rtkit_printf(...) \
+ do { \
+ debug_printf("rtkit(%s): ", rtk->name); \
+ debug_printf(__VA_ARGS__); \
+ } while (0)
+
+#define RTKIT_EP_MGMT 0
+#define RTKIT_EP_CRASHLOG 1
+#define RTKIT_EP_SYSLOG 2
+#define RTKIT_EP_DEBUG 3
+#define RTKIT_EP_IOREPORT 4
+#define RTKIT_EP_OSLOG 8
+
+#define MGMT_TYPE GENMASK(59, 52)
+
+#define MGMT_PWR_STATE GENMASK(15, 0)
+
+#define MSG_BUFFER_REQUEST 1
+#define MSG_BUFFER_REQUEST_SIZE GENMASK(51, 44)
+#define MSG_BUFFER_REQUEST_IOVA GENMASK(41, 0)
+
+#define MSG_SYSLOG_INIT 8
+#define MSG_SYSLOG_INIT_ENTRYSIZE GENMASK(39, 24)
+#define MSG_SYSLOG_INIT_COUNT GENMASK(15, 0)
+#define MSG_SYSLOG_LOG 5
+#define MSG_SYSLOG_LOG_INDEX GENMASK(7, 0)
+
+#define MSG_OSLOG_INIT 0x10
+#define MSG_OSLOG_ACK 0x30
+
+#define MGMT_MSG_HELLO 1
+#define MGMT_MSG_HELLO_ACK 2
+#define MGMT_MSG_HELLO_MINVER GENMASK(15, 0)
+#define MGMT_MSG_HELLO_MAXVER GENMASK(31, 16)
+
+#define MGMT_MSG_IOP_PWR_STATE 6
+#define MGMT_MSG_IOP_PWR_STATE_ACK 7
+
+#define MGMT_MSG_EPMAP 8
+#define MGMT_MSG_EPMAP_DONE BIT(51)
+#define MGMT_MSG_EPMAP_BASE GENMASK(34, 32)
+#define MGMT_MSG_EPMAP_BITMAP GENMASK(31, 0)
+
+#define MGMT_MSG_EPMAP_REPLY 8
+#define MGMT_MSG_EPMAP_REPLY_DONE BIT(51)
+#define MGMT_MSG_EPMAP_REPLY_MORE BIT(0)
+
+#define MGMT_MSG_AP_PWR_STATE 0xb
+#define MGMT_MSG_AP_PWR_STATE_ACK 0xb
+
+#define MGMT_MSG_START_EP 5
+#define MGMT_MSG_START_EP_IDX GENMASK(39, 32)
+#define MGMT_MSG_START_EP_FLAG BIT(1)
+
+#define RTKIT_MIN_VERSION 11
+#define RTKIT_MAX_VERSION 12
+
+#define IOVA_MASK GENMASK(35, 0)
+
+enum rtkit_power_state {
+ RTKIT_POWER_OFF = 0x00,
+ RTKIT_POWER_SLEEP = 0x01,
+ RTKIT_POWER_QUIESCED = 0x10,
+ RTKIT_POWER_ON = 0x20,
+ RTKIT_POWER_INIT = 0x220,
+};
+
+struct rtkit_dev {
+ char *name;
+
+ asc_dev_t *asc;
+ dart_dev_t *dart;
+ iova_domain_t *dart_iovad;
+ sart_dev_t *sart;
+
+ u64 dva_base;
+
+ enum rtkit_power_state iop_power;
+ enum rtkit_power_state ap_power;
+
+ struct rtkit_buffer syslog_bfr;
+ struct rtkit_buffer crashlog_bfr;
+ struct rtkit_buffer ioreport_bfr;
+
+ u32 syslog_cnt, syslog_size;
+
+ bool crashed;
+};
+
+struct syslog_log {
+ u32 hdr;
+ u32 unk;
+ char context[24];
+ char msg[];
+};
+
+struct crashlog_hdr {
+ u32 type;
+ u32 ver;
+ u32 total_size;
+ u32 flags;
+ u8 _padding[16];
+};
+
+struct crashlog_entry {
+ u32 type;
+ u32 _padding;
+ u32 flags;
+ u32 len;
+ u8 payload[];
+};
+
+rtkit_dev_t *rtkit_init(const char *name, asc_dev_t *asc, dart_dev_t *dart,
+ iova_domain_t *dart_iovad, sart_dev_t *sart)
+{
+ if (dart && sart) {
+ printf("rtkit: Cannot use both SART and DART simultaneously\n");
+ return NULL;
+ }
+
+ if (dart && !dart_iovad) {
+ printf("rtkit: if DART is used iovad is already required\n");
+ return NULL;
+ }
+
+ rtkit_dev_t *rtk = malloc(sizeof(*rtk));
+ if (!rtk)
+ return NULL;
+ memset(rtk, 0, sizeof(*rtk));
+
+ size_t name_len = strlen(name);
+ rtk->name = malloc(name_len + 1);
+ if (!rtk->name)
+ goto out_free_rtk;
+ strcpy(rtk->name, name);
+
+ rtk->asc = asc;
+ rtk->dart = dart;
+ rtk->dart_iovad = dart_iovad;
+ rtk->sart = sart;
+ rtk->iop_power = RTKIT_POWER_OFF;
+ rtk->ap_power = RTKIT_POWER_OFF;
+ rtk->dva_base = 0;
+
+ int iop_node = asc_get_iop_node(asc);
+ ADT_GETPROP(adt, iop_node, "asc-dram-mask", &rtk->dva_base);
+
+ return rtk;
+
+out_free_rtk:
+ free(rtk);
+ return NULL;
+}
+
+void rtkit_free(rtkit_dev_t *rtk)
+{
+ rtkit_free_buffer(rtk, &rtk->syslog_bfr);
+ rtkit_free_buffer(rtk, &rtk->crashlog_bfr);
+ rtkit_free_buffer(rtk, &rtk->ioreport_bfr);
+ free(rtk->name);
+ free(rtk);
+}
+
+bool rtkit_send(rtkit_dev_t *rtk, const struct rtkit_message *msg)
+{
+ struct asc_message asc_msg;
+
+ asc_msg.msg0 = msg->msg;
+ asc_msg.msg1 = msg->ep;
+
+ return asc_send(rtk->asc, &asc_msg);
+}
+
+bool rtkit_map(rtkit_dev_t *rtk, void *phys, size_t sz, u64 *dva)
+{
+ sz = ALIGN_UP(sz, 16384);
+
+ if (rtk->sart) {
+ if (!sart_add_allowed_region(rtk->sart, phys, sz)) {
+ rtkit_printf("sart_add_allowed_region failed (%p, 0x%lx)\n", phys, sz);
+ return false;
+ }
+ *dva = (u64)phys;
+ return true;
+ } else if (rtk->dart) {
+ u64 iova = iova_alloc(rtk->dart_iovad, sz);
+ if (!iova) {
+ rtkit_printf("failed to alloc iova (size 0x%lx)\n", sz);
+ return false;
+ }
+
+ if (dart_map(rtk->dart, iova, phys, sz) < 0) {
+ rtkit_printf("failed to DART map %p -> 0x%lx (0x%lx)\n", phys, iova, sz);
+ iova_free(rtk->dart_iovad, iova, sz);
+ return false;
+ }
+
+ *dva = iova | rtk->dva_base;
+ return true;
+ } else {
+ rtkit_printf("TODO: implement no IOMMU buffers\n");
+ return false;
+ }
+}
+
+bool rtkit_unmap(rtkit_dev_t *rtk, u64 dva, size_t sz)
+{
+ if (rtk->sart) {
+ if (!sart_remove_allowed_region(rtk->sart, (void *)dva, sz))
+ rtkit_printf("sart_remove_allowed_region failed (0x%lx, 0x%lx)\n", dva, sz);
+ return true;
+ } else if (rtk->dart) {
+ dva &= ~rtk->dva_base;
+ dart_unmap(rtk->dart, dva & IOVA_MASK, sz);
+ iova_free(rtk->dart_iovad, dva & IOVA_MASK, sz);
+ return true;
+ } else {
+ rtkit_printf("TODO: implement no IOMMU buffers\n");
+ return false;
+ }
+}
+
+bool rtkit_alloc_buffer(rtkit_dev_t *rtk, struct rtkit_buffer *bfr, size_t sz)
+{
+ bfr->bfr = memalign(SZ_16K, sz);
+ if (!bfr->bfr) {
+ rtkit_printf("unable to allocate %zu buffer\n", sz);
+ return false;
+ }
+
+ sz = ALIGN_UP(sz, 16384);
+
+ bfr->sz = sz;
+ if (!rtkit_map(rtk, bfr->bfr, sz, &bfr->dva))
+ goto error;
+
+ return true;
+
+error:
+ free(bfr->bfr);
+ bfr->bfr = NULL;
+ return false;
+}
+
+bool rtkit_free_buffer(rtkit_dev_t *rtk, struct rtkit_buffer *bfr)
+{
+ if (!bfr->bfr || !is_heap(bfr->bfr))
+ return true;
+
+ if (!rtkit_unmap(rtk, bfr->dva, bfr->sz))
+ return false;
+
+ free(bfr->bfr);
+
+ return false;
+}
+
+static bool rtkit_handle_buffer_request(rtkit_dev_t *rtk, struct rtkit_message *msg,
+ struct rtkit_buffer *bfr)
+{
+ size_t n_4kpages = FIELD_GET(MSG_BUFFER_REQUEST_SIZE, msg->msg);
+ size_t sz = n_4kpages << 12;
+ u64 addr = FIELD_GET(MSG_BUFFER_REQUEST_IOVA, msg->msg);
+
+ if (addr) {
+ bfr->dva = addr & ~rtk->dva_base;
+ bfr->sz = sz;
+ bfr->bfr = dart_translate(rtk->dart, bfr->dva & IOVA_MASK);
+ if (!bfr->bfr) {
+ rtkit_printf("failed to translate pre-allocated buffer (ep 0x%x, buf 0x%lx)\n", msg->ep,
+ addr);
+ return false;
+ } else {
+ rtkit_printf("pre-allocated buffer (ep 0x%x, dva 0x%lx, phys %p)\n", msg->ep, addr,
+ bfr->bfr);
+ }
+ return true;
+
+ } else {
+ if (!rtkit_alloc_buffer(rtk, bfr, sz)) {
+ rtkit_printf("unable to allocate buffer\n");
+ return false;
+ }
+ }
+
+ struct asc_message reply;
+ reply.msg1 = msg->ep;
+ reply.msg0 = FIELD_PREP(MGMT_TYPE, MSG_BUFFER_REQUEST);
+ reply.msg0 |= FIELD_PREP(MSG_BUFFER_REQUEST_SIZE, n_4kpages);
+ if (!addr)
+ reply.msg0 |= FIELD_PREP(MSG_BUFFER_REQUEST_IOVA, bfr->dva | rtk->dva_base);
+
+ if (!asc_send(rtk->asc, &reply)) {
+ rtkit_printf("unable to send buffer reply\n");
+ rtkit_free_buffer(rtk, bfr);
+ goto error;
+ }
+
+ return true;
+
+error:
+ return false;
+}
+
+static void rtkit_crashed(rtkit_dev_t *rtk)
+{
+ struct crashlog_hdr *hdr = rtk->crashlog_bfr.bfr;
+ rtk->crashed = true;
+
+ rtkit_printf("IOP crashed!\n");
+
+ if (hdr->type != 'CLHE') {
+ rtkit_printf("bad crashlog header 0x%x @ %p\n", hdr->type, hdr);
+ return;
+ }
+
+ struct crashlog_entry *p = (void *)(hdr + 1);
+
+ rtkit_printf("== CRASH INFO ==\n");
+ while (p->type != 'CLHE') {
+ switch (p->type) {
+ case 'Cstr':
+ rtkit_printf(" Message %d: %s\n", p->payload[0], &p->payload[4]);
+ break;
+ default:
+ rtkit_printf(" 0x%x\n", p->type);
+ break;
+ }
+ p = ((void *)p) + p->len;
+ }
+}
+
+int rtkit_recv(rtkit_dev_t *rtk, struct rtkit_message *msg)
+{
+ struct asc_message asc_msg;
+ bool ok = true;
+
+ if (rtk->crashed)
+ return -1;
+
+ while (asc_recv(rtk->asc, &asc_msg)) {
+ if (asc_msg.msg1 >= 0x100) {
+ rtkit_printf("WARNING: received message for invalid endpoint %x >= 0x100\n",
+ asc_msg.msg1);
+ continue;
+ }
+
+ msg->msg = asc_msg.msg0;
+ msg->ep = (u8)asc_msg.msg1;
+
+ /* if this is an app message we can just forwad it to the caller */
+ if (msg->ep >= 0x20)
+ return 1;
+
+ u32 msgtype = FIELD_GET(MGMT_TYPE, msg->msg);
+ switch (msg->ep) {
+ case RTKIT_EP_MGMT:
+ switch (msgtype) {
+ case MGMT_MSG_IOP_PWR_STATE_ACK:
+ rtk->iop_power = FIELD_GET(MGMT_PWR_STATE, msg->msg);
+ break;
+ case MGMT_MSG_AP_PWR_STATE_ACK:
+ rtk->ap_power = FIELD_GET(MGMT_PWR_STATE, msg->msg);
+ break;
+ default:
+ rtkit_printf("unknown management message %x\n", msgtype);
+ }
+ break;
+ case RTKIT_EP_SYSLOG:
+ switch (msgtype) {
+ case MSG_BUFFER_REQUEST:
+ ok = ok && rtkit_handle_buffer_request(rtk, msg, &rtk->syslog_bfr);
+ break;
+ case MSG_SYSLOG_INIT:
+ rtk->syslog_cnt = FIELD_GET(MSG_SYSLOG_INIT_COUNT, msg->msg);
+ rtk->syslog_size = FIELD_GET(MSG_SYSLOG_INIT_ENTRYSIZE, msg->msg);
+ break;
+ case MSG_SYSLOG_LOG:
+#ifdef RTKIT_SYSLOG
+ {
+ u64 index = FIELD_GET(MSG_SYSLOG_LOG_INDEX, msg->msg);
+ u64 stride = rtk->syslog_size + sizeof(struct syslog_log);
+ struct syslog_log *log = rtk->syslog_bfr.bfr + stride * index;
+ rtkit_printf("syslog: [%s]%s", log->context, log->msg);
+ if (log->msg[strlen(log->msg) - 1] != '\n')
+ printf("\n");
+ }
+#endif
+ if (!asc_send(rtk->asc, &asc_msg))
+ rtkit_printf("failed to ack syslog\n");
+ break;
+ default:
+ rtkit_printf("unknown syslog message %x\n", msgtype);
+ }
+ break;
+ case RTKIT_EP_CRASHLOG:
+ switch (msgtype) {
+ case MSG_BUFFER_REQUEST:
+ if (!rtk->crashlog_bfr.bfr) {
+ ok = ok && rtkit_handle_buffer_request(rtk, msg, &rtk->crashlog_bfr);
+ } else {
+ rtkit_crashed(rtk);
+ return -1;
+ }
+ break;
+ default:
+ rtkit_printf("unknown crashlog message %x\n", msgtype);
+ }
+ break;
+ case RTKIT_EP_IOREPORT:
+ switch (msgtype) {
+ case MSG_BUFFER_REQUEST:
+ ok = ok && rtkit_handle_buffer_request(rtk, msg, &rtk->ioreport_bfr);
+ break;
+ /* unknown but must be ACKed */
+ case 0x8:
+ case 0xc:
+ if (!rtkit_send(rtk, msg))
+ rtkit_printf("unable to ACK unknown ioreport message\n");
+ break;
+ default:
+ rtkit_printf("unknown ioreport message %x\n", msgtype);
+ }
+ break;
+ case RTKIT_EP_OSLOG:
+ switch (msgtype) {
+ case MSG_OSLOG_INIT:
+ msg->msg = FIELD_PREP(MGMT_TYPE, MSG_OSLOG_ACK);
+ if (!rtkit_send(rtk, msg))
+ rtkit_printf("unable to ACK oslog init message\n");
+ break;
+ default:
+ rtkit_printf("unknown oslog message %x\n", msgtype);
+ }
+ break;
+ default:
+ rtkit_printf("message to unknown system endpoint 0x%02x: %lx\n", msg->ep, msg->msg);
+ }
+
+ if (!ok) {
+ rtkit_printf("failed to handle system message 0x%02x: %lx\n", msg->ep, msg->msg);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+bool rtkit_start_ep(rtkit_dev_t *rtk, u8 ep)
+{
+ struct asc_message msg;
+
+ msg.msg0 = FIELD_PREP(MGMT_TYPE, MGMT_MSG_START_EP);
+ msg.msg0 |= MGMT_MSG_START_EP_FLAG;
+ msg.msg0 |= FIELD_PREP(MGMT_MSG_START_EP_IDX, ep);
+ msg.msg1 = RTKIT_EP_MGMT;
+
+ if (!asc_send(rtk->asc, &msg)) {
+ rtkit_printf("unable to start endpoint 0x%02x\n", ep);
+ return false;
+ }
+
+ return true;
+}
+
+bool rtkit_boot(rtkit_dev_t *rtk)
+{
+ struct asc_message msg;
+
+ /* boot the IOP if it isn't already */
+ asc_cpu_start(rtk->asc);
+ /* can be sent unconditionally to wake up a possibly sleeping IOP */
+ msg.msg0 = FIELD_PREP(MGMT_TYPE, MGMT_MSG_IOP_PWR_STATE) |
+ FIELD_PREP(MGMT_PWR_STATE, RTKIT_POWER_INIT);
+ msg.msg1 = RTKIT_EP_MGMT;
+ if (!asc_send(rtk->asc, &msg)) {
+ rtkit_printf("unable to send wakeup message\n");
+ return false;
+ }
+
+ if (!asc_recv_timeout(rtk->asc, &msg, USEC_PER_SEC)) {
+ rtkit_printf("did not receive HELLO\n");
+ return false;
+ }
+
+ if (msg.msg1 != RTKIT_EP_MGMT) {
+ rtkit_printf("expected HELLO but got message for EP 0x%x", msg.msg1);
+ return false;
+ }
+
+ u32 msgtype;
+ msgtype = FIELD_GET(MGMT_TYPE, msg.msg0);
+ if (msgtype != MGMT_MSG_HELLO) {
+ rtkit_printf("expected HELLO but got message with type 0x%02x", msgtype);
+
+ return false;
+ }
+
+ u32 min_ver, max_ver, want_ver;
+ min_ver = FIELD_GET(MGMT_MSG_HELLO_MINVER, msg.msg0);
+ max_ver = FIELD_GET(MGMT_MSG_HELLO_MAXVER, msg.msg0);
+ want_ver = min(RTKIT_MAX_VERSION, max_ver);
+
+ if (min_ver > RTKIT_MAX_VERSION || max_ver < RTKIT_MIN_VERSION) {
+ rtkit_printf("supported versions [%d,%d] must overlap versions [%d,%d]\n",
+ RTKIT_MIN_VERSION, RTKIT_MAX_VERSION, min_ver, max_ver);
+ return false;
+ }
+
+ rtkit_printf("booting with version %d\n", want_ver);
+
+ msg.msg0 = FIELD_PREP(MGMT_TYPE, MGMT_MSG_HELLO_ACK);
+ msg.msg0 |= FIELD_PREP(MGMT_MSG_HELLO_MINVER, want_ver);
+ msg.msg0 |= FIELD_PREP(MGMT_MSG_HELLO_MAXVER, want_ver);
+ msg.msg1 = RTKIT_EP_MGMT;
+ if (!asc_send(rtk->asc, &msg)) {
+ rtkit_printf("couldn't send HELLO ack\n");
+ return false;
+ }
+
+ bool has_crashlog = false;
+ bool has_debug = false;
+ bool has_ioreport = false;
+ bool has_syslog = false;
+ bool has_oslog = false;
+ bool got_epmap = false;
+ while (!got_epmap) {
+ if (!asc_recv_timeout(rtk->asc, &msg, USEC_PER_SEC)) {
+ rtkit_printf("couldn't receive message while waiting for endpoint map\n");
+ return false;
+ }
+
+ if (msg.msg1 != RTKIT_EP_MGMT) {
+ rtkit_printf("expected management message while waiting for endpoint map but got "
+ "message for endpoint 0x%x\n",
+ msg.msg1);
+ return false;
+ }
+
+ msgtype = FIELD_GET(MGMT_TYPE, msg.msg0);
+ if (msgtype != MGMT_MSG_EPMAP) {
+ rtkit_printf("expected endpoint map message but got 0x%x instead\n", msgtype);
+ return false;
+ }
+
+ u32 bitmap = FIELD_GET(MGMT_MSG_EPMAP_BITMAP, msg.msg0);
+ u32 base = FIELD_GET(MGMT_MSG_EPMAP_BASE, msg.msg0);
+ for (unsigned int i = 0; i < 32; i++) {
+ if (bitmap & (1U << i)) {
+ u8 ep_idx = 32 * base + i;
+
+ if (ep_idx >= 0x20)
+ continue;
+ switch (ep_idx) {
+ case RTKIT_EP_CRASHLOG:
+ has_crashlog = true;
+ break;
+ case RTKIT_EP_DEBUG:
+ has_debug = true;
+ break;
+ case RTKIT_EP_IOREPORT:
+ has_ioreport = true;
+ break;
+ case RTKIT_EP_SYSLOG:
+ has_syslog = true;
+ break;
+ case RTKIT_EP_OSLOG:
+ has_oslog = true;
+ case RTKIT_EP_MGMT:
+ break;
+ default:
+ rtkit_printf("unknown system endpoint 0x%02x\n", ep_idx);
+ }
+ }
+ }
+
+ if (msg.msg0 & MGMT_MSG_EPMAP_DONE)
+ got_epmap = true;
+
+ msg.msg0 = FIELD_PREP(MGMT_TYPE, MGMT_MSG_EPMAP_REPLY);
+ msg.msg0 |= FIELD_PREP(MGMT_MSG_EPMAP_BASE, base);
+ if (got_epmap)
+ msg.msg0 |= MGMT_MSG_EPMAP_REPLY_DONE;
+ else
+ msg.msg0 |= MGMT_MSG_EPMAP_REPLY_MORE;
+
+ msg.msg1 = RTKIT_EP_MGMT;
+
+ if (!asc_send(rtk->asc, &msg)) {
+ rtkit_printf("couldn't reply to endpoint map\n");
+ return false;
+ }
+ }
+
+ /* start all required system endpoints */
+ if (has_debug && !rtkit_start_ep(rtk, RTKIT_EP_DEBUG))
+ return false;
+ if (has_crashlog && !rtkit_start_ep(rtk, RTKIT_EP_CRASHLOG))
+ return false;
+ if (has_syslog && !rtkit_start_ep(rtk, RTKIT_EP_SYSLOG))
+ return false;
+ if (has_ioreport && !rtkit_start_ep(rtk, RTKIT_EP_IOREPORT))
+ return false;
+ if (has_oslog && !rtkit_start_ep(rtk, RTKIT_EP_OSLOG))
+ return false;
+
+ while (rtk->iop_power != RTKIT_POWER_ON) {
+ struct rtkit_message rtk_msg;
+ int ret = rtkit_recv(rtk, &rtk_msg);
+ if (ret == 1)
+ rtkit_printf("unexpected message to non-system endpoint 0x%02x during boot: %lx\n",
+ rtk_msg.ep, rtk_msg.msg);
+ else if (ret < 0)
+ return false;
+ }
+
+ /* this enables syslog */
+ msg.msg0 =
+ FIELD_PREP(MGMT_TYPE, MGMT_MSG_AP_PWR_STATE) | FIELD_PREP(MGMT_PWR_STATE, RTKIT_POWER_ON);
+ msg.msg1 = RTKIT_EP_MGMT;
+ if (!asc_send(rtk->asc, &msg)) {
+ rtkit_printf("unable to send AP power message\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool rtkit_switch_power_state(rtkit_dev_t *rtk, enum rtkit_power_state target)
+{
+ struct asc_message msg;
+
+ if (rtk->crashed)
+ return false;
+
+ /* AP power should always go to QUIESCED, otherwise rebooting doesn't work */
+ msg.msg0 = FIELD_PREP(MGMT_TYPE, MGMT_MSG_AP_PWR_STATE) |
+ FIELD_PREP(MGMT_PWR_STATE, RTKIT_POWER_QUIESCED);
+ msg.msg1 = RTKIT_EP_MGMT;
+ if (!asc_send(rtk->asc, &msg)) {
+ rtkit_printf("unable to send shutdown message\n");
+ return false;
+ }
+
+ while (rtk->ap_power != RTKIT_POWER_QUIESCED) {
+ struct rtkit_message rtk_msg;
+ int ret = rtkit_recv(rtk, &rtk_msg);
+
+ if (ret > 0) {
+ rtkit_printf("unexpected message to non-system endpoint 0x%02x during shutdown: %lx\n",
+ rtk_msg.ep, rtk_msg.msg);
+ continue;
+ } else if (ret < 0) {
+ rtkit_printf("IOP died during shutdown\n");
+ return false;
+ }
+ }
+
+ msg.msg0 = FIELD_PREP(MGMT_TYPE, MGMT_MSG_IOP_PWR_STATE) | FIELD_PREP(MGMT_PWR_STATE, target);
+ if (!asc_send(rtk->asc, &msg)) {
+ rtkit_printf("unable to send shutdown message\n");
+ return false;
+ }
+
+ while (rtk->iop_power != target) {
+ struct rtkit_message rtk_msg;
+ int ret = rtkit_recv(rtk, &rtk_msg);
+
+ if (ret > 0) {
+ rtkit_printf("unexpected message to non-system endpoint 0x%02x during shutdown: %lx\n",
+ rtk_msg.ep, rtk_msg.msg);
+ continue;
+ } else if (ret < 0) {
+ rtkit_printf("IOP died during shutdown\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool rtkit_quiesce(rtkit_dev_t *rtk)
+{
+ return rtkit_switch_power_state(rtk, RTKIT_POWER_QUIESCED);
+}
+
+bool rtkit_sleep(rtkit_dev_t *rtk)
+{
+ int ret = rtkit_switch_power_state(rtk, RTKIT_POWER_SLEEP);
+ if (ret < 0)
+ return ret;
+
+ asc_cpu_stop(rtk->asc);
+ return 0;
+}
diff --git a/tools/src/rtkit.h b/tools/src/rtkit.h
new file mode 100644
index 0000000..9d87ee1
--- /dev/null
+++ b/tools/src/rtkit.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef RTKIT_H
+#define RTKIT_H
+
+#include "asc.h"
+#include "dart.h"
+#include "iova.h"
+#include "sart.h"
+#include "types.h"
+
+typedef struct rtkit_dev rtkit_dev_t;
+
+struct rtkit_message {
+ u8 ep;
+ u64 msg;
+};
+
+struct rtkit_buffer {
+ void *bfr;
+ u64 dva;
+ size_t sz;
+};
+
+rtkit_dev_t *rtkit_init(const char *name, asc_dev_t *asc, dart_dev_t *dart,
+ iova_domain_t *dart_iovad, sart_dev_t *sart);
+bool rtkit_quiesce(rtkit_dev_t *rtk);
+bool rtkit_sleep(rtkit_dev_t *rtk);
+void rtkit_free(rtkit_dev_t *rtk);
+
+bool rtkit_start_ep(rtkit_dev_t *rtk, u8 ep);
+bool rtkit_boot(rtkit_dev_t *rtk);
+
+int rtkit_recv(rtkit_dev_t *rtk, struct rtkit_message *msg);
+bool rtkit_send(rtkit_dev_t *rtk, const struct rtkit_message *msg);
+
+bool rtkit_map(rtkit_dev_t *rtk, void *phys, size_t sz, u64 *dva);
+bool rtkit_unmap(rtkit_dev_t *rtk, u64 dva, size_t sz);
+
+bool rtkit_alloc_buffer(rtkit_dev_t *rtk, struct rtkit_buffer *bfr, size_t sz);
+bool rtkit_free_buffer(rtkit_dev_t *rtk, struct rtkit_buffer *bfr);
+
+#endif
diff --git a/tools/src/sart.c b/tools/src/sart.c
new file mode 100644
index 0000000..e0345cd
--- /dev/null
+++ b/tools/src/sart.c
@@ -0,0 +1,219 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "adt.h"
+#include "malloc.h"
+#include "sart.h"
+#include "string.h"
+#include "utils.h"
+
+struct sart_dev {
+ uintptr_t base;
+ u32 protected_entries;
+
+ void (*get_entry)(sart_dev_t *sart, int index, u8 *flags, void **paddr, size_t *size);
+ bool (*set_entry)(sart_dev_t *sart, int index, u8 flags, void *paddr, size_t size);
+};
+
+#define APPLE_SART_MAX_ENTRIES 16
+
+/* This is probably a bitfield but the exact meaning of each bit is unknown. */
+#define APPLE_SART_FLAGS_ALLOW 0xff
+
+/* SARTv2 registers */
+#define APPLE_SART2_CONFIG(idx) (0x00 + 4 * (idx))
+#define APPLE_SART2_CONFIG_FLAGS GENMASK(31, 24)
+#define APPLE_SART2_CONFIG_SIZE GENMASK(23, 0)
+#define APPLE_SART2_CONFIG_SIZE_SHIFT 12
+#define APPLE_SART2_CONFIG_SIZE_MAX GENMASK(23, 0)
+
+#define APPLE_SART2_PADDR(idx) (0x40 + 4 * (idx))
+#define APPLE_SART2_PADDR_SHIFT 12
+
+/* SARTv3 registers */
+#define APPLE_SART3_CONFIG(idx) (0x00 + 4 * (idx))
+
+#define APPLE_SART3_PADDR(idx) (0x40 + 4 * (idx))
+#define APPLE_SART3_PADDR_SHIFT 12
+
+#define APPLE_SART3_SIZE(idx) (0x80 + 4 * (idx))
+#define APPLE_SART3_SIZE_SHIFT 12
+#define APPLE_SART3_SIZE_MAX GENMASK(29, 0)
+
+static void sart2_get_entry(sart_dev_t *sart, int index, u8 *flags, void **paddr, size_t *size)
+{
+ u32 cfg = read32(sart->base + APPLE_SART2_CONFIG(index));
+ *flags = FIELD_GET(APPLE_SART2_CONFIG_FLAGS, cfg);
+ *size = (size_t)FIELD_GET(APPLE_SART2_CONFIG_SIZE, cfg) << APPLE_SART2_CONFIG_SIZE_SHIFT;
+ *paddr =
+ (void *)((u64)read32(sart->base + APPLE_SART2_PADDR(index)) << APPLE_SART2_PADDR_SHIFT);
+}
+
+static bool sart2_set_entry(sart_dev_t *sart, int index, u8 flags, void *paddr_, size_t size)
+{
+ u32 cfg;
+ u64 paddr = (u64)paddr_;
+
+ if (size & ((1 << APPLE_SART2_CONFIG_SIZE_SHIFT) - 1))
+ return false;
+ if (paddr & ((1 << APPLE_SART2_PADDR_SHIFT) - 1))
+ return false;
+
+ size >>= APPLE_SART2_CONFIG_SIZE_SHIFT;
+ paddr >>= APPLE_SART2_PADDR_SHIFT;
+
+ if (size > APPLE_SART2_CONFIG_SIZE_MAX)
+ return false;
+
+ cfg = FIELD_PREP(APPLE_SART2_CONFIG_FLAGS, flags);
+ cfg |= FIELD_PREP(APPLE_SART2_CONFIG_SIZE, size);
+
+ write32(sart->base + APPLE_SART2_PADDR(index), paddr);
+ write32(sart->base + APPLE_SART2_CONFIG(index), cfg);
+
+ return true;
+}
+
+static void sart3_get_entry(sart_dev_t *sart, int index, u8 *flags, void **paddr, size_t *size)
+{
+ *flags = read32(sart->base + APPLE_SART3_CONFIG(index));
+ *size = (size_t)read32(sart->base + APPLE_SART3_SIZE(index)) << APPLE_SART3_SIZE_SHIFT;
+ *paddr =
+ (void *)((u64)read32(sart->base + APPLE_SART3_PADDR(index)) << APPLE_SART3_PADDR_SHIFT);
+}
+
+static bool sart3_set_entry(sart_dev_t *sart, int index, u8 flags, void *paddr_, size_t size)
+{
+ u64 paddr = (u64)paddr_;
+ if (size & ((1 << APPLE_SART3_SIZE_SHIFT) - 1))
+ return false;
+ if (paddr & ((1 << APPLE_SART3_PADDR_SHIFT) - 1))
+ return false;
+
+ paddr >>= APPLE_SART3_PADDR_SHIFT;
+ size >>= APPLE_SART3_SIZE_SHIFT;
+
+ if (size > APPLE_SART3_SIZE_MAX)
+ return false;
+
+ write32(sart->base + APPLE_SART3_PADDR(index), paddr);
+ write32(sart->base + APPLE_SART3_SIZE(index), size);
+ write32(sart->base + APPLE_SART3_CONFIG(index), flags);
+
+ return true;
+}
+
+sart_dev_t *sart_init(const char *adt_path)
+{
+ int sart_path[8];
+ int node = adt_path_offset_trace(adt, adt_path, sart_path);
+ if (node < 0) {
+ printf("sart: Error getting SART node %s\n", adt_path);
+ return NULL;
+ }
+
+ u64 base;
+ if (adt_get_reg(adt, sart_path, "reg", 0, &base, NULL) < 0) {
+ printf("sart: Error getting SART %s base address.\n", adt_path);
+ return NULL;
+ }
+
+ const u32 *sart_version = adt_getprop(adt, node, "sart-version", NULL);
+ if (!sart_version) {
+ printf("sart: SART %s has no sart-version property\n", adt_path);
+ return NULL;
+ }
+
+ sart_dev_t *sart = malloc(sizeof(*sart));
+ if (!sart)
+ return NULL;
+
+ memset(sart, 0, sizeof(*sart));
+ sart->base = base;
+
+ switch (*sart_version) {
+ case 2:
+ sart->get_entry = sart2_get_entry;
+ sart->set_entry = sart2_set_entry;
+ break;
+ case 3:
+ sart->get_entry = sart3_get_entry;
+ sart->set_entry = sart3_set_entry;
+ break;
+ default:
+ printf("sart: SART %s has unknown version %d\n", adt_path, *sart_version);
+ free(sart);
+ return NULL;
+ }
+
+ printf("sart: SARTv%d %s at 0x%lx\n", *sart_version, adt_path, base);
+
+ sart->protected_entries = 0;
+ for (unsigned int i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) {
+ void *paddr;
+ u8 flags;
+ size_t sz;
+
+ sart->get_entry(sart, i, &flags, &paddr, &sz);
+ if (flags)
+ sart->protected_entries |= 1 << i;
+ }
+
+ return sart;
+}
+
+void sart_free(sart_dev_t *sart)
+{
+ for (unsigned int i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) {
+ if (sart->protected_entries & (1 << i))
+ continue;
+ sart->set_entry(sart, i, 0, NULL, 0);
+ }
+
+ free(sart);
+}
+
+bool sart_add_allowed_region(sart_dev_t *sart, void *paddr, size_t sz)
+{
+ for (unsigned int i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) {
+ void *e_paddr;
+ u8 e_flags;
+ size_t e_sz;
+
+ if (sart->protected_entries & (1 << i))
+ continue;
+
+ sart->get_entry(sart, i, &e_flags, &e_paddr, &e_sz);
+ if (e_flags)
+ continue;
+
+ return sart->set_entry(sart, i, APPLE_SART_FLAGS_ALLOW, paddr, sz);
+ }
+
+ printf("sart: no more free entries\n");
+ return false;
+}
+
+bool sart_remove_allowed_region(sart_dev_t *sart, void *paddr, size_t sz)
+{
+ for (unsigned int i = 0; i < APPLE_SART_MAX_ENTRIES; ++i) {
+ void *e_paddr;
+ u8 e_flags;
+ size_t e_sz;
+
+ if (sart->protected_entries & (1 << i))
+ continue;
+
+ sart->get_entry(sart, i, &e_flags, &e_paddr, &e_sz);
+ if (!e_flags)
+ continue;
+ if (e_paddr != paddr)
+ continue;
+ if (e_sz != sz)
+ continue;
+
+ return sart->set_entry(sart, i, 0, NULL, 0);
+ }
+
+ printf("sart: could not find entry to be removed\n");
+ return false;
+}
diff --git a/tools/src/sart.h b/tools/src/sart.h
new file mode 100644
index 0000000..37828c0
--- /dev/null
+++ b/tools/src/sart.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef SART_H
+#define SART_H
+
+#include "types.h"
+
+typedef struct sart_dev sart_dev_t;
+
+sart_dev_t *sart_init(const char *adt_path);
+void sart_free(sart_dev_t *asc);
+
+bool sart_add_allowed_region(sart_dev_t *sart, void *paddr, size_t sz);
+bool sart_remove_allowed_region(sart_dev_t *sart, void *paddr, size_t sz);
+
+#endif
diff --git a/tools/src/sep.c b/tools/src/sep.c
new file mode 100644
index 0000000..7a40fef
--- /dev/null
+++ b/tools/src/sep.c
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: MIT */
+
+#include <string.h>
+
+#include "asc.h"
+#include "sep.h"
+#include "types.h"
+#include "utils.h"
+
+#define SEP_MSG_EP GENMASK(7, 0)
+#define SEP_MSG_CMD GENMASK(23, 16)
+#define SEP_MSG_DATA GENMASK(63, 32)
+
+#define SEP_EP_ROM 0xff
+
+#define SEP_MSG_GETRAND 16
+#define SEP_REPLY_GETRAND 116
+
+#define SEP_TIMEOUT 1000
+
+static asc_dev_t *sep_asc = NULL;
+
+int sep_init(void)
+{
+ if (!sep_asc)
+ sep_asc = asc_init("/arm-io/sep");
+ if (!sep_asc)
+ return -1;
+ return 0;
+}
+
+size_t sep_get_random(void *buffer, size_t len)
+{
+ const struct asc_message msg_getrand = {.msg0 = FIELD_PREP(SEP_MSG_EP, SEP_EP_ROM) |
+ FIELD_PREP(SEP_MSG_CMD, SEP_MSG_GETRAND)};
+ int ret;
+ size_t done = 0;
+
+ ret = sep_init();
+ if (ret)
+ return 0;
+
+ while (len) {
+ struct asc_message reply;
+ u32 rng;
+ size_t copy;
+
+ if (!asc_send(sep_asc, &msg_getrand))
+ return done;
+ if (!asc_recv_timeout(sep_asc, &reply, SEP_TIMEOUT))
+ return done;
+ if (FIELD_GET(SEP_MSG_CMD, reply.msg0) != SEP_REPLY_GETRAND) {
+ printf("SEP: unexpected getrand reply: %016lx\n", reply.msg0);
+ return done;
+ }
+
+ rng = FIELD_GET(SEP_MSG_DATA, reply.msg0);
+ copy = sizeof(rng);
+ if (copy > len)
+ copy = len;
+ memcpy(buffer, &rng, copy);
+ done += copy;
+ len -= copy;
+ buffer += copy;
+ }
+
+ return done;
+}
diff --git a/tools/src/sep.h b/tools/src/sep.h
new file mode 100644
index 0000000..8d7d04a
--- /dev/null
+++ b/tools/src/sep.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef SEP_H
+#define SEP_H
+
+#include "asc.h"
+#include "types.h"
+
+int sep_init(void);
+size_t sep_get_random(void *buffer, size_t len);
+
+#endif
diff --git a/tools/src/smp.c b/tools/src/smp.c
new file mode 100644
index 0000000..6ed522d
--- /dev/null
+++ b/tools/src/smp.c
@@ -0,0 +1,296 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "smp.h"
+#include "adt.h"
+#include "cpu_regs.h"
+#include "malloc.h"
+#include "pmgr.h"
+#include "soc.h"
+#include "string.h"
+#include "types.h"
+#include "utils.h"
+
+#define CPU_START_OFF_T8103 0x54000
+#define CPU_START_OFF_T8112 0x34000
+
+#define CPU_REG_CORE GENMASK(7, 0)
+#define CPU_REG_CLUSTER GENMASK(10, 8)
+#define CPU_REG_DIE GENMASK(14, 11)
+
+struct spin_table {
+ u64 mpidr;
+ u64 flag;
+ u64 target;
+ u64 args[4];
+ u64 retval;
+};
+
+void *_reset_stack;
+
+#define DUMMY_STACK_SIZE 0x1000
+u8 dummy_stack[DUMMY_STACK_SIZE];
+
+u8 *secondary_stacks[MAX_CPUS] = {dummy_stack};
+
+static bool wfe_mode = false;
+
+static int target_cpu;
+static struct spin_table spin_table[MAX_CPUS];
+
+extern u8 _vectors_start[0];
+
+void smp_secondary_entry(void)
+{
+ struct spin_table *me = &spin_table[target_cpu];
+
+ if (in_el2())
+ msr(TPIDR_EL2, target_cpu);
+ else
+ msr(TPIDR_EL1, target_cpu);
+
+ printf(" Index: %d (table: %p)\n\n", target_cpu, me);
+
+ me->mpidr = mrs(MPIDR_EL1) & 0xFFFFFF;
+
+ sysop("dmb sy");
+ me->flag = 1;
+ sysop("dmb sy");
+ u64 target;
+
+ while (1) {
+ while (!(target = me->target)) {
+ if (wfe_mode) {
+ sysop("wfe");
+ } else {
+ deep_wfi();
+ msr(SYS_IMP_APL_IPI_SR_EL1, 1);
+ }
+ sysop("isb");
+ }
+ sysop("dmb sy");
+ me->flag++;
+ sysop("dmb sy");
+ me->retval = ((u64(*)(u64 a, u64 b, u64 c, u64 d))target)(me->args[0], me->args[1],
+ me->args[2], me->args[3]);
+ sysop("dmb sy");
+ me->target = 0;
+ sysop("dmb sy");
+ }
+}
+
+static void smp_start_cpu(int index, int die, int cluster, int core, u64 rvbar, u64 cpu_start_base)
+{
+ int i;
+
+ if (index >= MAX_CPUS)
+ return;
+
+ if (spin_table[index].flag)
+ return;
+
+ printf("Starting CPU %d (%d:%d:%d)... ", index, die, cluster, core);
+
+ memset(&spin_table[index], 0, sizeof(struct spin_table));
+
+ target_cpu = index;
+ secondary_stacks[index] = memalign(0x4000, SECONDARY_STACK_SIZE);
+ _reset_stack = secondary_stacks[index] + SECONDARY_STACK_SIZE;
+
+ sysop("dmb sy");
+
+ write64(rvbar, (u64)_vectors_start);
+
+ cpu_start_base += die * PMGR_DIE_OFFSET;
+
+ // Some kind of system level startup/status bit
+ // Without this, IRQs don't work
+ write32(cpu_start_base + 0x4, 1 << (4 * cluster + core));
+
+ // Actually start the core
+ write32(cpu_start_base + 0x8 + 4 * cluster, 1 << core);
+
+ for (i = 0; i < 500; i++) {
+ sysop("dmb ld");
+ if (spin_table[index].flag)
+ break;
+ udelay(1000);
+ }
+
+ if (i >= 500)
+ printf("Failed!\n");
+ else
+ printf(" Started.\n");
+
+ _reset_stack = dummy_stack + DUMMY_STACK_SIZE;
+}
+
+void smp_start_secondaries(void)
+{
+ printf("Starting secondary CPUs...\n");
+
+ int pmgr_path[8];
+ u64 pmgr_reg;
+
+ if (adt_path_offset_trace(adt, "/arm-io/pmgr", pmgr_path) < 0) {
+ printf("Error getting /arm-io/pmgr node\n");
+ return;
+ }
+ if (adt_get_reg(adt, pmgr_path, "reg", 0, &pmgr_reg, NULL) < 0) {
+ printf("Error getting /arm-io/pmgr regs\n");
+ return;
+ }
+
+ int node = adt_path_offset(adt, "/cpus");
+ if (node < 0) {
+ printf("Error getting /cpus node\n");
+ return;
+ }
+
+ int cpu_nodes[MAX_CPUS];
+ u64 cpu_start_off;
+
+ memset(cpu_nodes, 0, sizeof(cpu_nodes));
+
+ switch (chip_id) {
+ case T8103:
+ case T6000:
+ case T6001:
+ case T6002:
+ cpu_start_off = CPU_START_OFF_T8103;
+ break;
+ case T8112:
+ cpu_start_off = CPU_START_OFF_T8112;
+ break;
+ default:
+ printf("CPU start offset is unknown for this SoC!\n");
+ return;
+ }
+
+ ADT_FOREACH_CHILD(adt, node)
+ {
+ u32 cpu_id;
+
+ if (ADT_GETPROP(adt, node, "cpu-id", &cpu_id) < 0)
+ continue;
+ if (cpu_id >= MAX_CPUS) {
+ printf("cpu-id %d exceeds max CPU count %d: increase MAX_CPUS\n", cpu_id, MAX_CPUS);
+ continue;
+ }
+
+ cpu_nodes[cpu_id] = node;
+ }
+
+ for (int i = 1; i < MAX_CPUS; i++) {
+ int node = cpu_nodes[i];
+
+ if (!node)
+ continue;
+
+ u32 reg;
+ u64 cpu_impl_reg[2];
+ if (ADT_GETPROP(adt, node, "reg", &reg) < 0)
+ continue;
+ if (ADT_GETPROP_ARRAY(adt, node, "cpu-impl-reg", cpu_impl_reg) < 0)
+ continue;
+
+ u8 core = FIELD_GET(CPU_REG_CORE, reg);
+ u8 cluster = FIELD_GET(CPU_REG_CLUSTER, reg);
+ u8 die = FIELD_GET(CPU_REG_DIE, reg);
+
+ smp_start_cpu(i, die, cluster, core, cpu_impl_reg[0], pmgr_reg + cpu_start_off);
+ }
+
+ spin_table[0].mpidr = mrs(MPIDR_EL1) & 0xFFFFFF;
+}
+
+void smp_send_ipi(int cpu)
+{
+ if (cpu >= MAX_CPUS)
+ return;
+
+ u64 mpidr = spin_table[cpu].mpidr;
+ msr(SYS_IMP_APL_IPI_RR_GLOBAL_EL1, (mpidr & 0xff) | ((mpidr & 0xff00) << 8));
+}
+
+void smp_call4(int cpu, void *func, u64 arg0, u64 arg1, u64 arg2, u64 arg3)
+{
+ if (cpu >= MAX_CPUS)
+ return;
+
+ struct spin_table *target = &spin_table[cpu];
+
+ if (cpu == 0)
+ return;
+
+ u64 flag = target->flag;
+ target->args[0] = arg0;
+ target->args[1] = arg1;
+ target->args[2] = arg2;
+ target->args[3] = arg3;
+ sysop("dmb sy");
+ target->target = (u64)func;
+ sysop("dsb sy");
+
+ if (wfe_mode)
+ sysop("sev");
+ else
+ smp_send_ipi(cpu);
+
+ while (target->flag == flag)
+ sysop("dmb sy");
+}
+
+u64 smp_wait(int cpu)
+{
+ if (cpu >= MAX_CPUS)
+ return 0;
+
+ struct spin_table *target = &spin_table[cpu];
+
+ while (target->target)
+ sysop("dmb sy");
+
+ return target->retval;
+}
+
+void smp_set_wfe_mode(bool new_mode)
+{
+ wfe_mode = new_mode;
+ sysop("dsb sy");
+
+ for (int cpu = 1; cpu < MAX_CPUS; cpu++)
+ if (smp_is_alive(cpu))
+ smp_send_ipi(cpu);
+
+ sysop("sev");
+}
+
+bool smp_is_alive(int cpu)
+{
+ if (cpu >= MAX_CPUS)
+ return false;
+
+ return spin_table[cpu].flag;
+}
+
+uint64_t smp_get_mpidr(int cpu)
+{
+ if (cpu >= MAX_CPUS)
+ return 0;
+
+ return spin_table[cpu].mpidr;
+}
+
+u64 smp_get_release_addr(int cpu)
+{
+ struct spin_table *target = &spin_table[cpu];
+
+ if (cpu >= MAX_CPUS)
+ return 0;
+
+ target->args[0] = 0;
+ target->args[1] = 0;
+ target->args[2] = 0;
+ target->args[3] = 0;
+ return (u64)&target->target;
+}
diff --git a/tools/src/smp.h b/tools/src/smp.h
new file mode 100644
index 0000000..c802f3e
--- /dev/null
+++ b/tools/src/smp.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef __SMP_H__
+#define __SMP_H__
+
+#include "types.h"
+#include "utils.h"
+
+#define MAX_CPUS 20
+
+#define SECONDARY_STACK_SIZE 0x10000
+extern u8 *secondary_stacks[MAX_CPUS];
+
+void smp_secondary_entry(void);
+
+void smp_start_secondaries(void);
+
+#define smp_call0(i, f) smp_call4(i, f, 0, 0, 0, 0)
+#define smp_call1(i, f, a) smp_call4(i, f, a, 0, 0, 0)
+#define smp_call2(i, f, a, b) smp_call4(i, f, a, b, 0, 0)
+#define smp_call3(i, f, a, b, c) smp_call4(i, f, a, b, c, 0)
+
+void smp_call4(int cpu, void *func, u64 arg0, u64 arg1, u64 arg2, u64 arg3);
+
+u64 smp_wait(int cpu);
+
+bool smp_is_alive(int cpu);
+uint64_t smp_get_mpidr(int cpu);
+u64 smp_get_release_addr(int cpu);
+void smp_set_wfe_mode(bool new_mode);
+void smp_send_ipi(int cpu);
+
+static inline int smp_id(void)
+{
+ if (in_el2())
+ return mrs(TPIDR_EL2);
+ else
+ return mrs(TPIDR_EL1);
+}
+
+#endif
diff --git a/tools/src/soc.h b/tools/src/soc.h
new file mode 100644
index 0000000..26ddddc
--- /dev/null
+++ b/tools/src/soc.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef __SOC_H__
+#define __SOC_H__
+
+#include "../config.h"
+
+#define T8103 0x8103
+#define T8112 0x8112
+#define T6000 0x6000
+#define T6001 0x6001
+#define T6002 0x6002
+
+#ifdef TARGET
+
+#if TARGET == T8103
+#define EARLY_UART_BASE 0x235200000
+#elif TARGET == T6000 || TARGET == T6001 || TARGET == T6002
+#define EARLY_UART_BASE 0x39b200000
+#elif TARGET == T8112
+#define EARLY_UART_BASE 0x235200000
+#endif
+
+#endif
+#endif
diff --git a/tools/src/start.S b/tools/src/start.S
new file mode 100644
index 0000000..b0051e6
--- /dev/null
+++ b/tools/src/start.S
@@ -0,0 +1,176 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "soc.h"
+
+#define UTRSTAT 0x010
+#define UTXH 0x020
+
+.extern _start_c
+.extern _stack_bot
+.extern _v_sp0_sync
+.extern _v_sp0_irq
+.extern _v_sp0_fiq
+.extern _v_sp0_serr
+.extern _reset_stack
+.extern _cpu_reset_c
+.extern wdt_reboot
+
+.section .init, "ax"
+
+.align 11
+.globl _vectors_start
+_vectors_start:
+
+ mov x9, '0'
+ b cpu_reset
+ .align 7
+ mov x9, '1'
+ b exc_unk
+ .align 7
+ mov x9, '2'
+ b exc_unk
+ .align 7
+ mov x9, '3'
+ b exc_unk
+ .align 7
+ b _v_sp0_sync
+ .align 7
+ b _v_sp0_irq
+ .align 7
+ b _v_sp0_fiq
+ .align 7
+ b _v_sp0_serr
+ .align 7
+ b _v_sp0_sync
+ .align 7
+ b _v_sp0_irq
+ .align 7
+ b _v_sp0_fiq
+ .align 7
+ b _v_sp0_serr
+ .align 7
+ mov x9, 'p'
+ b exc_unk
+ .align 7
+ mov x9, 'q'
+ b exc_unk
+ .align 7
+ mov x9, 'r'
+ b exc_unk
+ .align 7
+ mov x9, 's'
+ b exc_unk
+ .align 7
+
+.globl _start
+.type _start, @function
+_start:
+ mov x19, x0
+
+ mov w0, 'm'
+ bl debug_putc
+
+ adrp x1, _stack_bot
+ mov sp, x1
+
+ mov w0, '1'
+ bl debug_putc
+
+ ldr x2, [sp, #-8]
+
+ mov w0, 'n'
+ bl debug_putc
+
+ adrp x0, _base
+ mov x20, x0
+ adrp x1, _rela_start
+ add x1, x1, :lo12:_rela_start
+ adrp x2, _rela_end
+ add x2, x2, :lo12:_rela_end
+ bl apply_rela
+
+ mov w0, '1'
+ bl debug_putc
+ mov w0, 0xd /* '\r', clang compat */
+ bl debug_putc
+ mov w0, '\n'
+ bl debug_putc
+
+ mov x0, x19
+ mov x1, x20
+ bl _start_c
+ b .
+
+.globl exc_unk
+.type exc_unk, @function
+exc_unk:
+ mov w0, 0xd /* '\r', clang compat */
+ bl debug_putc
+ mov w0, '\n'
+ bl debug_putc
+ mov w0, '!'
+ bl debug_putc
+ mov w0, 'E'
+ bl debug_putc
+ mov w0, 'x'
+ bl debug_putc
+ mov w0, 'C'
+ bl debug_putc
+ mov w0, ':'
+ bl debug_putc
+ mov w0, w9
+ bl debug_putc
+ mov w0, '!'
+ bl debug_putc
+ mov w0, 0xd /* '\r', clang compat */
+ bl debug_putc
+ mov w0, '\n'
+ bl debug_putc
+ b reboot
+
+.globl cpu_reset
+.type cpu_reset, @function
+cpu_reset:
+ mov w0, 'O'
+ bl debug_putc
+
+ adrp x1, _reset_stack
+ add x1, x1, :lo12:_reset_stack
+ ldr x1, [x1]
+ mov sp, x1
+
+ ldr x2, [sp, #-8]
+
+ mov w0, 'K'
+ bl debug_putc
+
+ mov x0, sp
+ bl _cpu_reset_c
+ b .
+
+.globl debug_putc
+.type debug_putc, @function
+debug_putc:
+#ifdef EARLY_UART_BASE
+ ldr x1, =EARLY_UART_BASE
+
+1:
+ ldr w2, [x1, UTRSTAT]
+ tst w2, #2
+ beq 1b
+ str w0, [x1, UTXH]
+#endif
+ ret
+
+.globl reboot
+.type reboot, @function
+reboot:
+ mrs x0, CurrentEL
+ cmp x0, #8
+ beq 1f
+ hvc #0
+1:
+ bl wdt_reboot
+ b .
+
+.pool
diff --git a/tools/src/startup.c b/tools/src/startup.c
new file mode 100644
index 0000000..1052707
--- /dev/null
+++ b/tools/src/startup.c
@@ -0,0 +1,121 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "chickens.h"
+#include "exception.h"
+#include "smp.h"
+#include "string.h"
+#include "types.h"
+#include "uart.h"
+#include "utils.h"
+#include "xnuboot.h"
+
+u64 boot_args_addr;
+struct boot_args cur_boot_args;
+void *adt;
+
+struct rela_entry {
+ uint64_t off, type, addend;
+};
+
+void debug_putc(char c);
+void m1n1_main(void);
+
+extern char _bss_start[0];
+extern char _bss_end[0];
+
+#define R_AARCH64_RELATIVE 1027
+
+void apply_rela(uint64_t base, struct rela_entry *rela_start, struct rela_entry *rela_end)
+{
+ struct rela_entry *e = rela_start;
+
+ while (e < rela_end) {
+ switch (e->type) {
+ case R_AARCH64_RELATIVE:
+ *(u64 *)(base + e->off) = base + e->addend;
+ break;
+ default:
+ debug_putc('R');
+ debug_putc('!');
+ while (1)
+ ;
+ }
+ e++;
+ }
+}
+
+void dump_boot_args(struct boot_args *ba)
+{
+ printf(" revision: %d\n", ba->revision);
+ printf(" version: %d\n", ba->version);
+ printf(" virt_base: 0x%lx\n", ba->virt_base);
+ printf(" phys_base: 0x%lx\n", ba->phys_base);
+ printf(" mem_size: 0x%lx\n", ba->mem_size);
+ printf(" top_of_kdata: 0x%lx\n", ba->top_of_kernel_data);
+ printf(" video:\n");
+ printf(" base: 0x%lx\n", ba->video.base);
+ printf(" display: 0x%lx\n", ba->video.display);
+ printf(" stride: 0x%lx\n", ba->video.stride);
+ printf(" width: %lu\n", ba->video.width);
+ printf(" height: %lu\n", ba->video.height);
+ printf(" depth: %lubpp\n", ba->video.depth & 0xff);
+ printf(" density: %lu\n", ba->video.depth >> 16);
+ printf(" machine_type: %d\n", ba->machine_type);
+ printf(" devtree: %p\n", ba->devtree);
+ printf(" devtree_size: 0x%x\n", ba->devtree_size);
+ printf(" cmdline: %s\n", ba->cmdline);
+ printf(" boot_flags: 0x%lx\n", ba->boot_flags);
+ printf(" mem_size_act: 0x%lx\n", ba->mem_size_actual);
+}
+
+void _start_c(void *boot_args, void *base)
+{
+ UNUSED(base);
+
+ if (in_el2())
+ msr(TPIDR_EL2, 0);
+ else
+ msr(TPIDR_EL1, 0);
+
+ memset64(_bss_start, 0, _bss_end - _bss_start);
+ boot_args_addr = (u64)boot_args;
+ memcpy(&cur_boot_args, boot_args, sizeof(cur_boot_args));
+
+ adt =
+ (void *)(((u64)cur_boot_args.devtree) - cur_boot_args.virt_base + cur_boot_args.phys_base);
+
+ int ret = uart_init();
+ if (ret < 0) {
+ debug_putc('!');
+ }
+
+ uart_puts("Initializing");
+ printf("CPU init (MIDR: 0x%lx)...\n", mrs(MIDR_EL1));
+ const char *type = init_cpu();
+ printf(" CPU: %s\n\n", type);
+
+ printf("boot_args at %p\n", boot_args);
+
+ dump_boot_args(&cur_boot_args);
+ printf("\n");
+
+ exception_initialize();
+ m1n1_main();
+}
+
+/* Secondary SMP core boot */
+void _cpu_reset_c(void *stack)
+{
+ if (mrs(MPIDR_EL1) & 0xffffff)
+ uart_puts("RVBAR entry on secondary CPU");
+ else
+ uart_puts("RVBAR entry on primary CPU");
+
+ printf("\n Stack base: %p\n", stack);
+ printf(" MPIDR: 0x%lx\n", mrs(MPIDR_EL1));
+ const char *type = init_cpu();
+ printf(" CPU: %s\n", type);
+
+ exception_initialize();
+ smp_secondary_entry();
+}
diff --git a/tools/src/string.c b/tools/src/string.c
new file mode 100644
index 0000000..318d0fc
--- /dev/null
+++ b/tools/src/string.c
@@ -0,0 +1,209 @@
+/* SPDX-License-Identifier: MIT */
+
+#include <stdbool.h>
+
+#include "string.h"
+
+// Routines based on The Public Domain C Library
+
+void *memcpy(void *s1, const void *s2, size_t n)
+{
+ char *dest = (char *)s1;
+ const char *src = (const char *)s2;
+
+ while (n--) {
+ *dest++ = *src++;
+ }
+
+ return s1;
+}
+
+void *memmove(void *s1, const void *s2, size_t n)
+{
+ char *dest = (char *)s1;
+ const char *src = (const char *)s2;
+
+ if (dest <= src) {
+ while (n--) {
+ *dest++ = *src++;
+ }
+ } else {
+ src += n;
+ dest += n;
+
+ while (n--) {
+ *--dest = *--src;
+ }
+ }
+
+ return s1;
+}
+
+int memcmp(const void *s1, const void *s2, size_t n)
+{
+ const unsigned char *p1 = (const unsigned char *)s1;
+ const unsigned char *p2 = (const unsigned char *)s2;
+
+ while (n--) {
+ if (*p1 != *p2) {
+ return *p1 - *p2;
+ }
+
+ ++p1;
+ ++p2;
+ }
+
+ return 0;
+}
+
+void *memset(void *s, int c, size_t n)
+{
+ unsigned char *p = (unsigned char *)s;
+
+ while (n--) {
+ *p++ = (unsigned char)c;
+ }
+
+ return s;
+}
+
+void *memchr(const void *s, int c, size_t n)
+{
+ const unsigned char *p = (const unsigned char *)s;
+
+ while (n--) {
+ if (*p == (unsigned char)c) {
+ return (void *)p;
+ }
+
+ ++p;
+ }
+
+ return NULL;
+}
+
+char *strcpy(char *s1, const char *s2)
+{
+ char *rc = s1;
+
+ while ((*s1++ = *s2++)) {
+ /* EMPTY */
+ }
+
+ return rc;
+}
+
+char *strncpy(char *s1, const char *s2, size_t n)
+{
+ char *rc = s1;
+
+ while (n && (*s1++ = *s2++)) {
+ /* Cannot do "n--" in the conditional as size_t is unsigned and we have
+ to check it again for >0 in the next loop below, so we must not risk
+ underflow.
+ */
+ --n;
+ }
+
+ /* Checking against 1 as we missed the last --n in the loop above. */
+ while (n-- > 1) {
+ *s1++ = '\0';
+ }
+
+ return rc;
+}
+
+int strcmp(const char *s1, const char *s2)
+{
+ while ((*s1) && (*s1 == *s2)) {
+ ++s1;
+ ++s2;
+ }
+
+ return (*(unsigned char *)s1 - *(unsigned char *)s2);
+}
+
+int strncmp(const char *s1, const char *s2, size_t n)
+{
+ while (n && *s1 && (*s1 == *s2)) {
+ ++s1;
+ ++s2;
+ --n;
+ }
+
+ if (n == 0) {
+ return 0;
+ } else {
+ return (*(unsigned char *)s1 - *(unsigned char *)s2);
+ }
+}
+
+size_t strlen(const char *s)
+{
+ size_t rc = 0;
+
+ while (s[rc]) {
+ ++rc;
+ }
+
+ return rc;
+}
+
+size_t strnlen(const char *s, size_t n)
+{
+ size_t rc = 0;
+
+ while (rc < n && s[rc]) {
+ ++rc;
+ }
+
+ return rc;
+}
+
+char *strchr(const char *s, int c)
+{
+ do {
+ if (*s == (char)c) {
+ return (char *)s;
+ }
+ } while (*s++);
+
+ return NULL;
+}
+
+char *strrchr(const char *s, int c)
+{
+ size_t i = 0;
+
+ while (s[i++]) {
+ /* EMPTY */
+ }
+
+ do {
+ if (s[--i] == (char)c) {
+ return (char *)s + i;
+ }
+ } while (i);
+
+ return NULL;
+}
+
+/* Very naive, no attempt to check for errors */
+long atol(const char *s)
+{
+ long val = 0;
+ bool neg = false;
+
+ if (*s == '-') {
+ neg = true;
+ s++;
+ }
+
+ while (*s >= '0' && *s <= '9')
+ val = (val * 10) + (*s++ - '0');
+
+ if (neg)
+ val = -val;
+
+ return val;
+}
diff --git a/tools/src/tinf/adler32.c b/tools/src/tinf/adler32.c
new file mode 100644
index 0000000..5b3c54f
--- /dev/null
+++ b/tools/src/tinf/adler32.c
@@ -0,0 +1,95 @@
+/*
+ * Adler-32 checksum
+ *
+ * Copyright (c) 2003-2019 Joergen Ibsen
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must
+ * not claim that you wrote the original software. If you use this
+ * software in a product, an acknowledgment in the product
+ * documentation would be appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must
+ * not be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ */
+
+/*
+ * Adler-32 algorithm taken from the zlib source, which is
+ * Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler
+ */
+
+#include "tinf.h"
+
+#define A32_BASE 65521
+#define A32_NMAX 5552
+
+unsigned int tinf_adler32(const void *data, unsigned int length)
+{
+ const unsigned char *buf = (const unsigned char *) data;
+
+ unsigned int s1 = 1;
+ unsigned int s2 = 0;
+
+ while (length > 0) {
+ int k = length < A32_NMAX ? length : A32_NMAX;
+ int i;
+
+ for (i = k / 16; i; --i, buf += 16) {
+ s1 += buf[0];
+ s2 += s1;
+ s1 += buf[1];
+ s2 += s1;
+ s1 += buf[2];
+ s2 += s1;
+ s1 += buf[3];
+ s2 += s1;
+ s1 += buf[4];
+ s2 += s1;
+ s1 += buf[5];
+ s2 += s1;
+ s1 += buf[6];
+ s2 += s1;
+ s1 += buf[7];
+ s2 += s1;
+
+ s1 += buf[8];
+ s2 += s1;
+ s1 += buf[9];
+ s2 += s1;
+ s1 += buf[10];
+ s2 += s1;
+ s1 += buf[11];
+ s2 += s1;
+ s1 += buf[12];
+ s2 += s1;
+ s1 += buf[13];
+ s2 += s1;
+ s1 += buf[14];
+ s2 += s1;
+ s1 += buf[15];
+ s2 += s1;
+ }
+
+ for (i = k % 16; i; --i) {
+ s1 += *buf++;
+ s2 += s1;
+ }
+
+ s1 %= A32_BASE;
+ s2 %= A32_BASE;
+
+ length -= k;
+ }
+
+ return (s2 << 16) | s1;
+}
diff --git a/tools/src/tinf/crc32.c b/tools/src/tinf/crc32.c
new file mode 100644
index 0000000..b83232c
--- /dev/null
+++ b/tools/src/tinf/crc32.c
@@ -0,0 +1,57 @@
+/*
+ * CRC32 checksum
+ *
+ * Copyright (c) 1998-2019 Joergen Ibsen
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must
+ * not claim that you wrote the original software. If you use this
+ * software in a product, an acknowledgment in the product
+ * documentation would be appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must
+ * not be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ */
+
+/*
+ * CRC32 algorithm taken from the zlib source, which is
+ * Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler
+ */
+
+#include "tinf.h"
+
+static const unsigned int tinf_crc32tab[16] = {
+ 0x00000000, 0x1DB71064, 0x3B6E20C8, 0x26D930AC, 0x76DC4190,
+ 0x6B6B51F4, 0x4DB26158, 0x5005713C, 0xEDB88320, 0xF00F9344,
+ 0xD6D6A3E8, 0xCB61B38C, 0x9B64C2B0, 0x86D3D2D4, 0xA00AE278,
+ 0xBDBDF21C
+};
+
+unsigned int tinf_crc32(const void *data, unsigned int length)
+{
+ const unsigned char *buf = (const unsigned char *) data;
+ unsigned int crc = 0xFFFFFFFF;
+ unsigned int i;
+
+ if (length == 0) {
+ return 0;
+ }
+
+ for (i = 0; i < length; ++i) {
+ crc ^= buf[i];
+ crc = tinf_crc32tab[crc & 0x0F] ^ (crc >> 4);
+ crc = tinf_crc32tab[crc & 0x0F] ^ (crc >> 4);
+ }
+
+ return crc ^ 0xFFFFFFFF;
+}
diff --git a/tools/src/tinf/tinf.h b/tools/src/tinf/tinf.h
new file mode 100644
index 0000000..ab23c83
--- /dev/null
+++ b/tools/src/tinf/tinf.h
@@ -0,0 +1,142 @@
+/*
+ * tinf - tiny inflate library (inflate, gzip, zlib)
+ *
+ * Copyright (c) 2003-2019 Joergen Ibsen
+ *
+ * This version of tinfzlib was modified for use with m1n1.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must
+ * not claim that you wrote the original software. If you use this
+ * software in a product, an acknowledgment in the product
+ * documentation would be appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must
+ * not be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ */
+
+#ifndef TINF_H_INCLUDED
+#define TINF_H_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define TINF_VER_MAJOR 1 /**< Major version number */
+#define TINF_VER_MINOR 2 /**< Minor version number */
+#define TINF_VER_PATCH 1 /**< Patch version number */
+#define TINF_VER_STRING "1.2.1" /**< Version number as a string */
+
+#ifndef TINFCC
+# ifdef __WATCOMC__
+# define TINFCC __cdecl
+# else
+# define TINFCC
+# endif
+#endif
+
+/**
+ * Status codes returned.
+ *
+ * @see tinf_uncompress, tinf_gzip_uncompress, tinf_zlib_uncompress
+ */
+typedef enum {
+ TINF_OK = 0, /**< Success */
+ TINF_DATA_ERROR = -3, /**< Input error */
+ TINF_BUF_ERROR = -5 /**< Not enough room for output */
+} tinf_error_code;
+
+/**
+ * Initialize global data used by tinf.
+ *
+ * @deprecated No longer required, may be removed in a future version.
+ */
+void TINFCC tinf_init(void);
+
+/**
+ * Decompress `sourceLen` bytes of deflate data from `source` to `dest`.
+ *
+ * The variable `destLen` points to must contain the size of `dest` on entry,
+ * and will be set to the size of the decompressed data on success.
+ *
+ * Reads at most `sourceLen` bytes from `source`.
+ * Writes at most `*destLen` bytes to `dest`.
+ *
+ * @param dest pointer to where to place decompressed data
+ * @param destLen pointer to variable containing size of `dest`
+ * @param source pointer to compressed data
+ * @param sourceLen size of compressed data
+ * @return `TINF_OK` on success, error code on error
+ */
+int TINFCC tinf_uncompress(void *dest, unsigned int *destLen,
+ const void *source, unsigned int *sourceLen);
+
+/**
+ * Decompress `sourceLen` bytes of gzip data from `source` to `dest`.
+ *
+ * The variable `destLen` points to must contain the size of `dest` on entry,
+ * and will be set to the size of the decompressed data on success.
+ *
+ * Reads at most `sourceLen` bytes from `source`.
+ * Writes at most `*destLen` bytes to `dest`.
+ *
+ * @param dest pointer to where to place decompressed data
+ * @param destLen pointer to variable containing size of `dest`
+ * @param source pointer to compressed data
+ * @param sourceLen size of compressed data
+ * @return `TINF_OK` on success, error code on error
+ */
+int TINFCC tinf_gzip_uncompress(void *dest, unsigned int *destLen,
+ const void *source, unsigned int *sourceLen);
+
+/**
+ * Decompress `sourceLen` bytes of zlib data from `source` to `dest`.
+ *
+ * The variable `destLen` points to must contain the size of `dest` on entry,
+ * and will be set to the size of the decompressed data on success.
+ *
+ * Reads at most `sourceLen` bytes from `source`.
+ * Writes at most `*destLen` bytes to `dest`.
+ *
+ * @param dest pointer to where to place decompressed data
+ * @param destLen pointer to variable containing size of `dest`
+ * @param source pointer to compressed data
+ * @param sourceLen size of compressed data
+ * @return `TINF_OK` on success, error code on error
+ */
+int TINFCC tinf_zlib_uncompress(void *dest, unsigned int *destLen,
+ const void *source, unsigned int *sourceLen);
+
+/**
+ * Compute Adler-32 checksum of `length` bytes starting at `data`.
+ *
+ * @param data pointer to data
+ * @param length size of data
+ * @return Adler-32 checksum
+ */
+unsigned int TINFCC tinf_adler32(const void *data, unsigned int length);
+
+/**
+ * Compute CRC32 checksum of `length` bytes starting at `data`.
+ *
+ * @param data pointer to data
+ * @param length size of data
+ * @return CRC32 checksum
+ */
+unsigned int TINFCC tinf_crc32(const void *data, unsigned int length);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* TINF_H_INCLUDED */
diff --git a/tools/src/tinf/tinfgzip.c b/tools/src/tinf/tinfgzip.c
new file mode 100644
index 0000000..ea07cd7
--- /dev/null
+++ b/tools/src/tinf/tinfgzip.c
@@ -0,0 +1,191 @@
+/*
+ * tinfgzip - tiny gzip decompressor
+ *
+ * Copyright (c) 2003-2019 Joergen Ibsen
+ *
+ * This version of tinfzlib was modified for use with m1n1.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must
+ * not claim that you wrote the original software. If you use this
+ * software in a product, an acknowledgment in the product
+ * documentation would be appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must
+ * not be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ */
+
+#include "tinf.h"
+
+typedef enum {
+ FTEXT = 1,
+ FHCRC = 2,
+ FEXTRA = 4,
+ FNAME = 8,
+ FCOMMENT = 16
+} tinf_gzip_flag;
+
+static unsigned int read_le16(const unsigned char *p)
+{
+ return ((unsigned int) p[0])
+ | ((unsigned int) p[1] << 8);
+}
+
+static unsigned int read_le32(const unsigned char *p)
+{
+ return ((unsigned int) p[0])
+ | ((unsigned int) p[1] << 8)
+ | ((unsigned int) p[2] << 16)
+ | ((unsigned int) p[3] << 24);
+}
+
+int tinf_gzip_uncompress(void *dest, unsigned int *destLen,
+ const void *source, unsigned int *sourceLen)
+{
+ const unsigned char *src = (const unsigned char *) source;
+ unsigned char *dst = (unsigned char *) dest;
+ const unsigned char *start;
+ unsigned int dlen, crc32;
+ int res;
+ unsigned char flg;
+ unsigned int sourceDataLen = 0;
+
+ /* -- Check header -- */
+
+ /* Check room for at least 10 byte header and 8 byte trailer */
+ if (*sourceLen && *sourceLen < 18) {
+ return TINF_DATA_ERROR;
+ }
+
+ /* Check id bytes */
+ if (src[0] != 0x1F || src[1] != 0x8B) {
+ return TINF_DATA_ERROR;
+ }
+
+ /* Check method is deflate */
+ if (src[2] != 8) {
+ return TINF_DATA_ERROR;
+ }
+
+ /* Get flag byte */
+ flg = src[3];
+
+ /* Check that reserved bits are zero */
+ if (flg & 0xE0) {
+ return TINF_DATA_ERROR;
+ }
+
+ /* -- Find start of compressed data -- */
+
+ /* Skip base header of 10 bytes */
+ start = src + 10;
+
+ /* Skip extra data if present */
+ if (flg & FEXTRA) {
+ unsigned int xlen = read_le16(start);
+
+ if (*sourceLen && xlen > *sourceLen - 12) {
+ return TINF_DATA_ERROR;
+ }
+
+ start += xlen + 2;
+ }
+
+ /* Skip file name if present */
+ if (flg & FNAME) {
+ do {
+ if (*sourceLen && start - src >= *sourceLen) {
+ return TINF_DATA_ERROR;
+ }
+ } while (*start++);
+ }
+
+ /* Skip file comment if present */
+ if (flg & FCOMMENT) {
+ do {
+ if (*sourceLen && start - src >= *sourceLen) {
+ return TINF_DATA_ERROR;
+ }
+ } while (*start++);
+ }
+
+ /* Check header crc if present */
+ if (flg & FHCRC) {
+ unsigned int hcrc;
+
+ if (*sourceLen && start - src > *sourceLen - 2) {
+ return TINF_DATA_ERROR;
+ }
+
+ hcrc = read_le16(start);
+
+ if (hcrc != (tinf_crc32(src, start - src) & 0x0000FFFF)) {
+ return TINF_DATA_ERROR;
+ }
+
+ start += 2;
+ }
+
+ /* -- Get decompressed length if available -- */
+
+ if (*sourceLen) {
+ dlen = read_le32(&src[*sourceLen - 4]);
+
+ if (dlen > *destLen) {
+ return TINF_BUF_ERROR;
+ }
+ }
+
+ /* -- Check source length if available -- */
+
+ if (*sourceLen) {
+ if ((src + *sourceLen) - start < 8) {
+ return TINF_DATA_ERROR;
+ }
+ sourceDataLen = (src + *sourceLen) - start - 8;
+ }
+
+ /* -- Decompress data -- */
+
+ res = tinf_uncompress(dst, destLen, start, &sourceDataLen);
+
+ if (res != TINF_OK) {
+ return TINF_DATA_ERROR;
+ }
+
+ sourceDataLen += (start - src) + 8;
+
+ if (*sourceLen && *sourceLen != sourceDataLen) {
+ return TINF_DATA_ERROR;
+ }
+
+ *sourceLen = sourceDataLen;
+
+ /* -- Check decompressed length -- */
+
+ dlen = read_le32(&src[*sourceLen - 4]);
+
+ if (*destLen != dlen) {
+ return TINF_DATA_ERROR;
+ }
+
+ /* -- Check CRC32 checksum -- */
+
+ crc32 = read_le32(&src[*sourceLen - 8]);
+
+ if (crc32 != tinf_crc32(dst, dlen)) {
+ return TINF_DATA_ERROR;
+ }
+
+ return TINF_OK;
+}
diff --git a/tools/src/tinf/tinflate.c b/tools/src/tinf/tinflate.c
new file mode 100644
index 0000000..c82526c
--- /dev/null
+++ b/tools/src/tinf/tinflate.c
@@ -0,0 +1,648 @@
+/*
+ * tinflate - tiny inflate
+ *
+ * Copyright (c) 2003-2019 Joergen Ibsen
+ *
+ * This version of tinfzlib was modified for use with m1n1.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must
+ * not claim that you wrote the original software. If you use this
+ * software in a product, an acknowledgment in the product
+ * documentation would be appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must
+ * not be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ */
+
+#include "tinf.h"
+
+#include <assert.h>
+#include <limits.h>
+
+#if defined(UINT_MAX) && (UINT_MAX) < 0xFFFFFFFFUL
+# error "tinf requires unsigned int to be at least 32-bit"
+#endif
+
+/* -- Internal data structures -- */
+
+struct tinf_tree {
+ unsigned short counts[16]; /* Number of codes with a given length */
+ unsigned short symbols[288]; /* Symbols sorted by code */
+ int max_sym;
+};
+
+struct tinf_data {
+ const unsigned char *source;
+ const unsigned char *source_end;
+ unsigned int tag;
+ int bitcount;
+ int overflow;
+
+ unsigned char *dest_start;
+ unsigned char *dest;
+ unsigned char *dest_end;
+
+ struct tinf_tree ltree; /* Literal/length tree */
+ struct tinf_tree dtree; /* Distance tree */
+};
+
+/* -- Utility functions -- */
+
+static unsigned int read_le16(const unsigned char *p)
+{
+ return ((unsigned int) p[0])
+ | ((unsigned int) p[1] << 8);
+}
+
+/* Build fixed Huffman trees */
+static void tinf_build_fixed_trees(struct tinf_tree *lt, struct tinf_tree *dt)
+{
+ int i;
+
+ /* Build fixed literal/length tree */
+ for (i = 0; i < 16; ++i) {
+ lt->counts[i] = 0;
+ }
+
+ lt->counts[7] = 24;
+ lt->counts[8] = 152;
+ lt->counts[9] = 112;
+
+ for (i = 0; i < 24; ++i) {
+ lt->symbols[i] = 256 + i;
+ }
+ for (i = 0; i < 144; ++i) {
+ lt->symbols[24 + i] = i;
+ }
+ for (i = 0; i < 8; ++i) {
+ lt->symbols[24 + 144 + i] = 280 + i;
+ }
+ for (i = 0; i < 112; ++i) {
+ lt->symbols[24 + 144 + 8 + i] = 144 + i;
+ }
+
+ lt->max_sym = 285;
+
+ /* Build fixed distance tree */
+ for (i = 0; i < 16; ++i) {
+ dt->counts[i] = 0;
+ }
+
+ dt->counts[5] = 32;
+
+ for (i = 0; i < 32; ++i) {
+ dt->symbols[i] = i;
+ }
+
+ dt->max_sym = 29;
+}
+
+/* Given an array of code lengths, build a tree */
+static int tinf_build_tree(struct tinf_tree *t, const unsigned char *lengths,
+ unsigned int num)
+{
+ unsigned short offs[16];
+ unsigned int i, num_codes, available;
+
+ assert(num <= 288);
+
+ for (i = 0; i < 16; ++i) {
+ t->counts[i] = 0;
+ }
+
+ t->max_sym = -1;
+
+ /* Count number of codes for each non-zero length */
+ for (i = 0; i < num; ++i) {
+ assert(lengths[i] <= 15);
+
+ if (lengths[i]) {
+ t->max_sym = i;
+ t->counts[lengths[i]]++;
+ }
+ }
+
+ /* Compute offset table for distribution sort */
+ for (available = 1, num_codes = 0, i = 0; i < 16; ++i) {
+ unsigned int used = t->counts[i];
+
+ /* Check length contains no more codes than available */
+ if (used > available) {
+ return TINF_DATA_ERROR;
+ }
+ available = 2 * (available - used);
+
+ offs[i] = num_codes;
+ num_codes += used;
+ }
+
+ /*
+ * Check all codes were used, or for the special case of only one
+ * code that it has length 1
+ */
+ if ((num_codes > 1 && available > 0)
+ || (num_codes == 1 && t->counts[1] != 1)) {
+ return TINF_DATA_ERROR;
+ }
+
+ /* Fill in symbols sorted by code */
+ for (i = 0; i < num; ++i) {
+ if (lengths[i]) {
+ t->symbols[offs[lengths[i]]++] = i;
+ }
+ }
+
+ /*
+ * For the special case of only one code (which will be 0) add a
+ * code 1 which results in a symbol that is too large
+ */
+ if (num_codes == 1) {
+ t->counts[1] = 2;
+ t->symbols[1] = t->max_sym + 1;
+ }
+
+ return TINF_OK;
+}
+
+/* -- Decode functions -- */
+
+static void tinf_refill(struct tinf_data *d, int num)
+{
+ assert(num >= 0 && num <= 32);
+
+ /* Read bytes until at least num bits available */
+ while (d->bitcount < num) {
+ if (d->source != d->source_end) {
+ d->tag |= (unsigned int) *d->source++ << d->bitcount;
+ }
+ else {
+ d->overflow = 1;
+ }
+ d->bitcount += 8;
+ }
+
+ assert(d->bitcount <= 32);
+}
+
+static unsigned int tinf_getbits_no_refill(struct tinf_data *d, int num)
+{
+ unsigned int bits;
+
+ assert(num >= 0 && num <= d->bitcount);
+
+ /* Get bits from tag */
+ bits = d->tag & ((1UL << num) - 1);
+
+ /* Remove bits from tag */
+ d->tag >>= num;
+ d->bitcount -= num;
+
+ return bits;
+}
+
+/* Get num bits from source stream */
+static unsigned int tinf_getbits(struct tinf_data *d, int num)
+{
+ tinf_refill(d, num);
+ return tinf_getbits_no_refill(d, num);
+}
+
+/* Read a num bit value from stream and add base */
+static unsigned int tinf_getbits_base(struct tinf_data *d, int num, int base)
+{
+ return base + (num ? tinf_getbits(d, num) : 0);
+}
+
+/* Given a data stream and a tree, decode a symbol */
+static int tinf_decode_symbol(struct tinf_data *d, const struct tinf_tree *t)
+{
+ int base = 0, offs = 0;
+ int len;
+
+ /*
+ * Get more bits while code index is above number of codes
+ *
+ * Rather than the actual code, we are computing the position of the
+ * code in the sorted order of codes, which is the index of the
+ * corresponding symbol.
+ *
+ * Conceptually, for each code length (level in the tree), there are
+ * counts[len] leaves on the left and internal nodes on the right.
+ * The index we have decoded so far is base + offs, and if that
+ * falls within the leaves we are done. Otherwise we adjust the range
+ * of offs and add one more bit to it.
+ */
+ for (len = 1; ; ++len) {
+ offs = 2 * offs + tinf_getbits(d, 1);
+
+ assert(len <= 15);
+
+ if (offs < t->counts[len]) {
+ break;
+ }
+
+ base += t->counts[len];
+ offs -= t->counts[len];
+ }
+
+ assert(base + offs >= 0 && base + offs < 288);
+
+ return t->symbols[base + offs];
+}
+
+/* Given a data stream, decode dynamic trees from it */
+static int tinf_decode_trees(struct tinf_data *d, struct tinf_tree *lt,
+ struct tinf_tree *dt)
+{
+ unsigned char lengths[288 + 32];
+
+ /* Special ordering of code length codes */
+ static const unsigned char clcidx[19] = {
+ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5,
+ 11, 4, 12, 3, 13, 2, 14, 1, 15
+ };
+
+ unsigned int hlit, hdist, hclen;
+ unsigned int i, num, length;
+ int res;
+
+ /* Get 5 bits HLIT (257-286) */
+ hlit = tinf_getbits_base(d, 5, 257);
+
+ /* Get 5 bits HDIST (1-32) */
+ hdist = tinf_getbits_base(d, 5, 1);
+
+ /* Get 4 bits HCLEN (4-19) */
+ hclen = tinf_getbits_base(d, 4, 4);
+
+ /*
+ * The RFC limits the range of HLIT to 286, but lists HDIST as range
+ * 1-32, even though distance codes 30 and 31 have no meaning. While
+ * we could allow the full range of HLIT and HDIST to make it possible
+ * to decode the fixed trees with this function, we consider it an
+ * error here.
+ *
+ * See also: https://github.com/madler/zlib/issues/82
+ */
+ if (hlit > 286 || hdist > 30) {
+ return TINF_DATA_ERROR;
+ }
+
+ for (i = 0; i < 19; ++i) {
+ lengths[i] = 0;
+ }
+
+ /* Read code lengths for code length alphabet */
+ for (i = 0; i < hclen; ++i) {
+ /* Get 3 bits code length (0-7) */
+ unsigned int clen = tinf_getbits(d, 3);
+
+ lengths[clcidx[i]] = clen;
+ }
+
+ /* Build code length tree (in literal/length tree to save space) */
+ res = tinf_build_tree(lt, lengths, 19);
+
+ if (res != TINF_OK) {
+ return res;
+ }
+
+ /* Check code length tree is not empty */
+ if (lt->max_sym == -1) {
+ return TINF_DATA_ERROR;
+ }
+
+ /* Decode code lengths for the dynamic trees */
+ for (num = 0; num < hlit + hdist; ) {
+ int sym = tinf_decode_symbol(d, lt);
+
+ if (sym > lt->max_sym) {
+ return TINF_DATA_ERROR;
+ }
+
+ switch (sym) {
+ case 16:
+ /* Copy previous code length 3-6 times (read 2 bits) */
+ if (num == 0) {
+ return TINF_DATA_ERROR;
+ }
+ sym = lengths[num - 1];
+ length = tinf_getbits_base(d, 2, 3);
+ break;
+ case 17:
+ /* Repeat code length 0 for 3-10 times (read 3 bits) */
+ sym = 0;
+ length = tinf_getbits_base(d, 3, 3);
+ break;
+ case 18:
+ /* Repeat code length 0 for 11-138 times (read 7 bits) */
+ sym = 0;
+ length = tinf_getbits_base(d, 7, 11);
+ break;
+ default:
+ /* Values 0-15 represent the actual code lengths */
+ length = 1;
+ break;
+ }
+
+ if (length > hlit + hdist - num) {
+ return TINF_DATA_ERROR;
+ }
+
+ while (length--) {
+ lengths[num++] = sym;
+ }
+ }
+
+ /* Check EOB symbol is present */
+ if (lengths[256] == 0) {
+ return TINF_DATA_ERROR;
+ }
+
+ /* Build dynamic trees */
+ res = tinf_build_tree(lt, lengths, hlit);
+
+ if (res != TINF_OK) {
+ return res;
+ }
+
+ res = tinf_build_tree(dt, lengths + hlit, hdist);
+
+ if (res != TINF_OK) {
+ return res;
+ }
+
+ return TINF_OK;
+}
+
+/* -- Block inflate functions -- */
+
+/* Given a stream and two trees, inflate a block of data */
+static int tinf_inflate_block_data(struct tinf_data *d, struct tinf_tree *lt,
+ struct tinf_tree *dt)
+{
+ /* Extra bits and base tables for length codes */
+ static const unsigned char length_bits[30] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,
+ 4, 4, 4, 4, 5, 5, 5, 5, 0, 127
+ };
+
+ static const unsigned short length_base[30] = {
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13,
+ 15, 17, 19, 23, 27, 31, 35, 43, 51, 59,
+ 67, 83, 99, 115, 131, 163, 195, 227, 258, 0
+ };
+
+ /* Extra bits and base tables for distance codes */
+ static const unsigned char dist_bits[30] = {
+ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3,
+ 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 9, 9, 10, 10, 11, 11, 12, 12, 13, 13
+ };
+
+ static const unsigned short dist_base[30] = {
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25,
+ 33, 49, 65, 97, 129, 193, 257, 385, 513, 769,
+ 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
+ };
+
+ for (;;) {
+ int sym = tinf_decode_symbol(d, lt);
+
+ /* Check for overflow in bit reader */
+ if (d->overflow) {
+ return TINF_DATA_ERROR;
+ }
+
+ if (sym < 256) {
+ if (d->dest == d->dest_end) {
+ return TINF_BUF_ERROR;
+ }
+ *d->dest++ = sym;
+ }
+ else {
+ int length, dist, offs;
+ int i;
+
+ /* Check for end of block */
+ if (sym == 256) {
+ return TINF_OK;
+ }
+
+ /* Check sym is within range and distance tree is not empty */
+ if (sym > lt->max_sym || sym - 257 > 28 || dt->max_sym == -1) {
+ return TINF_DATA_ERROR;
+ }
+
+ sym -= 257;
+
+ /* Possibly get more bits from length code */
+ length = tinf_getbits_base(d, length_bits[sym],
+ length_base[sym]);
+
+ dist = tinf_decode_symbol(d, dt);
+
+ /* Check dist is within range */
+ if (dist > dt->max_sym || dist > 29) {
+ return TINF_DATA_ERROR;
+ }
+
+ /* Possibly get more bits from distance code */
+ offs = tinf_getbits_base(d, dist_bits[dist],
+ dist_base[dist]);
+
+ if (offs > d->dest - d->dest_start) {
+ return TINF_DATA_ERROR;
+ }
+
+ if (d->dest_end - d->dest < length) {
+ return TINF_BUF_ERROR;
+ }
+
+ /* Copy match */
+ for (i = 0; i < length; ++i) {
+ d->dest[i] = d->dest[i - offs];
+ }
+
+ d->dest += length;
+ }
+ }
+}
+
+/* Inflate an uncompressed block of data */
+static int tinf_inflate_uncompressed_block(struct tinf_data *d)
+{
+ unsigned int length, invlength;
+
+ if (d->source_end && d->source_end - d->source < 4) {
+ return TINF_DATA_ERROR;
+ }
+
+ /* Get length */
+ length = read_le16(d->source);
+
+ /* Get one's complement of length */
+ invlength = read_le16(d->source + 2);
+
+ /* Check length */
+ if (length != (~invlength & 0x0000FFFF)) {
+ return TINF_DATA_ERROR;
+ }
+
+ d->source += 4;
+
+ if (d->source_end && d->source_end - d->source < length) {
+ return TINF_DATA_ERROR;
+ }
+
+ if (d->dest_end - d->dest < length) {
+ return TINF_BUF_ERROR;
+ }
+
+ /* Copy block */
+ while (length--) {
+ *d->dest++ = *d->source++;
+ }
+
+ /* Make sure we start next block on a byte boundary */
+ d->tag = 0;
+ d->bitcount = 0;
+
+ return TINF_OK;
+}
+
+/* Inflate a block of data compressed with fixed Huffman trees */
+static int tinf_inflate_fixed_block(struct tinf_data *d)
+{
+ /* Build fixed Huffman trees */
+ tinf_build_fixed_trees(&d->ltree, &d->dtree);
+
+ /* Decode block using fixed trees */
+ return tinf_inflate_block_data(d, &d->ltree, &d->dtree);
+}
+
+/* Inflate a block of data compressed with dynamic Huffman trees */
+static int tinf_inflate_dynamic_block(struct tinf_data *d)
+{
+ /* Decode trees from stream */
+ int res = tinf_decode_trees(d, &d->ltree, &d->dtree);
+
+ if (res != TINF_OK) {
+ return res;
+ }
+
+ /* Decode block using decoded trees */
+ return tinf_inflate_block_data(d, &d->ltree, &d->dtree);
+}
+
+/* -- Public functions -- */
+
+/* Initialize global (static) data */
+void tinf_init(void)
+{
+ return;
+}
+
+/* Inflate stream from source to dest */
+int tinf_uncompress(void *dest, unsigned int *destLen,
+ const void *source, unsigned int *sourceLen)
+{
+ struct tinf_data d;
+ int bfinal;
+
+ /* Initialise data */
+ d.source = (const unsigned char *) source;
+ if (sourceLen && *sourceLen)
+ d.source_end = d.source + *sourceLen;
+ else
+ d.source_end = 0;
+ d.tag = 0;
+ d.bitcount = 0;
+ d.overflow = 0;
+
+ d.dest = (unsigned char *) dest;
+ d.dest_start = d.dest;
+ d.dest_end = d.dest + *destLen;
+
+ do {
+ unsigned int btype;
+ int res;
+
+ /* Read final block flag */
+ bfinal = tinf_getbits(&d, 1);
+
+ /* Read block type (2 bits) */
+ btype = tinf_getbits(&d, 2);
+
+ /* Decompress block */
+ switch (btype) {
+ case 0:
+ /* Decompress uncompressed block */
+ res = tinf_inflate_uncompressed_block(&d);
+ break;
+ case 1:
+ /* Decompress block with fixed Huffman trees */
+ res = tinf_inflate_fixed_block(&d);
+ break;
+ case 2:
+ /* Decompress block with dynamic Huffman trees */
+ res = tinf_inflate_dynamic_block(&d);
+ break;
+ default:
+ res = TINF_DATA_ERROR;
+ break;
+ }
+
+ if (res != TINF_OK) {
+ return res;
+ }
+ } while (!bfinal);
+
+ /* Check for overflow in bit reader */
+ if (d.overflow) {
+ return TINF_DATA_ERROR;
+ }
+
+ if (sourceLen) {
+ unsigned int slen = d.source - (const unsigned char *)source;
+ if (!*sourceLen)
+ *sourceLen = slen;
+ else if (*sourceLen != slen)
+ return TINF_DATA_ERROR;
+ }
+
+ *destLen = d.dest - d.dest_start;
+ return TINF_OK;
+}
+
+/* clang -g -O1 -fsanitize=fuzzer,address -DTINF_FUZZING tinflate.c */
+#if defined(TINF_FUZZING)
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+unsigned char depacked[64 * 1024];
+
+extern int
+LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ if (size > UINT_MAX / 2) { return 0; }
+ unsigned int destLen = sizeof(depacked);
+ tinf_uncompress(depacked, &destLen, data, size);
+ return 0;
+}
+#endif
diff --git a/tools/src/tinf/tinfzlib.c b/tools/src/tinf/tinfzlib.c
new file mode 100644
index 0000000..6af07b8
--- /dev/null
+++ b/tools/src/tinf/tinfzlib.c
@@ -0,0 +1,99 @@
+/*
+ * tinfzlib - tiny zlib decompressor
+ *
+ * This version of tinfzlib was modified for use with m1n1.
+ *
+ * Copyright (c) 2003-2019 Joergen Ibsen
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must
+ * not claim that you wrote the original software. If you use this
+ * software in a product, an acknowledgment in the product
+ * documentation would be appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must
+ * not be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ */
+
+#include "tinf.h"
+
+static unsigned int read_be32(const unsigned char *p)
+{
+ return ((unsigned int) p[0] << 24)
+ | ((unsigned int) p[1] << 16)
+ | ((unsigned int) p[2] << 8)
+ | ((unsigned int) p[3]);
+}
+
+int tinf_zlib_uncompress(void *dest, unsigned int *destLen,
+ const void *source, unsigned int *sourceLen)
+{
+ const unsigned char *src = (const unsigned char *) source;
+ unsigned char *dst = (unsigned char *) dest;
+ unsigned int a32;
+ int res;
+ unsigned char cmf, flg;
+ unsigned int sourceDataLen = sourceLen ? *sourceLen - 6 : 0;
+
+ /* -- Check header -- */
+
+ /* Check room for at least 2 byte header and 4 byte trailer */
+ if (*sourceLen && *sourceLen < 6) {
+ return TINF_DATA_ERROR;
+ }
+
+ /* Get header bytes */
+ cmf = src[0];
+ flg = src[1];
+
+ /* Check checksum */
+ if ((256 * cmf + flg) % 31) {
+ return TINF_DATA_ERROR;
+ }
+
+ /* Check method is deflate */
+ if ((cmf & 0x0F) != 8) {
+ return TINF_DATA_ERROR;
+ }
+
+ /* Check window size is valid */
+ if ((cmf >> 4) > 7) {
+ return TINF_DATA_ERROR;
+ }
+
+ /* Check there is no preset dictionary */
+ if (flg & 0x20) {
+ return TINF_DATA_ERROR;
+ }
+
+ /* -- Decompress data -- */
+
+ res = tinf_uncompress(dst, destLen, src + 2, &sourceDataLen);
+
+ if (res != TINF_OK) {
+ return TINF_DATA_ERROR;
+ }
+
+ /* -- Check Adler-32 checksum -- */
+
+ a32 = read_be32(&src[sourceDataLen + 2]);
+
+ if (a32 != tinf_adler32(dst, *destLen)) {
+ return TINF_DATA_ERROR;
+ }
+
+ if (sourceLen)
+ *sourceLen = sourceDataLen + 6;
+
+ return TINF_OK;
+}
diff --git a/tools/src/tps6598x.c b/tools/src/tps6598x.c
new file mode 100644
index 0000000..fdb5e11
--- /dev/null
+++ b/tools/src/tps6598x.c
@@ -0,0 +1,172 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "tps6598x.h"
+#include "adt.h"
+#include "i2c.h"
+#include "iodev.h"
+#include "malloc.h"
+#include "types.h"
+#include "utils.h"
+
+#define TPS_REG_CMD1 0x08
+#define TPS_REG_DATA1 0x09
+#define TPS_REG_INT_EVENT1 0x14
+#define TPS_REG_INT_MASK1 0x16
+#define TPS_REG_INT_CLEAR1 0x18
+#define TPS_REG_POWER_STATE 0x20
+#define TPS_CMD_INVALID 0x21434d44 // !CMD
+
+struct tps6598x_dev {
+ i2c_dev_t *i2c;
+ u8 addr;
+};
+
+tps6598x_dev_t *tps6598x_init(const char *adt_node, i2c_dev_t *i2c)
+{
+ int adt_offset;
+ adt_offset = adt_path_offset(adt, adt_node);
+ if (adt_offset < 0) {
+ printf("tps6598x: Error getting %s node\n", adt_node);
+ return NULL;
+ }
+
+ const u8 *iic_addr = adt_getprop(adt, adt_offset, "hpm-iic-addr", NULL);
+ if (iic_addr == NULL) {
+ printf("tps6598x: Error getting %s hpm-iic-addr\n.", adt_node);
+ return NULL;
+ }
+
+ tps6598x_dev_t *dev = malloc(sizeof(*dev));
+ if (!dev)
+ return NULL;
+
+ dev->i2c = i2c;
+ dev->addr = *iic_addr;
+ return dev;
+}
+
+void tps6598x_shutdown(tps6598x_dev_t *dev)
+{
+ free(dev);
+}
+
+int tps6598x_command(tps6598x_dev_t *dev, const char *cmd, const u8 *data_in, size_t len_in,
+ u8 *data_out, size_t len_out)
+{
+ if (len_in) {
+ if (i2c_smbus_write(dev->i2c, dev->addr, TPS_REG_DATA1, data_in, len_in) < 0)
+ return -1;
+ }
+
+ if (i2c_smbus_write(dev->i2c, dev->addr, TPS_REG_CMD1, (const u8 *)cmd, 4) < 0)
+ return -1;
+
+ u32 cmd_status;
+ do {
+ if (i2c_smbus_read32(dev->i2c, dev->addr, TPS_REG_CMD1, &cmd_status))
+ return -1;
+ if (cmd_status == TPS_CMD_INVALID)
+ return -1;
+ udelay(100);
+ } while (cmd_status != 0);
+
+ if (len_out) {
+ if (i2c_smbus_read(dev->i2c, dev->addr, TPS_REG_DATA1, data_out, len_out) !=
+ (ssize_t)len_out)
+ return -1;
+ }
+
+ return 0;
+}
+
+int tps6598x_disable_irqs(tps6598x_dev_t *dev, tps6598x_irq_state_t *state)
+{
+ size_t read;
+ int written;
+ static const u8 zeros[CD3218B12_IRQ_WIDTH] = {0x00};
+ static const u8 ones[CD3218B12_IRQ_WIDTH] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF};
+
+ // store IntEvent 1 to restore it later
+ read = i2c_smbus_read(dev->i2c, dev->addr, TPS_REG_INT_MASK1, state->int_mask1,
+ sizeof(state->int_mask1));
+ if (read != CD3218B12_IRQ_WIDTH) {
+ printf("tps6598x: reading TPS_REG_INT_MASK1 failed\n");
+ return -1;
+ }
+ state->valid = 1;
+
+ // mask interrupts and ack all interrupt flags
+ written = i2c_smbus_write(dev->i2c, dev->addr, TPS_REG_INT_CLEAR1, ones, sizeof(ones));
+ if (written != sizeof(zeros)) {
+ printf("tps6598x: writing TPS_REG_INT_CLEAR1 failed, written: %d\n", written);
+ return -1;
+ }
+ written = i2c_smbus_write(dev->i2c, dev->addr, TPS_REG_INT_MASK1, zeros, sizeof(zeros));
+ if (written != sizeof(ones)) {
+ printf("tps6598x: writing TPS_REG_INT_MASK1 failed, written: %d\n", written);
+ return -1;
+ }
+
+#ifdef DEBUG
+ u8 tmp[CD3218B12_IRQ_WIDTH] = {0x00};
+ read = i2c_smbus_read(dev->i2c, dev->addr, TPS_REG_INT_MASK1, tmp, CD3218B12_IRQ_WIDTH);
+ if (read != CD3218B12_IRQ_WIDTH)
+ printf("tps6598x: failed verifcation, can't read TPS_REG_INT_MASK1\n");
+ else {
+ printf("tps6598x: verify: TPS_REG_INT_MASK1 vs. saved IntMask1\n");
+ hexdump(tmp, sizeof(tmp));
+ hexdump(state->int_mask1, sizeof(state->int_mask1));
+ }
+#endif
+ return 0;
+}
+
+int tps6598x_restore_irqs(tps6598x_dev_t *dev, tps6598x_irq_state_t *state)
+{
+ int written;
+
+ written = i2c_smbus_write(dev->i2c, dev->addr, TPS_REG_INT_MASK1, state->int_mask1,
+ sizeof(state->int_mask1));
+ if (written != sizeof(state->int_mask1)) {
+ printf("tps6598x: restoring TPS_REG_INT_MASK1 failed\n");
+ return -1;
+ }
+
+#ifdef DEBUG
+ int read;
+ u8 tmp[CD3218B12_IRQ_WIDTH];
+ read = i2c_smbus_read(dev->i2c, dev->addr, TPS_REG_INT_MASK1, tmp, sizeof(tmp));
+ if (read != sizeof(tmp))
+ printf("tps6598x: failed verifcation, can't read TPS_REG_INT_MASK1\n");
+ else {
+ printf("tps6598x: verify saved IntMask1 vs. TPS_REG_INT_MASK1:\n");
+ hexdump(state->int_mask1, sizeof(state->int_mask1));
+ hexdump(tmp, sizeof(tmp));
+ }
+#endif
+
+ return 0;
+}
+
+int tps6598x_powerup(tps6598x_dev_t *dev)
+{
+ u8 power_state;
+
+ if (i2c_smbus_read8(dev->i2c, dev->addr, TPS_REG_POWER_STATE, &power_state))
+ return -1;
+
+ if (power_state == 0)
+ return 0;
+
+ const u8 data = 0;
+ tps6598x_command(dev, "SSPS", &data, 1, NULL, 0);
+
+ if (i2c_smbus_read8(dev->i2c, dev->addr, TPS_REG_POWER_STATE, &power_state))
+ return -1;
+
+ if (power_state != 0)
+ return -1;
+
+ return 0;
+}
diff --git a/tools/src/tps6598x.h b/tools/src/tps6598x.h
new file mode 100644
index 0000000..9e6d26a
--- /dev/null
+++ b/tools/src/tps6598x.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef TPS6598X_H
+#define TPS6598X_H
+
+#include "i2c.h"
+#include "types.h"
+
+typedef struct tps6598x_dev tps6598x_dev_t;
+
+tps6598x_dev_t *tps6598x_init(const char *adt_path, i2c_dev_t *i2c);
+void tps6598x_shutdown(tps6598x_dev_t *dev);
+
+int tps6598x_command(tps6598x_dev_t *dev, const char *cmd, const u8 *data_in, size_t len_in,
+ u8 *data_out, size_t len_out);
+int tps6598x_powerup(tps6598x_dev_t *dev);
+
+#define CD3218B12_IRQ_WIDTH 9
+
+typedef struct tps6598x_irq_state {
+ u8 int_mask1[CD3218B12_IRQ_WIDTH];
+ bool valid;
+} tps6598x_irq_state_t;
+
+int tps6598x_disable_irqs(tps6598x_dev_t *dev, tps6598x_irq_state_t *state);
+int tps6598x_restore_irqs(tps6598x_dev_t *dev, tps6598x_irq_state_t *state);
+
+#endif
diff --git a/tools/src/tunables.c b/tools/src/tunables.c
new file mode 100644
index 0000000..ced789e
--- /dev/null
+++ b/tools/src/tunables.c
@@ -0,0 +1,124 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "adt.h"
+#include "tunables.h"
+#include "types.h"
+#include "utils.h"
+
+struct tunable_info {
+ int node_offset;
+ int node_path[8];
+ const u32 *tunable_raw;
+ u32 tunable_len;
+};
+
+static int tunables_adt_find(const char *path, const char *prop, struct tunable_info *info,
+ u32 item_size)
+{
+ info->node_offset = adt_path_offset_trace(adt, path, info->node_path);
+ if (info->node_offset < 0) {
+ printf("tunable: unable to find ADT node %s.\n", path);
+ return -1;
+ }
+
+ info->tunable_raw = adt_getprop(adt, info->node_offset, prop, &info->tunable_len);
+ if (info->tunable_raw == NULL || info->tunable_len == 0) {
+ printf("tunable: Error getting ADT node %s property %s .\n", path, prop);
+ return -1;
+ }
+
+ if (info->tunable_len % item_size) {
+ printf("tunable: tunable length needs to be a multiply of %d but is %d\n", item_size,
+ info->tunable_len);
+ return -1;
+ }
+
+ info->tunable_len /= item_size;
+
+ return 0;
+}
+
+struct tunable_global {
+ u32 reg_idx;
+ u32 offset;
+ u32 mask;
+ u32 value;
+} PACKED;
+
+int tunables_apply_global(const char *path, const char *prop)
+{
+ struct tunable_info info;
+
+ if (tunables_adt_find(path, prop, &info, sizeof(struct tunable_global)) < 0)
+ return -1;
+
+ const struct tunable_global *tunables = (const struct tunable_global *)info.tunable_raw;
+ for (u32 i = 0; i < info.tunable_len; ++i) {
+ const struct tunable_global *tunable = &tunables[i];
+
+ u64 addr;
+ if (adt_get_reg(adt, info.node_path, "reg", tunable->reg_idx, &addr, NULL) < 0) {
+ printf("tunable: Error getting regs with index %d\n", tunable->reg_idx);
+ return -1;
+ }
+
+ mask32(addr + tunable->offset, tunable->mask, tunable->value);
+ }
+
+ return 0;
+}
+
+struct tunable_local {
+ u32 offset;
+ u32 size;
+ u64 mask;
+ u64 value;
+} PACKED;
+
+int tunables_apply_local_addr(const char *path, const char *prop, uintptr_t base)
+{
+ struct tunable_info info;
+
+ if (tunables_adt_find(path, prop, &info, sizeof(struct tunable_local)) < 0)
+ return -1;
+
+ const struct tunable_local *tunables = (const struct tunable_local *)info.tunable_raw;
+ for (u32 i = 0; i < info.tunable_len; ++i) {
+ const struct tunable_local *tunable = &tunables[i];
+
+ switch (tunable->size) {
+ case 1:
+ mask8(base + tunable->offset, tunable->mask, tunable->value);
+ break;
+ case 2:
+ mask16(base + tunable->offset, tunable->mask, tunable->value);
+ break;
+ case 4:
+ mask32(base + tunable->offset, tunable->mask, tunable->value);
+ break;
+ case 8:
+ mask64(base + tunable->offset, tunable->mask, tunable->value);
+ break;
+ default:
+ printf("tunable: unknown tunable size 0x%08x\n", tunable->size);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int tunables_apply_local(const char *path, const char *prop, u32 reg_offset)
+{
+ struct tunable_info info;
+
+ if (tunables_adt_find(path, prop, &info, sizeof(struct tunable_local)) < 0)
+ return -1;
+
+ u64 base;
+ if (adt_get_reg(adt, info.node_path, "reg", reg_offset, &base, NULL) < 0) {
+ printf("tunable: Error getting regs\n");
+ return -1;
+ }
+
+ return tunables_apply_local_addr(path, prop, base);
+}
diff --git a/tools/src/tunables.h b/tools/src/tunables.h
new file mode 100644
index 0000000..cf3091a
--- /dev/null
+++ b/tools/src/tunables.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef TUNABLES_H
+#define TUNABLES_H
+
+#include "types.h"
+
+/*
+ * This function applies the tunables usually passed in the node "tunable".
+ * They usually apply to multiple entries from the "reg" node.
+ *
+ * Example usage for the USB DRD node:
+ * tunables_apply_global("/arm-io/usb-drd0", "tunable");
+ */
+int tunables_apply_global(const char *path, const char *prop);
+
+/*
+ * This function applies the tunables specified in device-specific tunable properties.
+ * These only apply to a single MMIO region from the "reg" node which needs to
+ * be specified.
+ *
+ * Example usage for two tunables from the USB DRD DART node:
+ * tunables_apply_local("/arm-io/dart-usb0", "dart-tunables-instance-0", 0);
+ * tunables_apply_local("/arm-io/dart-usb0", "dart-tunables-instance-1", 1);
+ *
+ */
+int tunables_apply_local(const char *path, const char *prop, u32 reg_idx);
+
+/*
+ * This functions does the same as tunables_apply_local except that it allows
+ * to specify the base address to which the tunables will be applied to instead
+ * of extracting it from the "regs" property.
+ *
+ * Example usage for two tunables for the USB DRD DART node:
+ * tunables_apply_local_addr("/arm-io/dart-usb0", "dart-tunables-instance-0", 0x382f00000);
+ * tunables_apply_local_addr("/arm-io/dart-usb0", "dart-tunables-instance-1", 0x382f80000);
+ */
+int tunables_apply_local_addr(const char *path, const char *prop, uintptr_t base);
+
+int tunables_apply_static(void);
+
+#endif
diff --git a/tools/src/tunables_static.c b/tools/src/tunables_static.c
new file mode 100644
index 0000000..e569e6b
--- /dev/null
+++ b/tools/src/tunables_static.c
@@ -0,0 +1,105 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "tunables.h"
+#include "adt.h"
+#include "pmgr.h"
+#include "soc.h"
+#include "types.h"
+#include "utils.h"
+
+/*
+ * These magic tunable sequences are hardcoded in various places in XNU, and are required for
+ * proper operation of various fabric features and other miscellanea. Without them, things tend
+ * to subtly break...
+ */
+
+struct entry {
+ u32 offset;
+ u32 clear;
+ u32 set;
+};
+
+struct entry t8103_agx_tunables[] = {
+ {0x30, 0xffffffff, 0x50014}, {0x34, 0xffffffff, 0xa003c},
+ {0x400, 0x400103ff, 0x40010001}, {0x600, 0x1ffffff, 0x1ffffff},
+ {0x738, 0x1ff01ff, 0x140034}, {0x798, 0x1ff01ff, 0x14003c},
+ {0x800, 0x100, 0x100}, {-1, 0, 0},
+};
+
+// TODO: check masks
+struct entry t600x_agx_tunables[] = {
+ {0x0, 0x1, 0x1},
+ {0x10, 0xfff0000, 0xd0000},
+ {0x14, 0x3, 0x1},
+ {0x18, 0x3, 0x1},
+ {0x1c, 0x3, 0x3},
+ {0x20, 0x3, 0x3},
+ {0x24, 0x3, 0x3},
+ {0x28, 0x3, 0x3},
+ {0x2c, 0x3, 0x3},
+ {0x400, 0x400103ff, 0x40010001},
+ {0x600, 0x1ffffff, 0x1ffffff},
+ {0x800, 0x100, 0x100},
+ {-1, 0, 0},
+};
+
+// TODO: check masks
+struct entry t8112_agx_tunables[] = {
+ {0x0, 0x200, 0x200},
+ {0x34, 0xffffffff, 0x50014},
+ {0x38, 0xffffffff, 0xa003c},
+ {0x400, 0xc00103ff, 0xc0010001},
+ {0x600, 0x1ffffff, 0x1ffffff},
+ {0x738, 0x1ff01ff, 0x14003c},
+ {0x798, 0x1ff01ff, 0x14003c},
+ {0x800, 0x100, 0x100},
+ {-1, 0, 0},
+};
+
+static void tunables_apply(u64 base, struct entry *entry)
+{
+ while (entry->offset != UINT32_MAX) {
+ mask32(base + entry->offset, entry->clear, entry->set);
+ entry++;
+ }
+}
+
+int power_and_apply(const char *path, u64 base, struct entry *entries)
+{
+ if (pmgr_adt_power_enable(path) < 0) {
+ printf("tunables: Failed to enable power: %s\n", path);
+ return -1;
+ }
+
+ tunables_apply(base, entries);
+
+ if (pmgr_adt_power_disable(path) < 0) {
+ printf("tunables: Failed to disable power: %s\n", path);
+ return -1;
+ }
+
+ return 0;
+}
+
+int tunables_apply_static(void)
+{
+ int ret = 0;
+
+ switch (chip_id) {
+ case T8103:
+ ret |= power_and_apply("/arm-io/sgx", 0x205000000, t8103_agx_tunables);
+ break;
+ case T8112:
+ ret |= power_and_apply("/arm-io/sgx", 0x205000000, t8112_agx_tunables);
+ break;
+ case T6000:
+ case T6001:
+ case T6002:
+ ret |= power_and_apply("/arm-io/sgx", 0x405000000, t600x_agx_tunables);
+ break;
+ default:
+ break;
+ }
+
+ return ret ? -1 : 0;
+}
diff --git a/tools/src/types.h b/tools/src/types.h
new file mode 100644
index 0000000..6fd0789
--- /dev/null
+++ b/tools/src/types.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef TYPES_H
+#define TYPES_H
+
+#ifndef __ASSEMBLER__
+
+#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+
+typedef int8_t s8;
+typedef int16_t s16;
+typedef int32_t s32;
+typedef int64_t s64;
+
+typedef u64 uintptr_t;
+typedef s64 ptrdiff_t;
+
+typedef s64 ssize_t;
+
+#endif
+
+#define UNUSED(x) (void)(x)
+#define ALIGNED(x) __attribute__((aligned(x)))
+#define PACKED __attribute__((packed))
+
+#define STACK_ALIGN(type, name, cnt, alignment) \
+ u8 _al__##name[((sizeof(type) * (cnt)) + (alignment) + \
+ (((sizeof(type) * (cnt)) % (alignment)) > 0 \
+ ? ((alignment) - ((sizeof(type) * (cnt)) % (alignment))) \
+ : 0))]; \
+ type *name = \
+ (type *)(((u32)(_al__##name)) + ((alignment) - (((u32)(_al__##name)) & ((alignment)-1))))
+
+#define HAVE_PTRDIFF_T 1
+#define HAVE_UINTPTR_T 1
+#define UPTRDIFF_T uintptr_t
+
+#define SZ_2K (1 << 11)
+#define SZ_4K (1 << 12)
+#define SZ_16K (1 << 14)
+#define SZ_1M (1 << 20)
+#define SZ_32M (1 << 25)
+
+#ifdef __ASSEMBLER__
+
+#define sys_reg(op0, op1, CRn, CRm, op2) s##op0##_##op1##_c##CRn##_c##CRm##_##op2
+
+#else
+
+#define sys_reg(op0, op1, CRn, CRm, op2) , _S, op0, op1, CRn, CRm, op2
+
+#endif
+
+#endif
diff --git a/tools/src/uart.c b/tools/src/uart.c
new file mode 100644
index 0000000..67aa0e3
--- /dev/null
+++ b/tools/src/uart.c
@@ -0,0 +1,180 @@
+/* SPDX-License-Identifier: MIT */
+
+#include <stdarg.h>
+
+#include "adt.h"
+#include "iodev.h"
+#include "types.h"
+#include "uart.h"
+#include "uart_regs.h"
+#include "utils.h"
+#include "vsprintf.h"
+
+#define UART_CLOCK 24000000
+
+static u64 uart_base = 0;
+
+int uart_init(void)
+{
+ int path[8];
+ int node = adt_path_offset_trace(adt, "/arm-io/uart0", path);
+
+ if (node < 0) {
+ printf("!!! UART node not found!\n");
+ return -1;
+ }
+
+ if (adt_get_reg(adt, path, "reg", 0, &uart_base, NULL)) {
+ printf("!!! Failed to get UART reg property!\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+void uart_putbyte(u8 c)
+{
+ if (!uart_base)
+ return;
+
+ while (!(read32(uart_base + UTRSTAT) & UTRSTAT_TXBE))
+ ;
+
+ write32(uart_base + UTXH, c);
+}
+
+u8 uart_getbyte(void)
+{
+ if (!uart_base)
+ return 0;
+
+ while (!(read32(uart_base + UTRSTAT) & UTRSTAT_RXD))
+ ;
+
+ return read32(uart_base + URXH);
+}
+
+void uart_putchar(u8 c)
+{
+ if (c == '\n')
+ uart_putbyte('\r');
+
+ uart_putbyte(c);
+}
+
+u8 uart_getchar(void)
+{
+ return uart_getbyte();
+}
+
+void uart_puts(const char *s)
+{
+ while (*s)
+ uart_putchar(*(s++));
+
+ uart_putchar('\n');
+}
+
+void uart_write(const void *buf, size_t count)
+{
+ const u8 *p = buf;
+
+ while (count--)
+ uart_putbyte(*p++);
+}
+
+size_t uart_read(void *buf, size_t count)
+{
+ u8 *p = buf;
+ size_t recvd = 0;
+
+ while (count--) {
+ *p++ = uart_getbyte();
+ recvd++;
+ }
+
+ return recvd;
+}
+
+void uart_setbaud(int baudrate)
+{
+ if (!uart_base)
+ return;
+
+ uart_flush();
+ write32(uart_base + UBRDIV, ((UART_CLOCK / baudrate + 7) / 16) - 1);
+}
+
+void uart_flush(void)
+{
+ if (!uart_base)
+ return;
+
+ while (!(read32(uart_base + UTRSTAT) & UTRSTAT_TXE))
+ ;
+}
+
+void uart_clear_irqs(void)
+{
+ if (!uart_base)
+ return;
+
+ write32(uart_base + UTRSTAT, UTRSTAT_TXTHRESH | UTRSTAT_RXTHRESH | UTRSTAT_RXTO);
+}
+
+int uart_printf(const char *fmt, ...)
+{
+ va_list args;
+ char buffer[512];
+ int i;
+
+ va_start(args, fmt);
+ i = vsnprintf(buffer, sizeof(buffer), fmt, args);
+ va_end(args);
+
+ uart_write(buffer, min(i, (int)(sizeof(buffer) - 1)));
+
+ return i;
+}
+
+static bool uart_iodev_can_write(void *opaque)
+{
+ UNUSED(opaque);
+ return true;
+}
+
+static ssize_t uart_iodev_can_read(void *opaque)
+{
+ UNUSED(opaque);
+
+ if (!uart_base)
+ return 0;
+
+ return (read32(uart_base + UTRSTAT) & UTRSTAT_RXD) ? 1 : 0;
+}
+
+static ssize_t uart_iodev_read(void *opaque, void *buf, size_t len)
+{
+ UNUSED(opaque);
+ return uart_read(buf, len);
+}
+
+static ssize_t uart_iodev_write(void *opaque, const void *buf, size_t len)
+{
+ UNUSED(opaque);
+ uart_write(buf, len);
+ return len;
+}
+
+static struct iodev_ops iodev_uart_ops = {
+ .can_read = uart_iodev_can_read,
+ .can_write = uart_iodev_can_write,
+ .read = uart_iodev_read,
+ .write = uart_iodev_write,
+};
+
+struct iodev iodev_uart = {
+ .ops = &iodev_uart_ops,
+ .usage = USAGE_CONSOLE | USAGE_UARTPROXY,
+ .lock = SPINLOCK_INIT,
+};
diff --git a/tools/src/uart.h b/tools/src/uart.h
new file mode 100644
index 0000000..0b03f2d
--- /dev/null
+++ b/tools/src/uart.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef UART_H
+#define UART_H
+
+#include "types.h"
+
+int uart_init(void);
+
+void uart_putbyte(u8 c);
+u8 uart_getbyte(void);
+
+void uart_putchar(u8 c);
+u8 uart_getchar(void);
+
+void uart_write(const void *buf, size_t count);
+size_t uart_read(void *buf, size_t count);
+
+void uart_puts(const char *s);
+
+void uart_setbaud(int baudrate);
+
+void uart_flush(void);
+
+void uart_clear_irqs(void);
+
+int uart_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
+
+#endif
diff --git a/tools/src/uart_regs.h b/tools/src/uart_regs.h
new file mode 100644
index 0000000..bca1fe4
--- /dev/null
+++ b/tools/src/uart_regs.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: MIT */
+
+#define ULCON 0x000
+#define UCON 0x004
+#define UFCON 0x008
+#define UTRSTAT 0x010
+#define UFSTAT 0x018
+#define UTXH 0x020
+#define URXH 0x024
+#define UBRDIV 0x028
+#define UFRACVAL 0x02c
+
+#define UCON_TXTHRESH_ENA BIT(13)
+#define UCON_RXTHRESH_ENA BIT(12)
+#define UCON_RXTO_ENA BIT(9)
+#define UCON_TXMODE GENMASK(3, 2)
+#define UCON_RXMODE GENMASK(1, 0)
+
+#define UCON_MODE_OFF 0
+#define UCON_MODE_IRQ 1
+
+#define UTRSTAT_RXTO BIT(9)
+#define UTRSTAT_TXTHRESH BIT(5)
+#define UTRSTAT_RXTHRESH BIT(4)
+#define UTRSTAT_TXE BIT(2)
+#define UTRSTAT_TXBE BIT(1)
+#define UTRSTAT_RXD BIT(0)
+
+#define UFSTAT_TXFULL BIT(9)
+#define UFSTAT_RXFULL BIT(8)
+#define UFSTAT_TXCNT GENMASK(7, 4)
+#define UFSTAT_RXCNT GENMASK(3, 0)
diff --git a/tools/src/uartproxy.c b/tools/src/uartproxy.c
new file mode 100644
index 0000000..fed9cc5
--- /dev/null
+++ b/tools/src/uartproxy.c
@@ -0,0 +1,317 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "uartproxy.h"
+#include "assert.h"
+#include "exception.h"
+#include "iodev.h"
+#include "proxy.h"
+#include "string.h"
+#include "types.h"
+#include "utils.h"
+
+#define REQ_SIZE 64
+
+typedef struct {
+ u32 _pad;
+ u32 type;
+ union {
+ ProxyRequest prequest;
+ struct {
+ u64 addr;
+ u64 size;
+ u32 dchecksum;
+ } mrequest;
+ u64 features;
+ };
+ u32 checksum;
+} UartRequest;
+
+#define REPLY_SIZE 36
+
+typedef struct {
+ u32 type;
+ s32 status;
+ union {
+ ProxyReply preply;
+ struct {
+ u32 dchecksum;
+ } mreply;
+ struct uartproxy_msg_start start;
+ u64 features;
+ };
+ u32 checksum;
+ u32 _dummy; // Not transferred
+} UartReply;
+
+typedef struct {
+ u32 type;
+ u16 len;
+ u16 event_type;
+} UartEventHdr;
+
+static_assert(sizeof(UartReply) == (REPLY_SIZE + 4), "Invalid UartReply size");
+
+#define REQ_NOP 0x00AA55FF
+#define REQ_PROXY 0x01AA55FF
+#define REQ_MEMREAD 0x02AA55FF
+#define REQ_MEMWRITE 0x03AA55FF
+#define REQ_BOOT 0x04AA55FF
+#define REQ_EVENT 0x05AA55FF
+
+#define ST_OK 0
+#define ST_BADCMD -1
+#define ST_INVAL -2
+#define ST_XFRERR -3
+#define ST_CSUMERR -4
+
+#define PROXY_FEAT_DISABLE_DATA_CSUMS 0x01
+#define PROXY_FEAT_ALL (PROXY_FEAT_DISABLE_DATA_CSUMS)
+
+static u32 iodev_proxy_buffer[IODEV_MAX];
+
+#define CHECKSUM_INIT 0xDEADBEEF
+#define CHECKSUM_FINAL 0xADDEDBAD
+#define CHECKSUM_SENTINEL 0xD0DECADE
+#define DATA_END_SENTINEL 0xB0CACC10
+
+static bool disable_data_csums = false;
+
+// I just totally pulled this out of my arse
+// Noinline so that this can be bailed out by exc_guard = EXC_RETURN
+// We assume this function does not use the stack
+static u32 __attribute__((noinline)) checksum_block(void *start, u32 length, u32 init)
+{
+ u32 sum = init;
+ u8 *d = (u8 *)start;
+
+ while (length--) {
+ sum *= 31337;
+ sum += (*d++) ^ 0x5A;
+ }
+ return sum;
+}
+
+static inline u32 checksum_start(void *start, u32 length)
+{
+ return checksum_block(start, length, CHECKSUM_INIT);
+}
+
+static inline u32 checksum_add(void *start, u32 length, u32 sum)
+{
+ return checksum_block(start, length, sum);
+}
+
+static inline u32 checksum_finish(u32 sum)
+{
+ return sum ^ CHECKSUM_FINAL;
+}
+
+static inline u32 checksum(void *start, u32 length)
+{
+ return checksum_finish(checksum_start(start, length));
+}
+
+static u64 data_checksum(void *start, u32 length)
+{
+ if (disable_data_csums) {
+ return CHECKSUM_SENTINEL;
+ }
+
+ return checksum(start, length);
+}
+
+iodev_id_t uartproxy_iodev;
+
+int uartproxy_run(struct uartproxy_msg_start *start)
+{
+ int ret;
+ int running = 1;
+ size_t bytes;
+ u64 checksum_val;
+ u64 enabled_features = 0;
+
+ iodev_id_t iodev = IODEV_MAX;
+
+ UartRequest request;
+ UartReply reply = {REQ_BOOT};
+ if (!start) {
+ // Startup notification only goes out via UART
+ reply.checksum = checksum(&reply, REPLY_SIZE - 4);
+ iodev_write(IODEV_UART, &reply, REPLY_SIZE);
+ } else {
+ // Exceptions / hooks keep the current iodev
+ iodev = uartproxy_iodev;
+ reply.start = *start;
+ reply.checksum = checksum(&reply, REPLY_SIZE - 4);
+ iodev_write(iodev, &reply, REPLY_SIZE);
+ }
+
+ while (running) {
+ if (!start) {
+ // Look for commands from any iodev on startup
+ for (iodev = 0; iodev < IODEV_MAX;) {
+ u8 b;
+ if ((iodev_get_usage(iodev) & USAGE_UARTPROXY)) {
+ iodev_handle_events(iodev);
+ if (iodev_can_read(iodev) && iodev_read(iodev, &b, 1) == 1) {
+ iodev_proxy_buffer[iodev] >>= 8;
+ iodev_proxy_buffer[iodev] |= b << 24;
+ if ((iodev_proxy_buffer[iodev] & 0xffffff) == 0xAA55FF)
+ break;
+ }
+ }
+ iodev++;
+ if (iodev == IODEV_MAX)
+ iodev = 0;
+ }
+ } else {
+ // Stick to the current iodev for exceptions
+ do {
+ u8 b;
+ iodev_handle_events(iodev);
+ if (iodev_read(iodev, &b, 1) != 1) {
+ printf("Proxy: iodev read failed, exiting.\n");
+ return -1;
+ }
+ iodev_proxy_buffer[iodev] >>= 8;
+ iodev_proxy_buffer[iodev] |= b << 24;
+ } while ((iodev_proxy_buffer[iodev] & 0xffffff) != 0xAA55FF);
+ }
+
+ memset(&request, 0, sizeof(request));
+ request.type = iodev_proxy_buffer[iodev];
+ bytes = iodev_read(iodev, (&request.type) + 1, REQ_SIZE - 4);
+ if (bytes != REQ_SIZE - 4)
+ continue;
+
+ if (checksum(&(request.type), REQ_SIZE - 4) != request.checksum) {
+ memset(&reply, 0, sizeof(reply));
+ reply.type = request.type;
+ reply.status = ST_CSUMERR;
+ reply.checksum = checksum(&reply, REPLY_SIZE - 4);
+ iodev_write(iodev, &reply, REPLY_SIZE);
+ continue;
+ }
+
+ memset(&reply, 0, sizeof(reply));
+ reply.type = request.type;
+ reply.status = ST_OK;
+
+ uartproxy_iodev = iodev;
+
+ switch (request.type) {
+ case REQ_NOP:
+ enabled_features = request.features & PROXY_FEAT_ALL;
+ if (iodev == IODEV_UART) {
+ // Don't allow disabling checksums on UART
+ enabled_features &= ~PROXY_FEAT_DISABLE_DATA_CSUMS;
+ }
+
+ disable_data_csums = enabled_features & PROXY_FEAT_DISABLE_DATA_CSUMS;
+ reply.features = enabled_features;
+ break;
+ case REQ_PROXY:
+ ret = proxy_process(&request.prequest, &reply.preply);
+ if (ret != 0)
+ running = 0;
+ if (ret < 0)
+ printf("Proxy req error: %d\n", ret);
+ break;
+ case REQ_MEMREAD:
+ if (request.mrequest.size == 0)
+ break;
+ exc_count = 0;
+ exc_guard = GUARD_RETURN;
+ checksum_val = data_checksum((void *)request.mrequest.addr, request.mrequest.size);
+ exc_guard = GUARD_OFF;
+ if (exc_count)
+ reply.status = ST_XFRERR;
+ reply.mreply.dchecksum = checksum_val;
+ break;
+ case REQ_MEMWRITE:
+ exc_count = 0;
+ exc_guard = GUARD_SKIP;
+ if (request.mrequest.size != 0) {
+ // Probe for exception guard
+ // We can't do the whole buffer easily, because we'd drop UART data
+ write8(request.mrequest.addr, 0);
+ write8(request.mrequest.addr + request.mrequest.size - 1, 0);
+ }
+ exc_guard = GUARD_OFF;
+ if (exc_count) {
+ reply.status = ST_XFRERR;
+ break;
+ }
+ bytes = iodev_read(iodev, (void *)request.mrequest.addr, request.mrequest.size);
+ if (bytes != request.mrequest.size) {
+ reply.status = ST_XFRERR;
+ break;
+ }
+ checksum_val = data_checksum((void *)request.mrequest.addr, request.mrequest.size);
+ reply.mreply.dchecksum = checksum_val;
+ if (reply.mreply.dchecksum != request.mrequest.dchecksum) {
+ reply.status = ST_XFRERR;
+ break;
+ }
+ if (disable_data_csums) {
+ // Check the sentinel that should be present after the data
+ u32 sentinel = 0;
+ bytes = iodev_read(iodev, &sentinel, sizeof(sentinel));
+ if (bytes != sizeof(sentinel) || sentinel != DATA_END_SENTINEL) {
+ reply.status = ST_XFRERR;
+ break;
+ }
+ }
+ break;
+ default:
+ reply.status = ST_BADCMD;
+ break;
+ }
+ sysop("dsb sy");
+ sysop("isb");
+ reply.checksum = checksum(&reply, REPLY_SIZE - 4);
+ iodev_lock(uartproxy_iodev);
+ iodev_queue(iodev, &reply, REPLY_SIZE);
+
+ if ((request.type == REQ_MEMREAD) && (reply.status == ST_OK)) {
+ iodev_queue(iodev, (void *)request.mrequest.addr, request.mrequest.size);
+
+ if (disable_data_csums) {
+ // Since there is no checksum, put a sentinel after the data so the receiver
+ // can check that no packets were lost.
+ u32 sentinel = DATA_END_SENTINEL;
+
+ iodev_queue(iodev, &sentinel, sizeof(sentinel));
+ }
+ }
+
+ iodev_unlock(uartproxy_iodev);
+ // Flush all queued data
+ iodev_write(iodev, NULL, 0);
+ iodev_flush(iodev);
+ }
+
+ return ret;
+}
+
+void uartproxy_send_event(u16 event_type, void *data, u16 length)
+{
+ UartEventHdr hdr;
+ u32 csum;
+
+ hdr.type = REQ_EVENT;
+ hdr.len = length;
+ hdr.event_type = event_type;
+
+ if (disable_data_csums) {
+ csum = CHECKSUM_SENTINEL;
+ } else {
+ csum = checksum_start(&hdr, sizeof(UartEventHdr));
+ csum = checksum_finish(checksum_add(data, length, csum));
+ }
+ iodev_lock(uartproxy_iodev);
+ iodev_queue(uartproxy_iodev, &hdr, sizeof(UartEventHdr));
+ iodev_queue(uartproxy_iodev, data, length);
+ iodev_write(uartproxy_iodev, &csum, sizeof(csum));
+ iodev_unlock(uartproxy_iodev);
+}
diff --git a/tools/src/uartproxy.h b/tools/src/uartproxy.h
new file mode 100644
index 0000000..23ddd67
--- /dev/null
+++ b/tools/src/uartproxy.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef __UARTPROXY_H__
+#define __UARTPROXY_H__
+
+#include "iodev.h"
+
+extern iodev_id_t uartproxy_iodev;
+
+typedef enum _uartproxy_start_reason_t {
+ START_BOOT,
+ START_EXCEPTION,
+ START_EXCEPTION_LOWER,
+ START_HV,
+} uartproxy_boot_reason_t;
+
+typedef enum _uartproxy_exc_code_t {
+ EXC_SYNC,
+ EXC_IRQ,
+ EXC_FIQ,
+ EXC_SERROR,
+} uartproxy_exc_code_t;
+
+typedef enum _uartproxy_exc_ret_t {
+ EXC_RET_UNHANDLED = 1,
+ EXC_RET_HANDLED = 2,
+ EXC_EXIT_GUEST = 3,
+} uartproxy_exc_ret_t;
+
+typedef enum _uartproxy_event_type_t {
+ EVT_MMIOTRACE = 1,
+ EVT_IRQTRACE = 2,
+} uartproxy_event_type_t;
+
+struct uartproxy_msg_start {
+ u32 reason;
+ u32 code;
+ void *info;
+ void *reserved;
+};
+
+int uartproxy_run(struct uartproxy_msg_start *start);
+void uartproxy_send_event(u16 event_type, void *data, u16 length);
+
+#endif
diff --git a/tools/src/usb.c b/tools/src/usb.c
new file mode 100644
index 0000000..1f516a1
--- /dev/null
+++ b/tools/src/usb.c
@@ -0,0 +1,343 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "usb.h"
+#include "adt.h"
+#include "dart.h"
+#include "i2c.h"
+#include "iodev.h"
+#include "malloc.h"
+#include "pmgr.h"
+#include "tps6598x.h"
+#include "types.h"
+#include "usb_dwc3.h"
+#include "usb_dwc3_regs.h"
+#include "utils.h"
+#include "vsprintf.h"
+
+struct usb_drd_regs {
+ uintptr_t drd_regs;
+ uintptr_t drd_regs_unk3;
+ uintptr_t atc;
+};
+
+#if USB_IODEV_COUNT > 100
+#error "USB_IODEV_COUNT is limited to 100 to prevent overflow in ADT path names"
+#endif
+
+// length of the format string is is used as buffer size
+// limits the USB instance numbers to reasonable 2 digits
+#define FMT_DART_PATH "/arm-io/dart-usb%u"
+#define FMT_DART_MAPPER_PATH "/arm-io/dart-usb%u/mapper-usb%u"
+#define FMT_ATC_PATH "/arm-io/atc-phy%u"
+#define FMT_DRD_PATH "/arm-io/usb-drd%u"
+#define FMT_HPM_PATH "/arm-io/i2c0/hpmBusManager/hpm%u"
+
+static tps6598x_irq_state_t tps6598x_irq_state[USB_IODEV_COUNT];
+static bool usb_is_initialized = false;
+
+static dart_dev_t *usb_dart_init(u32 idx)
+{
+ int mapper_offset;
+ char path[sizeof(FMT_DART_MAPPER_PATH)];
+
+ snprintf(path, sizeof(path), FMT_DART_MAPPER_PATH, idx, idx);
+ mapper_offset = adt_path_offset(adt, path);
+ if (mapper_offset < 0) {
+ // Device not present
+ return NULL;
+ }
+
+ u32 dart_idx;
+ if (ADT_GETPROP(adt, mapper_offset, "reg", &dart_idx) < 0) {
+ printf("usb: Error getting DART %s device index/\n", path);
+ return NULL;
+ }
+
+ snprintf(path, sizeof(path), FMT_DART_PATH, idx);
+ return dart_init_adt(path, 1, dart_idx, false);
+}
+
+static int usb_drd_get_regs(u32 idx, struct usb_drd_regs *regs)
+{
+ int adt_drd_path[8];
+ int adt_drd_offset;
+ int adt_phy_path[8];
+ int adt_phy_offset;
+ char phy_path[sizeof(FMT_ATC_PATH)];
+ char drd_path[sizeof(FMT_DRD_PATH)];
+
+ snprintf(drd_path, sizeof(drd_path), FMT_DRD_PATH, idx);
+ adt_drd_offset = adt_path_offset_trace(adt, drd_path, adt_drd_path);
+ if (adt_drd_offset < 0) {
+ // Nonexistent device
+ return -1;
+ }
+
+ snprintf(phy_path, sizeof(phy_path), FMT_ATC_PATH, idx);
+ adt_phy_offset = adt_path_offset_trace(adt, phy_path, adt_phy_path);
+ if (adt_phy_offset < 0) {
+ printf("usb: Error getting phy node %s\n", phy_path);
+ return -1;
+ }
+
+ if (adt_get_reg(adt, adt_phy_path, "reg", 0, &regs->atc, NULL) < 0) {
+ printf("usb: Error getting reg with index 0 for %s.\n", phy_path);
+ return -1;
+ }
+ if (adt_get_reg(adt, adt_drd_path, "reg", 0, &regs->drd_regs, NULL) < 0) {
+ printf("usb: Error getting reg with index 0 for %s.\n", drd_path);
+ return -1;
+ }
+ if (adt_get_reg(adt, adt_drd_path, "reg", 3, &regs->drd_regs_unk3, NULL) < 0) {
+ printf("usb: Error getting reg with index 3 for %s.\n", drd_path);
+ return -1;
+ }
+
+ return 0;
+}
+
+int usb_phy_bringup(u32 idx)
+{
+ char path[24];
+
+ if (idx >= USB_IODEV_COUNT)
+ return -1;
+
+ struct usb_drd_regs usb_regs;
+ if (usb_drd_get_regs(idx, &usb_regs) < 0)
+ return -1;
+
+ snprintf(path, sizeof(path), FMT_ATC_PATH, idx);
+ if (pmgr_adt_power_enable(path) < 0)
+ return -1;
+
+ snprintf(path, sizeof(path), FMT_DART_PATH, idx);
+ if (pmgr_adt_power_enable(path) < 0)
+ return -1;
+
+ snprintf(path, sizeof(path), FMT_DRD_PATH, idx);
+ if (pmgr_adt_power_enable(path) < 0)
+ return -1;
+
+ write32(usb_regs.atc + 0x08, 0x01c1000f);
+ write32(usb_regs.atc + 0x04, 0x00000003);
+ write32(usb_regs.atc + 0x04, 0x00000000);
+ write32(usb_regs.atc + 0x1c, 0x008c0813);
+ write32(usb_regs.atc + 0x00, 0x00000002);
+
+ write32(usb_regs.drd_regs_unk3 + 0x0c, 0x00000002);
+ write32(usb_regs.drd_regs_unk3 + 0x0c, 0x00000022);
+ write32(usb_regs.drd_regs_unk3 + 0x1c, 0x00000021);
+ write32(usb_regs.drd_regs_unk3 + 0x20, 0x00009332);
+
+ return 0;
+}
+
+dwc3_dev_t *usb_iodev_bringup(u32 idx)
+{
+ dart_dev_t *usb_dart = usb_dart_init(idx);
+ if (!usb_dart)
+ return NULL;
+
+ struct usb_drd_regs usb_reg;
+ if (usb_drd_get_regs(idx, &usb_reg) < 0)
+ return NULL;
+
+ return usb_dwc3_init(usb_reg.drd_regs, usb_dart);
+}
+
+#define USB_IODEV_WRAPPER(name, pipe) \
+ static ssize_t usb_##name##_can_read(void *dev) \
+ { \
+ return usb_dwc3_can_read(dev, pipe); \
+ } \
+ \
+ static bool usb_##name##_can_write(void *dev) \
+ { \
+ return usb_dwc3_can_write(dev, pipe); \
+ } \
+ \
+ static ssize_t usb_##name##_read(void *dev, void *buf, size_t count) \
+ { \
+ return usb_dwc3_read(dev, pipe, buf, count); \
+ } \
+ \
+ static ssize_t usb_##name##_write(void *dev, const void *buf, size_t count) \
+ { \
+ return usb_dwc3_write(dev, pipe, buf, count); \
+ } \
+ \
+ static ssize_t usb_##name##_queue(void *dev, const void *buf, size_t count) \
+ { \
+ return usb_dwc3_queue(dev, pipe, buf, count); \
+ } \
+ \
+ static void usb_##name##_handle_events(void *dev) \
+ { \
+ usb_dwc3_handle_events(dev); \
+ } \
+ \
+ static void usb_##name##_flush(void *dev) \
+ { \
+ usb_dwc3_flush(dev, pipe); \
+ }
+
+USB_IODEV_WRAPPER(0, CDC_ACM_PIPE_0)
+USB_IODEV_WRAPPER(1, CDC_ACM_PIPE_1)
+
+static struct iodev_ops iodev_usb_ops = {
+ .can_read = usb_0_can_read,
+ .can_write = usb_0_can_write,
+ .read = usb_0_read,
+ .write = usb_0_write,
+ .queue = usb_0_queue,
+ .flush = usb_0_flush,
+ .handle_events = usb_0_handle_events,
+};
+
+static struct iodev_ops iodev_usb_sec_ops = {
+ .can_read = usb_1_can_read,
+ .can_write = usb_1_can_write,
+ .read = usb_1_read,
+ .write = usb_1_write,
+ .queue = usb_1_queue,
+ .flush = usb_1_flush,
+ .handle_events = usb_1_handle_events,
+};
+
+struct iodev iodev_usb_vuart = {
+ .ops = &iodev_usb_sec_ops,
+ .usage = 0,
+ .lock = SPINLOCK_INIT,
+};
+
+static tps6598x_dev_t *hpm_init(i2c_dev_t *i2c, const char *hpm_path)
+{
+ tps6598x_dev_t *tps = tps6598x_init(hpm_path, i2c);
+ if (!tps) {
+ printf("usb: tps6598x_init failed for %s.\n", hpm_path);
+ return NULL;
+ }
+
+ if (tps6598x_powerup(tps) < 0) {
+ printf("usb: tps6598x_powerup failed for %s.\n", hpm_path);
+ tps6598x_shutdown(tps);
+ return NULL;
+ }
+
+ return tps;
+}
+
+void usb_init(void)
+{
+ char hpm_path[sizeof(FMT_HPM_PATH)];
+
+ if (usb_is_initialized)
+ return;
+
+ i2c_dev_t *i2c = i2c_init("/arm-io/i2c0");
+ if (!i2c) {
+ printf("usb: i2c init failed.\n");
+ return;
+ }
+
+ for (u32 idx = 0; idx < USB_IODEV_COUNT; ++idx) {
+ snprintf(hpm_path, sizeof(hpm_path), FMT_HPM_PATH, idx);
+ if (adt_path_offset(adt, hpm_path) < 0)
+ continue; // device not present
+ tps6598x_dev_t *tps = hpm_init(i2c, hpm_path);
+ if (!tps) {
+ printf("usb: failed to init hpm%d\n", idx);
+ continue;
+ }
+
+ if (tps6598x_disable_irqs(tps, &tps6598x_irq_state[idx]))
+ printf("usb: unable to disable IRQ masks for hpm%d\n", idx);
+
+ tps6598x_shutdown(tps);
+ }
+
+ i2c_shutdown(i2c);
+
+ for (int idx = 0; idx < USB_IODEV_COUNT; ++idx)
+ usb_phy_bringup(idx); /* Fails on missing devices, just continue */
+
+ usb_is_initialized = true;
+}
+
+void usb_hpm_restore_irqs(bool force)
+{
+ char hpm_path[sizeof(FMT_HPM_PATH)];
+
+ i2c_dev_t *i2c = i2c_init("/arm-io/i2c0");
+ if (!i2c) {
+ printf("usb: i2c init failed.\n");
+ return;
+ }
+
+ for (u32 idx = 0; idx < USB_IODEV_COUNT; ++idx) {
+ if (iodev_get_usage(IODEV_USB0 + idx) && !force)
+ continue;
+
+ if (tps6598x_irq_state[idx].valid) {
+ snprintf(hpm_path, sizeof(hpm_path), FMT_HPM_PATH, idx);
+ if (adt_path_offset(adt, hpm_path) < 0)
+ continue; // device not present
+ tps6598x_dev_t *tps = hpm_init(i2c, hpm_path);
+ if (!tps)
+ continue;
+
+ if (tps6598x_restore_irqs(tps, &tps6598x_irq_state[idx]))
+ printf("usb: unable to restore IRQ masks for hpm%d\n", idx);
+
+ tps6598x_shutdown(tps);
+ }
+ }
+
+ i2c_shutdown(i2c);
+}
+
+void usb_iodev_init(void)
+{
+ for (int i = 0; i < USB_IODEV_COUNT; i++) {
+ dwc3_dev_t *opaque;
+ struct iodev *usb_iodev;
+
+ opaque = usb_iodev_bringup(i);
+ if (!opaque)
+ continue;
+
+ usb_iodev = memalign(SPINLOCK_ALIGN, sizeof(*usb_iodev));
+ if (!usb_iodev)
+ continue;
+
+ usb_iodev->ops = &iodev_usb_ops;
+ usb_iodev->opaque = opaque;
+ usb_iodev->usage = USAGE_CONSOLE | USAGE_UARTPROXY;
+ spin_init(&usb_iodev->lock);
+
+ iodev_register_device(IODEV_USB0 + i, usb_iodev);
+ printf("USB%d: initialized at %p\n", i, opaque);
+ }
+}
+
+void usb_iodev_shutdown(void)
+{
+ for (int i = 0; i < USB_IODEV_COUNT; i++) {
+ struct iodev *usb_iodev = iodev_unregister_device(IODEV_USB0 + i);
+ if (!usb_iodev)
+ continue;
+
+ printf("USB%d: shutdown\n", i);
+ usb_dwc3_shutdown(usb_iodev->opaque);
+ free(usb_iodev);
+ }
+}
+
+void usb_iodev_vuart_setup(iodev_id_t iodev)
+{
+ if (iodev < IODEV_USB0 || iodev >= IODEV_USB0 + USB_IODEV_COUNT)
+ return;
+
+ iodev_usb_vuart.opaque = iodev_get_opaque(iodev);
+}
diff --git a/tools/src/usb.h b/tools/src/usb.h
new file mode 100644
index 0000000..1ba859a
--- /dev/null
+++ b/tools/src/usb.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef USB_H
+#define USB_H
+
+#include "iodev.h"
+#include "types.h"
+#include "usb_dwc3.h"
+
+dwc3_dev_t *usb_bringup(u32 idx);
+
+void usb_init(void);
+void usb_hpm_restore_irqs(bool force);
+void usb_iodev_init(void);
+void usb_iodev_shutdown(void);
+void usb_iodev_vuart_setup(iodev_id_t iodev);
+
+#endif
diff --git a/tools/src/usb_dwc3.c b/tools/src/usb_dwc3.c
new file mode 100644
index 0000000..de05c95
--- /dev/null
+++ b/tools/src/usb_dwc3.c
@@ -0,0 +1,1416 @@
+/* SPDX-License-Identifier: MIT */
+
+/*
+ * Useful references:
+ * - TI KeyStone II Architecture Universal Serial Bus 3.0 (USB 3.0) User's Guide
+ * Literature Number: SPRUHJ7A, https://www.ti.com/lit/ug/spruhj7a/spruhj7a.pdf
+ * - https://www.beyondlogic.org/usbnutshell/usb1.shtml
+ */
+
+#include "../build/build_tag.h"
+
+#include "usb_dwc3.h"
+#include "dart.h"
+#include "malloc.h"
+#include "memory.h"
+#include "ringbuffer.h"
+#include "string.h"
+#include "types.h"
+#include "usb_dwc3_regs.h"
+#include "usb_types.h"
+#include "utils.h"
+
+#define MAX_ENDPOINTS 16
+#define CDC_BUFFER_SIZE SZ_1M
+
+#define usb_debug_printf(fmt, ...) debug_printf("usb-dwc3@%lx: " fmt, dev->regs, ##__VA_ARGS__)
+
+#define STRING_DESCRIPTOR_LANGUAGES 0
+#define STRING_DESCRIPTOR_MANUFACTURER 1
+#define STRING_DESCRIPTOR_PRODUCT 2
+#define STRING_DESCRIPTOR_SERIAL 3
+
+#define CDC_DEVICE_CLASS 0x02
+
+#define CDC_USB_VID 0x1209
+#define CDC_USB_PID 0x316d
+
+#define CDC_INTERFACE_CLASS 0x02
+#define CDC_INTERFACE_CLASS_DATA 0x0a
+#define CDC_INTERFACE_SUBCLASS_ACM 0x02
+#define CDC_INTERFACE_PROTOCOL_NONE 0x00
+#define CDC_INTERFACE_PROTOCOL_AT 0x01
+
+#define DWC3_SCRATCHPAD_SIZE SZ_16K
+#define TRB_BUFFER_SIZE SZ_16K
+#define XFER_BUFFER_SIZE (SZ_16K * MAX_ENDPOINTS * 2)
+#define PAD_BUFFER_SIZE SZ_16K
+
+#define TRBS_PER_EP (TRB_BUFFER_SIZE / (MAX_ENDPOINTS * sizeof(struct dwc3_trb)))
+#define XFER_BUFFER_BYTES_PER_EP (XFER_BUFFER_SIZE / MAX_ENDPOINTS)
+
+#define XFER_SIZE SZ_16K
+
+#define SCRATCHPAD_IOVA 0xbeef0000
+#define EVENT_BUFFER_IOVA 0xdead0000
+#define XFER_BUFFER_IOVA 0xbabe0000
+#define TRB_BUFFER_IOVA 0xf00d0000
+
+/* these map to the control endpoint 0x00/0x80 */
+#define USB_LEP_CTRL_OUT 0
+#define USB_LEP_CTRL_IN 1
+
+/* maps to interrupt endpoint 0x81 */
+#define USB_LEP_CDC_INTR_IN 3
+
+/* these map to physical endpoints 0x02 and 0x82 */
+#define USB_LEP_CDC_BULK_OUT 4
+#define USB_LEP_CDC_BULK_IN 5
+
+/* maps to interrupt endpoint 0x83 */
+#define USB_LEP_CDC_INTR_IN_2 7
+
+/* these map to physical endpoints 0x04 and 0x84 */
+#define USB_LEP_CDC_BULK_OUT_2 8
+#define USB_LEP_CDC_BULK_IN_2 9
+
+/* content doesn't matter at all, this is the setting linux writes by default */
+static const u8 cdc_default_line_coding[] = {0x80, 0x25, 0x00, 0x00, 0x00, 0x00, 0x08};
+
+enum ep0_state {
+ USB_DWC3_EP0_STATE_IDLE,
+ USB_DWC3_EP0_STATE_SETUP_HANDLE,
+ USB_DWC3_EP0_STATE_DATA_SEND,
+ USB_DWC3_EP0_STATE_DATA_RECV,
+ USB_DWC3_EP0_STATE_DATA_SEND_DONE,
+ USB_DWC3_EP0_STATE_DATA_RECV_DONE,
+ USB_DWC3_EP0_STATE_DATA_RECV_STATUS,
+ USB_DWC3_EP0_STATE_DATA_RECV_STATUS_DONE,
+ USB_DWC3_EP0_STATE_DATA_SEND_STATUS,
+ USB_DWC3_EP0_STATE_DATA_SEND_STATUS_DONE
+};
+
+typedef struct dwc3_dev {
+ /* USB DRD */
+ uintptr_t regs;
+ dart_dev_t *dart;
+
+ enum ep0_state ep0_state;
+ const void *ep0_buffer;
+ u32 ep0_buffer_len;
+ void *ep0_read_buffer;
+ u32 ep0_read_buffer_len;
+
+ void *evtbuffer;
+ u32 evt_buffer_offset;
+
+ void *scratchpad;
+ void *xferbuffer;
+ struct dwc3_trb *trbs;
+
+ struct {
+ bool xfer_in_progress;
+ bool zlp_pending;
+
+ void *xfer_buffer;
+ uintptr_t xfer_buffer_iova;
+
+ struct dwc3_trb *trb;
+ uintptr_t trb_iova;
+ } endpoints[MAX_ENDPOINTS];
+
+ struct {
+ ringbuffer_t *host2device;
+ ringbuffer_t *device2host;
+ u8 ep_intr;
+ u8 ep_in;
+ u8 ep_out;
+ bool ready;
+ /* USB ACM CDC serial */
+ u8 cdc_line_coding[7];
+ } pipe[CDC_ACM_PIPE_MAX];
+
+} dwc3_dev_t;
+
+static const struct usb_string_descriptor str_manufacturer =
+ make_usb_string_descriptor("Asahi Linux");
+static const struct usb_string_descriptor str_product =
+ make_usb_string_descriptor("m1n1 uartproxy " BUILD_TAG);
+static const struct usb_string_descriptor str_serial = make_usb_string_descriptor("P-0");
+
+static const struct usb_string_descriptor_languages str_langs = {
+ .bLength = sizeof(str_langs) + 2,
+ .bDescriptorType = USB_STRING_DESCRIPTOR,
+ .wLANGID = {USB_LANGID_EN_US},
+};
+
+struct cdc_dev_desc {
+ const struct usb_configuration_descriptor configuration;
+ const struct usb_interface_descriptor interface_management;
+ const struct cdc_union_functional_descriptor cdc_union_func;
+ const struct usb_endpoint_descriptor endpoint_notification;
+ const struct usb_interface_descriptor interface_data;
+ const struct usb_endpoint_descriptor endpoint_data_in;
+ const struct usb_endpoint_descriptor endpoint_data_out;
+ const struct usb_interface_descriptor sec_interface_management;
+ const struct cdc_union_functional_descriptor sec_cdc_union_func;
+ const struct usb_endpoint_descriptor sec_endpoint_notification;
+ const struct usb_interface_descriptor sec_interface_data;
+ const struct usb_endpoint_descriptor sec_endpoint_data_in;
+ const struct usb_endpoint_descriptor sec_endpoint_data_out;
+} PACKED;
+
+static const struct usb_device_descriptor usb_cdc_device_descriptor = {
+ .bLength = sizeof(struct usb_device_descriptor),
+ .bDescriptorType = USB_DEVICE_DESCRIPTOR,
+ .bcdUSB = 0x0200,
+ .bDeviceClass = CDC_DEVICE_CLASS,
+ .bDeviceSubClass = 0, // unused
+ .bDeviceProtocol = 0, // unused
+ .bMaxPacketSize0 = 64,
+ .idVendor = CDC_USB_VID,
+ .idProduct = CDC_USB_PID,
+ .bcdDevice = 0x0100,
+ .iManufacturer = STRING_DESCRIPTOR_MANUFACTURER,
+ .iProduct = STRING_DESCRIPTOR_PRODUCT,
+ .iSerialNumber = STRING_DESCRIPTOR_SERIAL,
+ .bNumConfigurations = 1,
+};
+
+static const struct cdc_dev_desc cdc_configuration_descriptor = {
+ .configuration =
+ {
+ .bLength = sizeof(cdc_configuration_descriptor.configuration),
+ .bDescriptorType = USB_CONFIGURATION_DESCRIPTOR,
+ .wTotalLength = sizeof(cdc_configuration_descriptor),
+ .bNumInterfaces = 4,
+ .bConfigurationValue = 1,
+ .iConfiguration = 0,
+ .bmAttributes = USB_CONFIGURATION_ATTRIBUTE_RES1 | USB_CONFIGURATION_SELF_POWERED,
+ .bMaxPower = 250,
+
+ },
+ .interface_management =
+ {
+ .bLength = sizeof(cdc_configuration_descriptor.interface_management),
+ .bDescriptorType = USB_INTERFACE_DESCRIPTOR,
+ .bInterfaceNumber = 0,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = CDC_INTERFACE_CLASS,
+ .bInterfaceSubClass = CDC_INTERFACE_SUBCLASS_ACM,
+ .bInterfaceProtocol = CDC_INTERFACE_PROTOCOL_NONE,
+ .iInterface = 0,
+
+ },
+ .cdc_union_func =
+ {
+ .bFunctionLength = sizeof(cdc_configuration_descriptor.cdc_union_func),
+ .bDescriptorType = USB_CDC_INTERFACE_FUNCTIONAL_DESCRIPTOR,
+ .bDescriptorSubtype = USB_CDC_UNION_SUBTYPE,
+ .bControlInterface = 0,
+ .bDataInterface = 1,
+ },
+ /*
+ * we never use this endpoint, but it should exist and always be idle.
+ * it needs to exist in the descriptor though to make hosts correctly recognize
+ * us as a ACM CDC device.
+ */
+ .endpoint_notification =
+ {
+ .bLength = sizeof(cdc_configuration_descriptor.endpoint_notification),
+ .bDescriptorType = USB_ENDPOINT_DESCRIPTOR,
+ .bEndpointAddress = USB_ENDPOINT_ADDR_IN(1),
+ .bmAttributes = USB_ENDPOINT_ATTR_TYPE_INTERRUPT,
+ .wMaxPacketSize = 64,
+ .bInterval = 10,
+
+ },
+ .interface_data =
+ {
+ .bLength = sizeof(cdc_configuration_descriptor.interface_data),
+ .bDescriptorType = USB_INTERFACE_DESCRIPTOR,
+ .bInterfaceNumber = 1,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = CDC_INTERFACE_CLASS_DATA,
+ .bInterfaceSubClass = 0, // unused
+ .bInterfaceProtocol = 0, // unused
+ .iInterface = 0,
+ },
+ .endpoint_data_in =
+ {
+ .bLength = sizeof(cdc_configuration_descriptor.endpoint_data_in),
+ .bDescriptorType = USB_ENDPOINT_DESCRIPTOR,
+ .bEndpointAddress = USB_ENDPOINT_ADDR_OUT(2),
+ .bmAttributes = USB_ENDPOINT_ATTR_TYPE_BULK,
+ .wMaxPacketSize = 512,
+ .bInterval = 10,
+ },
+ .endpoint_data_out =
+ {
+ .bLength = sizeof(cdc_configuration_descriptor.endpoint_data_out),
+ .bDescriptorType = USB_ENDPOINT_DESCRIPTOR,
+ .bEndpointAddress = USB_ENDPOINT_ADDR_IN(2),
+ .bmAttributes = USB_ENDPOINT_ATTR_TYPE_BULK,
+ .wMaxPacketSize = 512,
+ .bInterval = 10,
+ },
+
+ /*
+ * CDC ACM interface for virtual uart
+ */
+
+ .sec_interface_management =
+ {
+ .bLength = sizeof(cdc_configuration_descriptor.sec_interface_management),
+ .bDescriptorType = USB_INTERFACE_DESCRIPTOR,
+ .bInterfaceNumber = 2,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = CDC_INTERFACE_CLASS,
+ .bInterfaceSubClass = CDC_INTERFACE_SUBCLASS_ACM,
+ .bInterfaceProtocol = CDC_INTERFACE_PROTOCOL_NONE,
+ .iInterface = 0,
+
+ },
+ .sec_cdc_union_func =
+ {
+ .bFunctionLength = sizeof(cdc_configuration_descriptor.sec_cdc_union_func),
+ .bDescriptorType = USB_CDC_INTERFACE_FUNCTIONAL_DESCRIPTOR,
+ .bDescriptorSubtype = USB_CDC_UNION_SUBTYPE,
+ .bControlInterface = 2,
+ .bDataInterface = 3,
+ },
+ /*
+ * we never use this endpoint, but it should exist and always be idle.
+ * it needs to exist in the descriptor though to make hosts correctly recognize
+ * us as a ACM CDC device.
+ */
+ .sec_endpoint_notification =
+ {
+ .bLength = sizeof(cdc_configuration_descriptor.sec_endpoint_notification),
+ .bDescriptorType = USB_ENDPOINT_DESCRIPTOR,
+ .bEndpointAddress = USB_ENDPOINT_ADDR_IN(3),
+ .bmAttributes = USB_ENDPOINT_ATTR_TYPE_INTERRUPT,
+ .wMaxPacketSize = 64,
+ .bInterval = 10,
+
+ },
+ .sec_interface_data =
+ {
+ .bLength = sizeof(cdc_configuration_descriptor.sec_interface_data),
+ .bDescriptorType = USB_INTERFACE_DESCRIPTOR,
+ .bInterfaceNumber = 3,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = CDC_INTERFACE_CLASS_DATA,
+ .bInterfaceSubClass = 0, // unused
+ .bInterfaceProtocol = 0, // unused
+ .iInterface = 0,
+ },
+ .sec_endpoint_data_in =
+ {
+ .bLength = sizeof(cdc_configuration_descriptor.sec_endpoint_data_in),
+ .bDescriptorType = USB_ENDPOINT_DESCRIPTOR,
+ .bEndpointAddress = USB_ENDPOINT_ADDR_OUT(4),
+ .bmAttributes = USB_ENDPOINT_ATTR_TYPE_BULK,
+ .wMaxPacketSize = 512,
+ .bInterval = 10,
+ },
+ .sec_endpoint_data_out =
+ {
+ .bLength = sizeof(cdc_configuration_descriptor.sec_endpoint_data_out),
+ .bDescriptorType = USB_ENDPOINT_DESCRIPTOR,
+ .bEndpointAddress = USB_ENDPOINT_ADDR_IN(4),
+ .bmAttributes = USB_ENDPOINT_ATTR_TYPE_BULK,
+ .wMaxPacketSize = 512,
+ .bInterval = 10,
+ },
+};
+
+static const struct usb_device_qualifier_descriptor usb_cdc_device_qualifier_descriptor = {
+ .bLength = sizeof(struct usb_device_qualifier_descriptor),
+ .bDescriptorType = USB_DEVICE_QUALIFIER_DESCRIPTOR,
+ .bcdUSB = 0x0200,
+ .bDeviceClass = CDC_DEVICE_CLASS,
+ .bDeviceSubClass = 0, // unused
+ .bDeviceProtocol = 0, // unused
+ .bMaxPacketSize0 = 64,
+ .bNumConfigurations = 0,
+};
+
+static const char *devt_names[] = {
+ "DisconnEvt", "USBRst", "ConnectDone", "ULStChng", "WkUpEvt", "Reserved", "EOPF",
+ "SOF", "Reserved", "ErrticErr", "CmdCmplt", "EvntOverflow", "VndrDevTstRcved"};
+static const char *depvt_names[] = {
+ "Reserved",
+ "XferComplete",
+ "XferInProgress",
+ "XferNotReady",
+ "RxTxFifoEvt (IN->Underrun, OUT->Overrun)",
+ "Reserved",
+ "StreamEvt",
+ "EPCmdCmplt",
+};
+
+static const char *ep0_state_names[] = {
+ "STATE_IDLE",
+ "STATE_SETUP_HANDLE",
+ "STATE_DATA_SEND",
+ "STATE_DATA_RECV",
+ "STATE_DATA_SEND_DONE",
+ "STATE_DATA_RECV_DONE",
+ "STATE_DATA_RECV_STATUS",
+ "STATE_DATA_RECV_STATUS_DONE",
+ "STATE_DATA_SEND_STATUS",
+ "STATE_DATA_SEND_STATUS_DONE",
+};
+
+static u8 ep_to_num(u8 epno)
+{
+ return (epno << 1) | (epno >> 7);
+}
+
+static int usb_dwc3_command(dwc3_dev_t *dev, u32 command, u32 par)
+{
+ write32(dev->regs + DWC3_DGCMDPAR, par);
+ write32(dev->regs + DWC3_DGCMD, command | DWC3_DGCMD_CMDACT);
+
+ if (poll32(dev->regs + DWC3_DGCMD, DWC3_DGCMD_CMDACT, 0, 1000)) {
+ usb_debug_printf("timeout while waiting for DWC3_DGCMD_CMDACT to clear.\n");
+ return -1;
+ }
+
+ return DWC3_DGCMD_STATUS(read32(dev->regs + DWC3_DGCMD));
+}
+
+static int usb_dwc3_ep_command(dwc3_dev_t *dev, u8 ep, u32 command, u32 par0, u32 par1, u32 par2)
+{
+ write32(dev->regs + DWC3_DEPCMDPAR0(ep), par0);
+ write32(dev->regs + DWC3_DEPCMDPAR1(ep), par1);
+ write32(dev->regs + DWC3_DEPCMDPAR2(ep), par2);
+ write32(dev->regs + DWC3_DEPCMD(ep), command | DWC3_DEPCMD_CMDACT);
+
+ if (poll32(dev->regs + DWC3_DEPCMD(ep), DWC3_DEPCMD_CMDACT, 0, 1000)) {
+ usb_debug_printf("timeout while waiting for DWC3_DEPCMD_CMDACT to clear.\n");
+ return -1;
+ }
+
+ return DWC3_DEPCMD_STATUS(read32(dev->regs + DWC3_DEPCMD(ep)));
+}
+
+static int usb_dwc3_ep_configure(dwc3_dev_t *dev, u8 ep, u8 type, u32 max_packet_len)
+{
+ u32 param0, param1;
+
+ param0 = DWC3_DEPCFG_EP_TYPE(type) | DWC3_DEPCFG_MAX_PACKET_SIZE(max_packet_len);
+ if (type != DWC3_DEPCMD_TYPE_CONTROL)
+ param0 |= DWC3_DEPCFG_FIFO_NUMBER(ep);
+
+ param1 =
+ DWC3_DEPCFG_XFER_COMPLETE_EN | DWC3_DEPCFG_XFER_NOT_READY_EN | DWC3_DEPCFG_EP_NUMBER(ep);
+
+ if (usb_dwc3_ep_command(dev, ep, DWC3_DEPCMD_SETEPCONFIG, param0, param1, 0)) {
+ usb_debug_printf("cannot issue DWC3_DEPCMD_SETEPCONFIG for EP %d.\n", ep);
+ return -1;
+ }
+
+ if (usb_dwc3_ep_command(dev, ep, DWC3_DEPCMD_SETTRANSFRESOURCE, 1, 0, 0)) {
+ usb_debug_printf("cannot issue DWC3_DEPCMD_SETTRANSFRESOURCE EP %d.\n", ep);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int usb_dwc3_ep_start_transfer(dwc3_dev_t *dev, u8 ep, uintptr_t trb_iova)
+{
+ if (dev->endpoints[ep].xfer_in_progress) {
+ usb_debug_printf(
+ "Tried to start a transfer for ep 0x%02x while another transfer is ongoing.\n", ep);
+ return -1;
+ }
+
+ dma_wmb();
+ int ret =
+ usb_dwc3_ep_command(dev, ep, DWC3_DEPCMD_STARTTRANSFER, trb_iova >> 32, (u32)trb_iova, 0);
+ if (ret) {
+ usb_debug_printf("cannot issue DWC3_DEPCMD_STARTTRANSFER for EP %d: %d.\n", ep, ret);
+ return ret;
+ }
+
+ dev->endpoints[ep].xfer_in_progress = true;
+ return 0;
+}
+
+static uintptr_t usb_dwc3_init_trb(dwc3_dev_t *dev, u8 ep, struct dwc3_trb **trb)
+{
+ struct dwc3_trb *next_trb = dev->endpoints[ep].trb;
+
+ if (trb)
+ *trb = next_trb;
+
+ next_trb->ctrl = DWC3_TRB_CTRL_HWO | DWC3_TRB_CTRL_ISP_IMI | DWC3_TRB_CTRL_LST;
+ next_trb->size = DWC3_TRB_SIZE_LENGTH(0);
+ next_trb->bph = 0;
+ next_trb->bpl = dev->endpoints[ep].xfer_buffer_iova;
+
+ return dev->endpoints[ep].trb_iova;
+}
+
+static int usb_dwc3_run_data_trb(dwc3_dev_t *dev, u8 ep, u32 data_len)
+{
+ struct dwc3_trb *trb;
+ uintptr_t trb_iova = usb_dwc3_init_trb(dev, ep, &trb);
+
+ trb->ctrl |= DWC3_TRBCTL_CONTROL_DATA;
+ trb->size = DWC3_TRB_SIZE_LENGTH(data_len);
+
+ return usb_dwc3_ep_start_transfer(dev, ep, trb_iova);
+}
+
+static int usb_dwc3_start_setup_phase(dwc3_dev_t *dev)
+{
+ struct dwc3_trb *trb;
+ uintptr_t trb_iova = usb_dwc3_init_trb(dev, USB_LEP_CTRL_OUT, &trb);
+
+ trb->ctrl |= DWC3_TRBCTL_CONTROL_SETUP;
+ trb->size = DWC3_TRB_SIZE_LENGTH(sizeof(union usb_setup_packet));
+ return usb_dwc3_ep_start_transfer(dev, USB_LEP_CTRL_OUT, trb_iova);
+}
+
+static int usb_dwc3_start_status_phase(dwc3_dev_t *dev, u8 ep)
+{
+ struct dwc3_trb *trb;
+ uintptr_t trb_iova = usb_dwc3_init_trb(dev, ep, &trb);
+
+ trb->ctrl |= DWC3_TRBCTL_CONTROL_STATUS2;
+ trb->size = DWC3_TRB_SIZE_LENGTH(0);
+
+ return usb_dwc3_ep_start_transfer(dev, ep, trb_iova);
+}
+
+static int usb_dwc3_ep0_start_data_send_phase(dwc3_dev_t *dev)
+{
+ if (dev->ep0_buffer_len > XFER_BUFFER_BYTES_PER_EP) {
+ usb_debug_printf("Cannot xfer more than %d bytes but was requested to xfer %d on ep 1\n",
+ XFER_BUFFER_BYTES_PER_EP, dev->ep0_buffer_len);
+ return -1;
+ }
+
+ memset(dev->endpoints[USB_LEP_CTRL_IN].xfer_buffer, 0, 64);
+ memcpy(dev->endpoints[USB_LEP_CTRL_IN].xfer_buffer, dev->ep0_buffer, dev->ep0_buffer_len);
+
+ return usb_dwc3_run_data_trb(dev, USB_LEP_CTRL_IN, dev->ep0_buffer_len);
+}
+
+static int usb_dwc3_ep0_start_data_recv_phase(dwc3_dev_t *dev)
+{
+ if (dev->ep0_buffer_len > XFER_BUFFER_BYTES_PER_EP) {
+ usb_debug_printf("Cannot xfer more than %d bytes but was requested to xfer %d on ep 0\n",
+ XFER_BUFFER_BYTES_PER_EP, dev->ep0_buffer_len);
+ return -1;
+ }
+
+ memset(dev->endpoints[USB_LEP_CTRL_OUT].xfer_buffer, 0, 64);
+
+ return usb_dwc3_run_data_trb(dev, USB_LEP_CTRL_OUT, 64);
+}
+
+static void usb_dwc3_ep_set_stall(dwc3_dev_t *dev, u8 ep, u8 stall)
+{
+ if (stall)
+ usb_dwc3_ep_command(dev, ep, DWC3_DEPCMD_SETSTALL, 0, 0, 0);
+ else
+ usb_dwc3_ep_command(dev, ep, DWC3_DEPCMD_CLEARSTALL, 0, 0, 0);
+}
+
+static void usb_cdc_get_string_descriptor(u32 index, const void **descriptor, u16 *descriptor_len)
+{
+ switch (index) {
+ case STRING_DESCRIPTOR_LANGUAGES:
+ *descriptor = &str_langs;
+ *descriptor_len = str_langs.bLength;
+ break;
+ case STRING_DESCRIPTOR_MANUFACTURER:
+ *descriptor = &str_manufacturer;
+ *descriptor_len = str_manufacturer.bLength;
+ break;
+ case STRING_DESCRIPTOR_PRODUCT:
+ *descriptor = &str_product;
+ *descriptor_len = str_product.bLength;
+ break;
+ case STRING_DESCRIPTOR_SERIAL:
+ *descriptor = &str_serial;
+ *descriptor_len = str_serial.bLength;
+ break;
+ default:
+ *descriptor = NULL;
+ *descriptor_len = 0;
+ }
+}
+
+static int
+usb_dwc3_handle_ep0_get_descriptor(dwc3_dev_t *dev,
+ const struct usb_setup_packet_get_descriptor *get_descriptor)
+{
+ const void *descriptor = NULL;
+ u16 descriptor_len = 0;
+
+ switch (get_descriptor->type) {
+ case USB_DEVICE_DESCRIPTOR:
+ descriptor = &usb_cdc_device_descriptor;
+ descriptor_len = usb_cdc_device_descriptor.bLength;
+ break;
+ case USB_CONFIGURATION_DESCRIPTOR:
+ descriptor = &cdc_configuration_descriptor;
+ descriptor_len = cdc_configuration_descriptor.configuration.wTotalLength;
+ break;
+ case USB_STRING_DESCRIPTOR:
+ usb_cdc_get_string_descriptor(get_descriptor->index, &descriptor, &descriptor_len);
+ break;
+ case USB_DEVICE_QUALIFIER_DESCRIPTOR:
+ descriptor = &usb_cdc_device_qualifier_descriptor;
+ descriptor_len = usb_cdc_device_qualifier_descriptor.bLength;
+ break;
+ default:
+ usb_debug_printf("Unknown descriptor type: %d\n", get_descriptor->type);
+ break;
+ }
+
+ if (descriptor) {
+ dev->ep0_buffer = descriptor;
+ dev->ep0_buffer_len = min(get_descriptor->wLength, descriptor_len);
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+static void usb_dwc3_ep0_handle_standard_device(dwc3_dev_t *dev,
+ const union usb_setup_packet *setup)
+{
+ switch (setup->raw.bRequest) {
+ case USB_REQUEST_SET_ADDRESS:
+ mask32(dev->regs + DWC3_DCFG, DWC3_DCFG_DEVADDR_MASK,
+ DWC3_DCFG_DEVADDR(setup->set_address.address));
+ dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND_STATUS;
+ break;
+
+ case USB_REQUEST_SET_CONFIGURATION:
+ switch (setup->set_configuration.configuration) {
+ case 0:
+ clear32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_OUT));
+ clear32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_IN));
+ clear32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_INTR_IN));
+ clear32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_OUT_2));
+ clear32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_IN_2));
+ clear32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_INTR_IN_2));
+ dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND_STATUS;
+ for (int i = 0; i < CDC_ACM_PIPE_MAX; i++)
+ dev->pipe[i].ready = false;
+ break;
+ case 1:
+ /* we've already configured these endpoints so that we just need to enable them
+ * here */
+ set32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_OUT));
+ set32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_IN));
+ set32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_INTR_IN));
+ set32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_OUT_2));
+ set32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_BULK_IN_2));
+ set32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(USB_LEP_CDC_INTR_IN_2));
+ dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND_STATUS;
+ break;
+ default:
+ usb_dwc3_ep_set_stall(dev, 0, 1);
+ dev->ep0_state = USB_DWC3_EP0_STATE_IDLE;
+ break;
+ }
+ break;
+
+ case USB_REQUEST_GET_DESCRIPTOR:
+ if (usb_dwc3_handle_ep0_get_descriptor(dev, &setup->get_descriptor) < 0) {
+ usb_dwc3_ep_set_stall(dev, 0, 1);
+ dev->ep0_state = USB_DWC3_EP0_STATE_IDLE;
+ } else {
+ dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND;
+ }
+ break;
+
+ case USB_REQUEST_GET_STATUS: {
+ static const u16 device_status = 0x0001; // self-powered
+ dev->ep0_buffer = &device_status;
+ dev->ep0_buffer_len = 2;
+ dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND;
+ break;
+ }
+
+ default:
+ usb_dwc3_ep_set_stall(dev, 0, 1);
+ dev->ep0_state = USB_DWC3_EP0_STATE_IDLE;
+ usb_debug_printf("unsupported SETUP packet\n");
+ }
+}
+
+static void usb_dwc3_ep0_handle_standard_interface(dwc3_dev_t *dev,
+ const union usb_setup_packet *setup)
+{
+ switch (setup->raw.bRequest) {
+ case USB_REQUEST_GET_STATUS: {
+ static const u16 device_status = 0x0000; // reserved
+ dev->ep0_buffer = &device_status;
+ dev->ep0_buffer_len = 2;
+ dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND;
+ break;
+ }
+ default:
+ usb_dwc3_ep_set_stall(dev, 0, 1);
+ dev->ep0_state = USB_DWC3_EP0_STATE_IDLE;
+ usb_debug_printf("unsupported SETUP packet\n");
+ }
+}
+
+static void usb_dwc3_ep0_handle_standard_endpoint(dwc3_dev_t *dev,
+ const union usb_setup_packet *setup)
+{
+ switch (setup->raw.bRequest) {
+ case USB_REQUEST_GET_STATUS: {
+ static const u16 device_status = 0x0000; // reserved
+ dev->ep0_buffer = &device_status;
+ dev->ep0_buffer_len = 2;
+ dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND;
+ break;
+ }
+ case USB_REQUEST_CLEAR_FEATURE: {
+ switch (setup->feature.wFeatureSelector) {
+ case USB_FEATURE_ENDPOINT_HALT:
+ usb_debug_printf("Host cleared EP 0x%x stall\n", setup->feature.wEndpoint);
+ usb_dwc3_ep_set_stall(dev, ep_to_num(setup->feature.wEndpoint), 0);
+ usb_dwc3_start_status_phase(dev, USB_LEP_CTRL_IN);
+ dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND_STATUS_DONE;
+ break;
+ default:
+ usb_dwc3_ep_set_stall(dev, 0, 1);
+ dev->ep0_state = USB_DWC3_EP0_STATE_IDLE;
+ usb_debug_printf("unsupported CLEAR FEATURE: 0x%x\n",
+ setup->feature.wFeatureSelector);
+ break;
+ }
+ break;
+ }
+ default:
+ usb_dwc3_ep_set_stall(dev, 0, 1);
+ dev->ep0_state = USB_DWC3_EP0_STATE_IDLE;
+ usb_debug_printf("unsupported SETUP packet\n");
+ }
+}
+
+static void usb_dwc3_ep0_handle_standard(dwc3_dev_t *dev, const union usb_setup_packet *setup)
+{
+ switch (setup->raw.bmRequestType & USB_REQUEST_TYPE_RECIPIENT_MASK) {
+ case USB_REQUEST_TYPE_RECIPIENT_DEVICE:
+ usb_dwc3_ep0_handle_standard_device(dev, setup);
+ break;
+
+ case USB_REQUEST_TYPE_RECIPIENT_INTERFACE:
+ usb_dwc3_ep0_handle_standard_interface(dev, setup);
+ break;
+
+ case USB_REQUEST_TYPE_RECIPIENT_ENDPOINT:
+ usb_dwc3_ep0_handle_standard_endpoint(dev, setup);
+ break;
+
+ default:
+ usb_dwc3_ep_set_stall(dev, 0, 1);
+ dev->ep0_state = USB_DWC3_EP0_STATE_IDLE;
+ usb_debug_printf("unimplemented request recipient\n");
+ }
+}
+
+static void usb_dwc3_ep0_handle_class(dwc3_dev_t *dev, const union usb_setup_packet *setup)
+{
+ int pipe = setup->raw.wIndex / 2;
+
+ switch (setup->raw.bRequest) {
+ case USB_REQUEST_CDC_GET_LINE_CODING:
+ dev->ep0_buffer_len = min(setup->raw.wLength, sizeof(dev->pipe[pipe].cdc_line_coding));
+ dev->ep0_buffer = dev->pipe[pipe].cdc_line_coding;
+ dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND;
+ break;
+
+ case USB_REQUEST_CDC_SET_CTRL_LINE_STATE:
+ if (setup->raw.wValue & 1) { // DTR
+ dev->pipe[pipe].ready = false;
+ usb_debug_printf("ACM device opened\n");
+ dev->pipe[pipe].ready = true;
+ } else {
+ dev->pipe[pipe].ready = false;
+ usb_debug_printf("ACM device closed\n");
+ }
+ usb_dwc3_start_status_phase(dev, USB_LEP_CTRL_IN);
+ dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND_STATUS_DONE;
+ break;
+
+ case USB_REQUEST_CDC_SET_LINE_CODING:
+ dev->ep0_read_buffer = dev->pipe[pipe].cdc_line_coding;
+ dev->ep0_read_buffer_len =
+ min(setup->raw.wLength, sizeof(dev->pipe[pipe].cdc_line_coding));
+ dev->ep0_state = USB_DWC3_EP0_STATE_DATA_RECV;
+ break;
+
+ default:
+ usb_dwc3_ep_set_stall(dev, 0, 1);
+ dev->ep0_state = USB_DWC3_EP0_STATE_IDLE;
+ usb_debug_printf("unsupported SETUP packet\n");
+ }
+}
+
+static void usb_dwc3_ep0_handle_setup(dwc3_dev_t *dev)
+{
+ const union usb_setup_packet *setup = dev->endpoints[0].xfer_buffer;
+
+ switch (setup->raw.bmRequestType & USB_REQUEST_TYPE_MASK) {
+ case USB_REQUEST_TYPE_STANDARD:
+ usb_dwc3_ep0_handle_standard(dev, setup);
+ break;
+ case USB_REQUEST_TYPE_CLASS:
+ usb_dwc3_ep0_handle_class(dev, setup);
+ break;
+ default:
+ usb_debug_printf("unsupported request type\n");
+ usb_dwc3_ep_set_stall(dev, 0, 1);
+ dev->ep0_state = USB_DWC3_EP0_STATE_IDLE;
+ }
+}
+
+static void usb_dwc3_ep0_handle_xfer_done(dwc3_dev_t *dev, const struct dwc3_event_depevt event)
+{
+ switch (dev->ep0_state) {
+ case USB_DWC3_EP0_STATE_SETUP_HANDLE:
+ usb_dwc3_ep0_handle_setup(dev);
+ break;
+
+ case USB_DWC3_EP0_STATE_DATA_RECV_STATUS_DONE:
+ case USB_DWC3_EP0_STATE_DATA_SEND_STATUS_DONE:
+ usb_dwc3_start_setup_phase(dev);
+ dev->ep0_state = USB_DWC3_EP0_STATE_SETUP_HANDLE;
+ break;
+
+ case USB_DWC3_EP0_STATE_DATA_SEND_DONE:
+ dev->ep0_state = USB_DWC3_EP0_STATE_DATA_RECV_STATUS;
+ break;
+
+ case USB_DWC3_EP0_STATE_DATA_RECV_DONE:
+ memcpy(dev->ep0_read_buffer, dev->endpoints[event.endpoint_number].xfer_buffer,
+ dev->ep0_read_buffer_len);
+ dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND_STATUS;
+ break;
+
+ case USB_DWC3_EP0_STATE_IDLE:
+ default:
+ usb_debug_printf("invalid state in usb_dwc3_ep0_handle_xfer_done: %d, %s\n",
+ dev->ep0_state, ep0_state_names[dev->ep0_state]);
+ usb_dwc3_ep_set_stall(dev, 0, 1);
+ dev->ep0_state = USB_DWC3_EP0_STATE_IDLE;
+ }
+}
+
+static void usb_dwc3_ep0_handle_xfer_not_ready(dwc3_dev_t *dev,
+ const struct dwc3_event_depevt event)
+{
+ switch (dev->ep0_state) {
+ case USB_DWC3_EP0_STATE_IDLE:
+ usb_dwc3_start_setup_phase(dev);
+ dev->ep0_state = USB_DWC3_EP0_STATE_SETUP_HANDLE;
+ break;
+
+ case USB_DWC3_EP0_STATE_DATA_SEND:
+ if (usb_dwc3_ep0_start_data_send_phase(dev))
+ usb_debug_printf("cannot start xtrl xfer data phase for EP 1.\n");
+ dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND_DONE;
+ break;
+
+ case USB_DWC3_EP0_STATE_DATA_RECV:
+ if (usb_dwc3_ep0_start_data_recv_phase(dev))
+ usb_debug_printf("cannot start xtrl xfer data phase for EP 0.\n");
+ dev->ep0_state = USB_DWC3_EP0_STATE_DATA_RECV_DONE;
+ break;
+
+ case USB_DWC3_EP0_STATE_DATA_RECV_STATUS:
+ usb_dwc3_start_status_phase(dev, USB_LEP_CTRL_OUT);
+ dev->ep0_state = USB_DWC3_EP0_STATE_DATA_RECV_STATUS_DONE;
+ break;
+
+ case USB_DWC3_EP0_STATE_DATA_SEND_STATUS:
+ usb_dwc3_start_status_phase(dev, USB_LEP_CTRL_IN);
+ dev->ep0_state = USB_DWC3_EP0_STATE_DATA_SEND_STATUS_DONE;
+ break;
+
+ default:
+ usb_debug_printf(
+ "invalid state in usb_dwc3_ep0_handle_xfer_not_ready: %d, %s for ep %d (%x)\n",
+ dev->ep0_state, ep0_state_names[dev->ep0_state], event.endpoint_number,
+ event.endpoint_event);
+ usb_dwc3_ep_set_stall(dev, 0, 1);
+ dev->ep0_state = USB_DWC3_EP0_STATE_IDLE;
+ }
+}
+
+ringbuffer_t *usb_dwc3_cdc_get_ringbuffer(dwc3_dev_t *dev, u8 endpoint_number)
+{
+ switch (endpoint_number) {
+ case USB_LEP_CDC_BULK_IN:
+ return dev->pipe[CDC_ACM_PIPE_0].device2host;
+ case USB_LEP_CDC_BULK_OUT:
+ return dev->pipe[CDC_ACM_PIPE_0].host2device;
+ case USB_LEP_CDC_BULK_IN_2:
+ return dev->pipe[CDC_ACM_PIPE_1].device2host;
+ case USB_LEP_CDC_BULK_OUT_2:
+ return dev->pipe[CDC_ACM_PIPE_1].host2device;
+ default:
+ return NULL;
+ }
+}
+
+static void usb_dwc3_cdc_start_bulk_out_xfer(dwc3_dev_t *dev, u8 endpoint_number)
+{
+ struct dwc3_trb *trb;
+ uintptr_t trb_iova;
+
+ if (dev->endpoints[endpoint_number].xfer_in_progress)
+ return;
+
+ ringbuffer_t *host2device = usb_dwc3_cdc_get_ringbuffer(dev, endpoint_number);
+ if (!host2device)
+ return;
+
+ if (ringbuffer_get_free(host2device) < XFER_SIZE)
+ return;
+
+ memset(dev->endpoints[endpoint_number].xfer_buffer, 0xaa, XFER_SIZE);
+ trb_iova = usb_dwc3_init_trb(dev, endpoint_number, &trb);
+ trb->ctrl |= DWC3_TRBCTL_NORMAL;
+ trb->size = DWC3_TRB_SIZE_LENGTH(XFER_SIZE);
+
+ usb_dwc3_ep_start_transfer(dev, endpoint_number, trb_iova);
+ dev->endpoints[endpoint_number].xfer_in_progress = true;
+}
+
+static void usb_dwc3_cdc_start_bulk_in_xfer(dwc3_dev_t *dev, u8 endpoint_number)
+{
+ struct dwc3_trb *trb;
+ uintptr_t trb_iova;
+
+ if (dev->endpoints[endpoint_number].xfer_in_progress)
+ return;
+
+ ringbuffer_t *device2host = usb_dwc3_cdc_get_ringbuffer(dev, endpoint_number);
+ if (!device2host)
+ return;
+
+ size_t len =
+ ringbuffer_read(dev->endpoints[endpoint_number].xfer_buffer, XFER_SIZE, device2host);
+
+ if (!len && !dev->endpoints[endpoint_number].zlp_pending)
+ return;
+
+ trb_iova = usb_dwc3_init_trb(dev, endpoint_number, &trb);
+ trb->ctrl |= DWC3_TRBCTL_NORMAL;
+ trb->size = DWC3_TRB_SIZE_LENGTH(len);
+
+ usb_dwc3_ep_start_transfer(dev, endpoint_number, trb_iova);
+ dev->endpoints[endpoint_number].xfer_in_progress = true;
+ dev->endpoints[endpoint_number].zlp_pending = (len % 512) == 0;
+}
+
+static void usb_dwc3_cdc_handle_bulk_out_xfer_done(dwc3_dev_t *dev,
+ const struct dwc3_event_depevt event)
+{
+ ringbuffer_t *host2device = usb_dwc3_cdc_get_ringbuffer(dev, event.endpoint_number);
+ if (!host2device)
+ return;
+ size_t len = min(XFER_SIZE, ringbuffer_get_free(host2device));
+ ringbuffer_write(dev->endpoints[event.endpoint_number].xfer_buffer,
+ len - dev->endpoints[event.endpoint_number].trb->size, host2device);
+}
+
+static void usb_dwc3_handle_event_ep(dwc3_dev_t *dev, const struct dwc3_event_depevt event)
+{
+ if (event.endpoint_event == DWC3_DEPEVT_XFERCOMPLETE) {
+ dev->endpoints[event.endpoint_number].xfer_in_progress = false;
+
+ switch (event.endpoint_number) {
+ case USB_LEP_CTRL_IN:
+ case USB_LEP_CTRL_OUT:
+ return usb_dwc3_ep0_handle_xfer_done(dev, event);
+ case USB_LEP_CDC_INTR_IN: // [[fallthrough]]
+ case USB_LEP_CDC_INTR_IN_2:
+ return;
+ case USB_LEP_CDC_BULK_IN: // [[fallthrough]]
+ case USB_LEP_CDC_BULK_IN_2:
+ return;
+ case USB_LEP_CDC_BULK_OUT: // [[fallthrough]]
+ case USB_LEP_CDC_BULK_OUT_2:
+ return usb_dwc3_cdc_handle_bulk_out_xfer_done(dev, event);
+ }
+ } else if (event.endpoint_event == DWC3_DEPEVT_XFERNOTREADY) {
+ /*
+ * this might be a bug, we sometimes get spurious events like these here.
+ * ignoring them works just fine though
+ */
+ if (dev->endpoints[event.endpoint_number].xfer_in_progress)
+ return;
+
+ switch (event.endpoint_number) {
+ case USB_LEP_CTRL_IN:
+ case USB_LEP_CTRL_OUT:
+ return usb_dwc3_ep0_handle_xfer_not_ready(dev, event);
+ case USB_LEP_CDC_INTR_IN: // [[fallthrough]]
+ case USB_LEP_CDC_INTR_IN_2:
+ return;
+ case USB_LEP_CDC_BULK_IN: // [[fallthrough]]
+ case USB_LEP_CDC_BULK_IN_2:
+ return usb_dwc3_cdc_start_bulk_in_xfer(dev, event.endpoint_number);
+ case USB_LEP_CDC_BULK_OUT: // [[fallthrough]]
+ case USB_LEP_CDC_BULK_OUT_2:
+ return usb_dwc3_cdc_start_bulk_out_xfer(dev, event.endpoint_number);
+ }
+ }
+
+ usb_debug_printf("unhandled EP %02x event: %s (0x%02x) (%d)\n", event.endpoint_number,
+ depvt_names[event.endpoint_event], event.endpoint_event,
+ dev->endpoints[event.endpoint_number].xfer_in_progress);
+ usb_dwc3_ep_set_stall(dev, event.endpoint_event, 1);
+}
+
+static void usb_dwc3_handle_event_usbrst(dwc3_dev_t *dev)
+{
+ /* clear STALL mode for all endpoints */
+ dev->endpoints[0].xfer_in_progress = false;
+ for (int i = 1; i < MAX_ENDPOINTS; ++i) {
+ dev->endpoints[i].xfer_in_progress = false;
+ memset(dev->endpoints[i].xfer_buffer, 0, XFER_BUFFER_BYTES_PER_EP);
+ memset(dev->endpoints[i].trb, 0, TRBS_PER_EP * sizeof(struct dwc3_trb));
+ usb_dwc3_ep_set_stall(dev, i, 0);
+ }
+
+ /* set device address back to zero */
+ mask32(dev->regs + DWC3_DCFG, DWC3_DCFG_DEVADDR_MASK, DWC3_DCFG_DEVADDR(0));
+
+ /* only keep control endpoints enabled */
+ write32(dev->regs + DWC3_DALEPENA, DWC3_DALEPENA_EP(0) | DWC3_DALEPENA_EP(1));
+}
+
+static void usb_dwc3_handle_event_connect_done(dwc3_dev_t *dev)
+{
+ u32 speed = read32(dev->regs + DWC3_DSTS) & DWC3_DSTS_CONNECTSPD;
+
+ if (speed != DWC3_DSTS_HIGHSPEED) {
+ usb_debug_printf(
+ "WARNING: we only support high speed right now but %02x was requested in DSTS\n",
+ speed);
+ }
+
+ usb_dwc3_start_setup_phase(dev);
+ dev->ep0_state = USB_DWC3_EP0_STATE_SETUP_HANDLE;
+}
+
+static void usb_dwc3_handle_event_dev(dwc3_dev_t *dev, const struct dwc3_event_devt event)
+{
+ usb_debug_printf("device event: %s (0x%02x)\n", devt_names[event.type], event.type);
+ switch (event.type) {
+ case DWC3_DEVT_USBRST:
+ usb_dwc3_handle_event_usbrst(dev);
+ break;
+ case DWC3_DEVT_CONNECTDONE:
+ usb_dwc3_handle_event_connect_done(dev);
+ break;
+ default:
+ usb_debug_printf("unhandled device event: %s (0x%02x)\n", devt_names[event.type],
+ event.type);
+ }
+}
+
+static void usb_dwc3_handle_event(dwc3_dev_t *dev, const union dwc3_event event)
+{
+ if (!event.type.is_devspec)
+ usb_dwc3_handle_event_ep(dev, event.depevt);
+ else if (event.type.type == DWC3_EVENT_TYPE_DEV)
+ usb_dwc3_handle_event_dev(dev, event.devt);
+ else
+ usb_debug_printf("unknown event %08x\n", event.raw);
+}
+
+void usb_dwc3_handle_events(dwc3_dev_t *dev)
+{
+ if (!dev)
+ return;
+
+ u32 n_events = read32(dev->regs + DWC3_GEVNTCOUNT(0)) / sizeof(union dwc3_event);
+ if (n_events == 0)
+ return;
+
+ dma_rmb();
+
+ const union dwc3_event *evtbuffer = dev->evtbuffer;
+ for (u32 i = 0; i < n_events; ++i) {
+ usb_dwc3_handle_event(dev, evtbuffer[dev->evt_buffer_offset]);
+
+ dev->evt_buffer_offset =
+ (dev->evt_buffer_offset + 1) % (DWC3_EVENT_BUFFERS_SIZE / sizeof(union dwc3_event));
+ }
+
+ write32(dev->regs + DWC3_GEVNTCOUNT(0), sizeof(union dwc3_event) * n_events);
+}
+
+dwc3_dev_t *usb_dwc3_init(uintptr_t regs, dart_dev_t *dart)
+{
+ /* sanity check */
+ u32 snpsid = read32(regs + DWC3_GSNPSID);
+ if ((snpsid & DWC3_GSNPSID_MASK) != 0x33310000) {
+ debug_printf("no DWC3 core found at 0x%lx: %08x\n", regs, snpsid);
+ return NULL;
+ }
+
+ dwc3_dev_t *dev = malloc(sizeof(*dev));
+ if (!dev)
+ return NULL;
+
+ memset(dev, 0, sizeof(*dev));
+ for (int i = 0; i < CDC_ACM_PIPE_MAX; i++)
+ memcpy(dev->pipe[i].cdc_line_coding, cdc_default_line_coding,
+ sizeof(cdc_default_line_coding));
+
+ dev->regs = regs;
+ dev->dart = dart;
+
+ /* allocate and map dma buffers */
+ dev->evtbuffer = memalign(SZ_16K, max(DWC3_EVENT_BUFFERS_SIZE, SZ_16K));
+ if (!dev->evtbuffer)
+ goto error;
+
+ dev->scratchpad = memalign(SZ_16K, max(DWC3_SCRATCHPAD_SIZE, SZ_16K));
+ if (!dev->scratchpad)
+ goto error;
+
+ dev->trbs = memalign(SZ_16K, TRB_BUFFER_SIZE);
+ if (!dev->trbs)
+ goto error;
+
+ dev->xferbuffer = memalign(SZ_16K, XFER_BUFFER_SIZE);
+ if (!dev->xferbuffer)
+ goto error;
+
+ memset(dev->evtbuffer, 0xaa, max(DWC3_EVENT_BUFFERS_SIZE, SZ_16K));
+ memset(dev->scratchpad, 0, max(DWC3_SCRATCHPAD_SIZE, SZ_16K));
+ memset(dev->xferbuffer, 0, XFER_BUFFER_SIZE);
+ memset(dev->trbs, 0, TRB_BUFFER_SIZE);
+
+ if (dart_map(dev->dart, EVENT_BUFFER_IOVA, dev->evtbuffer,
+ max(DWC3_EVENT_BUFFERS_SIZE, SZ_16K)))
+ goto error;
+ if (dart_map(dev->dart, SCRATCHPAD_IOVA, dev->scratchpad, max(DWC3_SCRATCHPAD_SIZE, SZ_16K)))
+ goto error;
+ if (dart_map(dev->dart, TRB_BUFFER_IOVA, dev->trbs, TRB_BUFFER_SIZE))
+ goto error;
+ if (dart_map(dev->dart, XFER_BUFFER_IOVA, dev->xferbuffer, XFER_BUFFER_SIZE))
+ goto error;
+
+ /* prepare endpoint buffers */
+ for (int i = 0; i < MAX_ENDPOINTS; ++i) {
+ u32 xferbuffer_offset = i * XFER_BUFFER_BYTES_PER_EP;
+ dev->endpoints[i].xfer_buffer = dev->xferbuffer + xferbuffer_offset;
+ dev->endpoints[i].xfer_buffer_iova = XFER_BUFFER_IOVA + xferbuffer_offset;
+
+ u32 trb_offset = i * TRBS_PER_EP;
+ dev->endpoints[i].trb = &dev->trbs[i * TRBS_PER_EP];
+ dev->endpoints[i].trb_iova = TRB_BUFFER_IOVA + trb_offset * sizeof(struct dwc3_trb);
+ }
+
+ /* reset the device side of the controller */
+ set32(dev->regs + DWC3_DCTL, DWC3_DCTL_CSFTRST);
+ if (poll32(dev->regs + DWC3_DCTL, DWC3_DCTL_CSFTRST, 0, 1000)) {
+ usb_debug_printf("timeout while waiting for DWC3_DCTL_CSFTRST to clear.\n");
+ goto error;
+ }
+
+ /* soft reset the core and phy */
+ set32(dev->regs + DWC3_GCTL, DWC3_GCTL_CORESOFTRESET);
+ set32(dev->regs + DWC3_GUSB3PIPECTL(0), DWC3_GUSB3PIPECTL_PHYSOFTRST);
+ set32(dev->regs + DWC3_GUSB2PHYCFG(0), DWC3_GUSB2PHYCFG_PHYSOFTRST);
+ mdelay(100);
+ clear32(dev->regs + DWC3_GUSB3PIPECTL(0), DWC3_GUSB3PIPECTL_PHYSOFTRST);
+ clear32(dev->regs + DWC3_GUSB2PHYCFG(0), DWC3_GUSB2PHYCFG_PHYSOFTRST);
+ mdelay(100);
+ clear32(dev->regs + DWC3_GCTL, DWC3_GCTL_CORESOFTRESET);
+ mdelay(100);
+
+ /* disable unused features */
+ clear32(dev->regs + DWC3_GCTL, DWC3_GCTL_SCALEDOWN_MASK | DWC3_GCTL_DISSCRAMBLE);
+
+ /* switch to device-only mode */
+ mask32(dev->regs + DWC3_GCTL, DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG),
+ DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_DEVICE));
+
+ /* stick to USB 2.0 high speed for now */
+ mask32(dev->regs + DWC3_DCFG, DWC3_DCFG_SPEED_MASK, DWC3_DCFG_HIGHSPEED);
+
+ /* setup scratchpad at SCRATCHPAD_IOVA */
+ if (usb_dwc3_command(dev, DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO, SCRATCHPAD_IOVA)) {
+ usb_debug_printf("DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO failed.");
+ goto error;
+ }
+ if (usb_dwc3_command(dev, DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI, 0)) {
+ usb_debug_printf("DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI failed.");
+ goto error;
+ }
+
+ /* setup a single event buffer at EVENT_BUFFER_IOVA */
+ write32(dev->regs + DWC3_GEVNTADRLO(0), EVENT_BUFFER_IOVA);
+ write32(dev->regs + DWC3_GEVNTADRHI(0), 0);
+ write32(dev->regs + DWC3_GEVNTSIZ(0), DWC3_EVENT_BUFFERS_SIZE);
+ write32(dev->regs + DWC3_GEVNTCOUNT(0), 0);
+
+ /* enable connect, disconnect and reset events */
+ write32(dev->regs + DWC3_DEVTEN,
+ DWC3_DEVTEN_DISCONNEVTEN | DWC3_DEVTEN_USBRSTEN | DWC3_DEVTEN_CONNECTDONEEN);
+
+ if (usb_dwc3_ep_command(dev, 0, DWC3_DEPCMD_DEPSTARTCFG, 0, 0, 0)) {
+ usb_debug_printf("cannot issue initial DWC3_DEPCMD_DEPSTARTCFG.\n");
+ goto error;
+ }
+
+ /* prepare control endpoint 0 IN and OUT */
+ if (usb_dwc3_ep_configure(dev, USB_LEP_CTRL_OUT, DWC3_DEPCMD_TYPE_CONTROL, 64))
+ goto error;
+ if (usb_dwc3_ep_configure(dev, USB_LEP_CTRL_IN, DWC3_DEPCMD_TYPE_CONTROL, 64))
+ goto error;
+
+ /* prepare CDC ACM interfaces */
+
+ dev->pipe[CDC_ACM_PIPE_0].ep_intr = USB_LEP_CDC_INTR_IN;
+ dev->pipe[CDC_ACM_PIPE_0].ep_in = USB_LEP_CDC_BULK_IN;
+ dev->pipe[CDC_ACM_PIPE_0].ep_out = USB_LEP_CDC_BULK_OUT;
+
+ dev->pipe[CDC_ACM_PIPE_1].ep_intr = USB_LEP_CDC_INTR_IN_2;
+ dev->pipe[CDC_ACM_PIPE_1].ep_in = USB_LEP_CDC_BULK_IN_2;
+ dev->pipe[CDC_ACM_PIPE_1].ep_out = USB_LEP_CDC_BULK_OUT_2;
+
+ for (int i = 0; i < CDC_ACM_PIPE_MAX; i++) {
+ dev->pipe[i].host2device = ringbuffer_alloc(CDC_BUFFER_SIZE);
+ if (!dev->pipe[i].host2device)
+ goto error;
+ dev->pipe[i].device2host = ringbuffer_alloc(CDC_BUFFER_SIZE);
+ if (!dev->pipe[i].device2host)
+ goto error;
+
+ /* prepare INTR endpoint so that we don't have to reconfigure this device later */
+ if (usb_dwc3_ep_configure(dev, dev->pipe[i].ep_intr, DWC3_DEPCMD_TYPE_INTR, 64))
+ goto error;
+
+ /* prepare BULK endpoints so that we don't have to reconfigure this device later */
+ if (usb_dwc3_ep_configure(dev, dev->pipe[i].ep_in, DWC3_DEPCMD_TYPE_BULK, 512))
+ goto error;
+ if (usb_dwc3_ep_configure(dev, dev->pipe[i].ep_out, DWC3_DEPCMD_TYPE_BULK, 512))
+ goto error;
+ }
+
+ /* prepare first control transfer */
+ dev->ep0_state = USB_DWC3_EP0_STATE_IDLE;
+
+ /* only enable control endpoints for now */
+ write32(dev->regs + DWC3_DALEPENA,
+ DWC3_DALEPENA_EP(USB_LEP_CTRL_IN) | DWC3_DALEPENA_EP(USB_LEP_CTRL_OUT));
+
+ /* and finally kick the device controller to go live! */
+ set32(dev->regs + DWC3_DCTL, DWC3_DCTL_RUN_STOP);
+
+ return dev;
+
+error:
+ usb_dwc3_shutdown(dev);
+ return NULL;
+}
+
+void usb_dwc3_shutdown(dwc3_dev_t *dev)
+{
+ for (int i = 0; i < CDC_ACM_PIPE_MAX; i++)
+ dev->pipe[i].ready = false;
+
+ /* stop all ongoing transfers */
+ for (int i = 1; i < MAX_ENDPOINTS; ++i) {
+ if (!dev->endpoints[i].xfer_in_progress)
+ continue;
+
+ if (usb_dwc3_ep_command(dev, i, DWC3_DEPCMD_ENDTRANSFER, 0, 0, 0))
+ usb_debug_printf("cannot issue DWC3_DEPCMD_ENDTRANSFER for EP %02x.\n", i);
+ }
+
+ /* disable events and all endpoints and stop the device controller */
+ write32(dev->regs + DWC3_DEVTEN, 0);
+ write32(dev->regs + DWC3_DALEPENA, 0);
+ clear32(dev->regs + DWC3_DCTL, DWC3_DCTL_RUN_STOP);
+
+ /* wait until the controller is shut down */
+ if (poll32(dev->regs + DWC3_DSTS, DWC3_DSTS_DEVCTRLHLT, DWC3_DSTS_DEVCTRLHLT, 1000))
+ usb_debug_printf("timeout while waiting for DWC3_DSTS_DEVCTRLHLT during shutdown.\n");
+
+ /* reset the device side of the controller just to be safe */
+ set32(dev->regs + DWC3_DCTL, DWC3_DCTL_CSFTRST);
+ if (poll32(dev->regs + DWC3_DCTL, DWC3_DCTL_CSFTRST, 0, 1000))
+ usb_debug_printf("timeout while waiting for DWC3_DCTL_CSFTRST to clear during shutdown.\n");
+
+ /* unmap and free dma buffers */
+ dart_unmap(dev->dart, TRB_BUFFER_IOVA, TRB_BUFFER_SIZE);
+ dart_unmap(dev->dart, XFER_BUFFER_IOVA, XFER_BUFFER_SIZE);
+ dart_unmap(dev->dart, SCRATCHPAD_IOVA, max(DWC3_SCRATCHPAD_SIZE, SZ_16K));
+ dart_unmap(dev->dart, EVENT_BUFFER_IOVA, max(DWC3_EVENT_BUFFERS_SIZE, SZ_16K));
+
+ free(dev->evtbuffer);
+ free(dev->scratchpad);
+ free(dev->xferbuffer);
+ free(dev->trbs);
+ for (int i = 0; i < CDC_ACM_PIPE_MAX; i++) {
+ ringbuffer_free(dev->pipe[i].device2host);
+ ringbuffer_free(dev->pipe[i].host2device);
+ }
+
+ if (dev->dart)
+ dart_shutdown(dev->dart);
+ free(dev);
+}
+
+u8 usb_dwc3_getbyte(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe)
+{
+ ringbuffer_t *host2device = dev->pipe[pipe].host2device;
+ if (!host2device)
+ return 0;
+
+ u8 ep = dev->pipe[pipe].ep_out;
+
+ u8 c;
+ while (ringbuffer_read(&c, 1, host2device) < 1) {
+ usb_dwc3_handle_events(dev);
+ usb_dwc3_cdc_start_bulk_out_xfer(dev, ep);
+ }
+ return c;
+}
+
+void usb_dwc3_putbyte(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe, u8 byte)
+{
+ ringbuffer_t *device2host = dev->pipe[pipe].device2host;
+ if (!device2host)
+ return;
+
+ u8 ep = dev->pipe[pipe].ep_in;
+
+ while (ringbuffer_write(&byte, 1, device2host) < 1) {
+ usb_dwc3_handle_events(dev);
+ usb_dwc3_cdc_start_bulk_in_xfer(dev, ep);
+ }
+}
+
+size_t usb_dwc3_queue(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe, const void *buf, size_t count)
+{
+ const u8 *p = buf;
+ size_t wrote, sent = 0;
+
+ if (!dev || !dev->pipe[pipe].ready)
+ return 0;
+
+ ringbuffer_t *device2host = dev->pipe[pipe].device2host;
+ if (!device2host)
+ return 0;
+
+ u8 ep = dev->pipe[pipe].ep_in;
+
+ while (count) {
+ wrote = ringbuffer_write(p, count, device2host);
+ count -= wrote;
+ p += wrote;
+ sent += wrote;
+ if (count) {
+ usb_dwc3_handle_events(dev);
+ usb_dwc3_cdc_start_bulk_in_xfer(dev, ep);
+ }
+ }
+
+ return sent;
+}
+
+size_t usb_dwc3_write(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe, const void *buf, size_t count)
+{
+ if (!dev)
+ return -1;
+
+ u8 ep = dev->pipe[pipe].ep_in;
+ size_t ret = usb_dwc3_queue(dev, pipe, buf, count);
+
+ usb_dwc3_cdc_start_bulk_in_xfer(dev, ep);
+
+ return ret;
+}
+
+size_t usb_dwc3_read(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe, void *buf, size_t count)
+{
+ u8 *p = buf;
+ size_t read, recvd = 0;
+
+ if (!dev || !dev->pipe[pipe].ready)
+ return 0;
+
+ ringbuffer_t *host2device = dev->pipe[pipe].host2device;
+ if (!host2device)
+ return 0;
+
+ u8 ep = dev->pipe[pipe].ep_out;
+
+ while (count) {
+ read = ringbuffer_read(p, count, host2device);
+ count -= read;
+ p += read;
+ recvd += read;
+ usb_dwc3_handle_events(dev);
+ usb_dwc3_cdc_start_bulk_out_xfer(dev, ep);
+ }
+
+ return recvd;
+}
+
+ssize_t usb_dwc3_can_read(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe)
+{
+ if (!dev || !dev->pipe[pipe].ready)
+ return 0;
+
+ ringbuffer_t *host2device = dev->pipe[pipe].host2device;
+ if (!host2device)
+ return 0;
+
+ return ringbuffer_get_used(host2device);
+}
+
+bool usb_dwc3_can_write(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe)
+{
+ (void)pipe;
+ if (!dev)
+ return false;
+
+ return dev->pipe[pipe].ready;
+}
+
+void usb_dwc3_flush(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe)
+{
+ if (!dev || !dev->pipe[pipe].ready)
+ return;
+
+ ringbuffer_t *device2host = dev->pipe[pipe].device2host;
+ if (!device2host)
+ return;
+
+ u8 ep = dev->pipe[pipe].ep_in;
+
+ while (ringbuffer_get_used(device2host) != 0 || dev->endpoints[ep].xfer_in_progress) {
+ usb_dwc3_handle_events(dev);
+ }
+}
diff --git a/tools/src/usb_dwc3.h b/tools/src/usb_dwc3.h
new file mode 100644
index 0000000..6b23c7c
--- /dev/null
+++ b/tools/src/usb_dwc3.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef USB_DWC3_H
+#define USB_DWC3_H
+
+#include "dart.h"
+#include "types.h"
+
+typedef struct dwc3_dev dwc3_dev_t;
+
+typedef enum _cdc_acm_pipe_id_t {
+ CDC_ACM_PIPE_0,
+ CDC_ACM_PIPE_1,
+ CDC_ACM_PIPE_MAX
+} cdc_acm_pipe_id_t;
+
+dwc3_dev_t *usb_dwc3_init(uintptr_t regs, dart_dev_t *dart);
+void usb_dwc3_shutdown(dwc3_dev_t *dev);
+
+void usb_dwc3_handle_events(dwc3_dev_t *dev);
+
+ssize_t usb_dwc3_can_read(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe);
+bool usb_dwc3_can_write(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe);
+
+u8 usb_dwc3_getbyte(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe);
+void usb_dwc3_putbyte(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe, u8 byte);
+
+size_t usb_dwc3_read(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe, void *buf, size_t count);
+size_t usb_dwc3_write(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe, const void *buf, size_t count);
+size_t usb_dwc3_queue(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe, const void *buf, size_t count);
+void usb_dwc3_flush(dwc3_dev_t *dev, cdc_acm_pipe_id_t pipe);
+
+#endif
diff --git a/tools/src/usb_dwc3_regs.h b/tools/src/usb_dwc3_regs.h
new file mode 100644
index 0000000..9c3d9ca
--- /dev/null
+++ b/tools/src/usb_dwc3_regs.h
@@ -0,0 +1,625 @@
+/**
+ * core.h - DesignWare USB3 DRD Core Header
+ * linux commit 7bc5a6ba369217e0137833f5955cf0b0f08b0712 before
+ * the switch to GPLv2 only
+ *
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Felipe Balbi <balbi@ti.com>,
+ * Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2, as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __DRIVERS_USB_DWC3_CORE_H
+#define __DRIVERS_USB_DWC3_CORE_H
+
+#include "types.h"
+
+/* Global constants */
+#define DWC3_EP0_BOUNCE_SIZE 512
+#define DWC3_ENDPOINTS_NUM 32
+#define DWC3_XHCI_RESOURCES_NUM 2
+
+#define DWC3_EVENT_SIZE 4 /* bytes */
+#define DWC3_EVENT_MAX_NUM 64 /* 2 events/endpoint */
+#define DWC3_EVENT_BUFFERS_SIZE (DWC3_EVENT_SIZE * DWC3_EVENT_MAX_NUM)
+#define DWC3_EVENT_TYPE_MASK 0xfe
+
+#define DWC3_EVENT_TYPE_DEV 0
+#define DWC3_EVENT_TYPE_CARKIT 3
+#define DWC3_EVENT_TYPE_I2C 4
+
+#define DWC3_DEVICE_EVENT_DISCONNECT 0
+#define DWC3_DEVICE_EVENT_RESET 1
+#define DWC3_DEVICE_EVENT_CONNECT_DONE 2
+#define DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE 3
+#define DWC3_DEVICE_EVENT_WAKEUP 4
+#define DWC3_DEVICE_EVENT_HIBER_REQ 5
+#define DWC3_DEVICE_EVENT_EOPF 6
+#define DWC3_DEVICE_EVENT_SOF 7
+#define DWC3_DEVICE_EVENT_ERRATIC_ERROR 9
+#define DWC3_DEVICE_EVENT_CMD_CMPL 10
+#define DWC3_DEVICE_EVENT_OVERFLOW 11
+
+#define DWC3_GEVNTCOUNT_MASK 0xfffc
+#define DWC3_GSNPSID_MASK 0xffff0000
+#define DWC3_GSNPSREV_MASK 0xffff
+
+/* DWC3 registers memory space boundries */
+#define DWC3_XHCI_REGS_START 0x0
+#define DWC3_XHCI_REGS_END 0x7fff
+#define DWC3_GLOBALS_REGS_START 0xc100
+#define DWC3_GLOBALS_REGS_END 0xc6ff
+#define DWC3_DEVICE_REGS_START 0xc700
+#define DWC3_DEVICE_REGS_END 0xcbff
+#define DWC3_OTG_REGS_START 0xcc00
+#define DWC3_OTG_REGS_END 0xccff
+
+/* Global Registers */
+#define DWC3_GSBUSCFG0 0xc100
+#define DWC3_GSBUSCFG1 0xc104
+#define DWC3_GTXTHRCFG 0xc108
+#define DWC3_GRXTHRCFG 0xc10c
+#define DWC3_GCTL 0xc110
+#define DWC3_GEVTEN 0xc114
+#define DWC3_GSTS 0xc118
+#define DWC3_GSNPSID 0xc120
+#define DWC3_GGPIO 0xc124
+#define DWC3_GUID 0xc128
+#define DWC3_GUCTL 0xc12c
+#define DWC3_GBUSERRADDR0 0xc130
+#define DWC3_GBUSERRADDR1 0xc134
+#define DWC3_GPRTBIMAP0 0xc138
+#define DWC3_GPRTBIMAP1 0xc13c
+#define DWC3_GHWPARAMS0 0xc140
+#define DWC3_GHWPARAMS1 0xc144
+#define DWC3_GHWPARAMS2 0xc148
+#define DWC3_GHWPARAMS3 0xc14c
+#define DWC3_GHWPARAMS4 0xc150
+#define DWC3_GHWPARAMS5 0xc154
+#define DWC3_GHWPARAMS6 0xc158
+#define DWC3_GHWPARAMS7 0xc15c
+#define DWC3_GDBGFIFOSPACE 0xc160
+#define DWC3_GDBGLTSSM 0xc164
+#define DWC3_GPRTBIMAP_HS0 0xc180
+#define DWC3_GPRTBIMAP_HS1 0xc184
+#define DWC3_GPRTBIMAP_FS0 0xc188
+#define DWC3_GPRTBIMAP_FS1 0xc18c
+
+#define DWC3_GUSB2PHYCFG(n) (0xc200 + (n * 0x04))
+#define DWC3_GUSB2I2CCTL(n) (0xc240 + (n * 0x04))
+
+#define DWC3_GUSB2PHYACC(n) (0xc280 + (n * 0x04))
+
+#define DWC3_GUSB3PIPECTL(n) (0xc2c0 + (n * 0x04))
+
+#define DWC3_GTXFIFOSIZ(n) (0xc300 + (n * 0x04))
+#define DWC3_GRXFIFOSIZ(n) (0xc380 + (n * 0x04))
+
+#define DWC3_GEVNTADRLO(n) (0xc400 + (n * 0x10))
+#define DWC3_GEVNTADRHI(n) (0xc404 + (n * 0x10))
+#define DWC3_GEVNTSIZ(n) (0xc408 + (n * 0x10))
+#define DWC3_GEVNTCOUNT(n) (0xc40c + (n * 0x10))
+
+#define DWC3_GHWPARAMS8 0xc600
+
+/* Device Registers */
+#define DWC3_DCFG 0xc700
+#define DWC3_DCTL 0xc704
+#define DWC3_DEVTEN 0xc708
+#define DWC3_DSTS 0xc70c
+#define DWC3_DGCMDPAR 0xc710
+#define DWC3_DGCMD 0xc714
+#define DWC3_DALEPENA 0xc720
+#define DWC3_DEPCMDPAR2(n) (0xc800 + (n * 0x10))
+#define DWC3_DEPCMDPAR1(n) (0xc804 + (n * 0x10))
+#define DWC3_DEPCMDPAR0(n) (0xc808 + (n * 0x10))
+#define DWC3_DEPCMD(n) (0xc80c + (n * 0x10))
+
+/* OTG Registers */
+#define DWC3_OCFG 0xcc00
+#define DWC3_OCTL 0xcc04
+#define DWC3_OEVT 0xcc08
+#define DWC3_OEVTEN 0xcc0C
+#define DWC3_OSTS 0xcc10
+
+/* Bit fields */
+
+/* Global Configuration Register */
+#define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19)
+#define DWC3_GCTL_U2RSTECN (1 << 16)
+#define DWC3_GCTL_RAMCLKSEL(x) (((x)&DWC3_GCTL_CLK_MASK) << 6)
+#define DWC3_GCTL_CLK_BUS (0)
+#define DWC3_GCTL_CLK_PIPE (1)
+#define DWC3_GCTL_CLK_PIPEHALF (2)
+#define DWC3_GCTL_CLK_MASK (3)
+
+#define DWC3_GCTL_PRTCAP(n) (((n) & (3 << 12)) >> 12)
+#define DWC3_GCTL_PRTCAPDIR(n) ((n) << 12)
+#define DWC3_GCTL_PRTCAP_HOST 1
+#define DWC3_GCTL_PRTCAP_DEVICE 2
+#define DWC3_GCTL_PRTCAP_OTG 3
+
+#define DWC3_GCTL_CORESOFTRESET (1 << 11)
+#define DWC3_GCTL_SCALEDOWN(n) ((n) << 4)
+#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3)
+#define DWC3_GCTL_DISSCRAMBLE (1 << 3)
+#define DWC3_GCTL_GBLHIBERNATIONEN (1 << 1)
+#define DWC3_GCTL_DSBLCLKGTNG (1 << 0)
+
+/* Global USB2 PHY Configuration Register */
+#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
+#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6)
+
+/* Global USB3 PIPE Control Register */
+#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
+#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17)
+
+/* Global TX Fifo Size Register */
+#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n)&0xffff)
+#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n)&0xffff0000)
+
+/* Global HWPARAMS1 Register */
+#define DWC3_GHWPARAMS1_EN_PWROPT(n) (((n) & (3 << 24)) >> 24)
+#define DWC3_GHWPARAMS1_EN_PWROPT_NO 0
+#define DWC3_GHWPARAMS1_EN_PWROPT_CLK 1
+#define DWC3_GHWPARAMS1_EN_PWROPT_HIB 2
+#define DWC3_GHWPARAMS1_PWROPT(n) ((n) << 24)
+#define DWC3_GHWPARAMS1_PWROPT_MASK DWC3_GHWPARAMS1_PWROPT(3)
+
+/* Global HWPARAMS4 Register */
+#define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13)
+#define DWC3_MAX_HIBER_SCRATCHBUFS 15
+
+/* Device Configuration Register */
+#define DWC3_DCFG_LPM_CAP (1 << 22)
+#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
+#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f)
+
+#define DWC3_DCFG_SPEED_MASK (7 << 0)
+#define DWC3_DCFG_SUPERSPEED (4 << 0)
+#define DWC3_DCFG_HIGHSPEED (0 << 0)
+#define DWC3_DCFG_FULLSPEED2 (1 << 0)
+#define DWC3_DCFG_LOWSPEED (2 << 0)
+#define DWC3_DCFG_FULLSPEED1 (3 << 0)
+
+#define DWC3_DCFG_LPM_CAP (1 << 22)
+
+/* Device Control Register */
+#define DWC3_DCTL_RUN_STOP (1 << 31)
+#define DWC3_DCTL_CSFTRST (1 << 30)
+#define DWC3_DCTL_LSFTRST (1 << 29)
+
+#define DWC3_DCTL_HIRD_THRES_MASK (0x1f << 24)
+#define DWC3_DCTL_HIRD_THRES(n) ((n) << 24)
+
+#define DWC3_DCTL_APPL1RES (1 << 23)
+
+/* These apply for core versions 1.87a and earlier */
+#define DWC3_DCTL_TRGTULST_MASK (0x0f << 17)
+#define DWC3_DCTL_TRGTULST(n) ((n) << 17)
+#define DWC3_DCTL_TRGTULST_U2 (DWC3_DCTL_TRGTULST(2))
+#define DWC3_DCTL_TRGTULST_U3 (DWC3_DCTL_TRGTULST(3))
+#define DWC3_DCTL_TRGTULST_SS_DIS (DWC3_DCTL_TRGTULST(4))
+#define DWC3_DCTL_TRGTULST_RX_DET (DWC3_DCTL_TRGTULST(5))
+#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6))
+
+/* These apply for core versions 1.94a and later */
+#define DWC3_DCTL_KEEP_CONNECT (1 << 19)
+#define DWC3_DCTL_L1_HIBER_EN (1 << 18)
+#define DWC3_DCTL_CRS (1 << 17)
+#define DWC3_DCTL_CSS (1 << 16)
+
+#define DWC3_DCTL_INITU2ENA (1 << 12)
+#define DWC3_DCTL_ACCEPTU2ENA (1 << 11)
+#define DWC3_DCTL_INITU1ENA (1 << 10)
+#define DWC3_DCTL_ACCEPTU1ENA (1 << 9)
+#define DWC3_DCTL_TSTCTRL_MASK (0xf << 1)
+
+#define DWC3_DCTL_ULSTCHNGREQ_MASK (0x0f << 5)
+#define DWC3_DCTL_ULSTCHNGREQ(n) (((n) << 5) & DWC3_DCTL_ULSTCHNGREQ_MASK)
+
+#define DWC3_DCTL_ULSTCHNG_NO_ACTION (DWC3_DCTL_ULSTCHNGREQ(0))
+#define DWC3_DCTL_ULSTCHNG_SS_DISABLED (DWC3_DCTL_ULSTCHNGREQ(4))
+#define DWC3_DCTL_ULSTCHNG_RX_DETECT (DWC3_DCTL_ULSTCHNGREQ(5))
+#define DWC3_DCTL_ULSTCHNG_SS_INACTIVE (DWC3_DCTL_ULSTCHNGREQ(6))
+#define DWC3_DCTL_ULSTCHNG_RECOVERY (DWC3_DCTL_ULSTCHNGREQ(8))
+#define DWC3_DCTL_ULSTCHNG_COMPLIANCE (DWC3_DCTL_ULSTCHNGREQ(10))
+#define DWC3_DCTL_ULSTCHNG_LOOPBACK (DWC3_DCTL_ULSTCHNGREQ(11))
+
+/* Device Event Enable Register */
+#define DWC3_DEVTEN_VNDRDEVTSTRCVEDEN (1 << 12)
+#define DWC3_DEVTEN_EVNTOVERFLOWEN (1 << 11)
+#define DWC3_DEVTEN_CMDCMPLTEN (1 << 10)
+#define DWC3_DEVTEN_ERRTICERREN (1 << 9)
+#define DWC3_DEVTEN_SOFEN (1 << 7)
+#define DWC3_DEVTEN_EOPFEN (1 << 6)
+#define DWC3_DEVTEN_HIBERNATIONREQEVTEN (1 << 5)
+#define DWC3_DEVTEN_WKUPEVTEN (1 << 4)
+#define DWC3_DEVTEN_ULSTCNGEN (1 << 3)
+#define DWC3_DEVTEN_CONNECTDONEEN (1 << 2)
+#define DWC3_DEVTEN_USBRSTEN (1 << 1)
+#define DWC3_DEVTEN_DISCONNEVTEN (1 << 0)
+
+/* Device Status Register */
+#define DWC3_DSTS_DCNRD (1 << 29)
+
+/* This applies for core versions 1.87a and earlier */
+#define DWC3_DSTS_PWRUPREQ (1 << 24)
+
+/* These apply for core versions 1.94a and later */
+#define DWC3_DSTS_RSS (1 << 25)
+#define DWC3_DSTS_SSS (1 << 24)
+
+#define DWC3_DSTS_COREIDLE (1 << 23)
+#define DWC3_DSTS_DEVCTRLHLT (1 << 22)
+
+#define DWC3_DSTS_USBLNKST_MASK (0x0f << 18)
+#define DWC3_DSTS_USBLNKST(n) (((n)&DWC3_DSTS_USBLNKST_MASK) >> 18)
+
+#define DWC3_DSTS_RXFIFOEMPTY (1 << 17)
+
+#define DWC3_DSTS_SOFFN_MASK (0x3fff << 3)
+#define DWC3_DSTS_SOFFN(n) (((n)&DWC3_DSTS_SOFFN_MASK) >> 3)
+
+#define DWC3_DSTS_CONNECTSPD (7 << 0)
+
+#define DWC3_DSTS_SUPERSPEED (4 << 0)
+#define DWC3_DSTS_HIGHSPEED (0 << 0)
+#define DWC3_DSTS_FULLSPEED2 (1 << 0)
+#define DWC3_DSTS_LOWSPEED (2 << 0)
+#define DWC3_DSTS_FULLSPEED1 (3 << 0)
+
+/* Device Generic Command Register */
+#define DWC3_DGCMD_SET_LMP 0x01
+#define DWC3_DGCMD_SET_PERIODIC_PAR 0x02
+#define DWC3_DGCMD_XMIT_FUNCTION 0x03
+
+/* These apply for core versions 1.94a and later */
+#define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO 0x04
+#define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI 0x05
+
+#define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09
+#define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a
+#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c
+#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10
+
+#define DWC3_DGCMD_STATUS(n) (((n) >> 15) & 1)
+#define DWC3_DGCMD_CMDACT (1 << 10)
+#define DWC3_DGCMD_CMDIOC (1 << 8)
+
+/* Device Generic Command Parameter Register */
+#define DWC3_DGCMDPAR_FORCE_LINKPM_ACCEPT (1 << 0)
+#define DWC3_DGCMDPAR_FIFO_NUM(n) ((n) << 0)
+#define DWC3_DGCMDPAR_RX_FIFO (0 << 5)
+#define DWC3_DGCMDPAR_TX_FIFO (1 << 5)
+#define DWC3_DGCMDPAR_LOOPBACK_DIS (0 << 0)
+#define DWC3_DGCMDPAR_LOOPBACK_ENA (1 << 0)
+
+/* Device Endpoint Command Register */
+#define DWC3_DEPCMD_PARAM_SHIFT 16
+#define DWC3_DEPCMD_PARAM(x) ((x) << DWC3_DEPCMD_PARAM_SHIFT)
+#define DWC3_DEPCMD_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f)
+#define DWC3_DEPCMD_STATUS(x) (((x) >> 15) & 1)
+#define DWC3_DEPCMD_HIPRI_FORCERM (1 << 11)
+#define DWC3_DEPCMD_CMDACT (1 << 10)
+#define DWC3_DEPCMD_CMDIOC (1 << 8)
+
+#define DWC3_DEPCMD_DEPSTARTCFG (0x09 << 0)
+#define DWC3_DEPCMD_ENDTRANSFER (0x08 << 0)
+#define DWC3_DEPCMD_UPDATETRANSFER (0x07 << 0)
+#define DWC3_DEPCMD_STARTTRANSFER (0x06 << 0)
+#define DWC3_DEPCMD_CLEARSTALL (0x05 << 0)
+#define DWC3_DEPCMD_SETSTALL (0x04 << 0)
+/* This applies for core versions 1.90a and earlier */
+#define DWC3_DEPCMD_GETSEQNUMBER (0x03 << 0)
+/* This applies for core versions 1.94a and later */
+#define DWC3_DEPCMD_GETEPSTATE (0x03 << 0)
+#define DWC3_DEPCMD_SETTRANSFRESOURCE (0x02 << 0)
+#define DWC3_DEPCMD_SETEPCONFIG (0x01 << 0)
+
+/* The EP number goes 0..31 so ep0 is always out and ep1 is always in */
+#define DWC3_DALEPENA_EP(n) (1 << n)
+
+#define DWC3_DEPCMD_TYPE_CONTROL 0
+#define DWC3_DEPCMD_TYPE_ISOC 1
+#define DWC3_DEPCMD_TYPE_BULK 2
+#define DWC3_DEPCMD_TYPE_INTR 3
+
+#define DWC3_EVENT_PENDING BIT(0)
+
+#define DWC3_EP_FLAG_STALLED (1 << 0)
+#define DWC3_EP_FLAG_WEDGED (1 << 1)
+
+#define DWC3_EP_DIRECTION_TX true
+#define DWC3_EP_DIRECTION_RX false
+
+#define DWC3_TRB_NUM 32
+#define DWC3_TRB_MASK (DWC3_TRB_NUM - 1)
+
+#define DWC3_EP_ENABLED (1 << 0)
+#define DWC3_EP_STALL (1 << 1)
+#define DWC3_EP_WEDGE (1 << 2)
+#define DWC3_EP_BUSY (1 << 4)
+#define DWC3_EP_PENDING_REQUEST (1 << 5)
+#define DWC3_EP_MISSED_ISOC (1 << 6)
+
+/* This last one is specific to EP0 */
+#define DWC3_EP0_DIR_IN (1 << 31)
+
+enum dwc3_link_state {
+ /* In SuperSpeed */
+ DWC3_LINK_STATE_U0 = 0x00, /* in HS, means ON */
+ DWC3_LINK_STATE_U1 = 0x01,
+ DWC3_LINK_STATE_U2 = 0x02, /* in HS, means SLEEP */
+ DWC3_LINK_STATE_U3 = 0x03, /* in HS, means SUSPEND */
+ DWC3_LINK_STATE_SS_DIS = 0x04,
+ DWC3_LINK_STATE_RX_DET = 0x05, /* in HS, means Early Suspend */
+ DWC3_LINK_STATE_SS_INACT = 0x06,
+ DWC3_LINK_STATE_POLL = 0x07,
+ DWC3_LINK_STATE_RECOV = 0x08,
+ DWC3_LINK_STATE_HRESET = 0x09,
+ DWC3_LINK_STATE_CMPLY = 0x0a,
+ DWC3_LINK_STATE_LPBK = 0x0b,
+ DWC3_LINK_STATE_RESET = 0x0e,
+ DWC3_LINK_STATE_RESUME = 0x0f,
+ DWC3_LINK_STATE_MASK = 0x0f,
+};
+
+/* TRB Length, PCM and Status */
+#define DWC3_TRB_SIZE_MASK (0x00ffffff)
+#define DWC3_TRB_SIZE_LENGTH(n) ((n)&DWC3_TRB_SIZE_MASK)
+#define DWC3_TRB_SIZE_PCM1(n) (((n)&0x03) << 24)
+#define DWC3_TRB_SIZE_TRBSTS(n) (((n) & (0x0f << 28)) >> 28)
+
+#define DWC3_TRBSTS_OK 0
+#define DWC3_TRBSTS_MISSED_ISOC 1
+#define DWC3_TRBSTS_SETUP_PENDING 2
+#define DWC3_TRB_STS_XFER_IN_PROG 4
+
+/* TRB Control */
+#define DWC3_TRB_CTRL_HWO (1 << 0)
+#define DWC3_TRB_CTRL_LST (1 << 1)
+#define DWC3_TRB_CTRL_CHN (1 << 2)
+#define DWC3_TRB_CTRL_CSP (1 << 3)
+#define DWC3_TRB_CTRL_TRBCTL(n) (((n)&0x3f) << 4)
+#define DWC3_TRB_CTRL_ISP_IMI (1 << 10)
+#define DWC3_TRB_CTRL_IOC (1 << 11)
+#define DWC3_TRB_CTRL_SID_SOFN(n) (((n)&0xffff) << 14)
+
+#define DWC3_TRBCTL_NORMAL DWC3_TRB_CTRL_TRBCTL(1)
+#define DWC3_TRBCTL_CONTROL_SETUP DWC3_TRB_CTRL_TRBCTL(2)
+#define DWC3_TRBCTL_CONTROL_STATUS2 DWC3_TRB_CTRL_TRBCTL(3)
+#define DWC3_TRBCTL_CONTROL_STATUS3 DWC3_TRB_CTRL_TRBCTL(4)
+#define DWC3_TRBCTL_CONTROL_DATA DWC3_TRB_CTRL_TRBCTL(5)
+#define DWC3_TRBCTL_ISOCHRONOUS_FIRST DWC3_TRB_CTRL_TRBCTL(6)
+#define DWC3_TRBCTL_ISOCHRONOUS DWC3_TRB_CTRL_TRBCTL(7)
+#define DWC3_TRBCTL_LINK_TRB DWC3_TRB_CTRL_TRBCTL(8)
+
+/**
+ * struct dwc3_trb - transfer request block (hw format)
+ * @bpl: DW0-3
+ * @bph: DW4-7
+ * @size: DW8-B
+ * @trl: DWC-F
+ */
+struct dwc3_trb {
+ u32 bpl;
+ u32 bph;
+ u32 size;
+ u32 ctrl;
+} PACKED;
+
+/* HWPARAMS0 */
+#define DWC3_MODE(n) ((n)&0x7)
+
+#define DWC3_MODE_DEVICE 0
+#define DWC3_MODE_HOST 1
+#define DWC3_MODE_DRD 2
+#define DWC3_MODE_HUB 3
+
+#define DWC3_MDWIDTH(n) (((n)&0xff00) >> 8)
+
+/* HWPARAMS1 */
+#define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15)
+
+/* HWPARAMS3 */
+#define DWC3_NUM_IN_EPS_MASK (0x1f << 18)
+#define DWC3_NUM_EPS_MASK (0x3f << 12)
+#define DWC3_NUM_EPS(p) (((p)->hwparams3 & (DWC3_NUM_EPS_MASK)) >> 12)
+#define DWC3_NUM_IN_EPS(p) (((p)->hwparams3 & (DWC3_NUM_IN_EPS_MASK)) >> 18)
+
+/* HWPARAMS7 */
+#define DWC3_RAM1_DEPTH(n) ((n)&0xffff)
+
+#define DWC3_REVISION_173A 0x5533173a
+#define DWC3_REVISION_175A 0x5533175a
+#define DWC3_REVISION_180A 0x5533180a
+#define DWC3_REVISION_183A 0x5533183a
+#define DWC3_REVISION_185A 0x5533185a
+#define DWC3_REVISION_187A 0x5533187a
+#define DWC3_REVISION_188A 0x5533188a
+#define DWC3_REVISION_190A 0x5533190a
+#define DWC3_REVISION_194A 0x5533194a
+#define DWC3_REVISION_200A 0x5533200a
+#define DWC3_REVISION_202A 0x5533202a
+#define DWC3_REVISION_210A 0x5533210a
+#define DWC3_REVISION_220A 0x5533220a
+#define DWC3_REVISION_230A 0x5533230a
+#define DWC3_REVISION_240A 0x5533240a
+#define DWC3_REVISION_250A 0x5533250a
+
+/* -------------------------------------------------------------------------- */
+
+/* -------------------------------------------------------------------------- */
+
+struct dwc3_event_type {
+ u32 is_devspec : 1;
+ u32 type : 7;
+ u32 reserved8_31 : 24;
+} PACKED;
+
+#define DWC3_DEPEVT_XFERCOMPLETE 0x01
+#define DWC3_DEPEVT_XFERINPROGRESS 0x02
+#define DWC3_DEPEVT_XFERNOTREADY 0x03
+#define DWC3_DEPEVT_RXTXFIFOEVT 0x04
+#define DWC3_DEPEVT_STREAMEVT 0x06
+#define DWC3_DEPEVT_EPCMDCMPLT 0x07
+
+/**
+ * struct dwc3_event_depvt - Device Endpoint Events
+ * @one_bit: indicates this is an endpoint event (not used)
+ * @endpoint_number: number of the endpoint
+ * @endpoint_event: The event we have:
+ * 0x00 - Reserved
+ * 0x01 - XferComplete
+ * 0x02 - XferInProgress
+ * 0x03 - XferNotReady
+ * 0x04 - RxTxFifoEvt (IN->Underrun, OUT->Overrun)
+ * 0x05 - Reserved
+ * 0x06 - StreamEvt
+ * 0x07 - EPCmdCmplt
+ * @reserved11_10: Reserved, don't use.
+ * @status: Indicates the status of the event. Refer to databook for
+ * more information.
+ * @parameters: Parameters of the current event. Refer to databook for
+ * more information.
+ */
+struct dwc3_event_depevt {
+ u32 one_bit : 1;
+ u32 endpoint_number : 5;
+ u32 endpoint_event : 4;
+ u32 reserved11_10 : 2;
+ u32 status : 4;
+
+/* Within XferNotReady */
+#define DEPEVT_STATUS_TRANSFER_ACTIVE (1 << 3)
+
+/* Within XferComplete */
+#define DEPEVT_STATUS_BUSERR (1 << 0)
+#define DEPEVT_STATUS_SHORT (1 << 1)
+#define DEPEVT_STATUS_IOC (1 << 2)
+#define DEPEVT_STATUS_LST (1 << 3)
+
+/* Stream event only */
+#define DEPEVT_STREAMEVT_FOUND 1
+#define DEPEVT_STREAMEVT_NOTFOUND 2
+
+/* Control-only Status */
+#define DEPEVT_STATUS_CONTROL_DATA 1
+#define DEPEVT_STATUS_CONTROL_STATUS 2
+
+ u32 parameters : 16;
+} PACKED;
+
+#define DWC3_DEVT_DISCONN 0x00
+#define DWC3_DEVT_USBRST 0x01
+#define DWC3_DEVT_CONNECTDONE 0x02
+#define DWC3_DEVT_ULSTCHNG 0x03
+#define DWC3_DEVT_WKUPEVT 0x04
+#define DWC3_DEVT_EOPF 0x06
+#define DWC3_DEVT_SOF 0x07
+#define DWC3_DEVT_ERRTICERR 0x09
+#define DWC3_DEVT_CMDCMPLT 0x0a
+#define DWC3_DEVT_EVNTOVERFLOW 0x0b
+#define DWC3_DEVT_VNDRDEVTSTRCVED 0x0c
+
+/**
+ * struct dwc3_event_devt - Device Events
+ * @one_bit: indicates this is a non-endpoint event (not used)
+ * @device_event: indicates it's a device event. Should read as 0x00
+ * @type: indicates the type of device event.
+ * 0 - DisconnEvt
+ * 1 - USBRst
+ * 2 - ConnectDone
+ * 3 - ULStChng
+ * 4 - WkUpEvt
+ * 5 - Reserved
+ * 6 - EOPF
+ * 7 - SOF
+ * 8 - Reserved
+ * 9 - ErrticErr
+ * 10 - CmdCmplt
+ * 11 - EvntOverflow
+ * 12 - VndrDevTstRcved
+ * @reserved15_12: Reserved, not used
+ * @event_info: Information about this event
+ * @reserved31_24: Reserved, not used
+ */
+struct dwc3_event_devt {
+ u32 one_bit : 1;
+ u32 device_event : 7;
+ u32 type : 4;
+ u32 reserved15_12 : 4;
+ u32 event_info : 8;
+ u32 reserved31_24 : 8;
+} PACKED;
+
+/**
+ * struct dwc3_event_gevt - Other Core Events
+ * @one_bit: indicates this is a non-endpoint event (not used)
+ * @device_event: indicates it's (0x03) Carkit or (0x04) I2C event.
+ * @phy_port_number: self-explanatory
+ * @reserved31_12: Reserved, not used.
+ */
+struct dwc3_event_gevt {
+ u32 one_bit : 1;
+ u32 device_event : 7;
+ u32 phy_port_number : 4;
+ u32 reserved31_12 : 20;
+} PACKED;
+
+union dwc3_event {
+ u32 raw;
+ struct dwc3_event_type type;
+ struct dwc3_event_depevt depevt;
+ struct dwc3_event_devt devt;
+ struct dwc3_event_gevt gevt;
+};
+
+#define DWC3_DEPCFG_EP_TYPE(n) (((n)&0x3) << 1)
+#define DWC3_DEPCFG_EP_NUMBER(n) (((n)&0x1f) << 25)
+#define DWC3_DEPCFG_FIFO_NUMBER(n) (((n)&0xf) << 17)
+#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) (((n)&0x7ff) << 3)
+
+#define DWC3_DEPCFG_INT_NUM(n) (((n)&0x1f) << 0)
+#define DWC3_DEPCFG_XFER_COMPLETE_EN BIT(8)
+#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN BIT(9)
+#define DWC3_DEPCFG_XFER_NOT_READY_EN BIT(10)
+#define DWC3_DEPCFG_FIFO_ERROR_EN BIT(11)
+#define DWC3_DEPCFG_STREAM_EVENT_EN BIT(13)
+#define DWC3_DEPCFG_BINTERVAL_M1(n) (((n)&0xff) << 16)
+#define DWC3_DEPCFG_STREAM_CAPABLE BIT(24)
+#define DWC3_DEPCFG_EP_NUMBER(n) (((n)&0x1f) << 25)
+#define DWC3_DEPCFG_BULK_BASED BIT(30)
+#define DWC3_DEPCFG_FIFO_BASED BIT(31)
+
+#endif /* __DRIVERS_USB_DWC3_CORE_H */
diff --git a/tools/src/usb_types.h b/tools/src/usb_types.h
new file mode 100644
index 0000000..2571a1a
--- /dev/null
+++ b/tools/src/usb_types.h
@@ -0,0 +1,209 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef USB_TYPES_H
+#define USB_TYPES_H
+
+#include "types.h"
+
+#define USB_REQUEST_TYPE_DIRECTION_SHIFT 7
+#define USB_REQUEST_TYPE_DIRECTION(d) ((d) << USB_REQUEST_TYPE_DIRECTION_SHIFT)
+#define USB_REQUEST_TYPE_DIRECTION_HOST2DEVICE 0
+#define USB_REQUEST_TYPE_DIRECTION_DEVICE2HOST 1
+
+#define USB_REQUEST_TYPE_SHIFT 5
+#define USB_REQUEST_TYPE(t) ((t) << USB_REQUEST_TYPE_SHIFT)
+#define USB_REQUEST_TYPE_STANDARD USB_REQUEST_TYPE(0b00)
+#define USB_REQUEST_TYPE_CLASS USB_REQUEST_TYPE(0b01)
+#define USB_REQUEST_TYPE_VENDOR USB_REQUEST_TYPE(0b10)
+#define USB_REQUEST_TYPE_MASK USB_REQUEST_TYPE(0b11)
+
+#define USB_REQUEST_TYPE_RECIPIENT_DEVICE 0
+#define USB_REQUEST_TYPE_RECIPIENT_INTERFACE 1
+#define USB_REQUEST_TYPE_RECIPIENT_ENDPOINT 2
+#define USB_REQUEST_TYPE_RECIPIENT_OTHER 3
+#define USB_REQUEST_TYPE_RECIPIENT_MASK 0b11
+
+#define USB_REQUEST_GET_STATUS 0x00
+#define USB_REQUEST_CLEAR_FEATURE 0x01
+#define USB_REQUEST_SET_FEATURE 0x03
+#define USB_REQUEST_SET_ADDRESS 0x05
+#define USB_REQUEST_GET_DESCRIPTOR 0x06
+#define USB_REQUEST_SET_DESCRIPTOR 0x07
+#define USB_REQUEST_GET_CONFIGURATION 0x08
+#define USB_REQUEST_SET_CONFIGURATION 0x09
+
+#define USB_EP_REQUEST_CLEAR_FEATURE 0x01
+#define USB_EP_REQUEST_SET_FEATURE 0x03
+
+#define USB_FEATURE_ENDPOINT_HALT 0x00
+
+#define USB_REQUEST_CDC_SET_LINE_CODING 0x20
+#define USB_REQUEST_CDC_GET_LINE_CODING 0x21
+#define USB_REQUEST_CDC_SET_CTRL_LINE_STATE 0x22
+
+struct usb_setup_packet_raw {
+ u8 bmRequestType;
+ u8 bRequest;
+ u16 wValue;
+ u16 wIndex;
+ u16 wLength;
+} PACKED;
+
+struct usb_setup_packet_get_descriptor {
+ u8 bmRequestType;
+ u8 bRequest;
+ u8 index;
+ u8 type;
+ u16 language;
+ u16 wLength;
+} PACKED;
+
+struct usb_set_packet_set_address {
+ u8 bmRequestType;
+ u8 bRequest;
+ u16 address;
+ u16 zero0;
+ u16 zero1;
+} PACKED;
+
+struct usb_set_packet_set_configuration {
+ u8 bmRequestType;
+ u8 bRequest;
+ u16 configuration;
+ u16 zero0;
+ u16 zero1;
+} PACKED;
+
+struct usb_setup_packet_feature {
+ u8 bmRequestType;
+ u8 bRequest;
+ u16 wFeatureSelector;
+ u16 wEndpoint;
+ u16 wLength;
+} PACKED;
+
+union usb_setup_packet {
+ struct usb_setup_packet_raw raw;
+ struct usb_setup_packet_get_descriptor get_descriptor;
+ struct usb_set_packet_set_address set_address;
+ struct usb_set_packet_set_configuration set_configuration;
+ struct usb_setup_packet_feature feature;
+};
+
+#define USB_DEVICE_DESCRIPTOR 0x01
+#define USB_CONFIGURATION_DESCRIPTOR 0x02
+#define USB_STRING_DESCRIPTOR 0x03
+#define USB_INTERFACE_DESCRIPTOR 0x04
+#define USB_ENDPOINT_DESCRIPTOR 0x05
+#define USB_DEVICE_QUALIFIER_DESCRIPTOR 0x06
+#define USB_OTHER_SPEED_CONFIGURATION_DESCRIPTOR 0x07
+
+#define USB_CDC_INTERFACE_FUNCTIONAL_DESCRIPTOR 0x24
+#define USB_CDC_UNION_SUBTYPE 0x06
+
+#define USB_CONFIGURATION_SELF_POWERED 0x40
+#define USB_CONFIGURATION_ATTRIBUTE_RES1 0x80
+
+#define USB_ENDPOINT_ADDR_IN(ep) (0x80 | (ep))
+#define USB_ENDPOINT_ADDR_OUT(ep) (0x00 | (ep))
+
+#define USB_ENDPOINT_ATTR_TYPE_CONTROL 0b00
+#define USB_ENDPOINT_ATTR_TYPE_ISOCHRONOUS 0b01
+#define USB_ENDPOINT_ATTR_TYPE_BULK 0b10
+#define USB_ENDPOINT_ATTR_TYPE_INTERRUPT 0b11
+
+#define USB_LANGID_EN_US 0x0409
+
+struct usb_device_descriptor {
+ u8 bLength;
+ u8 bDescriptorType;
+ u16 bcdUSB;
+ u8 bDeviceClass;
+ u8 bDeviceSubClass;
+ u8 bDeviceProtocol;
+ u8 bMaxPacketSize0;
+ u16 idVendor;
+ u16 idProduct;
+ u16 bcdDevice;
+ u8 iManufacturer;
+ u8 iProduct;
+ u8 iSerialNumber;
+ u8 bNumConfigurations;
+} PACKED;
+
+struct usb_configuration_descriptor {
+ u8 bLength;
+ u8 bDescriptorType;
+ u16 wTotalLength;
+ u8 bNumInterfaces;
+ u8 bConfigurationValue;
+ u8 iConfiguration;
+ u8 bmAttributes;
+ u8 bMaxPower;
+} PACKED;
+
+struct usb_interface_descriptor {
+ u8 bLength;
+ u8 bDescriptorType;
+ u8 bInterfaceNumber;
+ u8 bAlternateSetting;
+ u8 bNumEndpoints;
+ u8 bInterfaceClass;
+ u8 bInterfaceSubClass;
+ u8 bInterfaceProtocol;
+ u8 iInterface;
+} PACKED;
+
+struct usb_endpoint_descriptor {
+ u8 bLength;
+ u8 bDescriptorType;
+ u8 bEndpointAddress;
+ u8 bmAttributes;
+ u16 wMaxPacketSize;
+ u8 bInterval;
+} PACKED;
+
+struct usb_string_descriptor {
+ u8 bLength;
+ u8 bDescriptorType;
+ u16 bString[];
+} PACKED;
+
+struct usb_string_descriptor_languages {
+ u8 bLength;
+ u8 bDescriptorType;
+ u16 wLANGID[];
+} PACKED;
+
+struct cdc_union_functional_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype;
+ u8 bControlInterface;
+ u8 bDataInterface;
+} PACKED;
+
+struct usb_device_qualifier_descriptor {
+ u8 bLength;
+ u8 bDescriptorType;
+ u16 bcdUSB;
+ u8 bDeviceClass;
+ u8 bDeviceSubClass;
+ u8 bDeviceProtocol;
+ u8 bMaxPacketSize0;
+ u8 bNumConfigurations;
+ u8 bReserved;
+} PACKED;
+
+/*
+ * this macro is required because we need to convert any string literals
+ * to UTF16 and because we need to calculate the correct total size of the
+ * string descriptor.
+ */
+#define make_usb_string_descriptor(str) \
+ { \
+ .bLength = sizeof(struct usb_string_descriptor) + sizeof(u##str), \
+ .bDescriptorType = USB_STRING_DESCRIPTOR, .bString = u##str \
+ }
+
+#endif
diff --git a/tools/src/utils.c b/tools/src/utils.c
new file mode 100644
index 0000000..2343476
--- /dev/null
+++ b/tools/src/utils.c
@@ -0,0 +1,182 @@
+/* SPDX-License-Identifier: MIT */
+
+#include <assert.h>
+#include <stdarg.h>
+
+#include "utils.h"
+#include "iodev.h"
+#include "smp.h"
+#include "types.h"
+#include "vsprintf.h"
+#include "xnuboot.h"
+
+static char ascii(char s)
+{
+ if (s < 0x20)
+ return '.';
+ if (s > 0x7E)
+ return '.';
+ return s;
+}
+
+void hexdump(const void *d, size_t len)
+{
+ u8 *data;
+ size_t i, off;
+ data = (u8 *)d;
+ for (off = 0; off < len; off += 16) {
+ printf("%08lx ", off);
+ for (i = 0; i < 16; i++) {
+ if ((i + off) >= len)
+ printf(" ");
+ else
+ printf("%02x ", data[off + i]);
+ }
+
+ printf(" ");
+ for (i = 0; i < 16; i++) {
+ if ((i + off) >= len)
+ printf(" ");
+ else
+ printf("%c", ascii(data[off + i]));
+ }
+ printf("\n");
+ }
+}
+
+void regdump(u64 addr, size_t len)
+{
+ u64 i, off;
+ for (off = 0; off < len; off += 32) {
+ printf("%016lx ", addr + off);
+ for (i = 0; i < 32; i += 4) {
+ printf("%08x ", read32(addr + off + i));
+ }
+ printf("\n");
+ }
+}
+
+int snprintf(char *buffer, size_t size, const char *fmt, ...)
+{
+ va_list args;
+ int i;
+
+ va_start(args, fmt);
+ i = vsnprintf(buffer, size, fmt, args);
+ va_end(args);
+ return i;
+}
+
+int debug_printf(const char *fmt, ...)
+{
+ va_list args;
+ char buffer[512];
+ int i;
+
+ va_start(args, fmt);
+ i = vsnprintf(buffer, sizeof(buffer), fmt, args);
+ va_end(args);
+
+ iodev_console_write(buffer, min(i, (int)(sizeof(buffer) - 1)));
+
+ return i;
+}
+
+void __assert_fail(const char *assertion, const char *file, unsigned int line, const char *function)
+{
+ printf("Assertion failed: '%s' on %s:%d:%s\n", assertion, file, line, function);
+ flush_and_reboot();
+}
+
+void udelay(u32 d)
+{
+ u64 delay = ((u64)d) * mrs(CNTFRQ_EL0) / 1000000;
+ u64 val = mrs(CNTPCT_EL0);
+ while ((mrs(CNTPCT_EL0) - val) < delay)
+ ;
+ sysop("isb");
+}
+
+u64 ticks_to_msecs(u64 ticks)
+{
+ // NOTE: only accurate if freq is even kHz
+ return ticks / (mrs(CNTFRQ_EL0) / 1000);
+}
+
+u64 ticks_to_usecs(u64 ticks)
+{
+ // NOTE: only accurate if freq is even MHz
+ return ticks / (mrs(CNTFRQ_EL0) / 1000000);
+}
+
+u64 timeout_calculate(u32 usec)
+{
+ u64 delay = ((u64)usec) * mrs(CNTFRQ_EL0) / 1000000;
+ return mrs(CNTPCT_EL0) + delay;
+}
+
+bool timeout_expired(u64 timeout)
+{
+ bool expired = mrs(CNTPCT_EL0) > timeout;
+ sysop("isb");
+ return expired;
+}
+
+void flush_and_reboot(void)
+{
+ iodev_console_flush();
+ reboot();
+}
+
+void spin_init(spinlock_t *lock)
+{
+ lock->lock = -1;
+ lock->count = 0;
+}
+
+void spin_lock(spinlock_t *lock)
+{
+ s64 tmp;
+ s64 me = smp_id();
+ if (__atomic_load_n(&lock->lock, __ATOMIC_ACQUIRE) == me) {
+ lock->count++;
+ return;
+ }
+
+ __asm__ volatile("1:\n"
+ "mov\t%0, -1\n"
+ "2:\n"
+ "\tcasa\t%0, %2, %1\n"
+ "\tcmn\t%0, 1\n"
+ "\tbeq\t3f\n"
+ "\tldxr\t%0, %1\n"
+ "\tcmn\t%0, 1\n"
+ "\tbeq\t2b\n"
+ "\twfe\n"
+ "\tb\t1b\n"
+ "3:"
+ : "=&r"(tmp), "+m"(lock->lock)
+ : "r"(me)
+ : "cc", "memory");
+
+ assert(__atomic_load_n(&lock->lock, __ATOMIC_RELAXED) == me);
+ lock->count++;
+}
+
+void spin_unlock(spinlock_t *lock)
+{
+ s64 me = smp_id();
+ assert(__atomic_load_n(&lock->lock, __ATOMIC_RELAXED) == me);
+ assert(lock->count > 0);
+ if (!--lock->count)
+ __atomic_store_n(&lock->lock, -1L, __ATOMIC_RELEASE);
+}
+
+bool is_heap(void *addr)
+{
+ u64 p = (u64)addr;
+ u64 top_of_kernel_data = (u64)cur_boot_args.top_of_kernel_data;
+ u64 top_of_ram = cur_boot_args.mem_size + cur_boot_args.phys_base;
+
+ return p > top_of_kernel_data && p < top_of_ram;
+}
diff --git a/tools/src/utils.h b/tools/src/utils.h
new file mode 100644
index 0000000..1d053d2
--- /dev/null
+++ b/tools/src/utils.h
@@ -0,0 +1,444 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include "types.h"
+
+#define printf(...) debug_printf(__VA_ARGS__)
+
+#ifdef DEBUG
+#define dprintf(...) debug_printf(__VA_ARGS__)
+#else
+#define dprintf(...) \
+ do { \
+ } while (0)
+#endif
+
+#define ARRAY_SIZE(s) (sizeof(s) / sizeof((s)[0]))
+
+#define BIT(x) (1UL << (x))
+#define MASK(x) (BIT(x) - 1)
+#define GENMASK(msb, lsb) ((BIT((msb + 1) - (lsb)) - 1) << (lsb))
+#define _FIELD_LSB(field) ((field) & ~(field - 1))
+#define FIELD_PREP(field, val) ((val) * (_FIELD_LSB(field)))
+#define FIELD_GET(field, val) (((val) & (field)) / _FIELD_LSB(field))
+
+#define ALIGN_UP(x, a) (((x) + ((a)-1)) & ~((a)-1))
+#define ALIGN_DOWN(x, a) ((x) & ~((a)-1))
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+#define USEC_PER_SEC 1000000L
+
+static inline u64 read64(u64 addr)
+{
+ u64 data;
+ __asm__ volatile("ldr\t%0, [%1]" : "=r"(data) : "r"(addr) : "memory");
+ return data;
+}
+
+static inline void write64(u64 addr, u64 data)
+{
+ __asm__ volatile("str\t%0, [%1]" : : "r"(data), "r"(addr) : "memory");
+}
+
+static inline u64 set64(u64 addr, u64 set)
+{
+ u64 data;
+ __asm__ volatile("ldr\t%0, [%1]\n"
+ "\torr\t%0, %0, %2\n"
+ "\tstr\t%0, [%1]"
+ : "=&r"(data)
+ : "r"(addr), "r"(set)
+ : "memory");
+ return data;
+}
+
+static inline u64 clear64(u64 addr, u64 clear)
+{
+ u64 data;
+ __asm__ volatile("ldr\t%0, [%1]\n"
+ "\tbic\t%0, %0, %2\n"
+ "\tstr\t%0, [%1]"
+ : "=&r"(data)
+ : "r"(addr), "r"(clear)
+ : "memory");
+ return data;
+}
+
+static inline u64 mask64(u64 addr, u64 clear, u64 set)
+{
+ u64 data;
+ __asm__ volatile("ldr\t%0, [%1]\n"
+ "\tbic\t%0, %0, %3\n"
+ "\torr\t%0, %0, %2\n"
+ "\tstr\t%0, [%1]"
+ : "=&r"(data)
+ : "r"(addr), "r"(set), "r"(clear)
+ : "memory");
+ return data;
+}
+
+static inline u64 writeread64(u64 addr, u64 data)
+{
+ write64(addr, data);
+ return read64(addr);
+}
+
+static inline u32 read32(u64 addr)
+{
+ u32 data;
+ __asm__ volatile("ldr\t%w0, [%1]" : "=r"(data) : "r"(addr) : "memory");
+ return data;
+}
+
+static inline void write32(u64 addr, u32 data)
+{
+ __asm__ volatile("str\t%w0, [%1]" : : "r"(data), "r"(addr) : "memory");
+}
+
+static inline u32 writeread32(u64 addr, u32 data)
+{
+ write32(addr, data);
+ return read32(addr);
+}
+
+static inline u32 set32(u64 addr, u32 set)
+{
+ u32 data;
+ __asm__ volatile("ldr\t%w0, [%1]\n"
+ "\torr\t%w0, %w0, %w2\n"
+ "\tstr\t%w0, [%1]"
+ : "=&r"(data)
+ : "r"(addr), "r"(set)
+ : "memory");
+ return data;
+}
+
+static inline u32 clear32(u64 addr, u32 clear)
+{
+ u32 data;
+ __asm__ volatile("ldr\t%w0, [%1]\n"
+ "\tbic\t%w0, %w0, %w2\n"
+ "\tstr\t%w0, [%1]"
+ : "=&r"(data)
+ : "r"(addr), "r"(clear)
+ : "memory");
+ return data;
+}
+
+static inline u32 mask32(u64 addr, u32 clear, u32 set)
+{
+ u32 data;
+ __asm__ volatile("ldr\t%w0, [%1]\n"
+ "\tbic\t%w0, %w0, %w3\n"
+ "\torr\t%w0, %w0, %w2\n"
+ "\tstr\t%w0, [%1]"
+ : "=&r"(data)
+ : "r"(addr), "r"(set), "r"(clear)
+ : "memory");
+ return data;
+}
+
+static inline u16 read16(u64 addr)
+{
+ u32 data;
+ __asm__ volatile("ldrh\t%w0, [%1]" : "=r"(data) : "r"(addr) : "memory");
+ return data;
+}
+
+static inline void write16(u64 addr, u16 data)
+{
+ __asm__ volatile("strh\t%w0, [%1]" : : "r"(data), "r"(addr) : "memory");
+}
+
+static inline u16 set16(u64 addr, u16 set)
+{
+ u16 data;
+ __asm__ volatile("ldrh\t%w0, [%1]\n"
+ "\torr\t%w0, %w0, %w2\n"
+ "\tstrh\t%w0, [%1]"
+ : "=&r"(data)
+ : "r"(addr), "r"(set)
+ : "memory"
+
+ );
+ return data;
+}
+
+static inline u16 clear16(u64 addr, u16 clear)
+{
+ u16 data;
+ __asm__ volatile("ldrh\t%w0, [%1]\n"
+ "\tbic\t%w0, %w0, %w2\n"
+ "\tstrh\t%w0, [%1]"
+ : "=&r"(data)
+ : "r"(addr), "r"(clear)
+ : "memory");
+ return data;
+}
+
+static inline u16 mask16(u64 addr, u16 clear, u16 set)
+{
+ u16 data;
+ __asm__ volatile("ldrh\t%w0, [%1]\n"
+ "\tbic\t%w0, %w0, %w3\n"
+ "\torr\t%w0, %w0, %w2\n"
+ "\tstrh\t%w0, [%1]"
+ : "=&r"(data)
+ : "r"(addr), "r"(set), "r"(clear)
+ : "memory");
+ return data;
+}
+
+static inline u16 writeread16(u64 addr, u16 data)
+{
+ write16(addr, data);
+ return read16(addr);
+}
+
+static inline u8 read8(u64 addr)
+{
+ u32 data;
+ __asm__ volatile("ldrb\t%w0, [%1]" : "=r"(data) : "r"(addr) : "memory");
+ return data;
+}
+
+static inline void write8(u64 addr, u8 data)
+{
+ __asm__ volatile("strb\t%w0, [%1]" : : "r"(data), "r"(addr) : "memory");
+}
+
+static inline u8 set8(u64 addr, u8 set)
+{
+ u8 data;
+ __asm__ volatile("ldrb\t%w0, [%1]\n"
+ "\torr\t%w0, %w0, %w2\n"
+ "\tstrb\t%w0, [%1]"
+ : "=&r"(data)
+ : "r"(addr), "r"(set)
+ : "memory");
+ return data;
+}
+
+static inline u8 clear8(u64 addr, u8 clear)
+{
+ u8 data;
+ __asm__ volatile("ldrb\t%w0, [%1]\n"
+ "\tbic\t%w0, %w0, %w2\n"
+ "\tstrb\t%w0, [%1]"
+ : "=&r"(data)
+ : "r"(addr), "r"(clear)
+ : "memory");
+ return data;
+}
+
+static inline u8 mask8(u64 addr, u8 clear, u8 set)
+{
+ u8 data;
+ __asm__ volatile("ldrb\t%w0, [%1]\n"
+ "\tbic\t%w0, %w0, %w3\n"
+ "\torr\t%w0, %w0, %w2\n"
+ "\tstrb\t%w0, [%1]"
+ : "=&r"(data)
+ : "r"(addr), "r"(set), "r"(clear)
+ : "memory");
+ return data;
+}
+
+static inline u8 writeread8(u64 addr, u8 data)
+{
+ write8(addr, data);
+ return read8(addr);
+}
+
+static inline void write64_lo_hi(u64 addr, u64 val)
+{
+ write32(addr, val);
+ write32(addr + 4, val >> 32);
+}
+
+#define _concat(a, _1, b, ...) a##b
+
+#define _sr_tkn_S(_0, _1, op0, op1, CRn, CRm, op2) s##op0##_##op1##_c##CRn##_c##CRm##_##op2
+
+#define _sr_tkn(a) a
+
+#define sr_tkn(...) _concat(_sr_tkn, __VA_ARGS__, )(__VA_ARGS__)
+
+#define __mrs(reg) \
+ ({ \
+ u64 val; \
+ __asm__ volatile("mrs\t%0, " #reg : "=r"(val)); \
+ val; \
+ })
+#define _mrs(reg) __mrs(reg)
+
+#define __msr(reg, val) \
+ ({ \
+ u64 __val = (u64)val; \
+ __asm__ volatile("msr\t" #reg ", %0" : : "r"(__val)); \
+ })
+#define _msr(reg, val) __msr(reg, val)
+
+#define mrs(reg) _mrs(sr_tkn(reg))
+#define msr(reg, val) _msr(sr_tkn(reg), val)
+#define msr_sync(reg, val) \
+ ({ \
+ _msr(sr_tkn(reg), val); \
+ sysop("isb"); \
+ })
+
+#define reg_clr(reg, bits) _msr(sr_tkn(reg), _mrs(sr_tkn(reg)) & ~(bits))
+#define reg_set(reg, bits) _msr(sr_tkn(reg), _mrs(sr_tkn(reg)) | bits)
+#define reg_mask(reg, clr, set) _msr(sr_tkn(reg), (_mrs(sr_tkn(reg)) & ~(clr)) | set)
+
+#define reg_clr_sync(reg, bits) \
+ ({ \
+ reg_clr(sr_tkn(reg), bits); \
+ sysop("isb"); \
+ })
+#define reg_set_sync(reg, bits) \
+ ({ \
+ reg_set(sr_tkn(reg), bits); \
+ sysop("isb"); \
+ })
+#define reg_mask_sync(reg, clr, set) \
+ ({ \
+ reg_mask(sr_tkn(reg), clr, set); \
+ sysop("isb"); \
+ })
+
+#define sysop(op) __asm__ volatile(op ::: "memory")
+
+#define cacheop(op, val) ({ __asm__ volatile(op ", %0" : : "r"(val) : "memory"); })
+
+#define ic_ialluis() sysop("ic ialluis")
+#define ic_iallu() sysop("ic iallu")
+#define ic_iavau(p) cacheop("ic ivau", p)
+#define dc_ivac(p) cacheop("dc ivac", p)
+#define dc_isw(p) cacheop("dc isw", p)
+#define dc_csw(p) cacheop("dc csw", p)
+#define dc_cisw(p) cacheop("dc cisw", p)
+#define dc_zva(p) cacheop("dc zva", p)
+#define dc_cvac(p) cacheop("dc cvac", p)
+#define dc_cvau(p) cacheop("dc cvau", p)
+#define dc_civac(p) cacheop("dc civac", p)
+
+#define dma_mb() sysop("dmb osh")
+#define dma_rmb() sysop("dmb oshld")
+#define dma_wmb() sysop("dmb oshst")
+
+static inline int is_ecore(void)
+{
+ return !(mrs(MPIDR_EL1) & (1 << 16));
+}
+
+static inline int in_el2(void)
+{
+ return (mrs(CurrentEL) >> 2) == 2;
+}
+
+static inline int is_primary_core(void)
+{
+ return mrs(MPIDR_EL1) == 0x80000000;
+}
+
+extern char _base[];
+extern char _rodata_end[];
+extern char _end[];
+extern char _payload_start[];
+extern char _payload_end[];
+
+/*
+ * These functions are guaranteed to copy by reading from src and writing to dst
+ * in <n>-bit units If size is not aligned, the remaining bytes are not copied
+ */
+void memcpy128(void *dst, void *src, size_t size);
+void memset64(void *dst, u64 value, size_t size);
+void memcpy64(void *dst, void *src, size_t size);
+void memset32(void *dst, u32 value, size_t size);
+void memcpy32(void *dst, void *src, size_t size);
+void memset16(void *dst, u16 value, size_t size);
+void memcpy16(void *dst, void *src, size_t size);
+void memset8(void *dst, u8 value, size_t size);
+void memcpy8(void *dst, void *src, size_t size);
+
+void get_simd_state(void *state);
+void put_simd_state(void *state);
+
+void hexdump(const void *d, size_t len);
+void regdump(u64 addr, size_t len);
+int snprintf(char *str, size_t size, const char *fmt, ...);
+int debug_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
+void udelay(u32 d);
+
+static inline u64 get_ticks(void)
+{
+ return mrs(CNTPCT_EL0);
+}
+u64 ticks_to_msecs(u64 ticks);
+u64 ticks_to_usecs(u64 ticks);
+
+void reboot(void) __attribute__((noreturn));
+void flush_and_reboot(void) __attribute__((noreturn));
+
+u64 timeout_calculate(u32 usec);
+bool timeout_expired(u64 timeout);
+
+#define SPINLOCK_ALIGN 64
+
+typedef struct {
+ s64 lock;
+ int count;
+} spinlock_t ALIGNED(SPINLOCK_ALIGN);
+
+#define SPINLOCK_INIT \
+ { \
+ -1, 0 \
+ }
+#define DECLARE_SPINLOCK(n) spinlock_t n = SPINLOCK_INIT;
+
+void spin_init(spinlock_t *lock);
+void spin_lock(spinlock_t *lock);
+void spin_unlock(spinlock_t *lock);
+
+#define mdelay(m) udelay((m)*1000)
+
+#define panic(fmt, ...) \
+ do { \
+ debug_printf(fmt, ##__VA_ARGS__); \
+ flush_and_reboot(); \
+ } while (0)
+
+static inline int poll32(u64 addr, u32 mask, u32 target, u32 timeout)
+{
+ while (--timeout > 0) {
+ u32 value = read32(addr) & mask;
+ if (value == target)
+ return 0;
+ udelay(1);
+ }
+
+ return -1;
+}
+
+typedef u64(generic_func)(u64, u64, u64, u64, u64);
+
+struct vector_args {
+ generic_func *entry;
+ u64 args[5];
+ bool restore_logo;
+};
+
+extern u32 board_id, chip_id;
+
+extern struct vector_args next_stage;
+
+void deep_wfi(void);
+
+bool is_heap(void *addr);
+
+#endif
diff --git a/tools/src/utils_asm.S b/tools/src/utils_asm.S
new file mode 100644
index 0000000..7fe1cea
--- /dev/null
+++ b/tools/src/utils_asm.S
@@ -0,0 +1,182 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "cpu_regs.h"
+
+.text
+
+.globl memcpy128
+.type memcpy128, @function
+memcpy128:
+ ands x2, x2, #~15
+ beq 2f
+1: ldp x3, x4, [x1], #16
+ stp x3, x4, [x0], #16
+ subs x2, x2, #16
+ bne 1b
+2:
+ ret
+
+.globl memcpy64
+.type memcpy64, @function
+memcpy64:
+ ands x2, x2, #~7
+ beq 2f
+1: ldr x3, [x1], #8
+ str x3, [x0], #8
+ subs x2, x2, #8
+ bne 1b
+2:
+ ret
+
+.globl memset64
+.type memset64, @function
+memset64:
+ ands x2, x2, #~7
+ beq 2f
+1: str x1, [x0], #8
+ subs x2, x2, #8
+ bne 1b
+2:
+ ret
+
+.globl memcpy32
+.type memcpy32, @function
+memcpy32:
+ ands x2, x2, #~3
+ beq 2f
+1: ldr w3, [x1], #4
+ str w3, [x0], #4
+ subs x2, x2, #4
+ bne 1b
+2:
+ ret
+
+.globl memset32
+.type memset32, @function
+memset32:
+ ands x2, x2, #~3
+ beq 2f
+1: str w1, [x0], #4
+ subs x2, x2, #4
+ bne 1b
+2:
+ ret
+
+.globl memcpy16
+.type memcpy16, @function
+memcpy16:
+ ands x2, x2, #~1
+ beq 2f
+1: ldrh w3, [x1], #2
+ strh w3, [x0], #2
+ subs x2, x2, #2
+ bne 1b
+2:
+ ret
+
+.globl memset16
+.type memset16, @function
+memset16:
+ ands x2, x2, #~1
+ beq 2f
+1: strh w1, [x0], #2
+ subs x2, x2, #2
+ bne 1b
+2:
+ ret
+
+.globl memcpy8
+.type memcpy8, @function
+memcpy8:
+ cmp x2, #0
+ beq 2f
+1: ldrb w3, [x1], #1
+ strb w3, [x0], #1
+ subs x2, x2, #1
+ bne 1b
+2:
+ ret
+
+.globl memset8
+.type memset8, @function
+memset8:
+ cmp x2, #0
+ beq 2f
+1: strb w1, [x0], #1
+ subs x2, x2, #1
+ bne 1b
+2:
+ ret
+
+.globl get_simd_state
+.type get_simd_state, @function
+get_simd_state:
+ stp q0, q1, [x0], #32
+ stp q2, q3, [x0], #32
+ stp q4, q5, [x0], #32
+ stp q6, q7, [x0], #32
+ stp q8, q9, [x0], #32
+ stp q10, q11, [x0], #32
+ stp q12, q13, [x0], #32
+ stp q14, q15, [x0], #32
+ stp q16, q17, [x0], #32
+ stp q18, q19, [x0], #32
+ stp q20, q21, [x0], #32
+ stp q22, q23, [x0], #32
+ stp q24, q25, [x0], #32
+ stp q26, q27, [x0], #32
+ stp q28, q29, [x0], #32
+ stp q30, q31, [x0], #32
+ ret
+
+.globl put_simd_state
+.type put_simd_state, @function
+put_simd_state:
+ ldp q0, q1, [x0], #32
+ ldp q2, q3, [x0], #32
+ ldp q4, q5, [x0], #32
+ ldp q6, q7, [x0], #32
+ ldp q8, q9, [x0], #32
+ ldp q10, q11, [x0], #32
+ ldp q12, q13, [x0], #32
+ ldp q14, q15, [x0], #32
+ ldp q16, q17, [x0], #32
+ ldp q18, q19, [x0], #32
+ ldp q20, q21, [x0], #32
+ ldp q22, q23, [x0], #32
+ ldp q24, q25, [x0], #32
+ ldp q26, q27, [x0], #32
+ ldp q28, q29, [x0], #32
+ ldp q30, q31, [x0], #32
+ ret
+
+.globl deep_wfi
+.type deep_wfi, @function
+deep_wfi:
+ str x30, [sp, #-16]!
+ stp x28, x29, [sp, #-16]!
+ stp x26, x27, [sp, #-16]!
+ stp x24, x25, [sp, #-16]!
+ stp x22, x23, [sp, #-16]!
+ stp x20, x21, [sp, #-16]!
+ stp x18, x19, [sp, #-16]!
+
+ mrs x0, SYS_IMP_APL_CYC_OVRD
+ orr x0, x0, #(3L << 24)
+ msr SYS_IMP_APL_CYC_OVRD, x0
+
+ wfi
+
+ mrs x0, SYS_IMP_APL_CYC_OVRD
+ bic x0, x0, #(1L << 24)
+ msr SYS_IMP_APL_CYC_OVRD, x0
+
+ ldp x18, x19, [sp], #16
+ ldp x20, x21, [sp], #16
+ ldp x22, x23, [sp], #16
+ ldp x24, x25, [sp], #16
+ ldp x26, x27, [sp], #16
+ ldp x28, x29, [sp], #16
+ ldr x30, [sp], #16
+
+ ret
diff --git a/tools/src/vsprintf.c b/tools/src/vsprintf.c
new file mode 100644
index 0000000..daa5d29
--- /dev/null
+++ b/tools/src/vsprintf.c
@@ -0,0 +1,703 @@
+/*
+ * Copyright (c) 1995 Patrick Powell.
+ *
+ * This code is based on code written by Patrick Powell <papowell@astart.com>.
+ * It may be used for any purpose as long as this notice remains intact on all
+ * source code distributions.
+ */
+
+/*
+ * Copyright (c) 2008 Holger Weiss.
+ *
+ * This version of the code is maintained by Holger Weiss <holger@jhweiss.de>.
+ * My changes to the code may freely be used, modified and/or redistributed for
+ * any purpose. It would be nice if additions and fixes to this file (including
+ * trivial code cleanups) would be sent back in order to let me include them in
+ * the version available at <http://www.jhweiss.de/software/snprintf.html>.
+ * However, this is not a requirement for using or redistributing (possibly
+ * modified) versions of this file, nor is leaving this notice intact mandatory.
+ */
+
+/*
+ * History
+ *
+ * 2009-03-05 Hector Martin "marcan" <marcan@marcansoft.com>
+ *
+ * Hacked up and removed a lot of stuff including floating-point support,
+ * a bunch of ifs and defines, locales, and tests
+ *
+ * 2008-01-20 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.1:
+ *
+ * Fixed the detection of infinite floating point values on IRIX (and
+ * possibly other systems) and applied another few minor cleanups.
+ *
+ * 2008-01-06 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.0:
+ *
+ * Added a lot of new features, fixed many bugs, and incorporated various
+ * improvements done by Andrew Tridgell <tridge@samba.org>, Russ Allbery
+ * <rra@stanford.edu>, Hrvoje Niksic <hniksic@xemacs.org>, Damien Miller
+ * <djm@mindrot.org>, and others for the Samba, INN, Wget, and OpenSSH
+ * projects. The additions include: support the "e", "E", "g", "G", and
+ * "F" conversion specifiers (and use conversion style "f" or "F" for the
+ * still unsupported "a" and "A" specifiers); support the "hh", "ll", "j",
+ * "t", and "z" length modifiers; support the "#" flag and the (non-C99)
+ * "'" flag; use localeconv(3) (if available) to get both the current
+ * locale's decimal point character and the separator between groups of
+ * digits; fix the handling of various corner cases of field width and
+ * precision specifications; fix various floating point conversion bugs;
+ * handle infinite and NaN floating point values; don't attempt to write to
+ * the output buffer (which may be NULL) if a size of zero was specified;
+ * check for integer overflow of the field width, precision, and return
+ * values and during the floating point conversion; use the OUTCHAR() macro
+ * instead of a function for better performance; provide asprintf(3) and
+ * vasprintf(3) functions; add new test cases. The replacement functions
+ * have been renamed to use an "rpl_" prefix, the function calls in the
+ * main project (and in this file) must be redefined accordingly for each
+ * replacement function which is needed (by using Autoconf or other means).
+ * Various other minor improvements have been applied and the coding style
+ * was cleaned up for consistency.
+ *
+ * 2007-07-23 Holger Weiss <holger@jhweiss.de> for Mutt 1.5.13:
+ *
+ * C99 compliant snprintf(3) and vsnprintf(3) functions return the number
+ * of characters that would have been written to a sufficiently sized
+ * buffer (excluding the '\0'). The original code simply returned the
+ * length of the resulting output string, so that's been fixed.
+ *
+ * 1998-03-05 Michael Elkins <me@mutt.org> for Mutt 0.90.8:
+ *
+ * The original code assumed that both snprintf(3) and vsnprintf(3) were
+ * missing. Some systems only have snprintf(3) but not vsnprintf(3), so
+ * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
+ *
+ * 1998-01-27 Thomas Roessler <roessler@does-not-exist.org> for Mutt 0.89i:
+ *
+ * The PGP code was using unsigned hexadecimal formats. Unfortunately,
+ * unsigned formats simply didn't work.
+ *
+ * 1997-10-22 Brandon Long <blong@fiction.net> for Mutt 0.87.1:
+ *
+ * Ok, added some minimal floating point support, which means this probably
+ * requires libm on most operating systems. Don't yet support the exponent
+ * (e,E) and sigfig (g,G). Also, fmtint() was pretty badly broken, it just
+ * wasn't being exercised in ways which showed it, so that's been fixed.
+ * Also, formatted the code to Mutt conventions, and removed dead code left
+ * over from the original. Also, there is now a builtin-test, run with:
+ * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm && ./snprintf
+ *
+ * 2996-09-15 Brandon Long <blong@fiction.net> for Mutt 0.43:
+ *
+ * This was ugly. It is still ugly. I opted out of floating point
+ * numbers, but the formatter understands just about everything from the
+ * normal C string format, at least as far as I can tell from the Solaris
+ * 2.5 printf(3S) man page.
+ */
+
+#include <stdarg.h>
+
+#include "types.h"
+
+#define VA_START(ap, last) va_start(ap, last)
+#define VA_SHIFT(ap, value, type) /* No-op for ANSI C. */
+
+#define ULLONG unsigned long long
+#define UINTMAX_T unsigned long
+#define LLONG long
+#define INTMAX_T long
+
+/* Support for uintptr_t. */
+#ifndef UINTPTR_T
+#if HAVE_UINTPTR_T || defined(uintptr_t)
+#define UINTPTR_T uintptr_t
+#else
+#define UINTPTR_T unsigned long int
+#endif /* HAVE_UINTPTR_T || defined(uintptr_t) */
+#endif /* !defined(UINTPTR_T) */
+
+/* Support for ptrdiff_t. */
+#ifndef PTRDIFF_T
+#if HAVE_PTRDIFF_T || defined(ptrdiff_t)
+#define PTRDIFF_T ptrdiff_t
+#else
+#define PTRDIFF_T long int
+#endif /* HAVE_PTRDIFF_T || defined(ptrdiff_t) */
+#endif /* !defined(PTRDIFF_T) */
+
+/*
+ * We need an unsigned integer type corresponding to ptrdiff_t (cf. C99:
+ * 7.19.6.1, 7). However, we'll simply use PTRDIFF_T and convert it to an
+ * unsigned type if necessary. This should work just fine in practice.
+ */
+#ifndef UPTRDIFF_T
+#define UPTRDIFF_T PTRDIFF_T
+#endif /* !defined(UPTRDIFF_T) */
+
+/*
+ * We need a signed integer type corresponding to size_t (cf. C99: 7.19.6.1, 7).
+ * However, we'll simply use size_t and convert it to a signed type if
+ * necessary. This should work just fine in practice.
+ */
+#ifndef SSIZE_T
+#define SSIZE_T size_t
+#endif /* !defined(SSIZE_T) */
+
+/*
+ * Buffer size to hold the octal string representation of UINT128_MAX without
+ * nul-termination ("3777777777777777777777777777777777777777777").
+ */
+#ifdef MAX_CONVERT_LENGTH
+#undef MAX_CONVERT_LENGTH
+#endif /* defined(MAX_CONVERT_LENGTH) */
+#define MAX_CONVERT_LENGTH 43
+
+/* Format read states. */
+#define PRINT_S_DEFAULT 0
+#define PRINT_S_FLAGS 1
+#define PRINT_S_WIDTH 2
+#define PRINT_S_DOT 3
+#define PRINT_S_PRECISION 4
+#define PRINT_S_MOD 5
+#define PRINT_S_CONV 6
+
+/* Format flags. */
+#define PRINT_F_MINUS (1 << 0)
+#define PRINT_F_PLUS (1 << 1)
+#define PRINT_F_SPACE (1 << 2)
+#define PRINT_F_NUM (1 << 3)
+#define PRINT_F_ZERO (1 << 4)
+#define PRINT_F_QUOTE (1 << 5)
+#define PRINT_F_UP (1 << 6)
+#define PRINT_F_UNSIGNED (1 << 7)
+#define PRINT_F_TYPE_G (1 << 8)
+#define PRINT_F_TYPE_E (1 << 9)
+
+/* Conversion flags. */
+#define PRINT_C_CHAR 1
+#define PRINT_C_SHORT 2
+#define PRINT_C_LONG 3
+#define PRINT_C_LLONG 4
+// #define PRINT_C_LDOUBLE 5
+#define PRINT_C_SIZE 6
+#define PRINT_C_PTRDIFF 7
+#define PRINT_C_INTMAX 8
+
+#ifndef MAX
+#define MAX(x, y) ((x >= y) ? x : y)
+#endif /* !defined(MAX) */
+#ifndef CHARTOINT
+#define CHARTOINT(ch) (ch - '0')
+#endif /* !defined(CHARTOINT) */
+#ifndef ISDIGIT
+#define ISDIGIT(ch) ('0' <= (unsigned char)ch && (unsigned char)ch <= '9')
+#endif /* !defined(ISDIGIT) */
+
+#define OUTCHAR(str, len, size, ch) \
+ do { \
+ if (len + 1 < size) \
+ str[len] = ch; \
+ (len)++; \
+ } while (/* CONSTCOND */ 0)
+
+static void fmtstr(char *, size_t *, size_t, const char *, int, int, int);
+static void fmtint(char *, size_t *, size_t, INTMAX_T, int, int, int, int);
+static void printsep(char *, size_t *, size_t);
+static int getnumsep(int);
+static int convert(UINTMAX_T, char *, size_t, int, int);
+
+int vsnprintf(char *str, size_t size, const char *format, va_list args)
+{
+ INTMAX_T value;
+ unsigned char cvalue;
+ const char *strvalue;
+ INTMAX_T *intmaxptr;
+ PTRDIFF_T *ptrdiffptr;
+ SSIZE_T *sizeptr;
+ LLONG *llongptr;
+ long int *longptr;
+ int *intptr;
+ short int *shortptr;
+ signed char *charptr;
+ size_t len = 0;
+ int overflow = 0;
+ int base = 0;
+ int cflags = 0;
+ int flags = 0;
+ int width = 0;
+ int precision = -1;
+ int state = PRINT_S_DEFAULT;
+ char ch = *format++;
+
+ /*
+ * C99 says: "If `n' is zero, nothing is written, and `s' may be a null
+ * pointer." (7.19.6.5, 2) We're forgiving and allow a NULL pointer
+ * even if a size larger than zero was specified. At least NetBSD's
+ * snprintf(3) does the same, as well as other versions of this file.
+ * (Though some of these versions will write to a non-NULL buffer even
+ * if a size of zero was specified, which violates the standard.)
+ */
+ if (str == NULL && size != 0)
+ size = 0;
+
+ while (ch != '\0')
+ switch (state) {
+ case PRINT_S_DEFAULT:
+ if (ch == '%')
+ state = PRINT_S_FLAGS;
+ else
+ OUTCHAR(str, len, size, ch);
+ ch = *format++;
+ break;
+ case PRINT_S_FLAGS:
+ switch (ch) {
+ case '-':
+ flags |= PRINT_F_MINUS;
+ ch = *format++;
+ break;
+ case '+':
+ flags |= PRINT_F_PLUS;
+ ch = *format++;
+ break;
+ case ' ':
+ flags |= PRINT_F_SPACE;
+ ch = *format++;
+ break;
+ case '#':
+ flags |= PRINT_F_NUM;
+ ch = *format++;
+ break;
+ case '0':
+ flags |= PRINT_F_ZERO;
+ ch = *format++;
+ break;
+ case '\'': /* SUSv2 flag (not in C99). */
+ flags |= PRINT_F_QUOTE;
+ ch = *format++;
+ break;
+ default:
+ state = PRINT_S_WIDTH;
+ break;
+ }
+ break;
+ case PRINT_S_WIDTH:
+ if (ISDIGIT(ch)) {
+ ch = CHARTOINT(ch);
+ if (width > (INT_MAX - ch) / 10) {
+ overflow = 1;
+ goto out;
+ }
+ width = 10 * width + ch;
+ ch = *format++;
+ } else if (ch == '*') {
+ /*
+ * C99 says: "A negative field width argument is
+ * taken as a `-' flag followed by a positive
+ * field width." (7.19.6.1, 5)
+ */
+ if ((width = va_arg(args, int)) < 0) {
+ flags |= PRINT_F_MINUS;
+ width = -width;
+ }
+ ch = *format++;
+ state = PRINT_S_DOT;
+ } else
+ state = PRINT_S_DOT;
+ break;
+ case PRINT_S_DOT:
+ if (ch == '.') {
+ state = PRINT_S_PRECISION;
+ ch = *format++;
+ } else
+ state = PRINT_S_MOD;
+ break;
+ case PRINT_S_PRECISION:
+ if (precision == -1)
+ precision = 0;
+ if (ISDIGIT(ch)) {
+ ch = CHARTOINT(ch);
+ if (precision > (INT_MAX - ch) / 10) {
+ overflow = 1;
+ goto out;
+ }
+ precision = 10 * precision + ch;
+ ch = *format++;
+ } else if (ch == '*') {
+ /*
+ * C99 says: "A negative precision argument is
+ * taken as if the precision were omitted."
+ * (7.19.6.1, 5)
+ */
+ if ((precision = va_arg(args, int)) < 0)
+ precision = -1;
+ ch = *format++;
+ state = PRINT_S_MOD;
+ } else
+ state = PRINT_S_MOD;
+ break;
+ case PRINT_S_MOD:
+ switch (ch) {
+ case 'h':
+ ch = *format++;
+ if (ch == 'h') { /* It's a char. */
+ ch = *format++;
+ cflags = PRINT_C_CHAR;
+ } else
+ cflags = PRINT_C_SHORT;
+ break;
+ case 'l':
+ ch = *format++;
+ if (ch == 'l') { /* It's a long long. */
+ ch = *format++;
+ cflags = PRINT_C_LLONG;
+ } else
+ cflags = PRINT_C_LONG;
+ break;
+ case 'j':
+ cflags = PRINT_C_INTMAX;
+ ch = *format++;
+ break;
+ case 't':
+ cflags = PRINT_C_PTRDIFF;
+ ch = *format++;
+ break;
+ case 'z':
+ cflags = PRINT_C_SIZE;
+ ch = *format++;
+ break;
+ }
+ state = PRINT_S_CONV;
+ break;
+ case PRINT_S_CONV:
+ switch (ch) {
+ case 'd':
+ /* FALLTHROUGH */
+ case 'i':
+ switch (cflags) {
+ case PRINT_C_CHAR:
+ value = (signed char)va_arg(args, int);
+ break;
+ case PRINT_C_SHORT:
+ value = (short int)va_arg(args, int);
+ break;
+ case PRINT_C_LONG:
+ value = va_arg(args, long int);
+ break;
+ case PRINT_C_LLONG:
+ value = va_arg(args, LLONG);
+ break;
+ case PRINT_C_SIZE:
+ value = va_arg(args, SSIZE_T);
+ break;
+ case PRINT_C_INTMAX:
+ value = va_arg(args, INTMAX_T);
+ break;
+ case PRINT_C_PTRDIFF:
+ value = va_arg(args, PTRDIFF_T);
+ break;
+ default:
+ value = va_arg(args, int);
+ break;
+ }
+ fmtint(str, &len, size, value, 10, width, precision, flags);
+ break;
+ case 'X':
+ flags |= PRINT_F_UP;
+ /* FALLTHROUGH */
+ case 'x':
+ base = 16;
+ /* FALLTHROUGH */
+ case 'o':
+ if (base == 0)
+ base = 8;
+ /* FALLTHROUGH */
+ case 'u':
+ if (base == 0)
+ base = 10;
+ flags |= PRINT_F_UNSIGNED;
+ switch (cflags) {
+ case PRINT_C_CHAR:
+ value = (unsigned char)va_arg(args, unsigned int);
+ break;
+ case PRINT_C_SHORT:
+ value = (unsigned short int)va_arg(args, unsigned int);
+ break;
+ case PRINT_C_LONG:
+ value = va_arg(args, unsigned long int);
+ break;
+ case PRINT_C_LLONG:
+ value = va_arg(args, ULLONG);
+ break;
+ case PRINT_C_SIZE:
+ value = va_arg(args, size_t);
+ break;
+ case PRINT_C_INTMAX:
+ value = va_arg(args, UINTMAX_T);
+ break;
+ case PRINT_C_PTRDIFF:
+ value = va_arg(args, UPTRDIFF_T);
+ break;
+ default:
+ value = va_arg(args, unsigned int);
+ break;
+ }
+ fmtint(str, &len, size, value, base, width, precision, flags);
+ break;
+ case 'c':
+ cvalue = va_arg(args, int);
+ OUTCHAR(str, len, size, cvalue);
+ break;
+ case 's':
+ strvalue = va_arg(args, char *);
+ fmtstr(str, &len, size, strvalue, width, precision, flags);
+ break;
+ case 'p':
+ /*
+ * C99 says: "The value of the pointer is
+ * converted to a sequence of printing
+ * characters, in an implementation-defined
+ * manner." (C99: 7.19.6.1, 8)
+ */
+ if ((strvalue = va_arg(args, void *)) == NULL)
+ /*
+ * We use the glibc format. BSD prints
+ * "0x0", SysV "0".
+ */
+ fmtstr(str, &len, size, "(nil)", width, -1, flags);
+ else {
+ /*
+ * We use the BSD/glibc format. SysV
+ * omits the "0x" prefix (which we emit
+ * using the PRINT_F_NUM flag).
+ */
+ flags |= PRINT_F_NUM;
+ flags |= PRINT_F_UNSIGNED;
+ fmtint(str, &len, size, (UINTPTR_T)strvalue, 16, width, precision,
+ flags);
+ }
+ break;
+ case 'n':
+ switch (cflags) {
+ case PRINT_C_CHAR:
+ charptr = va_arg(args, signed char *);
+ *charptr = len;
+ break;
+ case PRINT_C_SHORT:
+ shortptr = va_arg(args, short int *);
+ *shortptr = len;
+ break;
+ case PRINT_C_LONG:
+ longptr = va_arg(args, long int *);
+ *longptr = len;
+ break;
+ case PRINT_C_LLONG:
+ llongptr = va_arg(args, LLONG *);
+ *llongptr = len;
+ break;
+ case PRINT_C_SIZE:
+ /*
+ * C99 says that with the "z" length
+ * modifier, "a following `n' conversion
+ * specifier applies to a pointer to a
+ * signed integer type corresponding to
+ * size_t argument." (7.19.6.1, 7)
+ */
+ sizeptr = va_arg(args, SSIZE_T *);
+ *sizeptr = len;
+ break;
+ case PRINT_C_INTMAX:
+ intmaxptr = va_arg(args, INTMAX_T *);
+ *intmaxptr = len;
+ break;
+ case PRINT_C_PTRDIFF:
+ ptrdiffptr = va_arg(args, PTRDIFF_T *);
+ *ptrdiffptr = len;
+ break;
+ default:
+ intptr = va_arg(args, int *);
+ *intptr = len;
+ break;
+ }
+ break;
+ case '%': /* Print a "%" character verbatim. */
+ OUTCHAR(str, len, size, ch);
+ break;
+ default: /* Skip other characters. */
+ break;
+ }
+ ch = *format++;
+ state = PRINT_S_DEFAULT;
+ base = cflags = flags = width = 0;
+ precision = -1;
+ break;
+ }
+out:
+ if (len < size)
+ str[len] = '\0';
+ else if (size > 0)
+ str[size - 1] = '\0';
+
+ if (overflow || len >= INT_MAX) {
+ return -1;
+ }
+ return (int)len;
+}
+
+static void fmtstr(char *str, size_t *len, size_t size, const char *value, int width, int precision,
+ int flags)
+{
+ int padlen, strln; /* Amount to pad. */
+ int noprecision = (precision == -1);
+
+ if (value == NULL) /* We're forgiving. */
+ value = "(null)";
+
+ /* If a precision was specified, don't read the string past it. */
+ for (strln = 0; value[strln] != '\0' && (noprecision || strln < precision); strln++)
+ continue;
+
+ if ((padlen = width - strln) < 0)
+ padlen = 0;
+ if (flags & PRINT_F_MINUS) /* Left justify. */
+ padlen = -padlen;
+
+ while (padlen > 0) { /* Leading spaces. */
+ OUTCHAR(str, *len, size, ' ');
+ padlen--;
+ }
+ while (*value != '\0' && (noprecision || precision-- > 0)) {
+ OUTCHAR(str, *len, size, *value);
+ value++;
+ }
+ while (padlen < 0) { /* Trailing spaces. */
+ OUTCHAR(str, *len, size, ' ');
+ padlen++;
+ }
+}
+
+static void fmtint(char *str, size_t *len, size_t size, INTMAX_T value, int base, int width,
+ int precision, int flags)
+{
+ UINTMAX_T uvalue;
+ char iconvert[MAX_CONVERT_LENGTH];
+ char sign = 0;
+ char hexprefix = 0;
+ int spadlen = 0; /* Amount to space pad. */
+ int zpadlen = 0; /* Amount to zero pad. */
+ int pos;
+ int separators = (flags & PRINT_F_QUOTE);
+ int noprecision = (precision == -1);
+
+ if (flags & PRINT_F_UNSIGNED)
+ uvalue = value;
+ else {
+ uvalue = (value >= 0) ? value : -value;
+ if (value < 0)
+ sign = '-';
+ else if (flags & PRINT_F_PLUS) /* Do a sign. */
+ sign = '+';
+ else if (flags & PRINT_F_SPACE)
+ sign = ' ';
+ }
+
+ pos = convert(uvalue, iconvert, sizeof(iconvert), base, flags & PRINT_F_UP);
+
+ if (flags & PRINT_F_NUM && uvalue != 0) {
+ /*
+ * C99 says: "The result is converted to an `alternative form'.
+ * For `o' conversion, it increases the precision, if and only
+ * if necessary, to force the first digit of the result to be a
+ * zero (if the value and precision are both 0, a single 0 is
+ * printed). For `x' (or `X') conversion, a nonzero result has
+ * `0x' (or `0X') prefixed to it." (7.19.6.1, 6)
+ */
+ switch (base) {
+ case 8:
+ if (precision <= pos)
+ precision = pos + 1;
+ break;
+ case 16:
+ hexprefix = (flags & PRINT_F_UP) ? 'X' : 'x';
+ break;
+ }
+ }
+
+ if (separators) /* Get the number of group separators we'll print. */
+ separators = getnumsep(pos);
+
+ zpadlen = precision - pos - separators;
+ spadlen = width /* Minimum field width. */
+ - separators /* Number of separators. */
+ - MAX(precision, pos) /* Number of integer digits. */
+ - ((sign != 0) ? 1 : 0) /* Will we print a sign? */
+ - ((hexprefix != 0) ? 2 : 0); /* Will we print a prefix? */
+
+ if (zpadlen < 0)
+ zpadlen = 0;
+ if (spadlen < 0)
+ spadlen = 0;
+
+ /*
+ * C99 says: "If the `0' and `-' flags both appear, the `0' flag is
+ * ignored. For `d', `i', `o', `u', `x', and `X' conversions, if a
+ * precision is specified, the `0' flag is ignored." (7.19.6.1, 6)
+ */
+ if (flags & PRINT_F_MINUS) /* Left justify. */
+ spadlen = -spadlen;
+ else if (flags & PRINT_F_ZERO && noprecision) {
+ zpadlen += spadlen;
+ spadlen = 0;
+ }
+ while (spadlen > 0) { /* Leading spaces. */
+ OUTCHAR(str, *len, size, ' ');
+ spadlen--;
+ }
+ if (sign != 0) /* Sign. */
+ OUTCHAR(str, *len, size, sign);
+ if (hexprefix != 0) { /* A "0x" or "0X" prefix. */
+ OUTCHAR(str, *len, size, '0');
+ OUTCHAR(str, *len, size, hexprefix);
+ }
+ while (zpadlen > 0) { /* Leading zeros. */
+ OUTCHAR(str, *len, size, '0');
+ zpadlen--;
+ }
+ while (pos > 0) { /* The actual digits. */
+ pos--;
+ OUTCHAR(str, *len, size, iconvert[pos]);
+ if (separators > 0 && pos > 0 && pos % 3 == 0)
+ printsep(str, len, size);
+ }
+ while (spadlen < 0) { /* Trailing spaces. */
+ OUTCHAR(str, *len, size, ' ');
+ spadlen++;
+ }
+}
+
+static void printsep(char *str, size_t *len, size_t size)
+{
+ OUTCHAR(str, *len, size, ',');
+}
+
+static int getnumsep(int digits)
+{
+ int separators = (digits - ((digits % 3 == 0) ? 1 : 0)) / 3;
+ return separators;
+}
+
+static int convert(UINTMAX_T value, char *buf, size_t size, int base, int caps)
+{
+ const char *digits = caps ? "0123456789ABCDEF" : "0123456789abcdef";
+ size_t pos = 0;
+
+ /* We return an unterminated buffer with the digits in reverse order. */
+ do {
+ buf[pos++] = digits[value % base];
+ value /= base;
+ } while (value != 0 && pos < size);
+
+ return (int)pos;
+}
+
+int vsprintf(char *buf, const char *fmt, va_list args)
+{
+ return vsnprintf(buf, INT_MAX, fmt, args);
+}
diff --git a/tools/src/vsprintf.h b/tools/src/vsprintf.h
new file mode 100644
index 0000000..cff6c93
--- /dev/null
+++ b/tools/src/vsprintf.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef VSPRINTF_H
+#define VSPRINTF_H
+
+#include <stdarg.h>
+
+int vsprintf(char *buf, const char *fmt, va_list args);
+int vsnprintf(char *buf, size_t size, const char *fmt, va_list args);
+
+#endif
diff --git a/tools/src/wdt.c b/tools/src/wdt.c
new file mode 100644
index 0000000..a3ebe3a
--- /dev/null
+++ b/tools/src/wdt.c
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "wdt.h"
+#include "adt.h"
+#include "types.h"
+#include "utils.h"
+
+#define WDT_COUNT 0x10
+#define WDT_ALARM 0x14
+#define WDT_CTL 0x1c
+
+static u64 wdt_base = 0;
+
+void wdt_disable(void)
+{
+ int path[8];
+ int node = adt_path_offset_trace(adt, "/arm-io/wdt", path);
+
+ if (node < 0) {
+ printf("WDT node not found!\n");
+ return;
+ }
+
+ if (adt_get_reg(adt, path, "reg", 0, &wdt_base, NULL)) {
+ printf("Failed to get WDT reg property!\n");
+ return;
+ }
+
+ printf("WDT registers @ 0x%lx\n", wdt_base);
+
+ write32(wdt_base + WDT_CTL, 0);
+
+ printf("WDT disabled\n");
+}
+
+void wdt_reboot(void)
+{
+ if (!wdt_base)
+ return;
+
+ write32(wdt_base + WDT_ALARM, 0x100000);
+ write32(wdt_base + WDT_COUNT, 0);
+ write32(wdt_base + WDT_CTL, 4);
+}
diff --git a/tools/src/wdt.h b/tools/src/wdt.h
new file mode 100644
index 0000000..6a48601
--- /dev/null
+++ b/tools/src/wdt.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef __WDT_H__
+#define __WDT_H__
+
+void wdt_disable(void);
+void wdt_reboot(void);
+
+#endif
diff --git a/tools/src/xnuboot.h b/tools/src/xnuboot.h
new file mode 100644
index 0000000..32623b3
--- /dev/null
+++ b/tools/src/xnuboot.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef XNUBOOT_H
+#define XNUBOOT_H
+
+#define CMDLINE_LENGTH 608
+
+struct boot_video {
+ u64 base;
+ u64 display;
+ u64 stride;
+ u64 width;
+ u64 height;
+ u64 depth;
+};
+
+struct boot_args {
+ u16 revision;
+ u16 version;
+ u64 virt_base;
+ u64 phys_base;
+ u64 mem_size;
+ u64 top_of_kernel_data;
+ struct boot_video video;
+ u32 machine_type;
+ void *devtree;
+ u32 devtree_size;
+ char cmdline[CMDLINE_LENGTH];
+ u64 boot_flags;
+ u64 mem_size_actual;
+};
+
+extern u64 boot_args_addr;
+extern struct boot_args cur_boot_args;
+
+#endif
diff --git a/tools/sysinc/assert.h b/tools/sysinc/assert.h
new file mode 100644
index 0000000..0afd561
--- /dev/null
+++ b/tools/sysinc/assert.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef ASSERT_H
+#define ASSERT_H
+
+void __assert_fail(const char *assertion, const char *file, unsigned int line,
+ const char *function);
+
+#define assert(expression) \
+ ((expression) ? (void)0 : __assert_fail(#expression, __FILE__, __LINE__, __func__))
+
+/* Requires C11 */
+#define static_assert _Static_assert
+
+#endif
diff --git a/tools/sysinc/endian.h b/tools/sysinc/endian.h
new file mode 100644
index 0000000..6115c0b
--- /dev/null
+++ b/tools/sysinc/endian.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef ENDIAN_H
+#define ENDIAN_H
+
+#define __LITTLE_ENDIAN 1234
+#define __BIG_ENDIAN 4321
+#define __PDP_ENDIAN 3412
+
+#define __BYTE_ORDER __LITTLE_ENDIAN
+
+#endif
diff --git a/tools/sysinc/errno.h b/tools/sysinc/errno.h
new file mode 100644
index 0000000..5aaf0dd
--- /dev/null
+++ b/tools/sysinc/errno.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef ERRNO_H
+#define ERRNO_H
+
+#define ENOMEM 12
+#define EINVAL 22
+
+#endif
diff --git a/tools/sysinc/limits.h b/tools/sysinc/limits.h
new file mode 100644
index 0000000..5efe2fc
--- /dev/null
+++ b/tools/sysinc/limits.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef LIMITS_H
+#define LIMITS_H
+
+#define INT_MAX (0x7fffffff)
+#define UINT_MAX (0xffffffffu)
+
+#define LONG_MAX (0x7fffffffffffffffl)
+#define ULONG_MAX (0xfffffffffffffffful)
+
+#define LLONG_MAX LONG_MAX
+#define ULLONG_MAX ULLONG_MAX
+
+#endif
diff --git a/tools/sysinc/malloc.h b/tools/sysinc/malloc.h
new file mode 100644
index 0000000..945bcff
--- /dev/null
+++ b/tools/sysinc/malloc.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef MALLOC_H
+#define MALLOC_H
+
+#include <stddef.h>
+
+void *malloc(size_t);
+void free(void *);
+void *calloc(size_t, size_t);
+void *realloc(void *, size_t);
+void *realloc_in_place(void *, size_t);
+void *memalign(size_t, size_t);
+int posix_memalign(void **, size_t, size_t);
+
+#endif
diff --git a/tools/sysinc/math.h b/tools/sysinc/math.h
new file mode 100644
index 0000000..3809446
--- /dev/null
+++ b/tools/sysinc/math.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef MATH_H
+#define MATH_H
+
+#if 100 * __GNUC__ + __GNUC_MINOR__ >= 303
+#define NAN __builtin_nanf("")
+#define INFINITY __builtin_inff()
+#else
+#define NAN (0.0f / 0.0f)
+#define INFINITY 1e5000f
+#endif
+
+float expf(float);
+
+#endif
diff --git a/tools/sysinc/string.h b/tools/sysinc/string.h
new file mode 100644
index 0000000..1aa9e26
--- /dev/null
+++ b/tools/sysinc/string.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef STRING_H
+#define STRING_H
+
+#include <stddef.h>
+
+void *memcpy(void *s1, const void *s2, size_t n);
+void *memmove(void *s1, const void *s2, size_t n);
+int memcmp(const void *s1, const void *s2, size_t n);
+void *memset(void *s, int c, size_t n);
+void *memchr(const void *s, int c, size_t n);
+char *strcpy(char *s1, const char *s2);
+char *strncpy(char *s1, const char *s2, size_t n);
+int strcmp(const char *s1, const char *s2);
+int strncmp(const char *s1, const char *s2, size_t n);
+size_t strlen(const char *s);
+size_t strnlen(const char *s, size_t n);
+char *strchr(const char *s, int c);
+char *strrchr(const char *s, int c);
+long atol(const char *s);
+
+static inline int tolower(int c)
+{
+ if (c >= 'A' && c <= 'Z')
+ return c - 'A' + 'a';
+ else
+ return c;
+}
+
+static inline int toupper(int c)
+{
+ if (c >= 'a' && c <= 'z')
+ return c - 'a' + 'A';
+ else
+ return c;
+}
+
+#endif
diff --git a/tools/tools/apple_regs.json b/tools/tools/apple_regs.json
new file mode 100644
index 0000000..60ee18c
--- /dev/null
+++ b/tools/tools/apple_regs.json
@@ -0,0 +1,319 @@
+[
+ {"index": 0, "name": "HID0_EL1", "fullname": "Hardware Implementation-Dependent Register 0", "enc": [3, 0, 15, 0, 0 ], "width": 64},
+ {"index": 0, "name": "EHID0_EL1", "fullname": "Hardware Implementation-Dependent Register 0 (E-core)", "enc": [3, 0, 15, 0, 1 ], "width": 64},
+ {"index": 0, "name": "HID1_EL1", "fullname": "Hardware Implementation-Dependent Register 1", "enc": [3, 0, 15, 1, 0 ], "width": 64},
+ {"index": 0, "name": "EHID1_EL1", "fullname": "Hardware Implementation-Dependent Register 1 (E-core)", "enc": [3, 0, 15, 1, 1 ], "width": 64},
+ {"index": 0, "name": "HID2_EL1", "fullname": "Hardware Implementation-Dependent Register 2", "enc": [3, 0, 15, 2, 0 ], "width": 64},
+ {"index": 0, "name": "EHID2_EL1", "fullname": "Hardware Implementation-Dependent Register 2 (E-core)", "enc": [3, 0, 15, 2, 1 ], "width": 64},
+ {"index": 0, "name": "HID3_EL1", "fullname": "Hardware Implementation-Dependent Register 3", "enc": [3, 0, 15, 3, 0 ], "width": 64},
+ {"index": 0, "name": "EHID3_EL1", "fullname": "Hardware Implementation-Dependent Register 3 (E-core)", "enc": [3, 0, 15, 3, 1 ], "width": 64},
+ {"index": 0, "name": "HID4_EL1", "fullname": "Hardware Implementation-Dependent Register 4", "enc": [3, 0, 15, 4, 0 ], "width": 64},
+ {"index": 0, "name": "EHID4_EL1", "fullname": "Hardware Implementation-Dependent Register 4 (E-core)", "enc": [3, 0, 15, 4, 1 ], "width": 64},
+ {"index": 0, "name": "HID5_EL1", "fullname": "Hardware Implementation-Dependent Register 5", "enc": [3, 0, 15, 5, 0 ], "width": 64},
+ {"index": 0, "name": "EHID5_EL1", "fullname": "Hardware Implementation-Dependent Register 5 (E-core)", "enc": [3, 0, 15, 5, 1 ], "width": 64},
+ {"index": 0, "name": "HID6_EL1", "fullname": "Hardware Implementation-Dependent Register 6", "enc": [3, 0, 15, 6, 0 ], "width": 64},
+ {"index": 0, "name": "HID7_EL1", "fullname": "Hardware Implementation-Dependent Register 7", "enc": [3, 0, 15, 7, 0 ], "width": 64},
+ {"index": 0, "name": "EHID7_EL1", "fullname": "Hardware Implementation-Dependent Register 7 (E-core)", "enc": [3, 0, 15, 7, 1 ], "width": 64},
+ {"index": 0, "name": "HID8_EL1", "fullname": "Hardware Implementation-Dependent Register 8", "enc": [3, 0, 15, 8, 0 ], "width": 64},
+ {"index": 0, "name": "HID9_EL1", "fullname": "Hardware Implementation-Dependent Register 9", "enc": [3, 0, 15, 9, 0 ], "width": 64},
+ {"index": 0, "name": "EHID9_EL1", "fullname": "Hardware Implementation-Dependent Register 9 (E-core)", "enc": [3, 0, 15, 9, 1 ], "width": 64},
+ {"index": 0, "name": "HID10_EL1", "fullname": "Hardware Implementation-Dependent Register 10", "enc": [3, 0, 15, 10, 0 ], "width": 64},
+ {"index": 0, "name": "EHID10_EL1", "fullname": "Hardware Implementation-Dependent Register 10 (E-core)", "enc": [3, 0, 15, 10, 1 ], "width": 64},
+ {"index": 0, "name": "HID11_EL1", "fullname": "Hardware Implementation-Dependent Register 11", "enc": [3, 0, 15, 11, 0 ], "width": 64},
+ {"index": 0, "name": "EHID11_EL1", "fullname": "Hardware Implementation-Dependent Register 11 (E-core)", "enc": [3, 0, 15, 11, 1 ], "width": 64},
+ {"index": 0, "name": "HID13_EL1", "fullname": "Hardware Implementation-Dependent Register 13", "enc": [3, 0, 15, 14, 0 ], "width": 64},
+ {"index": 0, "name": "HID14_EL1", "fullname": "Hardware Implementation-Dependent Register 14", "enc": [3, 0, 15, 15, 0 ], "width": 64},
+ {"index": 0, "name": "HID16_EL1", "fullname": "Hardware Implementation-Dependent Register 16", "enc": [3, 0, 15, 15, 2 ], "width": 64},
+ {"index": 0, "name": "HID17_EL1", "fullname": "Hardware Implementation-Dependent Register 17", "enc": [3, 0, 15, 15, 5 ], "width": 64},
+ {"index": 0, "name": "HID18_EL1", "fullname": "Hardware Implementation-Dependent Register 18", "enc": [3, 0, 15, 11, 2 ], "width": 64},
+ {"index": 0, "name": "EHID18_EL1", "fullname": "Hardware Implementation-Dependent Register 18 (E-core)", "enc": [3, 0, 15, 11, 3 ], "width": 64},
+ {"index": 0, "name": "EHID20_EL1", "fullname": "Hardware Implementation-Dependent Register 20 (E-core)", "enc": [3, 0, 15, 1, 2 ], "width": 64},
+ {"index": 0, "name": "HID21_EL1", "fullname": "Hardware Implementation-Dependent Register 21", "enc": [3, 0, 15, 1, 3 ], "width": 64},
+ {"index": 0, "name": "PMCR0_EL1", "fullname": "Performance Monitor Control Register 0", "enc": [3, 1, 15, 0, 0 ], "width": 64,
+ "fieldsets": [{"fields": [
+ {"name": "PMC0_EN", "msb": 0, "lsb": 0},
+ {"name": "PMC1_EN", "msb": 1, "lsb": 1},
+ {"name": "PMC2_EN", "msb": 2, "lsb": 2},
+ {"name": "PMC3_EN", "msb": 3, "lsb": 3},
+ {"name": "PMC4_EN", "msb": 4, "lsb": 4},
+ {"name": "PMC5_EN", "msb": 5, "lsb": 5},
+ {"name": "PMC6_EN", "msb": 6, "lsb": 6},
+ {"name": "PMC7_EN", "msb": 7, "lsb": 7},
+ {"name": "IRQ_MODE", "msb": 10, "lsb": 8},
+ {"name": "IRQ_ACTIVE", "msb": 11, "lsb": 11},
+ {"name": "PMC0_IRQ_EN", "msb": 12, "lsb": 12},
+ {"name": "PMC1_IRQ_EN", "msb": 13, "lsb": 13},
+ {"name": "PMC2_IRQ_EN", "msb": 14, "lsb": 14},
+ {"name": "PMC3_IRQ_EN", "msb": 15, "lsb": 15},
+ {"name": "PMC4_IRQ_EN", "msb": 16, "lsb": 16},
+ {"name": "PMC5_IRQ_EN", "msb": 17, "lsb": 17},
+ {"name": "PMC6_IRQ_EN", "msb": 18, "lsb": 18},
+ {"name": "PMC7_IRQ_EN", "msb": 19, "lsb": 19},
+ {"name": "DIS_CNT_PMI", "msb": 20, "lsb": 20},
+ {"name": "WAIT_ERET", "msb": 22, "lsb": 22},
+ {"name": "CNT_GLOBAL_L2C", "msb": 23, "lsb": 23},
+ {"name": "USER_EN", "msb": 30, "lsb": 30},
+ {"name": "PMC8_EN", "msb": 32, "lsb": 32},
+ {"name": "PMC9_EN", "msb": 33, "lsb": 33},
+ {"name": "PMC8_IRQ_EN", "msb": 44, "lsb": 44},
+ {"name": "PMC9_IRQ_EN", "msb": 45, "lsb": 45}
+ ]}]},
+ {"index": 0, "name": "PMCR1_EL1", "fullname": "Performance Monitor Control Register 1", "enc": [3, 1, 15, 1, 0 ], "width": 64,
+ "fieldsets": [{"fields": [
+ {"name": "EL0_A32_PMC_0_7", "msb": 7, "lsb": 0},
+ {"name": "EL0_A64_PMC_0_7", "msb": 15, "lsb": 8},
+ {"name": "EL1_A64_PMC_0_7", "msb": 23, "lsb": 16},
+ {"name": "EL3_A64_PMC_0_7", "msb": 31, "lsb": 24},
+ {"name": "EL0_A32_PMC_8_9", "msb": 33, "lsb": 32},
+ {"name": "EL0_A64_PMC_8_9", "msb": 41, "lsb": 40},
+ {"name": "EL1_A64_PMC_8_9", "msb": 49, "lsb": 48},
+ {"name": "EL3_A64_PMC_8_9", "msb": 57, "lsb": 56}
+ ]}]},
+ {"index": 0, "name": "PMCR2_EL1", "fullname": "Performance Monitor Control Register 2", "enc": [3, 1, 15, 2, 0 ], "width": 64},
+ {"index": 0, "name": "PMCR3_EL1", "fullname": "Performance Monitor Control Register 3", "enc": [3, 1, 15, 3, 0 ], "width": 64},
+ {"index": 0, "name": "PMCR4_EL1", "fullname": "Performance Monitor Control Register 4", "enc": [3, 1, 15, 4, 0 ], "width": 64},
+ {"index": 0, "name": "PMESR0_EL1", "fullname": "Performance Monitor Event Selection Register 0", "enc": [3, 1, 15, 5, 0 ], "width": 64,
+ "fieldsets": [{"fields": [
+ {"name": "PMC2_EVENT_SEL", "msb": 7, "lsb": 0},
+ {"name": "PMC3_EVENT_SEL", "msb": 15, "lsb": 8},
+ {"name": "PMC4_EVENT_SEL", "msb": 23, "lsb": 16},
+ {"name": "PMC5_EVENT_SEL", "msb": 31, "lsb": 24}
+ ]}]},
+ {"index": 0, "name": "PMESR1_EL1", "fullname": "Performance Monitor Event Selection Register 1", "enc": [3, 1, 15, 6, 0 ], "width": 64,
+ "fieldsets": [{"fields": [
+ {"name": "PMC6_EVENT_SEL", "msb": 7, "lsb": 0},
+ {"name": "PMC7_EVENT_SEL", "msb": 15, "lsb": 8},
+ {"name": "PMC8_EVENT_SEL", "msb": 23, "lsb": 16},
+ {"name": "PMC9_EVENT_SEL", "msb": 31, "lsb": 24}
+ ]}]},
+ {"index": 0, "name": "PMSR_EL1", "fullname": "Performance Monitor Status Register", "enc": [3, 1, 15, 13, 0 ], "width": 64,
+ "fieldsets": [{"fields": [
+ {"name": "PMC0_OVERFLOW", "msb": 0, "lsb": 0},
+ {"name": "PMC1_OVERFLOW", "msb": 1, "lsb": 1},
+ {"name": "PMC2_OVERFLOW", "msb": 2, "lsb": 2},
+ {"name": "PMC3_OVERFLOW", "msb": 3, "lsb": 3},
+ {"name": "PMC4_OVERFLOW", "msb": 4, "lsb": 4},
+ {"name": "PMC5_OVERFLOW", "msb": 5, "lsb": 5},
+ {"name": "PMC6_OVERFLOW", "msb": 6, "lsb": 6},
+ {"name": "PMC7_OVERFLOW", "msb": 7, "lsb": 7},
+ {"name": "PMC8_OVERFLOW", "msb": 8, "lsb": 8},
+ {"name": "PMC9_OVERFLOW", "msb": 9, "lsb": 9}
+ ]}]},
+ {"index": 0, "name": "PMC0_EL1", "fullname": "Performance Monitor Counter 0", "enc": [3, 2, 15, 0, 0 ], "width": 64},
+ {"index": 0, "name": "PMC1_EL1", "fullname": "Performance Monitor Counter 1", "enc": [3, 2, 15, 1, 0 ], "width": 64},
+ {"index": 0, "name": "PMC2_EL1", "fullname": "Performance Monitor Counter 2", "enc": [3, 2, 15, 2, 0 ], "width": 64},
+ {"index": 0, "name": "PMC3_EL1", "fullname": "Performance Monitor Counter 3", "enc": [3, 2, 15, 3, 0 ], "width": 64},
+ {"index": 0, "name": "PMC4_EL1", "fullname": "Performance Monitor Counter 4", "enc": [3, 2, 15, 4, 0 ], "width": 64},
+ {"index": 0, "name": "PMC5_EL1", "fullname": "Performance Monitor Counter 5", "enc": [3, 2, 15, 5, 0 ], "width": 64},
+ {"index": 0, "name": "PMC6_EL1", "fullname": "Performance Monitor Counter 6", "enc": [3, 2, 15, 6, 0 ], "width": 64},
+ {"index": 0, "name": "PMC7_EL1", "fullname": "Performance Monitor Counter 7", "enc": [3, 2, 15, 7, 0 ], "width": 64},
+ {"index": 0, "name": "PMC8_EL1", "fullname": "Performance Monitor Counter 8", "enc": [3, 2, 15, 9, 0 ], "width": 64},
+ {"index": 0, "name": "PMC9_EL1", "fullname": "Performance Monitor Counter 9", "enc": [3, 2, 15, 10, 0 ], "width": 64},
+ {"index": 0, "name": "LSU_ERR_STS_EL1", "fullname": "Load-Store Unit Error Status", "enc": [3, 3, 15, 0, 0 ], "width": 64},
+ {"index": 0, "name": "E_LSU_ERR_STS_EL1", "fullname": "Load-Store Unit Error Status (E-core)", "enc": [3, 3, 15, 2, 0 ], "width": 64},
+ {"index": 0, "name": "LSU_ERR_CTL_EL1", "fullname": "Load-Store Unit Error Control", "enc": [3, 3, 15, 1, 0 ], "width": 64},
+ {"index": 0, "name": "L2C_ERR_STS_EL1", "fullname": "L2 Cache Error Status", "enc": [3, 3, 15, 8, 0 ], "width": 64},
+ {"index": 0, "name": "L2C_ERR_ADR_EL1", "fullname": "L2 Cache Address", "enc": [3, 3, 15, 9, 0 ], "width": 64},
+ {"index": 0, "name": "L2C_ERR_INF_EL1", "fullname": "L2 Cache Error Information", "enc": [3, 3, 15, 10, 0 ], "width": 64},
+ {"index": 0, "name": "FED_ERR_STS_EL1", "fullname": "FED Error Status", "enc": [3, 4, 15, 0, 0 ], "width": 64},
+ {"index": 0, "name": "E_FED_ERR_STS_EL1", "fullname": "FED Error Status (E-Core)", "enc": [3, 4, 15, 0, 2 ], "width": 64},
+ {"index": 0, "name": "APCTL_EL1", "fullname": "Pointer Authentication Control", "enc": [3, 4, 15, 0, 4 ], "width": 64},
+ {"index": 0, "name": "KERNELKEYLO_EL1", "fullname": "Pointer Authentication Kernel Key Low", "enc": [3, 4, 15, 1, 0 ], "width": 64},
+ {"index": 0, "name": "KERNELKEYHI_EL1", "fullname": "Pointer Authentication Kernel Key High", "enc": [3, 4, 15, 1, 1 ], "width": 64},
+ {"index": 0, "name": "VMSA_LOCK_EL1", "fullname": "Virtual Memory System Architecture Lock", "enc": [3, 4, 15, 1, 2 ], "width": 64},
+ {"index": 0, "name": "AMX_CTL_EL1", "fullname": "AMX Control (EL1)", "enc": [3, 4, 15, 1, 4 ], "width": 64,
+ "fieldsets": [{"fields": [
+ {"name": "EN", "msb": 63, "lsb": 63}
+ ]}]},
+ {"index": 0, "name": "APRR_EL0", "fullname": "APRR EL0", "enc": [3, 4, 15, 2, 0 ], "width": 64},
+ {"index": 0, "name": "APRR_EL1", "fullname": "APRR EL1", "enc": [3, 4, 15, 2, 1 ], "width": 64},
+ {"index": 0, "name": "CTRR_LOCK_EL1", "fullname": "CTRR Lock", "enc": [3, 4, 15, 2, 2 ], "width": 64},
+ {"index": 0, "name": "CTRR_A_LWR_EL1", "fullname": "CTRR A Lower Address (EL1)", "enc": [3, 4, 15, 2, 3 ], "width": 64},
+ {"index": 0, "name": "CTRR_A_UPR_EL1", "fullname": "CTRR A Upper Address (EL1)", "enc": [3, 4, 15, 2, 4 ], "width": 64},
+ {"index": 0, "name": "CTRR_CTL_EL1", "fullname": "CTRR Control (EL1)", "enc": [3, 4, 15, 2, 5 ], "width": 64},
+ {"index": 0, "name": "APRR_JIT_ENABLE_EL2", "fullname": "APRR JIT Enable", "enc": [3, 4, 15, 2, 6 ], "width": 64},
+ {"index": 0, "name": "APRR_JIT_MASK_EL2", "fullname": "APRR JIT Mask", "enc": [3, 4, 15, 2, 7 ], "width": 64},
+ {"index": 0, "name": "AMX_CTL_EL12", "fullname": "AMX Control (EL12)", "enc": [3, 4, 15, 4, 6 ], "width": 64},
+ {"index": 0, "name": "AMX_CTL_EL2", "fullname": "AMX Control (EL2)", "enc": [3, 4, 15, 4, 7 ], "width": 64,
+ "fieldsets": [{"fields": [
+ {"name": "EN", "msb": 63, "lsb": 63},
+ {"name": "EN_EL1", "msb": 62, "lsb": 62}
+ ]}]},
+ {"index": 0, "name": "SPRR_PERM_EL20_SILLY_THING", "fullname": "SPRR Permission Configuration Register (EL20, useless)", "enc": [3, 4, 15, 5, 1 ], "width": 64},
+ {"index": 0, "name": "SPRR_PERM_EL02", "fullname": "SPRR Permission Configuration Register (EL02)", "enc": [3, 4, 15, 5, 2 ], "width": 64},
+ {"index": 0, "name": "SPRR_KMASK0_EL12", "fullname": "SPRR Kernel Permission Unlock Mask (EL12)", "enc": [3, 4, 15, 6, 0 ], "width": 32},
+ {"index": 0, "name": "SPRR_UMASK0_EL2", "fullname": "SPRR Permission Unlock Mask 0 (EL2)", "enc": [3, 4, 15, 7, 0 ], "width": 32},
+ {"index": 0, "name": "SPRR_UMASK1_EL2", "fullname": "SPRR Permission Unlock Mask 1 (EL2)", "enc": [3, 4, 15, 7, 1 ], "width": 32},
+ {"index": 0, "name": "SPRR_UMASK2_EL2", "fullname": "SPRR Permission Unlock Mask 2 (EL2)", "enc": [3, 4, 15, 7, 2 ], "width": 32},
+ {"index": 0, "name": "SPRR_UMASK3_EL2", "fullname": "SPRR Permission Unlock Mask 3 (EL2)", "enc": [3, 4, 15, 7, 3 ], "width": 32},
+ {"index": 0, "name": "SPRR_UMASK0_EL12", "fullname": "SPRR Permission Unlock Mask 0 (EL12)", "enc": [3, 4, 15, 8, 0 ], "width": 32},
+ {"index": 0, "name": "SPRR_UMASK1_EL12", "fullname": "SPRR Permission Unlock Mask 1 (EL12)", "enc": [3, 4, 15, 8, 1 ], "width": 32},
+ {"index": 0, "name": "SPRR_UMASK2_EL12", "fullname": "SPRR Permission Unlock Mask 2 (EL12)", "enc": [3, 4, 15, 8, 2 ], "width": 32},
+ {"index": 0, "name": "SPRR_UMASK3_EL12", "fullname": "SPRR Permission Unlock Mask 3 (EL12)", "enc": [3, 4, 15, 8, 3 ], "width": 32},
+ {"index": 0, "name": "CNTPCT_ALIAS_EL0", "fullname": "Physical timer counter register", "enc": [3, 4, 15, 10, 5 ], "width": 64},
+ {"index": 0, "name": "CNTVCT_ALIAS_EL0", "fullname": "Virtual timer counter register", "enc": [3, 4, 15, 10, 6 ], "width": 64},
+ {"index": 0, "name": "CTRR_A_LWR_EL2", "fullname": "CTRR A Lower Address (EL2)", "enc": [3, 4, 15, 11, 0 ], "width": 64},
+ {"index": 0, "name": "CTRR_A_UPR_EL2", "fullname": "CTRR A Upper Address (EL2)", "enc": [3, 4, 15, 11, 1 ], "width": 64},
+ {"index": 0, "name": "CTRR_CTL_EL2", "fullname": "CTRR Control (EL2)", "enc": [3, 4, 15, 11, 4 ], "width": 64},
+ {"index": 0, "name": "CTRR_LOCK_EL2", "fullname": "CTRR Lock", "enc": [3, 4, 15, 11, 5 ], "width": 64},
+ {"index": 0, "name": "IPI_RR_LOCAL_EL1", "fullname": "IPI Request Register (Local)", "enc": [3, 5, 15, 0, 0 ], "width": 64},
+ {"index": 0, "name": "IPI_RR_GLOBAL_EL1", "fullname": "IPI Request Register (Global)", "enc": [3, 5, 15, 0, 1 ], "width": 64},
+ {"index": 0, "name": "DPC_ERR_STS_EL1", "fullname": "DPC Error Status", "enc": [3, 5, 15, 0, 5 ], "width": 64},
+ {"index": 0, "name": "IPI_SR_EL1", "fullname": "IPI Status Register", "enc": [3, 5, 15, 1, 1 ], "width": 64,
+ "fieldsets": [{"fields": [
+ {"name": "PENDING", "msb": 0, "lsb": 0}
+ ]}]},
+ {"index": 0, "name": "VM_TMR_LR_EL2", "fullname": "VM Timer Link Register", "enc": [3, 5, 15, 1, 2 ], "width": 64},
+ {"index": 0, "name": "VM_TMR_FIQ_ENA_EL2", "fullname": "VM Timer FIQ Enable", "enc": [3, 5, 15, 1, 3 ], "width": 64,
+ "fieldsets": [{"fields": [
+ {"name": "ENA_V", "msb": 0, "lsb": 0},
+ {"name": "ENA_P", "msb": 1, "lsb": 1}
+ ]}]},
+ {"index": 0, "name": "IPI_CR_EL1", "fullname": "IPI Control Register", "enc": [3, 5, 15, 3, 1 ], "width": 64},
+ {"index": 0, "name": "ACC_CFG_EL1", "fullname": "Apple Core Cluster Configuration", "enc": [3, 5, 15, 4, 0 ], "width": 64},
+ {"index": 0, "name": "CYC_OVRD_EL1", "fullname": "Cyclone Override", "enc": [3, 5, 15, 5, 0 ], "width": 64},
+ {"index": 0, "name": "ACC_OVRD_EL1", "fullname": "Apple Core Cluster Override", "enc": [3, 5, 15, 6, 0 ], "width": 64},
+ {"index": 0, "name": "ACC_EBLK_OVRD_EL1", "fullname": "Apple Core Cluster E-Block Override", "enc": [3, 5, 15, 6, 1 ], "width": 64},
+ {"index": 0, "name": "MMU_ERR_STS_EL1", "fullname": "MMU Error Status", "enc": [3, 6, 15, 0, 0 ], "width": 64},
+ {"index": 0, "name": "AFSR1_GL1", "fullname": "Auxiliary Fault Status Register 1 (GL1)", "enc": [3, 6, 15, 0, 1 ], "width": 64},
+ {"index": 0, "name": "AFSR1_GL2", "fullname": "Auxiliary Fault Status Register 1 (GL2)", "enc": [3, 6, 15, 0, 2 ], "width": 64},
+ {"index": 0, "name": "AFSR1_GL12", "fullname": "Auxiliary Fault Status Register 1 (GL12)", "enc": [3, 6, 15, 0, 3 ], "width": 64},
+ {"index": 0, "name": "SPRR_CONFIG_EL1", "fullname": "SPRR Configuration Register (EL1)", "enc": [3, 6, 15, 1, 0 ], "width": 64,
+ "fieldsets": [{"fields": [
+ {"name": "EN", "msb": 0, "lsb": 0},
+ {"name": "LOCK_CONFIG", "msb": 1, "lsb": 1},
+ {"name": "LOCK_PERM", "msb": 4, "lsb": 4},
+ {"name": "LOCK_KERNEL_PERM", "msb": 5, "lsb": 5}
+ ]}]},
+ {"index": 0, "name": "GXF_CONFIG_EL1", "fullname": "GXF Configuration Register (EL1)", "enc": [3, 6, 15, 1, 2 ], "width": 64,
+ "fieldsets": [{"fields": [
+ {"name": "EN", "msb": 0, "lsb": 0}
+ ]}]},
+ {"index": 0, "name": "SPRR_UNK1_EL1", "fullname": "SPRR Unknown (EL1)", "enc": [3, 6, 15, 1, 3 ], "width": 64},
+ {"index": 0, "name": "GXF_CONFIG_EL2", "fullname": "GXF Configuration Register (EL2)", "enc": [3, 6, 15, 1, 4 ], "width": 64},
+ {"index": 0, "name": "SPRR_PERM_EL0", "fullname": "SPRR Permission Configuration Register (EL0)", "enc": [3, 6, 15, 1, 5 ], "width": 64},
+ {"index": 0, "name": "SPRR_PERM_EL1", "fullname": "SPRR Permission Configuration Register (EL1)", "enc": [3, 6, 15, 1, 6 ], "width": 64},
+ {"index": 0, "name": "SPRR_PERM_EL2", "fullname": "SPRR Permission Configuration Register (EL2)", "enc": [3, 6, 15, 1, 7 ], "width": 64},
+ {"index": 0, "name": "E_MMU_ERR_STS_EL1", "fullname": "MMU Error Status (E-Core)", "enc": [3, 6, 15, 2, 0 ], "width": 64},
+ {"index": 0, "name": "APGAKeyLo_EL12", "fullname": "Pointer Authentication Key A for Code Low (EL12)", "enc": [3, 6, 15, 2, 1 ], "width": 64},
+ {"index": 0, "name": "APGAKeyHi_EL12", "fullname": "Pointer Authentication Key A for Code High (EL12)", "enc": [3, 6, 15, 2, 2 ], "width": 64},
+ {"index": 0, "name": "KERNELKEYLO_EL12", "fullname": "Pointer Authentication Kernel Key Low (EL12)", "enc": [3, 6, 15, 2, 3 ], "width": 64},
+ {"index": 0, "name": "KERNELKEYHI_EL12", "fullname": "Pointer Authentication Kernel Key High (EL12)", "enc": [3, 6, 15, 2, 4 ], "width": 64},
+ {"index": 0, "name": "AFPCR_EL0", "fullname": "Apple Floating-Point Control Register", "enc": [3, 6, 15, 2, 5 ], "width": 64},
+ {"index": 0, "name": "AIDR2_EL1", "fullname": "Apple ID Register 2", "enc": [3, 6, 15, 2, 7 ], "width": 64},
+ {"index": 0, "name": "SPRR_UMASK0_EL1", "fullname": "SPRR Permission Unlock Mask 0 (EL1)", "enc": [3, 6, 15, 3, 0 ], "width": 32},
+ {"index": 0, "name": "SPRR_KMASK0_EL1", "fullname": "SPRR Kernel Permission Unlock Mask 0 (EL1)", "enc": [3, 6, 15, 3, 1 ], "width": 32},
+ {"index": 0, "name": "SPRR_KMASK0_EL2", "fullname": "SPRR Kernel Permission Unlock Mask 0 (EL2)", "enc": [3, 6, 15, 3, 2 ], "width": 32},
+ {"index": 0, "name": "SPRR_UMASK1_EL1", "fullname": "SPRR Permission Unlock Mask 1 (EL1)", "enc": [3, 6, 15, 3, 3 ], "width": 32},
+ {"index": 0, "name": "SPRR_UMASK2_EL1", "fullname": "SPRR Permission Unlock Mask 2 (EL1)", "enc": [3, 6, 15, 3, 4 ], "width": 32},
+ {"index": 0, "name": "SPRR_UMASK3_EL1", "fullname": "SPRR Permission Unlock Mask 3 (EL1)", "enc": [3, 6, 15, 3, 5 ], "width": 32},
+ {"index": 0, "name": "SPRR_KMASK1_EL1", "fullname": "SPRR Kernel Permission Unlock Mask 1 (EL12)", "enc": [3, 6, 15, 4, 2 ], "width": 32},
+ {"index": 0, "name": "SPRR_KMASK2_EL1", "fullname": "SPRR Kernel Permission Unlock Mask 2 (EL12)", "enc": [3, 6, 15, 4, 3 ], "width": 32},
+ {"index": 0, "name": "SPRR_KMASK3_EL1", "fullname": "SPRR Kernel Permission Unlock Mask 3 (EL12)", "enc": [3, 6, 15, 4, 4 ], "width": 32},
+ {"index": 0, "name": "SPRR_KMASK1_EL2", "fullname": "SPRR Kernel Permission Unlock Mask 1 (EL12)", "enc": [3, 6, 15, 5, 1 ], "width": 32},
+ {"index": 0, "name": "SPRR_KMASK2_EL2", "fullname": "SPRR Kernel Permission Unlock Mask 2 (EL12)", "enc": [3, 6, 15, 5, 2 ], "width": 32},
+ {"index": 0, "name": "SPRR_KMASK3_EL2", "fullname": "SPRR Kernel Permission Unlock Mask 3 (EL12)", "enc": [3, 6, 15, 5, 3 ], "width": 32},
+ {"index": 0, "name": "SPRR_KMASK0_EL12", "fullname": "SPRR Kernel Permission Unlock Mask 0 (EL12)", "enc": [3, 6, 15, 6, 0 ], "width": 32},
+ {"index": 0, "name": "SPRR_KMASK1_EL12", "fullname": "SPRR Kernel Permission Unlock Mask 1 (EL12)", "enc": [3, 6, 15, 6, 1 ], "width": 32},
+ {"index": 0, "name": "SPRR_KMASK2_EL12", "fullname": "SPRR Kernel Permission Unlock Mask 2 (EL12)", "enc": [3, 6, 15, 6, 2 ], "width": 32},
+ {"index": 0, "name": "SPRR_KMASK3_EL12", "fullname": "SPRR Kernel Permission Unlock Mask 3 (EL12)", "enc": [3, 6, 15, 6, 3 ], "width": 32},
+ {"index": 0, "name": "APIAKeyLo_EL12", "fullname": "Pointer Authentication Key A for Instruction Low (EL12)", "enc": [3, 6, 15, 7, 0 ], "width": 64},
+ {"index": 0, "name": "APIAKeyHi_EL12", "fullname": "Pointer Authentication Key A for Instruction High (EL12)", "enc": [3, 6, 15, 7, 1 ], "width": 64},
+ {"index": 0, "name": "APIBKeyLo_EL12", "fullname": "Pointer Authentication Key A for Instruction Low (EL12)", "enc": [3, 6, 15, 7, 2 ], "width": 64},
+ {"index": 0, "name": "APIBKeyHi_EL12", "fullname": "Pointer Authentication Key A for Instruction High (EL12)", "enc": [3, 6, 15, 7, 3 ], "width": 64},
+ {"index": 0, "name": "APDAKeyLo_EL12", "fullname": "Pointer Authentication Key A for Data Low (EL12)", "enc": [3, 6, 15, 7, 4 ], "width": 64},
+ {"index": 0, "name": "APDAKeyHi_EL12", "fullname": "Pointer Authentication Key A for Data High (EL12)", "enc": [3, 6, 15, 7, 5 ], "width": 64},
+ {"index": 0, "name": "APDBKeyLo_EL12", "fullname": "Pointer Authentication Key A for Data Low (EL12)", "enc": [3, 6, 15, 7, 6 ], "width": 64},
+ {"index": 0, "name": "APDBKeyHi_EL12", "fullname": "Pointer Authentication Key A for Data High (EL12)", "enc": [3, 6, 15, 7, 7 ], "width": 64},
+ {"index": 0, "name": "GXF_STATUS_EL1", "fullname": "GXF Status Register", "enc": [3, 6, 15, 8, 0 ], "width": 64,
+ "fieldsets": [{"fields": [
+ {"name": "GUARDED", "msb": 0, "lsb": 0}
+ ]}]},
+ {"index": 0, "name": "GXF_ENTER_EL1", "fullname": "GXF genter Entry Vector Register (EL1)", "enc": [3, 6, 15, 8, 1 ], "width": 64},
+ {"index": 0, "name": "GXF_ABORT_EL1", "fullname": "GXF Abort Vector Register (EL1)", "enc": [3, 6, 15, 8, 2 ], "width": 64},
+ {"index": 0, "name": "VBAR_GL12", "fullname": "Vector Base Address Register (GL12)", "enc": [3, 6, 15, 9, 2 ], "width": 64},
+ {"index": 0, "name": "SPSR_GL12", "fullname": "Saved Program Status Register (GL12)", "enc": [3, 6, 15, 9, 3 ], "width": 64},
+ {"index": 0, "name": "ASPSR_GL12", "fullname": "ASPSR (GL12)", "enc": [3, 6, 15, 9, 4 ], "width": 64},
+ {"index": 0, "name": "ESR_GL12", "fullname": "Exception Syndrome Register (GL12)", "enc": [3, 6, 15, 9, 5 ], "width": 64},
+ {"index": 0, "name": "ELR_GL12", "fullname": "Exception Link Register (GL12)", "enc": [3, 6, 15, 9, 6 ], "width": 64},
+ {"index": 0, "name": "SP_GL12", "fullname": "Stack Pointer Register (GL12)", "enc": [3, 6, 15, 10, 0 ], "width": 64},
+ {"index": 0, "name": "TPIDR_GL1", "fullname": "Software Thread ID Register (GL1)", "enc": [3, 6, 15, 10, 1 ], "width": 64},
+ {"index": 0, "name": "VBAR_GL1", "fullname": "Vector Base Address Register (GL1)", "enc": [3, 6, 15, 10, 2 ], "width": 64},
+ {"index": 0, "name": "SPSR_GL1", "fullname": "Saved Program Status Register (GL1)", "enc": [3, 6, 15, 10, 3 ], "width": 64},
+ {"index": 0, "name": "ASPSR_GL1", "fullname": "ASPSR (GL1)", "enc": [3, 6, 15, 10, 4 ], "width": 64},
+ {"index": 0, "name": "ESR_GL1", "fullname": "Exception Syndrome Register (GL1)", "enc": [3, 6, 15, 10, 5 ], "width": 64},
+ {"index": 0, "name": "ELR_GL1", "fullname": "Exception Link Register (GL1)", "enc": [3, 6, 15, 10, 6 ], "width": 64},
+ {"index": 0, "name": "FAR_GL1", "fullname": "Fault Address Register (GL1)", "enc": [3, 6, 15, 10, 7 ], "width": 64},
+ {"index": 0, "name": "TPIDR_GL2", "fullname": "Software Thread ID Register (GL2)", "enc": [3, 6, 15, 11, 1 ], "width": 64},
+ {"index": 0, "name": "VBAR_GL2", "fullname": "Vector Base Address Register (GL2)", "enc": [3, 6, 15, 11, 2 ], "width": 64},
+ {"index": 0, "name": "SPSR_GL2", "fullname": "Saved Program Status Register (GL2)", "enc": [3, 6, 15, 11, 3 ], "width": 64},
+ {"index": 0, "name": "ASPSR_GL2", "fullname": "ASPSR (GL2)", "enc": [3, 6, 15, 11, 4 ], "width": 64},
+ {"index": 0, "name": "ESR_GL2", "fullname": "Exception Syndrome Register (GL2)", "enc": [3, 6, 15, 11, 5 ], "width": 64},
+ {"index": 0, "name": "ELR_GL2", "fullname": "Exception Link Register (GL2)", "enc": [3, 6, 15, 11, 6 ], "width": 64},
+ {"index": 0, "name": "FAR_GL2", "fullname": "Fault Address Register (GL2)", "enc": [3, 6, 15, 11, 7 ], "width": 64},
+ {"index": 0, "name": "GXF_ENTER_EL2", "fullname": "GXF genter Entry Vector Register (EL2)", "enc": [3, 6, 15, 12, 0 ], "width": 64},
+ {"index": 0, "name": "GXF_ABORT_EL2", "fullname": "GXF Abort Vector Register (EL2)", "enc": [3, 6, 15, 12, 1 ], "width": 64},
+ {"index": 0, "name": "APCTL_EL2", "fullname": "Pointer Authentication Control (EL2)", "enc": [3, 6, 15, 12, 2 ], "width": 64},
+ {"index": 0, "name": "APSTS_EL2_MAYBE", "fullname": "Pointer Authentication Status (EL2, maybe)", "enc": [3, 6, 15, 12, 3 ], "width": 64},
+ {"index": 0, "name": "APSTS_EL1", "fullname": "Pointer Authentication Status", "enc": [3, 6, 15, 12, 4 ], "width": 64},
+ {"index": 0, "name": "SPRR_CONFIG_EL2", "fullname": "SPRR Configuration Register (EL2)", "enc": [3, 6, 15, 14, 2 ], "width": 64},
+ {"index": 0, "name": "SPRR_UNK1_EL2", "fullname": "SPRR Unknown (EL2)", "enc": [3, 6, 15, 14, 3 ], "width": 64},
+ {"index": 0, "name": "APVMKEYLO_EL2", "fullname": "Pointer Authentication VM Machine Key Low", "enc": [3, 6, 15, 14, 4 ], "width": 64},
+ {"index": 0, "name": "APVMKEYHI_EL2", "fullname": "Pointer Authentication VM Machine Key High", "enc": [3, 6, 15, 14, 5 ], "width": 64},
+ {"index": 0, "name": "ACTLR_EL12", "fullname": "Auxiliary Control Register (EL12)", "enc": [3, 6, 15, 14, 6 ], "width": 64},
+ {"index": 0, "name": "APSTS_EL12", "fullname": "Pointer Authentication Status (EL12)", "enc": [3, 6, 15, 14, 7 ], "width": 64},
+ {"index": 0, "name": "APCTL_EL12", "fullname": "Pointer Authentication Control (EL12)", "enc": [3, 6, 15, 15, 0 ], "width": 64},
+ {"index": 0, "name": "GXF_CONFIG_EL12", "fullname": "GXF Configuration Register (EL12)", "enc": [3, 6, 15, 15, 1 ], "width": 64},
+ {"index": 0, "name": "GXF_ENTER_EL12", "fullname": "GXF genter Entry Vector Register (EL12)", "enc": [3, 6, 15, 15, 2 ], "width": 64},
+ {"index": 0, "name": "GXF_ABORT_EL12", "fullname": "GXF Abort Vector Register (EL12)", "enc": [3, 6, 15, 15, 3 ], "width": 64},
+ {"index": 0, "name": "SPRR_CONFIG_EL12", "fullname": "SPRR Configuration Register (EL12)", "enc": [3, 6, 15, 15, 4 ], "width": 64},
+ {"index": 0, "name": "SPRR_UNK1_EL12", "fullname": "SPRR Unknown (EL2)", "enc": [3, 6, 15, 15, 5 ], "width": 64},
+ {"index": 0, "name": "SPRR_PERM_EL12", "fullname": "SPRR Permission Configuration Register (EL12)", "enc": [3, 6, 15, 15, 7 ], "width": 64},
+ {"index": 0, "name": "UPMCR0_EL1", "fullname": "Uncore Performance Monitor Control Register 0", "enc": [3, 7, 15, 0, 4 ], "width": 64},
+ {"index": 0, "name": "UPMESR0_EL1", "fullname": "Uncore Performance Monitor Event Selection Register 0", "enc": [3, 7, 15, 1, 4 ], "width": 64},
+ {"index": 0, "name": "UPMECM0_EL1", "fullname": "Uncore Performance Monitor Event Core Mask 0", "enc": [3, 7, 15, 3, 4 ], "width": 64},
+ {"index": 0, "name": "UPMECM1_EL1", "fullname": "Uncore Performance Monitor Event Core Mask 1", "enc": [3, 7, 15, 4, 4 ], "width": 64},
+ {"index": 0, "name": "UPMPCM_EL1", "fullname": "Uncore Performance Monitor PMI Core Mask", "enc": [3, 7, 15, 5, 4 ], "width": 64},
+ {"index": 0, "name": "UPMSR_EL1", "fullname": "Uncore Performance Monitor Status Register", "enc": [3, 7, 15, 6, 4 ], "width": 64},
+ {"index": 0, "name": "UPMECM2_EL1", "fullname": "Uncore Performance Monitor Event Core Mask 2", "enc": [3, 7, 15, 8, 5 ], "width": 64},
+ {"index": 0, "name": "UPMECM3_EL1", "fullname": "Uncore Performance Monitor Event Core Mask 3", "enc": [3, 7, 15, 9, 5 ], "width": 64},
+ {"index": 0, "name": "UPMESR1_EL1", "fullname": "Uncore Performance Monitor Event Selection Register 1", "enc": [3, 7, 15, 11, 5 ], "width": 64},
+ {"index": 0, "name": "UPMC0_EL1", "fullname": "Uncore Performance Monitor Counter 0", "enc": [3, 7, 15, 7, 4 ], "width": 64},
+ {"index": 0, "name": "UPMC1_EL1", "fullname": "Uncore Performance Monitor Counter 1", "enc": [3, 7, 15, 8, 4 ], "width": 64},
+ {"index": 0, "name": "UPMC2_EL1", "fullname": "Uncore Performance Monitor Counter 2", "enc": [3, 7, 15, 9, 4 ], "width": 64},
+ {"index": 0, "name": "UPMC3_EL1", "fullname": "Uncore Performance Monitor Counter 3", "enc": [3, 7, 15, 10, 4 ], "width": 64},
+ {"index": 0, "name": "UPMC4_EL1", "fullname": "Uncore Performance Monitor Counter 4", "enc": [3, 7, 15, 11, 4 ], "width": 64},
+ {"index": 0, "name": "UPMC5_EL1", "fullname": "Uncore Performance Monitor Counter 5", "enc": [3, 7, 15, 12, 4 ], "width": 64},
+ {"index": 0, "name": "UPMC6_EL1", "fullname": "Uncore Performance Monitor Counter 6", "enc": [3, 7, 15, 13, 4 ], "width": 64},
+ {"index": 0, "name": "UPMC7_EL1", "fullname": "Uncore Performance Monitor Counter 7", "enc": [3, 7, 15, 14, 4 ], "width": 64},
+ {"index": 0, "name": "UPMC8_EL1", "fullname": "Uncore Performance Monitor Counter 8", "enc": [3, 7, 15, 0, 5 ], "width": 64},
+ {"index": 0, "name": "UPMC9_EL1", "fullname": "Uncore Performance Monitor Counter 9", "enc": [3, 7, 15, 1, 5 ], "width": 64},
+ {"index": 0, "name": "UPMC10_EL1", "fullname": "Uncore Performance Monitor Counter 10", "enc": [3, 7, 15, 2, 5 ], "width": 64},
+ {"index": 0, "name": "UPMC11_EL1", "fullname": "Uncore Performance Monitor Counter 11", "enc": [3, 7, 15, 3, 5 ], "width": 64},
+ {"index": 0, "name": "UPMC12_EL1", "fullname": "Uncore Performance Monitor Counter 12", "enc": [3, 7, 15, 4, 5 ], "width": 64},
+ {"index": 0, "name": "UPMC13_EL1", "fullname": "Uncore Performance Monitor Counter 13", "enc": [3, 7, 15, 5, 5 ], "width": 64},
+ {"index": 0, "name": "UPMC14_EL1", "fullname": "Uncore Performance Monitor Counter 14", "enc": [3, 7, 15, 6, 5 ], "width": 64},
+ {"index": 0, "name": "UPMC15_EL1", "fullname": "Uncore Performance Monitor Counter 15", "enc": [3, 7, 15, 7, 5 ], "width": 64},
+ {"index": 0, "name": "HACR_EL2", "fullname": "Hypervisor Auxiliary Control Register", "enc": [3, 4, 1, 1, 7 ], "width": 64,
+ "fieldsets": [{"fields": [
+ {"name": "TRAP_CPU_EXT", "msb": 0, "lsb": 0},
+ {"name": "TRAP_AIDR", "msb": 4, "lsb": 4},
+ {"name": "TRAP_AMX", "msb": 10, "lsb": 10},
+ {"name": "TRAP_SPRR", "msb": 11, "lsb": 11},
+ {"name": "TRAP_GXF", "msb": 13, "lsb": 13},
+ {"name": "TRAP_CTRR", "msb": 14, "lsb": 14},
+ {"name": "TRAP_IPI", "msb": 16, "lsb": 16},
+ {"name": "TRAP_s3_4_c15_c5z6_x", "msb": 18, "lsb": 18},
+ {"name": "TRAP_s3_4_c15_c0z12_5", "msb": 19, "lsb": 19},
+ {"name": "GIC_CNTV", "msb": 20, "lsb": 20},
+ {"name": "TRAP_s3_4_c15_c10_4", "msb": 25, "lsb": 25},
+ {"name": "TRAP_SERROR_INFO", "msb": 48, "lsb": 48},
+ {"name": "TRAP_EHID", "msb": 49, "lsb": 49},
+ {"name": "TRAP_HID", "msb": 50, "lsb": 50},
+ {"name": "TRAP_s3_0_c15_c12_1z2", "msb": 51, "lsb": 51},
+ {"name": "TRAP_ACC", "msb": 52, "lsb": 52},
+ {"name": "TRAP_PM", "msb": 57, "lsb": 57},
+ {"name": "TRAP_UPM", "msb": 58, "lsb": 58},
+ {"name": "TRAP_s3_1z7_c15_cx_3", "msb": 59, "lsb": 59}
+ ]}]}
+]
diff --git a/tools/tools/arm_regs.json b/tools/tools/arm_regs.json
new file mode 100644
index 0000000..3edd7b3
--- /dev/null
+++ b/tools/tools/arm_regs.json
@@ -0,0 +1 @@
+[{"index": 0, "name": "ACCDATA_EL1", "fullname": "Accelerator Data", "enc": [3, 0, 13, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ACCDATA", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ACTLR_EL1", "fullname": "Auxiliary Control Register (EL1)", "enc": [3, 0, 1, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "ACTLR_EL2", "fullname": "Auxiliary Control Register (EL2)", "enc": [3, 4, 1, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "ACTLR_EL3", "fullname": "Auxiliary Control Register (EL3)", "enc": [3, 6, 1, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AFSR0_EL1", "fullname": "Auxiliary Fault Status Register 0 (EL1)", "enc": [3, 0, 5, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AFSR0_EL12", "fullname": "Auxiliary Fault Status Register 0 (EL1)", "enc": [3, 5, 5, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AFSR0_EL1", "fullname": "Auxiliary Fault Status Register 0 (EL2)", "enc": [3, 0, 5, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AFSR0_EL2", "fullname": "Auxiliary Fault Status Register 0 (EL2)", "enc": [3, 4, 5, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AFSR0_EL3", "fullname": "Auxiliary Fault Status Register 0 (EL3)", "enc": [3, 6, 5, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AFSR1_EL1", "fullname": "Auxiliary Fault Status Register 1 (EL1)", "enc": [3, 0, 5, 1, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AFSR1_EL12", "fullname": "Auxiliary Fault Status Register 1 (EL1)", "enc": [3, 5, 5, 1, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AFSR1_EL1", "fullname": "Auxiliary Fault Status Register 1 (EL2)", "enc": [3, 0, 5, 1, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AFSR1_EL2", "fullname": "Auxiliary Fault Status Register 1 (EL2)", "enc": [3, 4, 5, 1, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AFSR1_EL3", "fullname": "Auxiliary Fault Status Register 1 (EL3)", "enc": [3, 6, 5, 1, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AIDR_EL1", "fullname": "Auxiliary ID Register", "enc": [3, 1, 0, 0, 7], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AMAIR_EL1", "fullname": "Auxiliary Memory Attribute Indirection Register (EL1)", "enc": [3, 0, 10, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AMAIR_EL12", "fullname": "Auxiliary Memory Attribute Indirection Register (EL1)", "enc": [3, 5, 10, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AMAIR_EL1", "fullname": "Auxiliary Memory Attribute Indirection Register (EL2)", "enc": [3, 0, 10, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AMAIR_EL2", "fullname": "Auxiliary Memory Attribute Indirection Register (EL2)", "enc": [3, 4, 10, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AMAIR_EL3", "fullname": "Auxiliary Memory Attribute Indirection Register (EL3)", "enc": [3, 6, 10, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AMCFGR_EL0", "fullname": "Activity Monitors Configuration Register", "enc": [3, 3, 13, 2, 1], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "NCG", "msb": 31, "lsb": 28}, {"name": "HDBG", "msb": 24, "lsb": 24}, {"name": "SIZE", "msb": 13, "lsb": 8}, {"name": "N", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "AMCG1IDR_EL0", "fullname": "Activity Monitors Counter Group 1 Identification Register", "enc": [3, 3, 13, 2, 6], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "AMEVCNTOFF1<n>_EL2", "msb": 31, "lsb": 16}, {"name": "AMEVCNTR1<n>_EL0", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "AMCGCR_EL0", "fullname": "Activity Monitors Counter Group Configuration Register", "enc": [3, 3, 13, 2, 2], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "CG1NC", "msb": 15, "lsb": 8}, {"name": "CG0NC", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "AMCNTENCLR0_EL0", "fullname": "Activity Monitors Count Enable Clear Register 0", "enc": [3, 3, 13, 2, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P<n>", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "AMCNTENCLR1_EL0", "fullname": "Activity Monitors Count Enable Clear Register 1", "enc": [3, 3, 13, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P<n>", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "AMCNTENSET0_EL0", "fullname": "Activity Monitors Count Enable Set Register 0", "enc": [3, 3, 13, 2, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P<n>", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "AMCNTENSET1_EL0", "fullname": "Activity Monitors Count Enable Set Register 1", "enc": [3, 3, 13, 3, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P<n>", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "AMCR_EL0", "fullname": "Activity Monitors Control Register", "enc": [3, 3, 13, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "CG1RZ", "msb": 17, "lsb": 17}, {"name": "HDBG", "msb": 10, "lsb": 10}]}], "width": 64}, {"index": 0, "name": "AMEVCNTR00_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 1, "name": "AMEVCNTR01_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 2, "name": "AMEVCNTR02_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 3, "name": "AMEVCNTR03_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 4, "name": "AMEVCNTR04_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 5, "name": "AMEVCNTR05_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 6, "name": "AMEVCNTR06_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 7, "name": "AMEVCNTR07_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 8, "name": "AMEVCNTR08_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 9, "name": "AMEVCNTR09_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 10, "name": "AMEVCNTR010_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 11, "name": "AMEVCNTR011_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 12, "name": "AMEVCNTR012_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 13, "name": "AMEVCNTR013_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 14, "name": "AMEVCNTR014_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 15, "name": "AMEVCNTR015_EL0", "fullname": "Activity Monitors Event Counter Registers 0", "enc": [3, 3, 13, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AMEVCNTR10_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 1, "name": "AMEVCNTR11_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 2, "name": "AMEVCNTR12_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 3, "name": "AMEVCNTR13_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 4, "name": "AMEVCNTR14_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 5, "name": "AMEVCNTR15_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 6, "name": "AMEVCNTR16_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 7, "name": "AMEVCNTR17_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 8, "name": "AMEVCNTR18_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 9, "name": "AMEVCNTR19_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 10, "name": "AMEVCNTR110_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 11, "name": "AMEVCNTR111_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 12, "name": "AMEVCNTR112_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 13, "name": "AMEVCNTR113_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 14, "name": "AMEVCNTR114_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 15, "name": "AMEVCNTR115_EL0", "fullname": "Activity Monitors Event Counter Registers 1", "enc": [3, 3, 13, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AMEVCNTVOFF00_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 1, "name": "AMEVCNTVOFF01_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 2, "name": "AMEVCNTVOFF02_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 3, "name": "AMEVCNTVOFF03_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 4, "name": "AMEVCNTVOFF04_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 5, "name": "AMEVCNTVOFF05_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 6, "name": "AMEVCNTVOFF06_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 7, "name": "AMEVCNTVOFF07_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 8, "name": "AMEVCNTVOFF08_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 9, "name": "AMEVCNTVOFF09_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 10, "name": "AMEVCNTVOFF010_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 11, "name": "AMEVCNTVOFF011_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 12, "name": "AMEVCNTVOFF012_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 13, "name": "AMEVCNTVOFF013_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 14, "name": "AMEVCNTVOFF014_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 15, "name": "AMEVCNTVOFF015_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 0", "enc": [3, 4, 13, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AMEVCNTVOFF10_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 1, "name": "AMEVCNTVOFF11_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 2, "name": "AMEVCNTVOFF12_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 3, "name": "AMEVCNTVOFF13_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 4, "name": "AMEVCNTVOFF14_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 5, "name": "AMEVCNTVOFF15_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 6, "name": "AMEVCNTVOFF16_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 7, "name": "AMEVCNTVOFF17_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 8, "name": "AMEVCNTVOFF18_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 9, "name": "AMEVCNTVOFF19_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 10, "name": "AMEVCNTVOFF110_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 11, "name": "AMEVCNTVOFF111_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 12, "name": "AMEVCNTVOFF112_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 13, "name": "AMEVCNTVOFF113_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 14, "name": "AMEVCNTVOFF114_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 15, "name": "AMEVCNTVOFF115_EL2", "fullname": "Activity Monitors Event Counter Virtual Offset Registers 1", "enc": [3, 4, 13, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "AMEVTYPER00_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 1, "name": "AMEVTYPER01_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 2, "name": "AMEVTYPER02_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 3, "name": "AMEVTYPER03_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 4, "name": "AMEVTYPER04_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 5, "name": "AMEVTYPER05_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 6, "name": "AMEVTYPER06_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 7, "name": "AMEVTYPER07_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 8, "name": "AMEVTYPER08_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 9, "name": "AMEVTYPER09_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 10, "name": "AMEVTYPER010_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 11, "name": "AMEVTYPER011_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 12, "name": "AMEVTYPER012_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 13, "name": "AMEVTYPER013_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 14, "name": "AMEVTYPER014_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 15, "name": "AMEVTYPER015_EL0", "fullname": "Activity Monitors Event Type Registers 0", "enc": [3, 3, 13, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "AMEVTYPER10_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 1, "name": "AMEVTYPER11_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 2, "name": "AMEVTYPER12_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 3, "name": "AMEVTYPER13_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 4, "name": "AMEVTYPER14_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 5, "name": "AMEVTYPER15_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 6, "name": "AMEVTYPER16_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 7, "name": "AMEVTYPER17_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 8, "name": "AMEVTYPER18_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 9, "name": "AMEVTYPER19_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 10, "name": "AMEVTYPER110_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 11, "name": "AMEVTYPER111_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 12, "name": "AMEVTYPER112_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 13, "name": "AMEVTYPER113_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 14, "name": "AMEVTYPER114_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 15, "name": "AMEVTYPER115_EL0", "fullname": "Activity Monitors Event Type Registers 1", "enc": [3, 3, 13, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "evtCount", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "AMUSERENR_EL0", "fullname": "Activity Monitors User Enable Register", "enc": [3, 3, 13, 2, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "EN", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "APDAKeyHi_EL1", "fullname": "Pointer Authentication Key A for Data (bits[127:64]) ", "enc": [3, 0, 2, 2, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "APDAKeyLo_EL1", "fullname": "Pointer Authentication Key A for Data (bits[63:0]) ", "enc": [3, 0, 2, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "APDBKeyHi_EL1", "fullname": "Pointer Authentication Key B for Data (bits[127:64]) ", "enc": [3, 0, 2, 2, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "APDBKeyLo_EL1", "fullname": "Pointer Authentication Key B for Data (bits[63:0]) ", "enc": [3, 0, 2, 2, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "APGAKeyHi_EL1", "fullname": "Pointer Authentication Key A for Code (bits[127:64]) ", "enc": [3, 0, 2, 3, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "APGAKeyLo_EL1", "fullname": "Pointer Authentication Key A for Code (bits[63:0]) ", "enc": [3, 0, 2, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "APIAKeyHi_EL1", "fullname": "Pointer Authentication Key A for Instruction (bits[127:64]) ", "enc": [3, 0, 2, 1, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "APIAKeyLo_EL1", "fullname": "Pointer Authentication Key A for Instruction (bits[63:0]) ", "enc": [3, 0, 2, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "APIBKeyHi_EL1", "fullname": "Pointer Authentication Key B for Instruction (bits[127:64]) ", "enc": [3, 0, 2, 1, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "APIBKeyLo_EL1", "fullname": "Pointer Authentication Key B for Instruction (bits[63:0]) ", "enc": [3, 0, 2, 1, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S12E0R", "fullname": "Address Translate Stages 1 and 2 EL0 Read", "enc": [1, 4, 7, 8, 6], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S12E0W", "fullname": "Address Translate Stages 1 and 2 EL0 Write", "enc": [1, 4, 7, 8, 7], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S12E1R", "fullname": "Address Translate Stages 1 and 2 EL1 Read", "enc": [1, 4, 7, 8, 4], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S12E1W", "fullname": "Address Translate Stages 1 and 2 EL1 Write", "enc": [1, 4, 7, 8, 5], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S1E0R", "fullname": "Address Translate Stage 1 EL0 Read", "enc": [1, 0, 7, 8, 2], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S1E0W", "fullname": "Address Translate Stage 1 EL0 Write", "enc": [1, 0, 7, 8, 3], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S1E1RP", "fullname": "Address Translate Stage 1 EL1 Read PAN", "enc": [1, 0, 7, 9, 0], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S1E1R", "fullname": "Address Translate Stage 1 EL1 Read", "enc": [1, 0, 7, 8, 0], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S1E1WP", "fullname": "Address Translate Stage 1 EL1 Write PAN", "enc": [1, 0, 7, 9, 1], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S1E1W", "fullname": "Address Translate Stage 1 EL1 Write", "enc": [1, 0, 7, 8, 1], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S1E2R", "fullname": "Address Translate Stage 1 EL2 Read", "enc": [1, 4, 7, 8, 0], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S1E2W", "fullname": "Address Translate Stage 1 EL2 Write", "enc": [1, 4, 7, 8, 1], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S1E3R", "fullname": "Address Translate Stage 1 EL3 Read", "enc": [1, 6, 7, 8, 0], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "S1E3W", "fullname": "Address Translate Stage 1 EL3 Write", "enc": [1, 6, 7, 8, 1], "accessors": ["AT"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CCSIDR2_EL1", "fullname": "Current Cache Size ID Register 2", "enc": [3, 1, 0, 0, 2], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "NumSets", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CCSIDR_EL1", "fullname": "Current Cache Size ID Register", "enc": [3, 1, 0, 0, 0], "accessors": ["MRS"], "fieldsets": [{"instance": "IsFeatureImplemented(FEAT_CCIDX)", "fields": [{"name": "NumSets", "msb": 55, "lsb": 32}, {"name": "Associativity", "msb": 23, "lsb": 3}, {"name": "LineSize", "msb": 2, "lsb": 0}]}, {"instance": "!IsFeatureImplemented(FEAT_CCIDX)", "fields": [{"name": "NumSets", "msb": 27, "lsb": 13}, {"name": "Associativity", "msb": 12, "lsb": 3}, {"name": "LineSize", "msb": 2, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RCTX", "fullname": "Control Flow Prediction Restriction by Context", "enc": [1, 3, 7, 3, 4], "accessors": ["CFP"], "fieldsets": [{"fields": [{"name": "GVMID", "msb": 48, "lsb": 48}, {"name": "VMID", "msb": 47, "lsb": 32}, {"name": "NS", "msb": 26, "lsb": 26}, {"name": "EL", "msb": 25, "lsb": 24}, {"name": "GASID", "msb": 16, "lsb": 16}, {"name": "ASID", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CLIDR_EL1", "fullname": "Cache Level ID Register", "enc": [3, 1, 0, 0, 1], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Ttype<n>", "msb": 46, "lsb": 33}, {"name": "ICB", "msb": 32, "lsb": 30}, {"name": "LoUU", "msb": 29, "lsb": 27}, {"name": "LoC", "msb": 26, "lsb": 24}, {"name": "LoUIS", "msb": 23, "lsb": 21}, {"name": "Ctype<n>", "msb": 20, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTFRQ_EL0", "fullname": "Counter-timer Frequency register", "enc": [3, 3, 14, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTHCTL_EL2", "fullname": "Counter-timer Hypervisor Control register", "enc": [3, 4, 14, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "HCR_EL2.E2H == 1", "fields": [{"name": "EVNTIS", "msb": 17, "lsb": 17}, {"name": "EL1NVVCT", "msb": 16, "lsb": 16}, {"name": "EL1NVPCT", "msb": 15, "lsb": 15}, {"name": "EL1TVCT", "msb": 14, "lsb": 14}, {"name": "EL1TVT", "msb": 13, "lsb": 13}, {"name": "ECV", "msb": 12, "lsb": 12}, {"name": "EL1PTEN", "msb": 11, "lsb": 11}, {"name": "EL1PCTEN", "msb": 10, "lsb": 10}, {"name": "EL0PTEN", "msb": 9, "lsb": 9}, {"name": "EL0VTEN", "msb": 8, "lsb": 8}, {"name": "EVNTI", "msb": 7, "lsb": 4}, {"name": "EVNTDIR", "msb": 3, "lsb": 3}, {"name": "EVNTEN", "msb": 2, "lsb": 2}, {"name": "EL0VCTEN", "msb": 1, "lsb": 1}, {"name": "EL0PCTEN", "msb": 0, "lsb": 0}]}, {"instance": "HCR_EL2.E2H == 0", "fields": [{"name": "EVNTIS", "msb": 17, "lsb": 17}, {"name": "EL1NVVCT", "msb": 16, "lsb": 16}, {"name": "EL1NVPCT", "msb": 15, "lsb": 15}, {"name": "EL1TVCT", "msb": 14, "lsb": 14}, {"name": "EL1TVT", "msb": 13, "lsb": 13}, {"name": "ECV", "msb": 12, "lsb": 12}, {"name": "EVNTI", "msb": 7, "lsb": 4}, {"name": "EVNTDIR", "msb": 3, "lsb": 3}, {"name": "EVNTEN", "msb": 2, "lsb": 2}, {"name": "EL1PCEN", "msb": 1, "lsb": 1}, {"name": "EL1PCTEN", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTKCTL_EL1", "fullname": "Counter-timer Hypervisor Control register", "enc": [3, 0, 14, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "HCR_EL2.E2H == 1", "fields": [{"name": "EVNTIS", "msb": 17, "lsb": 17}, {"name": "EL1NVVCT", "msb": 16, "lsb": 16}, {"name": "EL1NVPCT", "msb": 15, "lsb": 15}, {"name": "EL1TVCT", "msb": 14, "lsb": 14}, {"name": "EL1TVT", "msb": 13, "lsb": 13}, {"name": "ECV", "msb": 12, "lsb": 12}, {"name": "EL1PTEN", "msb": 11, "lsb": 11}, {"name": "EL1PCTEN", "msb": 10, "lsb": 10}, {"name": "EL0PTEN", "msb": 9, "lsb": 9}, {"name": "EL0VTEN", "msb": 8, "lsb": 8}, {"name": "EVNTI", "msb": 7, "lsb": 4}, {"name": "EVNTDIR", "msb": 3, "lsb": 3}, {"name": "EVNTEN", "msb": 2, "lsb": 2}, {"name": "EL0VCTEN", "msb": 1, "lsb": 1}, {"name": "EL0PCTEN", "msb": 0, "lsb": 0}]}, {"instance": "HCR_EL2.E2H == 0", "fields": [{"name": "EVNTIS", "msb": 17, "lsb": 17}, {"name": "EL1NVVCT", "msb": 16, "lsb": 16}, {"name": "EL1NVPCT", "msb": 15, "lsb": 15}, {"name": "EL1TVCT", "msb": 14, "lsb": 14}, {"name": "EL1TVT", "msb": 13, "lsb": 13}, {"name": "ECV", "msb": 12, "lsb": 12}, {"name": "EVNTI", "msb": 7, "lsb": 4}, {"name": "EVNTDIR", "msb": 3, "lsb": 3}, {"name": "EVNTEN", "msb": 2, "lsb": 2}, {"name": "EL1PCEN", "msb": 1, "lsb": 1}, {"name": "EL1PCTEN", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTHP_CTL_EL2", "fullname": "Counter-timer Hypervisor Physical Timer Control register", "enc": [3, 4, 14, 2, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISTATUS", "msb": 2, "lsb": 2}, {"name": "IMASK", "msb": 1, "lsb": 1}, {"name": "ENABLE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTP_CTL_EL0", "fullname": "Counter-timer Hypervisor Physical Timer Control register", "enc": [3, 3, 14, 2, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISTATUS", "msb": 2, "lsb": 2}, {"name": "IMASK", "msb": 1, "lsb": 1}, {"name": "ENABLE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTHP_CVAL_EL2", "fullname": "Counter-timer Physical Timer CompareValue register (EL2)", "enc": [3, 4, 14, 2, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTP_CVAL_EL0", "fullname": "Counter-timer Physical Timer CompareValue register (EL2)", "enc": [3, 3, 14, 2, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTHPS_CTL_EL2", "fullname": "Counter-timer Secure Physical Timer Control register (EL2)", "enc": [3, 4, 14, 5, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISTATUS", "msb": 2, "lsb": 2}, {"name": "IMASK", "msb": 1, "lsb": 1}, {"name": "ENABLE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTP_CTL_EL0", "fullname": "Counter-timer Secure Physical Timer Control register (EL2)", "enc": [3, 3, 14, 2, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISTATUS", "msb": 2, "lsb": 2}, {"name": "IMASK", "msb": 1, "lsb": 1}, {"name": "ENABLE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTHPS_CVAL_EL2", "fullname": "Counter-timer Secure Physical Timer CompareValue register (EL2)", "enc": [3, 4, 14, 5, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTP_CVAL_EL0", "fullname": "Counter-timer Secure Physical Timer CompareValue register (EL2)", "enc": [3, 3, 14, 2, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTHPS_TVAL_EL2", "fullname": "Counter-timer Secure Physical Timer TimerValue register (EL2)", "enc": [3, 4, 14, 5, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TimerValue", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTP_TVAL_EL0", "fullname": "Counter-timer Secure Physical Timer TimerValue register (EL2)", "enc": [3, 3, 14, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TimerValue", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTHP_TVAL_EL2", "fullname": "Counter-timer Physical Timer TimerValue register (EL2)", "enc": [3, 4, 14, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TimerValue", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTP_TVAL_EL0", "fullname": "Counter-timer Physical Timer TimerValue register (EL2)", "enc": [3, 3, 14, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TimerValue", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTHV_CTL_EL2", "fullname": "Counter-timer Virtual Timer Control register (EL2)", "enc": [3, 4, 14, 3, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISTATUS", "msb": 2, "lsb": 2}, {"name": "IMASK", "msb": 1, "lsb": 1}, {"name": "ENABLE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTV_CTL_EL0", "fullname": "Counter-timer Virtual Timer Control register (EL2)", "enc": [3, 3, 14, 3, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISTATUS", "msb": 2, "lsb": 2}, {"name": "IMASK", "msb": 1, "lsb": 1}, {"name": "ENABLE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTHV_CVAL_EL2", "fullname": "Counter-timer Virtual Timer CompareValue register (EL2)", "enc": [3, 4, 14, 3, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTV_CVAL_EL0", "fullname": "Counter-timer Virtual Timer CompareValue register (EL2)", "enc": [3, 3, 14, 3, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTHVS_CTL_EL2", "fullname": "Counter-timer Secure Virtual Timer Control register (EL2)", "enc": [3, 4, 14, 4, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISTATUS", "msb": 2, "lsb": 2}, {"name": "IMASK", "msb": 1, "lsb": 1}, {"name": "ENABLE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTV_CTL_EL0", "fullname": "Counter-timer Secure Virtual Timer Control register (EL2)", "enc": [3, 3, 14, 3, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISTATUS", "msb": 2, "lsb": 2}, {"name": "IMASK", "msb": 1, "lsb": 1}, {"name": "ENABLE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTHVS_CVAL_EL2", "fullname": "Counter-timer Secure Virtual Timer CompareValue register (EL2)", "enc": [3, 4, 14, 4, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTV_CVAL_EL0", "fullname": "Counter-timer Secure Virtual Timer CompareValue register (EL2)", "enc": [3, 3, 14, 3, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTHVS_TVAL_EL2", "fullname": "Counter-timer Secure Virtual Timer TimerValue register (EL2)", "enc": [3, 4, 14, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TimerValue", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTV_TVAL_EL0", "fullname": "Counter-timer Secure Virtual Timer TimerValue register (EL2)", "enc": [3, 3, 14, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TimerValue", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTHV_TVAL_EL2", "fullname": "Counter-timer Virtual Timer TimerValue Register (EL2)", "enc": [3, 4, 14, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TimerValue", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTV_TVAL_EL0", "fullname": "Counter-timer Virtual Timer TimerValue Register (EL2)", "enc": [3, 3, 14, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TimerValue", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTKCTL_EL1", "fullname": "Counter-timer Kernel Control register", "enc": [3, 0, 14, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "EVNTIS", "msb": 17, "lsb": 17}, {"name": "EL0PTEN", "msb": 9, "lsb": 9}, {"name": "EL0VTEN", "msb": 8, "lsb": 8}, {"name": "EVNTI", "msb": 7, "lsb": 4}, {"name": "EVNTDIR", "msb": 3, "lsb": 3}, {"name": "EVNTEN", "msb": 2, "lsb": 2}, {"name": "EL0VCTEN", "msb": 1, "lsb": 1}, {"name": "EL0PCTEN", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTKCTL_EL12", "fullname": "Counter-timer Kernel Control register", "enc": [3, 5, 14, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "EVNTIS", "msb": 17, "lsb": 17}, {"name": "EL0PTEN", "msb": 9, "lsb": 9}, {"name": "EL0VTEN", "msb": 8, "lsb": 8}, {"name": "EVNTI", "msb": 7, "lsb": 4}, {"name": "EVNTDIR", "msb": 3, "lsb": 3}, {"name": "EVNTEN", "msb": 2, "lsb": 2}, {"name": "EL0VCTEN", "msb": 1, "lsb": 1}, {"name": "EL0PCTEN", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTPCT_EL0", "fullname": "Counter-timer Physical Count register", "enc": [3, 3, 14, 0, 1], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTP_CTL_EL0", "fullname": "Counter-timer Physical Timer Control register", "enc": [3, 3, 14, 2, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISTATUS", "msb": 2, "lsb": 2}, {"name": "IMASK", "msb": 1, "lsb": 1}, {"name": "ENABLE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTP_CTL_EL02", "fullname": "Counter-timer Physical Timer Control register", "enc": [3, 5, 14, 2, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISTATUS", "msb": 2, "lsb": 2}, {"name": "IMASK", "msb": 1, "lsb": 1}, {"name": "ENABLE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTPCTSS_EL0", "fullname": "Counter-timer Self-Synchronized Physical Count register", "enc": [3, 3, 14, 0, 5], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTP_CVAL_EL0", "fullname": "Counter-timer Physical Timer CompareValue register", "enc": [3, 3, 14, 2, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTP_CVAL_EL02", "fullname": "Counter-timer Physical Timer CompareValue register", "enc": [3, 5, 14, 2, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTPOFF_EL2", "fullname": "Counter-timer Physical Offset register", "enc": [3, 4, 14, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTPS_CTL_EL1", "fullname": "Counter-timer Physical Secure Timer Control register", "enc": [3, 7, 14, 2, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISTATUS", "msb": 2, "lsb": 2}, {"name": "IMASK", "msb": 1, "lsb": 1}, {"name": "ENABLE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTPS_CVAL_EL1", "fullname": "Counter-timer Physical Secure Timer CompareValue register", "enc": [3, 7, 14, 2, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTPS_TVAL_EL1", "fullname": "Counter-timer Physical Secure Timer TimerValue register", "enc": [3, 7, 14, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TimerValue", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTP_TVAL_EL0", "fullname": "Counter-timer Physical Timer TimerValue register", "enc": [3, 3, 14, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TimerValue", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTP_TVAL_EL02", "fullname": "Counter-timer Physical Timer TimerValue register", "enc": [3, 5, 14, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TimerValue", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTVCT_EL0", "fullname": "Counter-timer Virtual Count register", "enc": [3, 3, 14, 0, 2], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTV_CTL_EL0", "fullname": "Counter-timer Virtual Timer Control register", "enc": [3, 3, 14, 3, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISTATUS", "msb": 2, "lsb": 2}, {"name": "IMASK", "msb": 1, "lsb": 1}, {"name": "ENABLE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTV_CTL_EL02", "fullname": "Counter-timer Virtual Timer Control register", "enc": [3, 5, 14, 3, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISTATUS", "msb": 2, "lsb": 2}, {"name": "IMASK", "msb": 1, "lsb": 1}, {"name": "ENABLE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTVCTSS_EL0", "fullname": "Counter-timer Self-Synchronized Virtual Count register", "enc": [3, 3, 14, 0, 6], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTV_CVAL_EL0", "fullname": "Counter-timer Virtual Timer CompareValue register", "enc": [3, 3, 14, 3, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTV_CVAL_EL02", "fullname": "Counter-timer Virtual Timer CompareValue register", "enc": [3, 5, 14, 3, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTVOFF_EL2", "fullname": "Counter-timer Virtual Offset register", "enc": [3, 4, 14, 0, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CNTV_TVAL_EL0", "fullname": "Counter-timer Virtual Timer TimerValue register", "enc": [3, 3, 14, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TimerValue", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CNTV_TVAL_EL02", "fullname": "Counter-timer Virtual Timer TimerValue register", "enc": [3, 5, 14, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TimerValue", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CONTEXTIDR_EL1", "fullname": "Context ID Register (EL1)", "enc": [3, 0, 13, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PROCID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CONTEXTIDR_EL12", "fullname": "Context ID Register (EL1)", "enc": [3, 5, 13, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PROCID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CONTEXTIDR_EL1", "fullname": "Context ID Register (EL2)", "enc": [3, 0, 13, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PROCID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CONTEXTIDR_EL2", "fullname": "Context ID Register (EL2)", "enc": [3, 4, 13, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PROCID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CPACR_EL1", "fullname": "Architectural Feature Access Control Register", "enc": [3, 0, 1, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TTA", "msb": 28, "lsb": 28}, {"name": "FPEN", "msb": 21, "lsb": 20}, {"name": "ZEN", "msb": 17, "lsb": 16}]}], "width": 64}, {"index": 0, "name": "CPACR_EL12", "fullname": "Architectural Feature Access Control Register", "enc": [3, 5, 1, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TTA", "msb": 28, "lsb": 28}, {"name": "FPEN", "msb": 21, "lsb": 20}, {"name": "ZEN", "msb": 17, "lsb": 16}]}], "width": 64}, {"index": 0, "name": "RCTX", "fullname": "Cache Prefetch Prediction Restriction by Context", "enc": [1, 3, 7, 3, 7], "accessors": ["CPP"], "fieldsets": [{"fields": [{"name": "GVMID", "msb": 48, "lsb": 48}, {"name": "VMID", "msb": 47, "lsb": 32}, {"name": "NS", "msb": 26, "lsb": 26}, {"name": "EL", "msb": 25, "lsb": 24}, {"name": "GASID", "msb": 16, "lsb": 16}, {"name": "ASID", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CPACR_EL1", "fullname": "Architectural Feature Trap Register (EL2)", "enc": [3, 0, 1, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "HCR_EL2.E2H == 1", "fields": [{"name": "TCPAC", "msb": 31, "lsb": 31}, {"name": "TAM", "msb": 30, "lsb": 30}, {"name": "TTA", "msb": 28, "lsb": 28}, {"name": "FPEN", "msb": 21, "lsb": 20}, {"name": "ZEN", "msb": 17, "lsb": 16}]}, {"instance": "HCR_EL2.E2H == 0", "fields": [{"name": "TCPAC", "msb": 31, "lsb": 31}, {"name": "TAM", "msb": 30, "lsb": 30}, {"name": "TTA", "msb": 20, "lsb": 20}, {"name": "TFP", "msb": 10, "lsb": 10}, {"name": "TZ", "msb": 8, "lsb": 8}]}], "width": 64}, {"index": 0, "name": "CPTR_EL2", "fullname": "Architectural Feature Trap Register (EL2)", "enc": [3, 4, 1, 1, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "HCR_EL2.E2H == 1", "fields": [{"name": "TCPAC", "msb": 31, "lsb": 31}, {"name": "TAM", "msb": 30, "lsb": 30}, {"name": "TTA", "msb": 28, "lsb": 28}, {"name": "FPEN", "msb": 21, "lsb": 20}, {"name": "ZEN", "msb": 17, "lsb": 16}]}, {"instance": "HCR_EL2.E2H == 0", "fields": [{"name": "TCPAC", "msb": 31, "lsb": 31}, {"name": "TAM", "msb": 30, "lsb": 30}, {"name": "TTA", "msb": 20, "lsb": 20}, {"name": "TFP", "msb": 10, "lsb": 10}, {"name": "TZ", "msb": 8, "lsb": 8}]}], "width": 64}, {"index": 0, "name": "CPTR_EL3", "fullname": "Architectural Feature Trap Register (EL3)", "enc": [3, 6, 1, 1, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TCPAC", "msb": 31, "lsb": 31}, {"name": "TAM", "msb": 30, "lsb": 30}, {"name": "TTA", "msb": 20, "lsb": 20}, {"name": "TFP", "msb": 10, "lsb": 10}, {"name": "EZ", "msb": 8, "lsb": 8}]}], "width": 64}, {"index": 0, "name": "CSSELR_EL1", "fullname": "Cache Size Selection Register", "enc": [3, 2, 0, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TnD", "msb": 4, "lsb": 4}, {"name": "Level", "msb": 3, "lsb": 1}, {"name": "InD", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CTR_EL0", "fullname": "Cache Type Register", "enc": [3, 3, 0, 0, 1], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "TminLine", "msb": 37, "lsb": 32}, {"name": "DIC", "msb": 29, "lsb": 29}, {"name": "IDC", "msb": 28, "lsb": 28}, {"name": "CWG", "msb": 27, "lsb": 24}, {"name": "ERG", "msb": 23, "lsb": 20}, {"name": "DminLine", "msb": 19, "lsb": 16}, {"name": "L1Ip", "msb": 15, "lsb": 14}, {"name": "IminLine", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "CurrentEL", "fullname": "Current Exception Level", "enc": [3, 0, 4, 2, 2], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "EL", "msb": 3, "lsb": 2}]}], "width": 64}, {"index": 0, "name": "DACR32_EL2", "fullname": "Domain Access Control Register", "enc": [3, 4, 3, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "D<n>", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "DAIF", "fullname": "Interrupt Mask Bits", "enc": [3, 3, 4, 2, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "D", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}]}], "width": 64}, {"index": 0, "name": "DBGAUTHSTATUS_EL1", "fullname": "Debug Authentication Status register", "enc": [2, 0, 7, 14, 6], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "SNID", "msb": 7, "lsb": 6}, {"name": "SNID", "msb": 7, "lsb": 6}, {"name": "SID", "msb": 5, "lsb": 4}, {"name": "NSNID", "msb": 3, "lsb": 2}, {"name": "NSNID", "msb": 3, "lsb": 2}, {"name": "NSID", "msb": 1, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "DBGBCR0_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 1, "name": "DBGBCR1_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 2, "name": "DBGBCR2_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 3, "name": "DBGBCR3_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 4, "name": "DBGBCR4_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 5, "name": "DBGBCR5_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 6, "name": "DBGBCR6_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 7, "name": "DBGBCR7_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 8, "name": "DBGBCR8_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 9, "name": "DBGBCR9_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 10, "name": "DBGBCR10_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 11, "name": "DBGBCR11_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 12, "name": "DBGBCR12_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 13, "name": "DBGBCR13_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 14, "name": "DBGBCR14_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 15, "name": "DBGBCR15_EL1", "fullname": "Debug Breakpoint Control Registers", "enc": [2, 0, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BT", "msb": 23, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 8, "lsb": 5}, {"name": "PMC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "DBGBVR0_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR<n>_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR<n>_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 1, "name": "DBGBVR1_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR<n>_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR<n>_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 2, "name": "DBGBVR2_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR<n>_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR<n>_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 3, "name": "DBGBVR3_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR<n>_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR<n>_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 4, "name": "DBGBVR4_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR<n>_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR<n>_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 5, "name": "DBGBVR5_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR<n>_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR<n>_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 6, "name": "DBGBVR6_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR<n>_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR<n>_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 7, "name": "DBGBVR7_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR<n>_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR<n>_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 8, "name": "DBGBVR8_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR<n>_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR<n>_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 9, "name": "DBGBVR9_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR<n>_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR<n>_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 10, "name": "DBGBVR10_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR<n>_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR<n>_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 11, "name": "DBGBVR11_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR<n>_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR<n>_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 12, "name": "DBGBVR12_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR<n>_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR<n>_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 13, "name": "DBGBVR13_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR<n>_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR<n>_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 14, "name": "DBGBVR14_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR<n>_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR<n>_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 15, "name": "DBGBVR15_EL1", "fullname": "Debug Breakpoint Value Registers", "enc": [2, 0, 0, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "DBGBCR<n>_EL1.BT==0b000x", "fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}, {"instance": "DBGBCR<n>_EL1.BT==0b001x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b011x", "fields": [{"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b100x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b101x and EL2 implemented", "fields": [{"name": "VMID[15:8]", "msb": 47, "lsb": 40}, {"name": "VMID[7:0]", "msb": 39, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}, {"instance": "DBGBCR<n>_EL1.BT==0b110x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}]}, {"instance": "DBGBCR<n>_EL1.BT==0b111x, EL2 implemented, and (FEAT_VHE implemented or FEAT_Debugv8p2 implemented)", "fields": [{"name": "ContextID2", "msb": 63, "lsb": 32}, {"name": "ContextID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "DBGCLAIMCLR_EL1", "fullname": "Debug CLAIM Tag Clear register", "enc": [2, 0, 7, 9, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "CLAIM", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "DBGCLAIMSET_EL1", "fullname": "Debug CLAIM Tag Set register", "enc": [2, 0, 7, 8, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "CLAIM", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "DBGDTR_EL0", "fullname": "Debug Data Transfer Register, half-duplex", "enc": [2, 3, 0, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "HighWord", "msb": 63, "lsb": 32}, {"name": "LowWord", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "DBGDTRRX_EL0", "fullname": "Debug Data Transfer Register, Receive", "enc": [2, 3, 0, 5, 0], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "DBGDTRTX_EL0", "fullname": "Debug Data Transfer Register, Transmit", "enc": [2, 3, 0, 5, 0], "accessors": ["MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "DBGPRCR_EL1", "fullname": "Debug Power Control Register", "enc": [2, 0, 1, 4, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "CORENPDRQ", "msb": 0, "lsb": 0}, {"name": "CORENPDRQ", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "DBGVCR32_EL2", "fullname": "Debug Vector Catch Register", "enc": [2, 4, 0, 7, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "EL3 implemented and using AArch64", "fields": [{"name": "NSF", "msb": 31, "lsb": 31}, {"name": "NSI", "msb": 30, "lsb": 30}, {"name": "NSD", "msb": 28, "lsb": 28}, {"name": "NSP", "msb": 27, "lsb": 27}, {"name": "NSS", "msb": 26, "lsb": 26}, {"name": "NSU", "msb": 25, "lsb": 25}, {"name": "SF", "msb": 7, "lsb": 7}, {"name": "SI", "msb": 6, "lsb": 6}, {"name": "SD", "msb": 4, "lsb": 4}, {"name": "SP", "msb": 3, "lsb": 3}, {"name": "SS", "msb": 2, "lsb": 2}, {"name": "SU", "msb": 1, "lsb": 1}]}, {"instance": "EL3 not implemented", "fields": [{"name": "F", "msb": 7, "lsb": 7}, {"name": "I", "msb": 6, "lsb": 6}, {"name": "D", "msb": 4, "lsb": 4}, {"name": "P", "msb": 3, "lsb": 3}, {"name": "S", "msb": 2, "lsb": 2}, {"name": "U", "msb": 1, "lsb": 1}]}], "width": 64}, {"index": 0, "name": "DBGWCR0_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 1, "name": "DBGWCR1_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 2, "name": "DBGWCR2_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 3, "name": "DBGWCR3_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 4, "name": "DBGWCR4_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 5, "name": "DBGWCR5_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 6, "name": "DBGWCR6_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 7, "name": "DBGWCR7_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 8, "name": "DBGWCR8_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 9, "name": "DBGWCR9_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 10, "name": "DBGWCR10_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 11, "name": "DBGWCR11_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 12, "name": "DBGWCR12_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 13, "name": "DBGWCR13_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 14, "name": "DBGWCR14_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 15, "name": "DBGWCR15_EL1", "fullname": "Debug Watchpoint Control Registers", "enc": [2, 0, 0, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MASK", "msb": 28, "lsb": 24}, {"name": "WT", "msb": 20, "lsb": 20}, {"name": "LBN", "msb": 19, "lsb": 16}, {"name": "SSC", "msb": 15, "lsb": 14}, {"name": "HMC", "msb": 13, "lsb": 13}, {"name": "BAS", "msb": 12, "lsb": 5}, {"name": "LSC", "msb": 4, "lsb": 3}, {"name": "PAC", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "DBGWVR0_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 1, "name": "DBGWVR1_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 2, "name": "DBGWVR2_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 3, "name": "DBGWVR3_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 4, "name": "DBGWVR4_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 5, "name": "DBGWVR5_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 6, "name": "DBGWVR6_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 7, "name": "DBGWVR7_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 8, "name": "DBGWVR8_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 9, "name": "DBGWVR9_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 10, "name": "DBGWVR10_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 11, "name": "DBGWVR11_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 12, "name": "DBGWVR12_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 13, "name": "DBGWVR13_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 14, "name": "DBGWVR14_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 15, "name": "DBGWVR15_EL1", "fullname": "Debug Watchpoint Value Registers", "enc": [2, 0, 0, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS[14:4]", "msb": 63, "lsb": 53}, {"name": "VA[52:49]", "msb": 52, "lsb": 49}, {"name": "RESS[3:0]", "msb": 52, "lsb": 49}, {"name": "VA[48:2]", "msb": 48, "lsb": 2}]}], "width": 64}, {"index": 0, "name": "CGDSW", "fullname": "Clean of Data and Allocation Tags by Set/Way", "enc": [1, 0, 7, 10, 6], "accessors": ["DC"], "fieldsets": [{"fields": [{"name": "SetWay", "msb": 31, "lsb": 4}, {"name": "Level", "msb": 3, "lsb": 1}]}], "width": 64}, {"index": 0, "name": "CGDVAC", "fullname": "Clean of Data and Allocation Tags by VA to PoC", "enc": [1, 3, 7, 10, 5], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CGDVADP", "fullname": "Clean of Data and Allocation Tags by VA to PoDP", "enc": [1, 3, 7, 13, 5], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CGDVAP", "fullname": "Clean of Data and Allocation Tags by VA to PoP", "enc": [1, 3, 7, 12, 5], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CGSW", "fullname": "Clean of Allocation Tags by Set/Way", "enc": [1, 0, 7, 10, 4], "accessors": ["DC"], "fieldsets": [{"fields": [{"name": "SetWay", "msb": 31, "lsb": 4}, {"name": "Level", "msb": 3, "lsb": 1}]}], "width": 64}, {"index": 0, "name": "CGVAC", "fullname": "Clean of Allocation Tags by VA to PoC", "enc": [1, 3, 7, 10, 3], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CGVADP", "fullname": "Clean of Allocation Tags by VA to PoDP", "enc": [1, 3, 7, 13, 3], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CGVAP", "fullname": "Clean of Allocation Tags by VA to PoP", "enc": [1, 3, 7, 12, 3], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CIGDSW", "fullname": "Clean and Invalidate of Data and Allocation Tags by Set/Way", "enc": [1, 0, 7, 14, 6], "accessors": ["DC"], "fieldsets": [{"fields": [{"name": "SetWay", "msb": 31, "lsb": 4}, {"name": "Level", "msb": 3, "lsb": 1}]}], "width": 64}, {"index": 0, "name": "CIGDVAC", "fullname": "Clean and Invalidate of Data and Allocation Tags by VA to PoC", "enc": [1, 3, 7, 14, 5], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CIGSW", "fullname": "Clean and Invalidate of Allocation Tags by Set/Way", "enc": [1, 0, 7, 14, 4], "accessors": ["DC"], "fieldsets": [{"fields": [{"name": "SetWay", "msb": 31, "lsb": 4}, {"name": "Level", "msb": 3, "lsb": 1}]}], "width": 64}, {"index": 0, "name": "CIGVAC", "fullname": "Clean and Invalidate of Allocation Tags by VA to PoC", "enc": [1, 3, 7, 14, 3], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CISW", "fullname": "Data or unified Cache line Clean and Invalidate by Set/Way", "enc": [1, 0, 7, 14, 2], "accessors": ["DC"], "fieldsets": [{"fields": [{"name": "SetWay", "msb": 31, "lsb": 4}, {"name": "Level", "msb": 3, "lsb": 1}]}], "width": 64}, {"index": 0, "name": "CIVAC", "fullname": "Data or unified Cache line Clean and Invalidate by VA to PoC", "enc": [1, 3, 7, 14, 1], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CSW", "fullname": "Data or unified Cache line Clean by Set/Way", "enc": [1, 0, 7, 10, 2], "accessors": ["DC"], "fieldsets": [{"fields": [{"name": "SetWay", "msb": 31, "lsb": 4}, {"name": "Level", "msb": 3, "lsb": 1}]}], "width": 64}, {"index": 0, "name": "CVAC", "fullname": "Data or unified Cache line Clean by VA to PoC", "enc": [1, 3, 7, 10, 1], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CVADP", "fullname": "Data or unified Cache line Clean by VA to PoDP", "enc": [1, 3, 7, 13, 1], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CVAP", "fullname": "Data or unified Cache line Clean by VA to PoP", "enc": [1, 3, 7, 12, 1], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "CVAU", "fullname": "Data or unified Cache line Clean by VA to PoU", "enc": [1, 3, 7, 11, 1], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "GVA", "fullname": "Data Cache set Allocation Tag by VA", "enc": [1, 3, 7, 4, 3], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "GZVA", "fullname": "Data Cache set Allocation Tags and Zero by VA", "enc": [1, 3, 7, 4, 4], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "IGDSW", "fullname": "Invalidate of Data and Allocation Tags by Set/Way", "enc": [1, 0, 7, 6, 6], "accessors": ["DC"], "fieldsets": [{"fields": [{"name": "SetWay", "msb": 31, "lsb": 4}, {"name": "Level", "msb": 3, "lsb": 1}]}], "width": 64}, {"index": 0, "name": "IGDVAC", "fullname": "Invalidate of Data and Allocation Tags by VA to PoC", "enc": [1, 0, 7, 6, 5], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "IGSW", "fullname": "Invalidate of Allocation Tags by Set/Way", "enc": [1, 0, 7, 6, 4], "accessors": ["DC"], "fieldsets": [{"fields": [{"name": "SetWay", "msb": 31, "lsb": 4}, {"name": "Level", "msb": 3, "lsb": 1}]}], "width": 64}, {"index": 0, "name": "IGVAC", "fullname": "Invalidate of Allocation Tags by VA to PoC", "enc": [1, 0, 7, 6, 3], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "ISW", "fullname": "Data or unified Cache line Invalidate by Set/Way", "enc": [1, 0, 7, 6, 2], "accessors": ["DC"], "fieldsets": [{"fields": [{"name": "SetWay", "msb": 31, "lsb": 4}, {"name": "Level", "msb": 3, "lsb": 1}]}], "width": 64}, {"index": 0, "name": "IVAC", "fullname": "Data or unified Cache line Invalidate by VA to PoC", "enc": [1, 0, 7, 6, 1], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "DCZID_EL0", "fullname": "Data Cache Zero ID register", "enc": [3, 3, 0, 0, 7], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "DZP", "msb": 4, "lsb": 4}, {"name": "BS", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ZVA", "fullname": "Data Cache Zero by VA", "enc": [1, 3, 7, 4, 1], "accessors": ["DC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "DISR_EL1", "fullname": "Deferred Interrupt Status Register", "enc": [3, 0, 12, 1, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "A", "msb": 31, "lsb": 31}, {"name": "IDS", "msb": 24, "lsb": 24}, {"name": "AET", "msb": 12, "lsb": 10}, {"name": "EA", "msb": 9, "lsb": 9}, {"name": "DFSC", "msb": 5, "lsb": 0}]}, {"fields": [{"name": "A", "msb": 31, "lsb": 31}, {"name": "IDS", "msb": 24, "lsb": 24}, {"name": "ISS", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "DIT", "fullname": "Data Independent Timing", "enc": [3, 3, 4, 2, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "DIT", "msb": 24, "lsb": 24}]}], "width": 64}, {"index": 0, "name": "DLR_EL0", "fullname": "Debug Link Register", "enc": [3, 3, 4, 5, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "DSPSR_EL0", "fullname": "Debug Saved Program Status Register", "enc": [3, 3, 4, 5, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "exiting Debug state to AArch32 state", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "Q", "msb": 27, "lsb": 27}, {"name": "IT", "msb": 26, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "SSBS", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "GE", "msb": 19, "lsb": 16}, {"name": "IT[7:2]", "msb": 15, "lsb": 10}, {"name": "E", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "T", "msb": 5, "lsb": 5}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}, {"instance": "entering or exiting Debug state from or to AArch64 state", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "TCO", "msb": 25, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "UAO", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "SSBS", "msb": 12, "lsb": 12}, {"name": "BTYPE", "msb": 11, "lsb": 10}, {"name": "D", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RCTX", "fullname": "Data Value Prediction Restriction by Context", "enc": [1, 3, 7, 3, 5], "accessors": ["DVP"], "fieldsets": [{"fields": [{"name": "GVMID", "msb": 48, "lsb": 48}, {"name": "VMID", "msb": 47, "lsb": 32}, {"name": "NS", "msb": 26, "lsb": 26}, {"name": "EL", "msb": 25, "lsb": 24}, {"name": "GASID", "msb": 16, "lsb": 16}, {"name": "ASID", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ELR_EL1", "fullname": "Exception Link Register (EL1)", "enc": [3, 0, 4, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "ELR_EL12", "fullname": "Exception Link Register (EL1)", "enc": [3, 5, 4, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "ELR_EL2", "fullname": "Exception Link Register (EL1)", "enc": [3, 4, 4, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "ELR_EL1", "fullname": "Exception Link Register (EL2)", "enc": [3, 0, 4, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "ELR_EL2", "fullname": "Exception Link Register (EL2)", "enc": [3, 4, 4, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "ELR_EL3", "fullname": "Exception Link Register (EL3)", "enc": [3, 6, 4, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "ERRIDR_EL1", "fullname": "Error Record ID Register", "enc": [3, 0, 5, 3, 0], "accessors": ["MRS"], "fieldsets": [{"instance": "ERRIDR_EL1", "fields": [{"name": "NUM", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ERRSELR_EL1", "fullname": "Error Record Select Register", "enc": [3, 0, 5, 3, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "ERRSELR_EL1", "fields": [{"name": "SEL", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ERXADDR_EL1", "fullname": "Selected Error Record Address Register", "enc": [3, 0, 5, 4, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "ERXADDR_EL1", "fields": []}], "width": 64}, {"index": 0, "name": "ERXCTLR_EL1", "fullname": "Selected Error Record Control Register", "enc": [3, 0, 5, 4, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "ERXCTLR_EL1", "fields": []}], "width": 64}, {"index": 0, "name": "ERXFR_EL1", "fullname": "Selected Error Record Feature Register", "enc": [3, 0, 5, 4, 0], "accessors": ["MRS"], "fieldsets": [{"instance": "ERXFR_EL1", "fields": []}], "width": 64}, {"index": 0, "name": "ERXMISC0_EL1", "fullname": "Selected Error Record Miscellaneous Register 0", "enc": [3, 0, 5, 5, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "ERXMISC0_EL1", "fields": []}], "width": 64}, {"index": 0, "name": "ERXMISC1_EL1", "fullname": "Selected Error Record Miscellaneous Register 1", "enc": [3, 0, 5, 5, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "ERXMISC1_EL1", "fields": []}], "width": 64}, {"index": 0, "name": "ERXMISC2_EL1", "fullname": "Selected Error Record Miscellaneous Register 2", "enc": [3, 0, 5, 5, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "ERXMISC2_EL1", "fields": []}], "width": 64}, {"index": 0, "name": "ERXMISC3_EL1", "fullname": "Selected Error Record Miscellaneous Register 3", "enc": [3, 0, 5, 5, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "ERXMISC3_EL1", "fields": []}], "width": 64}, {"index": 0, "name": "ERXPFGCDN_EL1", "fullname": "Selected Pseudo-fault Generation Countdown register", "enc": [3, 0, 5, 4, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "ERXPFGCDN_EL1", "fields": []}], "width": 64}, {"index": 0, "name": "ERXPFGCTL_EL1", "fullname": "Selected Pseudo-fault Generation Control register", "enc": [3, 0, 5, 4, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "ERXPFGCTL_EL1", "fields": []}], "width": 64}, {"index": 0, "name": "ERXPFGF_EL1", "fullname": "Selected Pseudo-fault Generation Feature register", "enc": [3, 0, 5, 4, 4], "accessors": ["MRS"], "fieldsets": [{"instance": "ERXPFGF_EL1", "fields": []}], "width": 64}, {"index": 0, "name": "ERXSTATUS_EL1", "fullname": "Selected Error Record Primary Status Register", "enc": [3, 0, 5, 4, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "ERXSTATUS_EL1", "fields": []}], "width": 64}, {"index": 0, "name": "ESR_EL1", "fullname": "Exception Syndrome Register (EL1)", "enc": [3, 0, 5, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISS2", "msb": 36, "lsb": 32}, {"name": "EC", "msb": 31, "lsb": 26}, {"name": "IL", "msb": 25, "lsb": 25}, {"name": "ISS", "msb": 24, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ESR_EL12", "fullname": "Exception Syndrome Register (EL1)", "enc": [3, 5, 5, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISS2", "msb": 36, "lsb": 32}, {"name": "EC", "msb": 31, "lsb": 26}, {"name": "IL", "msb": 25, "lsb": 25}, {"name": "ISS", "msb": 24, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ESR_EL2", "fullname": "Exception Syndrome Register (EL1)", "enc": [3, 4, 5, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISS2", "msb": 36, "lsb": 32}, {"name": "EC", "msb": 31, "lsb": 26}, {"name": "IL", "msb": 25, "lsb": 25}, {"name": "ISS", "msb": 24, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ESR_EL1", "fullname": "Exception Syndrome Register (EL2)", "enc": [3, 0, 5, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISS2", "msb": 36, "lsb": 32}, {"name": "EC", "msb": 31, "lsb": 26}, {"name": "IL", "msb": 25, "lsb": 25}, {"name": "ISS", "msb": 24, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ESR_EL2", "fullname": "Exception Syndrome Register (EL2)", "enc": [3, 4, 5, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISS2", "msb": 36, "lsb": 32}, {"name": "EC", "msb": 31, "lsb": 26}, {"name": "IL", "msb": 25, "lsb": 25}, {"name": "ISS", "msb": 24, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ESR_EL3", "fullname": "Exception Syndrome Register (EL3)", "enc": [3, 6, 5, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ISS2", "msb": 36, "lsb": 32}, {"name": "EC", "msb": 31, "lsb": 26}, {"name": "IL", "msb": 25, "lsb": 25}, {"name": "ISS", "msb": 24, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "FAR_EL1", "fullname": "Fault Address Register (EL1)", "enc": [3, 0, 6, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "FAR_EL12", "fullname": "Fault Address Register (EL1)", "enc": [3, 5, 6, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "FAR_EL2", "fullname": "Fault Address Register (EL1)", "enc": [3, 4, 6, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "FAR_EL1", "fullname": "Fault Address Register (EL2)", "enc": [3, 0, 6, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "FAR_EL2", "fullname": "Fault Address Register (EL2)", "enc": [3, 4, 6, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "FAR_EL3", "fullname": "Fault Address Register (EL3)", "enc": [3, 6, 6, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "FPCR", "fullname": "Floating-point Control Register", "enc": [3, 3, 4, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "AHP", "msb": 26, "lsb": 26}, {"name": "DN", "msb": 25, "lsb": 25}, {"name": "FZ", "msb": 24, "lsb": 24}, {"name": "RMode", "msb": 23, "lsb": 22}, {"name": "Stride", "msb": 21, "lsb": 20}, {"name": "FZ16", "msb": 19, "lsb": 19}, {"name": "Len", "msb": 18, "lsb": 16}, {"name": "IDE", "msb": 15, "lsb": 15}, {"name": "IXE", "msb": 12, "lsb": 12}, {"name": "UFE", "msb": 11, "lsb": 11}, {"name": "OFE", "msb": 10, "lsb": 10}, {"name": "DZE", "msb": 9, "lsb": 9}, {"name": "IOE", "msb": 8, "lsb": 8}, {"name": "NEP", "msb": 2, "lsb": 2}, {"name": "AH", "msb": 1, "lsb": 1}, {"name": "FIZ", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "FPEXC32_EL2", "fullname": "Floating-Point Exception Control register", "enc": [3, 4, 5, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "EX", "msb": 31, "lsb": 31}, {"name": "EN", "msb": 30, "lsb": 30}, {"name": "DEX", "msb": 29, "lsb": 29}, {"name": "FP2V", "msb": 28, "lsb": 28}, {"name": "VV", "msb": 27, "lsb": 27}, {"name": "TFV", "msb": 26, "lsb": 26}, {"name": "VECITR", "msb": 10, "lsb": 8}, {"name": "IDF", "msb": 7, "lsb": 7}, {"name": "IXF", "msb": 4, "lsb": 4}, {"name": "UFF", "msb": 3, "lsb": 3}, {"name": "OFF", "msb": 2, "lsb": 2}, {"name": "DZF", "msb": 1, "lsb": 1}, {"name": "IOF", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "FPSR", "fullname": "Floating-point Status Register", "enc": [3, 3, 4, 4, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "QC", "msb": 27, "lsb": 27}, {"name": "IDC", "msb": 7, "lsb": 7}, {"name": "IXC", "msb": 4, "lsb": 4}, {"name": "UFC", "msb": 3, "lsb": 3}, {"name": "OFC", "msb": 2, "lsb": 2}, {"name": "DZC", "msb": 1, "lsb": 1}, {"name": "IOC", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "GCR_EL1", "fullname": "Tag Control Register.", "enc": [3, 0, 1, 0, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RRND", "msb": 16, "lsb": 16}, {"name": "Exclude", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "GMID_EL1", "fullname": " Multiple tag transfer ID register", "enc": [3, 1, 0, 0, 4], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "BS", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "HACR_EL2", "fullname": "Hypervisor Auxiliary Control Register", "enc": [3, 4, 1, 1, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "HAFGRTR_EL2", "fullname": "Hypervisor Activity Monitors Fine-Grained Read Trap Register", "enc": [3, 4, 3, 1, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "HAFGRTR_EL2", "fields": [{"name": "AMEVTYPER1<x>_EL0", "msb": 49, "lsb": 49}, {"name": "AMEVTYPER115_EL0", "msb": 49, "lsb": 49}, {"name": "AMEVCNTR1<x>_EL0", "msb": 48, "lsb": 48}, {"name": "AMEVCNTR115_EL0", "msb": 48, "lsb": 48}, {"name": "AMEVTYPER114_EL0", "msb": 47, "lsb": 47}, {"name": "AMEVCNTR114_EL0", "msb": 46, "lsb": 46}, {"name": "AMEVTYPER113_EL0", "msb": 45, "lsb": 45}, {"name": "AMEVCNTR113_EL0", "msb": 44, "lsb": 44}, {"name": "AMEVTYPER112_EL0", "msb": 43, "lsb": 43}, {"name": "AMEVCNTR112_EL0", "msb": 42, "lsb": 42}, {"name": "AMEVTYPER111_EL0", "msb": 41, "lsb": 41}, {"name": "AMEVCNTR111_EL0", "msb": 40, "lsb": 40}, {"name": "AMEVTYPER110_EL0", "msb": 39, "lsb": 39}, {"name": "AMEVCNTR110_EL0", "msb": 38, "lsb": 38}, {"name": "AMEVTYPER19_EL0", "msb": 37, "lsb": 37}, {"name": "AMEVCNTR19_EL0", "msb": 36, "lsb": 36}, {"name": "AMEVTYPER18_EL0", "msb": 35, "lsb": 35}, {"name": "AMEVCNTR18_EL0", "msb": 34, "lsb": 34}, {"name": "AMEVTYPER17_EL0", "msb": 33, "lsb": 33}, {"name": "AMEVCNTR17_EL0", "msb": 32, "lsb": 32}, {"name": "AMEVTYPER16_EL0", "msb": 31, "lsb": 31}, {"name": "AMEVCNTR16_EL0", "msb": 30, "lsb": 30}, {"name": "AMEVTYPER15_EL0", "msb": 29, "lsb": 29}, {"name": "AMEVCNTR15_EL0", "msb": 28, "lsb": 28}, {"name": "AMEVTYPER14_EL0", "msb": 27, "lsb": 27}, {"name": "AMEVCNTR14_EL0", "msb": 26, "lsb": 26}, {"name": "AMEVTYPER13_EL0", "msb": 25, "lsb": 25}, {"name": "AMEVCNTR13_EL0", "msb": 24, "lsb": 24}, {"name": "AMEVTYPER12_EL0", "msb": 23, "lsb": 23}, {"name": "AMEVCNTR12_EL0", "msb": 22, "lsb": 22}, {"name": "AMEVTYPER11_EL0", "msb": 21, "lsb": 21}, {"name": "AMEVCNTR11_EL0", "msb": 20, "lsb": 20}, {"name": "AMEVTYPER10_EL0", "msb": 19, "lsb": 19}, {"name": "AMEVCNTR10_EL0", "msb": 18, "lsb": 18}, {"name": "AMCNTEN<x>", "msb": 17, "lsb": 17}, {"name": "AMCNTEN1", "msb": 17, "lsb": 17}, {"name": "AMEVCNTR0<x>_EL0", "msb": 4, "lsb": 1}, {"name": "AMCNTEN0", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "HCR_EL2", "fullname": "Hypervisor Configuration Register", "enc": [3, 4, 1, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TWEDEL", "msb": 63, "lsb": 60}, {"name": "TWEDEn", "msb": 59, "lsb": 59}, {"name": "TID5", "msb": 58, "lsb": 58}, {"name": "DCT", "msb": 57, "lsb": 57}, {"name": "ATA", "msb": 56, "lsb": 56}, {"name": "TTLBOS", "msb": 55, "lsb": 55}, {"name": "TTLBIS", "msb": 54, "lsb": 54}, {"name": "EnSCXT", "msb": 53, "lsb": 53}, {"name": "TOCU", "msb": 52, "lsb": 52}, {"name": "AMVOFFEN", "msb": 51, "lsb": 51}, {"name": "TICAB", "msb": 50, "lsb": 50}, {"name": "TID4", "msb": 49, "lsb": 49}, {"name": "FIEN", "msb": 47, "lsb": 47}, {"name": "FWB", "msb": 46, "lsb": 46}, {"name": "NV2", "msb": 45, "lsb": 45}, {"name": "AT", "msb": 44, "lsb": 44}, {"name": "NV1", "msb": 43, "lsb": 43}, {"name": "NV1", "msb": 43, "lsb": 43}, {"name": "NV", "msb": 42, "lsb": 42}, {"name": "NV", "msb": 42, "lsb": 42}, {"name": "API", "msb": 41, "lsb": 41}, {"name": "APK", "msb": 40, "lsb": 40}, {"name": "MIOCNCE", "msb": 38, "lsb": 38}, {"name": "TEA", "msb": 37, "lsb": 37}, {"name": "TERR", "msb": 36, "lsb": 36}, {"name": "TLOR", "msb": 35, "lsb": 35}, {"name": "E2H", "msb": 34, "lsb": 34}, {"name": "ID", "msb": 33, "lsb": 33}, {"name": "CD", "msb": 32, "lsb": 32}, {"name": "RW", "msb": 31, "lsb": 31}, {"name": "TRVM", "msb": 30, "lsb": 30}, {"name": "HCD", "msb": 29, "lsb": 29}, {"name": "TDZ", "msb": 28, "lsb": 28}, {"name": "TGE", "msb": 27, "lsb": 27}, {"name": "TVM", "msb": 26, "lsb": 26}, {"name": "TTLB", "msb": 25, "lsb": 25}, {"name": "TPU", "msb": 24, "lsb": 24}, {"name": "TPCP", "msb": 23, "lsb": 23}, {"name": "TPC", "msb": 23, "lsb": 23}, {"name": "TSW", "msb": 22, "lsb": 22}, {"name": "TACR", "msb": 21, "lsb": 21}, {"name": "TIDCP", "msb": 20, "lsb": 20}, {"name": "TSC", "msb": 19, "lsb": 19}, {"name": "TID3", "msb": 18, "lsb": 18}, {"name": "TID2", "msb": 17, "lsb": 17}, {"name": "TID1", "msb": 16, "lsb": 16}, {"name": "TID0", "msb": 15, "lsb": 15}, {"name": "TWE", "msb": 14, "lsb": 14}, {"name": "TWI", "msb": 13, "lsb": 13}, {"name": "DC", "msb": 12, "lsb": 12}, {"name": "BSU", "msb": 11, "lsb": 10}, {"name": "FB", "msb": 9, "lsb": 9}, {"name": "VSE", "msb": 8, "lsb": 8}, {"name": "VI", "msb": 7, "lsb": 7}, {"name": "VF", "msb": 6, "lsb": 6}, {"name": "AMO", "msb": 5, "lsb": 5}, {"name": "IMO", "msb": 4, "lsb": 4}, {"name": "FMO", "msb": 3, "lsb": 3}, {"name": "PTW", "msb": 2, "lsb": 2}, {"name": "SWIO", "msb": 1, "lsb": 1}, {"name": "VM", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "HCRX_EL2", "fullname": "Extended Hypervisor Configuration Register", "enc": [3, 4, 1, 2, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "FGTnXS", "msb": 4, "lsb": 4}, {"name": "FnXS", "msb": 3, "lsb": 3}, {"name": "EnASR", "msb": 2, "lsb": 2}, {"name": "EnALS", "msb": 1, "lsb": 1}, {"name": "EnAS0", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "HDFGRTR_EL2", "fullname": "Hypervisor Debug Fine-Grained Read Trap Register", "enc": [3, 4, 3, 1, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "HDFGRTR_EL2", "fields": [{"name": "nPMSNEVFR_EL1", "msb": 62, "lsb": 62}, {"name": "PMCEIDn_EL0", "msb": 58, "lsb": 58}, {"name": "PMUSERENR_EL0", "msb": 57, "lsb": 57}, {"name": "TRCVICTLR", "msb": 48, "lsb": 48}, {"name": "TRCSTATR", "msb": 47, "lsb": 47}, {"name": "TRCSSCSRn", "msb": 46, "lsb": 46}, {"name": "TRCSEQSTR", "msb": 45, "lsb": 45}, {"name": "TRCPRGCTLR", "msb": 44, "lsb": 44}, {"name": "TRCOSLSR", "msb": 43, "lsb": 43}, {"name": "TRCIMSPECn", "msb": 41, "lsb": 41}, {"name": "TRCID", "msb": 40, "lsb": 40}, {"name": "TRCCNTVRn", "msb": 37, "lsb": 37}, {"name": "TRCCLAIM", "msb": 36, "lsb": 36}, {"name": "TRCAUXCTLR", "msb": 35, "lsb": 35}, {"name": "TRCAUTHSTATUS", "msb": 34, "lsb": 34}, {"name": "TRC", "msb": 33, "lsb": 33}, {"name": "PMSLATFR_EL1", "msb": 32, "lsb": 32}, {"name": "PMSIRR_EL1", "msb": 31, "lsb": 31}, {"name": "PMSIDR_EL1", "msb": 30, "lsb": 30}, {"name": "PMSICR_EL1", "msb": 29, "lsb": 29}, {"name": "PMSFCR_EL1", "msb": 28, "lsb": 28}, {"name": "PMSEVFR_EL1", "msb": 27, "lsb": 27}, {"name": "PMSCR_EL1", "msb": 26, "lsb": 26}, {"name": "PMBSR_EL1", "msb": 25, "lsb": 25}, {"name": "PMBPTR_EL1", "msb": 24, "lsb": 24}, {"name": "PMBLIMITR_EL1", "msb": 23, "lsb": 23}, {"name": "PMMIR_EL1", "msb": 22, "lsb": 22}, {"name": "PMSELR_EL0", "msb": 19, "lsb": 19}, {"name": "PMOVS", "msb": 18, "lsb": 18}, {"name": "PMINTEN", "msb": 17, "lsb": 17}, {"name": "PMCNTEN", "msb": 16, "lsb": 16}, {"name": "PMCCNTR_EL0", "msb": 15, "lsb": 15}, {"name": "PMCCFILTR_EL0", "msb": 14, "lsb": 14}, {"name": "PMEVTYPERn_EL0", "msb": 13, "lsb": 13}, {"name": "PMEVCNTRn_EL0", "msb": 12, "lsb": 12}, {"name": "OSDLR_EL1", "msb": 11, "lsb": 11}, {"name": "OSECCR_EL1", "msb": 10, "lsb": 10}, {"name": "OSLSR_EL1", "msb": 9, "lsb": 9}, {"name": "DBGPRCR_EL1", "msb": 7, "lsb": 7}, {"name": "DBGAUTHSTATUS_EL1", "msb": 6, "lsb": 6}, {"name": "DBGCLAIM", "msb": 5, "lsb": 5}, {"name": "MDSCR_EL1", "msb": 4, "lsb": 4}, {"name": "DBGWVRn_EL1", "msb": 3, "lsb": 3}, {"name": "DBGWCRn_EL1", "msb": 2, "lsb": 2}, {"name": "DBGBVRn_EL1", "msb": 1, "lsb": 1}, {"name": "DBGBCRn_EL1", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "HDFGWTR_EL2", "fullname": "Hypervisor Debug Fine-Grained Write Trap Register", "enc": [3, 4, 3, 1, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "HDFGWTR_EL2", "fields": [{"name": "nPMSNEVFR_EL1", "msb": 62, "lsb": 62}, {"name": "PMUSERENR_EL0", "msb": 57, "lsb": 57}, {"name": "TRFCR_EL1", "msb": 49, "lsb": 49}, {"name": "TRCVICTLR", "msb": 48, "lsb": 48}, {"name": "TRCSSCSRn", "msb": 46, "lsb": 46}, {"name": "TRCSEQSTR", "msb": 45, "lsb": 45}, {"name": "TRCPRGCTLR", "msb": 44, "lsb": 44}, {"name": "TRCOSLAR", "msb": 42, "lsb": 42}, {"name": "TRCIMSPECn", "msb": 41, "lsb": 41}, {"name": "TRCCNTVRn", "msb": 37, "lsb": 37}, {"name": "TRCCLAIM", "msb": 36, "lsb": 36}, {"name": "TRCAUXCTLR", "msb": 35, "lsb": 35}, {"name": "TRC", "msb": 33, "lsb": 33}, {"name": "PMSLATFR_EL1", "msb": 32, "lsb": 32}, {"name": "PMSIRR_EL1", "msb": 31, "lsb": 31}, {"name": "PMSICR_EL1", "msb": 29, "lsb": 29}, {"name": "PMSFCR_EL1", "msb": 28, "lsb": 28}, {"name": "PMSEVFR_EL1", "msb": 27, "lsb": 27}, {"name": "PMSCR_EL1", "msb": 26, "lsb": 26}, {"name": "PMBSR_EL1", "msb": 25, "lsb": 25}, {"name": "PMBPTR_EL1", "msb": 24, "lsb": 24}, {"name": "PMBLIMITR_EL1", "msb": 23, "lsb": 23}, {"name": "PMCR_EL0", "msb": 21, "lsb": 21}, {"name": "PMSWINC_EL0", "msb": 20, "lsb": 20}, {"name": "PMSELR_EL0", "msb": 19, "lsb": 19}, {"name": "PMOVS", "msb": 18, "lsb": 18}, {"name": "PMINTEN", "msb": 17, "lsb": 17}, {"name": "PMCNTEN", "msb": 16, "lsb": 16}, {"name": "PMCCNTR_EL0", "msb": 15, "lsb": 15}, {"name": "PMCCFILTR_EL0", "msb": 14, "lsb": 14}, {"name": "PMEVTYPERn_EL0", "msb": 13, "lsb": 13}, {"name": "PMEVCNTRn_EL0", "msb": 12, "lsb": 12}, {"name": "OSDLR_EL1", "msb": 11, "lsb": 11}, {"name": "OSECCR_EL1", "msb": 10, "lsb": 10}, {"name": "OSLAR_EL1", "msb": 8, "lsb": 8}, {"name": "DBGPRCR_EL1", "msb": 7, "lsb": 7}, {"name": "DBGCLAIM", "msb": 5, "lsb": 5}, {"name": "MDSCR_EL1", "msb": 4, "lsb": 4}, {"name": "DBGWVRn_EL1", "msb": 3, "lsb": 3}, {"name": "DBGWCRn_EL1", "msb": 2, "lsb": 2}, {"name": "DBGBVRn_EL1", "msb": 1, "lsb": 1}, {"name": "DBGBCRn_EL1", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "HFGITR_EL2", "fullname": "Hypervisor Fine-Grained Instruction Trap Register", "enc": [3, 4, 1, 1, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "HFGITR_EL2", "fields": [{"name": "DCCVAC", "msb": 54, "lsb": 54}, {"name": "SVC_EL1", "msb": 53, "lsb": 53}, {"name": "SVC_EL0", "msb": 52, "lsb": 52}, {"name": "ERET", "msb": 51, "lsb": 51}, {"name": "CPPRCTX", "msb": 50, "lsb": 50}, {"name": "DVPRCTX", "msb": 49, "lsb": 49}, {"name": "CFPRCTX", "msb": 48, "lsb": 48}, {"name": "TLBIVAALE1", "msb": 47, "lsb": 47}, {"name": "TLBIVALE1", "msb": 46, "lsb": 46}, {"name": "TLBIVAAE1", "msb": 45, "lsb": 45}, {"name": "TLBIASIDE1", "msb": 44, "lsb": 44}, {"name": "TLBIVAE1", "msb": 43, "lsb": 43}, {"name": "TLBIVMALLE1", "msb": 42, "lsb": 42}, {"name": "TLBIRVAALE1", "msb": 41, "lsb": 41}, {"name": "TLBIRVALE1", "msb": 40, "lsb": 40}, {"name": "TLBIRVAAE1", "msb": 39, "lsb": 39}, {"name": "TLBIRVAE1", "msb": 38, "lsb": 38}, {"name": "TLBIRVAALE1IS", "msb": 37, "lsb": 37}, {"name": "TLBIRVALE1IS", "msb": 36, "lsb": 36}, {"name": "TLBIRVAAE1IS", "msb": 35, "lsb": 35}, {"name": "TLBIRVAE1IS", "msb": 34, "lsb": 34}, {"name": "TLBIVAALE1IS", "msb": 33, "lsb": 33}, {"name": "TLBIVALE1IS", "msb": 32, "lsb": 32}, {"name": "TLBIVAAE1IS", "msb": 31, "lsb": 31}, {"name": "TLBIASIDE1IS", "msb": 30, "lsb": 30}, {"name": "TLBIVAE1IS", "msb": 29, "lsb": 29}, {"name": "TLBIVMALLE1IS", "msb": 28, "lsb": 28}, {"name": "TLBIRVAALE1OS", "msb": 27, "lsb": 27}, {"name": "TLBIRVALE1OS", "msb": 26, "lsb": 26}, {"name": "TLBIRVAAE1OS", "msb": 25, "lsb": 25}, {"name": "TLBIRVAE1OS", "msb": 24, "lsb": 24}, {"name": "TLBIVAALE1OS", "msb": 23, "lsb": 23}, {"name": "TLBIVALE1OS", "msb": 22, "lsb": 22}, {"name": "TLBIVAAE1OS", "msb": 21, "lsb": 21}, {"name": "TLBIASIDE1OS", "msb": 20, "lsb": 20}, {"name": "TLBIVAE1OS", "msb": 19, "lsb": 19}, {"name": "TLBIVMALLE1OS", "msb": 18, "lsb": 18}, {"name": "ATS1E1WP", "msb": 17, "lsb": 17}, {"name": "ATS1E1RP", "msb": 16, "lsb": 16}, {"name": "ATS1E0W", "msb": 15, "lsb": 15}, {"name": "ATS1E0R", "msb": 14, "lsb": 14}, {"name": "ATS1E1W", "msb": 13, "lsb": 13}, {"name": "ATS1E1R", "msb": 12, "lsb": 12}, {"name": "DCZVA", "msb": 11, "lsb": 11}, {"name": "DCCIVAC", "msb": 10, "lsb": 10}, {"name": "DCCVADP", "msb": 9, "lsb": 9}, {"name": "DCCVAP", "msb": 8, "lsb": 8}, {"name": "DCCVAU", "msb": 7, "lsb": 7}, {"name": "DCCISW", "msb": 6, "lsb": 6}, {"name": "DCCSW", "msb": 5, "lsb": 5}, {"name": "DCISW", "msb": 4, "lsb": 4}, {"name": "DCIVAC", "msb": 3, "lsb": 3}, {"name": "ICIVAU", "msb": 2, "lsb": 2}, {"name": "ICIALLU", "msb": 1, "lsb": 1}, {"name": "ICIALLUIS", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "HFGRTR_EL2", "fullname": "Hypervisor Fine-Grained Read Trap Register", "enc": [3, 4, 1, 1, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "HFGRTR_EL2", "fields": [{"name": "nACCDATA_EL1", "msb": 50, "lsb": 50}, {"name": "ERXADDR_EL1", "msb": 49, "lsb": 49}, {"name": "ERXPFGCDN_EL1", "msb": 48, "lsb": 48}, {"name": "ERXPFGCTL_EL1", "msb": 47, "lsb": 47}, {"name": "ERXPFGF_EL1", "msb": 46, "lsb": 46}, {"name": "ERXMISCn_EL1", "msb": 45, "lsb": 45}, {"name": "ERXSTATUS_EL1", "msb": 44, "lsb": 44}, {"name": "ERXCTLR_EL1", "msb": 43, "lsb": 43}, {"name": "ERXFR_EL1", "msb": 42, "lsb": 42}, {"name": "ERRSELR_EL1", "msb": 41, "lsb": 41}, {"name": "ERRIDR_EL1", "msb": 40, "lsb": 40}, {"name": "ICC_IGRPENn_EL1", "msb": 39, "lsb": 39}, {"name": "VBAR_EL1", "msb": 38, "lsb": 38}, {"name": "TTBR1_EL1", "msb": 37, "lsb": 37}, {"name": "TTBR0_EL1", "msb": 36, "lsb": 36}, {"name": "TPIDR_EL0", "msb": 35, "lsb": 35}, {"name": "TPIDRRO_EL0", "msb": 34, "lsb": 34}, {"name": "TPIDR_EL1", "msb": 33, "lsb": 33}, {"name": "TCR_EL1", "msb": 32, "lsb": 32}, {"name": "SCXTNUM_EL0", "msb": 31, "lsb": 31}, {"name": "SCXTNUM_EL1", "msb": 30, "lsb": 30}, {"name": "SCTLR_EL1", "msb": 29, "lsb": 29}, {"name": "REVIDR_EL1", "msb": 28, "lsb": 28}, {"name": "PAR_EL1", "msb": 27, "lsb": 27}, {"name": "MPIDR_EL1", "msb": 26, "lsb": 26}, {"name": "MIDR_EL1", "msb": 25, "lsb": 25}, {"name": "MAIR_EL1", "msb": 24, "lsb": 24}, {"name": "LORSA_EL1", "msb": 23, "lsb": 23}, {"name": "LORN_EL1", "msb": 22, "lsb": 22}, {"name": "LORID_EL1", "msb": 21, "lsb": 21}, {"name": "LOREA_EL1", "msb": 20, "lsb": 20}, {"name": "LORC_EL1", "msb": 19, "lsb": 19}, {"name": "ISR_EL1", "msb": 18, "lsb": 18}, {"name": "FAR_EL1", "msb": 17, "lsb": 17}, {"name": "ESR_EL1", "msb": 16, "lsb": 16}, {"name": "DCZID_EL0", "msb": 15, "lsb": 15}, {"name": "CTR_EL0", "msb": 14, "lsb": 14}, {"name": "CSSELR_EL1", "msb": 13, "lsb": 13}, {"name": "CPACR_EL1", "msb": 12, "lsb": 12}, {"name": "CONTEXTIDR_EL1", "msb": 11, "lsb": 11}, {"name": "CLIDR_EL1", "msb": 10, "lsb": 10}, {"name": "CCSIDR_EL1", "msb": 9, "lsb": 9}, {"name": "APIBKey", "msb": 8, "lsb": 8}, {"name": "APIAKey", "msb": 7, "lsb": 7}, {"name": "APGAKey", "msb": 6, "lsb": 6}, {"name": "APDBKey", "msb": 5, "lsb": 5}, {"name": "APDAKey", "msb": 4, "lsb": 4}, {"name": "AMAIR_EL1", "msb": 3, "lsb": 3}, {"name": "AIDR_EL1", "msb": 2, "lsb": 2}, {"name": "AFSR1_EL1", "msb": 1, "lsb": 1}, {"name": "AFSR0_EL1", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "HFGWTR_EL2", "fullname": "Hypervisor Fine-Grained Write Trap Register", "enc": [3, 4, 1, 1, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "HFGWTR_EL2", "fields": [{"name": "nACCDATA_EL1", "msb": 50, "lsb": 50}, {"name": "ERXADDR_EL1", "msb": 49, "lsb": 49}, {"name": "ERXPFGCDN_EL1", "msb": 48, "lsb": 48}, {"name": "ERXPFGCTL_EL1", "msb": 47, "lsb": 47}, {"name": "ERXMISCn_EL1", "msb": 45, "lsb": 45}, {"name": "ERXSTATUS_EL1", "msb": 44, "lsb": 44}, {"name": "ERXCTLR_EL1", "msb": 43, "lsb": 43}, {"name": "ERRSELR_EL1", "msb": 41, "lsb": 41}, {"name": "ICC_IGRPENn_EL1", "msb": 39, "lsb": 39}, {"name": "VBAR_EL1", "msb": 38, "lsb": 38}, {"name": "TTBR1_EL1", "msb": 37, "lsb": 37}, {"name": "TTBR0_EL1", "msb": 36, "lsb": 36}, {"name": "TPIDR_EL0", "msb": 35, "lsb": 35}, {"name": "TPIDRRO_EL0", "msb": 34, "lsb": 34}, {"name": "TPIDR_EL1", "msb": 33, "lsb": 33}, {"name": "TCR_EL1", "msb": 32, "lsb": 32}, {"name": "SCXTNUM_EL0", "msb": 31, "lsb": 31}, {"name": "SCXTNUM_EL1", "msb": 30, "lsb": 30}, {"name": "SCTLR_EL1", "msb": 29, "lsb": 29}, {"name": "PAR_EL1", "msb": 27, "lsb": 27}, {"name": "MAIR_EL1", "msb": 24, "lsb": 24}, {"name": "LORSA_EL1", "msb": 23, "lsb": 23}, {"name": "LORN_EL1", "msb": 22, "lsb": 22}, {"name": "LOREA_EL1", "msb": 20, "lsb": 20}, {"name": "LORC_EL1", "msb": 19, "lsb": 19}, {"name": "FAR_EL1", "msb": 17, "lsb": 17}, {"name": "ESR_EL1", "msb": 16, "lsb": 16}, {"name": "CSSELR_EL1", "msb": 13, "lsb": 13}, {"name": "CPACR_EL1", "msb": 12, "lsb": 12}, {"name": "CONTEXTIDR_EL1", "msb": 11, "lsb": 11}, {"name": "APIBKey", "msb": 8, "lsb": 8}, {"name": "APIAKey", "msb": 7, "lsb": 7}, {"name": "APGAKey", "msb": 6, "lsb": 6}, {"name": "APDBKey", "msb": 5, "lsb": 5}, {"name": "APDAKey", "msb": 4, "lsb": 4}, {"name": "AMAIR_EL1", "msb": 3, "lsb": 3}, {"name": "AFSR1_EL1", "msb": 1, "lsb": 1}, {"name": "AFSR0_EL1", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "HPFAR_EL2", "fullname": "Hypervisor IPA Fault Address Register", "enc": [3, 4, 6, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "FIPA", "msb": 43, "lsb": 4}]}], "width": 64}, {"index": 0, "name": "HSTR_EL2", "fullname": "Hypervisor System Trap Register", "enc": [3, 4, 1, 1, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "T<n>", "msb": 15, "lsb": 15}, {"name": "T15", "msb": 15, "lsb": 15}, {"name": "T13", "msb": 13, "lsb": 13}, {"name": "T12", "msb": 12, "lsb": 12}, {"name": "T11", "msb": 11, "lsb": 11}, {"name": "T10", "msb": 10, "lsb": 10}, {"name": "T9", "msb": 9, "lsb": 9}, {"name": "T8", "msb": 8, "lsb": 8}, {"name": "T7", "msb": 7, "lsb": 7}, {"name": "T6", "msb": 6, "lsb": 6}, {"name": "T5", "msb": 5, "lsb": 5}, {"name": "T3", "msb": 3, "lsb": 3}, {"name": "T2", "msb": 2, "lsb": 2}, {"name": "T1", "msb": 1, "lsb": 1}, {"name": "T0", "msb": 0, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ICC_AP0R0_EL1", "fullname": "Interrupt Controller Active Priorities Group 0 Registers", "enc": [3, 0, 12, 8, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 1, "name": "ICC_AP0R1_EL1", "fullname": "Interrupt Controller Active Priorities Group 0 Registers", "enc": [3, 0, 12, 8, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 2, "name": "ICC_AP0R2_EL1", "fullname": "Interrupt Controller Active Priorities Group 0 Registers", "enc": [3, 0, 12, 8, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 3, "name": "ICC_AP0R3_EL1", "fullname": "Interrupt Controller Active Priorities Group 0 Registers", "enc": [3, 0, 12, 8, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_AP1R0_EL1", "fullname": "Interrupt Controller Active Priorities Group 1 Registers", "enc": [3, 0, 12, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 1, "name": "ICC_AP1R1_EL1", "fullname": "Interrupt Controller Active Priorities Group 1 Registers", "enc": [3, 0, 12, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 2, "name": "ICC_AP1R2_EL1", "fullname": "Interrupt Controller Active Priorities Group 1 Registers", "enc": [3, 0, 12, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 3, "name": "ICC_AP1R3_EL1", "fullname": "Interrupt Controller Active Priorities Group 1 Registers", "enc": [3, 0, 12, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_ASGI1R_EL1", "fullname": "Interrupt Controller Alias Software Generated Interrupt Group 1 Register", "enc": [3, 0, 12, 11, 6], "accessors": ["MSR"], "fieldsets": [{"fields": [{"name": "Aff3", "msb": 55, "lsb": 48}, {"name": "RS", "msb": 47, "lsb": 44}, {"name": "IRM", "msb": 40, "lsb": 40}, {"name": "Aff2", "msb": 39, "lsb": 32}, {"name": "INTID", "msb": 27, "lsb": 24}, {"name": "Aff1", "msb": 23, "lsb": 16}, {"name": "TargetList", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_BPR0_EL1", "fullname": "Interrupt Controller Binary Point Register 0", "enc": [3, 0, 12, 8, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BinaryPoint", "msb": 2, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_BPR1_EL1", "fullname": "Interrupt Controller Binary Point Register 1", "enc": [3, 0, 12, 12, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BinaryPoint", "msb": 2, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_CTLR_EL1", "fullname": "Interrupt Controller Control Register (EL1)", "enc": [3, 0, 12, 12, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ExtRange", "msb": 19, "lsb": 19}, {"name": "RSS", "msb": 18, "lsb": 18}, {"name": "A3V", "msb": 15, "lsb": 15}, {"name": "SEIS", "msb": 14, "lsb": 14}, {"name": "IDbits", "msb": 13, "lsb": 11}, {"name": "PRIbits", "msb": 10, "lsb": 8}, {"name": "PMHE", "msb": 6, "lsb": 6}, {"name": "EOImode", "msb": 1, "lsb": 1}, {"name": "CBPR", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_CTLR_EL3", "fullname": "Interrupt Controller Control Register (EL3)", "enc": [3, 6, 12, 12, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ExtRange", "msb": 19, "lsb": 19}, {"name": "RSS", "msb": 18, "lsb": 18}, {"name": "nDS", "msb": 17, "lsb": 17}, {"name": "A3V", "msb": 15, "lsb": 15}, {"name": "SEIS", "msb": 14, "lsb": 14}, {"name": "IDbits", "msb": 13, "lsb": 11}, {"name": "PRIbits", "msb": 10, "lsb": 8}, {"name": "PMHE", "msb": 6, "lsb": 6}, {"name": "RM", "msb": 5, "lsb": 5}, {"name": "EOImode_EL1NS", "msb": 4, "lsb": 4}, {"name": "EOImode_EL1S", "msb": 3, "lsb": 3}, {"name": "EOImode_EL3", "msb": 2, "lsb": 2}, {"name": "CBPR_EL1NS", "msb": 1, "lsb": 1}, {"name": "CBPR_EL1S", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_DIR_EL1", "fullname": "Interrupt Controller Deactivate Interrupt Register", "enc": [3, 0, 12, 11, 1], "accessors": ["MSR"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_EOIR0_EL1", "fullname": "Interrupt Controller End Of Interrupt Register 0", "enc": [3, 0, 12, 8, 1], "accessors": ["MSR"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_EOIR1_EL1", "fullname": "Interrupt Controller End Of Interrupt Register 1", "enc": [3, 0, 12, 12, 1], "accessors": ["MSR"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_HPPIR0_EL1", "fullname": "Interrupt Controller Highest Priority Pending Interrupt Register 0", "enc": [3, 0, 12, 8, 2], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_HPPIR1_EL1", "fullname": "Interrupt Controller Highest Priority Pending Interrupt Register 1", "enc": [3, 0, 12, 12, 2], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_IAR0_EL1", "fullname": "Interrupt Controller Interrupt Acknowledge Register 0", "enc": [3, 0, 12, 8, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_IAR1_EL1", "fullname": "Interrupt Controller Interrupt Acknowledge Register 1", "enc": [3, 0, 12, 12, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_IGRPEN0_EL1", "fullname": "Interrupt Controller Interrupt Group 0 Enable register", "enc": [3, 0, 12, 12, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "Enable", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_IGRPEN1_EL1", "fullname": "Interrupt Controller Interrupt Group 1 Enable register", "enc": [3, 0, 12, 12, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "Enable", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_IGRPEN1_EL3", "fullname": "Interrupt Controller Interrupt Group 1 Enable register (EL3)", "enc": [3, 6, 12, 12, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "EnableGrp1S", "msb": 1, "lsb": 1}, {"name": "EnableGrp1NS", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_PMR_EL1", "fullname": "Interrupt Controller Interrupt Priority Mask Register", "enc": [3, 0, 4, 6, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "Priority", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_RPR_EL1", "fullname": "Interrupt Controller Running Priority Register", "enc": [3, 0, 12, 11, 3], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Priority", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_SGI0R_EL1", "fullname": "Interrupt Controller Software Generated Interrupt Group 0 Register", "enc": [3, 0, 12, 11, 7], "accessors": ["MSR"], "fieldsets": [{"fields": [{"name": "Aff3", "msb": 55, "lsb": 48}, {"name": "RS", "msb": 47, "lsb": 44}, {"name": "IRM", "msb": 40, "lsb": 40}, {"name": "Aff2", "msb": 39, "lsb": 32}, {"name": "INTID", "msb": 27, "lsb": 24}, {"name": "Aff1", "msb": 23, "lsb": 16}, {"name": "TargetList", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_SGI1R_EL1", "fullname": "Interrupt Controller Software Generated Interrupt Group 1 Register", "enc": [3, 0, 12, 11, 5], "accessors": ["MSR"], "fieldsets": [{"fields": [{"name": "Aff3", "msb": 55, "lsb": 48}, {"name": "RS", "msb": 47, "lsb": 44}, {"name": "IRM", "msb": 40, "lsb": 40}, {"name": "Aff2", "msb": 39, "lsb": 32}, {"name": "INTID", "msb": 27, "lsb": 24}, {"name": "Aff1", "msb": 23, "lsb": 16}, {"name": "TargetList", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_SRE_EL1", "fullname": "Interrupt Controller System Register Enable register (EL1)", "enc": [3, 0, 12, 12, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "DIB", "msb": 2, "lsb": 2}, {"name": "DFB", "msb": 1, "lsb": 1}, {"name": "SRE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_SRE_EL2", "fullname": "Interrupt Controller System Register Enable register (EL2)", "enc": [3, 4, 12, 9, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "Enable", "msb": 3, "lsb": 3}, {"name": "DIB", "msb": 2, "lsb": 2}, {"name": "DFB", "msb": 1, "lsb": 1}, {"name": "SRE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_SRE_EL3", "fullname": "Interrupt Controller System Register Enable register (EL3)", "enc": [3, 6, 12, 12, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "Enable", "msb": 3, "lsb": 3}, {"name": "DIB", "msb": 2, "lsb": 2}, {"name": "DFB", "msb": 1, "lsb": 1}, {"name": "SRE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICH_AP0R0_EL2", "fullname": "Interrupt Controller Hyp Active Priorities Group 0 Registers", "enc": [3, 4, 12, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P<x>", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 1, "name": "ICH_AP0R1_EL2", "fullname": "Interrupt Controller Hyp Active Priorities Group 0 Registers", "enc": [3, 4, 12, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P<x>", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 2, "name": "ICH_AP0R2_EL2", "fullname": "Interrupt Controller Hyp Active Priorities Group 0 Registers", "enc": [3, 4, 12, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P<x>", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 3, "name": "ICH_AP0R3_EL2", "fullname": "Interrupt Controller Hyp Active Priorities Group 0 Registers", "enc": [3, 4, 12, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P<x>", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICH_AP1R0_EL2", "fullname": "Interrupt Controller Hyp Active Priorities Group 1 Registers", "enc": [3, 4, 12, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P<x>", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 1, "name": "ICH_AP1R1_EL2", "fullname": "Interrupt Controller Hyp Active Priorities Group 1 Registers", "enc": [3, 4, 12, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P<x>", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 2, "name": "ICH_AP1R2_EL2", "fullname": "Interrupt Controller Hyp Active Priorities Group 1 Registers", "enc": [3, 4, 12, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P<x>", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 3, "name": "ICH_AP1R3_EL2", "fullname": "Interrupt Controller Hyp Active Priorities Group 1 Registers", "enc": [3, 4, 12, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P<x>", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICH_EISR_EL2", "fullname": "Interrupt Controller End of Interrupt Status Register", "enc": [3, 4, 12, 11, 3], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Status<n>", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICH_ELRSR_EL2", "fullname": "Interrupt Controller Empty List Register Status Register", "enc": [3, 4, 12, 11, 5], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Status<n>", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICH_HCR_EL2", "fullname": "Interrupt Controller Hyp Control Register", "enc": [3, 4, 12, 11, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "EOIcount", "msb": 31, "lsb": 27}, {"name": "DVIM", "msb": 15, "lsb": 15}, {"name": "TDIR", "msb": 14, "lsb": 14}, {"name": "TSEI", "msb": 13, "lsb": 13}, {"name": "TALL1", "msb": 12, "lsb": 12}, {"name": "TALL0", "msb": 11, "lsb": 11}, {"name": "TC", "msb": 10, "lsb": 10}, {"name": "vSGIEOICount", "msb": 8, "lsb": 8}, {"name": "VGrp1DIE", "msb": 7, "lsb": 7}, {"name": "VGrp1EIE", "msb": 6, "lsb": 6}, {"name": "VGrp0DIE", "msb": 5, "lsb": 5}, {"name": "VGrp0EIE", "msb": 4, "lsb": 4}, {"name": "NPIE", "msb": 3, "lsb": 3}, {"name": "LRENPIE", "msb": 2, "lsb": 2}, {"name": "UIE", "msb": 1, "lsb": 1}, {"name": "En", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICH_LR0_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 1, "name": "ICH_LR1_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 2, "name": "ICH_LR2_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 3, "name": "ICH_LR3_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 4, "name": "ICH_LR4_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 5, "name": "ICH_LR5_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 6, "name": "ICH_LR6_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 7, "name": "ICH_LR7_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 8, "name": "ICH_LR8_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 9, "name": "ICH_LR9_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 10, "name": "ICH_LR10_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 11, "name": "ICH_LR11_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 12, "name": "ICH_LR12_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 13, "name": "ICH_LR13_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 14, "name": "ICH_LR14_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 15, "name": "ICH_LR15_EL2", "fullname": "Interrupt Controller List Registers", "enc": [3, 4, 12, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "State", "msb": 63, "lsb": 62}, {"name": "HW", "msb": 61, "lsb": 61}, {"name": "Group", "msb": 60, "lsb": 60}, {"name": "Priority", "msb": 55, "lsb": 48}, {"name": "pINTID", "msb": 44, "lsb": 32}, {"name": "vINTID", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICH_MISR_EL2", "fullname": "Interrupt Controller Maintenance Interrupt State Register", "enc": [3, 4, 12, 11, 2], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "VGrp1D", "msb": 7, "lsb": 7}, {"name": "VGrp1E", "msb": 6, "lsb": 6}, {"name": "VGrp0D", "msb": 5, "lsb": 5}, {"name": "VGrp0E", "msb": 4, "lsb": 4}, {"name": "NP", "msb": 3, "lsb": 3}, {"name": "LRENP", "msb": 2, "lsb": 2}, {"name": "U", "msb": 1, "lsb": 1}, {"name": "EOI", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICH_VMCR_EL2", "fullname": "Interrupt Controller Virtual Machine Control Register", "enc": [3, 4, 12, 11, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "VPMR", "msb": 31, "lsb": 24}, {"name": "VBPR0", "msb": 23, "lsb": 21}, {"name": "VBPR1", "msb": 20, "lsb": 18}, {"name": "VEOIM", "msb": 9, "lsb": 9}, {"name": "VCBPR", "msb": 4, "lsb": 4}, {"name": "VFIQEn", "msb": 3, "lsb": 3}, {"name": "VAckCtl", "msb": 2, "lsb": 2}, {"name": "VENG1", "msb": 1, "lsb": 1}, {"name": "VENG0", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICH_VTR_EL2", "fullname": "Interrupt Controller VGIC Type Register", "enc": [3, 4, 12, 11, 1], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "PRIbits", "msb": 31, "lsb": 29}, {"name": "PREbits", "msb": 28, "lsb": 26}, {"name": "IDbits", "msb": 25, "lsb": 23}, {"name": "SEIS", "msb": 22, "lsb": 22}, {"name": "A3V", "msb": 21, "lsb": 21}, {"name": "nV4", "msb": 20, "lsb": 20}, {"name": "TDS", "msb": 19, "lsb": 19}, {"name": "DVIM", "msb": 18, "lsb": 18}, {"name": "ListRegs", "msb": 4, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "IALLUIS", "fullname": "Instruction Cache Invalidate All to PoU, Inner Shareable", "enc": [1, 0, 7, 1, 0], "accessors": ["IC"], "fieldsets": []}, {"index": 0, "name": "IALLU", "fullname": "Instruction Cache Invalidate All to PoU", "enc": [1, 0, 7, 5, 0], "accessors": ["IC"], "fieldsets": []}, {"index": 0, "name": "IVAU", "fullname": "Instruction Cache line Invalidate by VA to PoU", "enc": [1, 3, 7, 5, 1], "accessors": ["IC"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "ICC_AP0R0_EL1", "fullname": "Interrupt Controller Virtual Active Priorities Group 0 Registers", "enc": [3, 0, 12, 8, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 1, "name": "ICC_AP0R1_EL1", "fullname": "Interrupt Controller Virtual Active Priorities Group 0 Registers", "enc": [3, 0, 12, 8, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 2, "name": "ICC_AP0R2_EL1", "fullname": "Interrupt Controller Virtual Active Priorities Group 0 Registers", "enc": [3, 0, 12, 8, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 3, "name": "ICC_AP0R3_EL1", "fullname": "Interrupt Controller Virtual Active Priorities Group 0 Registers", "enc": [3, 0, 12, 8, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_AP1R0_EL1", "fullname": "Interrupt Controller Virtual Active Priorities Group 1 Registers", "enc": [3, 0, 12, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 1, "name": "ICC_AP1R1_EL1", "fullname": "Interrupt Controller Virtual Active Priorities Group 1 Registers", "enc": [3, 0, 12, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 2, "name": "ICC_AP1R2_EL1", "fullname": "Interrupt Controller Virtual Active Priorities Group 1 Registers", "enc": [3, 0, 12, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 3, "name": "ICC_AP1R3_EL1", "fullname": "Interrupt Controller Virtual Active Priorities Group 1 Registers", "enc": [3, 0, 12, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_BPR0_EL1", "fullname": "Interrupt Controller Virtual Binary Point Register 0", "enc": [3, 0, 12, 8, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BinaryPoint", "msb": 2, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_BPR1_EL1", "fullname": "Interrupt Controller Virtual Binary Point Register 1", "enc": [3, 0, 12, 12, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BinaryPoint", "msb": 2, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_CTLR_EL1", "fullname": "Interrupt Controller Virtual Control Register", "enc": [3, 0, 12, 12, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ExtRange", "msb": 19, "lsb": 19}, {"name": "RSS", "msb": 18, "lsb": 18}, {"name": "A3V", "msb": 15, "lsb": 15}, {"name": "SEIS", "msb": 14, "lsb": 14}, {"name": "IDbits", "msb": 13, "lsb": 11}, {"name": "PRIbits", "msb": 10, "lsb": 8}, {"name": "EOImode", "msb": 1, "lsb": 1}, {"name": "CBPR", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_DIR_EL1", "fullname": "Interrupt Controller Deactivate Virtual Interrupt Register", "enc": [3, 0, 12, 11, 1], "accessors": ["MSR"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_EOIR0_EL1", "fullname": "Interrupt Controller Virtual End Of Interrupt Register 0", "enc": [3, 0, 12, 8, 1], "accessors": ["MSR"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_EOIR1_EL1", "fullname": "Interrupt Controller Virtual End Of Interrupt Register 1", "enc": [3, 0, 12, 12, 1], "accessors": ["MSR"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_HPPIR0_EL1", "fullname": "Interrupt Controller Virtual Highest Priority Pending Interrupt Register 0", "enc": [3, 0, 12, 8, 2], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_HPPIR1_EL1", "fullname": "Interrupt Controller Virtual Highest Priority Pending Interrupt Register 1", "enc": [3, 0, 12, 12, 2], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_IAR0_EL1", "fullname": "Interrupt Controller Virtual Interrupt Acknowledge Register 0", "enc": [3, 0, 12, 8, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_IAR1_EL1", "fullname": "Interrupt Controller Virtual Interrupt Acknowledge Register 1", "enc": [3, 0, 12, 12, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "INTID", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_IGRPEN0_EL1", "fullname": "Interrupt Controller Virtual Interrupt Group 0 Enable register", "enc": [3, 0, 12, 12, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "Enable", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_IGRPEN1_EL1", "fullname": "Interrupt Controller Virtual Interrupt Group 1 Enable register", "enc": [3, 0, 12, 12, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "Enable", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_PMR_EL1", "fullname": "Interrupt Controller Virtual Interrupt Priority Mask Register", "enc": [3, 0, 4, 6, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "Priority", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ICC_RPR_EL1", "fullname": "Interrupt Controller Virtual Running Priority Register", "enc": [3, 0, 12, 11, 3], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Priority", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ID_AA64AFR0_EL1", "fullname": "AArch64 Auxiliary Feature Register 0", "enc": [3, 0, 0, 5, 4], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 31, "lsb": 28}, {"name": "IMPLEMENTATION DEFINED", "msb": 27, "lsb": 24}, {"name": "IMPLEMENTATION DEFINED", "msb": 23, "lsb": 20}, {"name": "IMPLEMENTATION DEFINED", "msb": 19, "lsb": 16}, {"name": "IMPLEMENTATION DEFINED", "msb": 15, "lsb": 12}, {"name": "IMPLEMENTATION DEFINED", "msb": 11, "lsb": 8}, {"name": "IMPLEMENTATION DEFINED", "msb": 7, "lsb": 4}, {"name": "IMPLEMENTATION DEFINED", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ID_AA64AFR1_EL1", "fullname": "AArch64 Auxiliary Feature Register 1", "enc": [3, 0, 0, 5, 5], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "ID_AA64DFR0_EL1", "fullname": "AArch64 Debug Feature Register 0", "enc": [3, 0, 0, 5, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "MTPMU", "msb": 51, "lsb": 48}, {"name": "TraceFilt", "msb": 43, "lsb": 40}, {"name": "DoubleLock", "msb": 39, "lsb": 36}, {"name": "PMSVer", "msb": 35, "lsb": 32}, {"name": "CTX_CMPs", "msb": 31, "lsb": 28}, {"name": "WRPs", "msb": 23, "lsb": 20}, {"name": "BRPs", "msb": 15, "lsb": 12}, {"name": "PMUVer", "msb": 11, "lsb": 8}, {"name": "TraceVer", "msb": 7, "lsb": 4}, {"name": "DebugVer", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ID_AA64DFR1_EL1", "fullname": "AArch64 Debug Feature Register 1", "enc": [3, 0, 0, 5, 1], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "ID_AA64ISAR0_EL1", "fullname": "AArch64 Instruction Set Attribute Register 0", "enc": [3, 0, 0, 6, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "RNDR", "msb": 63, "lsb": 60}, {"name": "TLB", "msb": 59, "lsb": 56}, {"name": "TS", "msb": 55, "lsb": 52}, {"name": "FHM", "msb": 51, "lsb": 48}, {"name": "DP", "msb": 47, "lsb": 44}, {"name": "SM4", "msb": 43, "lsb": 40}, {"name": "SM3", "msb": 39, "lsb": 36}, {"name": "SHA3", "msb": 35, "lsb": 32}, {"name": "RDM", "msb": 31, "lsb": 28}, {"name": "Atomic", "msb": 23, "lsb": 20}, {"name": "CRC32", "msb": 19, "lsb": 16}, {"name": "SHA2", "msb": 15, "lsb": 12}, {"name": "SHA1", "msb": 11, "lsb": 8}, {"name": "AES", "msb": 7, "lsb": 4}]}], "width": 64}, {"index": 0, "name": "ID_AA64ISAR1_EL1", "fullname": "AArch64 Instruction Set Attribute Register 1", "enc": [3, 0, 0, 6, 1], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "LS64", "msb": 63, "lsb": 60}, {"name": "XS", "msb": 59, "lsb": 56}, {"name": "I8MM", "msb": 55, "lsb": 52}, {"name": "DGH", "msb": 51, "lsb": 48}, {"name": "BF16", "msb": 47, "lsb": 44}, {"name": "SPECRES", "msb": 43, "lsb": 40}, {"name": "SB", "msb": 39, "lsb": 36}, {"name": "FRINTTS", "msb": 35, "lsb": 32}, {"name": "GPI", "msb": 31, "lsb": 28}, {"name": "GPA", "msb": 27, "lsb": 24}, {"name": "LRCPC", "msb": 23, "lsb": 20}, {"name": "FCMA", "msb": 19, "lsb": 16}, {"name": "JSCVT", "msb": 15, "lsb": 12}, {"name": "API", "msb": 11, "lsb": 8}, {"name": "APA", "msb": 7, "lsb": 4}, {"name": "DPB", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ID_AA64ISAR2_EL1", "fullname": "AArch64 Instruction Set Attribute Register 2", "enc": [3, 0, 0, 6, 2], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "RPRES", "msb": 7, "lsb": 4}, {"name": "WFxT", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ID_AA64MMFR0_EL1", "fullname": "AArch64 Memory Model Feature Register 0", "enc": [3, 0, 0, 7, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "ECV", "msb": 63, "lsb": 60}, {"name": "FGT", "msb": 59, "lsb": 56}, {"name": "ExS", "msb": 47, "lsb": 44}, {"name": "TGran4_2", "msb": 43, "lsb": 40}, {"name": "TGran64_2", "msb": 39, "lsb": 36}, {"name": "TGran16_2", "msb": 35, "lsb": 32}, {"name": "TGran4", "msb": 31, "lsb": 28}, {"name": "TGran64", "msb": 27, "lsb": 24}, {"name": "TGran16", "msb": 23, "lsb": 20}, {"name": "BigEndEL0", "msb": 19, "lsb": 16}, {"name": "SNSMem", "msb": 15, "lsb": 12}, {"name": "BigEnd", "msb": 11, "lsb": 8}, {"name": "ASIDBits", "msb": 7, "lsb": 4}, {"name": "PARange", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ID_AA64MMFR1_EL1", "fullname": "AArch64 Memory Model Feature Register 1", "enc": [3, 0, 0, 7, 1], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "nTLBPA", "msb": 51, "lsb": 48}, {"name": "AFP", "msb": 47, "lsb": 44}, {"name": "HCX", "msb": 43, "lsb": 40}, {"name": "ETS", "msb": 39, "lsb": 36}, {"name": "TWED", "msb": 35, "lsb": 32}, {"name": "XNX", "msb": 31, "lsb": 28}, {"name": "SpecSEI", "msb": 27, "lsb": 24}, {"name": "PAN", "msb": 23, "lsb": 20}, {"name": "LO", "msb": 19, "lsb": 16}, {"name": "HPDS", "msb": 15, "lsb": 12}, {"name": "VH", "msb": 11, "lsb": 8}, {"name": "VMIDBits", "msb": 7, "lsb": 4}, {"name": "HAFDBS", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ID_AA64MMFR2_EL1", "fullname": "AArch64 Memory Model Feature Register 2", "enc": [3, 0, 0, 7, 2], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "E0PD", "msb": 63, "lsb": 60}, {"name": "EVT", "msb": 59, "lsb": 56}, {"name": "BBM", "msb": 55, "lsb": 52}, {"name": "TTL", "msb": 51, "lsb": 48}, {"name": "FWB", "msb": 43, "lsb": 40}, {"name": "IDS", "msb": 39, "lsb": 36}, {"name": "AT", "msb": 35, "lsb": 32}, {"name": "ST", "msb": 31, "lsb": 28}, {"name": "NV", "msb": 27, "lsb": 24}, {"name": "CCIDX", "msb": 23, "lsb": 20}, {"name": "VARange", "msb": 19, "lsb": 16}, {"name": "IESB", "msb": 15, "lsb": 12}, {"name": "LSM", "msb": 11, "lsb": 8}, {"name": "UAO", "msb": 7, "lsb": 4}, {"name": "CnP", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ID_AA64PFR0_EL1", "fullname": "AArch64 Processor Feature Register 0", "enc": [3, 0, 0, 4, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "CSV3", "msb": 63, "lsb": 60}, {"name": "CSV2", "msb": 59, "lsb": 56}, {"name": "DIT", "msb": 51, "lsb": 48}, {"name": "AMU", "msb": 47, "lsb": 44}, {"name": "MPAM", "msb": 43, "lsb": 40}, {"name": "SEL2", "msb": 39, "lsb": 36}, {"name": "SVE", "msb": 35, "lsb": 32}, {"name": "RAS", "msb": 31, "lsb": 28}, {"name": "GIC", "msb": 27, "lsb": 24}, {"name": "AdvSIMD", "msb": 23, "lsb": 20}, {"name": "FP", "msb": 19, "lsb": 16}, {"name": "EL3", "msb": 15, "lsb": 12}, {"name": "EL2", "msb": 11, "lsb": 8}, {"name": "EL1", "msb": 7, "lsb": 4}, {"name": "EL0", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ID_AA64PFR1_EL1", "fullname": "AArch64 Processor Feature Register 1", "enc": [3, 0, 0, 4, 1], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "CSV2_frac", "msb": 35, "lsb": 32}, {"name": "MPAM_frac", "msb": 19, "lsb": 16}, {"name": "RAS_frac", "msb": 15, "lsb": 12}, {"name": "MTE", "msb": 11, "lsb": 8}, {"name": "SSBS", "msb": 7, "lsb": 4}, {"name": "BT", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ID_AA64ZFR0_EL1", "fullname": "SVE Feature ID register 0", "enc": [3, 0, 0, 4, 4], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "F64MM", "msb": 59, "lsb": 56}, {"name": "F32MM", "msb": 55, "lsb": 52}, {"name": "I8MM", "msb": 47, "lsb": 44}, {"name": "BF16", "msb": 23, "lsb": 20}, {"name": "SVEver", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ID_AFR0_EL1", "fullname": "AArch32 Auxiliary Feature Register 0", "enc": [3, 0, 0, 1, 3], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 15, "lsb": 12}, {"name": "IMPLEMENTATION DEFINED", "msb": 11, "lsb": 8}, {"name": "IMPLEMENTATION DEFINED", "msb": 7, "lsb": 4}, {"name": "IMPLEMENTATION DEFINED", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_DFR0_EL1", "fullname": "AArch32 Debug Feature Register 0", "enc": [3, 0, 0, 1, 2], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "TraceFilt", "msb": 31, "lsb": 28}, {"name": "PerfMon", "msb": 27, "lsb": 24}, {"name": "MProfDbg", "msb": 23, "lsb": 20}, {"name": "MMapTrc", "msb": 19, "lsb": 16}, {"name": "CopTrc", "msb": 15, "lsb": 12}, {"name": "MMapDbg", "msb": 11, "lsb": 8}, {"name": "CopSDbg", "msb": 7, "lsb": 4}, {"name": "CopDbg", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_DFR1_EL1", "fullname": "Debug Feature Register 1", "enc": [3, 0, 0, 3, 5], "accessors": ["MRS"], "fieldsets": [{"instance": "ID_DFR1_EL1", "fields": [{"name": "MTPMU", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_ISAR0_EL1", "fullname": "AArch32 Instruction Set Attribute Register 0", "enc": [3, 0, 0, 2, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Divide", "msb": 27, "lsb": 24}, {"name": "Debug", "msb": 23, "lsb": 20}, {"name": "Coproc", "msb": 19, "lsb": 16}, {"name": "CmpBranch", "msb": 15, "lsb": 12}, {"name": "BitField", "msb": 11, "lsb": 8}, {"name": "BitCount", "msb": 7, "lsb": 4}, {"name": "Swap", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_ISAR1_EL1", "fullname": "AArch32 Instruction Set Attribute Register 1", "enc": [3, 0, 0, 2, 1], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Jazelle", "msb": 31, "lsb": 28}, {"name": "Interwork", "msb": 27, "lsb": 24}, {"name": "Immediate", "msb": 23, "lsb": 20}, {"name": "IfThen", "msb": 19, "lsb": 16}, {"name": "Extend", "msb": 15, "lsb": 12}, {"name": "Except_AR", "msb": 11, "lsb": 8}, {"name": "Except", "msb": 7, "lsb": 4}, {"name": "Endian", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_ISAR2_EL1", "fullname": "AArch32 Instruction Set Attribute Register 2", "enc": [3, 0, 0, 2, 2], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Reversal", "msb": 31, "lsb": 28}, {"name": "PSR_AR", "msb": 27, "lsb": 24}, {"name": "MultU", "msb": 23, "lsb": 20}, {"name": "MultS", "msb": 19, "lsb": 16}, {"name": "Mult", "msb": 15, "lsb": 12}, {"name": "MultiAccessInt", "msb": 11, "lsb": 8}, {"name": "MemHint", "msb": 7, "lsb": 4}, {"name": "LoadStore", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_ISAR3_EL1", "fullname": "AArch32 Instruction Set Attribute Register 3", "enc": [3, 0, 0, 2, 3], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "T32EE", "msb": 31, "lsb": 28}, {"name": "TrueNOP", "msb": 27, "lsb": 24}, {"name": "T32Copy", "msb": 23, "lsb": 20}, {"name": "TabBranch", "msb": 19, "lsb": 16}, {"name": "SynchPrim", "msb": 15, "lsb": 12}, {"name": "SVC", "msb": 11, "lsb": 8}, {"name": "SIMD", "msb": 7, "lsb": 4}, {"name": "Saturate", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_ISAR4_EL1", "fullname": "AArch32 Instruction Set Attribute Register 4", "enc": [3, 0, 0, 2, 4], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "SWP_frac", "msb": 31, "lsb": 28}, {"name": "PSR_M", "msb": 27, "lsb": 24}, {"name": "SynchPrim_frac", "msb": 23, "lsb": 20}, {"name": "Barrier", "msb": 19, "lsb": 16}, {"name": "SMC", "msb": 15, "lsb": 12}, {"name": "Writeback", "msb": 11, "lsb": 8}, {"name": "WithShifts", "msb": 7, "lsb": 4}, {"name": "Unpriv", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_ISAR5_EL1", "fullname": "AArch32 Instruction Set Attribute Register 5", "enc": [3, 0, 0, 2, 5], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "VCMA", "msb": 31, "lsb": 28}, {"name": "RDM", "msb": 27, "lsb": 24}, {"name": "CRC32", "msb": 19, "lsb": 16}, {"name": "SHA2", "msb": 15, "lsb": 12}, {"name": "SHA1", "msb": 11, "lsb": 8}, {"name": "AES", "msb": 7, "lsb": 4}, {"name": "SEVL", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_ISAR6_EL1", "fullname": "AArch32 Instruction Set Attribute Register 6", "enc": [3, 0, 0, 2, 7], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "I8MM", "msb": 27, "lsb": 24}, {"name": "BF16", "msb": 23, "lsb": 20}, {"name": "SPECRES", "msb": 19, "lsb": 16}, {"name": "SB", "msb": 15, "lsb": 12}, {"name": "FHM", "msb": 11, "lsb": 8}, {"name": "DP", "msb": 7, "lsb": 4}, {"name": "JSCVT", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_MMFR0_EL1", "fullname": "AArch32 Memory Model Feature Register 0", "enc": [3, 0, 0, 1, 4], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "InnerShr", "msb": 31, "lsb": 28}, {"name": "FCSE", "msb": 27, "lsb": 24}, {"name": "AuxReg", "msb": 23, "lsb": 20}, {"name": "TCM", "msb": 19, "lsb": 16}, {"name": "ShareLvl", "msb": 15, "lsb": 12}, {"name": "OuterShr", "msb": 11, "lsb": 8}, {"name": "PMSA", "msb": 7, "lsb": 4}, {"name": "VMSA", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_MMFR1_EL1", "fullname": "AArch32 Memory Model Feature Register 1", "enc": [3, 0, 0, 1, 5], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "BPred", "msb": 31, "lsb": 28}, {"name": "L1TstCln", "msb": 27, "lsb": 24}, {"name": "L1Uni", "msb": 23, "lsb": 20}, {"name": "L1Hvd", "msb": 19, "lsb": 16}, {"name": "L1UniSW", "msb": 15, "lsb": 12}, {"name": "L1HvdSW", "msb": 11, "lsb": 8}, {"name": "L1UniVA", "msb": 7, "lsb": 4}, {"name": "L1HvdVA", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_MMFR2_EL1", "fullname": "AArch32 Memory Model Feature Register 2", "enc": [3, 0, 0, 1, 6], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "HWAccFlg", "msb": 31, "lsb": 28}, {"name": "WFIStall", "msb": 27, "lsb": 24}, {"name": "MemBarr", "msb": 23, "lsb": 20}, {"name": "UniTLB", "msb": 19, "lsb": 16}, {"name": "HvdTLB", "msb": 15, "lsb": 12}, {"name": "L1HvdRng", "msb": 11, "lsb": 8}, {"name": "L1HvdBG", "msb": 7, "lsb": 4}, {"name": "L1HvdFG", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_MMFR3_EL1", "fullname": "AArch32 Memory Model Feature Register 3", "enc": [3, 0, 0, 1, 7], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Supersec", "msb": 31, "lsb": 28}, {"name": "CMemSz", "msb": 27, "lsb": 24}, {"name": "CohWalk", "msb": 23, "lsb": 20}, {"name": "PAN", "msb": 19, "lsb": 16}, {"name": "MaintBcst", "msb": 15, "lsb": 12}, {"name": "BPMaint", "msb": 11, "lsb": 8}, {"name": "CMaintSW", "msb": 7, "lsb": 4}, {"name": "CMaintVA", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_MMFR4_EL1", "fullname": "AArch32 Memory Model Feature Register 4", "enc": [3, 0, 0, 2, 6], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "EVT", "msb": 31, "lsb": 28}, {"name": "CCIDX", "msb": 27, "lsb": 24}, {"name": "LSM", "msb": 23, "lsb": 20}, {"name": "HPDS", "msb": 19, "lsb": 16}, {"name": "CnP", "msb": 15, "lsb": 12}, {"name": "XNX", "msb": 11, "lsb": 8}, {"name": "AC2", "msb": 7, "lsb": 4}, {"name": "SpecSEI", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_MMFR5_EL1", "fullname": "AArch32 Memory Model Feature Register 5", "enc": [3, 0, 0, 3, 6], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "nTLBPA", "msb": 7, "lsb": 4}, {"name": "ETS", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_PFR0_EL1", "fullname": "AArch32 Processor Feature Register 0", "enc": [3, 0, 0, 1, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "RAS", "msb": 31, "lsb": 28}, {"name": "DIT", "msb": 27, "lsb": 24}, {"name": "AMU", "msb": 23, "lsb": 20}, {"name": "CSV2", "msb": 19, "lsb": 16}, {"name": "State3", "msb": 15, "lsb": 12}, {"name": "State2", "msb": 11, "lsb": 8}, {"name": "State1", "msb": 7, "lsb": 4}, {"name": "State0", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_PFR1_EL1", "fullname": "AArch32 Processor Feature Register 1", "enc": [3, 0, 0, 1, 1], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "GIC", "msb": 31, "lsb": 28}, {"name": "Virt_frac", "msb": 27, "lsb": 24}, {"name": "Sec_frac", "msb": 23, "lsb": 20}, {"name": "GenTimer", "msb": 19, "lsb": 16}, {"name": "Virtualization", "msb": 15, "lsb": 12}, {"name": "MProgMod", "msb": 11, "lsb": 8}, {"name": "Security", "msb": 7, "lsb": 4}, {"name": "ProgMod", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "ID_PFR2_EL1", "fullname": "AArch32 Processor Feature Register 2", "enc": [3, 0, 0, 3, 4], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "RAS_frac", "msb": 11, "lsb": 8}, {"name": "SSBS", "msb": 7, "lsb": 4}, {"name": "CSV3", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "IFSR32_EL2", "fullname": "Instruction Fault Status Register (EL2)", "enc": [3, 4, 5, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "TTBCR.EAE==0", "fields": [{"name": "FnV", "msb": 16, "lsb": 16}, {"name": "ExT", "msb": 12, "lsb": 12}, {"name": "FS", "msb": 10, "lsb": 10}, {"name": "LPAE", "msb": 9, "lsb": 9}, {"name": "FS[3:0]", "msb": 3, "lsb": 0}]}, {"instance": "TTBCR.EAE==1", "fields": [{"name": "FnV", "msb": 16, "lsb": 16}, {"name": "ExT", "msb": 12, "lsb": 12}, {"name": "LPAE", "msb": 9, "lsb": 9}, {"name": "STATUS", "msb": 5, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ISR_EL1", "fullname": "Interrupt Status Register", "enc": [3, 0, 12, 1, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}]}], "width": 64}, {"index": 0, "name": "LORC_EL1", "fullname": "LORegion Control (EL1)", "enc": [3, 0, 10, 4, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "DS", "msb": 9, "lsb": 2}, {"name": "EN", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "LOREA_EL1", "fullname": "LORegion End Address (EL1)", "enc": [3, 0, 10, 4, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "EA[51:48]", "msb": 51, "lsb": 48}, {"name": "EA[47:16]", "msb": 47, "lsb": 16}]}], "width": 64}, {"index": 0, "name": "LORID_EL1", "fullname": "LORegionID (EL1)", "enc": [3, 0, 10, 4, 7], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "LD", "msb": 23, "lsb": 16}, {"name": "LR", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "LORN_EL1", "fullname": "LORegion Number (EL1)", "enc": [3, 0, 10, 4, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "Num", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "LORSA_EL1", "fullname": "LORegion Start Address (EL1)", "enc": [3, 0, 10, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "SA", "msb": 51, "lsb": 16}, {"name": "Valid", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MAIR_EL1", "fullname": "Memory Attribute Indirection Register (EL1)", "enc": [3, 0, 10, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "MAIR_EL12", "fullname": "Memory Attribute Indirection Register (EL1)", "enc": [3, 5, 10, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "MAIR_EL1", "fullname": "Memory Attribute Indirection Register (EL2)", "enc": [3, 0, 10, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "MAIR_EL2", "fullname": "Memory Attribute Indirection Register (EL2)", "enc": [3, 4, 10, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "MAIR_EL3", "fullname": "Memory Attribute Indirection Register (EL3)", "enc": [3, 6, 10, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "MDCCINT_EL1", "fullname": "Monitor DCC Interrupt Enable Register", "enc": [2, 0, 0, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RX", "msb": 30, "lsb": 30}, {"name": "TX", "msb": 29, "lsb": 29}]}], "width": 64}, {"index": 0, "name": "MDCCSR_EL0", "fullname": "Monitor DCC Status Register", "enc": [2, 3, 0, 1, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "RXfull", "msb": 30, "lsb": 30}, {"name": "TXfull", "msb": 29, "lsb": 29}]}], "width": 64}, {"index": 0, "name": "MDCR_EL2", "fullname": "Monitor Debug Configuration Register (EL2)", "enc": [3, 4, 1, 1, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "HPMFZS", "msb": 36, "lsb": 36}, {"name": "HPMFZO", "msb": 29, "lsb": 29}, {"name": "MTPME", "msb": 28, "lsb": 28}, {"name": "TDCC", "msb": 27, "lsb": 27}, {"name": "HLP", "msb": 26, "lsb": 26}, {"name": "HCCD", "msb": 23, "lsb": 23}, {"name": "TTRF", "msb": 19, "lsb": 19}, {"name": "HPMD", "msb": 17, "lsb": 17}, {"name": "HPMD", "msb": 17, "lsb": 17}, {"name": "TPMS", "msb": 14, "lsb": 14}, {"name": "E2PB", "msb": 13, "lsb": 12}, {"name": "TDRA", "msb": 11, "lsb": 11}, {"name": "TDOSA", "msb": 10, "lsb": 10}, {"name": "TDOSA", "msb": 10, "lsb": 10}, {"name": "TDA", "msb": 9, "lsb": 9}, {"name": "TDE", "msb": 8, "lsb": 8}, {"name": "HPME", "msb": 7, "lsb": 7}, {"name": "TPM", "msb": 6, "lsb": 6}, {"name": "TPMCR", "msb": 5, "lsb": 5}, {"name": "HPMN", "msb": 4, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MDCR_EL3", "fullname": "Monitor Debug Configuration Register (EL3)", "enc": [3, 6, 1, 3, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "EnPMSN", "msb": 36, "lsb": 36}, {"name": "MPMX", "msb": 35, "lsb": 35}, {"name": "MCCD", "msb": 34, "lsb": 34}, {"name": "MTPME", "msb": 28, "lsb": 28}, {"name": "TDCC", "msb": 27, "lsb": 27}, {"name": "SCCD", "msb": 23, "lsb": 23}, {"name": "EPMAD", "msb": 21, "lsb": 21}, {"name": "EPMAD", "msb": 21, "lsb": 21}, {"name": "EDAD", "msb": 20, "lsb": 20}, {"name": "EDAD", "msb": 20, "lsb": 20}, {"name": "EDAD", "msb": 20, "lsb": 20}, {"name": "TTRF", "msb": 19, "lsb": 19}, {"name": "STE", "msb": 18, "lsb": 18}, {"name": "SPME", "msb": 17, "lsb": 17}, {"name": "SPME", "msb": 17, "lsb": 17}, {"name": "SPME", "msb": 17, "lsb": 17}, {"name": "SDD", "msb": 16, "lsb": 16}, {"name": "SPD32", "msb": 15, "lsb": 14}, {"name": "NSPB", "msb": 13, "lsb": 12}, {"name": "TDOSA", "msb": 10, "lsb": 10}, {"name": "TDOSA", "msb": 10, "lsb": 10}, {"name": "TDA", "msb": 9, "lsb": 9}, {"name": "TPM", "msb": 6, "lsb": 6}]}], "width": 64}, {"index": 0, "name": "MDRAR_EL1", "fullname": "Monitor Debug ROM Address Register", "enc": [2, 0, 1, 0, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "ROMADDR", "msb": 51, "lsb": 12}, {"name": "Valid", "msb": 1, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MDSCR_EL1", "fullname": "Monitor Debug System Control Register", "enc": [2, 0, 0, 2, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TFO", "msb": 31, "lsb": 31}, {"name": "RXfull", "msb": 30, "lsb": 30}, {"name": "TXfull", "msb": 29, "lsb": 29}, {"name": "RXO", "msb": 27, "lsb": 27}, {"name": "TXU", "msb": 26, "lsb": 26}, {"name": "INTdis", "msb": 23, "lsb": 22}, {"name": "TDA", "msb": 21, "lsb": 21}, {"name": "SC2", "msb": 19, "lsb": 19}, {"name": "MDE", "msb": 15, "lsb": 15}, {"name": "HDE", "msb": 14, "lsb": 14}, {"name": "KDE", "msb": 13, "lsb": 13}, {"name": "TDCC", "msb": 12, "lsb": 12}, {"name": "ERR", "msb": 6, "lsb": 6}, {"name": "SS", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MIDR_EL1", "fullname": "Main ID Register", "enc": [3, 0, 0, 0, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Implementer", "msb": 31, "lsb": 24}, {"name": "Variant", "msb": 23, "lsb": 20}, {"name": "Architecture", "msb": 19, "lsb": 16}, {"name": "PartNum", "msb": 15, "lsb": 4}, {"name": "Revision", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAM0_EL1", "fullname": "MPAM0 Register (EL1)", "enc": [3, 0, 10, 5, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PMG_D", "msb": 47, "lsb": 40}, {"name": "PMG_I", "msb": 39, "lsb": 32}, {"name": "PARTID_D", "msb": 31, "lsb": 16}, {"name": "PARTID_I", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAM1_EL1", "fullname": "MPAM1 Register (EL1)", "enc": [3, 0, 10, 5, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MPAMEN", "msb": 63, "lsb": 63}, {"name": "FORCED_NS", "msb": 60, "lsb": 60}, {"name": "PMG_D", "msb": 47, "lsb": 40}, {"name": "PMG_I", "msb": 39, "lsb": 32}, {"name": "PARTID_D", "msb": 31, "lsb": 16}, {"name": "PARTID_I", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAM1_EL12", "fullname": "MPAM1 Register (EL1)", "enc": [3, 5, 10, 5, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MPAMEN", "msb": 63, "lsb": 63}, {"name": "FORCED_NS", "msb": 60, "lsb": 60}, {"name": "PMG_D", "msb": 47, "lsb": 40}, {"name": "PMG_I", "msb": 39, "lsb": 32}, {"name": "PARTID_D", "msb": 31, "lsb": 16}, {"name": "PARTID_I", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAM1_EL1", "fullname": "MPAM2 Register (EL2)", "enc": [3, 0, 10, 5, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MPAMEN", "msb": 63, "lsb": 63}, {"name": "TIDR", "msb": 58, "lsb": 58}, {"name": "TRAPMPAM0EL1", "msb": 49, "lsb": 49}, {"name": "TRAPMPAM1EL1", "msb": 48, "lsb": 48}, {"name": "PMG_D", "msb": 47, "lsb": 40}, {"name": "PMG_I", "msb": 39, "lsb": 32}, {"name": "PARTID_D", "msb": 31, "lsb": 16}, {"name": "PARTID_I", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAM2_EL2", "fullname": "MPAM2 Register (EL2)", "enc": [3, 4, 10, 5, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MPAMEN", "msb": 63, "lsb": 63}, {"name": "TIDR", "msb": 58, "lsb": 58}, {"name": "TRAPMPAM0EL1", "msb": 49, "lsb": 49}, {"name": "TRAPMPAM1EL1", "msb": 48, "lsb": 48}, {"name": "PMG_D", "msb": 47, "lsb": 40}, {"name": "PMG_I", "msb": 39, "lsb": 32}, {"name": "PARTID_D", "msb": 31, "lsb": 16}, {"name": "PARTID_I", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAM3_EL3", "fullname": "MPAM3 Register (EL3)", "enc": [3, 6, 10, 5, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MPAMEN", "msb": 63, "lsb": 63}, {"name": "TRAPLOWER", "msb": 62, "lsb": 62}, {"name": "SDEFLT", "msb": 61, "lsb": 61}, {"name": "FORCE_NS", "msb": 60, "lsb": 60}, {"name": "PMG_D", "msb": 47, "lsb": 40}, {"name": "PMG_I", "msb": 39, "lsb": 32}, {"name": "PARTID_D", "msb": 31, "lsb": 16}, {"name": "PARTID_I", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAMHCR_EL2", "fullname": "MPAM Hypervisor Control Register (EL2)", "enc": [3, 4, 10, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TRAP_MPAMIDR_EL1", "msb": 31, "lsb": 31}, {"name": "GSTAPP_PLK", "msb": 8, "lsb": 8}, {"name": "EL1_VPMEN", "msb": 1, "lsb": 1}, {"name": "EL0_VPMEN", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAMIDR_EL1", "fullname": "MPAM ID Register (EL1)", "enc": [3, 0, 10, 4, 4], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "HAS_SDEFLT", "msb": 61, "lsb": 61}, {"name": "HAS_FORCE_NS", "msb": 60, "lsb": 60}, {"name": "HAS_TIDR", "msb": 58, "lsb": 58}, {"name": "PMG_MAX", "msb": 39, "lsb": 32}, {"name": "VPMR_MAX", "msb": 20, "lsb": 18}, {"name": "HAS_HCR", "msb": 17, "lsb": 17}, {"name": "PARTID_MAX", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAMVPM0_EL2", "fullname": "MPAM Virtual PARTID Mapping Register 0", "enc": [3, 4, 10, 6, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PhyPARTID3", "msb": 63, "lsb": 48}, {"name": "PhyPARTID2", "msb": 47, "lsb": 32}, {"name": "PhyPARTID1", "msb": 31, "lsb": 16}, {"name": "PhyPARTID0", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAMVPM1_EL2", "fullname": "MPAM Virtual PARTID Mapping Register 1", "enc": [3, 4, 10, 6, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PhyPARTID7", "msb": 63, "lsb": 48}, {"name": "PhyPARTID6", "msb": 47, "lsb": 32}, {"name": "PhyPARTID5", "msb": 31, "lsb": 16}, {"name": "PhyPARTID4", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAMVPM2_EL2", "fullname": "MPAM Virtual PARTID Mapping Register 2", "enc": [3, 4, 10, 6, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PhyPARTID11", "msb": 63, "lsb": 48}, {"name": "PhyPARTID10", "msb": 47, "lsb": 32}, {"name": "PhyPARTID9", "msb": 31, "lsb": 16}, {"name": "PhyPARTID8", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAMVPM3_EL2", "fullname": "MPAM Virtual PARTID Mapping Register 3", "enc": [3, 4, 10, 6, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PhyPARTID15", "msb": 63, "lsb": 48}, {"name": "PhyPARTID14", "msb": 47, "lsb": 32}, {"name": "PhyPARTID13", "msb": 31, "lsb": 16}, {"name": "PhyPARTID12", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAMVPM4_EL2", "fullname": "MPAM Virtual PARTID Mapping Register 4", "enc": [3, 4, 10, 6, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PhyPARTID19", "msb": 63, "lsb": 48}, {"name": "PhyPARTID18", "msb": 47, "lsb": 32}, {"name": "PhyPARTID17", "msb": 31, "lsb": 16}, {"name": "PhyPARTID16", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAMVPM5_EL2", "fullname": "MPAM Virtual PARTID Mapping Register 5", "enc": [3, 4, 10, 6, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PhyPARTID23", "msb": 63, "lsb": 48}, {"name": "PhyPARTID22", "msb": 47, "lsb": 32}, {"name": "PhyPARTID21", "msb": 31, "lsb": 16}, {"name": "PhyPARTID20", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAMVPM6_EL2", "fullname": "MPAM Virtual PARTID Mapping Register 6", "enc": [3, 4, 10, 6, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PhyPARTID27", "msb": 63, "lsb": 48}, {"name": "PhyPARTID26", "msb": 47, "lsb": 32}, {"name": "PhyPARTID25", "msb": 31, "lsb": 16}, {"name": "PhyPARTID24", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAMVPM7_EL2", "fullname": "MPAM Virtual PARTID Mapping Register 7", "enc": [3, 4, 10, 6, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PhyPARTID31", "msb": 63, "lsb": 48}, {"name": "PhyPARTID30", "msb": 47, "lsb": 32}, {"name": "PhyPARTID29", "msb": 31, "lsb": 16}, {"name": "PhyPARTID28", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPAMVPMV_EL2", "fullname": "MPAM Virtual Partition Mapping Valid Register", "enc": [3, 4, 10, 4, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "VPM_V<m>", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPIDR_EL1", "fullname": "Multiprocessor Affinity Register", "enc": [3, 0, 0, 0, 5], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Aff3", "msb": 39, "lsb": 32}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "MT", "msb": 24, "lsb": 24}, {"name": "Aff2", "msb": 23, "lsb": 16}, {"name": "Aff1", "msb": 15, "lsb": 8}, {"name": "Aff0", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MVFR0_EL1", "fullname": "AArch32 Media and VFP Feature Register 0", "enc": [3, 0, 0, 3, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "FPRound", "msb": 31, "lsb": 28}, {"name": "FPShVec", "msb": 27, "lsb": 24}, {"name": "FPSqrt", "msb": 23, "lsb": 20}, {"name": "FPDivide", "msb": 19, "lsb": 16}, {"name": "FPTrap", "msb": 15, "lsb": 12}, {"name": "FPDP", "msb": 11, "lsb": 8}, {"name": "FPSP", "msb": 7, "lsb": 4}, {"name": "SIMDReg", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "MVFR1_EL1", "fullname": "AArch32 Media and VFP Feature Register 1", "enc": [3, 0, 0, 3, 1], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "SIMDFMAC", "msb": 31, "lsb": 28}, {"name": "FPHP", "msb": 27, "lsb": 24}, {"name": "SIMDHP", "msb": 23, "lsb": 20}, {"name": "SIMDSP", "msb": 19, "lsb": 16}, {"name": "SIMDInt", "msb": 15, "lsb": 12}, {"name": "SIMDLS", "msb": 11, "lsb": 8}, {"name": "FPDNaN", "msb": 7, "lsb": 4}, {"name": "FPFtZ", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "MVFR2_EL1", "fullname": "AArch32 Media and VFP Feature Register 2", "enc": [3, 0, 0, 3, 2], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "FPMisc", "msb": 7, "lsb": 4}, {"name": "SIMDMisc", "msb": 3, "lsb": 0}]}, {"fields": []}], "width": 64}, {"index": 0, "name": "NZCV", "fullname": "Condition Flags", "enc": [3, 3, 4, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}]}], "width": 64}, {"index": 0, "name": "OSDLR_EL1", "fullname": "OS Double Lock Register", "enc": [2, 0, 1, 3, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "DLK", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "OSDTRRX_EL1", "fullname": "OS Lock Data Transfer Register, Receive", "enc": [2, 0, 0, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "OSDTRTX_EL1", "fullname": "OS Lock Data Transfer Register, Transmit", "enc": [2, 0, 0, 3, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "OSECCR_EL1", "fullname": "OS Lock Exception Catch Control Register", "enc": [2, 0, 0, 6, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "OSLSR_EL1.OSLK == 1", "fields": [{"name": "EDECCR", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "OSLAR_EL1", "fullname": "OS Lock Access Register", "enc": [2, 0, 1, 0, 4], "accessors": ["MSR"], "fieldsets": [{"fields": [{"name": "OSLK", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "OSLSR_EL1", "fullname": "OS Lock Status Register", "enc": [2, 0, 1, 1, 4], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "OSLM", "msb": 3, "lsb": 3}, {"name": "nTT", "msb": 2, "lsb": 2}, {"name": "OSLK", "msb": 1, "lsb": 1}, {"name": "OSLM[0]", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PAN", "fullname": "Privileged Access Never", "enc": [3, 0, 4, 2, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PAN", "msb": 22, "lsb": 22}]}], "width": 64}, {"index": 0, "name": "PAR_EL1", "fullname": "Physical Address Register", "enc": [3, 0, 7, 4, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "AArch64-PAR_EL1.F == 0b0", "fields": [{"name": "ATTR", "msb": 63, "lsb": 56}, {"name": "PA[51:48]", "msb": 51, "lsb": 48}, {"name": "PA[47:12]", "msb": 47, "lsb": 12}, {"name": "IMPLEMENTATION DEFINED", "msb": 10, "lsb": 10}, {"name": "NS", "msb": 9, "lsb": 9}, {"name": "SH", "msb": 8, "lsb": 7}, {"name": "F", "msb": 0, "lsb": 0}]}, {"instance": "AArch64-PAR_EL1.F == 0b1", "fields": [{"name": "IMPLEMENTATION DEFINED", "msb": 63, "lsb": 56}, {"name": "IMPLEMENTATION DEFINED", "msb": 55, "lsb": 52}, {"name": "IMPLEMENTATION DEFINED", "msb": 51, "lsb": 48}, {"name": "S", "msb": 9, "lsb": 9}, {"name": "PTW", "msb": 8, "lsb": 8}, {"name": "FST", "msb": 6, "lsb": 1}, {"name": "F", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMBIDR_EL1", "fullname": "Profiling Buffer ID Register", "enc": [3, 0, 9, 10, 7], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "F", "msb": 5, "lsb": 5}, {"name": "P", "msb": 4, "lsb": 4}, {"name": "Align", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMBLIMITR_EL1", "fullname": "Profiling Buffer Limit Address Register", "enc": [3, 0, 9, 10, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "LIMIT", "msb": 63, "lsb": 12}, {"name": "PMFZ", "msb": 5, "lsb": 5}, {"name": "FM", "msb": 2, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMBPTR_EL1", "fullname": "Profiling Buffer Write Pointer Register", "enc": [3, 0, 9, 10, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "PMBSR_EL1", "fullname": "Profiling Buffer Status/syndrome Register", "enc": [3, 0, 9, 10, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "EC", "msb": 31, "lsb": 26}, {"name": "DL", "msb": 19, "lsb": 19}, {"name": "EA", "msb": 18, "lsb": 18}, {"name": "S", "msb": 17, "lsb": 17}, {"name": "COLL", "msb": 16, "lsb": 16}, {"name": "MSS", "msb": 15, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMCCFILTR_EL0", "fullname": "Performance Monitors Cycle Count Filter Register", "enc": [3, 3, 14, 15, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "SH", "msb": 24, "lsb": 24}]}], "width": 64}, {"index": 0, "name": "PMCCNTR_EL0", "fullname": "Performance Monitors Cycle Count Register", "enc": [3, 3, 9, 13, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "PMCEID0_EL0", "fullname": "Performance Monitors Common Event Identification register 0", "enc": [3, 3, 9, 12, 6], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "IDhi<n>", "msb": 63, "lsb": 32}, {"name": "ID<n>", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMCEID1_EL0", "fullname": "Performance Monitors Common Event Identification register 1", "enc": [3, 3, 9, 12, 7], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "IDhi<n>", "msb": 63, "lsb": 32}, {"name": "ID<n>", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMCNTENCLR_EL0", "fullname": "Performance Monitors Count Enable Clear register", "enc": [3, 3, 9, 12, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "C", "msb": 31, "lsb": 31}, {"name": "P<n>", "msb": 30, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMCNTENSET_EL0", "fullname": "Performance Monitors Count Enable Set register", "enc": [3, 3, 9, 12, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "C", "msb": 31, "lsb": 31}, {"name": "P<n>", "msb": 30, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMCR_EL0", "fullname": "Performance Monitors Control Register", "enc": [3, 3, 9, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "FZS", "msb": 32, "lsb": 32}, {"name": "IMP", "msb": 31, "lsb": 24}, {"name": "IDCODE", "msb": 23, "lsb": 16}, {"name": "N", "msb": 15, "lsb": 11}, {"name": "FZO", "msb": 9, "lsb": 9}, {"name": "LP", "msb": 7, "lsb": 7}, {"name": "LC", "msb": 6, "lsb": 6}, {"name": "DP", "msb": 5, "lsb": 5}, {"name": "X", "msb": 4, "lsb": 4}, {"name": "D", "msb": 3, "lsb": 3}, {"name": "C", "msb": 2, "lsb": 2}, {"name": "P", "msb": 1, "lsb": 1}, {"name": "E", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMEVCNTR0_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 1, "name": "PMEVCNTR1_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 2, "name": "PMEVCNTR2_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 3, "name": "PMEVCNTR3_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 4, "name": "PMEVCNTR4_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 5, "name": "PMEVCNTR5_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 6, "name": "PMEVCNTR6_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 7, "name": "PMEVCNTR7_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 8, "name": "PMEVCNTR8_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 9, "name": "PMEVCNTR9_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 10, "name": "PMEVCNTR10_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 11, "name": "PMEVCNTR11_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 12, "name": "PMEVCNTR12_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 13, "name": "PMEVCNTR13_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 14, "name": "PMEVCNTR14_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 15, "name": "PMEVCNTR15_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 16, "name": "PMEVCNTR16_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 17, "name": "PMEVCNTR17_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 18, "name": "PMEVCNTR18_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 19, "name": "PMEVCNTR19_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 20, "name": "PMEVCNTR20_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 21, "name": "PMEVCNTR21_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 22, "name": "PMEVCNTR22_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 23, "name": "PMEVCNTR23_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 24, "name": "PMEVCNTR24_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 25, "name": "PMEVCNTR25_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 26, "name": "PMEVCNTR26_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 27, "name": "PMEVCNTR27_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 28, "name": "PMEVCNTR28_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 29, "name": "PMEVCNTR29_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 30, "name": "PMEVCNTR30_EL0", "fullname": "Performance Monitors Event Count Registers", "enc": [3, 3, 14, 8, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": []}], "width": 64}, {"index": 0, "name": "PMEVTYPER0_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 1, "name": "PMEVTYPER1_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 2, "name": "PMEVTYPER2_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 3, "name": "PMEVTYPER3_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 4, "name": "PMEVTYPER4_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 5, "name": "PMEVTYPER5_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 6, "name": "PMEVTYPER6_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 7, "name": "PMEVTYPER7_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 8, "name": "PMEVTYPER8_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 9, "name": "PMEVTYPER9_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 10, "name": "PMEVTYPER10_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 11, "name": "PMEVTYPER11_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 12, "name": "PMEVTYPER12_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 13, "name": "PMEVTYPER13_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 14, "name": "PMEVTYPER14_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 15, "name": "PMEVTYPER15_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 16, "name": "PMEVTYPER16_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 17, "name": "PMEVTYPER17_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 18, "name": "PMEVTYPER18_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 19, "name": "PMEVTYPER19_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 20, "name": "PMEVTYPER20_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 21, "name": "PMEVTYPER21_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 22, "name": "PMEVTYPER22_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 23, "name": "PMEVTYPER23_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 24, "name": "PMEVTYPER24_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 25, "name": "PMEVTYPER25_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 26, "name": "PMEVTYPER26_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 27, "name": "PMEVTYPER27_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 28, "name": "PMEVTYPER28_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 29, "name": "PMEVTYPER29_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 30, "name": "PMEVTYPER30_EL0", "fullname": "Performance Monitors Event Type Registers", "enc": [3, 3, 14, 12, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "P", "msb": 31, "lsb": 31}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "NSK", "msb": 29, "lsb": 29}, {"name": "NSU", "msb": 28, "lsb": 28}, {"name": "NSH", "msb": 27, "lsb": 27}, {"name": "M", "msb": 26, "lsb": 26}, {"name": "MT", "msb": 25, "lsb": 25}, {"name": "SH", "msb": 24, "lsb": 24}, {"name": "evtCount[15:10]", "msb": 15, "lsb": 10}, {"name": "evtCount[9:0]", "msb": 9, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMINTENCLR_EL1", "fullname": "Performance Monitors Interrupt Enable Clear register", "enc": [3, 0, 9, 14, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "C", "msb": 31, "lsb": 31}, {"name": "P<n>", "msb": 30, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMINTENSET_EL1", "fullname": "Performance Monitors Interrupt Enable Set register", "enc": [3, 0, 9, 14, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "C", "msb": 31, "lsb": 31}, {"name": "P<n>", "msb": 30, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMMIR_EL1", "fullname": "Performance Monitors Machine Identification Register", "enc": [3, 0, 9, 14, 6], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "BUS_WIDTH", "msb": 19, "lsb": 16}, {"name": "BUS_SLOTS", "msb": 15, "lsb": 8}, {"name": "SLOTS", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMOVSCLR_EL0", "fullname": "Performance Monitors Overflow Flag Status Clear Register", "enc": [3, 3, 9, 12, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "C", "msb": 31, "lsb": 31}, {"name": "P<n>", "msb": 30, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMOVSSET_EL0", "fullname": "Performance Monitors Overflow Flag Status Set register", "enc": [3, 3, 9, 14, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "C", "msb": 31, "lsb": 31}, {"name": "P<n>", "msb": 30, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMSCR_EL1", "fullname": "Statistical Profiling Control Register (EL1)", "enc": [3, 0, 9, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PCT", "msb": 7, "lsb": 6}, {"name": "PCT", "msb": 7, "lsb": 6}, {"name": "TS", "msb": 5, "lsb": 5}, {"name": "PA", "msb": 4, "lsb": 4}, {"name": "CX", "msb": 3, "lsb": 3}, {"name": "E1SPE", "msb": 1, "lsb": 1}, {"name": "E0SPE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMSCR_EL12", "fullname": "Statistical Profiling Control Register (EL1)", "enc": [3, 5, 9, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PCT", "msb": 7, "lsb": 6}, {"name": "PCT", "msb": 7, "lsb": 6}, {"name": "TS", "msb": 5, "lsb": 5}, {"name": "PA", "msb": 4, "lsb": 4}, {"name": "CX", "msb": 3, "lsb": 3}, {"name": "E1SPE", "msb": 1, "lsb": 1}, {"name": "E0SPE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMSCR_EL1", "fullname": "Statistical Profiling Control Register (EL2)", "enc": [3, 0, 9, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PCT", "msb": 7, "lsb": 6}, {"name": "TS", "msb": 5, "lsb": 5}, {"name": "PA", "msb": 4, "lsb": 4}, {"name": "CX", "msb": 3, "lsb": 3}, {"name": "E2SPE", "msb": 1, "lsb": 1}, {"name": "E0HSPE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMSCR_EL2", "fullname": "Statistical Profiling Control Register (EL2)", "enc": [3, 4, 9, 9, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "PCT", "msb": 7, "lsb": 6}, {"name": "TS", "msb": 5, "lsb": 5}, {"name": "PA", "msb": 4, "lsb": 4}, {"name": "CX", "msb": 3, "lsb": 3}, {"name": "E2SPE", "msb": 1, "lsb": 1}, {"name": "E0HSPE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMSELR_EL0", "fullname": "Performance Monitors Event Counter Selection Register", "enc": [3, 3, 9, 12, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "SEL", "msb": 4, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMSEVFR_EL1", "fullname": "Sampling Event Filter Register", "enc": [3, 0, 9, 9, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "E[63]", "msb": 63, "lsb": 63}, {"name": "E[62]", "msb": 62, "lsb": 62}, {"name": "E[61]", "msb": 61, "lsb": 61}, {"name": "E[60]", "msb": 60, "lsb": 60}, {"name": "E[59]", "msb": 59, "lsb": 59}, {"name": "E[58]", "msb": 58, "lsb": 58}, {"name": "E[57]", "msb": 57, "lsb": 57}, {"name": "E[56]", "msb": 56, "lsb": 56}, {"name": "E[55]", "msb": 55, "lsb": 55}, {"name": "E[54]", "msb": 54, "lsb": 54}, {"name": "E[53]", "msb": 53, "lsb": 53}, {"name": "E[52]", "msb": 52, "lsb": 52}, {"name": "E[51]", "msb": 51, "lsb": 51}, {"name": "E[50]", "msb": 50, "lsb": 50}, {"name": "E[49]", "msb": 49, "lsb": 49}, {"name": "E[<x>]", "msb": 63, "lsb": 48}, {"name": "E[48]", "msb": 48, "lsb": 48}, {"name": "E[31]", "msb": 31, "lsb": 31}, {"name": "E[30]", "msb": 30, "lsb": 30}, {"name": "E[29]", "msb": 29, "lsb": 29}, {"name": "E[28]", "msb": 28, "lsb": 28}, {"name": "E[27]", "msb": 27, "lsb": 27}, {"name": "E[26]", "msb": 26, "lsb": 26}, {"name": "E[25]", "msb": 25, "lsb": 25}, {"name": "E[24]", "msb": 24, "lsb": 24}, {"name": "E[18]", "msb": 18, "lsb": 18}, {"name": "E[17]", "msb": 17, "lsb": 17}, {"name": "E[15]", "msb": 15, "lsb": 15}, {"name": "E[14]", "msb": 14, "lsb": 14}, {"name": "E[13]", "msb": 13, "lsb": 13}, {"name": "E[12]", "msb": 12, "lsb": 12}, {"name": "E[11]", "msb": 11, "lsb": 11}, {"name": "E[7]", "msb": 7, "lsb": 7}, {"name": "E[6]", "msb": 6, "lsb": 6}, {"name": "E[5]", "msb": 5, "lsb": 5}, {"name": "E[3]", "msb": 3, "lsb": 3}, {"name": "E[1]", "msb": 1, "lsb": 1}]}], "width": 64}, {"index": 0, "name": "PMSFCR_EL1", "fullname": "Sampling Filter Control Register", "enc": [3, 0, 9, 9, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ST", "msb": 18, "lsb": 18}, {"name": "LD", "msb": 17, "lsb": 17}, {"name": "B", "msb": 16, "lsb": 16}, {"name": "FnE", "msb": 3, "lsb": 3}, {"name": "FL", "msb": 2, "lsb": 2}, {"name": "FT", "msb": 1, "lsb": 1}, {"name": "FE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMSICR_EL1", "fullname": "Sampling Interval Counter Register", "enc": [3, 0, 9, 9, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ECOUNT", "msb": 63, "lsb": 56}, {"name": "COUNT", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMSIDR_EL1", "fullname": "Sampling Profiling ID Register", "enc": [3, 0, 9, 9, 7], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Format", "msb": 23, "lsb": 20}, {"name": "CountSize", "msb": 19, "lsb": 16}, {"name": "MaxSize", "msb": 15, "lsb": 12}, {"name": "Interval", "msb": 11, "lsb": 8}, {"name": "FnE", "msb": 6, "lsb": 6}, {"name": "ERnd", "msb": 5, "lsb": 5}, {"name": "LDS", "msb": 4, "lsb": 4}, {"name": "ArchInst", "msb": 3, "lsb": 3}, {"name": "FL", "msb": 2, "lsb": 2}, {"name": "FT", "msb": 1, "lsb": 1}, {"name": "FE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMSIRR_EL1", "fullname": "Sampling Interval Reload Register", "enc": [3, 0, 9, 9, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "INTERVAL", "msb": 31, "lsb": 8}, {"name": "RND", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMSLATFR_EL1", "fullname": "Sampling Latency Filter Register", "enc": [3, 0, 9, 9, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "MINLAT", "msb": 11, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMSNEVFR_EL1", "fullname": "Sampling Inverted Event Filter Register", "enc": [3, 0, 9, 9, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "PMSNEVFR_EL1", "fields": [{"name": "E[63]", "msb": 63, "lsb": 63}, {"name": "E[62]", "msb": 62, "lsb": 62}, {"name": "E[61]", "msb": 61, "lsb": 61}, {"name": "E[60]", "msb": 60, "lsb": 60}, {"name": "E[59]", "msb": 59, "lsb": 59}, {"name": "E[58]", "msb": 58, "lsb": 58}, {"name": "E[57]", "msb": 57, "lsb": 57}, {"name": "E[56]", "msb": 56, "lsb": 56}, {"name": "E[55]", "msb": 55, "lsb": 55}, {"name": "E[54]", "msb": 54, "lsb": 54}, {"name": "E[53]", "msb": 53, "lsb": 53}, {"name": "E[52]", "msb": 52, "lsb": 52}, {"name": "E[51]", "msb": 51, "lsb": 51}, {"name": "E[50]", "msb": 50, "lsb": 50}, {"name": "E[49]", "msb": 49, "lsb": 49}, {"name": "E[<x>]", "msb": 63, "lsb": 48}, {"name": "E[48]", "msb": 48, "lsb": 48}, {"name": "E[31]", "msb": 31, "lsb": 31}, {"name": "E[30]", "msb": 30, "lsb": 30}, {"name": "E[29]", "msb": 29, "lsb": 29}, {"name": "E[28]", "msb": 28, "lsb": 28}, {"name": "E[27]", "msb": 27, "lsb": 27}, {"name": "E[26]", "msb": 26, "lsb": 26}, {"name": "E[25]", "msb": 25, "lsb": 25}, {"name": "E[24]", "msb": 24, "lsb": 24}, {"name": "E[18]", "msb": 18, "lsb": 18}, {"name": "E[17]", "msb": 17, "lsb": 17}, {"name": "E[15]", "msb": 15, "lsb": 15}, {"name": "E[14]", "msb": 14, "lsb": 14}, {"name": "E[13]", "msb": 13, "lsb": 13}, {"name": "E[12]", "msb": 12, "lsb": 12}, {"name": "E[11]", "msb": 11, "lsb": 11}, {"name": "E[7]", "msb": 7, "lsb": 7}, {"name": "E[6]", "msb": 6, "lsb": 6}, {"name": "E[5]", "msb": 5, "lsb": 5}, {"name": "E[3]", "msb": 3, "lsb": 3}, {"name": "E[1]", "msb": 1, "lsb": 1}]}], "width": 64}, {"index": 0, "name": "PMSWINC_EL0", "fullname": "Performance Monitors Software Increment register", "enc": [3, 3, 9, 12, 4], "accessors": ["MSR"], "fieldsets": [{"fields": [{"name": "P<n>", "msb": 30, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMUSERENR_EL0", "fullname": "Performance Monitors User Enable Register", "enc": [3, 3, 9, 14, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ER", "msb": 3, "lsb": 3}, {"name": "CR", "msb": 2, "lsb": 2}, {"name": "SW", "msb": 1, "lsb": 1}, {"name": "EN", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMXEVCNTR_EL0", "fullname": "Performance Monitors Selected Event Count Register", "enc": [3, 3, 9, 13, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}, {"fields": [{"name": "PMEVCNTR<n>", "msb": 31, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "PMXEVTYPER_EL0", "fullname": "Performance Monitors Selected Event Type Register", "enc": [3, 3, 9, 13, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "REVIDR_EL1", "fullname": "Revision ID Register", "enc": [3, 0, 0, 0, 6], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "RGSR_EL1", "fullname": "Random Allocation Tag Seed Register.", "enc": [3, 0, 1, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "SEED", "msb": 23, "lsb": 8}, {"name": "TAG", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RMR_EL1", "fullname": "Reset Management Register (EL1)", "enc": [3, 0, 12, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RR", "msb": 1, "lsb": 1}, {"name": "AA64", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RMR_EL2", "fullname": "Reset Management Register (EL2)", "enc": [3, 4, 12, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RR", "msb": 1, "lsb": 1}, {"name": "AA64", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RMR_EL3", "fullname": "Reset Management Register (EL3)", "enc": [3, 6, 12, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RR", "msb": 1, "lsb": 1}, {"name": "AA64", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RNDRRS", "fullname": "Reseeded Random Number", "enc": [3, 3, 2, 4, 1], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "RNDR", "fullname": "Random Number", "enc": [3, 3, 2, 4, 0], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "RVBAR_EL1", "fullname": "Reset Vector Base Address Register (if EL2 and EL3 not implemented)", "enc": [3, 0, 12, 0, 1], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "RVBAR_EL2", "fullname": "Reset Vector Base Address Register (if EL3 not implemented)", "enc": [3, 4, 12, 0, 1], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "RVBAR_EL3", "fullname": "Reset Vector Base Address Register (if EL3 implemented)", "enc": [3, 6, 12, 0, 1], "accessors": ["MRS"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "SCR_EL3", "fullname": "Secure Configuration Register", "enc": [3, 6, 1, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "HXEn", "msb": 38, "lsb": 38}, {"name": "ADEn", "msb": 37, "lsb": 37}, {"name": "EnAS0", "msb": 36, "lsb": 36}, {"name": "AMVOFFEN", "msb": 35, "lsb": 35}, {"name": "TWEDEL", "msb": 33, "lsb": 30}, {"name": "TWEDEn", "msb": 29, "lsb": 29}, {"name": "ECVEn", "msb": 28, "lsb": 28}, {"name": "FGTEn", "msb": 27, "lsb": 27}, {"name": "ATA", "msb": 26, "lsb": 26}, {"name": "EnSCXT", "msb": 25, "lsb": 25}, {"name": "FIEN", "msb": 21, "lsb": 21}, {"name": "NMEA", "msb": 20, "lsb": 20}, {"name": "EASE", "msb": 19, "lsb": 19}, {"name": "EEL2", "msb": 18, "lsb": 18}, {"name": "API", "msb": 17, "lsb": 17}, {"name": "API", "msb": 17, "lsb": 17}, {"name": "APK", "msb": 16, "lsb": 16}, {"name": "TERR", "msb": 15, "lsb": 15}, {"name": "TLOR", "msb": 14, "lsb": 14}, {"name": "TWE", "msb": 13, "lsb": 13}, {"name": "TWI", "msb": 12, "lsb": 12}, {"name": "ST", "msb": 11, "lsb": 11}, {"name": "RW", "msb": 10, "lsb": 10}, {"name": "SIF", "msb": 9, "lsb": 9}, {"name": "SIF", "msb": 9, "lsb": 9}, {"name": "HCE", "msb": 8, "lsb": 8}, {"name": "SMD", "msb": 7, "lsb": 7}, {"name": "EA", "msb": 3, "lsb": 3}, {"name": "FIQ", "msb": 2, "lsb": 2}, {"name": "IRQ", "msb": 1, "lsb": 1}, {"name": "NS", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SCTLR_EL1", "fullname": "System Control Register (EL1)", "enc": [3, 0, 1, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "EPAN", "msb": 57, "lsb": 57}, {"name": "EnALS", "msb": 56, "lsb": 56}, {"name": "EnAS0", "msb": 55, "lsb": 55}, {"name": "EnASR", "msb": 54, "lsb": 54}, {"name": "TWEDEL", "msb": 49, "lsb": 46}, {"name": "TWEDEn", "msb": 45, "lsb": 45}, {"name": "DSSBS", "msb": 44, "lsb": 44}, {"name": "ATA", "msb": 43, "lsb": 43}, {"name": "ATA0", "msb": 42, "lsb": 42}, {"name": "TCF", "msb": 41, "lsb": 40}, {"name": "TCF0", "msb": 39, "lsb": 38}, {"name": "ITFSB", "msb": 37, "lsb": 37}, {"name": "BT1", "msb": 36, "lsb": 36}, {"name": "BT0", "msb": 35, "lsb": 35}, {"name": "EnIA", "msb": 31, "lsb": 31}, {"name": "EnIB", "msb": 30, "lsb": 30}, {"name": "LSMAOE", "msb": 29, "lsb": 29}, {"name": "nTLSMD", "msb": 28, "lsb": 28}, {"name": "EnDA", "msb": 27, "lsb": 27}, {"name": "UCI", "msb": 26, "lsb": 26}, {"name": "EE", "msb": 25, "lsb": 25}, {"name": "E0E", "msb": 24, "lsb": 24}, {"name": "SPAN", "msb": 23, "lsb": 23}, {"name": "EIS", "msb": 22, "lsb": 22}, {"name": "IESB", "msb": 21, "lsb": 21}, {"name": "TSCXT", "msb": 20, "lsb": 20}, {"name": "WXN", "msb": 19, "lsb": 19}, {"name": "nTWE", "msb": 18, "lsb": 18}, {"name": "nTWI", "msb": 16, "lsb": 16}, {"name": "UCT", "msb": 15, "lsb": 15}, {"name": "DZE", "msb": 14, "lsb": 14}, {"name": "EnDB", "msb": 13, "lsb": 13}, {"name": "I", "msb": 12, "lsb": 12}, {"name": "EOS", "msb": 11, "lsb": 11}, {"name": "EnRCTX", "msb": 10, "lsb": 10}, {"name": "UMA", "msb": 9, "lsb": 9}, {"name": "SED", "msb": 8, "lsb": 8}, {"name": "ITD", "msb": 7, "lsb": 7}, {"name": "nAA", "msb": 6, "lsb": 6}, {"name": "CP15BEN", "msb": 5, "lsb": 5}, {"name": "SA0", "msb": 4, "lsb": 4}, {"name": "SA", "msb": 3, "lsb": 3}, {"name": "C", "msb": 2, "lsb": 2}, {"name": "A", "msb": 1, "lsb": 1}, {"name": "M", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SCTLR_EL12", "fullname": "System Control Register (EL1)", "enc": [3, 5, 1, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "EPAN", "msb": 57, "lsb": 57}, {"name": "EnALS", "msb": 56, "lsb": 56}, {"name": "EnAS0", "msb": 55, "lsb": 55}, {"name": "EnASR", "msb": 54, "lsb": 54}, {"name": "TWEDEL", "msb": 49, "lsb": 46}, {"name": "TWEDEn", "msb": 45, "lsb": 45}, {"name": "DSSBS", "msb": 44, "lsb": 44}, {"name": "ATA", "msb": 43, "lsb": 43}, {"name": "ATA0", "msb": 42, "lsb": 42}, {"name": "TCF", "msb": 41, "lsb": 40}, {"name": "TCF0", "msb": 39, "lsb": 38}, {"name": "ITFSB", "msb": 37, "lsb": 37}, {"name": "BT1", "msb": 36, "lsb": 36}, {"name": "BT0", "msb": 35, "lsb": 35}, {"name": "EnIA", "msb": 31, "lsb": 31}, {"name": "EnIB", "msb": 30, "lsb": 30}, {"name": "LSMAOE", "msb": 29, "lsb": 29}, {"name": "nTLSMD", "msb": 28, "lsb": 28}, {"name": "EnDA", "msb": 27, "lsb": 27}, {"name": "UCI", "msb": 26, "lsb": 26}, {"name": "EE", "msb": 25, "lsb": 25}, {"name": "E0E", "msb": 24, "lsb": 24}, {"name": "SPAN", "msb": 23, "lsb": 23}, {"name": "EIS", "msb": 22, "lsb": 22}, {"name": "IESB", "msb": 21, "lsb": 21}, {"name": "TSCXT", "msb": 20, "lsb": 20}, {"name": "WXN", "msb": 19, "lsb": 19}, {"name": "nTWE", "msb": 18, "lsb": 18}, {"name": "nTWI", "msb": 16, "lsb": 16}, {"name": "UCT", "msb": 15, "lsb": 15}, {"name": "DZE", "msb": 14, "lsb": 14}, {"name": "EnDB", "msb": 13, "lsb": 13}, {"name": "I", "msb": 12, "lsb": 12}, {"name": "EOS", "msb": 11, "lsb": 11}, {"name": "EnRCTX", "msb": 10, "lsb": 10}, {"name": "UMA", "msb": 9, "lsb": 9}, {"name": "SED", "msb": 8, "lsb": 8}, {"name": "ITD", "msb": 7, "lsb": 7}, {"name": "nAA", "msb": 6, "lsb": 6}, {"name": "CP15BEN", "msb": 5, "lsb": 5}, {"name": "SA0", "msb": 4, "lsb": 4}, {"name": "SA", "msb": 3, "lsb": 3}, {"name": "C", "msb": 2, "lsb": 2}, {"name": "A", "msb": 1, "lsb": 1}, {"name": "M", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SCTLR_EL1", "fullname": "System Control Register (EL2)", "enc": [3, 0, 1, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "EPAN", "msb": 57, "lsb": 57}, {"name": "EnALS", "msb": 56, "lsb": 56}, {"name": "EnAS0", "msb": 55, "lsb": 55}, {"name": "EnASR", "msb": 54, "lsb": 54}, {"name": "TWEDEL", "msb": 49, "lsb": 46}, {"name": "TWEDEn", "msb": 45, "lsb": 45}, {"name": "DSSBS", "msb": 44, "lsb": 44}, {"name": "ATA", "msb": 43, "lsb": 43}, {"name": "ATA0", "msb": 42, "lsb": 42}, {"name": "TCF", "msb": 41, "lsb": 40}, {"name": "TCF0", "msb": 39, "lsb": 38}, {"name": "ITFSB", "msb": 37, "lsb": 37}, {"name": "BT", "msb": 36, "lsb": 36}, {"name": "BT0", "msb": 35, "lsb": 35}, {"name": "EnIA", "msb": 31, "lsb": 31}, {"name": "EnIB", "msb": 30, "lsb": 30}, {"name": "LSMAOE", "msb": 29, "lsb": 29}, {"name": "nTLSMD", "msb": 28, "lsb": 28}, {"name": "EnDA", "msb": 27, "lsb": 27}, {"name": "UCI", "msb": 26, "lsb": 26}, {"name": "EE", "msb": 25, "lsb": 25}, {"name": "E0E", "msb": 24, "lsb": 24}, {"name": "SPAN", "msb": 23, "lsb": 23}, {"name": "EIS", "msb": 22, "lsb": 22}, {"name": "IESB", "msb": 21, "lsb": 21}, {"name": "TSCXT", "msb": 20, "lsb": 20}, {"name": "WXN", "msb": 19, "lsb": 19}, {"name": "nTWE", "msb": 18, "lsb": 18}, {"name": "nTWI", "msb": 16, "lsb": 16}, {"name": "UCT", "msb": 15, "lsb": 15}, {"name": "DZE", "msb": 14, "lsb": 14}, {"name": "EnDB", "msb": 13, "lsb": 13}, {"name": "I", "msb": 12, "lsb": 12}, {"name": "EOS", "msb": 11, "lsb": 11}, {"name": "EnRCTX", "msb": 10, "lsb": 10}, {"name": "SED", "msb": 8, "lsb": 8}, {"name": "ITD", "msb": 7, "lsb": 7}, {"name": "nAA", "msb": 6, "lsb": 6}, {"name": "CP15BEN", "msb": 5, "lsb": 5}, {"name": "SA0", "msb": 4, "lsb": 4}, {"name": "SA", "msb": 3, "lsb": 3}, {"name": "C", "msb": 2, "lsb": 2}, {"name": "A", "msb": 1, "lsb": 1}, {"name": "M", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SCTLR_EL2", "fullname": "System Control Register (EL2)", "enc": [3, 4, 1, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "EPAN", "msb": 57, "lsb": 57}, {"name": "EnALS", "msb": 56, "lsb": 56}, {"name": "EnAS0", "msb": 55, "lsb": 55}, {"name": "EnASR", "msb": 54, "lsb": 54}, {"name": "TWEDEL", "msb": 49, "lsb": 46}, {"name": "TWEDEn", "msb": 45, "lsb": 45}, {"name": "DSSBS", "msb": 44, "lsb": 44}, {"name": "ATA", "msb": 43, "lsb": 43}, {"name": "ATA0", "msb": 42, "lsb": 42}, {"name": "TCF", "msb": 41, "lsb": 40}, {"name": "TCF0", "msb": 39, "lsb": 38}, {"name": "ITFSB", "msb": 37, "lsb": 37}, {"name": "BT", "msb": 36, "lsb": 36}, {"name": "BT0", "msb": 35, "lsb": 35}, {"name": "EnIA", "msb": 31, "lsb": 31}, {"name": "EnIB", "msb": 30, "lsb": 30}, {"name": "LSMAOE", "msb": 29, "lsb": 29}, {"name": "nTLSMD", "msb": 28, "lsb": 28}, {"name": "EnDA", "msb": 27, "lsb": 27}, {"name": "UCI", "msb": 26, "lsb": 26}, {"name": "EE", "msb": 25, "lsb": 25}, {"name": "E0E", "msb": 24, "lsb": 24}, {"name": "SPAN", "msb": 23, "lsb": 23}, {"name": "EIS", "msb": 22, "lsb": 22}, {"name": "IESB", "msb": 21, "lsb": 21}, {"name": "TSCXT", "msb": 20, "lsb": 20}, {"name": "WXN", "msb": 19, "lsb": 19}, {"name": "nTWE", "msb": 18, "lsb": 18}, {"name": "nTWI", "msb": 16, "lsb": 16}, {"name": "UCT", "msb": 15, "lsb": 15}, {"name": "DZE", "msb": 14, "lsb": 14}, {"name": "EnDB", "msb": 13, "lsb": 13}, {"name": "I", "msb": 12, "lsb": 12}, {"name": "EOS", "msb": 11, "lsb": 11}, {"name": "EnRCTX", "msb": 10, "lsb": 10}, {"name": "SED", "msb": 8, "lsb": 8}, {"name": "ITD", "msb": 7, "lsb": 7}, {"name": "nAA", "msb": 6, "lsb": 6}, {"name": "CP15BEN", "msb": 5, "lsb": 5}, {"name": "SA0", "msb": 4, "lsb": 4}, {"name": "SA", "msb": 3, "lsb": 3}, {"name": "C", "msb": 2, "lsb": 2}, {"name": "A", "msb": 1, "lsb": 1}, {"name": "M", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SCTLR_EL3", "fullname": "System Control Register (EL3)", "enc": [3, 6, 1, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "DSSBS", "msb": 44, "lsb": 44}, {"name": "ATA", "msb": 43, "lsb": 43}, {"name": "TCF", "msb": 41, "lsb": 40}, {"name": "ITFSB", "msb": 37, "lsb": 37}, {"name": "BT", "msb": 36, "lsb": 36}, {"name": "EnIA", "msb": 31, "lsb": 31}, {"name": "EnIB", "msb": 30, "lsb": 30}, {"name": "EnDA", "msb": 27, "lsb": 27}, {"name": "EE", "msb": 25, "lsb": 25}, {"name": "EIS", "msb": 22, "lsb": 22}, {"name": "IESB", "msb": 21, "lsb": 21}, {"name": "WXN", "msb": 19, "lsb": 19}, {"name": "EnDB", "msb": 13, "lsb": 13}, {"name": "I", "msb": 12, "lsb": 12}, {"name": "EOS", "msb": 11, "lsb": 11}, {"name": "nAA", "msb": 6, "lsb": 6}, {"name": "SA", "msb": 3, "lsb": 3}, {"name": "C", "msb": 2, "lsb": 2}, {"name": "A", "msb": 1, "lsb": 1}, {"name": "M", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SCXTNUM_EL0", "fullname": "EL0 Read/Write Software Context Number", "enc": [3, 3, 13, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "SCXTNUM_EL1", "fullname": "EL1 Read/Write Software Context Number", "enc": [3, 0, 13, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "SCXTNUM_EL12", "fullname": "EL1 Read/Write Software Context Number", "enc": [3, 5, 13, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "SCXTNUM_EL1", "fullname": "EL2 Read/Write Software Context Number", "enc": [3, 0, 13, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "SCXTNUM_EL2", "fullname": "EL2 Read/Write Software Context Number", "enc": [3, 4, 13, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "SCXTNUM_EL3", "fullname": "EL3 Read/Write Software Context Number", "enc": [3, 6, 13, 0, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "SDER32_EL2", "fullname": "AArch32 Secure Debug Enable Register", "enc": [3, 4, 1, 3, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "SUNIDEN", "msb": 1, "lsb": 1}, {"name": "SUIDEN", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SDER32_EL3", "fullname": "AArch32 Secure Debug Enable Register", "enc": [3, 6, 1, 1, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "SUNIDEN", "msb": 1, "lsb": 1}, {"name": "SUIDEN", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SP_EL0", "fullname": "Stack Pointer (EL0)", "enc": [3, 0, 4, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "SP_EL1", "fullname": "Stack Pointer (EL1)", "enc": [3, 4, 4, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "SP_EL2", "fullname": "Stack Pointer (EL2)", "enc": [3, 6, 4, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "SPSel", "fullname": "Stack Pointer Select", "enc": [3, 0, 4, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "SP", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SPSR_abt", "fullname": "Saved Program Status Register (Abort mode)", "enc": [3, 4, 4, 3, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "Q", "msb": 27, "lsb": 27}, {"name": "IT", "msb": 26, "lsb": 25}, {"name": "J", "msb": 24, "lsb": 24}, {"name": "SSBS", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "DIT", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "GE", "msb": 19, "lsb": 16}, {"name": "IT[7:2]", "msb": 15, "lsb": 10}, {"name": "E", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "T", "msb": 5, "lsb": 5}, {"name": "M[4:0]", "msb": 4, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SPSR_EL1", "fullname": "Saved Program Status Register (EL1)", "enc": [3, 0, 4, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "exception taken from AArch32", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "Q", "msb": 27, "lsb": 27}, {"name": "IT", "msb": 26, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "SSBS", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "GE", "msb": 19, "lsb": 16}, {"name": "IT[7:2]", "msb": 15, "lsb": 10}, {"name": "E", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "T", "msb": 5, "lsb": 5}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}, {"instance": "exception taken from AArch64", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "TCO", "msb": 25, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "UAO", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "SSBS", "msb": 12, "lsb": 12}, {"name": "BTYPE", "msb": 11, "lsb": 10}, {"name": "D", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SPSR_EL12", "fullname": "Saved Program Status Register (EL1)", "enc": [3, 5, 4, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "exception taken from AArch32", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "Q", "msb": 27, "lsb": 27}, {"name": "IT", "msb": 26, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "SSBS", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "GE", "msb": 19, "lsb": 16}, {"name": "IT[7:2]", "msb": 15, "lsb": 10}, {"name": "E", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "T", "msb": 5, "lsb": 5}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}, {"instance": "exception taken from AArch64", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "TCO", "msb": 25, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "UAO", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "SSBS", "msb": 12, "lsb": 12}, {"name": "BTYPE", "msb": 11, "lsb": 10}, {"name": "D", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SPSR_EL2", "fullname": "Saved Program Status Register (EL1)", "enc": [3, 4, 4, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "exception taken from AArch32", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "Q", "msb": 27, "lsb": 27}, {"name": "IT", "msb": 26, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "SSBS", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "GE", "msb": 19, "lsb": 16}, {"name": "IT[7:2]", "msb": 15, "lsb": 10}, {"name": "E", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "T", "msb": 5, "lsb": 5}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}, {"instance": "exception taken from AArch64", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "TCO", "msb": 25, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "UAO", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "SSBS", "msb": 12, "lsb": 12}, {"name": "BTYPE", "msb": 11, "lsb": 10}, {"name": "D", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SPSR_EL1", "fullname": "Saved Program Status Register (EL2)", "enc": [3, 0, 4, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "exception taken from AArch32", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "Q", "msb": 27, "lsb": 27}, {"name": "IT", "msb": 26, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "SSBS", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "GE", "msb": 19, "lsb": 16}, {"name": "IT[7:2]", "msb": 15, "lsb": 10}, {"name": "E", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "T", "msb": 5, "lsb": 5}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}, {"instance": "exception taken from AArch64", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "TCO", "msb": 25, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "UAO", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "SSBS", "msb": 12, "lsb": 12}, {"name": "BTYPE", "msb": 11, "lsb": 10}, {"name": "D", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SPSR_EL2", "fullname": "Saved Program Status Register (EL2)", "enc": [3, 4, 4, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "exception taken from AArch32", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "Q", "msb": 27, "lsb": 27}, {"name": "IT", "msb": 26, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "SSBS", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "GE", "msb": 19, "lsb": 16}, {"name": "IT[7:2]", "msb": 15, "lsb": 10}, {"name": "E", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "T", "msb": 5, "lsb": 5}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}, {"instance": "exception taken from AArch64", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "TCO", "msb": 25, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "UAO", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "SSBS", "msb": 12, "lsb": 12}, {"name": "BTYPE", "msb": 11, "lsb": 10}, {"name": "D", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SPSR_EL3", "fullname": "Saved Program Status Register (EL3)", "enc": [3, 6, 4, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "exception taken from AArch32", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "Q", "msb": 27, "lsb": 27}, {"name": "IT", "msb": 26, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "SSBS", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "GE", "msb": 19, "lsb": 16}, {"name": "IT[7:2]", "msb": 15, "lsb": 10}, {"name": "E", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "T", "msb": 5, "lsb": 5}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}, {"instance": "exception taken from AArch64", "fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "TCO", "msb": 25, "lsb": 25}, {"name": "DIT", "msb": 24, "lsb": 24}, {"name": "UAO", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "SS", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "SSBS", "msb": 12, "lsb": 12}, {"name": "BTYPE", "msb": 11, "lsb": 10}, {"name": "D", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "M[4]", "msb": 4, "lsb": 4}, {"name": "M[3:0]", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SPSR_fiq", "fullname": "Saved Program Status Register (FIQ mode)", "enc": [3, 4, 4, 3, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "Q", "msb": 27, "lsb": 27}, {"name": "IT", "msb": 26, "lsb": 25}, {"name": "J", "msb": 24, "lsb": 24}, {"name": "SSBS", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "DIT", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "GE", "msb": 19, "lsb": 16}, {"name": "IT[7:2]", "msb": 15, "lsb": 10}, {"name": "E", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "T", "msb": 5, "lsb": 5}, {"name": "M[4:0]", "msb": 4, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SPSR_irq", "fullname": "Saved Program Status Register (IRQ mode)", "enc": [3, 4, 4, 3, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "Q", "msb": 27, "lsb": 27}, {"name": "IT", "msb": 26, "lsb": 25}, {"name": "J", "msb": 24, "lsb": 24}, {"name": "SSBS", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "DIT", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "GE", "msb": 19, "lsb": 16}, {"name": "IT[7:2]", "msb": 15, "lsb": 10}, {"name": "E", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "T", "msb": 5, "lsb": 5}, {"name": "M[4:0]", "msb": 4, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SPSR_und", "fullname": "Saved Program Status Register (Undefined mode)", "enc": [3, 4, 4, 3, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "N", "msb": 31, "lsb": 31}, {"name": "Z", "msb": 30, "lsb": 30}, {"name": "C", "msb": 29, "lsb": 29}, {"name": "V", "msb": 28, "lsb": 28}, {"name": "Q", "msb": 27, "lsb": 27}, {"name": "IT", "msb": 26, "lsb": 25}, {"name": "J", "msb": 24, "lsb": 24}, {"name": "SSBS", "msb": 23, "lsb": 23}, {"name": "PAN", "msb": 22, "lsb": 22}, {"name": "DIT", "msb": 21, "lsb": 21}, {"name": "IL", "msb": 20, "lsb": 20}, {"name": "GE", "msb": 19, "lsb": 16}, {"name": "IT[7:2]", "msb": 15, "lsb": 10}, {"name": "E", "msb": 9, "lsb": 9}, {"name": "A", "msb": 8, "lsb": 8}, {"name": "I", "msb": 7, "lsb": 7}, {"name": "F", "msb": 6, "lsb": 6}, {"name": "T", "msb": 5, "lsb": 5}, {"name": "M[4:0]", "msb": 4, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "SSBS", "fullname": "Speculative Store Bypass Safe", "enc": [3, 3, 4, 2, 6], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "SSBS", "msb": 12, "lsb": 12}]}], "width": 64}, {"index": 0, "name": "TCO", "fullname": "Tag Check Override", "enc": [3, 3, 4, 2, 7], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TCO", "msb": 25, "lsb": 25}]}], "width": 64}, {"index": 0, "name": "TCR_EL1", "fullname": "Translation Control Register (EL1)", "enc": [3, 0, 2, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "DS", "msb": 59, "lsb": 59}, {"name": "TCMA1", "msb": 58, "lsb": 58}, {"name": "TCMA0", "msb": 57, "lsb": 57}, {"name": "E0PD1", "msb": 56, "lsb": 56}, {"name": "E0PD0", "msb": 55, "lsb": 55}, {"name": "NFD1", "msb": 54, "lsb": 54}, {"name": "NFD0", "msb": 53, "lsb": 53}, {"name": "TBID1", "msb": 52, "lsb": 52}, {"name": "TBID0", "msb": 51, "lsb": 51}, {"name": "HWU162", "msb": 50, "lsb": 50}, {"name": "HWU161", "msb": 49, "lsb": 49}, {"name": "HWU160", "msb": 48, "lsb": 48}, {"name": "HWU159", "msb": 47, "lsb": 47}, {"name": "HWU062", "msb": 46, "lsb": 46}, {"name": "HWU061", "msb": 45, "lsb": 45}, {"name": "HWU060", "msb": 44, "lsb": 44}, {"name": "HWU059", "msb": 43, "lsb": 43}, {"name": "HPD1", "msb": 42, "lsb": 42}, {"name": "HPD0", "msb": 41, "lsb": 41}, {"name": "HD", "msb": 40, "lsb": 40}, {"name": "HA", "msb": 39, "lsb": 39}, {"name": "TBI1", "msb": 38, "lsb": 38}, {"name": "TBI0", "msb": 37, "lsb": 37}, {"name": "AS", "msb": 36, "lsb": 36}, {"name": "IPS", "msb": 34, "lsb": 32}, {"name": "TG1", "msb": 31, "lsb": 30}, {"name": "SH1", "msb": 29, "lsb": 28}, {"name": "ORGN1", "msb": 27, "lsb": 26}, {"name": "IRGN1", "msb": 25, "lsb": 24}, {"name": "EPD1", "msb": 23, "lsb": 23}, {"name": "A1", "msb": 22, "lsb": 22}, {"name": "T1SZ", "msb": 21, "lsb": 16}, {"name": "TG0", "msb": 15, "lsb": 14}, {"name": "SH0", "msb": 13, "lsb": 12}, {"name": "ORGN0", "msb": 11, "lsb": 10}, {"name": "IRGN0", "msb": 9, "lsb": 8}, {"name": "EPD0", "msb": 7, "lsb": 7}, {"name": "T0SZ", "msb": 5, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TCR_EL12", "fullname": "Translation Control Register (EL1)", "enc": [3, 5, 2, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "DS", "msb": 59, "lsb": 59}, {"name": "TCMA1", "msb": 58, "lsb": 58}, {"name": "TCMA0", "msb": 57, "lsb": 57}, {"name": "E0PD1", "msb": 56, "lsb": 56}, {"name": "E0PD0", "msb": 55, "lsb": 55}, {"name": "NFD1", "msb": 54, "lsb": 54}, {"name": "NFD0", "msb": 53, "lsb": 53}, {"name": "TBID1", "msb": 52, "lsb": 52}, {"name": "TBID0", "msb": 51, "lsb": 51}, {"name": "HWU162", "msb": 50, "lsb": 50}, {"name": "HWU161", "msb": 49, "lsb": 49}, {"name": "HWU160", "msb": 48, "lsb": 48}, {"name": "HWU159", "msb": 47, "lsb": 47}, {"name": "HWU062", "msb": 46, "lsb": 46}, {"name": "HWU061", "msb": 45, "lsb": 45}, {"name": "HWU060", "msb": 44, "lsb": 44}, {"name": "HWU059", "msb": 43, "lsb": 43}, {"name": "HPD1", "msb": 42, "lsb": 42}, {"name": "HPD0", "msb": 41, "lsb": 41}, {"name": "HD", "msb": 40, "lsb": 40}, {"name": "HA", "msb": 39, "lsb": 39}, {"name": "TBI1", "msb": 38, "lsb": 38}, {"name": "TBI0", "msb": 37, "lsb": 37}, {"name": "AS", "msb": 36, "lsb": 36}, {"name": "IPS", "msb": 34, "lsb": 32}, {"name": "TG1", "msb": 31, "lsb": 30}, {"name": "SH1", "msb": 29, "lsb": 28}, {"name": "ORGN1", "msb": 27, "lsb": 26}, {"name": "IRGN1", "msb": 25, "lsb": 24}, {"name": "EPD1", "msb": 23, "lsb": 23}, {"name": "A1", "msb": 22, "lsb": 22}, {"name": "T1SZ", "msb": 21, "lsb": 16}, {"name": "TG0", "msb": 15, "lsb": 14}, {"name": "SH0", "msb": 13, "lsb": 12}, {"name": "ORGN0", "msb": 11, "lsb": 10}, {"name": "IRGN0", "msb": 9, "lsb": 8}, {"name": "EPD0", "msb": 7, "lsb": 7}, {"name": "T0SZ", "msb": 5, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TCR_EL1", "fullname": "Translation Control Register (EL2)", "enc": [3, 0, 2, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "HCR_EL2.E2H==0", "fields": [{"name": "DS", "msb": 32, "lsb": 32}, {"name": "TCMA", "msb": 30, "lsb": 30}, {"name": "TBID", "msb": 29, "lsb": 29}, {"name": "HWU62", "msb": 28, "lsb": 28}, {"name": "HWU61", "msb": 27, "lsb": 27}, {"name": "HWU60", "msb": 26, "lsb": 26}, {"name": "HWU59", "msb": 25, "lsb": 25}, {"name": "HPD", "msb": 24, "lsb": 24}, {"name": "HD", "msb": 22, "lsb": 22}, {"name": "HA", "msb": 21, "lsb": 21}, {"name": "TBI", "msb": 20, "lsb": 20}, {"name": "PS", "msb": 18, "lsb": 16}, {"name": "TG0", "msb": 15, "lsb": 14}, {"name": "SH0", "msb": 13, "lsb": 12}, {"name": "ORGN0", "msb": 11, "lsb": 10}, {"name": "IRGN0", "msb": 9, "lsb": 8}, {"name": "T0SZ", "msb": 5, "lsb": 0}]}, {"instance": "HCR_EL2.E2H==1", "fields": [{"name": "DS", "msb": 59, "lsb": 59}, {"name": "TCMA1", "msb": 58, "lsb": 58}, {"name": "TCMA0", "msb": 57, "lsb": 57}, {"name": "E0PD1", "msb": 56, "lsb": 56}, {"name": "E0PD0", "msb": 55, "lsb": 55}, {"name": "NFD1", "msb": 54, "lsb": 54}, {"name": "NFD0", "msb": 53, "lsb": 53}, {"name": "TBID1", "msb": 52, "lsb": 52}, {"name": "TBID0", "msb": 51, "lsb": 51}, {"name": "HWU162", "msb": 50, "lsb": 50}, {"name": "HWU161", "msb": 49, "lsb": 49}, {"name": "HWU160", "msb": 48, "lsb": 48}, {"name": "HWU159", "msb": 47, "lsb": 47}, {"name": "HWU062", "msb": 46, "lsb": 46}, {"name": "HWU061", "msb": 45, "lsb": 45}, {"name": "HWU060", "msb": 44, "lsb": 44}, {"name": "HWU059", "msb": 43, "lsb": 43}, {"name": "HPD1", "msb": 42, "lsb": 42}, {"name": "HPD0", "msb": 41, "lsb": 41}, {"name": "HD", "msb": 40, "lsb": 40}, {"name": "HA", "msb": 39, "lsb": 39}, {"name": "TBI1", "msb": 38, "lsb": 38}, {"name": "TBI0", "msb": 37, "lsb": 37}, {"name": "AS", "msb": 36, "lsb": 36}, {"name": "IPS", "msb": 34, "lsb": 32}, {"name": "TG1", "msb": 31, "lsb": 30}, {"name": "SH1", "msb": 29, "lsb": 28}, {"name": "ORGN1", "msb": 27, "lsb": 26}, {"name": "IRGN1", "msb": 25, "lsb": 24}, {"name": "EPD1", "msb": 23, "lsb": 23}, {"name": "A1", "msb": 22, "lsb": 22}, {"name": "T1SZ", "msb": 21, "lsb": 16}, {"name": "TG0", "msb": 15, "lsb": 14}, {"name": "SH0", "msb": 13, "lsb": 12}, {"name": "ORGN0", "msb": 11, "lsb": 10}, {"name": "IRGN0", "msb": 9, "lsb": 8}, {"name": "EPD0", "msb": 7, "lsb": 7}, {"name": "T0SZ", "msb": 5, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TCR_EL2", "fullname": "Translation Control Register (EL2)", "enc": [3, 4, 2, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "HCR_EL2.E2H==0", "fields": [{"name": "DS", "msb": 32, "lsb": 32}, {"name": "TCMA", "msb": 30, "lsb": 30}, {"name": "TBID", "msb": 29, "lsb": 29}, {"name": "HWU62", "msb": 28, "lsb": 28}, {"name": "HWU61", "msb": 27, "lsb": 27}, {"name": "HWU60", "msb": 26, "lsb": 26}, {"name": "HWU59", "msb": 25, "lsb": 25}, {"name": "HPD", "msb": 24, "lsb": 24}, {"name": "HD", "msb": 22, "lsb": 22}, {"name": "HA", "msb": 21, "lsb": 21}, {"name": "TBI", "msb": 20, "lsb": 20}, {"name": "PS", "msb": 18, "lsb": 16}, {"name": "TG0", "msb": 15, "lsb": 14}, {"name": "SH0", "msb": 13, "lsb": 12}, {"name": "ORGN0", "msb": 11, "lsb": 10}, {"name": "IRGN0", "msb": 9, "lsb": 8}, {"name": "T0SZ", "msb": 5, "lsb": 0}]}, {"instance": "HCR_EL2.E2H==1", "fields": [{"name": "DS", "msb": 59, "lsb": 59}, {"name": "TCMA1", "msb": 58, "lsb": 58}, {"name": "TCMA0", "msb": 57, "lsb": 57}, {"name": "E0PD1", "msb": 56, "lsb": 56}, {"name": "E0PD0", "msb": 55, "lsb": 55}, {"name": "NFD1", "msb": 54, "lsb": 54}, {"name": "NFD0", "msb": 53, "lsb": 53}, {"name": "TBID1", "msb": 52, "lsb": 52}, {"name": "TBID0", "msb": 51, "lsb": 51}, {"name": "HWU162", "msb": 50, "lsb": 50}, {"name": "HWU161", "msb": 49, "lsb": 49}, {"name": "HWU160", "msb": 48, "lsb": 48}, {"name": "HWU159", "msb": 47, "lsb": 47}, {"name": "HWU062", "msb": 46, "lsb": 46}, {"name": "HWU061", "msb": 45, "lsb": 45}, {"name": "HWU060", "msb": 44, "lsb": 44}, {"name": "HWU059", "msb": 43, "lsb": 43}, {"name": "HPD1", "msb": 42, "lsb": 42}, {"name": "HPD0", "msb": 41, "lsb": 41}, {"name": "HD", "msb": 40, "lsb": 40}, {"name": "HA", "msb": 39, "lsb": 39}, {"name": "TBI1", "msb": 38, "lsb": 38}, {"name": "TBI0", "msb": 37, "lsb": 37}, {"name": "AS", "msb": 36, "lsb": 36}, {"name": "IPS", "msb": 34, "lsb": 32}, {"name": "TG1", "msb": 31, "lsb": 30}, {"name": "SH1", "msb": 29, "lsb": 28}, {"name": "ORGN1", "msb": 27, "lsb": 26}, {"name": "IRGN1", "msb": 25, "lsb": 24}, {"name": "EPD1", "msb": 23, "lsb": 23}, {"name": "A1", "msb": 22, "lsb": 22}, {"name": "T1SZ", "msb": 21, "lsb": 16}, {"name": "TG0", "msb": 15, "lsb": 14}, {"name": "SH0", "msb": 13, "lsb": 12}, {"name": "ORGN0", "msb": 11, "lsb": 10}, {"name": "IRGN0", "msb": 9, "lsb": 8}, {"name": "EPD0", "msb": 7, "lsb": 7}, {"name": "T0SZ", "msb": 5, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TCR_EL3", "fullname": "Translation Control Register (EL3)", "enc": [3, 6, 2, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "DS", "msb": 32, "lsb": 32}, {"name": "TCMA", "msb": 30, "lsb": 30}, {"name": "TBID", "msb": 29, "lsb": 29}, {"name": "HWU62", "msb": 28, "lsb": 28}, {"name": "HWU61", "msb": 27, "lsb": 27}, {"name": "HWU60", "msb": 26, "lsb": 26}, {"name": "HWU59", "msb": 25, "lsb": 25}, {"name": "HPD", "msb": 24, "lsb": 24}, {"name": "HD", "msb": 22, "lsb": 22}, {"name": "HA", "msb": 21, "lsb": 21}, {"name": "TBI", "msb": 20, "lsb": 20}, {"name": "PS", "msb": 18, "lsb": 16}, {"name": "TG0", "msb": 15, "lsb": 14}, {"name": "SH0", "msb": 13, "lsb": 12}, {"name": "ORGN0", "msb": 11, "lsb": 10}, {"name": "IRGN0", "msb": 9, "lsb": 8}, {"name": "T0SZ", "msb": 5, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TFSRE0_EL1", "fullname": "Tag Fault Status Register (EL0).", "enc": [3, 0, 5, 6, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TF1", "msb": 1, "lsb": 1}, {"name": "TF0", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TFSR_EL1", "fullname": "Tag Fault Status Register (EL1)", "enc": [3, 0, 5, 6, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TF1", "msb": 1, "lsb": 1}, {"name": "TF0", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TFSR_EL12", "fullname": "Tag Fault Status Register (EL1)", "enc": [3, 5, 5, 6, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TF1", "msb": 1, "lsb": 1}, {"name": "TF0", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TFSR_EL2", "fullname": "Tag Fault Status Register (EL1)", "enc": [3, 4, 5, 6, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TF1", "msb": 1, "lsb": 1}, {"name": "TF0", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TFSR_EL1", "fullname": "Tag Fault Status Register (EL2)", "enc": [3, 0, 5, 6, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TF1", "msb": 1, "lsb": 1}, {"name": "TF0", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TFSR_EL2", "fullname": "Tag Fault Status Register (EL2)", "enc": [3, 4, 5, 6, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TF1", "msb": 1, "lsb": 1}, {"name": "TF0", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TFSR_EL3", "fullname": "Tag Fault Status Register (EL3)", "enc": [3, 6, 5, 6, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TF0", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ALLE1IS", "fullname": "TLB Invalidate All, EL1, Inner Shareable", "enc": [1, 4, 8, 3, 4], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE1ISNXS", "fullname": "TLB Invalidate All, EL1, Inner Shareable", "enc": [1, 4, 9, 3, 4], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE1OS", "fullname": "TLB Invalidate All, EL1, Outer Shareable", "enc": [1, 4, 8, 1, 4], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE1OSNXS", "fullname": "TLB Invalidate All, EL1, Outer Shareable", "enc": [1, 4, 9, 1, 4], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE1", "fullname": "TLB Invalidate All, EL1", "enc": [1, 4, 8, 7, 4], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE1NXS", "fullname": "TLB Invalidate All, EL1", "enc": [1, 4, 9, 7, 4], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE2IS", "fullname": "TLB Invalidate All, EL2, Inner Shareable", "enc": [1, 4, 8, 3, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE2ISNXS", "fullname": "TLB Invalidate All, EL2, Inner Shareable", "enc": [1, 4, 9, 3, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE2OS", "fullname": "TLB Invalidate All, EL2, Outer Shareable", "enc": [1, 4, 8, 1, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE2OSNXS", "fullname": "TLB Invalidate All, EL2, Outer Shareable", "enc": [1, 4, 9, 1, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE2", "fullname": "TLB Invalidate All, EL2", "enc": [1, 4, 8, 7, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE2NXS", "fullname": "TLB Invalidate All, EL2", "enc": [1, 4, 9, 7, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE3IS", "fullname": "TLB Invalidate All, EL3, Inner Shareable", "enc": [1, 6, 8, 3, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE3ISNXS", "fullname": "TLB Invalidate All, EL3, Inner Shareable", "enc": [1, 6, 9, 3, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE3OS", "fullname": "TLB Invalidate All, EL3, Outer Shareable", "enc": [1, 6, 8, 1, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE3OSNXS", "fullname": "TLB Invalidate All, EL3, Outer Shareable", "enc": [1, 6, 9, 1, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE3", "fullname": "TLB Invalidate All, EL3", "enc": [1, 6, 8, 7, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ALLE3NXS", "fullname": "TLB Invalidate All, EL3", "enc": [1, 6, 9, 7, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "ASIDE1IS", "fullname": "TLB Invalidate by ASID, EL1, Inner Shareable", "enc": [1, 0, 8, 3, 2], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}]}], "width": 64}, {"index": 0, "name": "ASIDE1ISNXS", "fullname": "TLB Invalidate by ASID, EL1, Inner Shareable", "enc": [1, 0, 9, 3, 2], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}]}], "width": 64}, {"index": 0, "name": "ASIDE1OS", "fullname": "TLB Invalidate by ASID, EL1, Outer Shareable", "enc": [1, 0, 8, 1, 2], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}]}], "width": 64}, {"index": 0, "name": "ASIDE1OSNXS", "fullname": "TLB Invalidate by ASID, EL1, Outer Shareable", "enc": [1, 0, 9, 1, 2], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}]}], "width": 64}, {"index": 0, "name": "ASIDE1", "fullname": "TLB Invalidate by ASID, EL1", "enc": [1, 0, 8, 7, 2], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}]}], "width": 64}, {"index": 0, "name": "ASIDE1NXS", "fullname": "TLB Invalidate by ASID, EL1", "enc": [1, 0, 9, 7, 2], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}]}], "width": 64}, {"index": 0, "name": "IPAS2E1IS", "fullname": "TLB Invalidate by Intermediate Physical Address, Stage 2, EL1, Inner Shareable", "enc": [1, 4, 8, 0, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "IPA[51:48]", "msb": 39, "lsb": 36}, {"name": "IPA[47:12]", "msb": 35, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "IPAS2E1ISNXS", "fullname": "TLB Invalidate by Intermediate Physical Address, Stage 2, EL1, Inner Shareable", "enc": [1, 4, 9, 0, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "IPA[51:48]", "msb": 39, "lsb": 36}, {"name": "IPA[47:12]", "msb": 35, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "IPAS2E1OS", "fullname": "TLB Invalidate by Intermediate Physical Address, Stage 2, EL1, Outer Shareable", "enc": [1, 4, 8, 4, 0], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "IPA[51:48]", "msb": 39, "lsb": 36}, {"name": "IPA[47:12]", "msb": 35, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "IPAS2E1OSNXS", "fullname": "TLB Invalidate by Intermediate Physical Address, Stage 2, EL1, Outer Shareable", "enc": [1, 4, 9, 4, 0], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "IPA[51:48]", "msb": 39, "lsb": 36}, {"name": "IPA[47:12]", "msb": 35, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "IPAS2E1", "fullname": "TLB Invalidate by Intermediate Physical Address, Stage 2, EL1", "enc": [1, 4, 8, 4, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "IPA[51:48]", "msb": 39, "lsb": 36}, {"name": "IPA[47:12]", "msb": 35, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "IPAS2E1NXS", "fullname": "TLB Invalidate by Intermediate Physical Address, Stage 2, EL1", "enc": [1, 4, 9, 4, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "IPA[51:48]", "msb": 39, "lsb": 36}, {"name": "IPA[47:12]", "msb": 35, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "IPAS2LE1IS", "fullname": "TLB Invalidate by Intermediate Physical Address, Stage 2, Last level, EL1, Inner Shareable", "enc": [1, 4, 8, 0, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "IPA[51:48]", "msb": 39, "lsb": 36}, {"name": "IPA[47:12]", "msb": 35, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "IPAS2LE1ISNXS", "fullname": "TLB Invalidate by Intermediate Physical Address, Stage 2, Last level, EL1, Inner Shareable", "enc": [1, 4, 9, 0, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "IPA[51:48]", "msb": 39, "lsb": 36}, {"name": "IPA[47:12]", "msb": 35, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "IPAS2LE1OS", "fullname": "TLB Invalidate by Intermediate Physical Address, Stage 2, Last level, EL1, Outer Shareable", "enc": [1, 4, 8, 4, 4], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "IPA[51:48]", "msb": 39, "lsb": 36}, {"name": "IPA[47:12]", "msb": 35, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "IPAS2LE1OSNXS", "fullname": "TLB Invalidate by Intermediate Physical Address, Stage 2, Last level, EL1, Outer Shareable", "enc": [1, 4, 9, 4, 4], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "IPA[51:48]", "msb": 39, "lsb": 36}, {"name": "IPA[47:12]", "msb": 35, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "IPAS2LE1", "fullname": "TLB Invalidate by Intermediate Physical Address, Stage 2, Last level, EL1", "enc": [1, 4, 8, 4, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "IPA[51:48]", "msb": 39, "lsb": 36}, {"name": "IPA[47:12]", "msb": 35, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "IPAS2LE1NXS", "fullname": "TLB Invalidate by Intermediate Physical Address, Stage 2, Last level, EL1", "enc": [1, 4, 9, 4, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "IPA[51:48]", "msb": 39, "lsb": 36}, {"name": "IPA[47:12]", "msb": 35, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RIPAS2E1IS", "fullname": "TLB Range Invalidate by Intermediate Physical Address, Stage 2, EL1, Inner Shareable", "enc": [1, 4, 8, 0, 2], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RIPAS2E1ISNXS", "fullname": "TLB Range Invalidate by Intermediate Physical Address, Stage 2, EL1, Inner Shareable", "enc": [1, 4, 9, 0, 2], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RIPAS2E1OS", "fullname": "TLB Range Invalidate by Intermediate Physical Address, Stage 2, EL1, Outer Shareable", "enc": [1, 4, 8, 4, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RIPAS2E1OSNXS", "fullname": "TLB Range Invalidate by Intermediate Physical Address, Stage 2, EL1, Outer Shareable", "enc": [1, 4, 9, 4, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RIPAS2E1", "fullname": "TLB Range Invalidate by Intermediate Physical Address, Stage 2, EL1", "enc": [1, 4, 8, 4, 2], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RIPAS2E1NXS", "fullname": "TLB Range Invalidate by Intermediate Physical Address, Stage 2, EL1", "enc": [1, 4, 9, 4, 2], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RIPAS2LE1IS", "fullname": "TLB Range Invalidate by Intermediate Physical Address, Stage 2, Last level, EL1, Inner Shareable", "enc": [1, 4, 8, 0, 6], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RIPAS2LE1ISNXS", "fullname": "TLB Range Invalidate by Intermediate Physical Address, Stage 2, Last level, EL1, Inner Shareable", "enc": [1, 4, 9, 0, 6], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RIPAS2LE1OS", "fullname": "TLB Range Invalidate by Intermediate Physical Address, Stage 2, Last level, EL1, Outer Shareable", "enc": [1, 4, 8, 4, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RIPAS2LE1OSNXS", "fullname": "TLB Range Invalidate by Intermediate Physical Address, Stage 2, Last level, EL1, Outer Shareable", "enc": [1, 4, 9, 4, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RIPAS2LE1", "fullname": "TLB Range Invalidate by Intermediate Physical Address, Stage 2, Last level, EL1", "enc": [1, 4, 8, 4, 6], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RIPAS2LE1NXS", "fullname": "TLB Range Invalidate by Intermediate Physical Address, Stage 2, Last level, EL1", "enc": [1, 4, 9, 4, 6], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "NS", "msb": 63, "lsb": 63}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAAE1IS", "fullname": "TLB Range Invalidate by VA, All ASID, EL1, Inner Shareable", "enc": [1, 0, 8, 2, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAAE1ISNXS", "fullname": "TLB Range Invalidate by VA, All ASID, EL1, Inner Shareable", "enc": [1, 0, 9, 2, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAAE1OS", "fullname": "TLB Range Invalidate by VA, All ASID, EL1, Outer Shareable", "enc": [1, 0, 8, 5, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAAE1OSNXS", "fullname": "TLB Range Invalidate by VA, All ASID, EL1, Outer Shareable", "enc": [1, 0, 9, 5, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAAE1", "fullname": "TLB Range Invalidate by VA, All ASID, EL1", "enc": [1, 0, 8, 6, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAAE1NXS", "fullname": "TLB Range Invalidate by VA, All ASID, EL1", "enc": [1, 0, 9, 6, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAALE1IS", "fullname": "TLB Range Invalidate by VA, All ASID, Last Level, EL1, Inner Shareable", "enc": [1, 0, 8, 2, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAALE1ISNXS", "fullname": "TLB Range Invalidate by VA, All ASID, Last Level, EL1, Inner Shareable", "enc": [1, 0, 9, 2, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAALE1OS", "fullname": "TLB Range Invalidate by VA, All ASID, Last Level, EL1, Outer Shareable", "enc": [1, 0, 8, 5, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAALE1OSNXS", "fullname": "TLB Range Invalidate by VA, All ASID, Last Level, EL1, Outer Shareable", "enc": [1, 0, 9, 5, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAALE1", "fullname": "TLB Range Invalidate by VA, All ASID, Last level, EL1", "enc": [1, 0, 8, 6, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAALE1NXS", "fullname": "TLB Range Invalidate by VA, All ASID, Last level, EL1", "enc": [1, 0, 9, 6, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE1IS", "fullname": "TLB Range Invalidate by VA, EL1, Inner Shareable", "enc": [1, 0, 8, 2, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE1ISNXS", "fullname": "TLB Range Invalidate by VA, EL1, Inner Shareable", "enc": [1, 0, 9, 2, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE1OS", "fullname": "TLB Range Invalidate by VA, EL1, Outer Shareable", "enc": [1, 0, 8, 5, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE1OSNXS", "fullname": "TLB Range Invalidate by VA, EL1, Outer Shareable", "enc": [1, 0, 9, 5, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE1", "fullname": "TLB Range Invalidate by VA, EL1", "enc": [1, 0, 8, 6, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE1NXS", "fullname": "TLB Range Invalidate by VA, EL1", "enc": [1, 0, 9, 6, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE2IS", "fullname": "TLB Range Invalidate by VA, EL2, Inner Shareable", "enc": [1, 4, 8, 2, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE2ISNXS", "fullname": "TLB Range Invalidate by VA, EL2, Inner Shareable", "enc": [1, 4, 9, 2, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE2OS", "fullname": "TLB Range Invalidate by VA, EL2, Outer Shareable", "enc": [1, 4, 8, 5, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE2OSNXS", "fullname": "TLB Range Invalidate by VA, EL2, Outer Shareable", "enc": [1, 4, 9, 5, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE2", "fullname": "TLB Range Invalidate by VA, EL2", "enc": [1, 4, 8, 6, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE2NXS", "fullname": "TLB Range Invalidate by VA, EL2", "enc": [1, 4, 9, 6, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE3IS", "fullname": "TLB Range Invalidate by VA, EL3, Inner Shareable", "enc": [1, 6, 8, 2, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE3ISNXS", "fullname": "TLB Range Invalidate by VA, EL3, Inner Shareable", "enc": [1, 6, 9, 2, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE3OS", "fullname": "TLB Range Invalidate by VA, EL3, Outer Shareable", "enc": [1, 6, 8, 5, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE3OSNXS", "fullname": "TLB Range Invalidate by VA, EL3, Outer Shareable", "enc": [1, 6, 9, 5, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE3", "fullname": "TLB Range Invalidate by VA, EL3", "enc": [1, 6, 8, 6, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVAE3NXS", "fullname": "TLB Range Invalidate by VA, EL3", "enc": [1, 6, 9, 6, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE1IS", "fullname": "TLB Range Invalidate by VA, Last level, EL1, Inner Shareable", "enc": [1, 0, 8, 2, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE1ISNXS", "fullname": "TLB Range Invalidate by VA, Last level, EL1, Inner Shareable", "enc": [1, 0, 9, 2, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE1OS", "fullname": "TLB Range Invalidate by VA, Last level, EL1, Outer Shareable", "enc": [1, 0, 8, 5, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE1OSNXS", "fullname": "TLB Range Invalidate by VA, Last level, EL1, Outer Shareable", "enc": [1, 0, 9, 5, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE1", "fullname": "TLB Range Invalidate by VA, Last level, EL1", "enc": [1, 0, 8, 6, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE1NXS", "fullname": "TLB Range Invalidate by VA, Last level, EL1", "enc": [1, 0, 9, 6, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE2IS", "fullname": "TLB Range Invalidate by VA, Last level, EL2, Inner Shareable", "enc": [1, 4, 8, 2, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE2ISNXS", "fullname": "TLB Range Invalidate by VA, Last level, EL2, Inner Shareable", "enc": [1, 4, 9, 2, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE2OS", "fullname": "TLB Range Invalidate by VA, Last level, EL2, Outer Shareable", "enc": [1, 4, 8, 5, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE2OSNXS", "fullname": "TLB Range Invalidate by VA, Last level, EL2, Outer Shareable", "enc": [1, 4, 9, 5, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE2", "fullname": "TLB Range Invalidate by VA, Last level, EL2", "enc": [1, 4, 8, 6, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE2NXS", "fullname": "TLB Range Invalidate by VA, Last level, EL2", "enc": [1, 4, 9, 6, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE3IS", "fullname": "TLB Range Invalidate by VA, Last level, EL3, Inner Shareable", "enc": [1, 6, 8, 2, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE3ISNXS", "fullname": "TLB Range Invalidate by VA, Last level, EL3, Inner Shareable", "enc": [1, 6, 9, 2, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE3OS", "fullname": "TLB Range Invalidate by VA, Last level, EL3, Outer Shareable", "enc": [1, 6, 8, 5, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE3OSNXS", "fullname": "TLB Range Invalidate by VA, Last level, EL3, Outer Shareable", "enc": [1, 6, 9, 5, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE3", "fullname": "TLB Range Invalidate by VA, Last level, EL3", "enc": [1, 6, 8, 6, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "RVALE3NXS", "fullname": "TLB Range Invalidate by VA, Last level, EL3", "enc": [1, 6, 9, 6, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TG", "msb": 47, "lsb": 46}, {"name": "SCALE", "msb": 45, "lsb": 44}, {"name": "NUM", "msb": 43, "lsb": 39}, {"name": "TTL", "msb": 38, "lsb": 37}, {"name": "BaseADDR", "msb": 36, "lsb": 0}, {"name": "BaseADDR", "msb": 36, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAAE1IS", "fullname": "TLB Invalidate by VA, All ASID, EL1, Inner Shareable", "enc": [1, 0, 8, 3, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAAE1ISNXS", "fullname": "TLB Invalidate by VA, All ASID, EL1, Inner Shareable", "enc": [1, 0, 9, 3, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAAE1OS", "fullname": "TLB Invalidate by VA, All ASID, EL1, Outer Shareable", "enc": [1, 0, 8, 1, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAAE1OSNXS", "fullname": "TLB Invalidate by VA, All ASID, EL1, Outer Shareable", "enc": [1, 0, 9, 1, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAAE1", "fullname": "TLB Invalidate by VA, All ASID, EL1", "enc": [1, 0, 8, 7, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAAE1NXS", "fullname": "TLB Invalidate by VA, All ASID, EL1", "enc": [1, 0, 9, 7, 3], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAALE1IS", "fullname": "TLB Invalidate by VA, All ASID, Last Level, EL1, Inner Shareable", "enc": [1, 0, 8, 3, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAALE1ISNXS", "fullname": "TLB Invalidate by VA, All ASID, Last Level, EL1, Inner Shareable", "enc": [1, 0, 9, 3, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAALE1OS", "fullname": "TLB Invalidate by VA, All ASID, Last Level, EL1, Outer Shareable", "enc": [1, 0, 8, 1, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAALE1OSNXS", "fullname": "TLB Invalidate by VA, All ASID, Last Level, EL1, Outer Shareable", "enc": [1, 0, 9, 1, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAALE1", "fullname": "TLB Invalidate by VA, All ASID, Last level, EL1", "enc": [1, 0, 8, 7, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAALE1NXS", "fullname": "TLB Invalidate by VA, All ASID, Last level, EL1", "enc": [1, 0, 9, 7, 7], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE1IS", "fullname": "TLB Invalidate by VA, EL1, Inner Shareable", "enc": [1, 0, 8, 3, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE1ISNXS", "fullname": "TLB Invalidate by VA, EL1, Inner Shareable", "enc": [1, 0, 9, 3, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE1OS", "fullname": "TLB Invalidate by VA, EL1, Outer Shareable", "enc": [1, 0, 8, 1, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE1OSNXS", "fullname": "TLB Invalidate by VA, EL1, Outer Shareable", "enc": [1, 0, 9, 1, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE1", "fullname": "TLB Invalidate by VA, EL1", "enc": [1, 0, 8, 7, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE1NXS", "fullname": "TLB Invalidate by VA, EL1", "enc": [1, 0, 9, 7, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE2IS", "fullname": "TLB Invalidate by VA, EL2, Inner Shareable", "enc": [1, 4, 8, 3, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE2ISNXS", "fullname": "TLB Invalidate by VA, EL2, Inner Shareable", "enc": [1, 4, 9, 3, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE2OS", "fullname": "TLB Invalidate by VA, EL2, Outer Shareable", "enc": [1, 4, 8, 1, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE2OSNXS", "fullname": "TLB Invalidate by VA, EL2, Outer Shareable", "enc": [1, 4, 9, 1, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE2", "fullname": "TLB Invalidate by VA, EL2", "enc": [1, 4, 8, 7, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE2NXS", "fullname": "TLB Invalidate by VA, EL2", "enc": [1, 4, 9, 7, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE3IS", "fullname": "TLB Invalidate by VA, EL3, Inner Shareable", "enc": [1, 6, 8, 3, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE3ISNXS", "fullname": "TLB Invalidate by VA, EL3, Inner Shareable", "enc": [1, 6, 9, 3, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE3OS", "fullname": "TLB Invalidate by VA, EL3, Outer Shareable", "enc": [1, 6, 8, 1, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE3OSNXS", "fullname": "TLB Invalidate by VA, EL3, Outer Shareable", "enc": [1, 6, 9, 1, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE3", "fullname": "TLB Invalidate by VA, EL3", "enc": [1, 6, 8, 7, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VAE3NXS", "fullname": "TLB Invalidate by VA, EL3", "enc": [1, 6, 9, 7, 1], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE1IS", "fullname": "TLB Invalidate by VA, Last level, EL1, Inner Shareable", "enc": [1, 0, 8, 3, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE1ISNXS", "fullname": "TLB Invalidate by VA, Last level, EL1, Inner Shareable", "enc": [1, 0, 9, 3, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE1OS", "fullname": "TLB Invalidate by VA, Last level, EL1, Outer Shareable", "enc": [1, 0, 8, 1, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE1OSNXS", "fullname": "TLB Invalidate by VA, Last level, EL1, Outer Shareable", "enc": [1, 0, 9, 1, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE1", "fullname": "TLB Invalidate by VA, Last level, EL1", "enc": [1, 0, 8, 7, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE1NXS", "fullname": "TLB Invalidate by VA, Last level, EL1", "enc": [1, 0, 9, 7, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE2IS", "fullname": "TLB Invalidate by VA, Last level, EL2, Inner Shareable", "enc": [1, 4, 8, 3, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE2ISNXS", "fullname": "TLB Invalidate by VA, Last level, EL2, Inner Shareable", "enc": [1, 4, 9, 3, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE2OS", "fullname": "TLB Invalidate by VA, Last level, EL2, Outer Shareable", "enc": [1, 4, 8, 1, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE2OSNXS", "fullname": "TLB Invalidate by VA, Last level, EL2, Outer Shareable", "enc": [1, 4, 9, 1, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE2", "fullname": "TLB Invalidate by VA, Last level, EL2", "enc": [1, 4, 8, 7, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE2NXS", "fullname": "TLB Invalidate by VA, Last level, EL2", "enc": [1, 4, 9, 7, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE3IS", "fullname": "TLB Invalidate by VA, Last level, EL3, Inner Shareable", "enc": [1, 6, 8, 3, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE3ISNXS", "fullname": "TLB Invalidate by VA, Last level, EL3, Inner Shareable", "enc": [1, 6, 9, 3, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE3OS", "fullname": "TLB Invalidate by VA, Last level, EL3, Outer Shareable", "enc": [1, 6, 8, 1, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE3OSNXS", "fullname": "TLB Invalidate by VA, Last level, EL3, Outer Shareable", "enc": [1, 6, 9, 1, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE3", "fullname": "TLB Invalidate by VA, Last level, EL3", "enc": [1, 6, 8, 7, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VALE3NXS", "fullname": "TLB Invalidate by VA, Last level, EL3", "enc": [1, 6, 9, 7, 5], "accessors": ["TLBI"], "fieldsets": [{"fields": [{"name": "TTL", "msb": 47, "lsb": 44}, {"name": "VA[55:12]", "msb": 43, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VMALLE1IS", "fullname": "TLB Invalidate by VMID, All at stage 1, EL1, Inner Shareable", "enc": [1, 0, 8, 3, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "VMALLE1ISNXS", "fullname": "TLB Invalidate by VMID, All at stage 1, EL1, Inner Shareable", "enc": [1, 0, 9, 3, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "VMALLE1OS", "fullname": "TLB Invalidate by VMID, All at stage 1, EL1, Outer Shareable", "enc": [1, 0, 8, 1, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "VMALLE1OSNXS", "fullname": "TLB Invalidate by VMID, All at stage 1, EL1, Outer Shareable", "enc": [1, 0, 9, 1, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "VMALLE1", "fullname": "TLB Invalidate by VMID, All at stage 1, EL1", "enc": [1, 0, 8, 7, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "VMALLE1NXS", "fullname": "TLB Invalidate by VMID, All at stage 1, EL1", "enc": [1, 0, 9, 7, 0], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "VMALLS12E1IS", "fullname": "TLB Invalidate by VMID, All at Stage 1 and 2, EL1, Inner Shareable", "enc": [1, 4, 8, 3, 6], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "VMALLS12E1ISNXS", "fullname": "TLB Invalidate by VMID, All at Stage 1 and 2, EL1, Inner Shareable", "enc": [1, 4, 9, 3, 6], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "VMALLS12E1OS", "fullname": "TLB Invalidate by VMID, All at Stage 1 and 2, EL1, Outer Shareable", "enc": [1, 4, 8, 1, 6], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "VMALLS12E1OSNXS", "fullname": "TLB Invalidate by VMID, All at Stage 1 and 2, EL1, Outer Shareable", "enc": [1, 4, 9, 1, 6], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "VMALLS12E1", "fullname": "TLB Invalidate by VMID, All at Stage 1 and 2, EL1", "enc": [1, 4, 8, 7, 6], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "VMALLS12E1NXS", "fullname": "TLB Invalidate by VMID, All at Stage 1 and 2, EL1", "enc": [1, 4, 9, 7, 6], "accessors": ["TLBI"], "fieldsets": []}, {"index": 0, "name": "TPIDR_EL0", "fullname": "EL0 Read/Write Software Thread ID Register", "enc": [3, 3, 13, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "TPIDR_EL1", "fullname": "EL1 Software Thread ID Register", "enc": [3, 0, 13, 0, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "TPIDR_EL2", "fullname": "EL2 Software Thread ID Register", "enc": [3, 4, 13, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "TPIDR_EL3", "fullname": "EL3 Software Thread ID Register", "enc": [3, 6, 13, 0, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "TPIDRRO_EL0", "fullname": "EL0 Read-Only Software Thread ID Register", "enc": [3, 3, 13, 0, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "TRFCR_EL1", "fullname": "Trace Filter Control Register (EL1)", "enc": [3, 0, 1, 2, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TS", "msb": 6, "lsb": 5}, {"name": "E1TRE", "msb": 1, "lsb": 1}, {"name": "E0TRE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TRFCR_EL12", "fullname": "Trace Filter Control Register (EL1)", "enc": [3, 5, 1, 2, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TS", "msb": 6, "lsb": 5}, {"name": "E1TRE", "msb": 1, "lsb": 1}, {"name": "E0TRE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TRFCR_EL1", "fullname": "Trace Filter Control Register (EL2)", "enc": [3, 0, 1, 2, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TS", "msb": 6, "lsb": 5}, {"name": "CX", "msb": 3, "lsb": 3}, {"name": "E2TRE", "msb": 1, "lsb": 1}, {"name": "E0HTRE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TRFCR_EL2", "fullname": "Trace Filter Control Register (EL2)", "enc": [3, 4, 1, 2, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "TS", "msb": 6, "lsb": 5}, {"name": "CX", "msb": 3, "lsb": 3}, {"name": "E2TRE", "msb": 1, "lsb": 1}, {"name": "E0HTRE", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TTBR0_EL1", "fullname": "Translation Table Base Register 0 (EL1)", "enc": [3, 0, 2, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "BADDR[47:1]", "msb": 47, "lsb": 1}, {"name": "CnP", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TTBR0_EL12", "fullname": "Translation Table Base Register 0 (EL1)", "enc": [3, 5, 2, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "BADDR[47:1]", "msb": 47, "lsb": 1}, {"name": "CnP", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TTBR0_EL1", "fullname": "Translation Table Base Register 0 (EL2)", "enc": [3, 0, 2, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "BADDR[47:1]", "msb": 47, "lsb": 1}, {"name": "CnP", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TTBR0_EL2", "fullname": "Translation Table Base Register 0 (EL2)", "enc": [3, 4, 2, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "BADDR[47:1]", "msb": 47, "lsb": 1}, {"name": "CnP", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TTBR0_EL3", "fullname": "Translation Table Base Register 0 (EL3)", "enc": [3, 6, 2, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BADDR[47:1]", "msb": 47, "lsb": 1}, {"name": "CnP", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TTBR1_EL1", "fullname": "Translation Table Base Register 1 (EL1)", "enc": [3, 0, 2, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "BADDR[47:1]", "msb": 47, "lsb": 1}, {"name": "CnP", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TTBR1_EL12", "fullname": "Translation Table Base Register 1 (EL1)", "enc": [3, 5, 2, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "BADDR[47:1]", "msb": 47, "lsb": 1}, {"name": "CnP", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TTBR1_EL1", "fullname": "Translation Table Base Register 1 (EL2)", "enc": [3, 0, 2, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "BADDR[47:1]", "msb": 47, "lsb": 1}, {"name": "CnP", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "TTBR1_EL2", "fullname": "Translation Table Base Register 1 (EL2)", "enc": [3, 4, 2, 0, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "ASID", "msb": 63, "lsb": 48}, {"name": "BADDR[47:1]", "msb": 47, "lsb": 1}, {"name": "CnP", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "UAO", "fullname": "User Access Override", "enc": [3, 0, 4, 2, 4], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "UAO", "msb": 23, "lsb": 23}]}], "width": 64}, {"index": 0, "name": "VBAR_EL1", "fullname": "Vector Base Address Register (EL1)", "enc": [3, 0, 12, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "VBAR_EL12", "fullname": "Vector Base Address Register (EL1)", "enc": [3, 5, 12, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "VBAR_EL1", "fullname": "Vector Base Address Register (EL2)", "enc": [3, 0, 12, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "VBAR_EL2", "fullname": "Vector Base Address Register (EL2)", "enc": [3, 4, 12, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "VBAR_EL3", "fullname": "Vector Base Address Register (EL3)", "enc": [3, 6, 12, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": []}], "width": 64}, {"index": 0, "name": "DISR_EL1", "fullname": "Virtual Deferred Interrupt Status Register", "enc": [3, 0, 12, 1, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "A", "msb": 31, "lsb": 31}, {"name": "IDS", "msb": 24, "lsb": 24}, {"name": "ISS", "msb": 23, "lsb": 0}]}, {"fields": [{"name": "A", "msb": 31, "lsb": 31}, {"name": "AET", "msb": 15, "lsb": 14}, {"name": "ExT", "msb": 12, "lsb": 12}, {"name": "FS", "msb": 10, "lsb": 10}, {"name": "LPAE", "msb": 9, "lsb": 9}, {"name": "FS[3:0]", "msb": 3, "lsb": 0}]}, {"fields": [{"name": "A", "msb": 31, "lsb": 31}, {"name": "AET", "msb": 15, "lsb": 14}, {"name": "ExT", "msb": 12, "lsb": 12}, {"name": "LPAE", "msb": 9, "lsb": 9}, {"name": "STATUS", "msb": 5, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VDISR_EL2", "fullname": "Virtual Deferred Interrupt Status Register", "enc": [3, 4, 12, 1, 1], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "A", "msb": 31, "lsb": 31}, {"name": "IDS", "msb": 24, "lsb": 24}, {"name": "ISS", "msb": 23, "lsb": 0}]}, {"fields": [{"name": "A", "msb": 31, "lsb": 31}, {"name": "AET", "msb": 15, "lsb": 14}, {"name": "ExT", "msb": 12, "lsb": 12}, {"name": "FS", "msb": 10, "lsb": 10}, {"name": "LPAE", "msb": 9, "lsb": 9}, {"name": "FS[3:0]", "msb": 3, "lsb": 0}]}, {"fields": [{"name": "A", "msb": 31, "lsb": 31}, {"name": "AET", "msb": 15, "lsb": 14}, {"name": "ExT", "msb": 12, "lsb": 12}, {"name": "LPAE", "msb": 9, "lsb": 9}, {"name": "STATUS", "msb": 5, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "MPIDR_EL1", "fullname": "Virtualization Multiprocessor ID Register", "enc": [3, 0, 0, 0, 5], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Aff3", "msb": 39, "lsb": 32}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "MT", "msb": 24, "lsb": 24}, {"name": "Aff2", "msb": 23, "lsb": 16}, {"name": "Aff1", "msb": 15, "lsb": 8}, {"name": "Aff0", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VMPIDR_EL2", "fullname": "Virtualization Multiprocessor ID Register", "enc": [3, 4, 0, 0, 5], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "Aff3", "msb": 39, "lsb": 32}, {"name": "U", "msb": 30, "lsb": 30}, {"name": "MT", "msb": 24, "lsb": 24}, {"name": "Aff2", "msb": 23, "lsb": 16}, {"name": "Aff1", "msb": 15, "lsb": 8}, {"name": "Aff0", "msb": 7, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VNCR_EL2", "fullname": "Virtual Nested Control Register", "enc": [3, 4, 2, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "RESS", "msb": 63, "lsb": 53}, {"name": "BADDR", "msb": 52, "lsb": 12}]}], "width": 64}, {"index": 0, "name": "MIDR_EL1", "fullname": "Virtualization Processor ID Register", "enc": [3, 0, 0, 0, 0], "accessors": ["MRS"], "fieldsets": [{"fields": [{"name": "Implementer", "msb": 31, "lsb": 24}, {"name": "Variant", "msb": 23, "lsb": 20}, {"name": "Architecture", "msb": 19, "lsb": 16}, {"name": "PartNum", "msb": 15, "lsb": 4}, {"name": "Revision", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VPIDR_EL2", "fullname": "Virtualization Processor ID Register", "enc": [3, 4, 0, 0, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "Implementer", "msb": 31, "lsb": 24}, {"name": "Variant", "msb": 23, "lsb": 20}, {"name": "Architecture", "msb": 19, "lsb": 16}, {"name": "PartNum", "msb": 15, "lsb": 4}, {"name": "Revision", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VSESR_EL2", "fullname": "Virtual SError Exception Syndrome Register", "enc": [3, 4, 5, 2, 3], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "AET", "msb": 15, "lsb": 14}, {"name": "ExT", "msb": 12, "lsb": 12}]}, {"fields": [{"name": "IDS", "msb": 24, "lsb": 24}, {"name": "ISS", "msb": 23, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VSTCR_EL2", "fullname": "Virtualization Secure Translation Control Register", "enc": [3, 4, 2, 6, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "Profile(A)", "fields": [{"name": "SL2", "msb": 33, "lsb": 33}, {"name": "SA", "msb": 30, "lsb": 30}, {"name": "SW", "msb": 29, "lsb": 29}, {"name": "TG0", "msb": 15, "lsb": 14}, {"name": "SL0", "msb": 7, "lsb": 6}, {"name": "SL0", "msb": 7, "lsb": 6}, {"name": "T0SZ", "msb": 5, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VSTTBR_EL2", "fullname": "Virtualization Secure Translation Table Base Register", "enc": [3, 4, 2, 6, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "BADDR", "msb": 47, "lsb": 1}, {"name": "CnP", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VTCR_EL2", "fullname": "Virtualization Translation Control Register", "enc": [3, 4, 2, 1, 2], "accessors": ["MRS", "MSR"], "fieldsets": [{"instance": "Profile(A)", "fields": [{"name": "SL2", "msb": 33, "lsb": 33}, {"name": "DS", "msb": 32, "lsb": 32}, {"name": "NSA", "msb": 30, "lsb": 30}, {"name": "NSW", "msb": 29, "lsb": 29}, {"name": "HWU62", "msb": 28, "lsb": 28}, {"name": "HWU61", "msb": 27, "lsb": 27}, {"name": "HWU60", "msb": 26, "lsb": 26}, {"name": "HWU59", "msb": 25, "lsb": 25}, {"name": "HD", "msb": 22, "lsb": 22}, {"name": "HA", "msb": 21, "lsb": 21}, {"name": "VS", "msb": 19, "lsb": 19}, {"name": "PS", "msb": 18, "lsb": 16}, {"name": "TG0", "msb": 15, "lsb": 14}, {"name": "SH0", "msb": 13, "lsb": 12}, {"name": "ORGN0", "msb": 11, "lsb": 10}, {"name": "IRGN0", "msb": 9, "lsb": 8}, {"name": "SL0", "msb": 7, "lsb": 6}, {"name": "SL0", "msb": 7, "lsb": 6}, {"name": "T0SZ", "msb": 5, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "VTTBR_EL2", "fullname": "Virtualization Translation Table Base Register", "enc": [3, 4, 2, 1, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "VMID", "msb": 63, "lsb": 48}, {"name": "BADDR", "msb": 47, "lsb": 1}, {"name": "CnP", "msb": 0, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ZCR_EL1", "fullname": "SVE Control Register (EL1)", "enc": [3, 0, 1, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "LEN", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ZCR_EL12", "fullname": "SVE Control Register (EL1)", "enc": [3, 5, 1, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "LEN", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ZCR_EL1", "fullname": "SVE Control Register (EL2)", "enc": [3, 0, 1, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "LEN", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ZCR_EL2", "fullname": "SVE Control Register (EL2)", "enc": [3, 4, 1, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "LEN", "msb": 3, "lsb": 0}]}], "width": 64}, {"index": 0, "name": "ZCR_EL3", "fullname": "SVE Control Register (EL3)", "enc": [3, 6, 1, 2, 0], "accessors": ["MRS", "MSR"], "fieldsets": [{"fields": [{"name": "LEN", "msb": 3, "lsb": 0}]}], "width": 64}] \ No newline at end of file
diff --git a/tools/tools/gen_reg_class.py b/tools/tools/gen_reg_class.py
new file mode 100644
index 0000000..980708b
--- /dev/null
+++ b/tools/tools/gen_reg_class.py
@@ -0,0 +1,33 @@
+import json, sys
+import argparse
+
+parser = argparse.ArgumentParser()
+parser.add_argument("regfile")
+args = parser.parse_args()
+
+data = json.load(open(args.regfile))
+for reg in data:
+ name = reg['name']
+
+ if name[-4:-1] == "_EL":
+ name = name[:-4]
+
+ if not reg.get("fieldsets", []):
+ continue
+
+ print(f"# {reg['name']}")
+ print(f"class {name}(Register64):")
+
+ for fieldset in reg.get("fieldsets", []):
+ if "instance" in fieldset:
+ print(f"# {fieldset['instance']}")
+ for f in fieldset["fields"]:
+ fname = f["name"]
+ msb, lsb = f["msb"], f["lsb"]
+
+ if msb == lsb:
+ print(f" {fname} = {lsb}")
+ else:
+ print(f" {fname} = {msb}, {lsb}")
+
+ print()
diff --git a/tools/tools/gen_reg_include.py b/tools/tools/gen_reg_include.py
new file mode 100644
index 0000000..bc94001
--- /dev/null
+++ b/tools/tools/gen_reg_include.py
@@ -0,0 +1,35 @@
+import json, sys
+import argparse
+
+parser = argparse.ArgumentParser()
+parser.add_argument("--imp-apl-prefix", action="store_true")
+parser.add_argument("regfile")
+args = parser.parse_args()
+
+if args.imp_apl_prefix:
+ prefix = "IMP_APL_"
+else:
+ prefix = ""
+
+data = json.load(open(args.regfile))
+for reg in data:
+ name = reg['name']
+
+ print(f"#define SYS_{prefix}{name} sys_reg({', '.join(str(i) for i in reg['enc'])})")
+
+ if name[-4:-1] == "_EL":
+ name = name[:-4]
+
+ for fieldset in reg.get("fieldsets", []):
+ if "instance" in fieldset:
+ print(f"// {fieldset['instance']}")
+ for f in fieldset["fields"]:
+ fname = f["name"]
+ msb, lsb = f["msb"], f["lsb"]
+
+ if msb == lsb:
+ print(f"#define {name}_{fname} BIT({lsb})")
+ else:
+ print(f"#define {name}_{fname} GENMASK({msb}, {lsb})")
+
+ print()
diff --git a/tools/tools/reg2json.py b/tools/tools/reg2json.py
new file mode 100644
index 0000000..d58c708
--- /dev/null
+++ b/tools/tools/reg2json.py
@@ -0,0 +1,137 @@
+import sys, re, json
+from xml.etree import ElementTree
+
+def insert_n(s, nb):
+ sout = ""
+ def sub(g):
+ if g.group(2):
+ a, b = int(g.group(1)), int(g.group(2)[1:])
+ return nb[-a - 1:-b or None]
+ else:
+ a = int(g.group(1))
+ return nb[-a - 1]
+
+ s = re.sub(r'n\[(\d+)(:\d+)?\]', sub, s)
+ s = "".join(s.split(":"))
+ return int(s.replace("0b", ""), 2)
+
+def parse_one(regs, xml):
+ t = ElementTree.parse(xml)
+
+ for reg in t.findall('registers/register'):
+ data = {}
+
+ name = reg.find('reg_short_name').text
+ fullname = reg.find('reg_long_name').text
+
+ if name.startswith("S3_") or name.startswith("SYS S1_"):
+ continue
+
+ array = reg.find('reg_array')
+
+ start = end = 0
+
+ if array:
+ start = int(array.find("reg_array_start").text)
+ end = int(array.find("reg_array_end").text)
+
+ encs = {}
+ accessors = {}
+
+ for am in reg.findall('access_mechanisms/access_mechanism'):
+ accessor = am.attrib["accessor"]
+ if accessor.startswith("MSRimmediate"):
+ continue
+ ins = am.find("encoding/access_instruction").text.split(" ")[0]
+ regname = accessor.split(" ", 1)[1]
+ enc = {}
+ for e in am.findall("encoding/enc"):
+ enc[e.attrib["n"]] = e.attrib["v"]
+
+ enc = enc["op0"], enc["op1"], enc["CRn"], enc["CRm"], enc["op2"]
+ if regname in encs:
+ assert encs[regname] == enc
+ encs[regname] = enc
+ accessors.setdefault(regname, set()).add(ins)
+
+ if not encs:
+ continue
+
+ fieldsets = []
+
+ width = None
+
+ for fields_elem in reg.findall('reg_fieldsets/fields'):
+
+ fieldset = {}
+
+ if (instance_elem := fields_elem.find('fields_instance')) is not None:
+ fieldset["instance"] = instance_elem.text
+
+ fields = []
+
+ set_width = int(fields_elem.attrib["length"])
+
+ if width is None:
+ width = set_width
+ else:
+ assert width == set_width
+
+ single_field = False
+
+ for f in fields_elem.findall('field'):
+
+ if f.attrib.get("rwtype", None) in ("RES0", "RES1", "RAZ", "RAZ/WI", "RAO/WI", "UNKNOWN"):
+ continue
+ msb, lsb = int(f.find('field_msb').text), int(f.find('field_lsb').text)
+
+ assert not single_field
+
+ if msb == width - 1 and lsb == 0:
+ continue
+
+ if (name_elem := f.find('field_name')) is not None:
+ name = name_elem.text
+ else:
+ assert not fields
+ continue
+
+ field = {
+ "name": name,
+ "msb": msb,
+ "lsb": lsb,
+ }
+ fields.append(field)
+
+ fields.sort(key=lambda x: x["lsb"], reverse=True)
+
+ fieldset["fields"] = fields
+ fieldsets.append(fieldset)
+
+ for idx, n in enumerate(range(start, end + 1)):
+ nb = "{0:064b}".format(n)[::-1]
+ for name, enc in sorted(encs.items()):
+ enc = tuple(insert_n(i, nb) for i in enc)
+ data = {
+ "index": idx,
+ "name": name.replace("<n>", "%d" % n),
+ "fullname": fullname,
+ "enc": enc,
+ "accessors": sorted(list(accessors[name])),
+ "fieldsets": fieldsets,
+ }
+
+ if width is not None:
+ data["width"] = width
+
+ yield data
+
+if __name__ == "__main__":
+ regs = []
+ for i in sys.argv[1:]:
+ regs.extend(parse_one(regs, i))
+
+ json.dump(regs, sys.stdout)
+
+
+
diff --git a/tools/tools/reg_filter.py b/tools/tools/reg_filter.py
new file mode 100644
index 0000000..81591d5
--- /dev/null
+++ b/tools/tools/reg_filter.py
@@ -0,0 +1,35 @@
+import json, sys, re, math
+import argparse
+
+parser = argparse.ArgumentParser()
+parser.add_argument("regfile")
+args = parser.parse_args()
+
+data = json.load(open(args.regfile))
+
+name_map = {}
+
+for reg in data:
+ name = reg['name']
+ enc = reg['enc']
+ name_map[f"s{enc[0]}_{enc[1]}_c{enc[2]}_c{enc[3]}_{enc[4]}"] = name
+
+def reg_lookup(m):
+ s = m.group(0)
+ return name_map.get(s, s)
+
+def hex_parse(m):
+ v = int(m.group(0), 0)
+ if v and (v & (v - 1)) == 0:
+ bit = int(math.log2(v))
+ return f"BIT({bit})"
+ v ^= 0xffff_ffff_ffff_ffff
+ if v and (v & (v - 1)) == 0:
+ bit = int(math.log2(v))
+ return f"~BIT({bit})"
+ return m.group(0)
+
+for line in sys.stdin:
+ line = re.sub(r"s(\d+)_(\d+)_c(\d+)_c(\d+)_(\d+)", reg_lookup, line)
+ line = re.sub(r"\b0x[0-9a-f]+\b", hex_parse, line)
+ sys.stdout.write(line)
diff --git a/tools/udev/80-m1n1.rules b/tools/udev/80-m1n1.rules
new file mode 100644
index 0000000..495551e
--- /dev/null
+++ b/tools/udev/80-m1n1.rules
@@ -0,0 +1,6 @@
+SUBSYSTEM=="tty", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="316d", GOTO="m1n1"
+GOTO="not_m1n1"
+LABEL="m1n1"
+SUBSYSTEM=="tty", ATTRS{bInterfaceNumber}=="00", KERNEL=="ttyACM*", SYMLINK+="m1n1"
+SUBSYSTEM=="tty", ATTRS{bInterfaceNumber}=="02", KERNEL=="ttyACM*", SYMLINK+="m1n1-sec"
+LABEL="not_m1n1"
diff --git a/tools/version.sh b/tools/version.sh
new file mode 100755
index 0000000..c680fbe
--- /dev/null
+++ b/tools/version.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+cd "$(dirname "$0")"
+
+dirbase="$(basename "$(pwd)")"
+
+if [ -n "$M1N1_VERSION_TAG" ]; then
+ version="$M1N1_VERSION_TAG"
+elif [ -e ".git" ]; then
+ version="$(git describe --tags --always --dirty)"
+elif [ "$(echo "${dirbase}" | cut -c1-5)" = "m1n1-" ]; then
+ version=$(echo "${dirbase}" | cut -c6-)
+ version="v${version##v}"
+else
+ version="unknown"
+fi
+
+echo "#define BUILD_TAG \"$version\""