Compare commits

..

258 Commits

Author SHA1 Message Date
TuxSH
020cfb89c6 thermosphere: minor refactor 2021-02-19 21:52:39 +00:00
TuxSH
f40c064e80 thermosphere: reload debug registers (refactored) 2021-02-19 21:52:39 +00:00
TuxSH
6bcb5aca60 thermosphere: remove dead code 2021-02-19 21:52:38 +00:00
TuxSH
548367453b thermosphere: fix uart pinmuxing. As it's now possible to init all uarts at the same time, do it.
Thanks to @CTCaer

See dumped values from HOS: ccaa47ee04/kernel-dts/icosa-platforms/tegra210-icosa-pinmux.dtsi
2021-02-19 21:52:38 +00:00
TuxSH
6790895487 thermosphere: shiny new tegra 210 gpio rewrite 2021-02-19 21:52:38 +00:00
TuxSH
41d98b5e48 thermosphere: oops 2021-02-19 21:52:37 +00:00
TuxSH
b6dbdfe82d thermosphere: car driver rewrite 2021-02-19 21:52:37 +00:00
TuxSH
30b79c2fe7 thermosphere: pinmux driver C++ 2021-02-19 21:52:37 +00:00
TuxSH
3a79a7a961 thermosphere: tegra uart driver rewrite 2021-02-19 21:52:36 +00:00
TuxSH
cefd66e7af thermosphere: refactor pl011 driver a bit more 2021-02-19 21:52:36 +00:00
TuxSH
a8f28ab96d thermosphere: pl011 driver rewrite 2021-02-19 21:52:35 +00:00
TuxSH
2986967f2a thermosphere: guest mem rewrite 2021-02-19 21:52:35 +00:00
TuxSH
fc8a596409 thermosphere: safe io copy 2021-02-19 21:52:35 +00:00
TuxSH
36f48748a4 thermosphere: clean up start.s & exception_vector.s 2021-02-19 21:52:34 +00:00
TuxSH
6f423fcfab thermosphere: rewrite (host) memory map (wip, need to update start.s anyway) 2021-02-19 21:52:34 +00:00
TuxSH
fccadfdbf6 thermosphere: add TCR_TG0 back 2021-02-19 21:52:34 +00:00
TuxSH
47f343cda6 thermosphere: mmu: fix shareability 2021-02-19 21:52:33 +00:00
TuxSH
987731ea43 thermosphere: mmu table builder 2021-02-19 21:52:33 +00:00
TuxSH
d4bbb78a27 thermosphere: static inline -> inline 2021-02-19 21:52:33 +00:00
TuxSH
0126a6417f thermosphere: fix off by 1 in wp mgr 2021-02-19 21:52:32 +00:00
TuxSH
7ecb3a4aaf thermosphere: cleanup again 2021-02-19 21:52:32 +00:00
TuxSH
56d764d09c thermosphere: rewrite semihosting 2021-02-19 21:52:31 +00:00
TuxSH
0cb5eab933 thermosphere: generic timer rewrite 2021-02-19 21:52:31 +00:00
TuxSH
d15154f668 thermosphere: cleanup again 2021-02-19 21:52:30 +00:00
TuxSH
ea830bb5ab thermosphere: propagate changes 2021-02-19 21:52:30 +00:00
TuxSH
e8435784a7 thermosphere: rewrite smc traps 2021-02-19 21:52:30 +00:00
TuxSH
0437867449 thermosphere: sysreg traps 2021-02-19 21:52:29 +00:00
TuxSH
797cea0ac8 thermosphere: trap refactor WIP 2021-02-19 21:52:29 +00:00
TuxSH
874d1432be thermosphere: exception dispatcher rewrite 2021-02-19 21:52:29 +00:00
TuxSH
e8bfe8a311 thermosphere: guest timer header 2021-02-19 21:52:28 +00:00
TuxSH
036883c30f thermosphere: propagate some changes 2021-02-19 21:52:28 +00:00
TuxSH
5b56d05e11 thermosphere: move EnterInterruptibleHypervisorCode into IrqManager 2021-02-19 21:52:27 +00:00
TuxSH
4adb675072 thermosphere: hvisor_exception_frame.hpp 2021-02-19 21:52:27 +00:00
TuxSH
77fbbb4c68 thermosphere: singleton ctors should be private 2021-02-19 21:52:27 +00:00
TuxSH
f6793139c1 thermosphere: fix exception vector regression 2021-02-19 21:52:26 +00:00
TuxSH
37a889ccb2 thermosphere: rewrite core_ctx 2021-02-19 21:52:26 +00:00
TuxSH
ea7d161755 thermosphere: start corectx/exception stuff rewrite 2021-02-19 21:52:26 +00:00
TuxSH
d72fc3e8b9 thermosphere: cleanup cache trap 2021-02-19 21:52:25 +00:00
TuxSH
c7eaf71896 thermosphere: fpu register cache 2021-02-19 21:52:25 +00:00
TuxSH
5a445e9394 thermosphere: cache rewrite 2021-02-19 21:52:25 +00:00
TuxSH
613402121a thermosphere: file cleanup 2021-02-19 21:52:24 +00:00
TuxSH
2574f68484 thermopshere: interrupt refactoring 2021-02-19 21:52:24 +00:00
TuxSH
1ee289f5f1 thermosphere: C++ vgic 2021-02-19 21:52:24 +00:00
TuxSH
31e5ff7c1d thermosphere: continue vgic rewrite 2021-02-19 21:52:23 +00:00
TuxSH
02bbe1bb40 thermosphere: begin to write virtual gic code in C++ 2021-02-19 21:52:23 +00:00
TuxSH
b21c75b22b thermosphere: add singleton define 2021-02-19 21:52:22 +00:00
TuxSH
c99a77a0c3 thermosphere: interrupt mask guard 2021-02-19 21:52:22 +00:00
TuxSH
dd9b3ddb0d thermosphere: irq manager wip 2021-02-19 21:52:22 +00:00
TuxSH
785b7e1a37 thermosphere: mostly rewrite sw breakpoint manager 2021-02-19 21:52:21 +00:00
TuxSH
1eda049ada thermosphere: hw breakpoint/watchpoint managers 2021-02-19 21:52:21 +00:00
TuxSH
493a3c92e2 thermosphere: cpu intrinsics + synchronization primitives 2021-02-19 21:52:21 +00:00
TuxSH
dad84ac017 thermosphere: sysreg stuff 2021-02-19 21:52:20 +00:00
TuxSH
eab46ab1b6 thermosphere: C++ify gdb/verbose 2021-02-19 21:52:20 +00:00
TuxSH
192d2db4a9 thermospshere: c++ify xfer, query, etc. More string parsing utilities 2021-02-19 21:52:19 +00:00
TuxSH
ff2c835b0a thermosphere: introduce GDB_TEST_NO_CMD_DATA 2021-02-19 21:52:19 +00:00
TuxSH
dd7f0b805b thermosphere: partially rewrite gdb context top-level 2021-02-19 21:52:19 +00:00
TuxSH
fdd5481f63 thermosphere: C++ gdb/rcmd 2021-02-19 21:52:19 +00:00
TuxSH
0b8d0035b9 thermosphère: C++ gdb/regs 2021-02-19 21:52:18 +00:00
TuxSH
bfa917edf5 thermosphere: rewrite gdb/stop points in c++ 2021-02-19 21:52:18 +00:00
TuxSH
61a972abf3 thermosphere: rewrite gdb/mem in c++. Remove SearchMemory handler 2021-02-19 21:52:18 +00:00
TuxSH
697e61850f thermosphere: rewrite packet comms 2021-02-19 21:52:17 +00:00
TuxSH
2077062b79 thermosphere: cpp rewrite: constexpr string parsing, rewrote gdb/thread 2021-02-19 21:52:17 +00:00
TuxSH
b445fe1bf4 wip 2021-02-19 21:52:16 +00:00
TuxSH
b65f11d205 wip 2021-02-19 21:52:16 +00:00
TuxSH
5de560be30 thermosphere: fix watchpoint creation, fix wp&bp allocation 2021-02-19 21:52:16 +00:00
TuxSH
be6253d6ad thermosphere: rewrite watchpoints.c 2021-02-19 21:52:15 +00:00
TuxSH
78eea8a373 thermosphere: suppress potential unused variable warnings 2021-02-19 21:52:15 +00:00
TuxSH
53850a5976 thermosphere: reduce gdb work buf to least acceptable limit 2021-02-19 21:52:15 +00:00
TuxSH
788f331de0 thermosphere: the fpu cache is only being really modified by gdb anyway 2021-02-19 21:52:14 +00:00
TuxSH
edf2bbc30e thermosphere: I wish ld wasn't dumb (also, bugfix). This saves 4K 2021-02-19 21:52:14 +00:00
TuxSH
e4d189eee3 thermosphere: rewhoops 2021-02-19 21:52:14 +00:00
TuxSH
e6fdd6bc98 thermosphere: fix software breakpoints 2021-02-19 21:52:13 +00:00
TuxSH
3556c12960 thermosphere: gdb: fix IsThreadAlive 2021-02-19 21:52:13 +00:00
TuxSH
67daf5a73e thermosphère: fix deadlock 2021-02-19 21:52:12 +00:00
TuxSH
f1a241ffef thermosphere: fix sending bug when handling ctrl-c 2021-02-19 21:52:12 +00:00
TuxSH
bf7f077432 thermosphere: fix continue logic for full-stop & some refactoring 2021-02-19 21:52:12 +00:00
TuxSH
ebf8053b42 thermosphere: rewrite condition in debugManagerDoPauseCores 2021-02-19 21:52:11 +00:00
TuxSH
914790be01 thermosphere: fix bug in debug.c 2021-02-19 21:52:11 +00:00
TuxSH
036882f162 thermosphere: oops 2021-02-19 21:52:11 +00:00
TuxSH
b0ae19a6f9 thermosphere: better self-debug fault reporting 2021-02-19 21:52:10 +00:00
TuxSH
0b7efc0501 thermosphere: fix bug in exceptionReturnPreprocess 2021-02-19 21:52:10 +00:00
TuxSH
c67ff366ea thermosphere: forgot compiler barrier in get_sysreg 2021-02-19 21:52:10 +00:00
TuxSH
63e3f40fa5 thermosphere: fix gdb/regs.c assertions 2021-02-19 21:52:09 +00:00
TuxSH
3fe7c7537e thermopshere: GDB_ParseExceptionFrame: fix format error 2021-02-19 21:52:09 +00:00
TuxSH
256201922b thermosphere: fix bug where x0 isn't saved 2021-02-19 21:52:09 +00:00
TuxSH
46c82e2d77 thermosphere: fix thread reporting logic, etc 2021-02-19 21:52:08 +00:00
TuxSH
cb4d898579 thermosphere: fix reporting logic of initial break event 2021-02-19 21:52:08 +00:00
TuxSH
7acd5a9ec7 thermosphere: fix target.xml generation 2021-02-19 21:52:08 +00:00
TuxSH
7f7e4e8310 thermosphere: fix irq buffer overflow 2021-02-19 21:52:07 +00:00
TuxSH
8f25d4f77f thermosphere: add more debugging strings 2021-02-19 21:52:07 +00:00
TuxSH
e1a8bdd495 thermosphere: gdb: fix a few bugs 2021-02-19 21:52:07 +00:00
TuxSH
ef23db21e6 thermosphere: pl011: fix uartSetInterruptStatus
We don't need to forcefully clear the line level
2021-02-19 21:52:06 +00:00
TuxSH
46954a5359 thermosphere: actually report the debug events 2021-02-19 21:52:06 +00:00
TuxSH
6499d36722 thermosphere: gdb: fix GDB_SendStopReply 2021-02-19 21:52:06 +00:00
TuxSH
66ba05b302 thermosphere: pause at start, some cleanup, etc. 2021-02-19 21:52:05 +00:00
TuxSH
7a774adbc3 thermosphere: libc: fix missing macro 2021-02-19 21:52:05 +00:00
TuxSH
ce1df0ac23 thermosphere: qemu: make serial go through a socket 2021-02-19 21:52:04 +00:00
TuxSH
fc5d81dca3 thermosphere: oops 2021-02-19 21:52:04 +00:00
TuxSH
23ef4b94d6 thermosphere: reduce usage of nonvolatile memory by around 4KB 2021-02-19 21:52:04 +00:00
TuxSH
e4de512e6f thermosphere: gdb: add debugManagerInit 2021-02-19 21:52:03 +00:00
TuxSH
cf0b052590 thermosphere: gdb: add missing command list entries, fix warnings again 2021-02-19 21:52:03 +00:00
TuxSH
0509fa57ca thermosphere: add src/gdb to build list, fix subsequent warnings and errors 2021-02-19 21:52:03 +00:00
TuxSH
175f16627b thermosphere: fix break/continue (?), fix attach/detach 2021-02-19 21:52:02 +00:00
TuxSH
f0b9162d5e thermosphere: gdb: remove currentThreadId; migrate rx irq 2021-02-19 21:52:02 +00:00
TuxSH
02e2a1efa2 thermosphere: gdb: add core_on and core_off handling 2021-02-19 21:52:01 +00:00
TuxSH
ed5736e8d2 thermosphere: forgot to call exceptionReturnPreprocess in start.s 2021-02-19 21:52:01 +00:00
TuxSH
b0ca29d18e thermosphere: gdb: properly handle vStopped ack sequence 2021-02-19 21:52:01 +00:00
TuxSH
36ca87491d thermosphere: gdb/debug: avoid pause/unpause race condition in vCont + bugfix 2021-02-19 21:52:00 +00:00
TuxSH
9ef2532b9d thermosphere: gdb: fix parsing errors in vCont and hex decode 2021-02-19 21:52:00 +00:00
TuxSH
cbf3b305ca thermosphere: gdb add break & vCont handling 2021-02-19 21:52:00 +00:00
TuxSH
c0252e07f6 thermosphere: GDB_TrySignalDebugEvent, do nothing if not attached 2021-02-19 21:51:59 +00:00
TuxSH
71401b0731 thermosphere: add structural changes needed for range step 2021-02-19 21:51:59 +00:00
TuxSH
ff1aac0ab5 thermosphere: resend debug event if not handled 2021-02-19 21:51:59 +00:00
TuxSH
984f6776c6 thermosphere: impl. debug event dispatching, vStopped, "?" 2021-02-19 21:51:58 +00:00
TuxSH
0e47f7f46b thermosphere: debug manager wip 2021-02-19 21:51:58 +00:00
TuxSH
c00672654a thermosphere: gdb: remove server, rewrite data processing in gdb/context and gdb/net 2021-02-19 21:51:58 +00:00
TuxSH
8538fed043 thermosphere: optimize barrier and core_ctx 2021-02-19 21:51:57 +00:00
TuxSH
1f2b8e7918 thermopshere: add spinlock try lock 2021-02-19 21:51:57 +00:00
TuxSH
30a4a0d4c1 thermosphere: rewrite gdb/reg 2021-02-19 21:51:57 +00:00
TuxSH
97c4595a3a thermosphere: rework fpu register handling 2021-02-19 21:51:56 +00:00
TuxSH
5b545f89f5 thermosphere: introduce "ENSURE" 2021-02-19 21:51:56 +00:00
TuxSH
310048a32c thermosphere: small spinlock improvements 2021-02-19 21:51:56 +00:00
TuxSH
5473443057 thermosphere: refactor gdb/thread 2021-02-19 21:51:55 +00:00
TuxSH
78723164c1 thermosphere: gdb: target xml + various refactoring 2021-02-19 21:51:55 +00:00
TuxSH
58d52675cd thermosphere: rewrite gdb/mem 2021-02-19 21:51:55 +00:00
TuxSH
bd36796d5f thermosphere: gdb/net: reduce stack/memory usage by using memmove 2021-02-19 21:51:54 +00:00
TuxSH
779aeaa538 thermopshere: gdb: rewrite stop point handling 2021-02-19 21:51:54 +00:00
TuxSH
5de05ed8a8 thermosphere: retrieve wp direction 2021-02-19 21:51:54 +00:00
TuxSH
abeaa72f94 thermosphere: some gdb/debug refactor 2021-02-19 21:51:53 +00:00
TuxSH
c89ce085a6 thermopshère: rewrite some gdb/net functions 2021-02-19 21:51:53 +00:00
TuxSH
418cabbd53 thermosphere: add esr_el2 to exception frame 2021-02-19 21:51:53 +00:00
TuxSH
744491ca33 thermosphere: allow each core to pause itself in a lock-free manner & fix bugs 2021-02-19 21:51:52 +00:00
TuxSH
9ebf3c9580 thermosphere: wip gdb 2021-02-19 21:51:52 +00:00
TuxSH
f23fb45956 thermosphere: copy paste lots of gdb luma files (but don't build them yet) 2021-02-19 21:51:51 +00:00
TuxSH
61fec56c6e thermosphere: minor changes 2021-02-19 21:51:51 +00:00
TuxSH
a665f49b93 thermosphere: incl pattern utils 2021-02-19 21:51:51 +00:00
TuxSH
3e8bd764d5 thermosphere: unfuck sw breakpoint logic 2021-02-19 21:51:51 +00:00
TuxSH
c64ccd86ee thermosphere: uninline recursive lock funcs 2021-02-19 21:51:50 +00:00
TuxSH
217c1ad054 thermosphere: implement reading and writing guest memory 2021-02-19 21:51:50 +00:00
TuxSH
0f0228e240 thermosphere: we expose a GICv2, not a GICv1 2021-02-19 21:51:49 +00:00
TuxSH
3ca3e094fe thermosphere: use ish instead of sy in most places 2021-02-19 21:51:49 +00:00
TuxSH
d1cd17a9df thermosphere: fix fmt.c "l" handling 2021-02-19 21:51:49 +00:00
TuxSH
626f0ecb98 thermosphere: major refactor of memory map
- use recursive stage 1 page table (thanks @fincs for this idea)
- NULL now unmapped
- no identity mapping
- image + GICv2 now mapped at the same address for every platform
- tempbss mapped just after "real" bss, can now steal unused mem from
the latter
- no hardcoded VAs for other MMIO devices
- tegra: remove timers, use the generic timer instead
2021-02-19 21:51:48 +00:00
TuxSH
92a291cd41 thermosphere: disable interrupts in debugPauseCores 2021-02-19 21:51:48 +00:00
TuxSH
906d6a4f20 thermosphere: rewrite debug pause & fix single step state machine 2021-02-19 21:51:48 +00:00
TuxSH
6b8a843ffb thermosphere: trap set/way dcache access
note: qemu does not implement the trap
2021-02-19 21:51:47 +00:00
TuxSH
72d1992eec thermosphere: use barriers and caches *properly*. Cache code refactoring
- set/way cache ops create losses of coherency, do not broadcast and are only meant to be used on boot, period.

Cache ops by VA are **the only way** to do data cache maintenance.

Fix a bug where the L2 cache was evicted by each core. It shouldn't have.

- Cleaning dcache to PoU and invalidating icache to PoU, by VA is sufficient for self-modifying code

- Since we operate within a single cluster and don't do DMA, we almost always operate within the inner shareability domain

(commit untested on real hw)
2021-02-19 21:51:47 +00:00
TuxSH
1369697058 thermosphere: add debug pause logic 2021-02-19 21:51:47 +00:00
TuxSH
b6a130547a thermosphere: add common asm macros 2021-02-19 21:51:46 +00:00
TuxSH
067770334e thermosphere: add fpu regs save/restore 2021-02-19 21:51:46 +00:00
TuxSH
a7741c8576 thermosphere: add cctx->userFrame 2021-02-19 21:51:46 +00:00
TuxSH
dd96c8b32b thermosphere: fix ptimer time freezing (again) 2021-02-19 21:51:45 +00:00
TuxSH
68a1ce6dd2 thermosphere: properly implement guest timer stuff 2021-02-19 21:51:45 +00:00
TuxSH
388c245ce4 thermosphere: add TransportInterface abstraction layer 2021-02-19 21:51:45 +00:00
TuxSH
1086c0612c thermosphere: refactor tegra uart code, etc. 2021-02-19 21:51:44 +00:00
TuxSH
8dc9be9f8e thermosphere: pl011 uart refactor 2021-02-19 21:51:44 +00:00
TuxSH
018260645a thermosphere: fix pl101 uart reg definitions 2021-02-19 21:51:44 +00:00
TuxSH
a6d191bf4b thermosphere: add proper memory/instruction barriers for breakpoint stuff 2021-02-19 21:51:43 +00:00
TuxSH
1eb60a2a52 thermosphere: add hypervisor timer code 2021-02-19 21:51:43 +00:00
TuxSH
3d3a9925b9 thermosphere: qemu: get rid of arm tf
qemu impls psci anyway
2021-02-19 21:51:42 +00:00
TuxSH
501472324f thermosphere: refactor exception handlers & add stolen time/emulated ptimer logic 2021-02-19 21:51:42 +00:00
TuxSH
b9d07fccd6 thermosphere: rewrite sysreg trapping code, add skeleton code for timer val trap handling; support A32 EL1 once again 2021-02-19 21:51:42 +00:00
TuxSH
d42d9e60b9 thermosphere: don't trap memory register writes/don't migrate sw breakpoints
Makes no sense on a system with ASLR
2021-02-19 21:51:41 +00:00
TuxSH
28552da099 thermosphere: vgic: largely reduce the number of mmio accesses
since we have to use 64 bits for VirqState anyway
2021-02-19 21:51:41 +00:00
TuxSH
d56185e432 thermosphere: make the pending virq list ordering stable 2021-02-19 21:51:41 +00:00
TuxSH
c42aef6ba7 thermosphere: fix wrong icfgr shift; fix list handling bug 2021-02-19 21:51:40 +00:00
TuxSH
03fe744bc4 thermosphere: vgic: fix OOB accesses, fix icfgr and itargetsr handling
qemu actually allows SPIs to use the N-N model
2021-02-19 21:51:40 +00:00
TuxSH
e49a035455 thermosphere: fix is/ic registers usage; fix offset calculation 2021-02-19 21:51:40 +00:00
TuxSH
0811572889 thermosphere: fix truncation in vgicCleanupPendingList 2021-02-19 21:51:39 +00:00
TuxSH
76a5e745e4 thermosphere: honor irq config for ppis 2021-02-19 21:51:39 +00:00
TuxSH
7130b6efd1 thermosphere: yikes 2021-02-19 21:51:39 +00:00
TuxSH
37b14bc4b8 thermosphere: use strict volatile bitfields just in case 2021-02-19 21:51:38 +00:00
TuxSH
13174e7458 thermosphere: vgic: fix critical bug in vgicUpdateState, add more checks
Yikes.
2021-02-19 21:51:38 +00:00
TuxSH
ef79908594 thermosphere: add CFI where needed, add PANIC macro, etc. 2021-02-19 21:51:38 +00:00
TuxSH
3a13ab2e46 thermosphere: vgic: mostly fix vSGI handling, remove unimplementable/unused stuff + bugfixes
Still somewhat broken, though
2021-02-19 21:51:37 +00:00
TuxSH
676a895cca thermosphere: fix guest access to irq 25, etc; we don't need to raise VI manually
See Armv8a TRM "Virtual IRQ exception"
2021-02-19 21:51:37 +00:00
TuxSH
cdf3bc6942 thermosphere: add PPI definitions 2021-02-19 21:51:37 +00:00
TuxSH
fe0662a75d vgic: fix multiple bugs 2021-02-19 21:51:36 +00:00
TuxSH
f3ad62d1b8 thermosphere: fix various vgic bugs; fix register access OOB bug (xzr) 2021-02-19 21:51:36 +00:00
TuxSH
27859a7541 thermosphere: vgic: fix enabled state of virqs 2021-02-19 21:51:36 +00:00
TuxSH
e3b6d64f1b thermosphere: fix multiple bugs 2021-02-19 21:51:35 +00:00
TuxSH
c17b81aaf6 thermosphere: vgic code draft 2021-02-19 21:51:35 +00:00
TuxSH
176be2386d thermosphere: also trap GICH (to deny access) 2021-02-19 21:51:35 +00:00
TuxSH
f9ec21e99e thermosphere: handle stage2 data aborts, trap gicd accesses 2021-02-19 21:51:34 +00:00
TuxSH
1775d59977 thermosphere: implement stop point broadcast 2021-02-19 21:51:34 +00:00
TuxSH
b2c5ef2611 thermopshere: add "execute function" sgi 2021-02-19 21:51:34 +00:00
TuxSH
0b69407f8e thermosphere: barrier & active core mask 2021-02-19 21:51:33 +00:00
TuxSH
0a9a8c2f15 thermosphere: handle physical IRQs 2021-02-19 21:51:33 +00:00
TuxSH
271d2a0ddb thermosphere: add gicv2 register definitions 2021-02-19 21:51:33 +00:00
TuxSH
6289d2e398 thermosphere: sw breakpoint code, etc. 2021-02-19 21:51:32 +00:00
TuxSH
f8266775f6 thermosphere: remove breakpoint/watchpoint reg dump functions 2021-02-19 21:51:32 +00:00
TuxSH
83c6e2f0e7 thermosphere: add watchpoint + watchpoint merging code 2021-02-19 21:51:31 +00:00
TuxSH
9bc0ed2f70 thermosphere: refactor crt0 + watchpoint init 2021-02-19 21:51:31 +00:00
TuxSH
dc3f87a715 thermosphere: add actual breakpoint code 2021-02-19 21:51:31 +00:00
TuxSH
3649b94b5d thermosphere: add breakpoint/watchpoint enable/reset code 2021-02-19 21:51:30 +00:00
TuxSH
a3da478089 thermopshere: refactor & fix single-stepping code 2021-02-19 21:51:30 +00:00
TuxSH
ff9714d4f6 thermopshere: refactor jump-to-kernel ,add single-step code
not working under qemu yet though
2021-02-19 21:51:30 +00:00
TuxSH
cc232ef4f8 thermosphere: add spinlock code 2021-02-19 21:51:29 +00:00
TuxSH
b742b861ab thermometer: yeet most a32 support code 👌 2021-02-19 21:51:29 +00:00
TuxSH
eb27c36709 thermosphere: impl stage2 translation 2021-02-19 21:51:29 +00:00
TuxSH
e0339049b3 thermosphere: rework linkscrips, use discardable sections, better sp pivot on crash 2021-02-19 21:51:28 +00:00
TuxSH
e6c5eb3928 thermosphere: add shadow page table hooks
note: HCR.TVM not supported by qemu yet
2021-02-19 21:51:28 +00:00
TuxSH
045f556f80 thermosphere: enable EL2 stage1 translation (doesn't take much space)
Identity map using 1GB L1 blocks
2021-02-19 21:51:27 +00:00
TuxSH
a11b0b6e0e thermosphere: fix x18 init, etc. 2021-02-19 21:51:27 +00:00
TuxSH
3fa9133814 thermosphere: add semihosting support & load a kernel using it when needed
basically host i/o
2021-02-19 21:51:27 +00:00
TuxSH
ecb4857cbb thermosphere: seriaLog => debugLog, add DEBUG macro 2021-02-19 21:51:26 +00:00
TuxSH
6d33ebceef thermosphere: cpu_on hook & skeleton for other PSCI functions 2021-02-19 21:51:26 +00:00
TuxSH
4a5d05f32b thermosphere: add smc trap handler 2021-02-19 21:51:26 +00:00
TuxSH
b686af2008 thermosphere: use adrp 2021-02-19 21:51:25 +00:00
TuxSH
a291bddcc1 thermosphere: enable traps, works around qemu brk bug 2021-02-19 21:51:25 +00:00
TuxSH
ad6db14526 thermosphere: Fix wrong register allocation 2021-02-19 21:51:25 +00:00
TuxSH
61b6f06766 thermosphere: unfuck qemu JIT, fix exc. handling bug, add cache funcs 2021-02-19 21:51:24 +00:00
TuxSH
16cfa1305d thermosphere: use x18 but qemu shits the bed 2021-02-19 21:51:24 +00:00
TuxSH
af8e0f2519 thermosphere: add core_ctx.c/h 2021-02-19 21:51:24 +00:00
TuxSH
a560de8465 fml coke spilled all over this laptop's keyboard 2021-02-19 21:51:23 +00:00
TuxSH
3009438e54 thermosphere: sysreg stuff, continued 2021-02-19 21:51:23 +00:00
TuxSH
9af9408feb thermosphere: add remaining sysreg passthrough stuff 2021-02-19 21:51:23 +00:00
TuxSH
68469ea862 thermosphere: more sysreg code 2021-02-19 21:51:22 +00:00
TuxSH
ffa216c8c7 thermosphere: add some basic sysreg trapping code 2021-02-19 21:51:22 +00:00
TuxSH
1db0502b35 thermosphere: proper uart_reset impl for uart-b 2021-02-19 21:51:22 +00:00
TuxSH
6665245640 thermosphere: fix uart fifo init/flushing 2021-02-19 21:51:21 +00:00
TuxSH
9d6089dc86 thermosphere: rebase, fix some bugs
uart now works except for fifo flush
2021-02-19 21:51:21 +00:00
TuxSH
70a9caa7e9 thermosphere: add more sysreg stuff & start writing trap stuff 2021-02-19 21:51:21 +00:00
TuxSH
4952b3c9bf thermosphere: add sysreg list 2021-02-19 21:51:20 +00:00
TuxSH
bcc72896fd thermosphere: add hypercall support... even if unused 2021-02-19 21:51:20 +00:00
TuxSH
b5c6b06dad thermosphere: add ExceptionSyndromeRegister definition 2021-02-19 21:51:20 +00:00
TuxSH
4e0eef2784 thermosphere: start exception handling 2021-02-19 21:51:19 +00:00
TuxSH
ada6b180cc thermosphere: add qemu support 2021-02-19 21:51:19 +00:00
TuxSH
e6adccce6e thermosphere: uart fixes/ still not working 2021-02-19 21:51:19 +00:00
TuxSH
f6e1cff5f8 thermosphere: rebase, doesn't work 2021-02-19 21:51:18 +00:00
TuxSH
88382f4fc3 thermosphere: uart refactor, now it doesn't work at all 2021-02-19 21:51:18 +00:00
TuxSH
66b047255b thermosphere: set correct gpio config for uart (thanks @hexkyz) 2021-02-19 21:51:18 +00:00
TuxSH
076c988796 thermosphere: attempt to output to uart-c 2021-02-19 21:51:17 +00:00
TuxSH
4e6108839d thermosphere: fix bugs:
- missing barriers after setting elr/spsr
- .text.start* matching .text.startup (which contains main, thanks @fincs)
2021-02-19 21:51:17 +00:00
TuxSH
1d58ba8d52 thermosphere: attempt to run 2021-02-19 21:51:17 +00:00
TuxSH
bd9152215f thermosphere: "write" placeholder code 2021-02-19 21:51:16 +00:00
TuxSH
1f7a1f71d6 thermosphere: remove legacy code 2021-02-19 21:51:16 +00:00
Michael Scire
1545fa9d44 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "bc08912d"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "bc08912d"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2021-02-15 19:52:28 -08:00
Michael Scire
71add1add8 ams: bump version to 0.18.1 2021-02-15 19:51:48 -08:00
Michael Scire
a96786fd2c ams: add kernel to debug elf zip 2021-02-15 19:40:00 -08:00
Michael Scire
74e4e70053 fs.mitm: fix cache of non-current-process data storages (closes #1371) 2021-02-15 19:39:32 -08:00
znxDomain
26b6216fa0 Correct ams_mitm.md formatting
Move set_mitm H3's under correct H2 element
2021-02-11 05:47:31 -08:00
Michael Scire
fe5c850e69 psc: fix pm module init 2021-02-10 02:57:22 -08:00
Michael Scire
2b825d56dc usb: fix wrong command id for AppendConfigurationData 2021-02-07 17:14:48 -08:00
Michael Scire
13b17a5848 usb: add ds client api 2021-02-07 16:29:38 -08:00
Michael Scire
621520c30b kern: fix support for virtual core IDs 2021-02-05 14:59:03 -08:00
Michael Scire
846cc0b47a util: add FixedSet 2021-02-04 02:29:54 -08:00
Michael Scire
e82ad1cdc5 util: add FixedMap::clear 2021-02-04 02:16:37 -08:00
Michael Scire
b40da8f445 ams: add transient unit testing sysmodule to gitignore 2021-02-04 02:08:49 -08:00
Michael Scire
a9c6476416 util: various FixedMap fixes 2021-02-04 02:08:21 -08:00
Michael Scire
c1d93a9495 util: add FixedMap/FixedTree 2021-02-04 01:00:19 -08:00
Michael Scire
201b17f100 util: impl::AvailableIndexFinder 2021-02-03 17:47:32 -08:00
Michael Scire
c0e3cee657 dns.mitm: allow nullptr for port string (closes #1352) 2021-02-03 12:41:23 -08:00
Michael Scire
26d8db74f8 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "17960517"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "17960517"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2021-02-02 18:33:22 -08:00
Michael Scire
ee2e9d50fd hid: fix sm usage 2021-02-02 18:32:29 -08:00
Michael Scire
fd1a39996e ncm: fix ContentStorageImplBase constructor 2021-02-02 17:12:02 -08:00
Michael Scire
8eb65ab401 dns.mitm: fix hosts file parsing bug 2021-02-02 17:11:53 -08:00
Michael Scire
bcda834980 dns.mitm: make line ordering explicit, rather than implicit.
This doesn't actually change functionality, because this is how std::unordered_map worked anyway...

...but it's better for us to be explicit, I think.
2021-02-02 13:48:40 -08:00
184 changed files with 20817 additions and 2969 deletions

2
.gitignore vendored
View File

@@ -95,3 +95,5 @@ sept/sept-secondary/KEYS.py
**/build_nintendo_nx_arm
**/build_nintendo_nx_x64
**/build_nintendo_nx_x86
stratosphere/test/

View File

@@ -135,6 +135,7 @@ dist: dist-no-debug
cp exosphere/program/sc7fw/sc7fw.elf atmosphere-$(AMSVER)-debug/exosphere-sc7fw.elf
cp exosphere/program/rebootstub/rebootstub.elf atmosphere-$(AMSVER)-debug/exosphere-rebootstub.elf
cp mesosphere/kernel_ldr/kernel_ldr.elf atmosphere-$(AMSVER)-debug/kernel_ldr.elf
cp mesosphere/kernel/kernel.elf atmosphere-$(AMSVER)-debug/kernel.elf
cp stratosphere/ams_mitm/ams_mitm.elf atmosphere-$(AMSVER)-debug/ams_mitm.elf
cp stratosphere/boot/boot.elf atmosphere-$(AMSVER)-debug/boot.elf
cp stratosphere/boot2/boot2.elf atmosphere-$(AMSVER)-debug/boot2.elf

View File

@@ -1,4 +1,15 @@
# Changelog
## 0.18.1
+ A number of minor issues were fixed, including:
+ The new `dns.mitm` module added in 0.18.0 no longer fatal errors when receiving port=nullptr.
+ This fixes youtube ad-blocking, and possibly other usecases.
+ A bug was fixed that caused ams.mitm to incorrectly cache data storages.
+ This potentially broke DLC when using romfs mods, and could have caused other issues (e.g. with custom themes, and maybe other cases).
+ A bug was fixed in power state control module registration.
+ This might fix a weird edge case with system module dependencies on sleep/wake, but probably nobody should notice any differences.
+ A bug was fixed where mesosphere sometimes treated virtual core IDs as though they were physical core IDs.
+ This had zero impact, because for Switch virtual core == physical core, but it could have affected future platforms if it had remained unresolved.
+ Several issues were fixed, and usability and stability were improved.
## 0.18.0
+ A new mitm module was added (`dns.mitm`).
+ This provides a highly configurable mechanism for redirecting DNS resolution requests.

View File

@@ -25,12 +25,6 @@ set_mitm enables intercepting requests to the system settings service. It curren
+ `ns` system module and games (to allow for overriding game locales)
+ All firmware debug settings requests (to allow modification of system settings not directly exposed to the user)
## dns_mitm
dns_mitm enables intercepting requests to dns resolution services, to enable redirecting requests for specified hostnames.
For documentation, see [here](../../features/dns_mitm.md).
### Firmware Version
set_mitm intercepts the `GetFirmwareVersion` command, if the requester is `qlaunch` or `maintenance`.
It modifies the `display_version` field of the returned system version, causing the version to display
@@ -39,3 +33,8 @@ in settings as `#.#.#|AMS #.#.#|?` with `? = S` when running under system eMMC o
### System Settings
set_mitm intercepts the `GetSettingsItemValueSize` and `GetSettingsItemValue` commands for all requesters.
It does so in order to enable user configuration of system settings, which are parsed from `/atmosphere/system_settings.ini` on boot. See [here](../../features/configurations.md) for more information on the system settings format.
## dns_mitm
dns_mitm enables intercepting requests to dns resolution services, to enable redirecting requests for specified hostnames.
For documentation, see [here](../../features/dns_mitm.md).

View File

@@ -11,6 +11,8 @@ In particular, hosts files parsed by DNS.mitm have the following extensions to t
+ `*` is treated as a wildcard character, matching any collection of 0 or more characters wherever it occurs in a hostname.
+ `%` is treated as a stand-in for the value of `nsd!environment_identifier`. This is always `lp1`, on production devices.
If multiple entries in a host file match a domain, the last-defined match is used.
Please note that homebrew may trigger a hosts file re-parse by sending the extension IPC command 65000 ("AtmosphereReloadHostsFile") to a connected `sfdnsres` session.
### Hosts file selection

View File

@@ -35,9 +35,10 @@
static void package2_decrypt(package2_header_t *package2);
static size_t package2_get_src_section(void **section, package2_header_t *package2, unsigned int id);
static size_t package2_get_thermosphere(void **thermosphere);
static size_t package2_get_thermosphere(const void **thermosphere);
static ini1_header_t *package2_rebuild_ini1(ini1_header_t *ini1, uint32_t target_firmware, void *emummc, size_t emummc_size);
static void package2_append_section(unsigned int id, package2_header_t *package2, void *data, size_t size);
static void package2_append_section(unsigned int id, package2_header_t *package2, const void *data, size_t size);
static void package2_fixup_thermosphere_and_entrypoint(package2_header_t *package2);
static void package2_fixup_header_and_section_hashes(package2_header_t *package2, size_t size);
static inline size_t align_to_4(size_t s) {
@@ -50,7 +51,7 @@ void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firm
void *kernel;
size_t kernel_size;
bool is_sd_kernel = false;
void *thermosphere;
const void *thermosphere;
size_t thermosphere_size;
ini1_header_t *orig_ini1, *rebuilt_ini1;
@@ -67,6 +68,8 @@ void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firm
fatal_error(u8"Error: Package2 has no unused section for Thermosphère!\n");
}
package2->metadata.section_offsets[PACKAGE2_SECTION_UNUSED] = 0; /* base of DRAM */
/* Load Kernel from SD, if possible. */
{
size_t sd_kernel_size = get_file_size("atmosphere/kernel.bin");
@@ -143,6 +146,9 @@ void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firm
package2_append_section(PACKAGE2_SECTION_INI1, rebuilt_package2, rebuilt_ini1, rebuilt_ini1->size);
package2_append_section(PACKAGE2_SECTION_UNUSED, rebuilt_package2, thermosphere, thermosphere_size);
/* Swap entrypoint if Thermosphère is present */
package2_fixup_thermosphere_and_entrypoint(rebuilt_package2);
/* Fix all necessary data in the header to accomodate for the new patches. */
package2_fixup_header_and_section_hashes(rebuilt_package2, rebuilt_package2_size);
@@ -327,12 +333,9 @@ static size_t package2_get_src_section(void **section, package2_header_t *packag
return package2->metadata.section_sizes[id];
}
static size_t package2_get_thermosphere(void **thermosphere) {
/*extern const uint8_t thermosphere_bin[];
extern const uint32_t thermosphere_bin_size;*/
/* TODO: enable when tested. */
(*thermosphere) = NULL;
return 0;
static size_t package2_get_thermosphere(const void **thermosphere) {
(*thermosphere) = thermosphere_bin;
return thermosphere_bin_size;
}
static ini1_header_t *package2_rebuild_ini1(ini1_header_t *ini1, uint32_t target_firmware, void *emummc, size_t emummc_size) {
@@ -353,7 +356,7 @@ static ini1_header_t *package2_rebuild_ini1(ini1_header_t *ini1, uint32_t target
return merged;
}
static void package2_append_section(unsigned int id, package2_header_t *package2, void *data, size_t size) {
static void package2_append_section(unsigned int id, package2_header_t *package2, const void *data, size_t size) {
/* This function must be called in ascending order of id. */
/* We assume that the loading address doesn't need to be changed. */
uint8_t *dst = package2->data;
@@ -365,6 +368,22 @@ static void package2_append_section(unsigned int id, package2_header_t *package2
package2->metadata.section_sizes[id] = align_to_4(size);
}
static void package2_fixup_thermosphere_and_entrypoint(package2_header_t *package2) {
/* Return if Thermosphère is not present */
if (package2->metadata.section_sizes[PACKAGE2_SECTION_UNUSED] == 0) {
return;
}
uint8_t *dst = package2->data;
for (unsigned int i = 0; i < PACKAGE2_SECTION_UNUSED; i++) {
dst += package2->metadata.section_sizes[i];
}
/* Swap kernel entrypoint with Thermosphère */
*(uint64_t *)(dst + 8) = DRAM_BASE_PHYSICAL + package2->metadata.entrypoint;
package2->metadata.entrypoint = 0;
}
static void package2_fixup_header_and_section_hashes(package2_header_t *package2, size_t size) {
uint8_t *data = package2->data;

View File

@@ -59,6 +59,7 @@
#define PACKAGE2_MINVER_1100_CURRENT 0x10
#define NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS ((void *)(0xA9800000ull))
#define DRAM_BASE_PHYSICAL (0x80000000)
typedef struct {
union {

View File

@@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
branch = master
commit = 90d85295bb9e7917475e3aee13fe20311d73df0e
parent = 63440cab18f4a87e602d44d127cb3f70b652d05b
commit = bc08912dd31bb172467add8e24b4f0adac431939
parent = 71add1add8521e0c2115ec612c514400ac7ba688
method = merge
cmdver = 0.4.1

View File

@@ -45,7 +45,21 @@
namespace ams::kern {
static_assert(cpu::NumCores <= static_cast<s32>(BITSIZEOF(u64)));
static_assert(util::size(cpu::VirtualToPhysicalCoreMap) == BITSIZEOF(u64));
namespace cpu {
static constexpr inline size_t NumVirtualCores = BITSIZEOF(u64);
static constexpr inline u64 VirtualCoreMask = [] {
u64 mask = 0;
for (size_t i = 0; i < NumVirtualCores; ++i) {
mask |= (UINT64_C(1) << i);
}
return mask;
}();
}
static_assert(cpu::NumCores <= cpu::NumVirtualCores);
static_assert(util::size(cpu::VirtualToPhysicalCoreMap) == cpu::NumVirtualCores);
}

View File

@@ -22,7 +22,7 @@ namespace ams::kern {
/* Most fields have already been cleared by our constructor. */
/* Initial processes may run on all cores. */
m_core_mask = (1ul << cpu::NumCores) - 1;
m_core_mask = cpu::VirtualCoreMask;
/* Initial processes may use any user priority they like. */
m_priority_mask = ~0xFul;
@@ -55,18 +55,17 @@ namespace ams::kern {
const auto max_prio = cap.Get<CorePriority::LowestThreadPriority>();
const auto min_prio = cap.Get<CorePriority::HighestThreadPriority>();
R_UNLESS(min_core <= max_core, svc::ResultInvalidCombination());
R_UNLESS(min_prio <= max_prio, svc::ResultInvalidCombination());
R_UNLESS(max_core < cpu::NumCores, svc::ResultInvalidCoreId());
R_UNLESS(min_core <= max_core, svc::ResultInvalidCombination());
R_UNLESS(min_prio <= max_prio, svc::ResultInvalidCombination());
R_UNLESS(max_core < cpu::NumVirtualCores, svc::ResultInvalidCoreId());
MESOSPHERE_ASSERT(max_core < BITSIZEOF(u64));
MESOSPHERE_ASSERT(max_prio < BITSIZEOF(u64));
/* Set core mask. */
for (auto core_id = min_core; core_id <= max_core; core_id++) {
m_core_mask |= (1ul << core_id);
}
MESOSPHERE_ASSERT((m_core_mask & ((1ul << cpu::NumCores) - 1)) == m_core_mask);
MESOSPHERE_ASSERT((m_core_mask & cpu::VirtualCoreMask) == m_core_mask);
/* Set priority mask. */
for (auto prio = min_prio; prio <= max_prio; prio++) {

View File

@@ -216,7 +216,7 @@ namespace ams::kern::svc {
case ams::svc::InfoType_ThreadTickCount:
{
/* Verify the requested core is valid. */
const bool core_valid = (info_subtype == static_cast<u64>(-1ul)) || (info_subtype < util::size(cpu::VirtualToPhysicalCoreMap));
const bool core_valid = (info_subtype == static_cast<u64>(-1ul)) || (info_subtype < cpu::NumVirtualCores);
R_UNLESS(core_valid, svc::ResultInvalidCombination());
/* Get the thread from its handle. */

View File

@@ -21,8 +21,8 @@ namespace ams::kern::svc {
namespace {
constexpr bool IsValidCoreId(int32_t core_id) {
return (0 <= core_id && core_id < static_cast<int32_t>(cpu::NumCores));
constexpr bool IsValidVirtualCoreId(int32_t core_id) {
return (0 <= core_id && core_id < static_cast<int32_t>(cpu::NumVirtualCores));
}
void ExitProcess() {
@@ -275,7 +275,7 @@ namespace ams::kern::svc {
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
/* Validate the core id. */
R_UNLESS(IsValidCoreId(core_id), svc::ResultInvalidCoreId());
R_UNLESS(IsValidVirtualCoreId(core_id), svc::ResultInvalidCoreId());
R_UNLESS(((1ul << core_id) & process->GetCoreMask()) != 0, svc::ResultInvalidCoreId());
/* Validate the priority. */

View File

@@ -21,8 +21,8 @@ namespace ams::kern::svc {
namespace {
constexpr bool IsValidCoreId(int32_t core_id) {
return (0 <= core_id && core_id < static_cast<int32_t>(cpu::NumCores));
constexpr bool IsValidVirtualCoreId(int32_t core_id) {
return (0 <= core_id && core_id < static_cast<int32_t>(cpu::NumVirtualCores));
}
Result CreateThread(ams::svc::Handle *out, ams::svc::ThreadFunc f, uintptr_t arg, uintptr_t stack_bottom, int32_t priority, int32_t core_id) {
@@ -33,7 +33,7 @@ namespace ams::kern::svc {
}
/* Validate arguments. */
R_UNLESS(IsValidCoreId(core_id), svc::ResultInvalidCoreId());
R_UNLESS(IsValidVirtualCoreId(core_id), svc::ResultInvalidCoreId());
R_UNLESS(((1ul << core_id) & process.GetCoreMask()) != 0, svc::ResultInvalidCoreId());
R_UNLESS(ams::svc::HighestThreadPriority <= priority && priority <= ams::svc::LowestThreadPriority, svc::ResultInvalidPriority());
@@ -168,7 +168,7 @@ namespace ams::kern::svc {
R_UNLESS(affinity_mask != 0, svc::ResultInvalidCombination());
/* Validate the core id. */
if (IsValidCoreId(core_id)) {
if (IsValidVirtualCoreId(core_id)) {
R_UNLESS(((1ul << core_id) & affinity_mask) != 0, svc::ResultInvalidCombination());
} else {
R_UNLESS(core_id == ams::svc::IdealCoreNoUpdate || core_id == ams::svc::IdealCoreDontCare, svc::ResultInvalidCoreId());

View File

@@ -79,6 +79,7 @@
#include <stratosphere/spl.hpp>
#include <stratosphere/time.hpp>
#include <stratosphere/updater.hpp>
#include <stratosphere/usb.hpp>
#include <stratosphere/wec.hpp>
/* Include FS last. */

View File

@@ -19,7 +19,7 @@
namespace ams::psc {
enum PmModuleId : u16 {
enum PmModuleId : u32 {
PmModuleId_Usb = 4,
PmModuleId_Ethernet = 5,
PmModuleId_Fgm = 6,

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/usb/usb_limits.hpp>
#include <stratosphere/usb/usb_types.hpp>
#include <stratosphere/usb/usb_device_types.hpp>
#include <stratosphere/usb/usb_device.hpp>

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/usb/usb_limits.hpp>
#include <stratosphere/usb/usb_types.hpp>
#include <stratosphere/usb/usb_device_types.hpp>
#define AMS_USB_I_DS_ENDPOINT_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 0, Result, PostBufferAsync, (sf::Out<u32> out_urb_id, u64 address, u32 size), (out_urb_id, address, size)) \
AMS_SF_METHOD_INFO(C, H, 1, Result, Cancel, (), ()) \
AMS_SF_METHOD_INFO(C, H, 2, Result, GetCompletionEvent, (sf::OutCopyHandle out), (out)) \
AMS_SF_METHOD_INFO(C, H, 3, Result, GetUrbReport, (sf::Out<usb::UrbReport> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 4, Result, Stall, (), ()) \
AMS_SF_METHOD_INFO(C, H, 5, Result, SetZlt, (bool zlt), (zlt))
/* TODO: Deprecated interface? */
AMS_SF_DEFINE_INTERFACE(ams::usb::ds, IDsEndpoint, AMS_USB_I_DS_ENDPOINT_INTERFACE_INFO)

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/usb/usb_limits.hpp>
#include <stratosphere/usb/usb_types.hpp>
#include <stratosphere/usb/usb_device_types.hpp>
#include <stratosphere/usb/ds/usb_i_ds_endpoint.hpp>
#define AMS_USB_I_DS_INTERFACE_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 0, Result, RegisterEndpoint, (u8 endpoint_address, sf::Out<sf::SharedPointer<usb::ds::IDsEndpoint>> out), (endpoint_address, out)) \
AMS_SF_METHOD_INFO(C, H, 1, Result, GetSetupEvent, (sf::OutCopyHandle out), (out)) \
AMS_SF_METHOD_INFO(C, H, 2, Result, GetSetupPacket, (const sf::OutBuffer &out), (out)) \
AMS_SF_METHOD_INFO(C, H, 3, Result, CtrlInAsync, (sf::Out<u32> out_urb_id, u64 address, u32 size), (out_urb_id, address, size)) \
AMS_SF_METHOD_INFO(C, H, 4, Result, CtrlOutAsync, (sf::Out<u32> out_urb_id, u64 address, u32 size), (out_urb_id, address, size)) \
AMS_SF_METHOD_INFO(C, H, 5, Result, GetCtrlInCompletionEvent, (sf::OutCopyHandle out), (out)) \
AMS_SF_METHOD_INFO(C, H, 6, Result, GetCtrlInUrbReport, (sf::Out<usb::UrbReport> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 7, Result, GetCtrlOutCompletionEvent, (sf::OutCopyHandle out), (out)) \
AMS_SF_METHOD_INFO(C, H, 8, Result, GetCtrlOutUrbReport, (sf::Out<usb::UrbReport> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 9, Result, CtrlStall, (), ()) \
AMS_SF_METHOD_INFO(C, H, 10, Result, AppendConfigurationData, (u8 bInterfaceNumber, usb::UsbDeviceSpeed device_speed, const sf::InBuffer &data), (bInterfaceNumber, device_speed, data)) \
AMS_SF_METHOD_INFO(C, H, 65000, Result, Enable, (), ()) \
AMS_SF_METHOD_INFO(C, H, 65001, Result, Disable, (), ())
/* TODO: Deprecated interface? */
AMS_SF_DEFINE_INTERFACE(ams::usb::ds, IDsInterface, AMS_USB_I_DS_INTERFACE_INTERFACE_INFO)

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/usb/usb_limits.hpp>
#include <stratosphere/usb/usb_types.hpp>
#include <stratosphere/usb/usb_device_types.hpp>
#include <stratosphere/usb/ds/usb_i_ds_interface.hpp>
#define AMS_USB_I_DS_SERVICE_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 0, Result, Bind, (usb::ComplexId complex_id, sf::CopyHandle process_h), (complex_id, process_h)) \
AMS_SF_METHOD_INFO(C, H, 1, Result, RegisterInterface, (sf::Out<sf::SharedPointer<usb::ds::IDsInterface>> out, u8 bInterfaceNumber), (out, bInterfaceNumber)) \
AMS_SF_METHOD_INFO(C, H, 2, Result, GetStateChangeEvent, (sf::OutCopyHandle out), (out)) \
AMS_SF_METHOD_INFO(C, H, 3, Result, GetState, (sf::Out<usb::UsbState> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 4, Result, ClearDeviceData, (), ()) \
AMS_SF_METHOD_INFO(C, H, 5, Result, AddUsbStringDescriptor, (sf::Out<u8> out, const sf::InBuffer &desc), (out, desc)) \
AMS_SF_METHOD_INFO(C, H, 6, Result, DeleteUsbStringDescriptor, (u8 index), (index)) \
AMS_SF_METHOD_INFO(C, H, 7, Result, SetUsbDeviceDescriptor, (const sf::InBuffer &desc, usb::UsbDeviceSpeed speed), (desc, speed)) \
AMS_SF_METHOD_INFO(C, H, 8, Result, SetBinaryObjectStore, (const sf::InBuffer &bos), (bos)) \
AMS_SF_METHOD_INFO(C, H, 9, Result, Enable, (), ()) \
AMS_SF_METHOD_INFO(C, H, 10, Result, Disable, (), ())
/* TODO: Deprecated interface? */
AMS_SF_DEFINE_INTERFACE(ams::usb::ds, IDsService, AMS_USB_I_DS_SERVICE_INTERFACE_INFO)
#define AMS_USB_I_DS_ROOT_SERVICE_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 0, Result, GetService, (sf::Out<sf::SharedPointer<usb::ds::IDsService>> out), (out))
AMS_SF_DEFINE_INTERFACE(ams::usb::ds, IDsRootService, AMS_USB_I_DS_ROOT_SERVICE_INTERFACE_INFO)

View File

@@ -0,0 +1,145 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/os_system_event.hpp>
#include <stratosphere/sf/sf_lmem_utility.hpp>
#include <stratosphere/usb/usb_limits.hpp>
#include <stratosphere/usb/usb_device_types.hpp>
#include <stratosphere/usb/ds/usb_i_ds_service.hpp>
namespace ams::usb {
class DsInterface;
class DsEndpoint;
class DsClient {
friend class DsInterface;
friend class DsEndpoint;
private:
/* NOTE: Nintendo uses a UnitHeap here on newer firmware versions. */
/* For now, we'll use an ExpHeap and do it the old way. */
sf::ExpHeapAllocator m_allocator{};
u8 m_heap_buffer[32_KB];
lmem::HeapHandle m_heap_handle{};
sf::SharedPointer<ds::IDsRootService> m_root_service{};
sf::SharedPointer<ds::IDsService> m_ds_service{};
bool m_is_initialized{false};
std::atomic<int> m_reference_count{0};
os::SystemEventType m_state_change_event{};
DsInterface *m_interfaces[DsLimitMaxInterfacesPerConfigurationCount]{};
bool m_is_enabled{false};
public:
DsClient() = default;
~DsClient() { /* ... */ }
public:
Result Initialize(ComplexId complex_id);
Result Finalize();
bool IsInitialized();
Result EnableDevice();
Result DisableDevice();
os::SystemEventType *GetStateChangeEvent();
Result GetState(UsbState *out);
Result ClearDeviceData();
Result AddUsbStringDescriptor(u8 *out_index, UsbStringDescriptor *desc);
Result DeleteUsbStringDescriptor(u8 index);
Result SetUsbDeviceDescriptor(UsbDeviceDescriptor *desc, UsbDeviceSpeed speed);
Result SetBinaryObjectStore(u8 *data, int size);
private:
Result AddInterface(DsInterface *intf, sf::SharedPointer<ds::IDsInterface> *out_srv, uint8_t bInterfaceNumber);
Result DeleteInterface(uint8_t bInterfaceNumber);
};
class DsInterface {
friend class DsEndpoint;
private:
DsClient *m_client;
sf::SharedPointer<ds::IDsInterface> m_interface;
bool m_is_initialized;
std::atomic<int> m_reference_count;
os::SystemEventType m_setup_event;
os::SystemEventType m_ctrl_in_completion_event;
os::SystemEventType m_ctrl_out_completion_event;
UrbReport m_report;
u8 m_interface_num;
DsEndpoint *m_endpoints[UsbLimitMaxEndpointsCount];
public:
DsInterface() : m_client(nullptr), m_is_initialized(false), m_reference_count(0) { /* ... */ }
~DsInterface() { /* ... */ }
public:
Result Initialize(DsClient *client, u8 bInterfaceNumber);
Result Finalize();
Result AppendConfigurationData(UsbDeviceSpeed speed, void *data, u32 size);
bool IsInitialized();
os::SystemEventType *GetSetupEvent();
Result GetSetupPacket(UsbCtrlRequest *out);
Result Enable();
Result Disable();
Result CtrlRead(u32 *out_transferred, void *dst, u32 size);
Result CtrlWrite(u32 *out_transferred, void *dst, u32 size);
Result CtrlDone();
Result CtrlStall();
private:
Result AddEndpoint(DsEndpoint *ep, u8 bEndpointAddress, sf::SharedPointer<ds::IDsEndpoint> *out);
Result DeleteEndpoint(u8 bEndpointAddress);
Result CtrlIn(u32 *out_transferred, void *dst, u32 size);
Result CtrlOut(u32 *out_transferred, void *dst, u32 size);
};
class DsEndpoint {
private:
bool m_is_initialized;
bool m_is_new_format;
std::atomic<int> m_reference_count;
DsInterface *m_interface;
sf::SharedPointer<ds::IDsEndpoint> m_endpoint;
u8 m_address;
os::SystemEventType m_completion_event;
os::SystemEventType m_unknown_event;
public:
DsEndpoint() : m_is_initialized(false), m_is_new_format(false), m_reference_count(0) { /* ... */ }
public:
Result Initialize(DsInterface *interface, u8 bEndpointAddress);
Result Finalize();
bool IsInitialized();
Result PostBuffer(u32 *out_transferred, void *buf, u32 size);
Result PostBufferAsync(u32 *out_urb_id, void *buf, u32 size);
os::SystemEventType *GetCompletionEvent();
Result GetUrbReport(UrbReport *out);
Result Cancel();
Result SetZeroLengthTransfer(bool zlt);
};
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/usb/usb_limits.hpp>
#include <stratosphere/usb/usb_types.hpp>
namespace ams::usb {
constexpr inline u8 InterfaceNumberAuto = DsLimitMaxInterfacesPerConfigurationCount;
constexpr inline u8 EndpointAddressAutoIn = UsbEndpointAddressMask_DirDevicetoHost;
constexpr inline u8 EndpointAddressAutoOut = UsbEndpointAddressMask_DirHostToDevice;
enum UrbStatus {
UrbStatus_Invalid = 0,
UrbStatus_Pending = 1,
UrbStatus_Running = 2,
UrbStatus_Finished = 3,
UrbStatus_Cancelled = 4,
UrbStatus_Failed = 5,
};
struct UrbReport {
struct Report {
u32 id;
u32 requested_size;
u32 transferred_size;
UrbStatus status;
} reports[DsLimitRingSize];
u32 count;
};
enum DsString {
DsString_Max = 0x20,
};
struct DsVidPidBcd {
uint16_t idVendor;
uint16_t idProduct;
uint16_t bcdDevice;
char manufacturer[DsString_Max];
char product[DsString_Max];
char serial_number[DsString_Max];
};
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/dd/dd_device_address_space_common.hpp>
namespace ams::usb {
constexpr inline int HwLimitDmaBufferAlignmentSize = dd::DeviceAddressSpaceMemoryRegionAlignment;
constexpr inline int HwLimitDataCacheLineSize = 0x40;
constexpr inline int HwLimitMaxPortCount = 0x4;
constexpr inline int UsbLimitMaxEndpointsCount = 0x20;
constexpr inline int UsbLimitMaxEndpointPairCount = 0x10;
constexpr inline int DsLimitMaxConfigurationsPerDeviceCount = 1;
constexpr inline int DsLimitMaxInterfacesPerConfigurationCount = 4;
constexpr inline int DsLimitMaxNameSize = 0x40;
constexpr inline int DsLimitRingSize = 8;
}

View File

@@ -0,0 +1,223 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/usb/usb_limits.hpp>
namespace ams::usb {
constexpr ALWAYS_INLINE bool IsDmaAligned(u64 address) {
return util::IsAligned(address, static_cast<u64>(HwLimitDmaBufferAlignmentSize));
}
enum ComplexId {
ComplexId_Tegra21x = 2,
};
enum UsbDescriptorType {
UsbDescriptorType_Device = 1,
UsbDescriptorType_Config = 2,
UsbDescriptorType_String = 3,
UsbDescriptorType_Interface = 4,
UsbDescriptorType_Endpoint = 5,
UsbDescriptorType_DeviceQualifier = 6,
UsbDescriptorType_OtherSpeedConfig = 7,
UsbDescriptorType_InterfacePower = 8,
UsbDescriptorType_Otg = 9,
UsbDescriptorType_Debug = 10,
UsbDescriptorType_InterfaceAssociation = 11,
UsbDescriptorType_Bos = 15,
UsbDescriptorType_DeviceCapability = 16,
UsbDescriptorType_Hid = 33,
UsbDescriptorType_Report = 34,
UsbDescriptorType_Physical = 35,
UsbDescriptorType_Hub = 41,
UsbDescriptorType_EndpointCompanion = 48,
UsbDescriptorType_IsocEndpointCompanion = 49,
};
struct UsbDescriptorHeader {
uint8_t bLength;
uint8_t bDescriptorType;
} PACKED;
struct UsbInterfaceDescriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bInterfaceNumber;
uint8_t bAlternateSetting;
uint8_t bNumEndpoints;
uint8_t bInterfaceClass;
uint8_t bInterfaceSubClass;
uint8_t bInterfaceProtocol;
uint8_t iInterface;
} PACKED;
struct UsbEndpointDescriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bEndpointAddress;
uint8_t bmAttributes;
uint16_t wMaxPacketSize;
uint8_t bInterval;
} PACKED;
struct UsbDeviceDescriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bcdUSB;
uint8_t bDeviceClass;
uint8_t bDeviceSubClass;
uint8_t bDeviceProtocol;
uint8_t bMaxPacketSize0;
uint16_t idVendor;
uint16_t idProduct;
uint16_t bcdDevice;
uint8_t iManufacturer;
uint8_t iProduct;
uint8_t iSerialNumber;
uint8_t bNumConfigurations;
} PACKED;
struct UsbConfigDescriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t wTotalLength;
uint8_t bNumInterfaces;
uint8_t bConfigurationValue;
uint8_t iConfiguration;
uint8_t bmAttributes;
uint8_t bMaxPower;
} PACKED;
struct UsbEndpointCompanionDescriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bMaxBurst;
uint8_t bmAttributes;
uint16_t wBytesPerInterval;
} PACKED;
struct UsbStringDescriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t wData[DsLimitMaxNameSize];
} PACKED;
struct UsbCtrlRequest {
uint8_t bmRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
} PACKED;
enum UsbState {
UsbState_Detached = 0,
UsbState_Attached = 1,
UsbState_Powered = 2,
UsbState_Default = 3,
UsbState_Address = 4,
UsbState_Configured = 5,
UsbState_Suspended = 6,
};
enum UsbDescriptorSize {
UsbDescriptorSize_Interface = sizeof(UsbInterfaceDescriptor),
UsbDescriptorSize_Endpoint = sizeof(UsbEndpointDescriptor),
UsbDescriptorSize_Device = sizeof(UsbDeviceDescriptor),
UsbDescriptorSize_Config = sizeof(UsbConfigDescriptor),
UsbDescriptorSize_EndpointCompanion = sizeof(UsbEndpointCompanionDescriptor),
};
enum UsbDeviceSpeed {
UsbDeviceSpeed_Invalid = 0,
UsbDeviceSpeed_Low = 1,
UsbDeviceSpeed_Full = 2,
UsbDeviceSpeed_High = 3,
UsbDeviceSpeed_Super = 4,
UsbDeviceSpeed_SuperPlus = 5,
};
enum UsbEndpointAddressMask {
UsbEndpointAddressMask_EndpointNumber = (0xF << 0),
UsbEndpointAddressMask_Dir = (0x1 << 7),
UsbEndpointAddressMask_DirHostToDevice = (0x0 << 7),
UsbEndpointAddressMask_DirDevicetoHost = (0x1 << 7),
};
enum UsbEndpointAttributeMask {
UsbEndpointAttributeMask_XferType = (0x3 << 0),
UsbEndpointAttributeMask_XferTypeControl = (0x0 << 0),
UsbEndpointAttributeMask_XferTypeIsoc = (0x1 << 0),
UsbEndpointAttributeMask_XferTypeBulk = (0x2 << 0),
UsbEndpointAttributeMask_XferTypeInt = (0x3 << 0),
};
enum UsbEndpointDirection {
UsbEndpointDirection_Invalid = 0,
UsbEndpointDirection_ToDevice = 1,
UsbEndpointDirection_ToHost = 2,
UsbEndpointDirection_Control = 3,
};
constexpr inline u8 UsbGetEndpointNumber(const UsbEndpointDescriptor *desc) {
return desc->bEndpointAddress & UsbEndpointAddressMask_EndpointNumber;
}
constexpr inline bool UsbEndpointIsHostToDevice(const UsbEndpointDescriptor *desc) {
return (desc->bEndpointAddress & UsbEndpointAddressMask_Dir) == UsbEndpointAddressMask_DirHostToDevice;
}
constexpr inline bool UsbEndpointIsDeviceToHost(const UsbEndpointDescriptor *desc) {
return (desc->bEndpointAddress & UsbEndpointAddressMask_Dir) == UsbEndpointAddressMask_DirDevicetoHost;
}
constexpr inline u8 UsbGetEndpointAddress(u8 number, UsbEndpointDirection dir) {
u8 val = static_cast<u8>(number & UsbEndpointAddressMask_EndpointNumber);
if (dir == UsbEndpointDirection_ToHost) {
val |= UsbEndpointAddressMask_DirDevicetoHost;
} else {
val |= UsbEndpointAddressMask_DirHostToDevice;
}
return val;
}
constexpr inline UsbEndpointDirection GetUsbEndpointDirection(const UsbEndpointDescriptor *desc) {
if (UsbEndpointIsDeviceToHost(desc)) {
return UsbEndpointDirection_ToHost;
} else {
return UsbEndpointDirection_ToDevice;
}
}
constexpr inline bool UsbEndpointIsValid(const UsbEndpointDescriptor *desc) {
return desc != nullptr && desc->bLength >= UsbDescriptorSize_Endpoint && desc->bEndpointAddress != 0;
}
constexpr inline void UsbMarkEndpointInvalid(UsbEndpointDescriptor *desc) {
desc->bLength = 0;
desc->bEndpointAddress = 0;
}
}

View File

@@ -40,14 +40,12 @@ namespace ams::hid {
/* Helper. */
void InitializeHid() {
R_ABORT_UNLESS(smInitialize());
ON_SCOPE_EXIT { smExit(); };
{
sm::DoWithSession([&]() {
R_ABORT_UNLESS(hidInitialize());
hidInitializeNpad();
R_ABORT_UNLESS(hidSetSupportedNpadIdType(NpadIdTypes, NumNpadIdTypes));
R_ABORT_UNLESS(hidSetSupportedNpadStyleSet(HidNpadStyleSet_NpadStandard | HidNpadStyleTag_NpadSystemExt));
}
});
}
Result EnsureHidInitialized() {

View File

@@ -26,7 +26,7 @@ namespace ams::ncm {
MakeContentPathFunction make_content_path_func;
bool disabled;
protected:
ContentStorageImplBase() { /* ... */ }
ContentStorageImplBase() : make_content_path_func(), disabled(false) { /* ... */ }
protected:
/* Helpers. */
Result EnsureEnabled() const {

View File

@@ -46,9 +46,9 @@ namespace ams::psc {
Result PmModule::Initialize(const PmModuleId mid, const PmModuleId *dependencies, u32 dependency_count, os::EventClearMode clear_mode) {
R_UNLESS(!this->initialized, psc::ResultAlreadyInitialized());
static_assert(sizeof(*dependencies) == sizeof(u16));
static_assert(sizeof(*dependencies) == sizeof(u32));
::PscPmModule module;
R_TRY(::pscmGetPmModule(std::addressof(module), static_cast<::PscPmModuleId>(mid), reinterpret_cast<const u16 *>(dependencies), dependency_count, clear_mode == os::EventClearMode_AutoClear));
R_TRY(::pscmGetPmModule(std::addressof(module), static_cast<::PscPmModuleId>(mid), reinterpret_cast<const u32 *>(dependencies), dependency_count, clear_mode == os::EventClearMode_AutoClear));
this->intf = RemoteObjectFactory::CreateSharedEmplaced<psc::sf::IPmModule, RemotePmModule>(module);
this->system_event.AttachReadableHandle(module.event.revent, false, clear_mode);

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::usb::impl {
constexpr int GetEndpointIndex(u8 address) {
int idx = address & UsbEndpointAddressMask_EndpointNumber;
if ((address & UsbEndpointAddressMask_Dir) == UsbEndpointAddressMask_DirDevicetoHost) {
idx += 0x10;
}
return idx;
}
template<typename T>
class ScopedRefCount {
NON_COPYABLE(ScopedRefCount);
NON_MOVEABLE(ScopedRefCount);
private:
T &m_obj;
public:
ALWAYS_INLINE ScopedRefCount(T &o) : m_obj(o) {
++m_obj;
}
ALWAYS_INLINE ~ScopedRefCount() {
--m_obj;
}
};
}

View File

@@ -0,0 +1,803 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "usb_remote_ds_root_service.hpp"
#include "usb_remote_ds_service.hpp"
#include "impl/usb_util.hpp"
namespace ams::usb {
Result DsClient::Initialize(ComplexId complex_id) {
/* Clear interfaces. */
for (size_t i = 0; i < util::size(m_interfaces); ++i) {
m_interfaces[i] = nullptr;
}
/* Initialize heap. */
m_heap_handle = lmem::CreateExpHeap(m_heap_buffer, sizeof(m_heap_buffer), lmem::CreateOption_None);
R_UNLESS(m_heap_handle != nullptr, usb::ResultMemAllocFailure());
/* Attach our allocator. */
m_allocator.Attach(m_heap_handle);
/* Connect to usb:ds. */
/* NOTE: Here, Nintendo does m_domain.InitializeByDomain<...>(...); m_domain.SetSessionCount(1); */
{
Service srv;
R_TRY(sm::GetService(std::addressof(srv), sm::ServiceName::Encode("usb:ds")));
R_ABORT_UNLESS(serviceConvertToDomain(std::addressof(srv)));
using Allocator = decltype(m_allocator);
using ObjectFactory = sf::ObjectFactory<Allocator::Policy>;
if (hos::GetVersion() >= hos::Version_11_0_0) {
m_root_service = ObjectFactory::CreateSharedEmplaced<ds::IDsRootService, RemoteDsRootService>(std::addressof(m_allocator), srv, std::addressof(m_allocator));
R_TRY(m_root_service->GetService(std::addressof(m_ds_service)));
} else {
m_ds_service = ObjectFactory::CreateSharedEmplaced<ds::IDsService, RemoteDsService>(std::addressof(m_allocator), srv, std::addressof(m_allocator));
}
}
/* Bind the client process. */
R_TRY(m_ds_service->Bind(complex_id, dd::GetCurrentProcessHandle()));
/* Get the state change event. */
sf::CopyHandle event_handle;
R_TRY(m_ds_service->GetStateChangeEvent(std::addressof(event_handle)));
/* Attach the state change event handle to our event. */
os::AttachReadableHandleToSystemEvent(std::addressof(m_state_change_event), event_handle.GetValue(), true, os::EventClearMode_ManualClear);
/* Mark ourselves as initialized. */
m_is_initialized = true;
return ResultSuccess();
}
Result DsClient::Finalize() {
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
/* Check that we're initialized. */
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
/* Disable and finalize all interfaces. */
R_TRY(this->DisableDevice());
for (size_t i = 0; i < util::size(m_interfaces); ++i) {
if (m_interfaces[i] != nullptr) {
R_TRY(m_interfaces[i]->Finalize());
}
}
/* Check our reference count .*/
R_UNLESS(m_reference_count <= 1, usb::ResultResourceBusy());
/* Finalize members. */
m_is_initialized = false;
os::DestroySystemEvent(std::addressof(m_state_change_event));
lmem::DestroyExpHeap(m_heap_handle);
m_heap_handle = nullptr;
/* Destroy interface objects. */
m_ds_service = nullptr;
m_root_service = nullptr;
return ResultSuccess();
}
bool DsClient::IsInitialized() {
return m_is_initialized;
}
Result DsClient::EnableDevice() {
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
/* Check that we're initialized. */
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
/* Enable all interfaces. */
if (hos::GetVersion() < hos::Version_11_0_0) {
for (size_t i = 0; i < util::size(m_interfaces); ++i) {
if (m_interfaces[i] != nullptr) {
R_TRY(m_interfaces[i]->Enable());
}
}
}
/* Enable the device. */
R_TRY(m_ds_service->Enable());
/* Mark disabled. */
m_is_enabled = true;
return ResultSuccess();
}
Result DsClient::DisableDevice() {
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
/* Check that we're initialized. */
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
/* Disable the device. */
R_TRY(m_ds_service->Disable());
/* Disable all interfaces. */
if (hos::GetVersion() < hos::Version_11_0_0) {
for (size_t i = 0; i < util::size(m_interfaces); ++i) {
if (m_interfaces[i] != nullptr) {
R_TRY(m_interfaces[i]->Disable());
}
}
}
/* Mark disabled. */
m_is_enabled = false;
return ResultSuccess();
}
os::SystemEventType *DsClient::GetStateChangeEvent() {
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
return m_is_initialized ? std::addressof(m_state_change_event) : nullptr;
}
Result DsClient::GetState(UsbState *out) {
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
/* Check that we're initialized. */
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
/* Check that we have a service. */
AMS_ABORT_UNLESS(m_ds_service != nullptr);
return m_ds_service->GetState(out);
}
Result DsClient::ClearDeviceData() {
return m_ds_service->ClearDeviceData();
}
Result DsClient::AddUsbStringDescriptor(u8 *out_index, UsbStringDescriptor *desc) {
return m_ds_service->AddUsbStringDescriptor(out_index, sf::InBuffer(reinterpret_cast<const u8 *>(desc), sizeof(*desc)));
}
Result DsClient::DeleteUsbStringDescriptor(u8 index) {
return m_ds_service->DeleteUsbStringDescriptor(index);
}
Result DsClient::SetUsbDeviceDescriptor(UsbDeviceDescriptor *desc, UsbDeviceSpeed speed) {
return m_ds_service->SetUsbDeviceDescriptor(sf::InBuffer(reinterpret_cast<const u8 *>(desc), sizeof(*desc)), speed);
}
Result DsClient::SetBinaryObjectStore(u8 *data, int size) {
return m_ds_service->SetBinaryObjectStore(sf::InBuffer(reinterpret_cast<const u8 *>(data), size));
}
Result DsClient::AddInterface(DsInterface *intf, sf::SharedPointer<ds::IDsInterface> *out_srv, uint8_t bInterfaceNumber) {
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
/* Check that we're initialized. */
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
/* Check that we have a service. */
AMS_ABORT_UNLESS(m_ds_service != nullptr);
/* Register the interface. */
R_TRY(m_ds_service->RegisterInterface(out_srv, bInterfaceNumber));
/* Set interface. */
m_interfaces[bInterfaceNumber] = intf;
return ResultSuccess();
}
Result DsClient::DeleteInterface(uint8_t bInterfaceNumber) {
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
/* Check that we're initialized. */
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
/* Check that we have the interface. */
R_UNLESS(m_interfaces[bInterfaceNumber] != nullptr, usb::ResultOperationDenied());
/* Clear the interface. */
m_interfaces[bInterfaceNumber] = nullptr;
return ResultSuccess();
}
Result DsInterface::Initialize(DsClient *client, u8 bInterfaceNumber) {
/* Check that we haven't already initialized. */
R_UNLESS(!m_is_initialized, usb::ResultAlreadyInitialized());
/* Set our client. */
m_client = client;
/* Clear all endpoints. */
for (size_t i = 0; i < util::size(m_endpoints); ++i) {
m_endpoints[i] = nullptr;
}
/* Set our interface number. */
R_UNLESS(bInterfaceNumber < util::size(m_client->m_interfaces), usb::ResultInvalidParameter());
m_interface_num = bInterfaceNumber;
/* Add the interface. */
R_TRY(m_client->AddInterface(this, std::addressof(m_interface), m_interface_num));
/* Ensure we cleanup if we fail after this. */
auto intf_guard = SCOPE_GUARD { m_client->DeleteInterface(m_interface_num); m_interface = nullptr; };
/* Get events. */
sf::CopyHandle setup_event_handle;
sf::CopyHandle ctrl_in_event_handle;
sf::CopyHandle ctrl_out_event_handle;
R_TRY(m_interface->GetSetupEvent(std::addressof(setup_event_handle)));
R_TRY(m_interface->GetCtrlInCompletionEvent(std::addressof(ctrl_in_event_handle)));
R_TRY(m_interface->GetCtrlOutCompletionEvent(std::addressof(ctrl_out_event_handle)));
/* Attach events. */
os::AttachReadableHandleToSystemEvent(std::addressof(m_setup_event), setup_event_handle.GetValue(), true, os::EventClearMode_ManualClear);
os::AttachReadableHandleToSystemEvent(std::addressof(m_ctrl_in_completion_event), ctrl_in_event_handle.GetValue(), true, os::EventClearMode_ManualClear);
os::AttachReadableHandleToSystemEvent(std::addressof(m_ctrl_out_completion_event), ctrl_out_event_handle.GetValue(), true, os::EventClearMode_ManualClear);
/* Increment our client's reference count. */
++m_client->m_reference_count;
/* Set ourselves as initialized. */
m_is_initialized = true;
intf_guard.Cancel();
return ResultSuccess();
}
Result DsInterface::Finalize() {
/* Validate that we have a service. */
R_ABORT_UNLESS(m_interface != nullptr);
/* We must be disabled. */
R_UNLESS(!m_client->m_is_enabled, usb::ResultResourceBusy());
/* Finalize all endpoints. */
for (size_t i = 0; i < util::size(m_endpoints); ++i) {
if (m_endpoints[i] != nullptr) {
R_TRY(m_endpoints[i]->Finalize());
}
}
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
/* Check that we're initialized. */
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
/* Check our reference count .*/
R_UNLESS(m_reference_count <= 1, usb::ResultResourceBusy());
/* Finalize members. */
m_is_initialized = false;
os::DestroySystemEvent(std::addressof(m_setup_event));
os::DestroySystemEvent(std::addressof(m_ctrl_in_completion_event));
os::DestroySystemEvent(std::addressof(m_ctrl_out_completion_event));
/* Delete ourselves from our cleint. */
m_client->DeleteInterface(m_interface_num);
/* Destroy our service. */
m_interface = nullptr;
/* Close our reference to our client. */
--m_client->m_reference_count;
m_client = nullptr;
return ResultSuccess();
}
Result DsInterface::AppendConfigurationData(UsbDeviceSpeed speed, void *data, u32 size) {
return m_interface->AppendConfigurationData(m_interface_num, speed, sf::InBuffer(data, size));
}
bool DsInterface::IsInitialized() {
return m_is_initialized;
}
os::SystemEventType *DsInterface::GetSetupEvent() {
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
return m_is_initialized ? std::addressof(m_setup_event) : nullptr;
}
Result DsInterface::GetSetupPacket(UsbCtrlRequest *out) {
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
/* Check that we're initialized. */
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
/* Check that we have a service. */
AMS_ABORT_UNLESS(m_interface != nullptr);
return m_interface->GetSetupPacket(sf::OutBuffer(out, sizeof(*out)));
}
Result DsInterface::Enable() {
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
/* Check that we're initialized. */
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
/* If we're already enabled, nothing to do. */
R_SUCCEED_IF(m_client->m_is_enabled);
/* Check that we have a service. */
AMS_ABORT_UNLESS(m_interface != nullptr);
/* Perform the enable. */
R_TRY(m_interface->Enable());
return ResultSuccess();
}
Result DsInterface::Disable() {
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
/* Check that we're initialized. */
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
/* If we're already disabled, nothing to do. */
R_SUCCEED_IF(!m_client->m_is_enabled);
/* Check that we have a service. */
AMS_ABORT_UNLESS(m_interface != nullptr);
/* Perform the disable. */
R_TRY(m_interface->Disable());
return ResultSuccess();
}
Result DsInterface::AddEndpoint(DsEndpoint *ep, u8 bEndpointAddress, sf::SharedPointer<ds::IDsEndpoint> *out) {
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
/* Check that we're initialized. */
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
/* Check that we're not already enabled. */
R_UNLESS(!m_client->m_is_enabled, usb::ResultOperationDenied());
/* Check that we have a service. */
AMS_ABORT_UNLESS(m_interface != nullptr);
/* Register the endpoint. */
R_TRY(m_interface->RegisterEndpoint(bEndpointAddress, out));
/* Set the endpoint. */
m_endpoints[impl::GetEndpointIndex(bEndpointAddress)] = ep;
return ResultSuccess();
}
Result DsInterface::DeleteEndpoint(u8 bEndpointAddress) {
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
/* Check that we're initialized. */
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
/* Check that we're disabled and have the endpoint. */
const auto index = impl::GetEndpointIndex(bEndpointAddress);
R_UNLESS(!m_client->m_is_enabled, usb::ResultOperationDenied());
R_UNLESS(m_endpoints[index] != nullptr, usb::ResultOperationDenied());
/* Clear the endpoint. */
m_endpoints[index] = nullptr;
return ResultSuccess();
}
Result DsInterface::CtrlIn(u32 *out_transferred, void *dst, u32 size) {
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
/* Check that we're initialized. */
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
/* Check that we're enabled. */
R_UNLESS(m_client->m_is_enabled, usb::ResultOperationDenied());
/* Check that the data is aligned. */
R_UNLESS(usb::IsDmaAligned(reinterpret_cast<u64>(dst)), usb::ResultAlignmentError());
/* If we should, flush cache. */
if (size != 0) {
dd::FlushDataCache(dst, size);
}
/* Check that we have a service. */
AMS_ABORT_UNLESS(m_interface != nullptr);
/* Perform the transfer. */
u32 urb_id;
R_TRY(m_interface->CtrlInAsync(std::addressof(urb_id), reinterpret_cast<u64>(dst), size));
/* Wait for control to finish. */
os::WaitSystemEvent(std::addressof(m_ctrl_in_completion_event));
os::ClearSystemEvent(std::addressof(m_ctrl_in_completion_event));
/* Get the urb report. */
R_ABORT_UNLESS(m_interface->GetCtrlInUrbReport(std::addressof(m_report)));
/* Check the report is for our urb. */
R_UNLESS(m_report.count == 1, usb::ResultInternalStateError());
R_UNLESS(m_report.reports[0].id == urb_id, usb::ResultInternalStateError());
/* Set output bytes. */
if (out_transferred != nullptr) {
*out_transferred = m_report.reports[0].transferred_size;
}
/* Handle the report. */
switch (m_report.reports[0].status) {
case UrbStatus_Cancelled:
return usb::ResultInterrupted();
case UrbStatus_Failed:
return usb::ResultTransactionError();
case UrbStatus_Finished:
return ResultSuccess();
default:
return usb::ResultInternalStateError();
}
}
Result DsInterface::CtrlOut(u32 *out_transferred, void *dst, u32 size) {
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
/* Check that we're initialized. */
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
/* Check that we're enabled. */
R_UNLESS(m_client->m_is_enabled, usb::ResultOperationDenied());
/* Check that the data is aligned. */
R_UNLESS(usb::IsDmaAligned(reinterpret_cast<u64>(dst)), usb::ResultAlignmentError());
/* If we should, invalidate cache. */
if (size != 0) {
dd::InvalidateDataCache(dst, size);
}
/* Check that we have a service. */
AMS_ABORT_UNLESS(m_interface != nullptr);
/* Perform the transfer. */
u32 urb_id;
R_TRY(m_interface->CtrlOutAsync(std::addressof(urb_id), reinterpret_cast<u64>(dst), size));
/* Wait for control to finish. */
os::WaitSystemEvent(std::addressof(m_ctrl_out_completion_event));
os::ClearSystemEvent(std::addressof(m_ctrl_out_completion_event));
/* Ensure that cache remains consistent. */
ON_SCOPE_EXIT {
if (size != 0) {
dd::InvalidateDataCache(dst, size);
}
};
/* Get the urb report. */
R_ABORT_UNLESS(m_interface->GetCtrlOutUrbReport(std::addressof(m_report)));
/* Check the report is for our urb. */
R_UNLESS(m_report.count == 1, usb::ResultInternalStateError());
R_UNLESS(m_report.reports[0].id == urb_id, usb::ResultInternalStateError());
/* Set output bytes. */
if (out_transferred != nullptr) {
*out_transferred = m_report.reports[0].transferred_size;
}
/* Handle the report. */
switch (m_report.reports[0].status) {
case UrbStatus_Cancelled:
return usb::ResultInterrupted();
case UrbStatus_Failed:
return usb::ResultTransactionError();
case UrbStatus_Finished:
return ResultSuccess();
default:
return usb::ResultInternalStateError();
}
}
Result DsInterface::CtrlRead(u32 *out_transferred, void *dst, u32 size) {
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
/* Check that we're initialized. */
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
/* Do the data transfer. */
Result result = this->CtrlOut(out_transferred, dst, size);
/* Do the status transfer. */
if (R_SUCCEEDED(result)) {
result = this->CtrlIn(nullptr, nullptr, 0);
}
/* If we failed, stall. */
if (R_FAILED(result)) {
result = this->CtrlStall();
}
return result;
}
Result DsInterface::CtrlWrite(u32 *out_transferred, void *dst, u32 size) {
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
/* Check that we're initialized. */
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
/* Do the data transfer. */
Result result = this->CtrlIn(out_transferred, dst, size);
/* Do the status transfer. */
if (R_SUCCEEDED(result)) {
result = this->CtrlOut(nullptr, nullptr, 0);
}
/* If we failed, stall. */
if (R_FAILED(result)) {
result = this->CtrlStall();
}
return result;
}
Result DsInterface::CtrlDone() {
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
/* Check that we're initialized. */
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
/* Do the status transfer. */
Result result = this->CtrlIn(nullptr, nullptr, 0);
/* If we failed, stall. */
if (R_FAILED(result)) {
result = this->CtrlStall();
}
return result;
}
Result DsInterface::CtrlStall() {
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
/* Check that we're initialized. */
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
/* Check that we're enabled. */
R_UNLESS(m_client->m_is_enabled, usb::ResultOperationDenied());
/* Check that we have a service. */
AMS_ABORT_UNLESS(m_interface != nullptr);
return m_interface->CtrlStall();
}
Result DsEndpoint::Initialize(DsInterface *interface, u8 bEndpointAddress) {
/* Check that the interface is valid. */
AMS_ABORT_UNLESS(interface != nullptr);
/* Check that we're not already initialized. */
R_UNLESS(!m_is_initialized, usb::ResultAlreadyInitialized());
/* Set our interface. */
m_interface = interface;
/* Add the endpoint. */
R_TRY(m_interface->AddEndpoint(this, bEndpointAddress, std::addressof(m_endpoint)));
/* Set our address. */
m_address = bEndpointAddress;
/* Ensure we clean up if we fail after this. */
auto ep_guard = SCOPE_GUARD { m_interface->DeleteEndpoint(m_address); m_endpoint = nullptr; };
/* Get completion event. */
sf::CopyHandle event_handle;
R_TRY(m_endpoint->GetCompletionEvent(std::addressof(event_handle)));
/* Increment our interface's reference count. */
++m_interface->m_reference_count;
++m_interface->m_client->m_reference_count;
/* Attach our event. */
os::AttachReadableHandleToSystemEvent(std::addressof(m_completion_event), event_handle, true, os::EventClearMode_ManualClear);
/* Mark initialized. */
m_is_initialized = true;
ep_guard.Cancel();
return ResultSuccess();
}
Result DsEndpoint::Finalize() {
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
/* Check that we're initialized. */
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
/* Cancel any pending transactions. */
m_endpoint->Cancel();
/* Wait for us to be at one reference count. */
while (m_reference_count > 1) {
os::SleepThread(TimeSpan::FromMilliSeconds(25));
}
/* Destroy our event. */
os::DestroySystemEvent(std::addressof(m_completion_event));
/* Decrement our interface's reference count. */
--m_interface->m_reference_count;
--m_interface->m_client->m_reference_count;
/* Delete ourselves. */
R_TRY(m_interface->DeleteEndpoint(m_address));
/* Clear ourselves. */
m_interface = nullptr;
m_endpoint = nullptr;
/* Mark uninitialized. */
m_is_initialized = false;
return ResultSuccess();
}
bool DsEndpoint::IsInitialized() {
return m_is_initialized;
}
Result DsEndpoint::PostBuffer(u32 *out_transferred, void *buf, u32 size) {
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
/* Check that we're initialized. */
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
/* Post buffer. */
u32 urb_id;
R_TRY(this->PostBufferAsync(std::addressof(urb_id), buf, size));
/* Wait for completion. */
os::WaitSystemEvent(std::addressof(m_completion_event));
os::ClearSystemEvent(std::addressof(m_completion_event));
/* Get URB report. */
UrbReport report;
AMS_ABORT_UNLESS(m_endpoint != nullptr);
R_ABORT_UNLESS(m_endpoint->GetUrbReport(std::addressof(report)));
/* Check the report is for our urb. */
R_UNLESS(report.count == 1, usb::ResultInternalStateError());
R_UNLESS(report.reports[0].id == urb_id, usb::ResultInternalStateError());
/* Set output bytes. */
if (out_transferred != nullptr) {
*out_transferred = report.reports[0].transferred_size;
}
/* Handle the report. */
switch (report.reports[0].status) {
case UrbStatus_Cancelled:
return usb::ResultInterrupted();
case UrbStatus_Failed:
return usb::ResultTransactionError();
case UrbStatus_Finished:
return ResultSuccess();
default:
return usb::ResultInternalStateError();
}
}
Result DsEndpoint::PostBufferAsync(u32 *out_urb_id, void *buf, u32 size) {
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
/* Check that we're initialized. */
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
/* Check that the buffer is DMA aligned. */
R_UNLESS(usb::IsDmaAligned(reinterpret_cast<u64>(buf)), usb::ResultAlignmentError());
/* Check that we have a service. */
AMS_ABORT_UNLESS(m_endpoint != nullptr);
/* Post */
u32 urb_id = 0;
R_TRY(m_endpoint->PostBufferAsync(std::addressof(urb_id), reinterpret_cast<u64>(buf), size));
*out_urb_id = urb_id;
return ResultSuccess();
}
os::SystemEventType *DsEndpoint::GetCompletionEvent() {
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
return m_is_initialized ? std::addressof(m_completion_event) : nullptr;
}
Result DsEndpoint::GetUrbReport(UrbReport *out) {
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
/* Check that we're initialized. */
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
/* Check that we have a service. */
AMS_ABORT_UNLESS(m_endpoint != nullptr);
return m_endpoint->GetUrbReport(out);
}
Result DsEndpoint::Cancel() {
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
/* Check that we're initialized. */
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
/* Check that we have a service. */
AMS_ABORT_UNLESS(m_endpoint != nullptr);
return m_endpoint->Cancel();
}
Result DsEndpoint::SetZeroLengthTransfer(bool zlt) {
/* Create a scoped reference. */
impl::ScopedRefCount scoped_ref(m_reference_count);
/* Check that we're initialized. */
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
/* Check that we have a service. */
AMS_ABORT_UNLESS(m_endpoint != nullptr);
return m_endpoint->SetZlt(zlt);
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "usb_remote_ds_endpoint.hpp"
namespace ams::usb {
Result RemoteDsEndpoint::PostBufferAsync(sf::Out<u32> out_urb_id, u64 address, u32 size) {
const struct {
u32 size;
u64 address;
} in = { size, address };
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatchInOut(std::addressof(m_srv), 0, in, *out_urb_id);
}
Result RemoteDsEndpoint::Cancel() {
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatch(std::addressof(m_srv), 1);
}
Result RemoteDsEndpoint::GetCompletionEvent(sf::OutCopyHandle out) {
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatch(std::addressof(m_srv), 2,
.out_handle_attrs = { SfOutHandleAttr_HipcCopy },
.out_handles = out.GetHandlePointer(),
);
}
Result RemoteDsEndpoint::GetUrbReport(sf::Out<usb::UrbReport> out) {
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatchOut(std::addressof(m_srv), 3, *out);
}
Result RemoteDsEndpoint::Stall() {
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatch(std::addressof(m_srv), 4);
}
Result RemoteDsEndpoint::SetZlt(bool zlt) {
const u8 in = zlt ? 1 : 0;
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatchIn(std::addressof(m_srv), 5, in);
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::usb {
class RemoteDsEndpoint {
private:
Service m_srv;
public:
RemoteDsEndpoint(Service &srv) : m_srv(srv) { /* ... */ }
virtual ~RemoteDsEndpoint() { serviceClose(std::addressof(m_srv)); }
public:
Result PostBufferAsync(sf::Out<u32> out_urb_id, u64 address, u32 size);
Result Cancel();
Result GetCompletionEvent(sf::OutCopyHandle out);
Result GetUrbReport(sf::Out<usb::UrbReport> out);
Result Stall();
Result SetZlt(bool zlt);
};
static_assert(ds::IsIDsEndpoint<RemoteDsEndpoint>);
}

View File

@@ -0,0 +1,139 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "usb_remote_ds_interface.hpp"
#include "usb_remote_ds_endpoint.hpp"
namespace ams::usb {
Result RemoteDsInterface::RegisterEndpoint(u8 endpoint_address, sf::Out<sf::SharedPointer<usb::ds::IDsEndpoint>> out) {
Service srv;
serviceAssumeDomain(std::addressof(m_srv));
R_TRY(serviceDispatchIn(std::addressof(m_srv), 0, endpoint_address,
.out_num_objects = 1,
.out_objects = std::addressof(srv),
));
*out = ObjectFactory::CreateSharedEmplaced<ds::IDsEndpoint, RemoteDsEndpoint>(m_allocator, srv);
return ResultSuccess();
}
Result RemoteDsInterface::GetSetupEvent(sf::OutCopyHandle out) {
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatch(std::addressof(m_srv), 1,
.out_handle_attrs = { SfOutHandleAttr_HipcCopy },
.out_handles = out.GetHandlePointer(),
);
}
Result RemoteDsInterface::GetSetupPacket(const sf::OutBuffer &out) {
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatch(std::addressof(m_srv), 2,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
.buffers = { { out.GetPointer(), out.GetSize() } },
);
}
Result RemoteDsInterface::CtrlInAsync(sf::Out<u32> out_urb_id, u64 address, u32 size) {
const struct {
u32 size;
u64 address;
} in = { size, address };
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatchInOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 3 : 5, in, *out_urb_id);
}
Result RemoteDsInterface::CtrlOutAsync(sf::Out<u32> out_urb_id, u64 address, u32 size) {
const struct {
u32 size;
u64 address;
} in = { size, address };
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatchInOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 4 : 6, in, *out_urb_id);
}
Result RemoteDsInterface::GetCtrlInCompletionEvent(sf::OutCopyHandle out) {
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 5 : 7,
.out_handle_attrs = { SfOutHandleAttr_HipcCopy },
.out_handles = out.GetHandlePointer(),
);
}
Result RemoteDsInterface::GetCtrlInUrbReport(sf::Out<usb::UrbReport> out) {
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatchOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 6 : 8, *out);
}
Result RemoteDsInterface::GetCtrlOutCompletionEvent(sf::OutCopyHandle out) {
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 7 : 9,
.out_handle_attrs = { SfOutHandleAttr_HipcCopy },
.out_handles = out.GetHandlePointer(),
);
}
Result RemoteDsInterface::GetCtrlOutUrbReport(sf::Out<usb::UrbReport> out) {
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatchOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 8 : 10, *out);
}
Result RemoteDsInterface::CtrlStall() {
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 9 : 11);
}
Result RemoteDsInterface::AppendConfigurationData(u8 bInterfaceNumber, usb::UsbDeviceSpeed device_speed, const sf::InBuffer &data) {
if (hos::GetVersion() >= hos::Version_11_0_0) {
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatchIn(std::addressof(m_srv), 10, device_speed,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
.buffers = { { data.GetPointer(), data.GetSize() } },
);
} else {
const struct {
u8 bInterfaceNumber;
usb::UsbDeviceSpeed device_speed;
} in = { bInterfaceNumber, device_speed };
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatchIn(std::addressof(m_srv), 12, in,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
.buffers = { { data.GetPointer(), data.GetSize() } },
);
}
}
Result RemoteDsInterface::Enable() {
R_SUCCEED_IF(hos::GetVersion() >= hos::Version_11_0_0);
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatch(std::addressof(m_srv), 3);
}
Result RemoteDsInterface::Disable() {
R_SUCCEED_IF(hos::GetVersion() >= hos::Version_11_0_0);
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatch(std::addressof(m_srv), 3);
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::usb {
class RemoteDsInterface {
private:
using Allocator = sf::ExpHeapAllocator;
using ObjectFactory = sf::ObjectFactory<Allocator::Policy>;
private:
Service m_srv;
Allocator *m_allocator;
public:
RemoteDsInterface(Service &srv, sf::ExpHeapAllocator *allocator) : m_srv(srv), m_allocator(allocator) { /* ... */ }
virtual ~RemoteDsInterface() { serviceClose(std::addressof(m_srv)); }
public:
Result RegisterEndpoint(u8 endpoint_address, sf::Out<sf::SharedPointer<usb::ds::IDsEndpoint>> out);
Result GetSetupEvent(sf::OutCopyHandle out);
Result GetSetupPacket(const sf::OutBuffer & out);
Result CtrlInAsync(sf::Out<u32> out_urb_id, u64 address, u32 size);
Result CtrlOutAsync(sf::Out<u32> out_urb_id, u64 address, u32 size);
Result GetCtrlInCompletionEvent(sf::OutCopyHandle out);
Result GetCtrlInUrbReport(sf::Out<usb::UrbReport> out);
Result GetCtrlOutCompletionEvent(sf::OutCopyHandle out);
Result GetCtrlOutUrbReport(sf::Out<usb::UrbReport> out);
Result CtrlStall();
Result AppendConfigurationData(u8 bInterfaceNumber, usb::UsbDeviceSpeed device_speed, const sf::InBuffer &data);
Result Enable();
Result Disable();
};
static_assert(ds::IsIDsInterface<RemoteDsInterface>);
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "usb_remote_ds_root_service.hpp"
#include "usb_remote_ds_service.hpp"
namespace ams::usb {
Result RemoteDsRootService::GetService(sf::Out<sf::SharedPointer<usb::ds::IDsService>> out) {
Service srv;
serviceAssumeDomain(std::addressof(m_srv));
R_TRY(serviceDispatch(std::addressof(m_srv), 0, .out_num_objects = 1, .out_objects = std::addressof(srv)));
*out = ObjectFactory::CreateSharedEmplaced<ds::IDsService, RemoteDsService>(m_allocator, srv, m_allocator);
return ResultSuccess();
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::usb {
class RemoteDsRootService {
private:
using Allocator = sf::ExpHeapAllocator;
using ObjectFactory = sf::ObjectFactory<Allocator::Policy>;
private:
Service m_srv;
Allocator *m_allocator;
public:
RemoteDsRootService(Service &srv, sf::ExpHeapAllocator *allocator) : m_srv(srv), m_allocator(allocator) { /* ... */ }
virtual ~RemoteDsRootService() { serviceClose(std::addressof(m_srv)); }
public:
Result GetService(sf::Out<sf::SharedPointer<usb::ds::IDsService>> out);
};
static_assert(ds::IsIDsRootService<RemoteDsRootService>);
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "usb_remote_ds_service.hpp"
#include "usb_remote_ds_interface.hpp"
namespace ams::usb {
Result RemoteDsService::Bind(usb::ComplexId complex_id, sf::CopyHandle process_h) {
if (hos::GetVersion() >= hos::Version_11_0_0) {
serviceAssumeDomain(std::addressof(m_srv));
R_TRY(serviceDispatchIn(std::addressof(m_srv), 0, complex_id,
.in_num_handles = 1,
.in_handles = { process_h.GetValue() }
));
} else {
serviceAssumeDomain(std::addressof(m_srv));
R_TRY(serviceDispatchIn(std::addressof(m_srv), 0, complex_id));
serviceAssumeDomain(std::addressof(m_srv));
R_TRY(serviceDispatch(std::addressof(m_srv), 1,
.in_num_handles = 1,
.in_handles = { process_h.GetValue() })
);
}
return ResultSuccess();
}
Result RemoteDsService::RegisterInterface(sf::Out<sf::SharedPointer<usb::ds::IDsInterface>> out, u8 bInterfaceNumber) {
Service srv;
serviceAssumeDomain(std::addressof(m_srv));
R_TRY(serviceDispatchIn(std::addressof(m_srv), (hos::GetVersion() >= hos::Version_11_0_0 ? 1 : 2), bInterfaceNumber,
.out_num_objects = 1,
.out_objects = std::addressof(srv),
));
*out = ObjectFactory::CreateSharedEmplaced<ds::IDsInterface, RemoteDsInterface>(m_allocator, srv, m_allocator);
return ResultSuccess();
}
Result RemoteDsService::GetStateChangeEvent(sf::OutCopyHandle out) {
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 2 : 3,
.out_handle_attrs = { SfOutHandleAttr_HipcCopy },
.out_handles = out.GetHandlePointer(),
);
}
Result RemoteDsService::GetState(sf::Out<usb::UsbState> out) {
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatchOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 3 : 4, *out);
}
Result RemoteDsService::ClearDeviceData() {
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 4 : 5);
}
Result RemoteDsService::AddUsbStringDescriptor(sf::Out<u8> out, const sf::InBuffer &desc) {
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatchOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 5 : 6, *out,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
.buffers = { { desc.GetPointer(), desc.GetSize() } },
);
}
Result RemoteDsService::DeleteUsbStringDescriptor(u8 index) {
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatchIn(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 6 : 7, index);
}
Result RemoteDsService::SetUsbDeviceDescriptor(const sf::InBuffer &desc, usb::UsbDeviceSpeed speed) {
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatchIn(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 7 : 8, speed,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
.buffers = { { desc.GetPointer(), desc.GetSize() } },
);
}
Result RemoteDsService::SetBinaryObjectStore(const sf::InBuffer &bos) {
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 8 : 9,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
.buffers = { { bos.GetPointer(), bos.GetSize() } },
);
}
Result RemoteDsService::Enable() {
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 9 : 10);
}
Result RemoteDsService::Disable() {
serviceAssumeDomain(std::addressof(m_srv));
return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 10 : 11);
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::usb {
class RemoteDsService {
private:
using Allocator = sf::ExpHeapAllocator;
using ObjectFactory = sf::ObjectFactory<Allocator::Policy>;
private:
Service m_srv;
Allocator *m_allocator;
public:
RemoteDsService(Service &srv, sf::ExpHeapAllocator *allocator) : m_srv(srv), m_allocator(allocator) { /* ... */ }
virtual ~RemoteDsService() { serviceClose(std::addressof(m_srv)); }
public:
Result Bind(usb::ComplexId complex_id, sf::CopyHandle process_h);
Result RegisterInterface(sf::Out<sf::SharedPointer<usb::ds::IDsInterface>> out, u8 bInterfaceNumber);
Result GetStateChangeEvent(sf::OutCopyHandle out);
Result GetState(sf::Out<usb::UsbState> out);
Result ClearDeviceData();
Result AddUsbStringDescriptor(sf::Out<u8> out, const sf::InBuffer &desc);
Result DeleteUsbStringDescriptor(u8 index);
Result SetUsbDeviceDescriptor(const sf::InBuffer &desc, usb::UsbDeviceSpeed speed);
Result SetBinaryObjectStore(const sf::InBuffer &bos);
Result Enable();
Result Disable();
};
static_assert(ds::IsIDsService<RemoteDsService>);
}

View File

@@ -17,7 +17,7 @@
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 0
#define ATMOSPHERE_RELEASE_VERSION_MINOR 18
#define ATMOSPHERE_RELEASE_VERSION_MICRO 0
#define ATMOSPHERE_RELEASE_VERSION_MICRO 1
#define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO

View File

@@ -59,6 +59,7 @@
#include <vapours/results/svc_results.hpp>
#include <vapours/results/time_results.hpp>
#include <vapours/results/updater_results.hpp>
#include <vapours/results/usb_results.hpp>
#include <vapours/results/vi_results.hpp>
/* Unofficial. */

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::usb {
R_DEFINE_NAMESPACE_RESULT_MODULE(140);
R_DEFINE_ERROR_RESULT(NotInitialized, 0);
R_DEFINE_ERROR_RESULT(AlreadyInitialized, 1);
R_DEFINE_ERROR_RANGE(InvalidParameter, 100, 199);
R_DEFINE_ERROR_RESULT(AlignmentError, 103);
R_DEFINE_ERROR_RESULT(OperationDenied, 201);
R_DEFINE_ERROR_RESULT(MemAllocFailure, 202);
R_DEFINE_ERROR_RESULT(ResourceBusy, 206);
R_DEFINE_ERROR_RESULT(InternalStateError, 207);
R_DEFINE_ERROR_RESULT(TransactionError, 401);
R_DEFINE_ERROR_RESULT(Interrupted, 409);
}

View File

@@ -46,6 +46,9 @@
#include <vapours/util/util_format_string.hpp>
#include <vapours/util/util_range.hpp>
#include <vapours/util/util_fixed_map.hpp>
#include <vapours/util/util_fixed_set.hpp>
#ifdef ATMOSPHERE_IS_STRATOSPHERE
#include <vapours/util/util_mutex_utils.hpp>
#endif

View File

@@ -0,0 +1,220 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/util/util_fourcc.hpp>
namespace ams::util::impl {
class AvailableIndexFinder {
private:
static constexpr int MaxDepthOfBox = 5;
struct BitFlags64 {
private:
u64 m_data;
public:
constexpr ALWAYS_INLINE bool GetFlag(int index) const {
AMS_ASSERT(index < 64);
return ((m_data >> index) & UINT64_C(1)) != 0;
}
constexpr ALWAYS_INLINE void SetFlag(int index) {
AMS_ASSERT(index < 64);
m_data |= (UINT64_C(1) << index);
}
constexpr ALWAYS_INLINE void ClearFlag(int index) {
AMS_ASSERT(index < 64);
m_data &= ~(UINT64_C(1) << index);
}
constexpr ALWAYS_INLINE bool IsFull() const {
return m_data == ~(UINT64_C(0));
}
constexpr ALWAYS_INLINE int FindIndexOfBitZero() const {
AMS_ASSERT(!this->IsFull());
return __builtin_ctzll(~m_data);
}
};
private:
int *m_p_current_index;
int *m_p_map_index;
void *m_buffer;
int m_depth;
BitFlags64 *m_flags;
private:
static constexpr int Pow64(int e) {
switch (e) {
case 0: return 0x1;
case 1: return 0x40;
case 2: return 0x1000;
case 3: return 0x40000;
case 4: return 0x1000000;
case 5: return 0x40000000;
default: return -1;
}
}
static constexpr u64 Roundup64(u64 value) {
return (value + (64 - 1)) / 64;
}
static constexpr size_t GetRequiredNumOfBox(int depth, size_t num_elements) {
if (depth == 1) {
return Roundup64(num_elements);
} else if (depth == 2) {
return Roundup64(num_elements) + Pow64(0);
} else if (depth == 3) {
return Roundup64(num_elements) + Pow64(0) + Pow64(1) + Pow64(2);
} else if (depth == 4) {
return Roundup64(num_elements) + Pow64(0) + Pow64(1) + Pow64(2) + Pow64(3);
} else if (depth == 5) {
return Roundup64(num_elements) + Pow64(0) + Pow64(1) + Pow64(2) + Pow64(3) + Pow64(4);
} else {
return 0;
}
}
static constexpr int CalcOffset(int *arr, int depth) {
int offset = 0;
for (auto i = 0; i < depth; ++i) {
offset += Pow64(i);
}
for (auto i = 0; i < depth; ++i) {
offset += Pow64(i) - arr[depth - 1 - i];
}
return offset;
}
public:
static consteval int GetSignature() {
return static_cast<int>(util::ReverseFourCC<'B', 'I', 'T', 'S'>::Code);
}
static constexpr int GetNeedDepth(size_t num_elements) {
if (num_elements <= 0x40) {
return 1;
} else if (num_elements <= 0x1000) {
return 2;
} else if (num_elements <= 0x40000) {
return 3;
} else if (num_elements <= 0x1000000) {
return 4;
} else if (num_elements <= 0x40000000) {
return 5;
} else {
return -1;
}
}
static constexpr size_t GetRequiredMemorySize(size_t num_elements) {
return sizeof(BitFlags64) * GetRequiredNumOfBox(GetNeedDepth(num_elements), num_elements);
}
public:
void Initialize(int *cur, int *map, u8 *buf) {
const size_t num_elements = static_cast<size_t>(*map);
AMS_ASSERT(num_elements <= static_cast<size_t>(Pow64(MaxDepthOfBox - 1)));
/* Set fields. */
m_p_current_index = cur;
m_p_map_index = map;
m_buffer = buf;
m_depth = GetNeedDepth(num_elements);
/* Validate fields. */
AMS_ASSERT(m_depth > 0);
/* Setup memory. */
std::memset(m_buffer, 0, GetRequiredMemorySize(num_elements));
m_flags = reinterpret_cast<BitFlags64 *>(m_buffer);
}
int AcquireIndex() {
/* Validate pre-conditions. */
AMS_ASSERT(*m_p_current_index < *m_p_map_index);
/* Build up arrays. */
int table[MaxDepthOfBox];
BitFlags64 *pos[MaxDepthOfBox];
for (auto i = 0; i < m_depth; ++i) {
/* Determine the position. */
pos[i] = std::addressof(m_flags[CalcOffset(table, i)]);
/* Set table entry. */
table[i] = pos[i]->FindIndexOfBitZero();
AMS_ASSERT(table[i] != BITSIZEOF(u64));
}
/* Validate that the index is not acquired. */
AMS_ASSERT(!pos[m_depth - 1]->GetFlag(table[m_depth - 1]));
/* Acquire the index. */
pos[m_depth - 1]->SetFlag(table[m_depth - 1]);
/* Validate that the index was acquired. */
AMS_ASSERT(pos[m_depth - 1]->GetFlag(table[m_depth - 1]));
/* Update tracking flags. */
for (auto i = m_depth - 1; i > 0; --i) {
if (pos[i]->IsFull()) {
pos[i - 1]->SetFlag(table[i - 1]);
}
}
/* Calculate the index we acquired. */
int index = 0, pow = 0;
for (auto i = m_depth; i > 0; --i, ++pow) {
index += Pow64(pow) * table[i - 1];
}
/* Increment current index. */
++(*m_p_current_index);
return index;
}
void ReleaseIndex(int index) {
/* Convert index to table. */
int table[MaxDepthOfBox];
for (auto i = 0; i < m_depth; ++i) {
table[m_depth - 1 - i] = index % BITSIZEOF(u64);
index /= BITSIZEOF(u64);
}
/* Build up arrays. */
BitFlags64 *pos[MaxDepthOfBox];
for (auto i = 0; i < m_depth; ++i) {
/* Determine the position. */
pos[i] = std::addressof(m_flags[CalcOffset(table, i)]);
}
/* Validate that the flag is set. */
AMS_ASSERT(pos[m_depth - 1]->GetFlag(table[m_depth - 1]));
/* Clear the flags. */
for (auto i = m_depth - 1; i >= 0; --i) {
pos[i]->ClearFlag(table[i]);
}
/* Decrement current index. */
--(*m_p_current_index);
}
};
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/util/util_fixed_tree.hpp>
namespace ams::util {
template<typename Key, typename Value, typename Compare = std::less<Key>, size_t BufferAlignment = 8>
class FixedMap {
private:
using KeyValuePair = std::pair<Key const, Value>;
struct LessTypeForMap {
constexpr ALWAYS_INLINE bool operator()(const KeyValuePair &lhs, const KeyValuePair &rhs) const {
return Compare{}(lhs.first, rhs.first);
}
};
using TreeType = ::ams::util::FixedTree<KeyValuePair, LessTypeForMap, KeyValuePair, BufferAlignment>;
using iterator = typename TreeType::iterator;
using const_iterator = typename TreeType::const_iterator;
public:
static constexpr size_t GetRequiredMemorySize(size_t num_elements) {
return TreeType::GetRequiredMemorySize(num_elements);
}
private:
TreeType m_tree;
public:
FixedMap() : m_tree() { /* ... */ }
void Initialize(size_t num_elements, void *buffer, size_t buffer_size) {
return m_tree.Initialize(num_elements, buffer, buffer_size);
}
ALWAYS_INLINE iterator begin() { return m_tree.begin(); }
ALWAYS_INLINE const_iterator begin() const { return m_tree.begin(); }
ALWAYS_INLINE iterator end() { return m_tree.end(); }
ALWAYS_INLINE const_iterator end() const { return m_tree.end(); }
ALWAYS_INLINE bool erase(const Key &key) { const KeyValuePair pair(key, Value{}); return m_tree.erase(pair); }
ALWAYS_INLINE iterator find(const Key &key) { const KeyValuePair pair(key, Value{}); return m_tree.find(pair); }
ALWAYS_INLINE const_iterator find(const Key &key) const { const KeyValuePair pair(key, Value{}); return m_tree.find(pair); }
ALWAYS_INLINE std::pair<iterator, bool> insert(const KeyValuePair &pair) { return m_tree.insert(pair); }
ALWAYS_INLINE size_t size() const { return m_tree.size(); }
ALWAYS_INLINE void clear() { return m_tree.clear(); }
};
}

View File

@@ -13,24 +13,16 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "printk.h"
#include "vsprintf.h"
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/util/util_fixed_tree.hpp>
/**
* Temporary stand-in main printk.
*
* TODO: This should print via UART, console framebuffer, and to a ring for
* consumption by Horizon
*/
void printk(char *fmt, ...)
{
va_list list;
char buf[512];
va_start(list, fmt);
vsnprintf(buf, sizeof(buf), fmt, list);
namespace ams::util {
/* FIXME: print via UART */
template<typename Element, typename Compare = std::less<Element>, size_t BufferAlignment = 8>
class FixedSet : public ::ams::util::FixedTree<Element, Compare, const Element, BufferAlignment> {
/* ... */
};
va_end(list);
}

View File

@@ -0,0 +1,998 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/util/impl/util_available_index_finder.hpp>
#include <vapours/util/util_alignment.hpp>
namespace ams::util {
template<typename Member, typename Compare, typename IteratorMember, size_t BufferAlignment = 8> requires std::convertible_to<Member &, IteratorMember &>
class FixedTree {
private:
class IteratorBase;
friend class IteratorBase;
private:
enum class Color : u8 {
Red = 0,
Black = 1,
};
static constexpr inline int Index_Nil = -1;
static constexpr inline int Index_Leaf = -2;
static constexpr inline int Index_BeforeBegin = -3;
static constexpr inline int Index_AfterEnd = -4;
static constexpr inline size_t max_size = 0x40000000;
struct Header {
/* "Nintendo Red-Black tree" */
static constexpr u32 Signature = util::ReverseFourCC<'N','N','R','B'>::Code;
u32 header_size;
u32 header_signature;
u32 _08;
s32 max_elements;
s32 cur_elements;
s32 root_index;
s32 left_most_index;
s32 right_most_index;
s32 index_signature;
u32 buffer_size;
u32 node_size;
u32 element_size;
u32 _30;
u32 _34;
u32 _38;
u32 _3C;
u32 _40;
u32 _44;
u32 _48;
u32 _4C;
void InitializeHeader(u32 _08, s32 max_e, s32 cur_e, u32 ind_sig, u32 buf_sz, u32 node_sz, u32 e_sz, u32 _30, u32 _34, u32 _38, u32 _3C, u32 _40, u32 _44) {
this->header_size = sizeof(Header);
this->header_signature = Signature;
this->_08 = _08;
this->max_elements = max_e;
this->cur_elements = cur_e;
this->root_index = Index_Nil;
this->left_most_index = Index_Nil;
this->right_most_index = Index_Nil;
this->index_signature = ind_sig;
this->buffer_size = buf_sz;
this->node_size = node_sz;
this->element_size = e_sz;
this->_30 = _30;
this->_34 = _34;
this->_38 = _38;
this->_3C = _3C;
this->_40 = _40;
this->_44 = _44;
this->_48 = 0;
this->_4C = 0;
}
};
static_assert(sizeof(Header) == 0x50);
struct IndexPair {
int first;
int last;
};
struct Node {
Member m_data;
int m_parent;
int m_right;
int m_left;
Color m_color;
void SetLeft(int l, Node *n, int p) {
m_left = l;
n->m_parent = p;
}
void SetRight(int r, Node *n, int p) {
m_right = r;
n->m_parent = p;
}
};
class Iterator;
class ConstIterator;
class IteratorBase {
private:
friend class ConstIterator;
private:
const FixedTree *m_this;
int m_index;
protected:
constexpr ALWAYS_INLINE IteratorBase(const FixedTree *tree, int index) : m_this(tree), m_index(index) { /* ... */ }
constexpr bool IsEqualImpl(const IteratorBase &rhs) const {
/* Validate pre-conditions. */
AMS_ASSERT(m_this);
/* Check for tree equality. */
if (m_this != rhs.m_this) {
return false;
}
/* Check for nil. */
if (m_this->IsNil(m_index) && m_this->IsNil(rhs.m_index)) {
return true;
}
/* Check for index equality. */
return m_index == rhs.m_index;
}
constexpr IteratorMember &DereferenceImpl() const {
/* Validate pre-conditions. */
AMS_ASSERT(m_this);
if (!m_this->IsNil(m_index)) {
return m_this->m_nodes[m_index].m_data;
} else {
AMS_ASSERT(false);
return m_this->GetNode(std::numeric_limits<int>::max())->m_data;
}
}
constexpr ALWAYS_INLINE IteratorBase &IncrementImpl() {
/* Validate pre-conditions. */
AMS_ASSERT(m_this);
this->OperateIndex(true);
return *this;
}
constexpr ALWAYS_INLINE IteratorBase &DecrementImpl() {
/* Validate pre-conditions. */
AMS_ASSERT(m_this);
this->OperateIndex(false);
return *this;
}
constexpr void OperateIndex(bool increment) {
if (increment) {
/* We're incrementing. */
if (m_index == Index_BeforeBegin) {
m_index = 0;
} else {
m_index = m_this->UncheckedPP(m_index);
if (m_this->IsNil(m_index)) {
m_index = Index_AfterEnd;
}
}
} else {
/* We're decrementing. */
if (m_index == Index_AfterEnd) {
m_index = static_cast<int>(m_this->size()) - 1;
} else {
m_index = m_this->UncheckedMM(m_index);
if (m_this->IsNil(m_index)) {
m_index = Index_BeforeBegin;
}
}
}
}
};
class Iterator : public IteratorBase {
public:
constexpr ALWAYS_INLINE Iterator(const FixedTree &tree) : IteratorBase(std::addressof(tree), tree.size() ? tree.GetLMost() : Index_Leaf) { /* ... */ }
constexpr ALWAYS_INLINE Iterator(const FixedTree &tree, int index) : IteratorBase(std::addressof(tree), index) { /* ... */ }
constexpr ALWAYS_INLINE Iterator(const Iterator &rhs) = default;
constexpr ALWAYS_INLINE bool operator==(const Iterator &rhs) const {
return this->IsEqualImpl(rhs);
}
constexpr ALWAYS_INLINE bool operator!=(const Iterator &rhs) const {
return !(*this == rhs);
}
constexpr ALWAYS_INLINE IteratorMember &operator*() const {
return static_cast<IteratorMember &>(this->DereferenceImpl());
}
constexpr ALWAYS_INLINE IteratorMember *operator->() const {
return std::addressof(this->operator *());
}
constexpr ALWAYS_INLINE Iterator &operator++() {
return static_cast<Iterator &>(this->IncrementImpl());
}
constexpr ALWAYS_INLINE Iterator &operator--() {
return static_cast<Iterator &>(this->DecrementImpl());
}
};
class ConstIterator : public IteratorBase {
public:
constexpr ALWAYS_INLINE ConstIterator(const FixedTree &tree) : IteratorBase(std::addressof(tree), tree.size() ? tree.GetLMost() : Index_Leaf) { /* ... */ }
constexpr ALWAYS_INLINE ConstIterator(const FixedTree &tree, int index) : IteratorBase(std::addressof(tree), index) { /* ... */ }
constexpr ALWAYS_INLINE ConstIterator(const ConstIterator &rhs) = default;
constexpr ALWAYS_INLINE ConstIterator(const Iterator &rhs) : IteratorBase(rhs.m_this, rhs.m_index) { /* ... */ }
constexpr ALWAYS_INLINE bool operator==(const ConstIterator &rhs) const {
return this->IsEqualImpl(rhs);
}
constexpr ALWAYS_INLINE bool operator!=(const ConstIterator &rhs) const {
return !(*this == rhs);
}
constexpr ALWAYS_INLINE const IteratorMember &operator*() const {
return static_cast<const IteratorMember &>(this->DereferenceImpl());
}
constexpr ALWAYS_INLINE const IteratorMember *operator->() const {
return std::addressof(this->operator *());
}
constexpr ALWAYS_INLINE ConstIterator &operator++() {
return static_cast<ConstIterator &>(this->IncrementImpl());
}
constexpr ALWAYS_INLINE ConstIterator &operator--() {
return static_cast<ConstIterator &>(this->DecrementImpl());
}
};
public:
using iterator = Iterator;
using const_iterator = ConstIterator;
private:
impl::AvailableIndexFinder m_index_finder;
Node m_dummy_leaf;
Node *m_p_dummy_leaf;
u8 *m_buffer;
Header *m_header;
Node *m_nodes;
iterator m_end_iterator;
public:
FixedTree() : m_end_iterator(*this, Index_Nil) {
this->SetDummyMemory();
}
protected:
void InitializeImpl(int num_elements, void *buffer, size_t buffer_size) {
/* Check pre-conditions. */
AMS_ASSERT(num_elements > 0);
AMS_ASSERT(static_cast<size_t>(num_elements) <= max_size);
AMS_ASSERT(util::IsAligned(reinterpret_cast<uintptr_t>(buffer), BufferAlignment));
AMS_ASSERT(buffer_size == GetRequiredMemorySize(num_elements));
/* Set buffer. */
m_buffer = static_cast<u8 *>(buffer);
m_header = reinterpret_cast<Header *>(m_buffer);
/* Setup memory. */
this->InitializeMemory(num_elements, buffer_size, impl::AvailableIndexFinder::GetSignature());
/* Check that buffer was set up correctly. */
AMS_ASSERT(static_cast<u32>(buffer_size) == m_header->buffer_size);
/* Setup dummy leaf. */
this->SetDummyMemory();
}
public:
static constexpr size_t SizeOfNodes(size_t num_elements) {
return util::AlignUp(sizeof(Node) * num_elements, BufferAlignment);
}
static constexpr size_t SizeOfIndex(size_t num_elements) {
return impl::AvailableIndexFinder::GetRequiredMemorySize(num_elements);
}
static constexpr size_t GetRequiredMemorySize(size_t num_elements) {
return sizeof(Header) + SizeOfNodes(num_elements) + SizeOfIndex(num_elements);
}
private:
void SetDummyMemory() {
m_dummy_leaf.m_color = Color::Black;
m_dummy_leaf.m_parent = Index_Nil;
m_dummy_leaf.m_left = Index_Leaf;
m_dummy_leaf.m_right = Index_Leaf;
m_p_dummy_leaf = std::addressof(m_dummy_leaf);
}
void InitializeMemory(int num_elements, u32 buffer_size, u32 signature) {
/* Initialize the header. */
m_header->InitializeHeader(1, num_elements, 0, signature, buffer_size, sizeof(Node), sizeof(Member), 4, 4, 4, 4, 4, BufferAlignment);
/* Setup index finder. */
m_index_finder.Initialize(std::addressof(m_header->cur_elements), std::addressof(m_header->max_elements), m_buffer + sizeof(*m_header) + SizeOfNodes(num_elements));
/* Set nodes array. */
m_nodes = reinterpret_cast<Node *>(m_buffer + sizeof(*m_header));
}
Node *GetNode(int index) const {
if (index >= 0) {
return m_nodes + index;
} else {
return m_p_dummy_leaf;
}
}
constexpr ALWAYS_INLINE bool IsNil(int index) const {
return index < 0;
}
constexpr ALWAYS_INLINE bool IsLeaf(int index) const {
return index == Index_Leaf;
}
int GetRoot() const { return m_header->root_index; }
void SetRoot(int index) {
if (index == Index_Leaf) {
index = Index_Nil;
}
m_header->root_index = index;
}
int GetLMost() const { return m_header->left_most_index; }
void SetLMost(int index) { m_header->left_most_index = index; }
int GetRMost() const { return m_header->right_most_index; }
void SetRMost(int index) { m_header->right_most_index = index; }
int GetParent(int index) const {
return this->GetNode(index)->m_parent;
}
int AcquireIndex() { return m_index_finder.AcquireIndex(); }
void ReleaseIndex(int index) { return m_index_finder.ReleaseIndex(index); }
int EraseByIndex(int target_index) {
/* Setup tracking variables. */
const auto next_index = this->UncheckedPP(target_index);
auto *target_node = this->GetNode(target_index);
auto a_index = Index_Leaf;
auto *a_node = this->GetNode(a_index);
auto b_index = Index_Leaf;
auto *b_node = this->GetNode(b_index);
auto cur_index = target_index;
auto *cur_node = this->GetNode(cur_index);
if (cur_node->m_left == Index_Leaf) {
a_index = cur_node->m_right;
a_node = this->GetNode(a_index);
m_p_dummy_leaf->m_parent = cur_index;
} else {
if (cur_node->m_right == Index_Leaf) {
a_index = cur_node->m_left;
} else {
cur_index = next_index;
cur_node = this->GetNode(cur_index);
a_index = cur_node->m_right;
}
a_node = this->GetNode(a_index);
m_p_dummy_leaf->m_parent = cur_index;
}
/* Ensure the a node is updated (redundant) */
a_node = this->GetNode(a_index);
/* Update relevant metrics/links. */
if (cur_index == target_index) {
/* No left, but has right. */
b_index = target_node->m_parent;
b_node = this->GetNode(b_index);
if (a_index != Index_Leaf) {
a_node->m_parent = b_index;
}
if (this->GetRoot() == target_index) {
this->SetRoot(a_index);
} else if (b_node->m_left == target_index) {
b_node->m_left = a_index;
} else {
b_node->m_right = a_index;
}
if (this->GetLMost() == target_index) {
this->SetLMost((a_index != Index_Leaf) ? this->FindMinInSubtree(a_index) : b_index);
}
if (this->GetRMost() == target_index) {
this->SetRMost((a_index != Index_Leaf) ? this->FindMaxInSubtree(a_index) : b_index);
}
} else {
/* Has left or doesn't have right. */
/* Fix left links. */
this->GetNode(target_node->m_left)->m_parent = cur_index;
cur_node->m_left = target_node->m_left;
if (cur_index == target_node->m_right) {
b_index = cur_index;
b_node = this->GetNode(b_index);
} else {
b_index = cur_node->m_parent;
b_node = this->GetNode(b_index);
if (!this->IsNil(a_index)) {
a_node->m_parent = b_index;
}
b_node->m_left = a_index;
cur_node->m_right = target_node->m_right;
this->GetNode(target_node->m_right)->m_parent = cur_index;
}
if (this->GetRoot() == target_index) {
this->SetRoot(cur_index);
} else {
if (this->GetNode(target_node->m_parent)->m_left == target_index) {
this->GetNode(target_node->m_parent)->m_left = cur_index;
} else {
this->GetNode(target_node->m_parent)->m_right = cur_index;
}
}
cur_node->m_parent = target_node->m_parent;
std::swap(cur_node->m_color, target_node->m_color);
}
/* Ensure the tree remains balanced. */
if (target_node->m_color == Color::Black) {
while (true) {
if (a_index == this->GetRoot() || a_node->m_color != Color::Black) {
break;
}
if (a_index == b_node->m_left) {
cur_index = b_node->m_right;
cur_node = this->GetNode(cur_index);
if (cur_node->m_color == Color::Red) {
cur_node->m_color = Color::Black;
b_node->m_color = Color::Red;
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
this->RotateLeft(b_index);
cur_index = b_node->m_right;
cur_node = this->GetNode(cur_index);
}
if (this->IsNil(cur_index)) {
a_index = b_index;
a_node = b_node;
} else {
if (this->GetNode(cur_node->m_left)->m_color != Color::Black || this->GetNode(cur_node->m_right)->m_color != Color::Black) {
if (this->GetNode(cur_node->m_right)->m_color == Color::Black) {
this->GetNode(cur_node->m_left)->m_color = Color::Black;
cur_node->m_color = Color::Red;
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
this->RotateRight(cur_index);
cur_index = b_node->m_right;
cur_node = this->GetNode(cur_index);
}
cur_node->m_color = b_node->m_color;
b_node->m_color = Color::Black;
this->GetNode(cur_node->m_right)->m_color = Color::Black;
this->RotateLeft(b_index);
break;
}
cur_node->m_color = Color::Red;
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
a_index = b_index;
a_node = b_node;
}
} else {
cur_index = b_node->m_left;
cur_node = this->GetNode(cur_index);
if (cur_node->m_color == Color::Red) {
cur_node->m_color = Color::Black;
b_node->m_color = Color::Red;
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
this->RotateRight(b_index);
cur_index = b_node->m_left;
cur_node = this->GetNode(cur_index);
}
if (this->IsNil(cur_index)) {
a_index = b_index;
a_node = b_node;
} else {
if (this->GetNode(cur_node->m_right)->m_color != Color::Black || this->GetNode(cur_node->m_left)->m_color != Color::Black) {
if (this->GetNode(cur_node->m_left)->m_color == Color::Black) {
this->GetNode(cur_node->m_right)->m_color = Color::Black;
cur_node->m_color = Color::Red;
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
this->RotateLeft(cur_index);
cur_index = b_node->m_left;
cur_node = this->GetNode(cur_index);
}
cur_node->m_color = b_node->m_color;
b_node->m_color = Color::Black;
this->GetNode(cur_node->m_left)->m_color = Color::Black;
this->RotateRight(b_index);
break;
}
cur_node->m_color = Color::Red;
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
a_index = b_index;
a_node = b_node;
}
}
b_index = a_node->m_parent;
b_node = this->GetNode(b_index);
}
a_node->m_color = Color::Black;
}
/* Release the index. */
this->ReleaseIndex(target_index);
return target_index;
}
int FindIndex(const Member &elem) const {
return this->FindIndexSub(this->GetRoot(), elem);
}
int FindIndexSub(int index, const Member &elem) const {
if (index != Index_Nil) {
auto *node = this->GetNode(index);
if (Compare{}(elem, node->m_data)) {
if (!this->IsLeaf(node->m_left)) {
return this->FindIndexSub(node->m_left, elem);
}
} else {
if (!Compare{}(node->m_data, elem)) {
return index;
}
if (!this->IsLeaf(node->m_right)) {
return this->FindIndexSub(node->m_right, elem);
}
}
}
return Index_Nil;
}
int FindMaxInSubtree(int index) const {
int max = index;
for (auto *node = this->GetNode(index); !this->IsNil(node->m_right); node = this->GetNode(node->m_right)) {
max = node->m_right;
}
return max;
}
int FindMinInSubtree(int index) const {
int min = index;
for (auto *node = this->GetNode(index); !this->IsNil(node->m_left); node = this->GetNode(node->m_left)) {
min = node->m_left;
}
return min;
}
int InsertAt(bool before, int parent, const Member &elem) {
/* Get an index for the new element. */
const auto index = this->AcquireIndex();
/* Create the node. */
auto *node = this->GetNode(index);
node->m_color = Color::Red;
node->m_parent = parent;
node->m_right = Index_Leaf;
node->m_left = Index_Leaf;
std::memcpy(reinterpret_cast<u8 *>(std::addressof(node->m_data)), reinterpret_cast<const u8 *>(std::addressof(elem)), sizeof(node->m_data));
/* Fix up the parent node. */
auto *parent_node = this->GetNode(parent);
if (before) {
parent_node->m_left = index;
if (parent == this->GetLMost()) {
this->SetLMost(index);
}
} else {
parent_node->m_right = index;
if (parent == this->GetRMost()) {
this->SetRMost(index);
}
}
/* Ensure the tree is balanced. */
int cur_index = index;
while (true) {
auto *cur_node = this->GetNode(cur_index);
if (this->GetNode(cur_node->m_parent)->m_color != Color::Red) {
break;
}
auto *p_node = this->GetNode(cur_node->m_parent);
auto *g_node = this->GetNode(p_node->m_parent);
if (cur_node->m_parent == g_node->m_left) {
if (auto *gr_node = this->GetNode(g_node->m_right); gr_node->m_color == Color::Red) {
p_node->m_color = Color::Black;
gr_node->m_color = Color::Black;
g_node->m_color = Color::Red;
AMS_ASSERT(m_p_dummy_leaf->m_color != Color::Red);
cur_index = p_node->m_parent;
continue;
}
if (cur_index == p_node->m_right) {
cur_index = cur_node->m_parent;
cur_node = this->GetNode(cur_index);
this->RotateLeft(cur_index);
}
p_node = this->GetNode(cur_node->m_parent);
p_node->m_color = Color::Black;
g_node = this->GetNode(p_node->m_parent);
g_node->m_color = Color::Red;
AMS_ASSERT(m_p_dummy_leaf->m_color != Color::Red);
this->RotateRight(p_node->m_parent);
} else {
if (auto *gl_node = this->GetNode(g_node->m_left); gl_node->m_color == Color::Red) {
p_node->m_color = Color::Black;
gl_node->m_color = Color::Black;
g_node->m_color = Color::Red;
AMS_ASSERT(m_p_dummy_leaf->m_color != Color::Red);
cur_index = p_node->m_parent;
continue;
}
if (cur_index == p_node->m_left) {
cur_index = cur_node->m_parent;
cur_node = this->GetNode(cur_index);
this->RotateRight(cur_index);
}
p_node = this->GetNode(cur_node->m_parent);
p_node->m_color = Color::Black;
g_node = this->GetNode(p_node->m_parent);
g_node->m_color = Color::Red;
AMS_ASSERT(m_p_dummy_leaf->m_color != Color::Red);
this->RotateLeft(p_node->m_parent);
}
}
/* Set root color. */
this->GetNode(this->GetRoot())->m_color = Color::Black;
return index;
}
int InsertNoHint(bool before, const Member &elem) {
int cur_index = this->GetRoot();
int prev_index = Index_Nil;
bool less = true;
while (cur_index != Index_Nil && cur_index != Index_Leaf) {
auto *node = this->GetNode(cur_index);
prev_index = cur_index;
if (before) {
less = Compare{}(node->m_data, elem);
} else {
less = Compare{}(elem, node->m_data);
}
if (less) {
cur_index = node->m_left;
} else {
cur_index = node->m_right;
}
}
if (cur_index == Index_Nil) {
/* Create a new node. */
const auto index = this->AcquireIndex();
auto *node = this->GetNode(index);
node->m_color = Color::Black;
node->m_parent = Index_Nil;
node->m_right = Index_Leaf;
node->m_left = Index_Leaf;
std::memcpy(reinterpret_cast<u8 *>(std::addressof(node->m_data)), reinterpret_cast<const u8 *>(std::addressof(elem)), sizeof(node->m_data));
this->SetRoot(index);
this->SetLMost(index);
this->SetRMost(index);
return index;
} else {
auto *compare_node = this->GetNode(prev_index);
if (less) {
if (prev_index == this->GetLMost()) {
return this->InsertAt(less, prev_index, elem);
} else {
compare_node = this->GetNode(this->UncheckedMM(prev_index));
}
}
if (Compare{}(compare_node->m_data, elem)) {
return this->InsertAt(less, prev_index, elem);
} else {
return Index_Nil;
}
}
}
void RotateLeft(int index) {
/* Determine indices. */
const auto p_index = this->GetParent(index);
const auto r_index = this->GetNode(index)->m_right;
const auto l_index = this->GetNode(index)->m_left;
const auto rl_index = this->GetNode(r_index)->m_left;
const auto rr_index = this->GetNode(r_index)->m_right;
/* Get nodes. */
auto *node = this->GetNode(index);
auto *p_node = this->GetNode(p_index);
auto *r_node = this->GetNode(r_index);
auto *l_node = this->GetNode(l_index);
auto *rl_node = this->GetNode(rl_index);
auto *rr_node = this->GetNode(rr_index);
/* Perform the rotation. */
if (p_index == Index_Nil) {
r_node->m_parent = Index_Nil;
m_header->root_index = r_index;
} else if (p_node->m_left == index) {
p_node->SetLeft(r_index, r_node, p_index);
} else {
p_node->SetRight(r_index, r_node, p_index);
}
r_node->SetLeft(index, node, r_index);
r_node->SetRight(rr_index, rr_node, r_index);
node->SetLeft(l_index, l_node, index);
node->SetRight(rl_index, rl_node, index);
}
void RotateRight(int index) {
/* Determine indices. */
const auto p_index = this->GetParent(index);
const auto l_index = this->GetNode(index)->m_left;
const auto ll_index = this->GetNode(l_index)->m_left;
const auto lr_index = this->GetNode(l_index)->m_right;
const auto r_index = this->GetNode(index)->m_right;
/* Get nodes. */
auto *node = this->GetNode(index);
auto *p_node = this->GetNode(p_index);
auto *l_node = this->GetNode(l_index);
auto *ll_node = this->GetNode(ll_index);
auto *lr_node = this->GetNode(lr_index);
auto *r_node = this->GetNode(r_index);
/* Perform the rotation. */
if (p_index == Index_Nil) {
l_node->m_parent = Index_Nil;
m_header->root_index = l_index;
} else if (p_node->m_left == index) {
p_node->SetLeft(l_index, l_node, p_index);
} else {
p_node->SetRight(l_index, l_node, p_index);
}
l_node->SetLeft(ll_index, ll_node, l_index);
l_node->SetRight(index, node, l_index);
node->SetLeft(lr_index, lr_node, index);
node->SetRight(r_index, r_node, index);
}
int UncheckedMM(int index) const {
auto *node = this->GetNode(index);
if (this->IsNil(index)) {
index = this->GetRMost();
node = this->GetNode(index);
} else if (this->IsNil(node->m_left)) {
int parent = node->m_parent;
Node *p;
for (p = this->GetNode(parent); !this->IsNil(parent) && index == p->m_left; p = this->GetNode(parent)) {
index = parent;
node = p;
parent = p->m_parent;
}
if (!this->IsNil(index)) {
index = parent;
node = p;
}
} else {
index = this->FindMaxInSubtree(node->m_left);
node = this->GetNode(index);
}
if (this->IsNil(index)) {
return Index_Leaf;
} else {
return index;
}
}
int UncheckedPP(int index) const {
auto *node = this->GetNode(index);
if (!this->IsNil(index)) {
if (this->IsNil(node->m_right)) {
int parent = node->m_parent;
Node *p;
for (p = this->GetNode(parent); !this->IsNil(parent) && index == p->m_right; p = this->GetNode(parent)) {
index = parent;
node = p;
parent = p->m_parent;
}
index = parent;
node = p;
} else {
index = this->FindMinInSubtree(node->m_right);
node = this->GetNode(index);
}
}
if (this->IsNil(index)) {
return Index_Leaf;
} else {
return index;
}
}
public:
void Initialize(size_t num_elements, void *buffer, size_t buffer_size) {
AMS_ASSERT(num_elements <= max_size);
return this->InitializeImpl(static_cast<int>(num_elements), buffer, buffer_size);
}
iterator begin() { return iterator(*this); }
const_iterator begin() const { return const_iterator(*this); }
iterator end() { return m_end_iterator; }
const_iterator end() const { return m_end_iterator; }
size_t size() const { return m_header->cur_elements; }
void clear() {
const auto num_elements = m_header->max_elements;
const auto buffer_size = m_header->buffer_size;
AMS_ASSERT(buffer_size == static_cast<u32>(GetRequiredMemorySize(num_elements)));
return this->InitializeMemory(num_elements, buffer_size, impl::AvailableIndexFinder::GetSignature());
}
bool erase(const Member &elem) {
const auto range = this->equal_range(elem);
if (range.first != range.last) {
this->EraseByIndex(range.first);
return true;
} else {
return false;
}
}
iterator find(const Member &elem) {
if (const auto index = this->FindIndex(elem); index >= 0) {
return iterator(*this, index);
} else {
return this->end();
}
}
const_iterator find(const Member &elem) const {
if (const auto index = this->FindIndex(elem); index >= 0) {
return const_iterator(*this, index);
} else {
return this->end();
}
}
std::pair<iterator, bool> insert(const Member &elem) {
const auto index = this->InsertNoHint(false, elem);
const auto it = iterator(*this, index);
return std::make_pair(it, !this->IsNil(index));
}
IndexPair equal_range(const Member &elem) {
/* Get node to start iteration. */
auto cur_index = this->GetRoot();
auto cur_node = this->GetNode(cur_index);
auto min_index = Index_Leaf;
auto min_node = this->GetNode(min_index);
auto max_index = Index_Leaf;
auto max_node = this->GetNode(max_index);
/* Iterate until current is leaf, to find min/max. */
while (cur_index != Index_Leaf) {
if (Compare{}(cur_node->m_data, elem)) {
cur_index = cur_node->m_right;
cur_node = this->GetNode(cur_index);
} else {
if (max_index == Index_Leaf && Compare{}(elem, cur_node->m_data)) {
max_index = cur_index;
max_node = this->GetNode(max_index);
}
min_index = cur_index;
min_node = this->GetNode(min_index);
cur_index = cur_node->m_left;
cur_node = this->GetNode(cur_index);
}
}
/* Iterate again, to find correct range extent for max. */
cur_index = (max_index == Index_Leaf) ? this->GetRoot() : max_node->m_left;
cur_node = this->GetNode(cur_index);
while (cur_index != Index_Leaf) {
if (Compare{}(elem, cur_node->m_data)) {
max_index = cur_index;
max_node = cur_node;
cur_index = cur_node->m_left;
} else {
cur_index = cur_node->m_right;
}
cur_node = this->GetNode(cur_index);
}
AMS_UNUSED(min_node);
return IndexPair{min_index, max_index};
}
};
}

View File

@@ -13,8 +13,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Scope guard logic lovingly taken from Andrei Alexandrescu's "Systemic Error Handling in C++" */
/* Scope guard logic lovingly taken from Andrei Alexandrescu's "Systemic Error Handling in C++" */
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>

View File

@@ -69,7 +69,21 @@ namespace ams::mitm::socket::resolver {
"127.0.0.1 receive-%.dg.srv.nintendo.net receive-%.er.srv.nintendo.net\n";
constinit os::SdkMutex g_redirection_lock;
std::unordered_map<std::string, ams::socket::InAddrT> g_redirection_map;
std::vector<std::pair<std::string, ams::socket::InAddrT>> g_redirection_list;
void RemoveRedirection(const char *hostname) {
for (auto it = g_redirection_list.begin(); it != g_redirection_list.end(); ++it) {
if (std::strcmp(it->first.c_str(), hostname) == 0) {
g_redirection_list.erase(it);
break;
}
}
}
void AddRedirection(const char *hostname, ams::socket::InAddrT addr) {
RemoveRedirection(hostname);
g_redirection_list.emplace(g_redirection_list.begin(), std::string(hostname), addr);
}
constinit char g_specific_emummc_hosts_path[0x40] = {};
@@ -111,6 +125,8 @@ namespace ams::mitm::socket::resolver {
current_address = 0;
work = static_cast<u32>(c - '0');
state = State::Ip1;
} else if (c == '\n') {
state = State::BeginLine;
} else {
state = State::IgnoredLine;
}
@@ -206,7 +222,7 @@ namespace ams::mitm::socket::resolver {
AMS_ABORT_UNLESS(work < sizeof(current_hostname));
current_hostname[work] = '\x00';
g_redirection_map[static_cast<const char *>(current_hostname)] = current_address;
AddRedirection(current_hostname, current_address);
work = 0;
if (c == '\n') {
@@ -229,7 +245,7 @@ namespace ams::mitm::socket::resolver {
AMS_ABORT_UNLESS(work < sizeof(current_hostname));
current_hostname[work] = '\x00';
g_redirection_map[static_cast<const char *>(current_hostname)] = current_address;
AddRedirection(current_hostname, current_address);
}
}
@@ -293,7 +309,7 @@ namespace ams::mitm::socket::resolver {
std::scoped_lock lk(g_redirection_lock);
/* Clear the redirections map. */
g_redirection_map.clear();
g_redirection_list.clear();
/* Open log file. */
::FsFile log_file;
@@ -362,7 +378,7 @@ namespace ams::mitm::socket::resolver {
/* Print the redirections. */
Log(log_file, "Redirections:\n");
for (const auto &[host, address] : g_redirection_map) {
for (const auto &[host, address] : g_redirection_list) {
Log(log_file, " `%s` -> %u.%u.%u.%u\n", host.c_str(), (address >> 0) & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF, (address >> 24) & 0xFF);
}
}
@@ -370,7 +386,7 @@ namespace ams::mitm::socket::resolver {
bool GetRedirectedHostByName(ams::socket::InAddrT *out, const char *hostname) {
std::scoped_lock lk(g_redirection_lock);
for (const auto &[host, address] : g_redirection_map) {
for (const auto &[host, address] : g_redirection_list) {
if (wildcardcmp(host.c_str(), hostname)) {
*out = address;
return true;

View File

@@ -111,10 +111,12 @@ namespace ams::mitm::socket::resolver {
R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession());
u16 port = 0;
for (const char *cur = reinterpret_cast<const char *>(srv.GetPointer()); *cur != 0; ++cur) {
AMS_ABORT_UNLESS(std::isdigit(static_cast<unsigned char>(*cur)));
port *= 10;
port += *cur - '0';
if (srv.GetPointer() != nullptr) {
for (const char *cur = reinterpret_cast<const char *>(srv.GetPointer()); *cur != 0; ++cur) {
AMS_ABORT_UNLESS(std::isdigit(static_cast<unsigned char>(*cur)));
port *= 10;
port += *cur - '0';
}
}
LogDebug("[%016lx]: Redirecting %s:%u to %u.%u.%u.%u\n", this->client_info.program_id.value, hostname, port, (redirect_addr >> 0) & 0xFF, (redirect_addr >> 8) & 0xFF, (redirect_addr >> 16) & 0xFF, (redirect_addr >> 24) & 0xFF);
@@ -162,10 +164,12 @@ namespace ams::mitm::socket::resolver {
R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession());
u16 port = 0;
for (const char *cur = reinterpret_cast<const char *>(srv.GetPointer()); *cur != 0; ++cur) {
AMS_ABORT_UNLESS(std::isdigit(static_cast<unsigned char>(*cur)));
port *= 10;
port += *cur - '0';
if (srv.GetPointer() != nullptr) {
for (const char *cur = reinterpret_cast<const char *>(srv.GetPointer()); *cur != 0; ++cur) {
AMS_ABORT_UNLESS(std::isdigit(static_cast<unsigned char>(*cur)));
port *= 10;
port += *cur - '0';
}
}
LogDebug("[%016lx]: Redirecting %s:%u to %u.%u.%u.%u\n", this->client_info.program_id.value, hostname, port, (redirect_addr >> 0) & 0xFF, (redirect_addr >> 8) & 0xFF, (redirect_addr >> 16) & 0xFF, (redirect_addr >> 24) & 0xFF);

View File

@@ -380,7 +380,7 @@ namespace ams::mitm::fs {
/* Try to get a storage from the cache. */
{
std::shared_ptr<fs::IStorage> cached_storage = GetStorageCacheEntry(this->client_info.program_id);
std::shared_ptr<fs::IStorage> cached_storage = GetStorageCacheEntry(data_id);
if (cached_storage != nullptr) {
out.SetValue(MakeSharedStorage(cached_storage), target_object_id);
return ResultSuccess();
@@ -403,7 +403,7 @@ namespace ams::mitm::fs {
new_storage = std::move(layered_storage);
}
SetStorageCacheEntry(this->client_info.program_id, &new_storage);
SetStorageCacheEntry(data_id, &new_storage);
out.SetValue(MakeSharedStorage(new_storage), target_object_id);
}

View File

@@ -1 +0,0 @@
out

View File

@@ -9,13 +9,39 @@ endif
TOPDIR ?= $(CURDIR)
include $(DEVKITPRO)/devkitA64/base_rules
export AMSLIBSDIR := $(TOPDIR)/../libraries
AMSBRANCH := $(shell git symbolic-ref --short HEAD)
AMSHASH = $(shell git rev-parse --short=16 HEAD)
AMSREV := $(AMSBRANCH)-$(shell git rev-parse --short HEAD)
ifneq (, $(strip $(shell git status --porcelain 2>/dev/null)))
AMSREV := $(AMSREV)-dirty
endif
ifeq ($(PLATFORM), qemu)
export PLATFORM := qemu
PLATFORM_SOURCES := src/platform/qemu
PLATFORM_DEFINES := -DPLATFORM_QEMU -DMAX_CORE=4 -DMAX_BCR=6 -DMAX_WCR=4
else ifeq ($(PLATFORM), tegra-t210-arm-tf)
export PLATFORM := tegra-t210-arm-tf
PLATFORM_SOURCES := src/platform/tegra
PLATFORM_DEFINES := -DPLATFORM_TEGRA -DPLATFORM_TEGRA_T210_ARM_TF -DMAX_CORE=4 -DMAX_BCR=6 -DMAX_WCR=4
else
export PLATFORM := tegra-t210-nintendo
PLATFORM_SOURCES := src/platform/tegra
PLATFORM_DEFINES := -DPLATFORM_TEGRA -D DPLATFORM_TEGRA_T210_NINTENDO -DMAX_CORE=4 -DMAX_BCR=6 -DMAX_WCR=4
endif
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
@@ -25,43 +51,62 @@ endif
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := src src/lib
SOURCES := src src/libc src/platform src/gdb $(PLATFORM_SOURCES)
DATA := data
INCLUDES := include ../common/include
INCLUDES :=
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a -mtune=cortex-a57
DEFINES := -DATMOSPHERE_GIT_BRANCH=\"$(AMSBRANCH)\" -DATMOSPHERE_GIT_REV=\"$(AMSREV)\"
# Note: -ffixed-x18 and -mgeneral-regs-only are very important and must be enabled
ARCH := -march=armv8-a -mtune=cortex-a57 -mgeneral-regs-only -ffixed-x18 -Wno-psabi
DEFINES := $(PLATFORM_DEFINES)
CFLAGS := \
-g \
-O2 \
-ffunction-sections \
-fdata-sections \
-mgeneral-regs-only \
-fomit-frame-pointer \
-std=gnu11 \
-Werror \
-Wall \
-Wno-main \
$(ARCH) $(DEFINES)
-g \
-fmacro-prefix-map=$(TOPDIR)/src/= \
-Os \
-ffunction-sections \
-fdata-sections \
-fomit-frame-pointer \
-fno-asynchronous-unwind-tables \
-fno-unwind-tables \
-fno-stack-protector \
-fstrict-volatile-bitfields \
-Wall \
-Werror \
-Wno-main \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__CCPLEX__
export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \
-Wl,--wrap,__cxa_throw \
-Wl,--wrap,__cxa_rethrow \
-Wl,--wrap,__cxa_allocate_exception \
-Wl,--wrap,__cxa_free_exception \
-Wl,--wrap,__cxa_begin_catch \
-Wl,--wrap,__cxa_end_catch \
-Wl,--wrap,__cxa_call_unexpected \
-Wl,--wrap,__cxa_call_terminate \
-Wl,--wrap,__gxx_personality_v0 \
-Wl,--wrap,_Unwind_Resume \
-Wl,--wrap,_Unwind_Resume \
-Wl,--wrap,_ZSt19__throw_logic_errorPKc \
-Wl,--wrap,_ZSt20__throw_length_errorPKc \
-Wl,--wrap,_ZNSt11logic_errorC2EPKc
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
CFLAGS += $(INCLUDE)
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++2a
CFLAGS += -std=gnu11
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(TOPDIR)/linker.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
ASFLAGS := -g $(ARCH) $(DEFINES)
LDFLAGS = -specs=$(TOPDIR)/linker.specs -nostartfiles -nostdlib -g $(ARCH) $(CXXWRAPS) -Wl,-Map,$(notdir $*.map)
LIBS :=
LIBS := -lgcc
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS :=
LIBDIRS := $(AMSLIBSDIR)/libvapours
#---------------------------------------------------------------------------------
@@ -100,7 +145,7 @@ endif
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
@@ -109,11 +154,29 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
.PHONY: $(BUILD) clean all
.PHONY: $(BUILD) clean all qemu qemudbg
#---------------------------------------------------------------------------------
all: $(BUILD)
ifeq ($(PLATFORM), qemu)
export QEMU := qemu-system-aarch64
#export QEMU := ~/qemu/aarch64-softmmu/qemu-system-aarch64
QEMUFLAGS := -nographic -machine virt,virtualization=on,accel=tcg,gic-version=2 -cpu cortex-a57 -smp 4 -m 1024\
-kernel thermosphere.elf -d unimp,guest_errors -semihosting-config enable,target=native\
-chardev socket,id=uart,port=2222,host=0.0.0.0,server,nowait -chardev stdio,id=test -serial chardev:uart\
-monitor tcp:localhost:3333,server,nowait
qemu: all
@$(QEMU) $(QEMUFLAGS)
qemudbg: all
@$(QEMU) $(QEMUFLAGS) -s -S
endif
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
@@ -151,7 +214,7 @@ $(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o : %.bin
%.bin.o %_bin.h: %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)

View File

@@ -1,6 +0,0 @@
Thermosphère
=====
![License](https://img.shields.io/badge/License-GPLv2-blue.svg)
Thermosphère is a hypervisor for the Nintendo Switch.

View File

@@ -1,55 +1,209 @@
OUTPUT_FORMAT("elf64-littleaarch64")
OUTPUT_ARCH(aarch64)
ENTRY(_start)
PHDRS
{
main PT_LOAD;
}
MEMORY
{
mainVa : ORIGIN = 0x7FFFE10000, LENGTH = 2M - 64K
}
SECTIONS
{
. = 0x800D0000;
__start_pa__ = ABSOLUTE(ORIGIN(main));
__temp_pa__ = ABSOLUTE(ORIGIN(temp));
__max_image_size__ = ABSOLUTE(LENGTH(main));
__max_temp_size__ = ABSOLUTE(LENGTH(temp) - 0x1000);
.text :
{
. = ALIGN(8);
__start__ = ABSOLUTE(.);
KEEP(*(.crt0*));
*(.text.unlikely .text.*_unlikely .text.unlikely.*)
*(.text.exit .text.exit.*)
*(.text.startup .text.startup.*)
*(.text.hot .text.hot.*)
*(.text .stub .text.* .gnu.linkonce.t.*)
. = ALIGN(0x800);
__vectors_start__ = ABSOLUTE(.);
KEEP(*(.vectors*));
__vectors_end__ = ABSOLUTE(.);
ASSERT(__vectors_end__ - __vectors_start__ <= 0x800, "Exception vectors section should be max 0x800 in size!");
. = ALIGN(8);
} >mainVa AT>main :main
.init :
{
KEEP( *(.init) )
. = ALIGN(8);
} >mainVa AT>main :main
.plt :
{
*(.plt)
*(.iplt)
. = ALIGN(8);
} >mainVa AT>main :main
.fini :
{
KEEP( *(.fini) )
. = ALIGN(8);
} >mainVa AT>main :main
.rodata :
{
*(.rodata .rodata.* .gnu.linkonce.r.*)
SORT(CONSTRUCTORS)
. = ALIGN(8);
} >mainVa AT>main :main
.got : { __got_start__ = ABSOLUTE(.); *(.got) *(.igot) } >mainVa AT>main :main
.got.plt : { *(.got.plt) *(.igot.plt) __got_end__ = ABSOLUTE(.);} >mainVa AT>main :main
.preinit_array :
{
. = ALIGN(8);
PROVIDE (__preinit_array_start = ABSOLUTE(.));
KEEP (*(.preinit_array))
PROVIDE (__preinit_array_end = ABSOLUTE(.));
ASSERT(__preinit_array_end == __preinit_array_start, ".preinit_array not empty!");
. = ALIGN(8);
} >mainVa AT>main :main
.init_array :
{
PROVIDE (__init_array_start = ABSOLUTE(.));
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
PROVIDE (__init_array_end = ABSOLUTE(.));
ASSERT(__init_array_end == __init_array_start, ".init_array not empty!");
} >mainVa AT>main :main
.fini_array :
{
. = ALIGN(8);
PROVIDE (__fini_array_start = ABSOLUTE(.));
KEEP (*(.fini_array))
KEEP (*(SORT(.fini_array.*)))
PROVIDE (__fini_array_end = ABSOLUTE(.));
. = ALIGN(8);
ASSERT(__fini_array_end == __fini_array_start, ".fini_array not empty!");
} >mainVa AT>main :main
.ctors :
{
. = ALIGN(8);
KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
. = ALIGN(8);
} >mainVa AT>main :main
.dtors ALIGN(8) :
{
. = ALIGN(8);
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
. = ALIGN(8);
} >mainVa AT>main :main
.data ALIGN(8) :
{
*(.data .data.* .gnu.linkonce.d.*)
CONSTRUCTORS
. = ALIGN(8);
} >mainVa AT>main :main
.dynamic : { *(.dynamic) } >mainVa AT>main :main
.interp : { *(.interp) } >mainVa AT>main :main
.note.gnu.build-id : { *(.note.gnu.build-id) } >mainVa AT>main :main
.hash : { *(.hash) } >mainVa AT>main :main
.gnu.hash : { *(.gnu.hash) } >mainVa AT>main :main
.gnu.version : { *(.gnu.version) } >mainVa AT>main :main
.gnu.version_d : { *(.gnu.version_d) } >mainVa AT>main :main
.gnu.version_r : { *(.gnu.version_r) } >mainVa AT>main :main
.dynsym : { *(.dynsym) } >mainVa AT>main :main
.dynstr : { *(.dynstr) } >mainVa AT>main :main
.rela.dyn : { *(.rela.*); __main_end__ = ABSOLUTE(.);} >mainVa AT>main :main
.bss (NOLOAD) :
{
__bss_start__ = ABSOLUTE(.);
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
} >mainVa :NONE
.tempbss (NOLOAD) :
{
. = ALIGN(0x1000);
__real_bss_end__ = ABSOLUTE(.);
__image_size__ = ABSOLUTE(__real_bss_end__ - __start__);
/*ASSERT(__image_size__ <= __max_image_size__, "Image too big!");*/
*(.tempbss .tempbss.*)
. = ALIGN(0x1000);
__bss_end__ = ABSOLUTE(.);
__temp_size__ = ABSOLUTE(__bss_end__ - __real_bss_end__);
ASSERT(__temp_size__ <= __max_temp_size__, "tempbss too big!");
} >mainVa :NONE
. = ALIGN(4);
.text : {
PROVIDE(lds_thermo_start = .);
start.o (.text*)
*(.text*)
}
. = ALIGN(8);
.rodata : {
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*)))
}
. = ALIGN(8);
.data : {
*(.data*)
}
/* Shit we keep in the elf but otherwise discard */
.eh_frame_hdr (NOLOAD) : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } >mainVa :NONE
.eh_frame (NOLOAD) : { KEEP (*(.eh_frame)) *(.eh_frame.*) } >mainVa :NONE
.gcc_except_table (NOLOAD) : { *(.gcc_except_table .gcc_except_table.*) } >mainVa :NONE
.gnu_extab (NOLOAD) : { *(.gnu_extab*) } >mainVa :NONE
.exception_ranges (NOLOAD) : { *(.exception_ranges .exception_ranges*) } >mainVa :NONE
/* Uninitialised data */
. = ALIGN(8);
PROVIDE(lds_bss_start = .);
.bss (NOLOAD) : {
*(.bss*) . = ALIGN(8);
}
PROVIDE(lds_bss_end = .);
/* ==================
==== Metadata ====
================== */
/* EL2 stack */
. = ALIGN(16);
. += 0x10000; /* 64 KiB stack */
el2_stack_end = .;
/* Discard sections that difficult post-processing */
/DISCARD/ : { *(.group .comment .note) }
/* Page align the end of binary */
. = ALIGN(512);
PROVIDE(lds_el2_thermo_end = .);
/* Stabs debugging sections. */
.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) }
/* EL1 stack */
. = ALIGN(16);
. += 0x10000; /* 64 KiB stack */
el1_stack_end = .;
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
lds_thermo_end = .;
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/DISCARD/ : { *(.dynstr*) }
/DISCARD/ : { *(.dynamic*) }
/DISCARD/ : { *(.plt*) }
/DISCARD/ : { *(.interp*) }
/DISCARD/ : { *(.gnu*) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info) }
.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) }
}

View File

@@ -1,7 +1,4 @@
%rename link old_link
*link:
%(old_link) -T %:getenv(TOPDIR /linker.ld) --nmagic --gc-sections
*startfile:
crti%O%s crtbegin%O%s
%(old_link) -T %:getenv(TOPDIR /%:getenv(PLATFORM .mem)) -T %:getenv(TOPDIR /linker.ld) -no-pie --nmagic --gc-sections

6
thermosphere/qemu.mem Normal file
View File

@@ -0,0 +1,6 @@
MEMORY
{
NULL : ORIGIN = 0, LENGTH = 0x1000
main : ORIGIN = 0x60000000, LENGTH = 64M /* QEMU's memory map changes dynamically? */
temp : ORIGIN = 0x64000000, LENGTH = 64M
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#include "defines.hpp"
extern "C" {
/* Redefine abort to trigger these handlers. */
void abort();
/* Redefine C++ exception handlers. Requires wrap linker flag. */
#define WRAP_ABORT_FUNC(func) void NORETURN __wrap_##func(void) { abort(); __builtin_unreachable(); }
WRAP_ABORT_FUNC(__cxa_pure_virtual)
WRAP_ABORT_FUNC(__cxa_throw)
WRAP_ABORT_FUNC(__cxa_rethrow)
WRAP_ABORT_FUNC(__cxa_allocate_exception)
WRAP_ABORT_FUNC(__cxa_free_exception)
WRAP_ABORT_FUNC(__cxa_begin_catch)
WRAP_ABORT_FUNC(__cxa_end_catch)
WRAP_ABORT_FUNC(__cxa_call_unexpected)
WRAP_ABORT_FUNC(__cxa_call_terminate)
WRAP_ABORT_FUNC(__gxx_personality_v0)
WRAP_ABORT_FUNC(_ZSt19__throw_logic_errorPKc)
WRAP_ABORT_FUNC(_ZSt20__throw_length_errorPKc)
WRAP_ABORT_FUNC(_ZNSt11logic_errorC2EPKc)
/* TODO: We may wish to consider intentionally not defining an _Unwind_Resume wrapper. */
/* This would mean that a failure to wrap all exception functions is a linker error. */
WRAP_ABORT_FUNC(_Unwind_Resume)
#undef WRAP_ABORT_FUNC
}
/* Custom abort handler, so that std::abort will trigger these. */
void abort()
{
#ifndef PLATFORM_QEMU
__builtin_trap();
#endif
for (;;);
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#define EXCEP_STACK_FRAME_SIZE 0x140
.macro FUNCTION name
.section .text.\name, "ax", %progbits
.global \name
.type \name, %function
.func \name
.cfi_sections .debug_frame
.cfi_startproc
\name:
.endm
.macro END_FUNCTION
.cfi_endproc
.endfunc
.endm

View File

@@ -0,0 +1,156 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#include "hvisor_cpu_caches.hpp"
#define DEFINE_CACHE_RANGE_FUNC(isn, name, cache, post)\
void name(const void *addr, size_t size)\
{\
u32 lineCacheSize = GetSmallest##cache##CacheLineSize();\
uintptr_t begin = reinterpret_cast<uintptr_t>(addr) & ~(lineCacheSize - 1);\
uintptr_t end = (reinterpret_cast<uintptr_t>(addr) + size + lineCacheSize - 1) & ~(lineCacheSize - 1);\
for (uintptr_t pos = begin; pos < end; pos += lineCacheSize) {\
__asm__ __volatile__ (isn ", %0" :: "r"(pos) : "memory");\
}\
post;\
}
namespace {
ALWAYS_INLINE void SelectCacheLevel(bool instructionCache, u32 level)
{
u32 ibit = instructionCache ? 1 : 0;
u32 lbits = (level & 7) << 1;
THERMOSPHERE_SET_SYSREG(csselr_el1, lbits | ibit);
ams::hvisor::cpu::isb();
}
[[gnu::optimize("O2")]] ALWAYS_INLINE void InvalidateDataCacheLevel(u32 level)
{
SelectCacheLevel(false, level);
u32 ccsidr = static_cast<u32>(THERMOSPHERE_GET_SYSREG(ccsidr_el1));
u32 numWays = 1 + ((ccsidr >> 3) & 0x3FF);
u32 numSets = 1 + ((ccsidr >> 13) & 0x7FFF);
u32 wayShift = __builtin_clz(numWays);
u32 setShift = (ccsidr & 7) + 4;
u32 lbits = (level & 7) << 1;
for (u32 way = 0; way < numWays; way++) {
for (u32 set = 0; set < numSets; set++) {
u64 val = ((u64)way << wayShift) | ((u64)set << setShift) | lbits;
__asm__ __volatile__ ("dc isw, %0" :: "r"(val) : "memory");
}
}
}
ALWAYS_INLINE void CleanInvalidateDataCacheLevel(u32 level)
{
SelectCacheLevel(false, level);
u32 ccsidr = static_cast<u32>(THERMOSPHERE_GET_SYSREG(ccsidr_el1));
u32 numWays = 1 + ((ccsidr >> 3) & 0x3FF);
u32 numSets = 1 + ((ccsidr >> 13) & 0x7FFF);
u32 wayShift = __builtin_clz(numWays);
u32 setShift = (ccsidr & 7) + 4;
u32 lbits = (level & 7) << 1;
for (u32 way = 0; way < numWays; way++) {
for (u32 set = 0; set < numSets; set++) {
u64 val = ((u64)way << wayShift) | ((u64)set << setShift) | lbits;
__asm__ __volatile__ ("dc cisw, %0" :: "r"(val) : "memory");
}
}
}
[[gnu::optimize("O2")]] ALWAYS_INLINE void InvalidateDataCacheLevels(u32 from, u32 to)
{
// Let's hope it doesn't generate a stack frame...
for (u32 level = from; level < to; level++) {
InvalidateDataCacheLevel(level);
}
ams::hvisor::cpu::dsbSy();
ams::hvisor::cpu::isb();
}
}
namespace ams::hvisor::cpu {
DEFINE_CACHE_RANGE_FUNC("dc civac", CleanInvalidateDataCacheRange, Data, dsbSy())
DEFINE_CACHE_RANGE_FUNC("dc cvau", CleanDataCacheRangePoU, Data, dsb())
DEFINE_CACHE_RANGE_FUNC("ic ivau", InvalidateInstructionCacheRangePoU, Instruction, dsb(); isb())
void HandleSelfModifyingCodePoU(const void *addr, size_t size)
{
// See docs for ctr_el0.{dic, idc}. It's unclear when these bits have been added, but they're
// RES0 if not implemented, so that's fine
u32 ctr = static_cast<u32>(THERMOSPHERE_GET_SYSREG(ctr_el0));
if (!(ctr & BIT(28))) {
CleanDataCacheRangePoU(addr, size);
}
if (!(ctr & BIT(29))) {
InvalidateInstructionCacheRangePoU(addr, size);
} else {
// Make sure we have at least a dsb/isb
dsb();
isb();
}
}
[[gnu::optimize("O2")]] void ClearSharedDataCachesOnBoot(void)
{
u32 clidr = static_cast<u32>(THERMOSPHERE_GET_SYSREG(clidr_el1));
u32 louis = (clidr >> 21) & 7;
u32 loc = (clidr >> 24) & 7;
InvalidateDataCacheLevels(louis, loc);
}
[[gnu::optimize("O2")]] void ClearLocalDataCacheOnBoot(void)
{
u32 clidr = static_cast<u32>(THERMOSPHERE_GET_SYSREG(clidr_el1));
u32 louis = (clidr >> 21) & 7;
InvalidateDataCacheLevels(0, louis);
}
/* Ok so:
- cache set/way ops can't really be virtualized
- since we have only one guest OS & don't care about security (for space limitations),
we do the following:
- ignore all cache s/w ops applying before the Level Of Unification Inner Shareable (L1, typically).
These clearly break coherency and should only be done once, on power on/off/suspend/resume only. And we already
do it ourselves...
- allow ops after the LoUIS, but do it ourselves and ignore the next (numSets*numWay - 1) requests. This is because
we have to handle Nintendo's dodgy code (check if SetWay == 0)
- transform all s/w cache ops into clean and invalidate
*/
void HandleTrappedSetWayOperation(u32 val)
{
u32 clidr = static_cast<u32>(THERMOSPHERE_GET_SYSREG(clidr_el1));
u32 louis = (clidr >> 21) & 7;
u32 level = val >> 1 & 7;
u32 setway = val >> 3;
if (level < louis) {
return;
}
if (setway == 0) {
CleanInvalidateDataCacheLevel(level);
dsbSy();
isb();
}
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "hvisor_cpu_instructions.hpp"
#include "hvisor_cpu_sysreg_general.hpp"
namespace ams::hvisor::cpu {
inline u32 GetInstructionCachePolicy(void)
{
u32 ctr = static_cast<u32>(THERMOSPHERE_GET_SYSREG(ctr_el0));
return (ctr >> 14) & 3;
}
inline u32 GetSmallestInstructionCacheLineSize(void)
{
u32 ctr = static_cast<u32>(THERMOSPHERE_GET_SYSREG(ctr_el0));
u32 shift = ctr & 0xF;
// "log2 of the number of words"...
return 4 << shift;
}
inline u32 GetSmallestDataCacheLineSize(void)
{
u32 ctr = static_cast<u32>(THERMOSPHERE_GET_SYSREG(ctr_el0));
u32 shift = (ctr >> 16) & 0xF;
// "log2 of the number of words"...
return 4 << shift;
}
ALWAYS_INLINE void InvalidateInstructionCache(void)
{
__asm__ __volatile__ ("ic ialluis" ::: "memory");
cpu::isb();
}
ALWAYS_INLINE void InvalidateInstructionCacheLocal(void)
{
__asm__ __volatile__ ("ic iallu" ::: "memory");
cpu::isb();
}
void CleanInvalidateDataCacheRange(const void *addr, size_t size);
void CleanDataCacheRangePoU(const void *addr, size_t size);
void InvalidateInstructionCacheRangePoU(const void *addr, size_t size);
void HandleSelfModifyingCodePoU(const void *addr, size_t size);
void ClearSharedDataCachesOnBoot(void);
void ClearLocalDataCacheOnBoot(void);
// Dunno where else to put that
void HandleTrappedSetWayOperation(u32 val);
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../defines.hpp"
namespace ams::hvisor::cpu {
// TODO GCC 10, use enum class.
// Would be nice if gcc didn't take 9+ years to fix a trivial bug ("too small to fit")
struct DebugRegisterPair {
// For breakpoints only
/// BT[3:1] or res0. BT[0]/WT[0] is "is linked"
enum BreakpointType : u32 {
AddressMatch = 0,
VheContextIdMatch = 1,
ContextIdMatch = 3,
VmidMatch = 4,
VmidContextIdMatch = 5,
VmidVheContextIdMatch = 6,
FullVheContextIdMatch = 7,
};
// Note: some SSC HMC PMC combinations are invalid
// Refer to "Table D2-9 Summary of breakpoint HMC, SSC, and PMC encodings"
/// Security State Control
enum SecurityStateControl : u32 {
Both = 0,
NonSecure = 1,
Secure = 2,
SecureIfLowerOrBoth = 3,
};
/// Higher Mode Control
enum HigherModeControl : u32 {
LowerEl = 0,
HigherEl = 1,
};
/// Privilege Mode Control (called PAC for watchpoints)
enum PrivilegeModeControl : u32 {
NeitherEl1Nor0 = 0,
El1 = 1,
El0 = 2,
El1And0 = 3,
};
// Watchpoints only
enum LoadStoreControl : u32 {
NotAWatchpoint = 0,
Load = 1,
Store = 2,
LoadStore = 3,
};
// bas only 4 bits for breakpoints, other bits res0.
// lsc, mask only for watchpoints, res0 for breakpoints
// bt only from breakpoints, res0 for watchpoints
struct ControlRegister {
union {
struct {
bool enabled : 1;
PrivilegeModeControl pmc : 2;
LoadStoreControl lsc : 2;
u32 bas : 8;
HigherModeControl hmc : 1;
SecurityStateControl ssc : 2;
u32 lbn : 4;
bool linked : 1;
BreakpointType bt : 3;
u32 mask : 5;
u64 res0 : 35;
};
u64 raw;
};
};
ControlRegister cr;
u64 vr;
constexpr void SetDefaults()
{
cr.linked = false;
// NS EL1&0 only
cr.hmc = LowerEl;
cr.ssc = NonSecure;
cr.pmc = El1And0;
}
};
static_assert(std::is_standard_layout_v<DebugRegisterPair>);
static_assert(std::is_trivial_v<DebugRegisterPair>);
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../defines.hpp"
#include "hvisor_cpu_sysreg_general.hpp"
namespace ams::hvisor::cpu {
// FIXME GCC 10
struct ExceptionSyndromeRegister {
enum ExceptionClass : u32 {
Uncategorized = 0x0,
WFxTrap = 0x1,
CP15RTTrap = 0x3,
CP15RRTTrap = 0x4,
CP14RTTrap = 0x5,
CP14DTTrap = 0x6,
AdvSIMDFPAccessTrap = 0x7,
FPIDTrap = 0x8,
PACTrap = 0x9,
CP14RRTTrap = 0xC,
BranchTargetException = 0xD, // No official enum field name from Arm yet
IllegalState = 0xE,
SupervisorCallA32 = 0x11,
HypervisorCallA32 = 0x12,
MonitorCallA32 = 0x13,
SupervisorCallA64 = 0x15,
HypervisorCallA64 = 0x16,
MonitorCallA64 = 0x17,
SystemRegisterTrap = 0x18,
SVEAccessTrap = 0x19,
ERetTrap = 0x1A,
El3_ImplementationDefined = 0x1F,
InstructionAbortLowerEl = 0x20,
InstructionAbortSameEl = 0x21,
PCAlignment = 0x22,
DataAbortLowerEl = 0x24,
DataAbortSameEl = 0x25,
SPAlignment = 0x26,
FPTrappedExceptionA32 = 0x28,
FPTrappedExceptionA64 = 0x2C,
SError = 0x2F,
BreakpointLowerEl = 0x30,
BreakpointSameEl = 0x31,
SoftwareStepLowerEl = 0x32,
SoftwareStepSameEl = 0x33,
WatchpointLowerEl = 0x34,
WatchpointSameEl = 0x35,
SoftwareBreakpointA32 = 0x38,
VectorCatchA32 = 0x3A,
SoftwareBreakpointA64 = 0x3C,
};
u32 iss : 25; // Instruction Specific Syndrome
u32 il : 1; // Instruction Length (16 or 32-bit)
ExceptionClass ec : 6; // Exception Class
u32 res0 : 32;
constexpr size_t GetInstructionLength()
{
return il == 0 ? 2 : 4;
}
};
struct DataAbortIss {
u32 dfsc : 6; // Fault status code
u32 wnr : 1; // Write, not Read
u32 s1ptw : 1; // Stage1 page table walk fault
u32 cm : 1; // Cache maintenance
u32 ea : 1; // External abort
u32 fnv : 1; // FAR not Valid
u32 set : 2; // Synchronous error type
u32 vncr : 1; // vncr_el2 trap
u32 ar : 1; // Acquire/release. Bit 14
u32 sf : 1; // 64-bit register used
u32 srt : 5; // Syndrome register transfer (register used)
u32 sse : 1; // Syndrome sign extend
u32 sas : 2; // Syndrome access size. Bit 23
u32 isv : 1; // Instruction syndrome valid (ISS[23:14] valid)
constexpr bool HasValidFar()
{
return isv && !fnv;
}
constexpr size_t GetAccessSize()
{
return BITL(sas);
}
};
static_assert(std::is_standard_layout_v<ExceptionSyndromeRegister>);
static_assert(std::is_standard_layout_v<DataAbortIss>);
static_assert(std::is_trivial_v<ExceptionSyndromeRegister>);
static_assert(std::is_trivial_v<DataAbortIss>);
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../defines.hpp"
#define _ASM_ARITHMETIC_UNARY_HELPER(sz, regalloc, op) ({\
u##sz res;\
__asm__ __volatile__ (STRINGIZE(op) " %" STRINGIZE(regalloc) "[res], %" STRINGIZE(regalloc) "[val]" : [res] "=r" (res) : [val] "r" (val));\
res;\
})
#define DECLARE_SINGLE_ASM_INSN2(name, what) ALWAYS_INLINE void name() { __asm__ __volatile__ (what ::: "memory"); }
#define DECLARE_SINGLE_ASM_INSN(name) ALWAYS_INLINE void name() { __asm__ __volatile__ (STRINGIZE(name) ::: "memory"); }
namespace ams::hvisor::cpu {
template<typename T>
ALWAYS_INLINE static T rbit(T val)
{
static_assert(std::is_integral_v<T> && (sizeof(T) == 8 || sizeof(T) == 4));
if constexpr (sizeof(T) == 8) {
return _ASM_ARITHMETIC_UNARY_HELPER(64, x, rbit);
} else {
return _ASM_ARITHMETIC_UNARY_HELPER(32, w, rbit);
}
}
DECLARE_SINGLE_ASM_INSN(wfi)
DECLARE_SINGLE_ASM_INSN(wfe)
DECLARE_SINGLE_ASM_INSN(sevl)
DECLARE_SINGLE_ASM_INSN(sev)
DECLARE_SINGLE_ASM_INSN2(dmb, "dmb ish")
DECLARE_SINGLE_ASM_INSN2(dmbSy, "dmb sy")
DECLARE_SINGLE_ASM_INSN2(dsb, "dsb ish")
DECLARE_SINGLE_ASM_INSN2(dsbSy, "dsb sy")
DECLARE_SINGLE_ASM_INSN(isb)
DECLARE_SINGLE_ASM_INSN2(TlbInvalidateEl2Local, "tlbi alle2")
DECLARE_SINGLE_ASM_INSN2(TlbInvalidateEl2, "tlbi alle2is")
DECLARE_SINGLE_ASM_INSN2(TlbInvalidateEl1, "tlbi vmalle1is")
DECLARE_SINGLE_ASM_INSN2(TlbInvalidateEl1Stage12, "tlbi alle1is")
DECLARE_SINGLE_ASM_INSN2(TlbInvalidateEl1Stage12Local, "tlbi alle1")
ALWAYS_INLINE void TlbInvalidateEl2Page(uintptr_t addr)
{
__asm__ __volatile__ ("tlbi vae2is, %0" :: "r"(addr) : "memory");
}
}
#undef DECLARE_SINGLE_ASM_INSN
#undef DECLARE_SINGLE_ASM_INSN2
#undef _ASM_ARITHMETIC_UNARY_HELPER

View File

@@ -0,0 +1,54 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "hvisor_cpu_sysreg_general.hpp"
namespace ams::hvisor::cpu {
ALWAYS_INLINE u64 MaskIrq()
{
u64 daif = THERMOSPHERE_GET_SYSREG(daif);
THERMOSPHERE_SET_SYSREG_IMM(daifset, BIT(1));
return daif;
}
ALWAYS_INLINE u64 UnmaskIrq()
{
u64 daif = THERMOSPHERE_GET_SYSREG(daif);
THERMOSPHERE_SET_SYSREG_IMM(daifclr, BIT(1));
return daif;
}
ALWAYS_INLINE void RestoreInterruptFlags(u64 flags)
{
THERMOSPHERE_SET_SYSREG(daif, flags);
}
class InterruptMaskGuard final {
NON_COPYABLE(InterruptMaskGuard);
NON_MOVEABLE(InterruptMaskGuard);
private:
u64 m_flags;
public:
ALWAYS_INLINE InterruptMaskGuard() : m_flags(MaskIrq()) {}
ALWAYS_INLINE ~InterruptMaskGuard()
{
RestoreInterruptFlags(m_flags);
}
};
}

View File

@@ -0,0 +1,198 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "hvisor_cpu_sysreg_general.hpp"
namespace ams::hvisor::cpu {
// Assumes addr is valid, must be called with interrupts masked
inline uintptr_t Va2Pa(const void *vaddrEl2) {
uintptr_t va = reinterpret_cast<uintptr_t>(vaddrEl2);
__asm__ __volatile__("at s1e2r, %0" :: "r"(va) : "memory");
return (THERMOSPHERE_GET_SYSREG(par_el1) & MASK2L(47, 12)) | (va & MASKL(12));
}
enum MmuPteType : u64 {
MMU_ENTRY_FAULT = 0,
MMU_ENTRY_BLOCK = 1,
MMU_ENTRY_TABLE = 3,
// L3 (this definition allows for recursive page tables)
MMU_ENTRY_PAGE = 3,
};
// Multi-byte attributes...
constexpr u64 MMU_ATTRINDX(u64 idx) { return (idx & 8) << 2; }
constexpr u64 MMU_MEMATTR(u64 attr) { return (attr & 0xF) << 2; }
constexpr u64 MMU_SH(u64 sh) { return (sh & 3) << 8; }
// Attributes. They are defined in a way that allows recursive page tables (assuming PBHA isn't used)
enum MmuPteAttributes : u64 {
// Stage 1 Table only, the rest is block/page only
MMU_NS_TABLE = BITL(62),
MMU_AP_TABLE = BITL(61),
MMU_XN_TABLE = BITL(60),
MMU_PXN_TABLE = BITL(59),
MMU_UXN = BITL(54), // EL1&0 only
MMU_PXN = BITL(53), // EL1&0 only
MMU_XN = MMU_UXN,
MMU_XN0 = MMU_PXN, // Armv8.2, stage 2 only
MMU_CONTIGUOUS = BITL(52),
MMU_DBM = BITL(51), // stage 1 only
MMU_GP = BITL(50), // undocumented
// ARMv8.4-TTRem only
MMU_NT = BITL(16),
// EL1&0 only
MMU_NG = BITL(11),
MMU_AF = BITL(10),
// SH[1:0]
MMU_NON_SHAREABLE = MMU_SH(0),
MMU_OUTER_SHAREABLE = MMU_SH(2),
MMU_INNER_SHAREABLE = MMU_SH(3),
// AP[2:1], stage 1 only. AP[0] does not exist.
MMU_AP_PRIV_RW = 0 << 6,
MMU_AP_RW = 1 << 6,
MMU_AP_PRIV_RO = 2 << 6,
MMU_AP_RO = 3 << 6,
// S2AP[1:0], stage 2 only
MMU_S2AP_NONE = 0 << 6,
MMU_S2AP_RO = 1 << 6,
MMU_S2AP_WO = 2 << 6,
MMU_S2AP_RW = 3 << 6,
// NS, stage 1 only
MMU_NS = BITL(5),
// See above...
// MemAttr[3:0], stage 2 only (convenience defs). When combining, strongest memory type applies
MMU_MEMATTR_DEVICE_NGNRE = MMU_MEMATTR(2),
MMU_MEMATTR_UNCHANGED = MMU_MEMATTR(0xF),
// Other useful defines for stage 2:
MMU_SAME_SHAREABILITY = MMU_NON_SHAREABLE,
};
template<u32 Level, u32 AddressSpaceSize, bool IsMmuEnabled = false, TranslationGranuleSize GranuleSize = TranslationGranule_4K>
class MmuTableBuilder final {
private:
static constexpr u32 tgBitSize = GetTranslationGranuleBitSize(GranuleSize);
// tgBitSize - 3 = log2(tg / sizeof(u64))
static constexpr u32 levelShift = tgBitSize + (tgBitSize - 3) * (3 - Level);
static constexpr u32 levelBitSize = std::min(AddressSpaceSize - levelShift, tgBitSize - 3);
static constexpr u64 levelMask = MASKL(levelBitSize);
static constexpr size_t ComputeIndex(uintptr_t va)
{
return (va >> levelShift) & levelMask;
}
private:
u64 *m_pageTable = nullptr;
public:
using NextLevelBuilder = MmuTableBuilder<Level + 1, AddressSpaceSize, IsMmuEnabled, GranuleSize>;
static_assert(Level <= 3, "Invalid translation table level");
static_assert(AddressSpaceSize <= 48);
static_assert(AddressSpaceSize > levelShift, "Address space size mismatch with translation level");
static constexpr size_t blockSize = BITL(levelShift);
static constexpr size_t tableSize = BITL(levelBitSize);
public:
constexpr MmuTableBuilder(u64 *pageTable = nullptr) : m_pageTable{pageTable} {}
constexpr MmuTableBuilder &InitializeTable()
{
std::memset(m_pageTable, 0, 8 * tableSize);
// Fails to optimize before GCC 10: std::fill_n(m_pageTable, tableSize, MMU_ENTRY_FAULT);
return *this;
}
// Precondition: va and pa bits in range
constexpr NextLevelBuilder MapTable(uintptr_t va, uintptr_t pa, u64 *table, u64 attribs = 0) const
{
static_assert(Level < 3, "Level 3 is the last level of translation");
m_pageTable[ComputeIndex(va)] = pa | attribs | MMU_ENTRY_TABLE;
return NextLevelBuilder{table};
}
NextLevelBuilder MapTable(uintptr_t va, u64 *table, u64 attribs = 0) const
{
if constexpr (IsMmuEnabled) {
return MapTable(va, Va2Pa(table), table, attribs);
} else {
return MapTable(va, reinterpret_cast<uintptr_t>(table), table, attribs);
}
}
constexpr MmuTableBuilder &Unmap(uintptr_t va)
{
m_pageTable[ComputeIndex(va)] = MMU_ENTRY_FAULT;
return *this;
}
// Precondition: guardSize == 0 if Level == 0
constexpr MmuTableBuilder &UnmapRange(uintptr_t va, size_t size, size_t guardSize = 0)
{
for (size_t off = 0, offVa = 0; off < size; off += blockSize, offVa += blockSize + guardSize) {
Unmap(va + offVa);
}
return *this;
}
// Precondition: va and pa bits in range
constexpr MmuTableBuilder &MapBlock(uintptr_t va, uintptr_t pa, u64 attribs)
{
static_assert(Level > 0, "Can only map L1 tables at L0");
constexpr u64 entryType = Level == 3 ? MMU_ENTRY_PAGE : MMU_ENTRY_BLOCK;
m_pageTable[ComputeIndex(va)] = pa | attribs | MMU_AF | entryType;
return *this;
}
constexpr MmuTableBuilder &MapBlock(uintptr_t pa, u64 attribs)
{
return MapBlock(pa, pa, attribs);
}
// Precondition: size and guardSize are multiples of blockSize
constexpr MmuTableBuilder &MapBlockRange(uintptr_t va, uintptr_t pa, size_t size, u64 attribs, size_t guardSize = 0)
{
for (size_t off = 0, offVa = 0; off < size; off += blockSize, offVa += blockSize + guardSize) {
MapBlock(va + offVa, pa + off, attribs);
UnmapRange(va + offVa + blockSize, guardSize, 0);
}
return *this;
}
constexpr MmuTableBuilder &MapBlockRange(uintptr_t pa, size_t size, u64 attribs)
{
return MapBlockRange(pa, pa, attribs, size, 0);
}
};
}

View File

@@ -0,0 +1,495 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../preprocessor.h"
#include "../defines.hpp"
#define THERMOSPHERE_GET_SYSREG(r) ({\
u64 __val; \
__asm__ __volatile__("mrs %0, " STRINGIZE(r) : "=r" (__val) :: "memory"); \
__val; \
})
#define THERMOSPHERE_SET_SYSREG(reg, val)\
do {\
u64 temp_reg = (val);\
__asm__ __volatile__ ("msr " STRINGIZE(reg) ", %0" :: "r"(temp_reg) : "memory");\
} while(false)
#define THERMOSPHERE_SET_SYSREG_IMM(reg, imm)\
do {\
__asm__ __volatile__ ("msr " STRINGIZE(reg) ", %0" :: "I"(imm) : "memory", "cc");\
} while(false)
namespace ams::hvisor::cpu {
using SysregEncoding = std::array<u8, 5>;
constexpr u32 EncodeSysregIss(SysregEncoding reg)
{
auto [op0, op1, crn, crm, op2] = reg;
return op0 << 20 | op2 << 17 | op1 << 14 | crn << 10 | crm << 1;
}
constexpr u32 MakeMsrFromEncoding(SysregEncoding reg, u32 Rt)
{
auto [op0, op1, crn, crm, op2] = reg;
u32 enc = op0 << 19 | op1 << 16 | crn << 12 | crm << 8 | op2 << 5;
return 0xD5000000u | enc | (Rt & 0x1Fu);
}
constexpr u32 MakeMrsFromEncoding(SysregEncoding reg, u32 Rt)
{
auto [op0, op1, crn, crm, op2] = reg;
u32 enc = op0 << 19 | op1 << 16 | crn << 12 | crm << 8 | op2 << 5;
return 0xD5200000u | enc | (Rt & 0x1Fu);
}
// The list mostly includes EL1 registers as these are the one we're trapping
constexpr SysregEncoding dbgbvrN_el1(u8 n) { return {2, 0, 0, n, 4}; }
constexpr SysregEncoding dbgbcrN_el1(u8 n) { return {2, 0, 0, n, 5}; }
constexpr SysregEncoding dbgwvrN_el1(u8 n) { return {2, 0, 0, n, 6}; }
constexpr SysregEncoding dbgwcrN_el1(u8 n) { return {2, 0, 0, n, 7}; }
constexpr inline SysregEncoding dc_isw = {1, 0, 7, 6, 2};
constexpr inline SysregEncoding dc_csw = {1, 0, 7, 10, 2};
constexpr inline SysregEncoding dc_cisw = {1, 0, 7, 14, 2};
constexpr inline SysregEncoding osdtrrx_el1 = {2, 0, 0, 0, 2};
constexpr inline SysregEncoding mdccint_el1 = {2, 0, 0, 2, 0};
constexpr inline SysregEncoding mdscr_el1 = {2, 0, 0, 2, 2};
constexpr inline SysregEncoding osdtrtx_el1 = {2, 0, 0, 3, 2};
constexpr inline SysregEncoding oseccr_el1 = {2, 0, 0, 6, 2};
constexpr inline SysregEncoding mdrar_el1 = {2, 0, 1, 0, 0};
constexpr inline SysregEncoding oslar_el1 = {2, 0, 1, 0, 4};
constexpr inline SysregEncoding oslsr_el1 = {2, 0, 1, 1, 4};
constexpr inline SysregEncoding osdlr_el1 = {2, 0, 1, 3, 4};
constexpr inline SysregEncoding dbgprcr_el1 = {2, 0, 1, 4, 4};
constexpr inline SysregEncoding dbgclaimset_el1 = {2, 0, 7, 8, 6};
constexpr inline SysregEncoding dbgclaimclr_el1 = {2, 0, 7, 9, 6};
constexpr inline SysregEncoding dbgauthstatus_el1 = {2, 0, 7, 14, 6};
constexpr inline SysregEncoding mdccsr_el0 = {2, 3, 0, 1, 0};
constexpr inline SysregEncoding dbgdtr_el0 = {2, 3, 0, 4, 0};
constexpr inline SysregEncoding dbgdtrrx_el0 = {2, 3, 0, 5, 0};
constexpr inline SysregEncoding dbgdtrtx_el0 = {2, 3, 0, 5, 0};
constexpr inline SysregEncoding dbgvcr32_el2 = {2, 4, 0, 7, 0};
constexpr inline SysregEncoding midr_el1 = {3, 0, 0, 0, 0};
constexpr inline SysregEncoding mpidr_el1 = {3, 0, 0, 0, 5};
constexpr inline SysregEncoding revidr_el1 = {3, 0, 0, 0, 6};
constexpr inline SysregEncoding id_pfr0_el1 = {3, 0, 0, 1, 0};
constexpr inline SysregEncoding id_pfr1_el1 = {3, 0, 0, 1, 1};
constexpr inline SysregEncoding id_dfr0_el1 = {3, 0, 0, 1, 2};
constexpr inline SysregEncoding id_afr0_el1 = {3, 0, 0, 1, 3};
constexpr inline SysregEncoding id_mmfr0_el1 = {3, 0, 0, 1, 4};
constexpr inline SysregEncoding id_mmfr1_el1 = {3, 0, 0, 1, 5};
constexpr inline SysregEncoding id_mmfr2_el1 = {3, 0, 0, 1, 6};
constexpr inline SysregEncoding id_mmfr3_el1 = {3, 0, 0, 1, 7};
constexpr inline SysregEncoding id_isar0_el1 = {3, 0, 0, 2, 0};
constexpr inline SysregEncoding id_isar1_el1 = {3, 0, 0, 2, 1};
constexpr inline SysregEncoding id_isar2_el1 = {3, 0, 0, 2, 2};
constexpr inline SysregEncoding id_isar3_el1 = {3, 0, 0, 2, 3};
constexpr inline SysregEncoding id_isar4_el1 = {3, 0, 0, 2, 4};
constexpr inline SysregEncoding id_isar5_el1 = {3, 0, 0, 2, 5};
constexpr inline SysregEncoding id_mmfr4_el1 = {3, 0, 0, 2, 6};
constexpr inline SysregEncoding mvfr0_el1 = {3, 0, 0, 3, 0};
constexpr inline SysregEncoding mvfr1_el1 = {3, 0, 0, 3, 1};
constexpr inline SysregEncoding mvfr2_el1 = {3, 0, 0, 3, 2};
constexpr inline SysregEncoding id_aa64pfr0_el1 = {3, 0, 0, 4, 0};
constexpr inline SysregEncoding id_aa64pfr1_el1 = {3, 0, 0, 4, 1};
constexpr inline SysregEncoding id_aa64zfr0_el1 = {3, 0, 0, 4, 4};
constexpr inline SysregEncoding id_aa64dfr0_el1 = {3, 0, 0, 5, 0};
constexpr inline SysregEncoding id_aa64dfr1_el1 = {3, 0, 0, 5, 1};
constexpr inline SysregEncoding id_aa64afr0_el1 = {3, 0, 0, 5, 4};
constexpr inline SysregEncoding id_aa64afr1_el1 = {3, 0, 0, 5, 5};
constexpr inline SysregEncoding id_aa64isar0_el1 = {3, 0, 0, 6, 0};
constexpr inline SysregEncoding id_aa64isar1_el1 = {3, 0, 0, 6, 1};
constexpr inline SysregEncoding id_aa64mmfr0_el1 = {3, 0, 0, 7, 0};
constexpr inline SysregEncoding id_aa64mmfr1_el1 = {3, 0, 0, 7, 1};
constexpr inline SysregEncoding id_aa64mmfr2_el1 = {3, 0, 0, 7, 2};
constexpr inline SysregEncoding sctlr_el1 = {3, 0, 1, 0, 0};
constexpr inline SysregEncoding actlr_el1 = {3, 0, 1, 0, 1};
constexpr inline SysregEncoding cpacr_el1 = {3, 0, 1, 0, 2};
constexpr inline SysregEncoding zcr_el1 = {3, 0, 1, 2, 0};
constexpr inline SysregEncoding ttbr0_el1 = {3, 0, 2, 0, 0};
constexpr inline SysregEncoding ttbr1_el1 = {3, 0, 2, 0, 1};
constexpr inline SysregEncoding tcr_el1 = {3, 0, 2, 0, 2};
constexpr inline SysregEncoding apiakeylo_el1 = {3, 0, 2, 1, 0};
constexpr inline SysregEncoding apiakeyhi_el1 = {3, 0, 2, 1, 1};
constexpr inline SysregEncoding apibkeylo_el1 = {3, 0, 2, 1, 2};
constexpr inline SysregEncoding apibkeyhi_el1 = {3, 0, 2, 1, 3};
constexpr inline SysregEncoding apdakeylo_el1 = {3, 0, 2, 2, 0};
constexpr inline SysregEncoding apdakeyhi_el1 = {3, 0, 2, 2, 1};
constexpr inline SysregEncoding apdbkeylo_el1 = {3, 0, 2, 2, 2};
constexpr inline SysregEncoding apdbkeyhi_el1 = {3, 0, 2, 2, 3};
constexpr inline SysregEncoding apgakeylo_el1 = {3, 0, 2, 3, 0};
constexpr inline SysregEncoding apgakeyhi_el1 = {3, 0, 2, 3, 1};
constexpr inline SysregEncoding afsr0_el1 = {3, 0, 5, 1, 0};
constexpr inline SysregEncoding afsr1_el1 = {3, 0, 5, 1, 1};
constexpr inline SysregEncoding esr_el1 = {3, 0, 5, 2, 0};
constexpr inline SysregEncoding erridr_el1 = {3, 0, 5, 3, 0};
constexpr inline SysregEncoding errselr_el1 = {3, 0, 5, 3, 1};
constexpr inline SysregEncoding erxfr_el1 = {3, 0, 5, 4, 0};
constexpr inline SysregEncoding erxctlr_el1 = {3, 0, 5, 4, 1};
constexpr inline SysregEncoding erxstatus_el1 = {3, 0, 5, 4, 2};
constexpr inline SysregEncoding erxaddr_el1 = {3, 0, 5, 4, 3};
constexpr inline SysregEncoding erxmisc0_el1 = {3, 0, 5, 5, 0};
constexpr inline SysregEncoding erxmisc1_el1 = {3, 0, 5, 5, 1};
constexpr inline SysregEncoding far_el1 = {3, 0, 6, 0, 0};
constexpr inline SysregEncoding par_el1 = {3, 0, 7, 4, 0};
constexpr inline SysregEncoding pmsidr_el1 = {3, 0, 9, 9, 7};
constexpr inline SysregEncoding pmbidr_el1 = {3, 0, 9, 10, 7};
constexpr inline SysregEncoding pmscr_el1 = {3, 0, 9, 9, 0};
constexpr inline SysregEncoding pmscr_el2 = {3, 4, 9, 9, 0};
constexpr inline SysregEncoding pmsicr_el1 = {3, 0, 9, 9, 2};
constexpr inline SysregEncoding pmsirr_el1 = {3, 0, 9, 9, 3};
constexpr inline SysregEncoding pmsfcr_el1 = {3, 0, 9, 9, 4};
constexpr inline SysregEncoding pmsevfr_el1 = {3, 0, 9, 9, 5};
constexpr inline SysregEncoding pmslatfr_el1 = {3, 0, 9, 9, 6};
constexpr inline SysregEncoding pmblimitr_el1 = {3, 0, 9, 10, 0};
constexpr inline SysregEncoding pmbptr_el1 = {3, 0, 9, 10, 1};
constexpr inline SysregEncoding pmbsr_el1 = {3, 0, 9, 10, 3};
constexpr inline SysregEncoding pmintenset_el1 = {3, 0, 9, 14, 1};
constexpr inline SysregEncoding pmintenclr_el1 = {3, 0, 9, 14, 2};
constexpr inline SysregEncoding mair_el1 = {3, 0, 10, 2, 0};
constexpr inline SysregEncoding amair_el1 = {3, 0, 10, 3, 0};
constexpr inline SysregEncoding lorsa_el1 = {3, 0, 10, 4, 0};
constexpr inline SysregEncoding lorea_el1 = {3, 0, 10, 4, 1};
constexpr inline SysregEncoding lorn_el1 = {3, 0, 10, 4, 2};
constexpr inline SysregEncoding lorc_el1 = {3, 0, 10, 4, 3};
constexpr inline SysregEncoding lorid_el1 = {3, 0, 10, 4, 7};
constexpr inline SysregEncoding vbar_el1 = {3, 0, 12, 0, 0};
constexpr inline SysregEncoding disr_el1 = {3, 0, 12, 1, 1};
constexpr inline SysregEncoding contextidr_el1 = {3, 0, 13, 0, 1};
constexpr inline SysregEncoding tpidr_el1 = {3, 0, 13, 0, 4};
constexpr inline SysregEncoding cntkctl_el1 = {3, 0, 14, 1, 0};
constexpr inline SysregEncoding ccsidr_el1 = {3, 1, 0, 0, 0};
constexpr inline SysregEncoding clidr_el1 = {3, 1, 0, 0, 1};
constexpr inline SysregEncoding aidr_el1 = {3, 1, 0, 0, 7};
constexpr inline SysregEncoding csselr_el1 = {3, 2, 0, 0, 0};
constexpr inline SysregEncoding ctr_el0 = {3, 3, 0, 0, 1};
constexpr inline SysregEncoding dczid_el0 = {3, 3, 0, 0, 7};
constexpr inline SysregEncoding pmcr_el0 = {3, 3, 9, 12, 0};
constexpr inline SysregEncoding pmcntenset_el0 = {3, 3, 9, 12, 1};
constexpr inline SysregEncoding pmcntenclr_el0 = {3, 3, 9, 12, 2};
constexpr inline SysregEncoding pmovsclr_el0 = {3, 3, 9, 12, 3};
constexpr inline SysregEncoding pmswinc_el0 = {3, 3, 9, 12, 4};
constexpr inline SysregEncoding pmselr_el0 = {3, 3, 9, 12, 5};
constexpr inline SysregEncoding pmceid0_el0 = {3, 3, 9, 12, 6};
constexpr inline SysregEncoding pmceid1_el0 = {3, 3, 9, 12, 7};
constexpr inline SysregEncoding pmccntr_el0 = {3, 3, 9, 13, 0};
constexpr inline SysregEncoding pmxevtyper_el0 = {3, 3, 9, 13, 1};
constexpr inline SysregEncoding pmxevcntr_el0 = {3, 3, 9, 13, 2};
constexpr inline SysregEncoding pmuserenr_el0 = {3, 3, 9, 14, 0};
constexpr inline SysregEncoding pmovsset_el0 = {3, 3, 9, 14, 3};
constexpr inline SysregEncoding tpidr_el0 = {3, 3, 13, 0, 2};
constexpr inline SysregEncoding tpidrro_el0 = {3, 3, 13, 0, 3};
constexpr inline SysregEncoding cntfrq_el0 = {3, 3, 14, 0, 0};
constexpr inline SysregEncoding cntpct_el0 = {3, 3, 14, 0, 1};
constexpr inline SysregEncoding cntvct_el0 = {3, 3, 14, 0, 2};
constexpr inline SysregEncoding cntp_tval_el0 = {3, 3, 14, 2, 0};
constexpr inline SysregEncoding cntp_ctl_el0 = {3, 3, 14, 2, 1};
constexpr inline SysregEncoding cntp_cval_el0 = {3, 3, 14, 2, 2};
constexpr inline SysregEncoding cntv_tval_el0 = {3, 3, 14, 3, 0};
constexpr inline SysregEncoding cntv_ctl_el0 = {3, 3, 14, 3, 1};
constexpr inline SysregEncoding cntv_cval_el0 = {3, 3, 14, 3, 2};
constexpr inline SysregEncoding cntvoff_el2 = {3, 4, 14, 0, 3};
constexpr inline SysregEncoding cnthctl_el2 = {3, 4, 14, 1, 0};
constexpr inline SysregEncoding cnthp_cval_el2 = {3, 4, 14, 2, 2};
constexpr inline SysregEncoding pmccfiltr_el0 = {3, 3, 14, 15, 7};
constexpr inline SysregEncoding zcr_el2 = {3, 4, 1, 2, 0};
constexpr inline SysregEncoding dacr32_el2 = {3, 4, 3, 0, 0};
constexpr inline SysregEncoding ifsr32_el2 = {3, 4, 5, 0, 1};
constexpr inline SysregEncoding vsesr_el2 = {3, 4, 5, 2, 3};
constexpr inline SysregEncoding fpexc32_el2 = {3, 4, 5, 3, 0};
constexpr inline SysregEncoding zcr_el12 = {3, 5, 1, 2, 0};
enum SctlrFlags {
SCTLR_ELx_DSSBS = BITL(44),
SCTLR_ELx_ENIA = BITL(31),
SCTLR_ELx_ENIB = BITL(30),
SCTLR_ELx_ENDA = BITL(27),
SCTLR_ELx_EE = BITL(25),
SCTLR_ELx_IESB = BITL(21),
SCTLR_ELx_WXN = BITL(19),
SCTLR_ELx_ENDB = BITL(13),
SCTLR_ELx_I = BITL(12),
SCTLR_ELx_SA = BITL(3),
SCTLR_ELx_C = BITL(2),
SCTLR_ELx_A = BITL(1),
SCTLR_ELx_M = BITL(0),
SCTLR_EL1_UCI = BITL(26),
SCTLR_EL1_E0E = BITL(24),
SCTLR_EL1_SPAN = BITL(23),
SCTLR_EL1_NTWE = BITL(18),
SCTLR_EL1_NTWI = BITL(16),
SCTLR_EL1_UCT = BITL(15),
SCTLR_EL1_DZE = BITL(14),
SCTLR_EL1_UMA = BITL(9),
SCTLR_EL1_SED = BITL(8),
SCTLR_EL1_ITD = BITL(7),
SCTLR_EL1_CP15BEN = BITL(5),
SCTLR_EL1_SA0 = BITL(4),
SCTLR_EL2_RES1 = util::CombineBits<u64>(29, 28, 23, 22, 18, 16, 11, 5, 4),
SCTLR_EL2_RES0 = (0xFFFFEFFFull << 32) | util::CombineBits<u64>(
31, 30, 27, 26, 24, 20, 17, 15, 14, 13, 10, 9, 8, 7, 6
),
SCTLR_EL1_RES1 = util::CombineBits<u64>(29, 28, 22, 20, 11),
SCTLR_EL1_RES0 = (0xFFFFEFFFull << 32) | util::CombineBits<u64>(31, 30, 27, 17, 13, 10, 6),
};
// HCR Flags
enum HcrFlags {
HCR_FWB = BITL(46),
HCR_API = BITL(41),
HCR_APK = BITL(40),
HCR_TEA = BITL(37),
HCR_TERR = BITL(36),
HCR_TLOR = BITL(35),
HCR_E2H = BITL(34),
HCR_ID = BITL(33),
HCR_CD = BITL(32),
HCR_RW = BITL(31),
HCR_TRVM = BITL(30),
HCR_HCD = BITL(29),
HCR_TDZ = BITL(28),
HCR_TGE = BITL(27),
HCR_TVM = BITL(26),
HCR_TTLB = BITL(25),
HCR_TPU = BITL(24),
HCR_TPC = BITL(23),
HCR_TSW = BITL(22),
HCR_TAC = BITL(21),
HCR_TIDCP = BITL(20),
HCR_TSC = BITL(19),
HCR_TID3 = BITL(18),
HCR_TID2 = BITL(17),
HCR_TID1 = BITL(16),
HCR_TID0 = BITL(15),
HCR_TWE = BITL(14),
HCR_TWI = BITL(13),
HCR_DC = BITL(12),
HCR_BSU = (3ul << 10),
HCR_BSU_IS = BITL(10),
HCR_FB = BITL(9),
HCR_VSE = BITL(8),
HCR_VI = BITL(7),
HCR_VF = BITL(6),
HCR_AMO = BITL(5),
HCR_IMO = BITL(4),
HCR_FMO = BITL(3),
HCR_PTW = BITL(2),
HCR_SWI = BITL(1),
HCR_VM = BITL(0),
};
// CPTR flags
enum CptrFlags {
CPTR_TCPAC = BITL(31),
CPTR_TAM = BITL(30),
CPTR_TTA = BITL(20),
CPTR_TFP = BITL(10),
CPTR_TZ = BITL(8), // (EL2)
CPTR_EZ = BITL(8), // (EL3)
CPTR_RES1 = 0x000032FFul,
};
// MDCR flags (EL2)
enum MdcrEl2Flags {
MDCR_EL2_TPMS = BITL(14),
MDCR_EL2_E2PB_MASK = 3ul,
MDCR_EL2_E2PB_SHIFT = 12,
MDCR_EL2_TDRA = BITL(11),
MDCR_EL2_TDOSA = BITL(10),
MDCR_EL2_TDA = BITL(9),
MDCR_EL2_TDE = BITL(8),
MDCR_EL2_HPME = BITL(7),
MDCR_EL2_TPM = BITL(6),
MDCR_EL2_TPMCR = BITL(5),
MDCR_EL2_HPMN_MASK = 0x1Ful,
};
// Some MDSCR flags
enum MdscrFlags {
MDSCR_MDE = BITL(15),
MDSCR_KDE = BITL(13),
MDSCR_SS = BITL(0),
};
// Some CNTHCTL flags + shifts
enum CnthctlFlags {
CNTHCTL_EVNTI_MASK = 0xFul,
CNTHCTL_EVNTI_SHIFT = 4,
CNTHCTL_EVNTDIR = BITL(3),
CNTHCTL_EVNTEN = BITL(2),
CNTHCTL_EL1PCEN = BITL(1),
CNTHCTL_EL1PCTEN = BITL(0),
};
// PAR_EL1 flags, shifts, masks
enum ParFlags {
PAR_F = BITL(0),
// Successful translation:
PAR_ATTR_SHIFT = 56,
PAR_ATTR_MASK = 0xFFul,
PAR_PA_MASK = MASK2L(51, 12),// bits 51-48 RES0 if not implemented
PAR_NS = BITL(9),
PAR_SH_SHIFT = 7,
PAR_SH_MASK = 3ul,
// Faulting translation:
PAR_S = BITL(9),
PAR_PTW = BITL(8),
PAR_FST_SHIFT = 1,
PAR_FST_MASK = 0x3Ful,
};
// Some (S)PSR flags, masks, shifts
enum PsrFlags {
PSR_AA32_IT10_SHIFT = 25,
PSR_AA32_IT10_MASK = 3ul,
PSR_SS = BITL(21),
PSR_AA32_IT72_SHIFT = 10,
PSR_AA32_IT72_MASK = 0x3Ful,
PSR_DAIF_SHIFT = 6,
PSR_D = BITL(9),
PSR_A = BITL(8),
PSR_I = BITL(7),
PSR_F = BITL(6),
PSR_AA32_THUMB = BITL(5),
PSR_MODE32 = BITL(4),
PSR_EL_SHIFT = 2,
PSR_EL_MASK = 3ul,
PSR_SP_ELX = BITL(0),
};
// cnt*_ctl flags
enum CntCtlFlags {
CNTCTL_ISTATUS = BITL(2),
CNTCTL_IMASK = BITL(1),
CNTCTL_ENABLE = BITL(0),
};
// TCR_ELx flags
enum TcrFlags {
TCR_IRGN_NC = (0 << 8),
TCR_IRGN_WBWA = (1 << 8),
TCR_IRGN_WT = (2 << 8),
TCR_IRGN_WBNWA = (3 << 8),
TCR_IRGN_MASK = (3 << 8),
TCR_ORGN_NC = (0 << 10),
TCR_ORGN_WBWA = (1 << 10),
TCR_ORGN_WT = (2 << 10),
TCR_ORGN_WBNWA = (3 << 10),
TCR_ORGN_MASK = (3 << 10),
TCR_NOT_SHARED = (0 << 12),
TCR_SHARED_OUTER = (2 << 12),
TCR_SHARED_INNER = (3 << 12),
TCR_EPD1_DISABLE = BITL(23),
TCR_EL1_RSVD = BITL(31),
TCR_EL2_RSVD = (BITL(31) | BITL(23)),
VTCR_EL2_RSVD = BITL(31),
TCR_EL3_RSVD = (BITL(31) | BITL(23)),
};
// Could have used enum class here, but can't start identifiers with a digit...
enum TranslationGranuleSize : u64 {
TranslationGranule_4K = 0,
TranslationGranule_64K = 1,
TranslationGranule_16K = 2,
};
constexpr size_t GetTranslationGranuleBitSize(TranslationGranuleSize granuleSize)
{
switch (granuleSize) {
case TranslationGranule_4K: return 12;
case TranslationGranule_64K: return 16;
case TranslationGranule_16K: return 14;
default: return 0;
}
}
constexpr u64 TCR_TG0(TranslationGranuleSize granuleSize)
{
return (granuleSize & 3) << 14;
}
constexpr u64 TCR_T0SZ(size_t addressSpaceSize) { return (64ul - (addressSpaceSize & 0x3F)) << 0; }
constexpr u64 TCR_PS(u64 n) { return (n & 7) << 16; }
constexpr u64 VTCR_SL0(u64 n) { return (n & 3) << 6; }
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include "debug_log.h"
#include "platform/uart.h"
#include "semihosting.h"
#include "utils.h"
#include "transport_interface.h"
#include "platform/uart.h"
#ifndef DLOG_USE_SEMIHOSTING_WRITE0
#define DLOG_USE_SEMIHOSTING_WRITE0 1
#endif
static TransportInterface *g_debugLogTransportInterface;
void debugLogInit(void)
{
if (!DLOG_USE_SEMIHOSTING_WRITE0) {
transportInterfaceCreate(TRANSPORT_INTERFACE_TYPE_UART, DEFAULT_UART, DEFAULT_UART_FLAGS, NULL, NULL, NULL);
}
}
void debugLogRaw(const char *str)
{
// Use semihosting if available (we assume qemu was launched with -semihosting), otherwise UART
if (DLOG_USE_SEMIHOSTING_WRITE0 && semihosting_connection_supported()) {
semihosting_write_string(str);
} else {
transportInterfaceWriteData(g_debugLogTransportInterface, str, strlen(str));
}
}
// NOTE: UNSAFE!
int debugLog(const char *fmt, ...)
{
char buf[128];
va_list args;
va_start(args, fmt);
int res = vsprintf(buf, fmt, args);
va_end(args);
debugLogRaw(buf);
return res;
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdarg.h>
#ifndef NDEBUG
#define DEBUG(...) debugLog(__VA_ARGS__)
#define DEBUGRAW(str) debugLogRaw(str)
#else
#define DEBUG(...)
#define DEBUGRAW(str)
#endif
void debugLogInit(void);
void debugLogRaw(const char *str);
int debugLog(const char *fmt, ...);

View File

@@ -0,0 +1,263 @@
/*
* Copyright (c) 2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#include <stdatomic.h>
#include <stdarg.h>
#include <string.h>
#include "debug_manager.h"
#include "core_ctx.h"
#include "irq.h"
#include "spinlock.h"
#include "single_step.h"
#include "gdb/debug.h"
GDBContext g_gdbContext = { 0 };
typedef struct DebugManager {
DebugEventInfo debugEventInfos[MAX_CORE];
uintptr_t steppingRangeStartAddrs[MAX_CORE];
uintptr_t steppingRangeEndAddrs[MAX_CORE];
ALIGN(64) atomic_uint pausedCoreList;
atomic_uint singleStepCoreList;
atomic_uint eventsSentList;
Barrier pauseBarrier;
atomic_bool reportingEnabled;
} DebugManager;
static DebugManager g_debugManager = { 0 };
static void debugManagerDoPauseCores(u32 coreList)
{
__builtin_prefetch(&g_debugManager.pausedCoreList, 1, 0);
u32 desiredList = coreList;
u32 remainingList = coreList;
u32 readList = atomic_load(&g_debugManager.pausedCoreList);
do {
desiredList |= readList;
remainingList &= ~readList;
} while (!atomic_compare_exchange_weak(&g_debugManager.pausedCoreList, &readList, desiredList));
if (remainingList & ~BIT(currentCoreCtx->coreId)) {
// We need to notify other cores...
u32 otherCores = remainingList & ~BIT(currentCoreCtx->coreId);
barrierInit(&g_debugManager.pauseBarrier, otherCores | BIT(currentCoreCtx->coreId));
generateSgiForList(ThermosphereSgi_DebugPause, otherCores);
barrierWait(&g_debugManager.pauseBarrier);
}
if (remainingList & BIT(currentCoreCtx->coreId)) {
currentCoreCtx->wasPaused = true;
}
__sev();
}
void debugManagerPauseSgiHandler(void)
{
currentCoreCtx->wasPaused = true;
barrierWait(&g_debugManager.pauseBarrier);
}
void debugManagerInit(TransportInterfaceType gdbIfaceType, u32 gdbIfaceId, u32 gdbIfaceFlags)
{
memset(&g_debugManager, 0, sizeof(DebugManager));
GDB_InitializeContext(&g_gdbContext, gdbIfaceType, gdbIfaceId, gdbIfaceFlags);
GDB_MigrateRxIrq(&g_gdbContext, currentCoreCtx->coreId);
}
bool debugManagerHandlePause(void)
{
u32 coreId = currentCoreCtx->coreId;
__builtin_prefetch(&g_debugManager.pausedCoreList, 0, 3);
if (atomic_load(&g_debugManager.pausedCoreList) & BIT(coreId)) {
unmaskIrq();
do {
__wfe();
} while (atomic_load(&g_debugManager.pausedCoreList) & BIT(coreId));
maskIrq();
if (!g_debugManager.debugEventInfos[coreId].handled) {
// Do we still have an unhandled debug event?
GDB_TrySignalDebugEvent(&g_gdbContext, &g_debugManager.debugEventInfos[coreId]);
return false;
}
}
currentCoreCtx->wasPaused = false;
// Single-step: if inactive and requested, start single step; cancel if active and not requested
u32 ssReqd = (atomic_load(&g_debugManager.singleStepCoreList) & BIT(currentCoreCtx->coreId)) != 0;
SingleStepState singleStepState = singleStepGetNextState(currentCoreCtx->guestFrame);
if (ssReqd) {
currentCoreCtx->steppingRangeStartAddr = g_debugManager.steppingRangeStartAddrs[coreId];
currentCoreCtx->steppingRangeEndAddr = g_debugManager.steppingRangeEndAddrs[coreId];
if(singleStepState == SingleStepState_Inactive) {
singleStepSetNextState(currentCoreCtx->guestFrame, SingleStepState_ActiveNotPending);
}
} else if (!ssReqd && singleStepState != SingleStepState_Inactive) {
singleStepSetNextState(currentCoreCtx->guestFrame, SingleStepState_Inactive);
}
return true;
}
void debugManagerSetReportingEnabled(bool enabled)
{
atomic_store(&g_debugManager.reportingEnabled, enabled);
}
bool debugManagerHasDebugEvent(u32 coreId)
{
bool isPaused = debugManagerIsCorePaused(coreId);
return isPaused && g_debugManager.debugEventInfos[coreId].type != DBGEVENT_NONE;
}
void debugManagerPauseCores(u32 coreList)
{
u64 flags = maskIrq();
debugManagerDoPauseCores(coreList);
restoreInterruptFlags(flags);
}
void debugManagerSetSingleStepCoreList(u32 coreList)
{
atomic_store(&g_debugManager.singleStepCoreList, coreList);
}
void debugManagerUnpauseCores(u32 coreList)
{
FOREACH_BIT (tmp, coreId, coreList) {
if (&g_debugManager.debugEventInfos[coreId].handled) {
// Discard already handled debug events
g_debugManager.debugEventInfos[coreId].type = DBGEVENT_NONE;
}
}
atomic_fetch_and(&g_debugManager.pausedCoreList, ~coreList);
__sev();
}
void debugManagerSetSteppingRange(u32 coreId, uintptr_t startAddr, uintptr_t endAddr)
{
g_debugManager.steppingRangeStartAddrs[coreId] = startAddr;
g_debugManager.steppingRangeEndAddrs[coreId] = endAddr;
}
u32 debugManagerGetPausedCoreList(void)
{
return atomic_load(&g_debugManager.pausedCoreList);
}
DebugEventInfo *debugManagerGetDebugEvent(u32 coreId)
{
return &g_debugManager.debugEventInfos[coreId];
}
void debugManagerReportEvent(DebugEventType type, ...)
{
u64 flags = maskIrq();
bool reportingEnabled = atomic_load(&g_debugManager.reportingEnabled);
if (!reportingEnabled && type != DBGEVENT_DEBUGGER_BREAK) {
restoreInterruptFlags(flags);
return;
}
u32 coreId = currentCoreCtx->coreId;
DebugEventInfo *info = &g_debugManager.debugEventInfos[coreId];
memset(info, 0 , sizeof(DebugEventInfo));
info->type = type;
info->coreId = coreId;
info->frame = currentCoreCtx->guestFrame;
va_list args;
va_start(args, type);
switch (type) {
case DBGEVENT_OUTPUT_STRING:
info->outputString.address = va_arg(args, uintptr_t);
info->outputString.size = va_arg(args, size_t);
break;
default:
break;
}
va_end(args);
// Now, pause ourselves and try to signal we have a debug event
debugManagerDoPauseCores(BIT(coreId));
if (reportingEnabled) {
exceptionEnterInterruptibleHypervisorCode();
unmaskIrq();
GDB_TrySignalDebugEvent(&g_gdbContext, info);
}
restoreInterruptFlags(flags);
}
void debugManagerBreakCores(u32 coreList)
{
u32 coreId = currentCoreCtx->coreId;
if (coreList & ~BIT(coreId)) {
generateSgiForList(ThermosphereSgi_ReportDebuggerBreak, coreList & ~BIT(coreId));
}
if ((coreList & BIT(coreId)) && !debugManagerHasDebugEvent(coreId)) {
debugManagerReportEvent(DBGEVENT_DEBUGGER_BREAK);
}
// Wait for all cores
__sevl();
do {
__wfe();
} while ((atomic_load(&g_debugManager.pausedCoreList) & coreList) != coreList);
}
void debugManagerContinueCores(u32 coreList)
{
u32 coreId = currentCoreCtx->coreId;
if (coreList & ~BIT(coreId)) {
generateSgiForList(ThermosphereSgi_DebuggerContinue, coreList & ~BIT(coreId));
}
if (coreList & BIT(coreId) && debugManagerIsCorePaused(coreId)) {
debugManagerUnpauseCores(BIT(coreId));
}
// Wait for all cores
__sevl();
do {
__wfe();
} while ((atomic_load(&g_debugManager.pausedCoreList) & coreList) != 0);
}
/* u64 mdcr = GET_SYSREG(mdcr_el2);
// Trap Debug Exceptions, and accesses to debug registers.
mdcr |= MDCR_EL2_TDE;
// Implied from TDE
mdcr |= MDCR_EL2_TDRA | MDCR_EL2_TDOSA | MDCR_EL2_TDA;
SET_SYSREG(mdcr_el2, mdcr);
*/

View File

@@ -0,0 +1,84 @@
/*
* Copyright (c) 2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
//#include "exceptions.h"
struct ExceptionStackFrame;
//#include "gdb/hvisor_context.h"
#include "transport_interface.h"
//extern GDBContext g_gdbContext;
typedef enum DebugEventType {
DBGEVENT_NONE = 0,
DBGEVENT_DEBUGGER_BREAK,
DBGEVENT_EXCEPTION,
DBGEVENT_CORE_ON,
DBGEVENT_CORE_OFF,
DBGEVENT_EXIT,
DBGEVENT_OUTPUT_STRING,
} DebugEventType;
typedef struct OutputStringDebugEventInfo {
uintptr_t address;
size_t size;
} OutputStringDebugEventInfo;
typedef struct DebugEventInfo {
DebugEventType type;
bool preprocessed;
bool handled;
u32 coreId;
struct ExceptionStackFrame *frame;
union {
OutputStringDebugEventInfo outputString;
};
} DebugEventInfo;
void debugManagerPauseSgiHandler(void);
void debugManagerInit(TransportInterfaceType gdbIfaceType, u32 gdbIfaceId, u32 gdbIfaceFlags);
void debugManagerSetReportingEnabled(bool enabled);
// Hypervisor interrupts will be serviced during the pause-wait
bool debugManagerHandlePause(void);
DebugEventInfo *debugManagerGetDebugEvent(u32 coreId);
bool debugManagerHasDebugEvent(u32 coreId);
// Note: these functions are not reentrant EXCEPT debugPauseCores(1 << currentCoreId)
// "Pause" makes sure all cores reaches the pause function before proceeding.
// "Unpause" doesn't synchronize, it just makes sure the core resumes & that "pause" can be called again.
void debugManagerPauseCores(u32 coreList);
void debugManagerUnpauseCores(u32 coreList);
void debugManagerSetSingleStepCoreList(u32 coreList);
void debugManagerSetSteppingRange(u32 coreId, uintptr_t startAddr, uintptr_t endAddr);
u32 debugManagerGetPausedCoreList(void);
void debugManagerReportEvent(DebugEventType type, ...);
void debugManagerBreakCores(u32 coreList);
void debugManagerContinueCores(u32 coreList);
static inline bool debugManagerIsCorePaused(u32 coreId)
{
return (debugManagerGetPausedCoreList() & BIT(coreId)) != 0;
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#include <vapours/defines.hpp>
#include <vapours/util/util_bitutil.hpp>
#include <atomic>
#include <utility>
#include <optional>
#include <functional>
#include <tuple>
#include <array>
#include "preprocessor.h"
#include "debug_log.h"
#define SINGLETON(cl) \
NON_COPYABLE(cl);\
NON_MOVEABLE(cl);\
private:\
static cl instance;\
public:\
static cl &GetInstance() { return instance; }
#define SINGLETON_WITH_ATTRS(cl, attrs) \
NON_COPYABLE(cl);\
NON_MOVEABLE(cl);\
private:\
attrs static cl instance;\
public:\
static cl &GetInstance() { return instance; }
//FIXME
#ifndef ENSURE
#define ENSURE(...)
#endif
//FIXME
#ifndef ENSURE2
#define ENSURE2(...)
#endif

View File

@@ -0,0 +1,129 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "hvisor_drivers_arm_pl011.hpp"
namespace ams::hvisor::drivers::arm {
void PL011::Initialize(u32 baudRate, u32 clkRate) const
{
/* The TRM (DDI0183) reads:
Program the control registers as follows:
1. Disable the UART.
2. Wait for the end of transmission or reception of the current character.
3. Flush the transmit FIFO by disabling bit 4 (FEN) in the line control register
(UARTCLR_H).
4. Reprogram the control register.
5. Enable the UART.
*/
// First, disable the UART. Flush the receive FIFO, wait for tx to complete, and disable both FIFOs.
m_regs->cr &= ~CR_UARTEN;
while (!(m_regs->fr & FR_RXFE)) {
m_regs->dr;
}
while (m_regs->fr & FR_BUSY);
// This flushes the transmit FIFO:
m_regs->lcr_h &= ~LCR_H_FEN;
// Divisor = clkRate / (16 * baudRate). Integer part (16 bits) in IBRD, 6 fractional bits in FBRD (fixed point)
// This means the encoded divisor is 2^6 * divisor = 4*clkRate / baudRate
u32 rawDivisor = (4 * clkRate) / baudRate;
m_regs->ibrd = (rawDivisor >> 6) & 0xFFFF;
m_regs->fbrd = rawDivisor & 0x3F;
// Select FIFO fill levels for interrupts
m_regs->ifls = IFLS_RX4_8 | IFLS_TX4_8;
// FIFO Enabled / No Parity / 8 Data bit / One Stop Bit
m_regs->lcr_h = LCR_H_FEN | LCR_H_WLEN_8;
// Select the interrupts we want to have
// RX timeout and TX/RX fill interrupts
m_regs->imsc = RTI | RXI | RXI;
// Clear any pending errors
m_regs->ecr = 0;
// Clear all interrupts
m_regs->icr = ALL_INTERRUPTS_MASK;
// Enable tx, rx, and uart overall
m_regs->cr = CR_RXE | CR_TXE | CR_UARTEN;
}
void PL011::WriteData(const void *buffer, size_t size) const
{
const u8 *buf8 = reinterpret_cast<const u8 *>(buffer);
for (size_t i = 0; i < size; i++) {
while (m_regs->fr & FR_TXFF); // while TX FIFO full
m_regs->dr = buf8[i];
}
}
void PL011::ReadData(void *buffer, size_t size) const
{
u8 *buf8 = reinterpret_cast<u8 *>(buffer);
size_t i;
for (i = 0; i < size; i++) {
while (m_regs->fr & FR_RXFE);
buf8[i] = m_regs->dr;
}
}
size_t PL011::ReadDataMax(void *buffer, size_t maxSize) const
{
u8 *buf8 = reinterpret_cast<u8 *>(buffer);
size_t count = 0;
for (size_t i = 0; i < maxSize && !(m_regs->fr & FR_RXFE); i++) {
buf8[i] = m_regs->dr;
++count;
}
return count;
}
size_t PL011::ReadDataUntil(char *buffer, size_t maxSize, char delimiter) const
{
size_t count = 0;
for (size_t i = 0; i < maxSize; i++) {
while (m_regs->fr & FR_RXFE);
buffer[i] = m_regs->dr;
++count;
if (buffer[i] == delimiter) {
break;
}
}
return count;
}
void PL011::SetRxInterruptEnabled(bool enabled) const
{
constexpr u32 mask = RTI | RXI;
// We don't support any other interrupt here.
m_regs->imsc = enabled ? mask : 0;
}
}

View File

@@ -0,0 +1,152 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../../defines.hpp"
// AMBA PL011 driver
// Originally from
/*
* Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
namespace ams::hvisor::drivers::arm {
class PL011 final {
private:
struct Registers {
u32 dr;
union {
u32 sr;
u32 ecr;
};
u32 _0x08, _0x0C, _0x10, _0x14;
u32 fr;
u32 _0x1C;
u32 ilpr;
u32 ibrd;
u32 fbrd;
u32 lcr_h;
u32 cr;
u32 ifls;
u32 imsc;
u32 ris;
u32 mis;
u32 icr;
u32 dmacr;
};
static_assert(std::is_standard_layout_v<Registers>);
static_assert(std::is_trivial_v<Registers>);
enum Mask : u32 {
DATA_ERROR_MASK = 0x0F00, // Data status bits
PL011_STATUS_ERROR_MASK = 0x0F, // Status reg bits
};
enum Error : u32 {
OE = BIT(3), // Overrun error
BE = BIT(2), // Break error
PE = BIT(1), // Parity error
FE = BIT(0), // Framing error
};
enum Interrupt : u32 {
OEI = BIT(10), // Overrun error interrupt
BEI = BIT(9), // Break error interrupt
PEI = BIT(8), // Parity error interrupt
FEI = BIT(7), // Framing error interrupt
RTI = BIT(6), // Receive timeout interrupt
TXI = BIT(5), // Transmit interrupt
RXI = BIT(4), // Receive interrupt
DSRMI = BIT(3), // DSR modem interrupt
DCDMI = BIT(2), // DCD modem interrupt
CTSMI = BIT(1), // CTS modem interrupt
RIMI = BIT(0), // RI modem interrupt
ALL_INTERRUPTS_MASK = MASK(11),
};
// Flag reg bits
enum FrFlags : u32 {
FR_RI = BIT(8), // Ring indicator
FR_TXFE = BIT(7), // Transmit FIFO empty
FR_RXFF = BIT(6), // Receive FIFO full
FR_TXFF = BIT(5), // Transmit FIFO full
FR_RXFE = BIT(4), // Receive FIFO empty
FR_BUSY = BIT(3), // UART busy
FR_DCD = BIT(2), // Data carrier detect
FR_DSR = BIT(1), // Data set ready
FR_CTS = BIT(0), // Clear to send
};
// Control reg bits
enum CrFlags : u32 {
CR_CTSEN = BIT(15), // CTS hardware flow control enable
CR_RTSEN = BIT(14), // RTS hardware flow control enable
CR_RTS = BIT(11), // Request to send
CR_DTR = BIT(10), // Data transmit ready.
CR_RXE = BIT(9), // Receive enable
CR_TXE = BIT(8), // Transmit enable
CR_LBE = BIT(7), // Loopback enable
CR_UARTEN = BIT(0), // UART Enable
};
// Line Control Register Bits
enum LcrFlags : u32 {
LCR_H_SPS = BIT(7), // Stick parity select
LCR_H_WLEN_8 = (3 << 5),
LCR_H_WLEN_7 = (2 << 5),
LCR_H_WLEN_6 = BIT(5),
LCR_H_WLEN_5 = (0 << 5),
LCR_H_FEN = BIT(4), // FIFOs Enable
LCR_H_STP2 = BIT(3), // Two stop bits select
LCR_H_EPS = BIT(2), // Even parity select
LCR_H_PEN = BIT(1), // Parity Enable
LCR_H_BRK = BIT(0), // Send break
};
// FIFO level select register
enum IflsLevels : u32 {
IFLS_RX1_8 = (0 << 3),
IFLS_RX2_8 = (1 << 3),
IFLS_RX4_8 = (2 << 3),
IFLS_RX6_8 = (3 << 3),
IFLS_RX7_8 = (4 << 3),
IFLS_TX1_8 = (0 << 0),
IFLS_TX2_8 = (1 << 0),
IFLS_TX4_8 = (2 << 0),
IFLS_TX6_8 = (3 << 0),
IFLS_TX7_8 = (4 << 0),
};
private:
// TODO friend
volatile Registers *m_regs = nullptr;
private:
void Initialize(u32 baudRate, u32 clkRate) const;
public:
void WriteData(const void *buffer, size_t size) const;
void ReadData(void *buffer, size_t size) const;
size_t ReadDataMax(void *buffer, size_t maxSize) const;
size_t ReadDataUntil(char *buffer, size_t maxSize, char delimiter) const;
void SetRxInterruptEnabled(bool enabled) const;
};
}

View File

@@ -0,0 +1,131 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "hvisor_drivers_tegra_uart.hpp"
#include "../../hvisor_generic_timer.hpp"
#include <chrono>
using namespace ams::hvisor;
namespace {
inline void WaitCycles(u32 baudRate, u32 num)
{
u32 t = (num * 1000000 + 16 * baudRate - 1) / (16 * baudRate);
GenericTimer::GetInstance().Wait(std::chrono::microseconds{t});
}
inline void WaitSyms(u32 baudRate, u32 num)
{
u32 t = (num * 1000000 + baudRate - 1) / baudRate;
GenericTimer::GetInstance().Wait(std::chrono::microseconds{t});
}
}
namespace ams::hvisor::drivers::tegra {
void Uart::Initialize(u32 baudRate, u32 clkRate, bool invertTx) const
{
// Calculate baud rate, round to nearest (clkRate / (16 * baudRate))
u32 divisor = (8 * baudRate + clkRate) / (16 * baudRate);
m_regs->lcr &= ~LCR_DLAB; // Disable DLAB.
m_regs->ier = 0; // Disable all interrupts.
m_regs->mcr = 0;
// Setup UART in FIFO mode
m_regs->lcr = LCR_DLAB | LCR_WD_LENGTH_8; // Enable DLAB and set word length 8.
m_regs->dll = divisor & 0xFF; // Divisor latch LSB.
m_regs->dlh = (divisor >> 8) & 0xFF; // Divisor latch MSB.
m_regs->lcr &= ~LCR_DLAB; // Disable DLAB.
m_regs->spr; // Dummy read.
WaitSyms(baudRate, 3); // Wait for 3 symbols at the new baudrate.
// Enable FIFO with default settings.
m_regs->fcr = FCR_FCR_EN_FIFO;
m_regs->irda_csr = invertTx ? IRDA_CSR_INVERT_TXD : 0; // Invert TX if needed
m_regs->spr; // Dummy read as mandated by TRM.
WaitCycles(baudRate, 3); // Wait for 3 baud cycles, as mandated by TRM (erratum).
// Flush FIFO.
WaitIdle(STATUS_TX_IDLE); // Make sure there's no data being written in TX FIFO (TRM).
m_regs->fcr |= FCR_RX_CLR | FCR_TX_CLR; // Clear TX and RX FIFOs.
WaitCycles(baudRate, 32); // Wait for 32 baud cycles (TRM, erratum).
// Wait for idle state (TRM).
WaitIdle(STATUS_TX_IDLE | STATUS_RX_IDLE);
}
void Uart::WriteData(const void *buffer, size_t size) const
{
const u8 *buf8 = reinterpret_cast<const u8 *>(buffer);
for (size_t i = 0; i < size; i++) {
while (!(m_regs->lsr & LSR_THRE)); // Wait until it's possible to send data.
m_regs->thr = buf8[i];
}
}
void Uart::ReadData(void *buffer, size_t size) const
{
u8 *buf8 = reinterpret_cast<u8 *>(buffer);
for (size_t i = 0; i < size; i++) {
while (!(m_regs->lsr & LSR_RDR)) // Wait until it's possible to receive data.
buf8[i] = m_regs->rbr;
}
}
size_t Uart::ReadDataMax(void *buffer, size_t maxSize) const
{
u8 *buf8 = reinterpret_cast<u8 *>(buffer);
size_t count = 0;
for (size_t i = 0; i < maxSize && (m_regs->lsr & LSR_RDR); i++) {
buf8[i] = m_regs->rbr;
++count;
}
return count;
}
size_t Uart::ReadDataUntil(char *buffer, size_t maxSize, char delimiter) const
{
size_t count = 0;
for (size_t i = 0; i < maxSize && (m_regs->lsr & LSR_RDR); i++) {
while (!(m_regs->lsr & LSR_RDR)) // Wait until it's possible to receive data.
buffer[i] = m_regs->rbr;
++count;
if (buffer[i] == delimiter) {
break;
}
}
return count;
}
void Uart::SetRxInterruptEnabled(bool enabled) const
{
constexpr u32 mask = IER_IE_RX_TIMEOUT | IER_IE_RHR;
// We don't support any other interrupt here.
m_regs->ier = enabled ? mask : 0;
}
}

View File

@@ -0,0 +1,210 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../../defines.hpp"
namespace ams::hvisor::drivers::tegra {
class Uart final {
private:
struct Registers {
union {
// UART_THR_DLAB_0
u32 thr;
u32 rbr;
u32 dll;
};
union {
// UART_IER_DLAB_0
u32 ier;
u32 dlh;
};
union {
// UART_IIR_FCR_0
u32 iir;
u32 fcr;
};
u32 lcr;
u32 mcr;
u32 lsr;
u32 msr;
u32 spr;
u32 irda_csr;
u32 rx_fifo_cfg;
u32 mie;
u32 vendor_status;
u8 _0x30[0x0C];
u32 asr;
};
static_assert(std::is_standard_layout_v<Registers>);
static_assert(std::is_trivial_v<Registers>);
// 36.3.12 UART_VENDOR_STATUS_0_0
enum VendorStatusFlags : u32 {
STATUS_TX_FIFO_COUNTER = 0b111111 << 24, // reflects number of current entries in TX FIFO
STATUS_RX_FIFO_COUNTER = 0b111111 << 16, // reflects number of current entries in RX FIFO
/*
This bit is set to 1 when write data is issued to the TX FIFO when
it is already full and gets cleared on register read (sticky bit until read):
0 = NO_OVERRUN
1 = OVERRUN
*/
STATUS_TX_OVERRUN = BIT(3),
/*
This bit is set to 1 when a read is issued to an empty FIFO and
gets cleared on register read (sticky bit until read):
0 = NO_UNDERRUN
1 = UNDERRUN
*/
STATUS_RX_UNDERRUN = BIT(2),
STATUS_RX_IDLE = BIT(1),
STATUS_TX_IDLE = BIT(0),
};
// 36.3.6 UART_LSR_0
enum LsrFlags : u32 {
LSR_RX_FIFO_EMPTY = BIT(9), // Receiver FIFO empty status
LSR_TX_FIFO_FULL = BIT(8), // Transmitter FIFO full status
LSR_FIFOE = BIT(7), // Receive FIFO Error
LSR_TMTY = BIT(6), // Transmit Shift Register empty status
LSR_THRE = BIT(5), // Transmit Holding Register is Empty -- OK to write data
LSR_BRK = BIT(4), // BREAK condition detected on line
LSR_FERR = BIT(3), // Framing Error
LSR_PERR = BIT(2), // Parity Error
LSR_OVRF = BIT(1), // Receiver Overrun Error
LSR_RDR = BIT(0), // Receiver Data Ready
};
// 36.3.4 UART_LCR_0
enum LcrFlags : u32 {
/*
STOP:
0 = Transmit 1 stop bit
1 = Transmit 2 stop bits (receiver always checks for 1 stop bit)
*/
LCR_DLAB = BIT(7), // Divisor Latch Access Bit (set to allow programming of the DLH, DLM Divisors)
LCR_SET_B = BIT(6), // Set BREAK condition -- Transmitter sends all zeroes to indicate BREAK
LCR_SET_P = BIT(5), // Set (force) parity to value in LCR[4]
LCR_EVEN = BIT(4), // Even parity format. There will always be an even number of 1s in the binary representation (PAR = 1)
LCR_PAR = BIT(3), // Parity enabled
LCR_STOP = BIT(2),
LCR_WD_LENGTH_5 = 0 << 0, // word length 5
LCR_WD_LENGTH_6 = 1 << 0, // word length 6
LCR_WD_LENGTH_7 = 2 << 0, // word length 7
LCR_WD_LENGTH_8 = 3 << 0, // word length 8
};
// 36.3.3 UART_IIR_FCR_0
enum FcrFlags : u32{
// RX_TRIG
FCR_RX_TRIG_MASK = 3 << 6,
FCR_RX_TRIG_FIFO_COUNT_GREATER_1 = 0 << 6,
FCR_RX_TRIG_FIFO_COUNT_GREATER_4 = 1 << 6,
FCR_RX_TRIG_FIFO_COUNT_GREATER_8 = 2 << 6,
FCR_RX_TRIG_FIFO_COUNT_GREATER_16 = 3 << 6,
// TX_TRIG
FCR_TX_TRIG_MASK = 3 << 4,
FCR_TX_TRIG_FIFO_COUNT_GREATER_16 = 0 << 4,
FCR_TX_TRIG_FIFO_COUNT_GREATER_8 = 1 << 4,
FCR_TX_TRIG_FIFO_COUNT_GREATER_4 = 2 << 4,
FCR_TX_TRIG_FIFO_COUNT_GREATER_1 = 3 << 4,
/*
DMA:
0 = DMA_MODE_0
1 = DMA_MODE_1
*/
FCR_DMA = BIT(3),
/*
RX/TX_CLR:
Clears the contents of the receive (resp. transmit) FIFO and resets
its counter logic to 0.
The receive (resp. transmit) shift register is not cleared or altered.
This bit returns to 0 after clearing the FIFOs.
*/
FCR_TX_CLR = BIT(2), // See above
FCR_RX_CLR = BIT(1), // See above
FCR_FCR_EN_FIFO = BIT(0), // Enable the transmit and receive FIFOs. This bit should be enabled
};
// 36.3.2 UART_IER_DLAB_0_0
enum IerFlags : u32 {
IER_IE_EORD = BIT(5), // Interrupt enable for Interrupt Enable for End of Received Data
IER_IE_RX_TIMEOUT = BIT(4), // Interrupt enable for RX FIFO timeout
IER_IE_MSI = BIT(3), // Interrupt enable for Modem Status Interrupt
IER_IE_RXS = BIT(2), // Interrupt enable for Receiver Line Status Interrupt
IER_IE_THR = BIT(1), // Interrupt enable for Transmitter Holding Register Empty interrupt
IER_IE_RHR = BIT(0), // Interrupt enable for Received Data Interrupt
};
// 6.3.3 UART_IIR_FCR_0
enum IirFlags : u32 {
IIR_EN_FIFO_MASK = 3 << 6,
IIR_MODE_16450 = 0 << 6,
IIR_MODE_16550 = 1 << 6,
IIR_IS_PRI2 = BIT(3), // Encoded Interrupt ID Refer to IIR[3:0] table
IIR_IS_PRI1 = BIT(2), // Encoded Interrupt ID Refer to IIR[3:0] table
IIR_IS_PRI0 = BIT(1), // Encoded Interrupt ID Refer to IIR[3:0] table [36.3.3]
IIR_IS_STA = BIT(0), // Interrupt Pending if ZERO
};
// 36.3.9 UART_IRDA_CSR_0
enum IrdaCsrFlags : u32{
IRDA_CSR_SIR_A = BIT(7),
IRDA_CSR_PWT_A_BAUD_PULSE_3_14 = 0 << 6,
IRDA_CSR_PWT_A_BAUD_PULSE_4_14 = 1 << 6,
IRDA_CSR_INVERT_RTS = BIT(3),
IRDA_CSR_INVERT_CTS = BIT(2),
IRDA_CSR_INVERT_TXD = BIT(1),
IRDA_CSR_INVERT_RXD = BIT(0),
};
private:
// TODO friend
volatile Registers *m_regs = nullptr;
private:
void Initialize(u32 baudRate, u32 clkRate, bool invertTx) const;
void WaitIdle(u32 status) const
{
if (status & STATUS_TX_IDLE) {
while (!(m_regs->lsr & LSR_TMTY));
}
if (status & STATUS_RX_IDLE) {
while (m_regs->lsr & LSR_RDR);
}
}
public:
void WriteData(const void *buffer, size_t size) const;
void ReadData(void *buffer, size_t size) const;
size_t ReadDataMax(void *buffer, size_t maxSize) const;
size_t ReadDataUntil(char *buffer, size_t maxSize, char delimiter) const;
void SetRxInterruptEnabled(bool enabled) const;
};
}

View File

@@ -0,0 +1,654 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../../../defines.hpp"
namespace ams::hvisor::drivers::tegra::t210 {
class Car final {
private:
struct Registers {
u32 rst_src; // _RST_SOURCE_0, 0x000
// _RST_DEVICES_L/H/U_0 0x4-0xc
u32 rst_dev_l;
u32 rst_dev_h;
u32 rst_dev_u;
// _CLK_OUT_ENB_L/H/U_0 0x10-0x18
u32 clk_out_enb_l;
u32 clk_out_enb_h;
u32 clk_out_enb_u;
u32 _0x1C;
u32 cclk_brst_pol; // _CCLK_BURST_POLICY_0, 0x020
u32 super_cclk_div; // _SUPER_CCLK_DIVIDER_0, 0x024
u32 sclk_brst_pol; // _SCLK_BURST_POLICY_0, 0x028
u32 super_sclk_div; // _SUPER_SCLK_DIVIDER_0, 0x02c
u32 clk_sys_rate; // _CLK_SYSTEM_RATE_0, 0x030
u32 prog_dly_clk; // _PROG_DLY_CLK_0, 0x034
u32 aud_sync_clk_rate; // _AUDIO_SYNC_CLK_RATE_0, 0x038
u32 _0x3C;
u32 cop_clk_skip_plcy; // _COP_CLK_SKIP_POLICY_0, 0x040
u32 clk_mask_arm; // _CLK_MASK_ARM_0, 0x044
u32 misc_clk_enb; // _MISC_CLK_ENB_0, 0x048
u32 clk_cpu_cmplx; // _CLK_CPU_CMPLX_0, 0x04c
u32 osc_ctrl; // _OSC_CTRL_0, 0x050
u32 pll_lfsr; // _PLL_LFSR_0, 0x054
u32 osc_freq_det; // _OSC_FREQ_DET_0, 0x058
u32 osc_freq_det_stat; // _OSC_FREQ_DET_STATUS_0, 0x05c
u32 _0x60[2];
u32 plle_ss_cntl; // _PLLE_SS_CNTL_0, 0x068
u32 plle_misc1; // _PLLE_MISC1_0, 0x06c
u32 _0x70[4];
// PLLC 0x80-0x8c
u32 pllc_base;
u32 pllc_out;
u32 pllc_misc0;
u32 pllc_misc1;
// PLLM 0x90-0x9c
u32 pllm_base;
u32 pllm_out;
u32 pllm_misc1;
u32 pllm_misc2;
// PLLP 0xa0-0xac
u32 pllp_base;
u32 pllp_outa;
u32 pllp_outb;
u32 pllp_misc;
// PLLA 0xb0-0xbc
u32 plla_base;
u32 plla_out;
u32 plla_misc0;
u32 plla_misc1;
// PLLU 0xc0-0xcc
u32 pllu_base;
u32 pllu_out;
u32 pllu_misc1;
u32 pllu_misc2;
// PLLD 0xd0-0xdc
u32 plld_base;
u32 plld_out;
u32 plld_misc1;
u32 plld_misc2;
// PLLX 0xe0-0xe4
u32 pllx_base;
u32 pllx_misc;
// PLLE 0xe8-0xf4
u32 plle_base;
u32 plle_misc;
u32 plle_ss_cntl1;
u32 plle_ss_cntl2;
u32 lvl2_clk_gate_ovra; // _LVL2_CLK_GATE_OVRA_0, 0x0f8
u32 lvl2_clk_gate_ovrb; // _LVL2_CLK_GATE_OVRB_0, 0x0fc
u32 clk_source_i2s2; // _CLK_SOURCE_I2S2_0, 0x100
u32 clk_source_i2s3; // _CLK_SOURCE_I2S3_0, 0x104
u32 clk_source_spdif_out; // _CLK_SOURCE_SPDIF_OUT_0, 0x108
u32 clk_source_spdif_in; // _CLK_SOURCE_SPDIF_IN_0, 0x10c
u32 clk_source_pwm; // _CLK_SOURCE_PWM_0, 0x110
u32 _0x114;
u32 clk_source_spi2; // _CLK_SOURCE_SPI2_0, 0x118
u32 clk_source_spi3; // _CLK_SOURCE_SPI3_0, 0x11c
u32 _0x120;
u32 clk_source_i2c1; // _CLK_SOURCE_I2C1_0, 0x124
u32 clk_source_i2c5; // _CLK_SOURCE_I2C5_0, 0x128
u32 _0x12c[2];
u32 clk_source_spi1; // _CLK_SOURCE_SPI1_0, 0x134
u32 clk_source_disp1; // _CLK_SOURCE_DISP1_0, 0x138
u32 clk_source_disp2; // _CLK_SOURCE_DISP2_0, 0x13c
u32 _0x140;
u32 clk_source_isp; // _CLK_SOURCE_ISP_0, 0x144
u32 clk_source_vi; // _CLK_SOURCE_VI_0, 0x148
u32 _0x14c;
u32 clk_source_sdmmc1; // _CLK_SOURCE_SDMMC1_0, 0x150
u32 clk_source_sdmmc2; // _CLK_SOURCE_SDMMC2_0, 0x154
u32 _0x158[3];
u32 clk_source_sdmmc4; // _CLK_SOURCE_SDMMC4_0, 0x164
u32 _0x168[4];
u32 clk_source_uarta; // _CLK_SOURCE_UARTA_0, 0x178
u32 clk_source_uartb; // _CLK_SOURCE_UARTB_0, 0x17c
u32 clk_source_host1x; // _CLK_SOURCE_HOST1X_0, 0x180
u32 _0x184[5];
u32 clk_source_i2c2; // _CLK_SOURCE_I2C2_0, 0x198
u32 clk_source_emc; // _CLK_SOURCE_EMC_0, 0x19c
u32 clk_source_uartc; // _CLK_SOURCE_UARTC_0, 0x1a0
u32 _0x1a4;
u32 clk_source_vi_sensor; // _CLK_SOURCE_VI_SENSOR_0, 0x1a8
u32 _0x1ac[2];
u32 clk_source_spi4; // _CLK_SOURCE_SPI4_0, 0x1b4
u32 clk_source_i2c3; // _CLK_SOURCE_I2C3_0, 0x1b8
u32 clk_source_sdmmc3; // _CLK_SOURCE_SDMMC3_0, 0x1bc
u32 clk_source_uartd; // _CLK_SOURCE_UARTD_0, 0x1c0
u32 _0x1c4[2];
u32 clk_source_owr; // _CLK_SOURCE_OWR_0, 0x1cc
u32 _0x1d0;
u32 clk_source_csite; // _CLK_SOURCE_CSITE_0, 0x1d4
u32 clk_source_i2s1; // _CLK_SOURCE_I2S1_0, 0x1d8
u32 clk_source_dtv; // _CLK_SOURCE_DTV_0, 0x1dc
u32 _0x1e0[5];
u32 clk_source_tsec; // _CLK_SOURCE_TSEC_0, 0x1f4
u32 _0x1f8;
u32 clk_spare2; // _CLK_SPARE2_0, 0x1fc
u32 _0x200[32];
u32 clk_out_enb_x; // _CLK_OUT_ENB_X_0, 0x280
u32 clk_enb_x_set; // _CLK_ENB_X_SET_0, 0x284
u32 clk_enb_x_clr; // _CLK_ENB_X_CLR_0, 0x288
u32 rst_devices_x; // _RST_DEVICES_X_0, 0x28c
u32 rst_dev_x_set; // _RST_DEV_X_SET_0, 0x290
u32 rst_dev_x_clr; // _RST_DEV_X_CLR_0, 0x294
u32 clk_out_enb_y; // _CLK_OUT_ENB_Y_0, 0x298
u32 clk_enb_y_set; // _CLK_ENB_Y_SET_0, 0x29c
u32 clk_enb_y_clr; // _CLK_ENB_Y_CLR_0, 0x2a0
u32 rst_devices_y; // _RST_DEVICES_Y_0, 0x2a4
u32 rst_dev_y_set; // _RST_DEV_Y_SET_0, 0x2a8
u32 rst_dev_y_clr; // _RST_DEV_Y_CLR_0, 0x2ac
u32 _0x2b0[17];
u32 dfll_base; // _DFLL_BASE_0, 0x2f4
u32 _0x2f8[2];
// _RST_DEV_L/H/U_SET_0 0x300-0x314
u32 rst_dev_l_set;
u32 rst_dev_l_clr;
u32 rst_dev_h_set;
u32 rst_dev_h_clr;
u32 rst_dev_u_set;
u32 rst_dev_u_clr;
u32 _0x318[2];
// _CLK_ENB_L/H/U_CLR_0 0x320-0x334
u32 clk_enb_l_set;
u32 clk_enb_l_clr;
u32 clk_enb_h_set;
u32 clk_enb_h_clr;
u32 clk_enb_u_set;
u32 clk_enb_u_clr;
u32 _0x338;
u32 ccplex_pg_sm_ovrd; // _CCPLEX_PG_SM_OVRD_0, 0x33c
u32 rst_cpu_cmplx_set; // _RST_CPU_CMPLX_SET_0, 0x340
u32 rst_cpu_cmplx_clr; // _RST_CPU_CMPLX_CLR_0, 0x344
// Additional (T30) registers
u32 clk_cpu_cmplx_set; // _CLK_CPU_CMPLX_SET_0, 0x348
u32 clk_cpu_cmplx_clr; // _CLK_CPU_CMPLX_SET_0, 0x34c
u32 _0x350[2];
u32 rst_dev_v; // _RST_DEVICES_V_0, 0x358
u32 rst_dev_w; // _RST_DEVICES_W_0, 0x35c
u32 clk_out_enb_v; // _CLK_OUT_ENB_V_0, 0x360
u32 clk_out_enb_w; // _CLK_OUT_ENB_W_0, 0x364
u32 cclkg_brst_pol; // _CCLKG_BURST_POLICY_0, 0x368
u32 super_cclkg_div; // _SUPER_CCLKG_DIVIDER_0, 0x36c
u32 cclklp_brst_pol; // _CCLKLP_BURST_POLICY_0, 0x370
u32 super_cclkp_div; // _SUPER_CCLKLP_DIVIDER_0, 0x374
u32 clk_cpug_cmplx; // _CLK_CPUG_CMPLX_0, 0x378
u32 clk_cpulp_cmplx; // _CLK_CPULP_CMPLX_0, 0x37c
u32 cpu_softrst_ctrl; // _CPU_SOFTRST_CTRL_0, 0x380
u32 cpu_softrst_ctrl1; // _CPU_SOFTRST_CTRL1_0, 0x384
u32 cpu_softrst_ctrl2; // _CPU_SOFTRST_CTRL2_0, 0x388
u32 _0x38c[5];
u32 lvl2_clk_gate_ovrc; // _LVL2_CLK_GATE_OVRC, 0x3a0
u32 lvl2_clk_gate_ovrd; // _LVL2_CLK_GATE_OVRD, 0x3a4
u32 _0x3a8[2];
u32 _0x3b0;
u32 clk_source_mselect; // _CLK_SOURCE_MSELECT_0, 0x3b4
u32 clk_source_tsensor; // _CLK_SOURCE_TSENSOR_0, 0x3b8
u32 clk_source_i2s4; // _CLK_SOURCE_I2S4_0, 0x3bc
u32 clk_source_i2s5; // _CLK_SOURCE_I2S5_0, 0x3c0
u32 clk_source_i2c4; // _CLK_SOURCE_I2C4_0, 0x3c4
u32 _0x3c8[2];
u32 clk_source_ahub; // _CLK_SOURCE_AHUB_0, 0x3d0
u32 _0x3d4[4];
u32 clk_source_hda2codec_2x; // _CLK_SOURCE_HDA2CODEC_2X_0, 0x3e4
u32 clk_source_actmon; // _CLK_SOURCE_ACTMON_0, 0x3e8
u32 clk_source_extperiph1; // _CLK_SOURCE_EXTPERIPH1_0, 0x3ec
u32 clk_source_extperiph2; // _CLK_SOURCE_EXTPERIPH2_0, 0x3f0
u32 clk_source_extperiph3; // _CLK_SOURCE_EXTPERIPH3_0, 0x3f4
u32 _0x3f8;
u32 clk_source_i2c_slow; // _CLK_SOURCE_I2C_SLOW_0, 0x3fc
u32 clk_source_sys; // _CLK_SOURCE_SYS_0, 0x400
u32 clk_source_ispb; // _CLK_SOURCE_ISPB_0, 0x404
u32 _0x408[2];
u32 clk_source_sor1; // _CLK_SOURCE_SOR1_0, 0x410
u32 clk_source_sor0; // _CLK_SOURCE_SOR0_0, 0x414
u32 _0x418[2];
u32 clk_source_sata_oob; // _CLK_SOURCE_SATA_OOB_0, 0x420
u32 clk_source_sata; // _CLK_SOURCE_SATA_0, 0x424
u32 clk_source_hda; // _CLK_SOURCE_HDA_0, 0x428
u32 _0x42c;
// _RST_DEV_V/W_SET_0 0x430-0x43c
u32 rst_dev_v_set;
u32 rst_dev_v_clr;
u32 rst_dev_w_set;
u32 rst_dev_w_clr;
// _CLK_ENB_V/W_CLR_0 0x440-0x44c
u32 clk_enb_v_set;
u32 clk_enb_v_clr;
u32 clk_enb_w_set;
u32 clk_enb_w_clr;
// Additional (T114+) registers
u32 rst_cpug_cmplx_set; // _RST_CPUG_CMPLX_SET_0, 0x450
u32 rst_cpug_cmplx_clr; // _RST_CPUG_CMPLX_CLR_0, 0x454
u32 rst_cpulp_cmplx_set; // _RST_CPULP_CMPLX_SET_0, 0x458
u32 rst_cpulp_cmplx_clr; // _RST_CPULP_CMPLX_CLR_0, 0x45c
u32 clk_cpug_cmplx_set; // _CLK_CPUG_CMPLX_SET_0, 0x460
u32 clk_cpug_cmplx_clr; // _CLK_CPUG_CMPLX_CLR_0, 0x464
u32 clk_cpulp_cmplx_set; // _CLK_CPULP_CMPLX_SET_0, 0x468
u32 clk_cpulp_cmplx_clr; // _CLK_CPULP_CMPLX_CLR_0, 0x46c
u32 cpu_cmplx_status; // _CPU_CMPLX_STATUS_0, 0x470
u32 _0x474;
u32 intstatus; // _INTSTATUS_0, 0x478
u32 intmask; // _INTMASK_0, 0x47c
u32 utmip_pll_cfg0; // _UTMIP_PLL_CFG0_0, 0x480
u32 utmip_pll_cfg1; // _UTMIP_PLL_CFG1_0, 0x484
u32 utmip_pll_cfg2; // _UTMIP_PLL_CFG2_0, 0x488
u32 plle_aux; // _PLLE_AUX_0, 0x48c
u32 sata_pll_cfg0; // _SATA_PLL_CFG0_0, 0x490
u32 sata_pll_cfg1; // _SATA_PLL_CFG1_0, 0x494
u32 pcie_pll_cfg0; // _PCIE_PLL_CFG0_0, 0x498
u32 prog_audio_dly_clk; // _PROG_AUDIO_DLY_CLK_0, 0x49c
u32 audio_sync_clk_i2s0; // _AUDIO_SYNC_CLK_I2S0_0, 0x4a0
u32 audio_sync_clk_i2s1; // _AUDIO_SYNC_CLK_I2S1_0, 0x4a4
u32 audio_sync_clk_i2s2; // _AUDIO_SYNC_CLK_I2S2_0, 0x4a8
u32 audio_sync_clk_i2s3; // _AUDIO_SYNC_CLK_I2S3_0, 0x4ac
u32 audio_sync_clk_i2s4; // _AUDIO_SYNC_CLK_I2S4_0, 0x4b0
u32 audio_sync_clk_spdif; // _AUDIO_SYNC_CLK_SPDIF_0, 0x4b4
u32 plld2_base; // _PLLD2_BASE_0, 0x4b8
u32 plld2_misc; // _PLLD2_MISC_0, 0x4bc
u32 utmip_pll_cfg3; // _UTMIP_PLL_CFG3_0, 0x4c0
u32 pllrefe_base; // _PLLREFE_BASE_0, 0x4c4
u32 pllrefe_misc; // _PLLREFE_MISC_0, 0x4c8
u32 pllrefe_out; // _PLLREFE_OUT_0, 0x4cc
u32 cpu_finetrim_byp; // _CPU_FINETRIM_BYP_0, 0x4d0
u32 cpu_finetrim_select; // _CPU_FINETRIM_SELECT_0, 0x4d4
u32 cpu_finetrim_dr; // _CPU_FINETRIM_DR_0, 0x4d8
u32 cpu_finetrim_df; // _CPU_FINETRIM_DF_0, 0x4dc
u32 cpu_finetrim_f; // _CPU_FINETRIM_F_0, 0x4e0
u32 cpu_finetrim_r; // _CPU_FINETRIM_R_0, 0x4e4
u32 pllc2_base; // _PLLC2_BASE_0, 0x4e8
u32 pllc2_misc0; // _PLLC2_MISC_0_0, 0x4ec
u32 pllc2_misc1; // _PLLC2_MISC_1_0, 0x4f0
u32 pllc2_misc2; // _PLLC2_MISC_2_0, 0x4f4
u32 pllc2_misc3; // _PLLC2_MISC_3_0, 0x4f8
u32 pllc3_base; // _PLLC3_BASE_0, 0x4fc
u32 pllc3_misc0; // _PLLC3_MISC_0_0, 0x500
u32 pllc3_misc1; // _PLLC3_MISC_1_0, 0x504
u32 pllc3_misc2; // _PLLC3_MISC_2_0, 0x508
u32 pllc3_misc3; // _PLLC3_MISC_3_0, 0x50c
u32 pllx_misc1; // _PLLX_MISC_1_0, 0x510
u32 pllx_misc2; // _PLLX_MISC_2_0, 0x514
u32 pllx_misc3; // _PLLX_MISC_3_0, 0x518
u32 xusbio_pll_cfg0; // _XUSBIO_PLL_CFG0_0, 0x51c
u32 xusbio_pll_cfg1; // _XUSBIO_PLL_CFG0_1, 0x520
u32 plle_aux1; // _PLLE_AUX1_0, 0x524
u32 pllp_reshift; // _PLLP_RESHIFT_0, 0x528
u32 utmipll_hw_pwrdn_cfg0; // _UTMIPLL_HW_PWRDN_CFG0_0, 0x52c
u32 pllu_hw_pwrdn_cfg0; // _PLLU_HW_PWRDN_CFG0_0, 0x530
u32 xusb_pll_cfg0; // _XUSB_PLL_CFG0_0, 0x534
u32 _0x538;
u32 clk_cpu_misc; // _CLK_CPU_MISC_0, 0x53c
u32 clk_cpug_misc; // _CLK_CPUG_MISC_0, 0x540
u32 clk_cpulp_misc; // _CLK_CPULP_MISC_0, 0x544
u32 pllx_hw_ctrl_cfg; // _PLLX_HW_CTRL_CFG_0, 0x548
u32 pllx_sw_ramp_cfg; // _PLLX_SW_RAMP_CFG_0, 0x54c
u32 pllx_hw_ctrl_status; // _PLLX_HW_CTRL_STATUS_0, 0x550
u32 lvl2_clk_gate_ovre; // _LVL2_CLK_GATE_OVRE, 0x554
u32 super_gr3d_clk_div; // _SUPER_GR3D_CLK_DIVIDER_0, 0x558
u32 spare_reg0; // _SPARE_REG0_0, 0x55c
u32 audio_sync_clk_dmic1; // _AUDIO_SYNC_CLK_DMIC1_0, 0x560
u32 audio_sync_clk_dmic2; // _AUDIO_SYNC_CLK_DMIC2_0, 0x564
u32 _0x568[2];
u32 plld2_ss_cfg; // _PLLD2_SS_CFG, 0x570
u32 plld2_ss_ctrl1; // _PLLD2_SS_CTRL1_0, 0x574
u32 plld2_ss_ctrl2; // _PLLD2_SS_CTRL2_0, 0x578
u32 _0x57c[5];
u32 plldp_base; // _PLLDP_BASE, 0x590
u32 plldp_misc; // _PLLDP_MISC, 0x594
u32 plldp_ss_cfg; // _PLLDP_SS_CFG, 0x598
u32 plldp_ss_ctrl1; // _PLLDP_SS_CTRL1_0, 0x59c
u32 plldp_ss_ctrl2; // _PLLDP_SS_CTRL2_0, 0x5a0
u32 pllc4_base; // _PLLC4_BASE_0, 0x5a4
u32 pllc4_misc; // _PLLC4_MISC_0, 0x5a8
u32 _0x5ac[6];
u32 clk_spare0; // _CLK_SPARE0_0, 0x5c4
u32 clk_spare1; // _CLK_SPARE1_0, 0x5c8
u32 gpu_isob_ctrl; // _GPU_ISOB_CTRL_0, 0x5cc
u32 pllc_misc2; // _PLLC_MISC_2_0, 0x5d0
u32 pllc_misc3; // _PLLC_MISC_3_0, 0x5d4
u32 plla_misc2; // _PLLA_MISC2_0, 0x5d8
u32 _0x5dc[2];
u32 pllc4_out; // _PLLC4_OUT_0, 0x5e4
u32 pllmb_base; // _PLLMB_BASE_0, 0x5e8
u32 pllmb_misc1; // _PLLMB_MISC1_0, 0x5ec
u32 pllx_misc4; // _PLLX_MISC_4_0, 0x5f0
u32 pllx_misc5; // _PLLX_MISC_5_0, 0x5f4
u32 _0x5f8[2];
u32 clk_source_xusb_core_host; // _CLK_SOURCE_XUSB_CORE_HOST_0, 0x600
u32 clk_source_xusb_falcon; // _CLK_SOURCE_XUSB_FALCON_0, 0x604
u32 clk_source_xusb_fs; // _CLK_SOURCE_XUSB_FS_0, 0x608
u32 clk_source_xusb_core_dev; // _CLK_SOURCE_XUSB_CORE_DEV_0, 0x60c
u32 clk_source_xusb_ss; // _CLK_SOURCE_XUSB_SS_0, 0x610
u32 clk_source_cilab; // _CLK_SOURCE_CILAB_0, 0x614
u32 clk_source_cilcd; // _CLK_SOURCE_CILCD_0, 0x618
u32 clk_source_cilef; // _CLK_SOURCE_CILEF_0, 0x61c
u32 clk_source_dsia_lp; // _CLK_SOURCE_DSIA_LP_0, 0x620
u32 clk_source_dsib_lp; // _CLK_SOURCE_DSIB_LP_0, 0x624
u32 clk_source_entropy; // _CLK_SOURCE_ENTROPY_0, 0x628
u32 clk_source_dvfs_ref; // _CLK_SOURCE_DVFS_REF_0, 0x62c
u32 clk_source_dvfs_soc; // _CLK_SOURCE_DVFS_SOC_0, 0x630
u32 _0x634[3];
u32 clk_source_emc_latency; // _CLK_SOURCE_EMC_LATENCY_0, 0x640
u32 clk_source_soc_therm; // _CLK_SOURCE_SOC_THERM_0, 0x644
u32 _0x648;
u32 clk_source_dmic1; // _CLK_SOURCE_DMIC1_0, 0x64c
u32 clk_source_dmic2; // _CLK_SOURCE_DMIC2_0, 0x650
u32 _0x654;
u32 clk_source_vi_sensor2; // _CLK_SOURCE_VI_SENSOR2_0, 0x658
u32 clk_source_i2c6; // _CLK_SOURCE_I2C6_0, 0x65c
u32 clk_source_mipibif; // _CLK_SOURCE_MIPIBIF_0, 0x660
u32 clk_source_emc_dll; // _CLK_SOURCE_EMC_DLL_0, 0x664
u32 _0x668;
u32 clk_source_uart_fst_mipi_cal; // _CLK_SOURCE_UART_FST_MIPI_CAL_0, 0x66c
u32 _0x670[2];
u32 clk_source_vic; // _CLK_SOURCE_VIC_0, 0x678
u32 pllp_outc; // _PLLP_OUTC_0, 0x67c
u32 pllp_misc1; // _PLLP_MISC1_0, 0x680
u32 _0x684[2];
u32 emc_div_clk_shaper_ctrl; // _EMC_DIV_CLK_SHAPER_CTRL_0, 0x68c
u32 emc_pllc_shaper_ctrl; // _EMC_PLLC_SHAPER_CTRL_0, 0x690
u32 clk_source_sdmmc_legacy_tm; // _CLK_SOURCE_SDMMC_LEGACY_TM_0, 0x694
u32 clk_source_nvdec; // _CLK_SOURCE_NVDEC_0, 0x698
u32 clk_source_nvjpg; // _CLK_SOURCE_NVJPG_0, 0x69c
u32 clk_source_nvenc; // _CLK_SOURCE_NVENC_0, 0x6a0
u32 plla1_base; // _PLLA1_BASE_0, 0x6a4
u32 plla1_misc0; // _PLLA1_MISC_0_0, 0x6a8
u32 plla1_misc1; // _PLLA1_MISC_1_0, 0x6ac
u32 plla1_misc2; // _PLLA1_MISC_2_0, 0x6b0
u32 plla1_misc3; // _PLLA1_MISC_3_0, 0x6b4
u32 audio_sync_clk_dmic3; // _AUDIO_SYNC_CLK_DMIC3_0, 0x6b8
u32 clk_source_dmic3; // _CLK_SOURCE_DMIC3_0, 0x6bc
u32 clk_source_ape; // _CLK_SOURCE_APE_0, 0x6c0
u32 clk_source_qspi; // _CLK_SOURCE_QSPI_0, 0x6c4
u32 clk_source_vi_i2c; // _CLK_SOURCE_VI_I2C_0, 0x6c8
u32 clk_source_usb2_hsic_trk; // _CLK_SOURCE_USB2_HSIC_TRK_0, 0x6cc
u32 clk_source_pex_sata_usb_rx_byp; // _CLK_SOURCE_PEX_SATA_USB_RX_BYP_0, 0x6d0
u32 clk_source_maud; // _CLK_SOURCE_MAUD_0, 0x6d4
u32 clk_source_tsecb; // _CLK_SOURCE_TSECB_0, 0x6d8
u32 clk_cpug_misc1; // _CLK_CPUG_MISC1_0, 0x6dc
u32 aclk_burst_policy; // _ACLK_BURST_POLICY_0, 0x6e0
u32 super_aclk_divider; // _SUPER_ACLK_DIVIDER_0, 0x6e4
u32 nvenc_super_clk_divider; // _NVENC_SUPER_CLK_DIVIDER_0, 0x6e8
u32 vi_super_clk_divider; // _VI_SUPER_CLK_DIVIDER_0, 0x6ec
u32 vic_super_clk_divider; // _VIC_SUPER_CLK_DIVIDER_0, 0x6f0
u32 nvdec_super_clk_divider; // _NVDEC_SUPER_CLK_DIVIDER_0, 0x6f4
u32 isp_super_clk_divider; // _ISP_SUPER_CLK_DIVIDER_0, 0x6f8
u32 ispb_super_clk_divider; // _ISPB_SUPER_CLK_DIVIDER_0, 0x6fc
u32 nvjpg_super_clk_divider; // _NVJPG_SUPER_CLK_DIVIDER_0, 0x700
u32 se_super_clk_divider; // _SE_SUPER_CLK_DIVIDER_0, 0x704
u32 tsec_super_clk_divider; // _TSEC_SUPER_CLK_DIVIDER_0, 0x708
u32 tsecb_super_clk_divider; // _TSECB_SUPER_CLK_DIVIDER_0, 0x70c
u32 clk_source_uartape; // _CLK_SOURCE_UARTAPE_0, 0x710
u32 clk_cpug_misc2; // _CLK_CPUG_MISC2_0, 0x714
u32 clk_source_dbgapb; // _CLK_SOURCE_DBGAPB_0, 0x718
u32 clk_ccplex_cc4_ret_clk_enb; // _CLK_CCPLEX_CC4_RET_CLK_ENB_0, 0x71c
u32 actmon_cpu_clk; // _ACTMON_CPU_CLK_0, 0x720
u32 clk_source_emc_safe; // _CLK_SOURCE_EMC_SAFE_0, 0x724
u32 sdmmc2_pllc4_out0_shaper_ctrl; // _SDMMC2_PLLC4_OUT0_SHAPER_CTRL_0, 0x728
u32 sdmmc2_pllc4_out1_shaper_ctrl; // _SDMMC2_PLLC4_OUT1_SHAPER_CTRL_0, 0x72c
u32 sdmmc2_pllc4_out2_shaper_ctrl; // _SDMMC2_PLLC4_OUT2_SHAPER_CTRL_0, 0x730
u32 sdmmc2_div_clk_shaper_ctrl; // _SDMMC2_DIV_CLK_SHAPER_CTRL_0, 0x734
u32 sdmmc4_pllc4_out0_shaper_ctrl; // _SDMMC4_PLLC4_OUT0_SHAPER_CTRL_0, 0x738
u32 sdmmc4_pllc4_out1_shaper_ctrl; // _SDMMC4_PLLC4_OUT1_SHAPER_CTRL_0, 0x73c
u32 sdmmc4_pllc4_out2_shaper_ctrl; // _SDMMC4_PLLC4_OUT2_SHAPER_CTRL_0, 0x740
u32 sdmmc4_div_clk_shaper_ctrl; // _SDMMC4_DIV_CLK_SHAPER_CTRL_0, 0x744
};
static_assert(std::is_standard_layout_v<Registers>);
static_assert(std::is_trivial_v<Registers>);
private:
static constexpr u32 clkRegOffsets[] = { 0x010, 0x014, 0x018, 0x360, 0x364, 0x280, 0x298 };
static constexpr u32 rstRegOffsets[] = { 0x004, 0x008, 0x00C, 0x358, 0x35C, 0x28C, 0x2A4 };
private:
volatile Registers *m_regs = nullptr;
// TODO friend
private:
vu32 *RegisterAt(u32 offset) const
{
return reinterpret_cast<vu32 *>(reinterpret_cast<uintptr_t>(m_regs) + offset);
}
public:
union Device {
struct {
u32 bank : 3;
u32 bitPos : 5;
u32 regOffset : 12;
u32 value : 3;
u32 : 0;
u32 divisor : 16;
};
u64 raw;
};
static_assert(std::is_standard_layout_v<Device>);
static_assert(std::is_trivial_v<Device>);
static_assert(sizeof(Device) == 8);
static constexpr Device uartA = {{
.bank = 5,
.bitPos = 6,
.regOffset = 0x178,
.value = 0,
.divisor = 0,
}};
static constexpr Device uartB = {{
.bank = 0,
.bitPos = 7,
.regOffset = 0x17C,
.value = 0,
.divisor = 0,
}};
static constexpr Device uartC = {{
.bank = 1,
.bitPos = 23,
.regOffset = 0x1A0,
.value = 0,
.divisor = 0,
}};
static constexpr Device uartD = {{
.bank = 2,
.bitPos = 1,
.regOffset = 0x1C0,
.value = 0,
.divisor = 0,
}};
static constexpr Device i2c1 = {{
.bank = 0,
.bitPos = 12,
.regOffset = 0x124,
.value = 6,
.divisor = 0,
}};
static constexpr Device i2c5 = {{
.bank = 1,
.bitPos = 15,
.regOffset = 0x128,
.value = 6,
.divisor = 0,
}};
static constexpr Device tzram = {{
.bank = 3,
.bitPos = 30,
}};
static constexpr Device se = {{
.bank = 3,
.bitPos = 31,
.regOffset = 0x42C,
.value = 0,
.divisor = 0,
}};
static constexpr Device host1x = {{
.bank = 0,
.bitPos = 28,
.regOffset = 0x180,
.value = 4,
.divisor = 3,
}};
static constexpr Device tsec = {{
.bank = 2,
.bitPos = 19,
.regOffset = 0x1F4,
.value = 0,
.divisor = 2,
}};
static constexpr Device sorSafe = {{
.bank = 6,
.bitPos = 30,
}};
static constexpr Device sor0 = {{
.bank = 5,
.bitPos = 22,
}};
static constexpr Device sor1 = {{
.bank = 5,
.bitPos = 30,
.regOffset = 0x410,
.value = 0,
.divisor = 2,
}};
static constexpr Device kfuse = {{
.bank = 1,
.bitPos = 8,
}};
static constexpr Device clDvfs = {{
.bank = 4,
.bitPos = 27,
}};
static constexpr Device bpmp = {{
.bank = 0,
.bitPos = 1,
}};
static constexpr Device actmon = {{
.bank = 3,
.bitPos = 23,
.regOffset = 0x3E8,
.value = 6,
.divisor = 0,
}};
static constexpr Device coresight = {{
.bank = 2,
.bitPos = 9,
.regOffset = 0x1D4,
.value = 0,
.divisor = 4,
}};
public:
void EnableClock(Device dev) const
{
// Configure default PLL and divisor
if (dev.regOffset != 0) {
*RegisterAt(dev.regOffset) = dev.value << 29 | dev.divisor;
}
// Enable the clock
*RegisterAt(clkRegOffsets[dev.bank]) |= BIT(dev.bitPos);
}
void DisableClock(Device dev) const
{
*RegisterAt(clkRegOffsets[dev.bank]) &= ~BIT(dev.bitPos);
}
void EnableReset(Device dev) const
{
*RegisterAt(rstRegOffsets[dev.bank]) |= BIT(dev.bitPos);
}
void DisableReset(Device dev) const
{
*RegisterAt(rstRegOffsets[dev.bank]) &= ~BIT(dev.bitPos);
}
void Enable(Device dev) const
{
EnableClock(dev);
DisableReset(dev);
}
void Disable(Device dev) const
{
EnableReset(dev);
DisableClock(dev);
}
void Reboot(Device dev) const {
Disable(dev);
// KFUSE needs a workaround
/*if (dev.raw == kfuse.raw) {
EnableClock(dev);
// Wait 100us
DisableReset(dev);
// Wait 200us
}*/
Enable(dev);
}
void SetFuseRegsEnabled(bool enabled) const
{
u32 mask = enabled ? BIT(28) : 0;
m_regs->misc_clk_enb = (m_regs->misc_clk_enb & ~BIT(28)) | mask;
}
};
}

View File

@@ -0,0 +1,207 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../../../defines.hpp"
namespace ams::hvisor::drivers::tegra::t210 {
class Gpio final {
private:
static constexpr size_t numBanks = 8;
static constexpr size_t portsPerBank = 4;
struct Bank {
u32 cnf[portsPerBank];
u32 oe[portsPerBank];
u32 out[portsPerBank];
u32 in[portsPerBank];
u32 int_sta[portsPerBank];
u32 int_enb[portsPerBank];
u32 int_lvl[portsPerBank];
u32 int_clr[portsPerBank];
u32 msk_cnf[portsPerBank];
u32 msk_oe[portsPerBank];
u32 msk_out[portsPerBank];
u32 db_ctrl_p[portsPerBank];
u32 msk_int_sta[portsPerBank];
u32 msk_int_enb[portsPerBank];
u32 msk_int_lvl[portsPerBank];
u32 db_cnt_p[portsPerBank];
};
struct Registers {
Bank bank[numBanks];
};
static_assert(std::is_standard_layout_v<Registers>);
static_assert(std::is_trivial_v<Registers>);
public:
enum Port {
PORT_A = 0,
PORT_B = 1,
PORT_C = 2,
PORT_D = 3,
PORT_E = 4,
PORT_F = 5,
PORT_G = 6,
PORT_H = 7,
PORT_I = 8,
PORT_J = 9,
PORT_K = 10,
PORT_L = 11,
PORT_M = 12,
PORT_N = 13,
PORT_O = 14,
PORT_P = 15,
PORT_Q = 16,
PORT_R = 17,
PORT_S = 18,
PORT_T = 19,
PORT_U = 20,
PORT_V = 21,
PORT_W = 22,
PORT_X = 23,
PORT_Y = 24,
PORT_Z = 25,
PORT_AA = 26,
PORT_BB = 27,
PORT_CC = 28,
PORT_DD = 29,
PORT_EE = 30,
PORT_FF = 31,
};
enum class Mode {
Sfio = 0,
Gpio = 1,
};
enum class Direction {
Tristate = 0, // Input
Driven = 1, // Output
Input = Tristate,
Output = Driven,
};
enum class Level {
Low = 0,
High = 1,
};
private:
// For msk_* fields
static constexpr u32 MakeMaskedWriteValue(u32 pos, bool value)
{
return BIT(8 + pos) | ((value ? 1 : 0) << pos);
}
static constexpr u32 MakeMaskedWriteValueContiguous(u32 pos, size_t n, bool value)
{
u32 msk = MASK2(pos + n - 1, pos);
return (msk << 8) | (value ? msk : 0);
}
private:
struct Pin {
Port port;
u8 pos;
};
static_assert(std::is_standard_layout_v<Pin>);
static_assert(std::is_trivial_v<Pin>);
static_assert(sizeof(Pin) <= 8);
public:
static constexpr Pin uart3Tx = {PORT_D, 1};
static constexpr Pin uart3Rx = {PORT_D, 2};
static constexpr Pin uart3Rts = {PORT_D, 3};
static constexpr Pin uart3Cts = {PORT_D, 4};
static constexpr Pin uart2Tx = {PORT_G, 0};
static constexpr Pin uart2Rx = {PORT_G, 1};
static constexpr Pin uart2Rts = {PORT_G, 2};
static constexpr Pin uart2Cts = {PORT_G, 3};
static constexpr Pin uart4Tx = {PORT_I, 4};
static constexpr Pin uart4Rx = {PORT_I, 5};
static constexpr Pin uart4Rts = {PORT_I, 6};
static constexpr Pin uart4Cts = {PORT_I, 7};
static constexpr Pin uart1Tx = {PORT_U, 0};
static constexpr Pin uart1Rx = {PORT_U, 1};
static constexpr Pin uart1Rts = {PORT_U, 2};
static constexpr Pin uart1Cts = {PORT_U, 3};
static constexpr Pin volUp = {PORT_X, 6};
static constexpr Pin volDown = {PORT_X, 7};
static constexpr Pin microSdCardDetect = {PORT_Z, 1};
static constexpr Pin microSdWriteProtect = {PORT_Z, 4};
static constexpr Pin microSdSupplyEnable = {PORT_E, 4};
static constexpr Pin lcdBlP5v = {PORT_I, 0};
static constexpr Pin lcdBlN5v = {PORT_I, 1};
static constexpr Pin lcdBlPwm = {PORT_V, 0};
static constexpr Pin lcdBlEn = {PORT_V, 1};
static constexpr Pin lcdBlRst = {PORT_V, 2};
private:
volatile Registers *m_regs = nullptr;
public:
void SetMode(Pin pin, Mode mode) const
{
m_regs->bank[pin.port / portsPerBank].msk_cnf[pin.port % portsPerBank] = MakeMaskedWriteValue(pin.pos, mode == Mode::Gpio);
}
void SetModeContiguous(Pin pin, size_t n, Mode mode) const
{
m_regs->bank[pin.port / portsPerBank].msk_cnf[pin.port % portsPerBank] = MakeMaskedWriteValueContiguous(pin.pos, n, mode == Mode::Gpio);
}
// Only valid for GPIO (not SFIO)
void SetDirection(Pin pin, Direction direction) const
{
m_regs->bank[pin.port / portsPerBank].msk_oe[pin.port % portsPerBank] = MakeMaskedWriteValue(pin.pos, direction == Direction::Output);
}
// Only valid for GPIO (not SFIO)
void SetDirectionContiguous(Pin pin, size_t n, Direction direction) const
{
m_regs->bank[pin.port / portsPerBank].msk_oe[pin.port % portsPerBank] = MakeMaskedWriteValueContiguous(pin.pos, n, direction == Direction::Output);
}
// Only valid for GPIO (not SFIO)
void Write(Pin pin, Level level) const
{
m_regs->bank[pin.port / portsPerBank].msk_out[pin.port % portsPerBank] = MakeMaskedWriteValue(pin.pos, level == Level::High);
}
// Only valid for GPIO (not SFIO)
Level Read(Pin pin) const
{
return static_cast<Level>((m_regs->bank[pin.port / portsPerBank].in[pin.port % portsPerBank] >> pin.pos) & 1);
}
void ConfigureUartPins()
{
constexpr Pin uartPins[] = {uart1Tx, uart2Tx, uart3Tx, uart4Tx};
// Set SFIO to all the 4 contiguous pins (tx, rx, rts, cts)
for (Pin pin : uartPins) {
SetModeContiguous(pin, 4, Mode::Sfio);
}
}
};
}

View File

@@ -0,0 +1,248 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../../../defines.hpp"
namespace ams::hvisor::drivers::tegra::t210 {
class Pinmux final {
private:
struct Registers {
u32 sdmmc1_clk;
u32 sdmmc1_cmd;
u32 sdmmc1_dat3;
u32 sdmmc1_dat2;
u32 sdmmc1_dat1;
u32 sdmmc1_dat0;
u32 _r18;
u32 sdmmc3_clk;
u32 sdmmc3_cmd;
u32 sdmmc3_dat0;
u32 sdmmc3_dat1;
u32 sdmmc3_dat2;
u32 sdmmc3_dat3;
u32 _r34;
u32 pex_l0_rst_n;
u32 pex_l0_clkreq_n;
u32 pex_wake_n;
u32 pex_l1_rst_n;
u32 pex_l1_clkreq_n;
u32 sata_led_active;
u32 spi1_mosi;
u32 spi1_miso;
u32 spi1_sck;
u32 spi1_cs0;
u32 spi1_cs1;
u32 spi2_mosi;
u32 spi2_miso;
u32 spi2_sck;
u32 spi2_cs0;
u32 spi2_cs1;
u32 spi4_mosi;
u32 spi4_miso;
u32 spi4_sck;
u32 spi4_cs0;
u32 qspi_sck;
u32 qspi_cs_n;
u32 qspi_io0;
u32 qspi_io1;
u32 qspi_io2;
u32 qspi_io3;
u32 _ra0;
u32 dmic1_clk;
u32 dmic1_dat;
u32 dmic2_clk;
u32 dmic2_dat;
u32 dmic3_clk;
u32 dmic3_dat;
u32 gen1_i2c_scl;
u32 gen1_i2c_sda;
u32 gen2_i2c_scl;
u32 gen2_i2c_sda;
u32 gen3_i2c_scl;
u32 gen3_i2c_sda;
u32 cam_i2c_scl;
u32 cam_i2c_sda;
u32 pwr_i2c_scl;
u32 pwr_i2c_sda;
u32 uart1_tx;
u32 uart1_rx;
u32 uart1_rts;
u32 uart1_cts;
u32 uart2_tx;
u32 uart2_rx;
u32 uart2_rts;
u32 uart2_cts;
u32 uart3_tx;
u32 uart3_rx;
u32 uart3_rts;
u32 uart3_cts;
u32 uart4_tx;
u32 uart4_rx;
u32 uart4_rts;
u32 uart4_cts;
u32 dap1_fs;
u32 dap1_din;
u32 dap1_dout;
u32 dap1_sclk;
u32 dap2_fs;
u32 dap2_din;
u32 dap2_dout;
u32 dap2_sclk;
u32 dap4_fs;
u32 dap4_din;
u32 dap4_dout;
u32 dap4_sclk;
u32 cam1_mclk;
u32 cam2_mclk;
u32 jtag_rtck;
u32 clk_32k_in;
u32 clk_32k_out;
u32 batt_bcl;
u32 clk_req;
u32 cpu_pwr_req;
u32 pwr_int_n;
u32 shutdown;
u32 core_pwr_req;
u32 aud_mclk;
u32 dvfs_pwm;
u32 dvfs_clk;
u32 gpio_x1_aud;
u32 gpio_x3_aud;
u32 pcc7;
u32 hdmi_cec;
u32 hdmi_int_dp_hpd;
u32 spdif_out;
u32 spdif_in;
u32 usb_vbus_en0;
u32 usb_vbus_en1;
u32 dp_hpd0;
u32 wifi_en;
u32 wifi_rst;
u32 wifi_wake_ap;
u32 ap_wake_bt;
u32 bt_rst;
u32 bt_wake_ap;
u32 ap_wake_nfc;
u32 nfc_en;
u32 nfc_int;
u32 gps_en;
u32 gps_rst;
u32 cam_rst;
u32 cam_af_en;
u32 cam_flash_en;
u32 cam1_pwdn;
u32 cam2_pwdn;
u32 cam1_strobe;
u32 lcd_te;
u32 lcd_bl_pwm;
u32 lcd_bl_en;
u32 lcd_rst;
u32 lcd_gpio1;
u32 lcd_gpio2;
u32 ap_ready;
u32 touch_rst;
u32 touch_clk;
u32 modem_wake_ap;
u32 touch_int;
u32 motion_int;
u32 als_prox_int;
u32 temp_alert;
u32 button_power_on;
u32 button_vol_up;
u32 button_vol_down;
u32 button_slide_sw;
u32 button_home;
u32 pa6;
u32 pe6;
u32 pe7;
u32 ph6;
u32 pk0;
u32 pk1;
u32 pk2;
u32 pk3;
u32 pk4;
u32 pk5;
u32 pk6;
u32 pk7;
u32 pl0;
u32 pl1;
u32 pz0;
u32 pz1;
u32 pz2;
u32 pz3;
u32 pz4;
u32 pz5;
};
static_assert(std::is_standard_layout_v<Registers>);
static_assert(std::is_trivial_v<Registers>);
enum Flags : u32 {
PREEMP_ENABLED = BIT(15),
DRIVE_1X = 0 << 13,
DRIVE_2X = 1 << 13,
DRIVE_3X = 2 << 13,
DRIVE_4X = 3 << 13,
SCHMT_ENABLED = BIT(12),
OD_ENABLED = BIT(11),
IO_HV_ENABLED = BIT(10),
HSM_ENABLED = BIT(9),
LPDR_ENABLED = BIT(8),
LOCKED = BIT(7),
INPUT = BIT(6),
PARKED = BIT(5),
TRISTATE = BIT(4),
PULL_NONE = 0 << 2,
PULL_DOWN = 1 << 2,
PULL_UP = 2 << 2,
SELECT_FUNCTION0 = 0 << 0,
SELECT_FUNCTION1 = 1 << 0,
SELECT_FUNCTION2 = 2 << 0,
SELECT_FUNCTION3 = 3 << 0,
};
private:
// TODO friend
volatile Registers *m_regs = nullptr;
public:
void ConfigureUartPins() const
{
m_regs->uart1_tx = 0 | 0 | PULL_NONE | SELECT_FUNCTION0;
m_regs->uart1_rx = INPUT | TRISTATE | PULL_UP | SELECT_FUNCTION0;
m_regs->uart1_rts = 0 | 0 | PULL_NONE | SELECT_FUNCTION0;
m_regs->uart1_cts = INPUT | TRISTATE | PULL_DOWN | SELECT_FUNCTION0;
m_regs->uart2_tx = 0 | 0 | PULL_NONE | SELECT_FUNCTION0;
m_regs->uart2_rx = INPUT | TRISTATE | PULL_NONE | SELECT_FUNCTION0;
m_regs->uart2_rts = 0 | 0 | PULL_DOWN | SELECT_FUNCTION0;
m_regs->uart2_cts = INPUT | TRISTATE | PULL_NONE | SELECT_FUNCTION0;
m_regs->uart3_tx = 0 | 0 | PULL_NONE | SELECT_FUNCTION0;
m_regs->uart3_rx = INPUT | TRISTATE | PULL_NONE | SELECT_FUNCTION0;
m_regs->uart3_rts = 0 | 0 | PULL_DOWN | SELECT_FUNCTION0;
m_regs->uart3_cts = INPUT | TRISTATE | PULL_NONE | SELECT_FUNCTION0;
m_regs->uart4_tx = 0 | 0 | PULL_DOWN | SELECT_FUNCTION0;
m_regs->uart4_rx = INPUT | TRISTATE | PULL_DOWN | SELECT_FUNCTION0;
m_regs->uart4_cts = 0 | 0 | PULL_DOWN | SELECT_FUNCTION0;
m_regs->uart4_rts = INPUT | TRISTATE | PULL_DOWN | SELECT_FUNCTION0;
}
};
}

View File

@@ -0,0 +1,375 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#include "asm_macros.s"
/* Some macros taken from https://github.com/ARM-software/arm-trusted-firmware/blob/master/include/common/aarch64/asm_macros.S */
/*
* Copyright (c) 2013-2017, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* Declare the exception vector table, enforcing it is aligned on a
* 2KB boundary, as required by the ARMv8 architecture.
* Use zero bytes as the fill value to be stored in the padding bytes
* so that it inserts illegal AArch64 instructions. This increases
* security, robustness and potentially facilitates debugging.
*/
.macro vector_base label, section_name=.vectors
.section \section_name, "ax"
.align 11, 0
\label:
.endm
/*
* Create an entry in the exception vector table, enforcing it is
* aligned on a 128-byte boundary, as required by the ARMv8 architecture.
* Use zero bytes as the fill value to be stored in the padding bytes
* so that it inserts illegal AArch64 instructions. This increases
* security, robustness and potentially facilitates debugging.
*/
.macro vector_entry label, section_name=.vectors
.cfi_sections .debug_frame
.section \section_name, "ax"
.align 7, 0
.type \label, %function
.func \label
.cfi_startproc
\label:
.endm
/*
* This macro verifies that the given vector doesnt exceed the
* architectural limit of 32 instructions. This is meant to be placed
* immediately after the last instruction in the vector. It takes the
* vector entry as the parameter
*/
.macro check_vector_size since
.endfunc
.cfi_endproc
.if (. - \since) > (32 * 4)
.error "Vector exceeds 32 instructions"
.endif
.endm
.macro SAVE_MOST_REGISTERS
sub sp, sp, #EXCEP_STACK_FRAME_SIZE
stp x28, x29, [sp, #-0x20]
stp x30, xzr, [sp, #-0x10]
mrs x28, far_el2
mrs x29, cntpct_el0
bl _saveMostRegisters
.endm
.macro PIVOT_STACK_FOR_CRASH
// Note: replace sp_el1 with crashing sp (for convenience)
// (sp_el2 is not accessible at el2)
msr spsel, #0
stp x0, x1, [sp, #-0x10]
mov x0, sp
msr spsel, #1
mov x1, sp
mov sp, x0
msr sp_el1, x1
ldp x0, x1, [sp, #-0x10]
.endm
#define EXCEPTION_TYPE_HOST 0
#define EXCEPTION_TYPE_GUEST 1
#define EXCEPTION_TYPE_HOST_CRASH 2
.macro EXCEPTION_HANDLER_START name, type
vector_entry \name
.if \type == EXCEPTION_TYPE_HOST_CRASH
PIVOT_STACK_FOR_CRASH
.endif
SAVE_MOST_REGISTERS
mov x0, sp
.if \type == EXCEPTION_TYPE_GUEST
ldp x18, x19, [sp, #EXCEP_STACK_FRAME_SIZE]
msr sp_el0, x19
prfm pstl1keep, [x18]
mov w1, #1
.else
mov w1, #0
.endif
// ams::hvisor::ExceptionEntryPostprocess(ams::hvisor::ExceptionStackFrame*, bool)
bl _ZN3ams6hvisor25ExceptionEntryPostprocessEPNS0_19ExceptionStackFrameEb
.endm
.macro EXCEPTION_HANDLER_END name, type
.if \type != EXCEPTION_TYPE_HOST_CRASH
mov x0, sp
// ams::hvisor::ExceptionReturnPreprocess(ams::hvisor::ExceptionStackFrame*)
bl _ZN3ams6hvisor25ExceptionReturnPreprocessEPNS0_19ExceptionStackFrameE
b _restoreAllRegisters
.else
b .
.endif
check_vector_size \name
.endm
.macro UNKNOWN_EXCEPTION name
vector_entry \name
bl _unknownException
check_vector_size \name
.endm
/* Actual Vectors for Thermosphere. */
.global g_thermosphereVectors
vector_base g_thermosphereVectors
/* Current EL, SP0 */
vector_entry _synchSp0
// Safecpy
cbz x18, _handleSafecpy
// Used when we enable the MMU
msr elr_el2, x18
// Note: non-broadcasting TLB maintenance op
tlbi alle2
dsb ish
isb
eret
_handleSafecpy:
// Set x16 to 1
mov x16, #1
eret
check_vector_size _synchSp0
_unknownException:
PIVOT_STACK_FOR_CRASH
mov x0, x30
adr x1, g_thermosphereVectors + 4
sub x0, x0, x1
// ams::hvisor::HandleUnknownException(unsigned int)
bl _ZN3ams6hvisor22HandleUnknownExceptionEj
b .
UNKNOWN_EXCEPTION _irqSp0
/* To save space, insert in an unused vector segment. */
_saveMostRegisters:
stp x0, x1, [sp, #0x00]
stp x2, x3, [sp, #0x10]
stp x4, x5, [sp, #0x20]
stp x6, x7, [sp, #0x30]
stp x8, x9, [sp, #0x40]
stp x10, x11, [sp, #0x50]
stp x12, x13, [sp, #0x60]
stp x14, x15, [sp, #0x70]
stp x16, x17, [sp, #0x80]
stp x18, x19, [sp, #0x90]
stp x20, x21, [sp, #0xA0]
stp x22, x23, [sp, #0xB0]
stp x24, x25, [sp, #0xC0]
stp x26, x27, [sp, #0xD0]
mrs x20, sp_el1
mrs x21, sp_el0
mrs x22, elr_el2
mrs x23, spsr_el2
mrs x24, esr_el2
mov x25, x28 // far_el2
mov x26, x29 // cntpct_el0
// See SAVE_MOST_REGISTERS macro
ldp x28, x29, [sp, #-0x20]
ldp x19, xzr, [sp, #-0x10]
stp x28, x29, [sp, #0xE0]
stp x19, x20, [sp, #0xF0]
stp x21, x22, [sp, #0x100]
stp x23, x24, [sp, #0x110]
stp x25, x26, [sp, #0x120]
ret
UNKNOWN_EXCEPTION _fiqSp0
/* To save space, insert in an unused vector segment. */
// Accessed by start.s
.global _restoreAllRegisters
.type _restoreAllRegisters, %function
_restoreAllRegisters:
ldp x30, x20, [sp, #0xF0]
ldp x21, x22, [sp, #0x100]
ldp x23, xzr, [sp, #0x110]
msr sp_el1, x20
msr sp_el0, x21
msr elr_el2, x22
msr spsr_el2, x23
ldp x0, x1, [sp, #0x00]
ldp x2, x3, [sp, #0x10]
ldp x4, x5, [sp, #0x20]
ldp x6, x7, [sp, #0x30]
ldp x8, x9, [sp, #0x40]
ldp x10, x11, [sp, #0x50]
ldp x12, x13, [sp, #0x60]
ldp x14, x15, [sp, #0x70]
ldp x16, x17, [sp, #0x80]
ldp x18, x19, [sp, #0x90]
ldp x20, x21, [sp, #0xA0]
ldp x22, x23, [sp, #0xB0]
ldp x24, x25, [sp, #0xC0]
ldp x26, x27, [sp, #0xD0]
ldp x28, x29, [sp, #0xE0]
add sp, sp, #EXCEP_STACK_FRAME_SIZE
eret
UNKNOWN_EXCEPTION _serrorSp0
// To save space, insert in an unused vector segment.
// ams::hvisor::traps::CallSmc0(ams::hvisor::ExceptionStackFrame*):
.global _ZN3ams6hvisor5traps8CallSmc0EPNS0_19ExceptionStackFrameE
.type _ZN3ams6hvisor5traps8CallSmc0EPNS0_19ExceptionStackFrameE, %function
.func _ZN3ams6hvisor5traps8CallSmc0EPNS0_19ExceptionStackFrameE
.cfi_startproc
.cfi_sections .debug_frame
// ams::hvisor::callSmcTemplate[]
.global _ZN3ams6hvisor5traps15callSmcTemplateE
_ZN3ams6hvisor5traps15callSmcTemplateE:
_ZN3ams6hvisor5traps8CallSmc0EPNS0_19ExceptionStackFrameE:
stp x19, x20, [sp, #-0x10]!
mov x19, x0
ldp x0, x1, [x19, #0x00]
ldp x2, x3, [x19, #0x10]
ldp x4, x5, [x19, #0x20]
ldp x6, x7, [x19, #0x30]
_callSmcTemplateSmcInstruction:
smc #0
// Note that NN's secure monitor can return results in x4-x7, this differs from Arm's spec.
stp x0, x1, [x19, #0x00]
stp x2, x3, [x19, #0x10]
stp x4, x5, [x19, #0x20]
stp x6, x7, [x19, #0x30]
ldp x19, x20, [sp], #0x10
ret
_callSmcTemplateEnd:
.cfi_endproc
.endfunc
// ams::hvisor::traps::callSmcTemplateInstructionOffset
.global _ZN3ams6hvisor5traps32callSmcTemplateInstructionOffsetE
_ZN3ams6hvisor5traps32callSmcTemplateInstructionOffsetE:
.word _callSmcTemplateSmcInstruction - _ZN3ams6hvisor5traps15callSmcTemplateE
// ams::hvisor::traps::callSmcTemplateSize
.global _ZN3ams6hvisor5traps19callSmcTemplateSizeE
_ZN3ams6hvisor5traps19callSmcTemplateSizeE:
.word _callSmcTemplateEnd - _ZN3ams6hvisor5traps15callSmcTemplateE
// ams::hvisor::traps::CallSmc1(ams::hvisor::ExceptionStackFrame*):
.global _ZN3ams6hvisor5traps8CallSmc1EPNS0_19ExceptionStackFrameE
.type _ZN3ams6hvisor5traps8CallSmc1EPNS0_19ExceptionStackFrameE, %function
.func _ZN3ams6hvisor5traps8CallSmc1EPNS0_19ExceptionStackFrameE
.cfi_startproc
.cfi_sections .debug_frame
_ZN3ams6hvisor5traps8CallSmc1EPNS0_19ExceptionStackFrameE:
stp x19, x20, [sp, #-0x10]!
mov x19, x0
ldp x0, x1, [x19, #0x00]
ldp x2, x3, [x19, #0x10]
ldp x4, x5, [x19, #0x20]
ldp x6, x7, [x19, #0x30]
smc #1
// Note that NN's secure monitor can return results in x4-x7, this differs from Arm's spec.
stp x0, x1, [x19, #0x00]
stp x2, x3, [x19, #0x10]
stp x4, x5, [x19, #0x20]
stp x6, x7, [x19, #0x30]
ldp x19, x20, [sp], #0x10
ret
.cfi_endproc
.endfunc
/* Current EL, SPx */
EXCEPTION_HANDLER_START _synchSpx, EXCEPTION_TYPE_HOST
mov x0, sp
// ams::hvisor::HandleSameElSyncException(ams::hvisor::ExceptionStackFrame*):
bl _ZN3ams6hvisor25HandleSameElSyncExceptionEPNS0_19ExceptionStackFrameE
EXCEPTION_HANDLER_END _synchSpx
EXCEPTION_HANDLER_START _irqSpx, EXCEPTION_TYPE_HOST
mov x0, sp
mov w1, #0
mov w2, #0
// ams::hvisor::IrqManager::HandleInterrupt(ams::hvisor::ExceptionStackFrame*):
bl _ZN3ams6hvisor10IrqManager15HandleInterruptEPNS0_19ExceptionStackFrameE
EXCEPTION_HANDLER_END _irqSpx, EXCEPTION_TYPE_HOST
UNKNOWN_EXCEPTION _fiqSpx
UNKNOWN_EXCEPTION _serrorSpx
/* Lower EL, A64 */
EXCEPTION_HANDLER_START _synchA64, EXCEPTION_TYPE_GUEST
mov x0, sp
// ams::hvisor::HandleLowerElSyncException(ams::hvisor::ExceptionStackFrame*)
bl _ZN3ams6hvisor26HandleLowerElSyncExceptionEPNS0_19ExceptionStackFrameE
EXCEPTION_HANDLER_END _synchA64, EXCEPTION_TYPE_GUEST
EXCEPTION_HANDLER_START _irqA64, EXCEPTION_TYPE_GUEST
mov x0, sp
mov w1, #1
mov w2, #0
// ams::hvisor::IrqManager::HandleInterrupt(ams::hvisor::ExceptionStackFrame*):
bl _ZN3ams6hvisor10IrqManager15HandleInterruptEPNS0_19ExceptionStackFrameE
EXCEPTION_HANDLER_END _irqA64, EXCEPTION_TYPE_GUEST
UNKNOWN_EXCEPTION _fiqA64
UNKNOWN_EXCEPTION _serrorA64
/* Lower EL, A32 */
EXCEPTION_HANDLER_START _synchA32, EXCEPTION_TYPE_GUEST
mov x0, sp
// ams::hvisor::HandleLowerElSyncException(ams::hvisor::ExceptionStackFrame*)
bl _ZN3ams6hvisor26HandleLowerElSyncExceptionEPNS0_19ExceptionStackFrameE
EXCEPTION_HANDLER_END _synchA32, EXCEPTION_TYPE_GUEST
EXCEPTION_HANDLER_START _irqA32, EXCEPTION_TYPE_GUEST
mov x0, sp
mov w1, #1
mov w2, #1
// ams::hvisor::IrqManager::HandleInterrupt(ams::hvisor::ExceptionStackFrame*):
bl _ZN3ams6hvisor10IrqManager15HandleInterruptEPNS0_19ExceptionStackFrameE
EXCEPTION_HANDLER_END _irqA32, EXCEPTION_TYPE_GUEST
UNKNOWN_EXCEPTION _fiqA32
UNKNOWN_EXCEPTION _serrorA32

View File

@@ -1,108 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <stddef.h>
#include "exceptions.h"
#include "lib/printk.h"
/**
* Simple debug function that prints all of our saved registers.
*/
static void print_registers(struct guest_state *regs)
{
// print x0-29
for(int i = 0; i < 30; i += 2) {
printk("x%d:\t0x%p\t", i, regs->x[i]);
printk("x%d:\t0x%p\n", i + 1, regs->x[i + 1]);
}
// print x30; don't bother with x31 (SP), as it's used by the stack that's
// storing this stuff; we really care about the saved SP anyways
printk("x30:\t0x%p\n", regs->x[30]);
// Special registers.
printk("pc:\t0x%p\tcpsr:\t0x%p\n", regs->pc, regs->cpsr);
printk("sp_el1:\t0x%p\tsp_el0:\t0x%p\n", regs->sp_el1, regs->sp_el0);
printk("elr_el1:0x%p\tspsr_el1:0x%p\n", regs->elr_el1, regs->spsr_el1);
// Note that we don't print ESR_EL2, as this isn't really part of the saved state.
}
/**
* Placeholder function that triggers whenever a vector happens we're not
* expecting. Currently prints out some debug information.
*/
void unhandled_vector(struct guest_state *regs)
{
printk("\nAn unexpected vector happened!\n");
print_registers(regs);
printk("\n\n");
}
/**
* Handles an HVC call.
*/
static void handle_hvc(struct guest_state *regs, int call_number)
{
switch(call_number) {
default:
printk("Got a HVC call from 64-bit code.\n");
printk("Calling instruction was: hvc %d\n\n", call_number);
printk("Calling context (you can use these regs as hypercall args!):\n");
print_registers(regs);
printk("\n\n");
break;
}
}
/**
* Placeholder function that triggers whenever a user event triggers a
* synchronous interrupt. Currently, we really only care about 'hvc',
* so that's all we're going to handle here.
*/
void handle_hypercall(struct guest_state *regs)
{
// This is demonstration code.
// In the future, you'd stick your hypercall table here.
switch (regs->esr_el2.ec) {
case HSR_EC_HVC64: {
// Read the hypercall number.
int hvc_nr = regs->esr_el2.iss & 0xFFFF;
// ... and handle the hypercall.
handle_hvc(regs, hvc_nr);
break;
}
default:
printk("Unexpected hypercall! ESR=%p\n", regs->esr_el2.bits);
print_registers(regs);
printk("\n\n");
break;
}
}

View File

@@ -1,185 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef __EXCEPTION_H__
#define __EXCEPTION_H__
/**
* Borrowed fom Xen (not copyrightable as these are facts).
* Description of the EL2 exception syndrome register.
*/
#define HSR_EC_UNKNOWN 0x00
#define HSR_EC_WFI_WFE 0x01
#define HSR_EC_CP15_32 0x03
#define HSR_EC_CP15_64 0x04
#define HSR_EC_CP14_32 0x05 /* Trapped MCR or MRC access to CP14 */
#define HSR_EC_CP14_DBG 0x06 /* Trapped LDC/STC access to CP14 (only for debug registers) */
#define HSR_EC_CP 0x07 /* HCPTR-trapped access to CP0-CP13 */
#define HSR_EC_CP10 0x08
#define HSR_EC_JAZELLE 0x09
#define HSR_EC_BXJ 0x0a
#define HSR_EC_CP14_64 0x0c
#define HSR_EC_SVC32 0x11
#define HSR_EC_HVC32 0x12
#define HSR_EC_SMC32 0x13
#define HSR_EC_HVC64 0x16
#define HSR_EC_SMC64 0x17
#define HSR_EC_SYSREG 0x18
#define HSR_EC_INSTR_ABORT_LOWER_EL 0x20
#define HSR_EC_INSTR_ABORT_CURR_EL 0x21
#define HSR_EC_DATA_ABORT_LOWER_EL 0x24
#define HSR_EC_DATA_ABORT_CURR_EL 0x25
#define HSR_EC_BRK 0x3c
/**
* Borrowed fom Xen (not copyrightable as these are facts).
* Description of the EL2 exception syndrome register.
*/
union esr {
uint32_t bits;
struct {
unsigned long iss:25; /* Instruction Specific Syndrome */
unsigned long len:1; /* Instruction length */
unsigned long ec:6; /* Exception Class */
};
/* Common to all conditional exception classes (0x0N, except 0x00). */
struct hsr_cond {
unsigned long iss:20; /* Instruction Specific Syndrome */
unsigned long cc:4; /* Condition Code */
unsigned long ccvalid:1;/* CC Valid */
unsigned long len:1; /* Instruction length */
unsigned long ec:6; /* Exception Class */
} cond;
struct hsr_wfi_wfe {
unsigned long ti:1; /* Trapped instruction */
unsigned long sbzp:19;
unsigned long cc:4; /* Condition Code */
unsigned long ccvalid:1;/* CC Valid */
unsigned long len:1; /* Instruction length */
unsigned long ec:6; /* Exception Class */
} wfi_wfe;
/* reg, reg0, reg1 are 4 bits on AArch32, the fifth bit is sbzp. */
struct hsr_cp32 {
unsigned long read:1; /* Direction */
unsigned long crm:4; /* CRm */
unsigned long reg:5; /* Rt */
unsigned long crn:4; /* CRn */
unsigned long op1:3; /* Op1 */
unsigned long op2:3; /* Op2 */
unsigned long cc:4; /* Condition Code */
unsigned long ccvalid:1;/* CC Valid */
unsigned long len:1; /* Instruction length */
unsigned long ec:6; /* Exception Class */
} cp32; /* HSR_EC_CP15_32, CP14_32, CP10 */
struct hsr_cp64 {
unsigned long read:1; /* Direction */
unsigned long crm:4; /* CRm */
unsigned long reg1:5; /* Rt1 */
unsigned long reg2:5; /* Rt2 */
unsigned long sbzp2:1;
unsigned long op1:4; /* Op1 */
unsigned long cc:4; /* Condition Code */
unsigned long ccvalid:1;/* CC Valid */
unsigned long len:1; /* Instruction length */
unsigned long ec:6; /* Exception Class */
} cp64; /* HSR_EC_CP15_64, HSR_EC_CP14_64 */
struct hsr_cp {
unsigned long coproc:4; /* Number of coproc accessed */
unsigned long sbz0p:1;
unsigned long tas:1; /* Trapped Advanced SIMD */
unsigned long res0:14;
unsigned long cc:4; /* Condition Code */
unsigned long ccvalid:1;/* CC Valid */
unsigned long len:1; /* Instruction length */
unsigned long ec:6; /* Exception Class */
} cp; /* HSR_EC_CP */
struct hsr_sysreg {
unsigned long read:1; /* Direction */
unsigned long crm:4; /* CRm */
unsigned long reg:5; /* Rt */
unsigned long crn:4; /* CRn */
unsigned long op1:3; /* Op1 */
unsigned long op2:3; /* Op2 */
unsigned long op0:2; /* Op0 */
unsigned long res0:3;
unsigned long len:1; /* Instruction length */
unsigned long ec:6;
} sysreg; /* HSR_EC_SYSREG */
struct hsr_iabt {
unsigned long ifsc:6; /* Instruction fault status code */
unsigned long res0:1;
unsigned long s1ptw:1; /* Stage 2 fault during stage 1 translation */
unsigned long res1:1;
unsigned long eat:1; /* External abort type */
unsigned long res2:15;
unsigned long len:1; /* Instruction length */
unsigned long ec:6; /* Exception Class */
} iabt; /* HSR_EC_INSTR_ABORT_* */
struct hsr_dabt {
unsigned long dfsc:6; /* Data Fault Status Code */
unsigned long write:1; /* Write / not Read */
unsigned long s1ptw:1; /* Stage 2 fault during stage 1 translation */
unsigned long cache:1; /* Cache Maintenance */
unsigned long eat:1; /* External Abort Type */
unsigned long sbzp0:4;
unsigned long ar:1; /* Acquire Release */
unsigned long sf:1; /* Sixty Four bit register */
unsigned long reg:5; /* Register */
unsigned long sign:1; /* Sign extend */
unsigned long size:2; /* Access Size */
unsigned long valid:1; /* Syndrome Valid */
unsigned long len:1; /* Instruction length */
unsigned long ec:6; /* Exception Class */
} dabt; /* HSR_EC_DATA_ABORT_* */
struct hsr_brk {
unsigned long comment:16; /* Comment */
unsigned long res0:9;
unsigned long len:1; /* Instruction length */
unsigned long ec:6; /* Exception Class */
} brk;
};
/**
* Structure that stores the saved register values on a hypercall.
*/
struct guest_state {
uint64_t pc;
uint64_t cpsr;
uint64_t elr_el1;
uint64_t spsr_el1;
uint64_t sp_el0;
uint64_t sp_el1;
union esr esr_el2;
uint64_t x[31];
}
__attribute__((packed));
#endif

View File

@@ -0,0 +1,556 @@
/*
* This file is part of Luma3DS.
* Copyright (C) 2016-2019 Aurora Wright, TuxSH
*
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
*/
#define _GNU_SOURCE // for strchrnul
#include <stdio.h>
#include <string.h>
#include "../debug_manager.h"
#include "../watchpoints.h"
#include "debug.h"
#include "net.h"
#include "context.h"
#include "verbose.h"
#include "thread.h"
#include "mem.h"
#include "hio.h"
#include <stdlib.h>
#include <signal.h>
static bool GDB_PreprocessDebugEvent(GDBContext *ctx, DebugEventInfo *info)
{
u64 irqFlags = maskIrq();
bool shouldSignal;
switch (info->type) {
case DBGEVENT_CORE_ON: {
shouldSignal = ctx->catchThreadEvents;
if (!info->preprocessed) {
ctx->attachedCoreList |= BIT(info->coreId);
}
break;
}
case DBGEVENT_CORE_OFF: {
if (!info->preprocessed) {
u32 newLst = ctx->attachedCoreList & ~BIT(info->coreId);
if (ctx->selectedThreadId == info->coreId && newLst != 0) {
ctx->selectedThreadId = __builtin_ctz(newLst);
GDB_MigrateRxIrq(ctx, ctx->selectedThreadId);
}
ctx->attachedCoreList = newLst;
shouldSignal = ctx->catchThreadEvents || newLst == 0;
} else {
shouldSignal = ctx->catchThreadEvents || ctx->attachedCoreList == 0;
}
break;
}
default:
shouldSignal = true;
break;
}
info->preprocessed = true;
restoreInterruptFlags(irqFlags);
return shouldSignal;
}
static inline void GDB_MarkDebugEventAcked(GDBContext *ctx, const DebugEventInfo *info)
{
ctx->acknowledgedDebugEventCoreList |= BIT(info->coreId);
}
static int GDB_ParseExceptionFrame(char *out, const DebugEventInfo *info, int sig)
{
u32 coreId = info->coreId;
ExceptionStackFrame *frame = info->frame;
int n = sprintf(out, "T%02xthread:%x;core:%x;", sig, 1 + coreId, coreId);
// Dump the GPRs & sp & pc & cpsr (cpsr is 32-bit in the xml desc)
// For performance reasons, we don't include the FPU registers here
for (u32 i = 0; i < 31; i++) {
n += sprintf(out + n, "%x:%016lx;", i, __builtin_bswap64(ReadRegister(frame, i)));
}
n += sprintf(
out + n,
"1f:%016lx;20:%016lx;21:%08x;",
__builtin_bswap64(*exceptionGetSpPtr(frame)),
__builtin_bswap64(frame->elr_el2),
__builtin_bswap32((u32)frame->spsr_el2)
);
return n;
}
int GDB_SendStopReply(GDBContext *ctx, const DebugEventInfo *info, bool asNotification)
{
char *buf = ctx->buffer + 1;
int n;
bool invalid = false;
buf[0] = 0;
if (asNotification) {
strcpy(buf, "Stopped:");
}
n = strlen(buf);
// Even if the info is invalid:
ctx->lastDebugEvent = info;
ctx->sentDebugEventCoreList |= BIT(info->coreId);
switch(info->type) {
case DBGEVENT_DEBUGGER_BREAK: {
n += GDB_ParseExceptionFrame(buf + n, info, 0);
break;
}
case DBGEVENT_CORE_ON: {
if (ctx->catchThreadEvents) {
n += GDB_ParseExceptionFrame(buf + n, info, SIGTRAP);
strcat(buf, "create:;");
} else {
invalid = true;
}
break;
}
case DBGEVENT_CORE_OFF: {
if (ctx->attachedCoreList == 0) {
// All cores have exited, must report an exit
ctx->processExited = true;
ctx->processEnded = true;
strcat(buf, "W00");
} else if(ctx->catchThreadEvents) {
sprintf(buf, "w0;%x", info->coreId + 1);
} else {
invalid = true;
}
break;
}
case DBGEVENT_EXIT: {
// exited (no error / unhandled exception), SIGTERM (process terminated) * 2
static const char *processExitReplies[] = { "W00", "X0f" };
strcat(buf, processExitReplies[ctx->processExited ? 0 : 1]);
break;
}
case DBGEVENT_EXCEPTION: {
ExceptionClass ec = info->frame->esr_el2.ec;
// Aside from stage 2 translation faults and other pre-handled exceptions,
// the only notable exceptions we get are stop point/single step events from the debugee (basically classes 0x3x)
switch(ec) {
case Exception_BreakpointLowerEl: {
n += GDB_ParseExceptionFrame(buf + n, info, SIGTRAP);
strcat(buf, "hwbreak:;");
break;
}
case Exception_WatchpointLowerEl: {
static const char *kinds[] = { "", "r", "", "a" };
// Note: exception info doesn't provide us with the access size. Use 1.
bool wnr = (info->frame->esr_el2.iss & BIT(6)) != 0;
WatchpointLoadStoreControl dr = wnr ? WatchpointLoadStoreControl_Store : WatchpointLoadStoreControl_Load;
DebugControlRegister cr = retrieveWatchpointConfig(info->frame->far_el2, dr);
if (!cr.enabled) {
DEBUG("GDB: oops, unhandled watchpoint for core id %u, far=%016lx\n", info->coreId, info->frame->far_el2);
} else {
n += GDB_ParseExceptionFrame(buf + n, info, SIGTRAP);
sprintf(buf + n, "%swatch:%016lx;", kinds[cr.lsc], info->frame->far_el2);
}
break;
}
case Exception_SoftwareStepLowerEl: {
n += GDB_ParseExceptionFrame(buf + n, info, SIGTRAP);
break;
}
// Note: we don't really support 32-bit sw breakpoints, we'll still report them
// if the guest has inserted some of them manually...
case Exception_SoftwareBreakpointA64:
case Exception_SoftwareBreakpointA32: {
n += GDB_ParseExceptionFrame(buf + n, info, SIGTRAP);
strcat(buf, "swbreak:;");
break;
}
default: {
invalid = true;
DEBUG("GDB: oops, unhandled exception for core id %u\n", info->coreId);
break;
}
}
break;
}
case DBGEVENT_OUTPUT_STRING: {
if (!GDB_IsNonStop(ctx)) {
uintptr_t addr = info->outputString.address;
size_t remaining = info->outputString.size;
size_t sent = 0;
size_t total = 0;
while (remaining > 0) {
size_t pending = (GDB_BUF_LEN - 1) / 2;
pending = pending < remaining ? pending : remaining;
int res = GDB_SendMemory(ctx, "O", 1, addr + sent, pending);
if(res < 0 || res != 5 + 2 * pending)
break;
sent += pending;
remaining -= pending;
total += res;
}
return (int)total;
} else {
invalid = true;
break;
}
}
// TODO: HIO
default: {
invalid = true;
DEBUG("GDB: unknown exception type %u, core id %u\n", (u32)info->type, info->coreId);
break;
}
}
if (invalid) {
return 0;
} else if (asNotification) {
return GDB_SendNotificationPacket(ctx, buf, strlen(buf));
} else {
return GDB_SendPacket(ctx, buf, strlen(buf));
}
}
/*
Non-stop mode:
-> %Stop:<info>
<- $vStopped
-> $<info>
<- vStopped, etc.
-> $OK
If we're the first to try to send a notification, send it.
Otherwise don't, the core which will handle the GDB packets then will see the changes.
GDB can also send the "?" packet. This aborts the current notfication/vStopped sequence,
and asks to resend the events for each stopped core, no matter if already sent before.
Full-stop mode (default):
If we lose the race, we have to wait until we're continued to send the remaining events...
*/
int GDB_TrySignalDebugEvent(GDBContext *ctx, DebugEventInfo *info)
{
int ret = 0;
// Acquire the gdb lock/disable rx irq. We most likely block here.
GDB_AcquireContext(ctx);
// Need to put it here otherwise core on/off would never be seen
bool shouldSignal = GDB_PreprocessDebugEvent(ctx, info);
// Are we still paused & has the packet not been handled & are we allowed to send on our own?
if (shouldSignal && !ctx->sendOwnDebugEventDisallowed && !info->handled && debugManagerIsCorePaused(info->coreId)) {
bool nonStop = GDB_IsNonStop(ctx);
info->handled = true;
// Full-stop mode: stop other cores
if (!nonStop) {
debugManagerPauseCores(ctx->attachedCoreList & ~BIT(info->coreId));
}
ctx->sendOwnDebugEventDisallowed = true;
ret = GDB_SendStopReply(ctx, info, nonStop);
}
if (!shouldSignal) {
debugManagerContinueCores(BIT(currentCoreCtx->coreId));
}
GDB_ReleaseContext(ctx);
return ret;
}
void GDB_BreakAllCores(GDBContext *ctx)
{
if (GDB_IsNonStop(ctx)) {
debugManagerBreakCores(ctx->attachedCoreList);
} else {
// Break all cores too, but mark everything but the first has handled
debugManagerBreakCores(ctx->attachedCoreList);
u32 rem = ctx->attachedCoreList & ~BIT(currentCoreCtx->coreId);
FOREACH_BIT (tmp, coreId, rem) {
DebugEventInfo *info = debugManagerGetDebugEvent(coreId);
info->handled = true;
info->preprocessed = true;
}
}
}
GDB_DECLARE_VERBOSE_HANDLER(Stopped)
{
u32 coreList = debugManagerGetPausedCoreList() & ctx->attachedCoreList;
u32 remaining = coreList & ~ctx->sentDebugEventCoreList;
// Ack
if (ctx->lastDebugEvent != NULL) {
GDB_MarkDebugEventAcked(ctx, ctx->lastDebugEvent);
}
for (;;) {
if (remaining != 0) {
// Send one more debug event (marking it as handled)
u32 coreId = __builtin_ctz(remaining);
DebugEventInfo *info = debugManagerGetDebugEvent(coreId);
if (GDB_PreprocessDebugEvent(ctx, info)) {
ctx->sendOwnDebugEventDisallowed = true;
return GDB_SendStopReply(ctx, info, false);
} else {
remaining &= ~BIT(coreId);
}
} else {
// vStopped sequenced finished
ctx->sendOwnDebugEventDisallowed = false;
return GDB_ReplyOk(ctx);
}
}
}
GDB_DECLARE_HANDLER(GetStopReason)
{
if (!GDB_IsNonStop(ctx)) {
// Full-stop:
return GDB_SendStopReply(ctx, ctx->lastDebugEvent, false);
} else {
// Non-stop, start new vStopped sequence
ctx->sentDebugEventCoreList = 0;
ctx->acknowledgedDebugEventCoreList = 0;
ctx->lastDebugEvent = NULL;
ctx->sendOwnDebugEventDisallowed = true;
return GDB_HandleVerboseStopped(ctx);
}
}
GDB_DECLARE_HANDLER(Detach)
{
ctx->state = GDB_STATE_DETACHING;
return GDB_ReplyOk(ctx);
}
GDB_DECLARE_HANDLER(Kill)
{
ctx->state = GDB_STATE_DETACHING;
ctx->flags |= GDB_FLAG_TERMINATE;
return 0;
}
GDB_DECLARE_VERBOSE_HANDLER(CtrlC)
{
int ret = GDB_ReplyOk(ctx);
GDB_BreakAllCores(ctx);
return ret;
}
GDB_DECLARE_HANDLER(ContinueOrStepDeprecated)
{
char *addrStart = NULL;
char cmd = ctx->commandData[-1];
// This deprecated command should not be permitted in non-stop mode
/*if (GDB_IsNonStop(ctx)) {
return GDB_ReplyErrno(ctx, EPERM);
}*/
if(cmd == 'C' || cmd == 'S') {
// Check the presence of the two-digit signature, even if we ignore it.
u8 sg;
if (GDB_DecodeHex(&sg, ctx->commandData, 1) != 1) {
return GDB_ReplyErrno(ctx, EILSEQ);
}
// Check: [;addr] or [nothing]
if (ctx->commandData[2] != 0 && ctx->commandData[2] != ';') {
return GDB_ReplyErrno(ctx, EILSEQ);
}
if(ctx->commandData[2] == ';') {
addrStart = ctx->commandData + 3;
}
}
else {
// 'c', 's'
if (ctx->commandData[0] != 0) {
addrStart = ctx->commandData;
}
}
// Only support the simplest form, with no address
// Only degenerate clients will use ;addr, anyway (and the packets are deprecated in favor
// of vCont anyway)
if (addrStart != NULL) {
return GDB_ReplyErrno(ctx, ENOSYS);
}
u32 coreList = ctx->selectedThreadIdForContinuing == -1 ? ctx->attachedCoreList : BIT(ctx->selectedThreadIdForContinuing);
u32 ssMask = (cmd == 's' || cmd == 'S') ? coreList : 0;
FOREACH_BIT (tmp, coreId, ssMask) {
debugManagerSetSteppingRange(coreId, 0, 0);
}
u32 mask = ctx->acknowledgedDebugEventCoreList;
debugManagerSetSingleStepCoreList(ssMask & mask);
debugManagerUnpauseCores(coreList & mask);
return 0;
}
GDB_DECLARE_VERBOSE_HANDLER(Continue)
{
u32 parsedCoreList = 0;
u32 continueCoreList = 0;
u32 stepCoreList = 0;
u32 stopCoreList = 0;
char *cmd = ctx->commandData;
while (cmd != NULL) {
char *nextCmd;
char *threadIdPart;
int threadId;
u32 curMask = 0;
const char *cmdEnd;
// It it always fine if we set the single-stepping range to 0,0 by default
// Because the fields we set are the shadow fields copied to the real fields after debug unpause
uintptr_t ssStartAddr = 0;
uintptr_t ssEndAddr = 0;
// Locate next command, replace delimiter by NUL
nextCmd = strchr(cmd, ';');
if (nextCmd != NULL && *nextCmd == ';') {
*nextCmd++ = 0;
}
// Locate thread-id part, parse thread id
threadIdPart = strchr(cmd, ':');
if (threadIdPart != NULL) {
*threadIdPart++ = 0;
}
if (threadIdPart == NULL || strcmp(threadIdPart, "-1") == 0) {
// Default action...
threadId = -1;
curMask = ctx->attachedCoreList;
} else {
unsigned long id;
if(GDB_ParseHexIntegerList(&id, threadIdPart, 1, 0) == NULL) {
return GDB_ReplyErrno(ctx, EILSEQ);
} else if (id >= MAX_CORE + 1) {
return GDB_ReplyErrno(ctx, EINVAL);
}
threadId = id == 0 ? (int)currentCoreCtx->coreId : (int)id;
curMask = BIT(threadId - 1) & ctx->attachedCoreList;
}
// Parse the command itself
// Note that we may already have handled that thread in a previous command
curMask &= ~parsedCoreList;
switch (cmd[0]) {
case 'S':
case 'C': {
// Check the presence of the two-digit signature, even if we ignore it.
u8 sg;
if (GDB_DecodeHex(&sg, cmd + 1, 1) != 1) {
return GDB_ReplyErrno(ctx, EILSEQ);
}
stepCoreList |= cmd[0] == 'S' ? curMask : 0;
continueCoreList |= curMask;
cmdEnd = cmd + 3;
break;
}
case 's':
stepCoreList |= curMask;
continueCoreList |= curMask;
cmdEnd = cmd + 1;
break;
case 'c':
continueCoreList |= curMask;
cmdEnd = cmd + 1;
break;
case 't':
stopCoreList |= curMask;
cmdEnd = cmd + 1;
break;
case 'r': {
// Range step
unsigned long tmp[2];
cmdEnd = GDB_ParseHexIntegerList(tmp, cmd + 1, 2, 0);
if (cmdEnd == NULL) {
return GDB_ReplyErrno(ctx, EILSEQ);
}
ssStartAddr = tmp[0];
ssEndAddr = tmp[1];
stepCoreList |= curMask;
continueCoreList |= curMask;
break;
}
default:
return GDB_ReplyErrno(ctx, EILSEQ);
}
if (*cmdEnd != 0) {
// We've got garbage data...
return GDB_ReplyErrno(ctx, EILSEQ);
}
FOREACH_BIT (tmp, t, curMask) {
// Set/unset stepping range for all threads affected by this command
debugManagerSetSteppingRange(t, ssStartAddr, ssEndAddr);
}
parsedCoreList |= curMask;
cmd = nextCmd;
}
// "Note: In non-stop mode, a thread is considered running until GDB acknowledges
// an asynchronous stop notification for it with the vStopped packet (see Remote Non-Stop)."
u32 mask;
if (GDB_IsNonStop(ctx)) {
mask = ctx->acknowledgedDebugEventCoreList;
} else {
mask = ctx->attachedCoreList;
ctx->sendOwnDebugEventDisallowed = (continueCoreList & mask) == 0;
}
debugManagerSetSingleStepCoreList(stepCoreList & mask);
debugManagerBreakCores(stopCoreList & ~mask);
debugManagerContinueCores(continueCoreList & mask);
return 0;
}

View File

@@ -0,0 +1,17 @@
/*
* This file is part of Luma3DS.
* Copyright (C) 2016-2019 Aurora Wright, TuxSH
*
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
*/
#pragma once
#include "gdb_context.hpp"
#include "../core_ctx.h"
#include "../debug_manager.h"
int GDB_SendStopReply(GDBContext *ctx, const DebugEventInfo *info, bool asNotification);
int GDB_TrySignalDebugEvent(GDBContext *ctx, DebugEventInfo *info);
void GDB_BreakAllCores(GDBContext *ctx);

133
thermosphere/src/gdb/hio.c Normal file
View File

@@ -0,0 +1,133 @@
/*
* This file is part of Luma3DS.
* Copyright (C) 2016-2019 Aurora Wright, TuxSH
*
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
*/
#include <string.h>
#include "hio.h"
#include "net.h"
#include "mem.h"
#include "debug.h"
/*
bool GDB_FetchPackedHioRequest(GDBContext *ctx, u32 addr)
{
u32 total = GDB_ReadTargetMemory(&ctx->currentHioRequest, ctx, addr, sizeof(PackedGdbHioRequest));
if (total != sizeof(PackedGdbHioRequest) || memcmp(&ctx->currentHioRequest.magic, "GDB\x00", 4) != 0)
{
memset(&ctx->currentHioRequest, 0, sizeof(PackedGdbHioRequest));
ctx->currentHioRequestTargetAddr = 0;
return false;
}
else
{
ctx->currentHioRequestTargetAddr = addr;
return true;
}
}
bool GDB_IsHioInProgress(GDBContext *ctx)
{
return ctx->currentHioRequestTargetAddr != 0;
}
int GDB_SendCurrentHioRequest(GDBContext *ctx)
{
char buf[256+1];
char tmp[32+1];
u32 nStr = 0;
sprintf(buf, "F%s", ctx->currentHioRequest.functionName);
for (u32 i = 0; i < 8 && ctx->currentHioRequest.paramFormat[i] != 0; i++)
{
switch (ctx->currentHioRequest.paramFormat[i])
{
case 'i':
case 'I':
case 'p':
sprintf(tmp, ",%lx", (u32)ctx->currentHioRequest.parameters[i]);
break;
case 'l':
case 'L':
sprintf(tmp, ",%llx", ctx->currentHioRequest.parameters[i]);
break;
case 's':
sprintf(tmp, ",%lx/%x", (u32)ctx->currentHioRequest.parameters[i], ctx->currentHioRequest.stringLengths[nStr++]);
break;
default:
tmp[0] = 0;
break;
}
strcat(buf, tmp);
}
return GDB_SendPacket(ctx, buf, strlen(buf));
}*/
GDB_DECLARE_HANDLER(HioReply)
{
return 0;
/* if (!GDB_IsHioInProgress(ctx))
return GDB_ReplyErrno(ctx, EPERM);
// Reply in the form of Fretcode,errno,Ctrl-C flag;call-specific attachment
// "Call specific attachement" is always empty, though.
const char *pos = ctx->commandData;
u64 retval;
if (*pos == 0 || *pos == ',')
return GDB_ReplyErrno(ctx, EILSEQ);
else if (*pos == '-')
{
pos++;
ctx->currentHioRequest.retval = -1ll;
}
else if (*pos == '+')
{
pos++;
ctx->currentHioRequest.retval = 1;
}
else
ctx->currentHioRequest.retval = 1;
pos = GDB_ParseHexIntegerList64(&retval, pos, 1, ',');
if (pos == NULL)
return GDB_ReplyErrno(ctx, EILSEQ);
ctx->currentHioRequest.retval *= retval;
ctx->currentHioRequest.gdbErrno = 0;
ctx->currentHioRequest.ctrlC = false;
if (*pos != 0)
{
u32 errno_;
// GDB protocol technically allows errno to have a +/- prefix but this will never happen.
pos = GDB_ParseHexIntegerList(&errno_, ++pos, 1, ',');
ctx->currentHioRequest.gdbErrno = (int)errno_;
if (pos == NULL)
return GDB_ReplyErrno(ctx, EILSEQ);
if (*pos != 0)
{
if (*pos != 'C')
return GDB_ReplyErrno(ctx, EILSEQ);
ctx->currentHioRequest.ctrlC = true;
}
}
memset(ctx->currentHioRequest.paramFormat, 0, sizeof(ctx->currentHioRequest.paramFormat));
u32 total = GDB_WriteTargetMemory(ctx, &ctx->currentHioRequest, ctx->currentHioRequestTargetAddr, sizeof(PackedGdbHioRequest));
memset(&ctx->currentHioRequest, 0, sizeof(PackedGdbHioRequest));
ctx->currentHioRequestTargetAddr = 0;
GDB_ContinueExecution(ctx);
return total == sizeof(PackedGdbHioRequest) ? 0 : GDB_ReplyErrno(ctx, EFAULT);*/
}

View File

@@ -0,0 +1,14 @@
/*
* This file is part of Luma3DS.
* Copyright (C) 2016-2019 Aurora Wright, TuxSH
*
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
*/
#pragma once
#include "context.h"
bool GDB_FetchPackedHioRequest(GDBContext *ctx, u32 addr);
bool GDB_IsHioInProgress(GDBContext *ctx);
int GDB_SendCurrentHioRequest(GDBContext *ctx);

View File

@@ -0,0 +1,226 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#include <cstdio>
#include "hvisor_gdb_defines_internal.hpp"
#include "hvisor_gdb_packet_data.hpp"
namespace {
void WriteAck(TransportInterface *iface)
{
char c = '+';
transportInterfaceWriteData(iface, &c, 1);
}
int WriteNack(TransportInterface *iface)
{
char c = '-';
transportInterfaceWriteData(iface, &c, 1);
return 1;
}
}
namespace ams::hvisor::gdb {
int Context::ReceivePacket()
{
char hdr;
bool ctrlC = false;
TransportInterface *iface = m_transportInterface;
// Read the first character...
transportInterfaceReadData(iface, &hdr, 1);
switch (hdr) {
case '+': {
// Ack, don't do anything else except maybe NoAckMode state transition
if (m_noAckSent) {
m_noAck = true;
m_noAckSent = false;
}
return 0;
}
case '-':
// Nack, return the previous packet
transportInterfaceWriteData(iface, m_buffer, m_lastSentPacketSize);
return m_lastSentPacketSize;
case '$':
// Normal packet, handled below
break;
case '\x03':
// Normal packet (Control-C), handled below
ctrlC = true;
break;
default:
// Oops, send a nack
DEBUG("Received a packed with an invalid header from GDB, hdr=%c\n", hdr);
return WriteNack(iface);
}
// We didn't get a nack past this point, read the remaining data if any
m_buffer[0] = hdr;
if (ctrlC) {
// Will never normally happen, but ok
if (m_state < State::Attached) {
DEBUG("Received connection from GDB, now attaching...\n");
Attach();
m_state = State::Attached;
}
return 1;
}
size_t delimPos = transportInterfaceReadDataUntil(iface, m_buffer + 1, 4 + GDB_BUF_LEN - 1, '#');
if (m_buffer[delimPos] != '#' || delimPos == 1) {
// The packet is malformed, send a nack. Refuse empty packets
return WriteNack(iface);
}
m_commandLetter = m_buffer[1];
m_commandData = std::string_view{m_buffer + 1, delimPos};
// Read the checksum
size_t checksumPos = delimPos + 1;
transportInterfaceReadData(iface, m_buffer + checksumPos, 2);
auto checksumOpt = DecodeHexByte(std::string_view{m_buffer + checksumPos, 2});
if (!checksumOpt || *checksumOpt != ComputeChecksum(m_commandData)) {
// Malformed or invalid checksum
return WriteNack(iface);
} else if (!m_noAck) {
WriteAck(iface);
}
// Remove command letter
m_commandData.remove_prefix(1);
// State transitions...
if (m_state < State::Attached) {
DEBUG("Received connection from GDB, now attaching...\n");
Attach();
m_state = State::Attached;
}
// Debug
/*m_buffer[checksumPos + 2] = '\0';
DEBUGRAW("->");
DEBUGRAW(m_buffer);
DEBUGRAW("\n");*/
return static_cast<int>(delimPos + 2);
}
int Context::DoSendPacket(size_t len)
{
transportInterfaceWriteData(m_transportInterface, m_buffer, len);
m_lastSentPacketSize = len;
// Debugging:
/*m_buffer[len] = 0;
DEBUGRAW("<-");
DEBUGRAW(ctx->buffer);
DEBUGRAW("\n");*/
return static_cast<int>(len);
}
int Context::SendPacket(std::string_view packetData, char hdr)
{
u8 checksum = ComputeChecksum(packetData);
if (packetData.data() != m_buffer + 1) {
std::memmove(m_buffer + 1, packetData.data(), packetData.size());
}
size_t checksumPos = 1 + packetData.size() + 1;
m_buffer[0] = '$';
m_buffer[checksumPos - 1] = '#';
EncodeHex(m_buffer + checksumPos, &checksum, 1);
return DoSendPacket(4 + packetData.size());
}
int Context::SendFormattedPacket(const char *packetDataFmt, ...)
{
va_list args;
va_start(args, packetDataFmt);
int n = vsprintf(m_buffer + 1, packetDataFmt, args);
va_end(args);
if (n < 0) {
return -1;
} else {
return SendPacket(std::string_view{m_buffer + 1, n});
}
}
int Context::SendHexPacket(const void *packetData, size_t len)
{
if (4 + 2 * len < GDB_BUF_LEN) {
return -1;
}
EncodeHex(m_buffer + 1, packetData, len);
return SendPacket(std::string_view{m_buffer + 1, 2 * len});
}
int Context::SendStreamData(std::string_view streamData, size_t offset, size_t length, bool forceEmptyLast)
{
size_t totalSize = streamData.size();
// GDB_BUF_LEN does not include the usual $#<1-byte checksum>
length = std::min(length, GDB_BUF_LEN - 1ul);
char letter;
if ((forceEmptyLast && offset >= totalSize) || (!forceEmptyLast && offset + length >= totalSize)) {
length = offset >= totalSize ? 0 : totalSize - offset;
letter = 'l';
} else {
letter = 'm';
}
// Note: ctx->buffer[0] = '$'
if (streamData.data() + offset != m_buffer + 2) {
memmove(m_buffer + 2, streamData.data() + offset, length);
}
m_buffer[1] = letter;
return SendPacket(std::string_view{m_buffer + 1, 1 + length});
}
int Context::ReplyOk()
{
return SendPacket("OK");
}
int Context::ReplyEmpty()
{
return SendPacket("");
}
int Context::ReplyErrno(int no)
{
u8 no8 = static_cast<u8>(no);
char resp[] = "E00";
EncodeHex(resp + 1, &no8, 1);
return SendPacket(resp);
}
}

View File

@@ -0,0 +1,257 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
// Lots of code from:
/*
* This file is part of Luma3DS.
* Copyright (C) 2016-2019 Aurora Wright, TuxSH
*
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
*/
#include "hvisor_gdb_defines_internal.hpp"
#include "hvisor_gdb_packet_data.hpp"
#include "../hvisor_hw_breakpoint_manager.hpp"
#include "../hvisor_sw_breakpoint_manager.hpp"
#include "../hvisor_watchpoint_manager.hpp"
#include "../hvisor_fpu_register_cache.hpp"
#include "../debug_manager.h"
namespace {
TEMPORARY char g_gdbWorkBuffer[GDB_WORK_BUF_LEN];
TEMPORARY char g_gdbBuffer[GDB_BUF_LEN + 4 + 1];
}
namespace ams::hvisor::gdb {
void Context::Disconnect()
{
Detach();
auto *iface = m_transportInterface;
*this = {};
m_transportInterface = iface;
}
void Context::Initialize(TransportInterfaceType ifaceType, u32 ifaceId, u32 ifaceFlags)
{
m_workBuffer = g_gdbWorkBuffer;
m_buffer = g_gdbBuffer;
/*m_transportInterface = transportInterfaceCreate(
ifaceType,
ifaceId,
ifaceFlags,
GDB_ReceiveDataCallback,
GDB_ProcessDataCallback,
ctx
);*/
}
void Context::Attach()
{
// TODO: move the debug traps enable here?
m_attachedCoreList = CoreContext::GetActiveCoreMask();
// We're in full-stop mode at this point
// Break cores, but don't send the debug event (it will be fetched with '?')
// Initialize lastDebugEvent
debugManagerSetReportingEnabled(true);
m_sendOwnDebugEventDisallowed = true;
BreakAllCores();
DebugEventInfo *info = debugManagerGetDebugEvent(currentCoreCtx->GetCoreId());
info->preprocessed = true;
info->handled = true;
m_lastDebugEvent = info;
m_state = State::Attached;
m_sendOwnDebugEventDisallowed = false;
}
void Context::Detach()
{
WatchpointManager::GetInstance().RemoveAll();
HwBreakpointManager::GetInstance().RemoveAll();
SwBreakpointManager::GetInstance().RemoveAll(true);
// Reports to gdb are prevented because of "detaching" state?
// TODO: disable debug traps
m_currentHioRequestTargetAddr = 0;
memset(&m_currentHioRequest, 0, sizeof(PackedGdbHioRequest));
debugManagerSetReportingEnabled(false);
debugManagerContinueCores(CoreContext::GetActiveCoreMask());
}
void Context::MigrateRxIrq(u32 coreId) const
{
FpuRegisterCache::GetInstance().CleanInvalidate();
//transportInterfaceSetInterruptAffinity(ctx->transportInterface, BIT(coreId));
}
GDB_DEFINE_HANDLER(Unsupported)
{
return ReplyEmpty();
}
#define COMMAND_CASE(letter, method) case letter: return GDB_HANDLER(method)();
int Context::ProcessPacket()
{
m_commandLetter = m_commandData[0];
m_commandData.remove_prefix(1);
switch (m_commandLetter) {
COMMAND_CASE('?', GetStopReason)
//COMMAND_CASE('c', ContinueOrStepDeprecated)
//COMMAND_CASE('C', ContinueOrStepDeprecated)
COMMAND_CASE('D', Detach)
COMMAND_CASE('F', HioReply)
COMMAND_CASE('g', ReadRegisters)
COMMAND_CASE('G', WriteRegisters)
COMMAND_CASE('H', SetThreadId)
COMMAND_CASE('k', Kill)
COMMAND_CASE('m', ReadMemory)
COMMAND_CASE('M', WriteMemory)
COMMAND_CASE('p', ReadRegister)
COMMAND_CASE('P', WriteRegister)
COMMAND_CASE('q', Query)
COMMAND_CASE('Q', Query)
//COMMAND_CASE('s', ContinueOrStepDeprecated)
//COMMAND_CASE('S', ContinueOrStepDeprecated)
COMMAND_CASE('T', IsThreadAlive)
COMMAND_CASE('v', VerboseCommand)
COMMAND_CASE('X', WriteMemoryRaw)
COMMAND_CASE('z', ToggleStopPoint)
COMMAND_CASE('Z', ToggleStopPoint)
default:
return HandleUnsupported();
}
}
#undef COMMAND_CASE
/*
static const struct{
char command;
GDBCommandHandler handler;
} gdbCommandHandlers[] = {
{ '?', GDB_HANDLER(GetStopReason) },
//{ '!', GDB_HANDLER(EnableExtendedMode) }, // note: stubbed
//{ 'c', GDB_HANDLER(ContinueOrStepDeprecated) },
//{ 'C', GDB_HANDLER(ContinueOrStepDeprecated) },
{ 'D', GDB_HANDLER(Detach) },
{ 'F', GDB_HANDLER(HioReply) },
{ 'g', GDB_HANDLER(ReadRegisters) },
{ 'G', GDB_HANDLER(WriteRegisters) },
{ 'H', GDB_HANDLER(SetThreadId) },
{ 'k', GDB_HANDLER(Kill) },
{ 'm', GDB_HANDLER(ReadMemory) },
{ 'M', GDB_HANDLER(WriteMemory) },
{ 'p', GDB_HANDLER(ReadRegister) },
{ 'P', GDB_HANDLER(WriteRegister) },
{ 'q', GDB_HANDLER(ReadQuery) },
{ 'Q', GDB_HANDLER(WriteQuery) },
//{ 's', GDB_HANDLER(ContinueOrStepDeprecated) },
//{ 'S', GDB_HANDLER(ContinueOrStepDeprecated) },
{ 'T', GDB_HANDLER(IsThreadAlive) },
{ 'v', GDB_HANDLER(VerboseCommand) },
{ 'X', GDB_HANDLER(WriteMemoryRaw) },
{ 'z', GDB_HANDLER(ToggleStopPoint) },
{ 'Z', GDB_HANDLER(ToggleStopPoint) },
};
static inline GDBCommandHandler GDB_GetCommandHandler(char command)
{
static const u32 nbHandlers = sizeof(gdbCommandHandlers) / sizeof(gdbCommandHandlers[0]);
size_t i;
for (i = 0; i < nbHandlers && gdbCommandHandlers[i].command != command; i++);
return i < nbHandlers ? gdbCommandHandlers[i].handler : GDB_HANDLER(Unsupported);
}
static int GDB_ProcessPacket(GDBContext *ctx, size_t len)
{
int ret;
ENSURE(ctx->state != GDB_STATE_DISCONNECTED);
// Handle the packet...
if (ctx->buffer[0] == '\x03') {
GDB_BreakAllCores(ctx);
ret = 0;
} else {
GDBCommandHandler handler = GDB_GetCommandHandler(ctx->buffer[1]);
ctx->commandData = ctx->buffer + 2;
ret = handler(ctx);
}
// State changes...
if (ctx->state == GDB_STATE_DETACHING) {
return -1;
}
return ret;
}
static size_t GDB_ReceiveDataCallback(TransportInterface *iface, void *ctxVoid)
{
return (size_t)GDB_ReceivePacket((GDBContext *)ctxVoid);
}
static void GDB_ProcessDataCallback(TransportInterface *iface, void *ctxVoid, size_t sz)
{
int r = (int)sz;
GDBContext *ctx = (GDBContext *)ctxVoid;
if (r == -1) {
// Not sure if GDB has something to forcefully close connections over UART...
char c = '\x04'; // ctrl-D
transportInterfaceWriteData(iface, &c, 1);
GDB_Disconnect(ctx);
}
r = GDB_ProcessPacket(ctx, sz);
if (r == -1) {
GDB_Disconnect(ctx);
}
}
void GDB_AcquireContext(GDBContext *ctx)
{
transportInterfaceAcquire(ctx->transportInterface);
}
void GDB_ReleaseContext(GDBContext *ctx)
{
transportInterfaceRelease(ctx->transportInterface);
}
*/
}

View File

@@ -0,0 +1,230 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
// Lots of code from:
/*
* This file is part of Luma3DS.
* Copyright (C) 2016-2019 Aurora Wright, TuxSH
*
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
*/
#pragma once
#include "../defines.hpp"
#include "../transport_interface.h"
#include <string_view>
#define _REENT_ONLY
#include <cerrno>
#define DECLARE_HANDLER(name) int Handle##name()
#define DECLARE_QUERY_HANDLER(name) DECLARE_HANDLER(Query##name)
#define DECLARE_VERBOSE_HANDLER(name) DECLARE_HANDLER(Verbose##name)
#define DECLARE_REMOTE_HANDLER(name) DECLARE_HANDLER(Remote##name)
#define DECLARE_XFER_HANDLER(name) int HandleXfer##name(bool write, std::string_view annex, size_t offset, size_t length)
struct DebugEventInfo;
namespace ams::hvisor::gdb {
struct PackedGdbHioRequest {
// TODO revamp
char magic[4]; // "GDB\x00"
u32 version;
// Request
char functionName[16+1];
char paramFormat[8+1];
u64 parameters[8];
size_t stringLengths[8];
// Return
s64 retval;
int gdbErrno;
bool ctrlC;
};
class Context final {
private:
enum class State {
Disconnected = 0,
Connected,
Attached,
Detaching
};
private:
// No need for a lock, it's in the transport interface layer...
TransportInterface *m_transportInterface = nullptr;
State m_state = State::Disconnected;
bool m_noAckSent = false;
bool m_noAck = false;
bool m_nonStop = false;
u32 m_attachedCoreList = 0;
int m_selectedCoreId = 0;
int m_selectedCoreIdForContinuing = 0;
u32 m_sentDebugEventCoreList = 0;
u32 m_acknowledgedDebugEventCoreList = 0;
bool m_sendOwnDebugEventDisallowed = 0;
bool m_catchThreadEvents = false;
bool m_processEnded = false;
bool m_processExited = false;
const struct DebugEventInfo *m_lastDebugEvent = nullptr;
uintptr_t m_currentHioRequestTargetAddr = 0ul;
PackedGdbHioRequest m_currentHioRequest{};
std::string_view m_targetXml{};
char m_commandLetter = '\0';
std::string_view m_commandData{};
size_t m_lastSentPacketSize = 0ul;
char *m_buffer = nullptr;
char *m_workBuffer = nullptr;
private:
Context(const Context &) = default;
Context &operator=(const Context &) = default;
Context(Context &&) = default;
Context &operator=(Context &&) = default;
private:
void MigrateRxIrq(u32 coreId) const;
int ProcessPacket();
void Disconnect();
// Debug
void BreakAllCores();
// Comms
int ReceivePacket();
int DoSendPacket(size_t len);
int SendPacket(std::string_view packetData, char hdr = '$');
int SendFormattedPacket(const char *packetDataFmt, ...);
int SendHexPacket(const void *packetData, size_t len);
int SendStreamData(std::string_view streamData, size_t offset, size_t length, bool forceEmptyLast);
int ReplyOk();
int ReplyEmpty();
int ReplyErrno(int no);
// Memory
int SendMemory(uintptr_t addr, size_t len, std::string_view prefix = {});
int WriteMemoryImpl(size_t (*decoder)(void *, const void *, size_t));
// Helpers
constexpr char *GetInPlaceOutputBuffer() const
{
return m_buffer + 1;
}
constexpr char *GetWorkBuffer() const
{
return m_workBuffer;
}
private:
// Meta
DECLARE_HANDLER(Unsupported);
DECLARE_HANDLER(Query);
DECLARE_QUERY_HANDLER(Xfer);
DECLARE_HANDLER(VerboseCommand);
// General queries
DECLARE_QUERY_HANDLER(Supported);
DECLARE_QUERY_HANDLER(StartNoAckMode);
DECLARE_QUERY_HANDLER(Attached);
// XML Transfer
DECLARE_XFER_HANDLER(Features);
// Resuming features enumeration
DECLARE_VERBOSE_HANDLER(ContinueSupported);
// "Threads"
// Capitalization in "GetTLSAddr" is intended.
DECLARE_HANDLER(SetThreadId);
DECLARE_HANDLER(IsThreadAlive);
DECLARE_QUERY_HANDLER(CurrentThreadId);
DECLARE_QUERY_HANDLER(fThreadInfo);
DECLARE_QUERY_HANDLER(sThreadInfo);
DECLARE_QUERY_HANDLER(ThreadEvents);
DECLARE_QUERY_HANDLER(ThreadExtraInfo);
DECLARE_QUERY_HANDLER(GetTLSAddr);
// Debug
DECLARE_VERBOSE_HANDLER(Stopped);
DECLARE_HANDLER(Detach);
DECLARE_HANDLER(Kill);
DECLARE_VERBOSE_HANDLER(CtrlC);
DECLARE_HANDLER(ContinueOrStepDeprecated);
DECLARE_VERBOSE_HANDLER(Continue);
DECLARE_HANDLER(GetStopReason);
// Stop points
DECLARE_HANDLER(ToggleStopPoint);
// Memory
DECLARE_HANDLER(ReadMemory);
DECLARE_HANDLER(WriteMemory);
DECLARE_HANDLER(WriteMemoryRaw);
DECLARE_QUERY_HANDLER(SearchMemory);
// Registers
DECLARE_HANDLER(ReadRegisters);
DECLARE_HANDLER(WriteRegisters);
DECLARE_HANDLER(ReadRegister);
DECLARE_HANDLER(WriteRegister);
// Hio
DECLARE_HANDLER(HioReply);
// Custom commands
DECLARE_QUERY_HANDLER(Rcmd);
public:
Context() = default;
void Initialize(TransportInterfaceType ifaceType, u32 ifaceId, u32 ifaceFlags);
void Attach();
void Detach();
void lock();
void unlock();
/* TODO: parent
void Acquire();
void Release();
*/
constexpr bool IsAttached() const
{
return m_state == State::Attached;
}
};
}
#undef DECLARE_HANDLER
#undef DECLARE_QUERY_HANDLER
#undef DECLARE_VERBOSE_HANDLER
#undef DECLARE_REMOTE_HANDLER
#undef DECLARE_XFER_HANDLER

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
// Some code from:
/*
* This file is part of Luma3DS.
* Copyright (C) 2016-2019 Aurora Wright, TuxSH
*
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
*/
#pragma once
#include "hvisor_gdb_context.hpp"
// 512+24 is the ideal size as IDA will try to read exactly 0x100 bytes at a time.
// IDA seems to want additional bytes as well.
// 1024 is fine enough to put all regs in the 'T' stop reply packets
// Add 4 to this for the actual allocated size, for $#<checksum>, see below.
#define GDB_BUF_LEN 0x800
#define GDB_WORK_BUF_LEN 0x1000
#define GDB_HANDLER(name) Handle##name
#define GDB_QUERY_HANDLER(name) GDB_HANDLER(Query##name)
#define GDB_VERBOSE_HANDLER(name) GDB_HANDLER(Verbose##name)
#define GDB_REMOTE_COMMAND_HANDLER(name) GDB_HANDLER(RemoteCommand##name)
#define GDB_XFER_HANDLER(name) GDB_HANDLER(Xfer##name)
#define GDB_DEFINE_HANDLER(name) int Context::GDB_HANDLER(name)()
#define GDB_DEFINE_QUERY_HANDLER(name) GDB_DEFINE_HANDLER(Query##name)
#define GDB_DEFINE_VERBOSE_HANDLER(name) GDB_DEFINE_HANDLER(Verbose##name)
#define GDB_DEFINE_REMOTE_COMMAND_HANDLER(name) GDB_DEFINE_HANDLER(RemoteCommand##name)
#define GDB_DEFINE_XFER_HANDLER(name)\
int Context::GDB_XFER_HANDLER(name)(bool write, std::string_view annex, size_t offset, size_t length)
#define GDB_CHECK_NO_CMD_DATA() do { if (!m_commandData.empty()) return ReplyErrno(EILSEQ); } while (false)

View File

@@ -0,0 +1,101 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
/*
* This file is part of Luma3DS.
* Copyright (C) 2016-2019 Aurora Wright, TuxSH
*
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
*/
#include "hvisor_gdb_defines_internal.hpp"
#include "hvisor_gdb_packet_data.hpp"
#include "../hvisor_guest_memory.hpp"
namespace ams::hvisor::gdb {
int Context::SendMemory(uintptr_t addr, size_t len, std::string_view prefix)
{
char *buf = GetInPlaceOutputBuffer();
char *membuf = GetWorkBuffer();
size_t prefixLen = prefix.size();
if(prefixLen + 2 * len > GDB_BUF_LEN) {
// gdb shouldn't send requests which responses don't fit in a packet
return prefixLen == 0 ? ReplyErrno(ENOMEM) : -1;
}
size_t total = GuestReadMemory(addr, len, membuf);
if (total == 0) {
return prefixLen == 0 ? ReplyErrno(EFAULT) : -EFAULT;
} else {
std::copy(prefix.begin(), prefix.end(), buf);
EncodeHex(buf + prefixLen, membuf, total);
return SendPacket(std::string_view{buf, prefixLen + 2 * total});
}
}
int Context::WriteMemoryImpl(size_t (*decoder)(void *, const void *, size_t))
{
char *workbuf = GetWorkBuffer();
auto [nread, addr, len] = ParseHexIntegerList<2>(m_commandData, ':');
if (nread == 0) {
return ReplyErrno(EILSEQ);
}
m_commandData.remove_prefix(nread);
if (len > m_commandData.length() / 2) {
// Data len field doesn't match what we got...
return ReplyErrno(ENOMEM);
}
size_t n = decoder(workbuf, m_commandData.data(), m_commandData.size());
if(n != len) {
// Decoding error...
return ReplyErrno(EILSEQ);
}
size_t total = GuestWriteMemory(addr, len, workbuf);
return total == len ? ReplyOk() : ReplyErrno(EFAULT);
}
GDB_DEFINE_HANDLER(ReadMemory)
{
auto [nparsed, addr, len] = ParseHexIntegerList<2>(m_commandData);
if (nparsed == 0) {
return ReplyErrno(EILSEQ);
}
return SendMemory(addr, len);
}
GDB_DEFINE_HANDLER(WriteMemory)
{
return WriteMemoryImpl(DecodeHex);
}
GDB_DEFINE_HANDLER(WriteMemoryRaw)
{
return WriteMemoryImpl(UnescapeBinaryData);
}
}

View File

@@ -0,0 +1,106 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "hvisor_gdb_packet_data.hpp"
namespace ams::hvisor::gdb {
u8 ComputeChecksum(std::string_view packetData)
{
return std::accumulate(packetData.cbegin(), packetData.cend(), u8{0u});
}
size_t EncodeHex(char *dst, const void *src, size_t len)
{
static const char *alphabet = "0123456789abcdef";
const u8 *src8 = reinterpret_cast<const u8 *>(src);
for (size_t i = 0; i < len; i++) {
dst[2 * i] = alphabet[(src8[i] & 0xF0) >> 4];
dst[2 * i + 1] = alphabet[src8[i] & 0x0F];
}
return 2 * len;
}
size_t DecodeHex(void *dst, std::string_view data)
{
size_t i = 0;
u8 *dst8 = reinterpret_cast<u8 *>(dst);
for (i = 0; i < data.size() / 2; i++) {
auto bOpt = DecodeHexByte(data);
if (!bOpt) {
return i;
}
dst8[i] = *bOpt;
data.remove_prefix(2);
}
return i;
}
size_t DecodeHex(void *dst, const void *src, size_t len)
{
return DecodeHex(dst, std::string_view{reinterpret_cast<const char *>(src), len});
}
size_t EscapeBinaryData(size_t *encodedCount, void *dst, const void *src, size_t len, size_t maxLen)
{
u8 *dst8 = reinterpret_cast<u8 *>(dst);
const u8 *src8 = reinterpret_cast<const u8 *>(src);
len = std::min(len, maxLen);
u8 *dstMax = dst8 + len;
while (dst8 < dstMax) {
if (*src8 == '$' || *src8 == '#' || *src8 == '}' || *src8 == '*') {
if (dst8 + 1 >= dstMax) {
break;
}
*dst8++ = '}';
*dst8++ = *src8++ ^ 0x20;
}
else {
*dst8++ = *src8++;
}
}
*encodedCount = dst8 - reinterpret_cast<u8 *>(dst);
return src8 - reinterpret_cast<const u8 *>(src);
}
size_t UnescapeBinaryData(void *dst, const void *src, size_t len)
{
u8 *dst8 = reinterpret_cast<u8 *>(dst);
const u8 *src8 = reinterpret_cast<const u8 *>(src);
const u8 *srcEnd = src8 + len;
while (src8 < srcEnd) {
if (*src8 == '}') {
src8++;
*dst8++ = *src8++ ^ 0x20;
} else {
*dst8++ = *src8++;
}
}
return dst8 - reinterpret_cast<u8 *>(dst);
}
}

View File

@@ -0,0 +1,199 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../defines.hpp"
#include <string_view>
namespace ams::hvisor::gdb {
constexpr unsigned long DecodeHexDigit(char src)
{
switch (src) {
case '0' ... '9': return 0 + (src - '0');
case 'a' ... 'f': return 10 + (src - 'a');
case 'A' ... 'F': return 10 + (src - 'A');
default:
return 16;
}
}
constexpr auto ParseInteger(std::string_view str, u32 base = 0, bool allowPrefix = true)
{
unsigned long res = 0;
long mult = 1;
auto errval = std::tuple{0ul, 0ul};
size_t total = 0;
if ((base == 0 && !allowPrefix) || base > 16 || str.empty()) {
return errval;
}
// Check for +, -
if (str[0] == '+') {
if (!allowPrefix) {
return errval;
}
str.remove_prefix(1);
++total;
} else if (str[0] == '-') {
if (!allowPrefix) {
return errval;
}
str.remove_prefix(1);
mult = -1;
++total;
}
if (str.empty()) {
// Oops
return errval;
}
// Now, check for 0x or leading 0
if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') {
if (!allowPrefix || (base != 16 && base != 0)) {
return errval;
} else {
str.remove_prefix(2);
base = 16;
total += 2;
}
} else if (base == 0 && str[0] == '0') {
base = 8;
} else if (base == 0) {
base = 10;
}
if (str.empty()) {
// Oops
return errval;
}
auto it = str.begin();
for (; it != str.end(); ++it) {
unsigned long v = DecodeHexDigit(*it);
if (v >= base) {
break;
}
res *= base;
res += v;
++total;
}
return std::tuple{total, res * mult};
}
template<size_t N>
constexpr auto ParseIntegerList(std::string_view str, u32 base, bool allowPrefix, char sep, char lastSep = '\0')
{
// First element is parsed size
std::array<unsigned long, 1+N> res{ 0 };
size_t total = 0;
for (size_t i = 0; i < N && !str.empty(); i++) {
auto [nread, val] = ParseInteger(str, base, allowPrefix);
// Parse failure
if (nread == 0) {
return res;
}
str.remove_prefix(nread);
// Check separators
if (i != N - 1) {
if (str.empty() || str[0] != sep) {
return res;
}
str.remove_prefix(1);
++total;
} else if (i == N - 1) {
if ((lastSep == '\0') && !str.empty()) {
return res;
} else if (lastSep != '\0') {
if (str.empty() || str[0] != lastSep) {
return res;
}
str.remove_prefix(1);
++total;
}
}
total += nread;
res[1 + i] = val;
}
res[0] = total;
return res;
}
template<size_t N>
constexpr auto ParseHexIntegerList(std::string_view str, char lastSep = '\0')
{
return ParseIntegerList<N>(str, 16, false, ',', lastSep);
}
template<size_t N>
constexpr auto SplitString(std::string_view data, char delim)
{
static_assert(N != 0);
std::array<std::string_view, N> res = {};
size_t delimPos = 0;
for (size_t i = 0; i < N - 1; i++) {
delimPos = data.find(delim);
if (delimPos == std::string_view::npos) {
return res;
}
res[i] = std::string_view{data.data(), delimPos};
data.remove_prefix(delimPos + 1);
}
res[N - 1] = data;
return res;
}
constexpr std::optional<u8> DecodeHexByte(std::string_view data)
{
if (data.size() < 2) {
return {};
}
auto v1 = DecodeHexDigit(data[0]);
auto v2 = DecodeHexDigit(data[1]);
if (v1 >= 16 || v2 >= 16) {
return {};
}
return (v1 << 4) | v2;
}
u8 ComputeChecksum(std::string_view packetData);
size_t EncodeHex(char *dst, const void *src, size_t len);
size_t DecodeHex(void *dst, std::string_view data);
size_t DecodeHex(void *dst, const void *src, size_t len);
size_t EscapeBinaryData(size_t *encodedCount, void *dst, const void *src, size_t len, size_t maxLen);
size_t UnescapeBinaryData(void *dst, const void *src, size_t len);
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
/*
* This file is part of Luma3DS.
* Copyright (C) 2016-2019 Aurora Wright, TuxSH
*
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
*/
#include "hvisor_gdb_defines_internal.hpp"
#include "hvisor_gdb_packet_data.hpp"
namespace ams::hvisor::gdb {
GDB_DEFINE_QUERY_HANDLER(Supported)
{
// Ignore what gdb sent...
return SendFormattedPacket(
"PacketSize=%x;"
"qXfer:features:read+;"
"QStartNoAckMode+;QThreadEvents+"
"vContSupported+;swbreak+;hwbreak+",
GDB_BUF_LEN
);
}
GDB_DEFINE_QUERY_HANDLER(StartNoAckMode)
{
GDB_CHECK_NO_CMD_DATA();
m_noAckSent = true;
return ReplyOk();
}
GDB_DEFINE_QUERY_HANDLER(Attached)
{
GDB_CHECK_NO_CMD_DATA();
return SendPacket("1");
}
#define QUERY_CMD_CASE2(name, fun) if (cmdName==name) { return GDB_QUERY_HANDLER(fun)(); } else
#define QUERY_CMD_CASE(fun) QUERY_CMD_CASE2(STRINGIZE(fun), fun)
GDB_DEFINE_HANDLER(Query)
{
// Extract name
char delim = ':';
size_t delimPos = m_commandData.find_first_of(":,");
std::string_view cmdName = m_commandData;
if (delimPos != std::string_view::npos) {
delim = m_commandData[delimPos];
cmdName.remove_suffix(cmdName.size() - delimPos);
m_commandData.remove_prefix(delimPos + 1);
}
// Only 2 commands are delimited by a comma, all with lowercase 'q' prefix
// We don't handle qP nor qL
if (delim != ':') {
if (m_commandLetter != 'q') {
return ReplyErrno(EILSEQ);
} else if (cmdName != "Rcmd" && cmdName != "ThreadExtraInfo") {
return ReplyErrno(EILSEQ);
}
}
if (m_commandLetter == 'q') {
QUERY_CMD_CASE(Supported)
QUERY_CMD_CASE(Xfer)
QUERY_CMD_CASE(Attached)
QUERY_CMD_CASE(fThreadInfo)
QUERY_CMD_CASE(sThreadInfo)
QUERY_CMD_CASE(ThreadExtraInfo)
QUERY_CMD_CASE2("C", CurrentThreadId)
QUERY_CMD_CASE(Rcmd)
/*default :*/{
return HandleUnsupported();
}
} else {
QUERY_CMD_CASE(StartNoAckMode)
QUERY_CMD_CASE(ThreadEvents)
/*default :*/{
return HandleUnsupported();
}
}
}
#undef QUERY_CMD_CASE
#undef QUERY_CMD_CASE2
}

View File

@@ -0,0 +1,227 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
/*
* This file is part of Luma3DS.
* Copyright (C) 2016-2019 Aurora Wright, TuxSH
*
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
*/
#include "hvisor_gdb_defines_internal.hpp"
#include "hvisor_gdb_packet_data.hpp"
#include "../hvisor_exception_stack_frame.hpp"
#include "../hvisor_fpu_register_cache.hpp"
namespace {
auto GetRegisterPointerAndSize(unsigned long id, ams::hvisor::ExceptionStackFrame *frame, ams::hvisor::FpuRegisterCache::Storage &fpuRegStorage)
{
void *outPtr = nullptr;
size_t outSz = 0;
switch (id) {
case 0 ... 30:
outPtr = &frame->x[id];
outSz = 8;
break;
case 31:
outPtr = &frame->GetSpRef();
outSz = 8;
break;
case 32:
outPtr = &frame->spsr_el2;
outSz = 4;
break;
case 33 ... 64:
outPtr = &fpuRegStorage.q[id - 33];
outSz = 16;
break;
case 65:
outPtr = &fpuRegStorage.fpsr;
outSz = 4;
break;
case 66:
outPtr = &fpuRegStorage.fpcr;
outSz = 4;
break;
default:
__builtin_unreachable();
break;
}
return std::tuple{outPtr, outSz};
}
}
namespace ams::hvisor::gdb {
// Note: GDB treats cpsr, fpsr, fpcr as 32-bit integers...
GDB_DEFINE_HANDLER(ReadRegisters)
{
ENSURE(m_selectedCoreId == currentCoreCtx->GetCoreId());
GDB_CHECK_NO_CMD_DATA();
ExceptionStackFrame *frame = currentCoreCtx->GetGuestFrame();
auto &fpuRegStorage = FpuRegisterCache::GetInstance().ReadRegisters();
char *buf = GetInPlaceOutputBuffer();
size_t n = 0;
struct {
u64 sp;
u64 pc;
u32 cpsr;
} cpuSprs = {
.sp = frame->GetSpRef(),
.pc = frame->elr_el2,
.cpsr = static_cast<u32>(frame->spsr_el2),
};
u32 fpuSprs[2] = {
static_cast<u32>(fpuRegStorage.fpsr),
static_cast<u32>(fpuRegStorage.fpcr),
};
n += EncodeHex(buf + n, frame->x, sizeof(frame->x));
n += EncodeHex(buf + n, &cpuSprs, 8+8+4);
n += EncodeHex(buf + n, fpuRegStorage.q, sizeof(fpuRegStorage.q));
n += EncodeHex(buf + n, fpuSprs, sizeof(fpuSprs));
return SendPacket(std::string_view{buf, n});
}
GDB_DEFINE_HANDLER(WriteRegisters)
{
ENSURE(m_selectedCoreId == currentCoreCtx->GetCoreId());
ExceptionStackFrame *frame = currentCoreCtx->GetGuestFrame();
auto &fpuRegStorage = FpuRegisterCache::GetInstance().ReadRegisters();
char *tmp = GetWorkBuffer();
size_t n = 0;
struct {
u64 sp;
u64 pc;
u32 cpsr;
} cpuSprs;
u32 fpuSprs[2];
struct {
void *dst;
size_t sz;
} infos[4] = {
{ frame->x, sizeof(frame->x) },
{ &cpuSprs, 8+8+4 },
{ fpuRegStorage.q, sizeof(fpuRegStorage.q) },
{ fpuSprs, sizeof(fpuSprs) },
};
// Parse & return on error
for (const auto &info: infos) {
// Fuck std::string_view.substr throwing exceptions
if (DecodeHex(tmp + n, m_commandData.data(), info.sz) != info.sz) {
return ReplyErrno(EILSEQ);
}
m_commandData.remove_prefix(2 * info.sz);
n += info.sz;
}
// Copy. Note: we don't check if cpsr (spsr_el2) was modified to return to EL2...
n = 0;
for (const auto &info: infos) {
std::copy(tmp + n, tmp + n + info.sz, info.dst);
n += info.sz;
}
frame->GetSpRef() = cpuSprs.sp;
frame->elr_el2 = cpuSprs.pc;
frame->spsr_el2 = cpuSprs.cpsr;
fpuRegStorage.fpsr = fpuSprs[0];
fpuRegStorage.fpcr = fpuSprs[1];
FpuRegisterCache::GetInstance().CommitRegisters();
return ReplyOk();
}
GDB_DEFINE_HANDLER(ReadRegister)
{
ENSURE(m_selectedCoreId == currentCoreCtx->GetCoreId());
ExceptionStackFrame *frame = currentCoreCtx->GetGuestFrame();
FpuRegisterCache::Storage *fpuRegStorage = nullptr;
auto [nread, gdbRegNum] = ParseHexIntegerList<1>(m_commandData);
if (nread == 0) {
return ReplyErrno(EILSEQ);
}
// Check the register number
if (gdbRegNum >= 31 + 3 + 32 + 2) {
return ReplyErrno(EINVAL);
}
if (gdbRegNum > 31 + 3) {
// FPU register -- must read the FPU registers first
fpuRegStorage = &FpuRegisterCache::GetInstance().ReadRegisters();
}
return std::apply(SendHexPacket, GetRegisterPointerAndSize(gdbRegNum, frame, *fpuRegStorage));
}
GDB_DEFINE_HANDLER(WriteRegister)
{
ENSURE(m_selectedCoreId == currentCoreCtx->GetCoreId());
char *tmp = GetWorkBuffer();
ExceptionStackFrame *frame = currentCoreCtx->GetGuestFrame();
auto &fpuRegStorage = FpuRegisterCache::GetInstance().GetStorageRef();
auto [nread, gdbRegNum] = ParseHexIntegerList<1>(m_commandData, '=');
if (nread == 0) {
return ReplyErrno(EILSEQ);
}
m_commandData.remove_prefix(nread);
// Check the register number
if (gdbRegNum >= 31 + 3 + 32 + 2) {
return ReplyErrno(EINVAL);
}
auto [regPtr, sz] = GetRegisterPointerAndSize(gdbRegNum, frame, fpuRegStorage);
// Decode, check for errors
if (m_commandData.size() != 2 * sz || DecodeHex(tmp, m_commandData) != sz) {
return ReplyErrno(EILSEQ);
}
std::copy(tmp, tmp + sz, regPtr);
if (gdbRegNum > 31 + 3) {
// FPU register -- must commit the FPU registers
FpuRegisterCache::GetInstance().CommitRegisters();
}
return ReplyOk();
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
/*
* This file is part of Luma3DS.
* Copyright (C) 2016-2019 Aurora Wright, TuxSH
*
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
*/
#include "hvisor_gdb_defines_internal.hpp"
#include "hvisor_gdb_packet_data.hpp"
namespace {
constexpr std::string_view SkipSpaces(std::string_view str)
{
size_t n = str.find_first_not_of("\t\v\n\f\r ");
if (n == std::string_view::npos) {
return {};
} else {
str.remove_prefix(n);
return str;
}
}
}
namespace ams::hvisor::gdb {
GDB_DEFINE_QUERY_HANDLER(Rcmd)
{
char *buf = GetInPlaceOutputBuffer();
size_t encodedLen = m_commandData.size();
if (encodedLen == 0 || encodedLen % 2 != 0) {
ReplyErrno(EILSEQ);
}
// Decode in place
if (DecodeHex(buf, m_commandData) != encodedLen / 2) {
ReplyErrno(EILSEQ);
}
// Extract command name, data
m_commandData = std::string_view{buf, encodedLen / 2};
size_t nameSize = m_commandData.find_first_of("\t\v\n\f\r ");
std::string_view commandName = m_commandData;
if (nameSize != std::string_view::npos) {
commandName.remove_suffix(commandName.size() - nameSize);
m_commandData.remove_prefix(nameSize);
m_commandData = SkipSpaces(m_commandData);
} else {
m_commandData = std::string_view{};
}
// Nothing implemented yet
(void)commandName;
return SendHexPacket("Unrecognized command.\n");
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
/*
* This file is part of Luma3DS.
* Copyright (C) 2016-2019 Aurora Wright, TuxSH
*
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
*/
#include "hvisor_gdb_defines_internal.hpp"
#include "hvisor_gdb_packet_data.hpp"
#include "../hvisor_hw_breakpoint_manager.hpp"
#include "../hvisor_sw_breakpoint_manager.hpp"
#include "../hvisor_watchpoint_manager.hpp"
namespace ams::hvisor::gdb {
GDB_DEFINE_HANDLER(ToggleStopPoint)
{
bool add = m_commandLetter == 'Z';
auto [nread, kind, addr, size] = ParseHexIntegerList<3>(m_commandData, ';');
if (nread == 0) {
return ReplyErrno(EILSEQ);
}
m_commandData.remove_prefix(nread);
// We don't support cond_list
bool persist = m_commandData == "cmds:1";
// In theory we should reject leading zeroes in "kind". Oh well...
int res;
static const cpu::DebugRegisterPair::LoadStoreControl kinds[3] = {
cpu::DebugRegisterPair::Store,
cpu::DebugRegisterPair::Load,
cpu::DebugRegisterPair::LoadStore,
};
auto &hwBpMgr = HwBreakpointManager::GetInstance();
auto &swBpMgr = SwBreakpointManager::GetInstance();
auto &wpMgr = WatchpointManager::GetInstance();
switch(kind) {
// Software breakpoint
case 0: {
if(size != 4) {
return ReplyErrno(EINVAL);
}
res = add ? swBpMgr.Add(addr, persist) : swBpMgr.Remove(addr, false);
return res == 0 ? ReplyOk() : ReplyErrno(-res);
}
// Hardware breakpoint
case 1: {
if(size != 4) {
return ReplyErrno(EINVAL);
}
res = add ? hwBpMgr.Add(addr) : hwBpMgr.Remove(addr);
return res == 0 ? ReplyOk() : ReplyErrno(-res);
}
// Watchpoints
case 2:
case 3:
case 4: {
res = add ? wpMgr.Add(addr, size, kinds[kind - 2]) : wpMgr.Remove(addr, size, kinds[kind - 2]);
return res == 0 ? ReplyOk() : ReplyErrno(-res);
}
default: {
return ReplyEmpty();
}
}
}
}

View File

@@ -0,0 +1,159 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#include <cstdio>
#include "hvisor_gdb_thread.hpp"
#include "hvisor_gdb_defines_internal.hpp"
#include "hvisor_gdb_packet_data.hpp"
#include "../hvisor_core_context.hpp"
namespace ams::hvisor::gdb {
int ConvertTidToCoreId(unsigned long tid)
{
switch (tid) {
case ULONG_MAX:
return -1;
case 0:
return currentCoreCtx->GetCoreId();
default:
return currentCoreCtx->GetCoreId() - 1;
}
}
std::optional<int> ParseConvertExactlyOneTid(std::string_view str)
{
if (str.size() == 2 && str[0] == '-' && str[1] == '1') {
return -1;
} else {
auto [n, tid] = ParseHexIntegerList<1>(str);
if (n != 0 && tid < MAX_CORE + 1) {
return ConvertTidToCoreId(tid);
} else {
return {};
}
}
}
// Hg<tid>, Hc<tid>
GDB_DEFINE_HANDLER(SetThreadId)
{
if (!m_commandData.starts_with('g') && !m_commandData.starts_with('c')) {
return ReplyErrno(EINVAL);
}
char kind = m_commandData[0];
m_commandData.remove_prefix(1);
auto coreIdOpt = ParseConvertExactlyOneTid(m_commandData);
if (!coreIdOpt) {
return ReplyErrno(EILSEQ);
}
int coreId = *coreIdOpt;
if (kind == 'g') {
if (coreId = -1) {
return ReplyErrno(EINVAL);
}
m_selectedCoreId = coreId;
MigrateRxIrq(m_selectedCoreId);
} else {
m_selectedCoreIdForContinuing = coreId;
}
return ReplyOk();
}
GDB_DEFINE_HANDLER(IsThreadAlive)
{
int coreId = ParseConvertExactlyOneTid(m_commandData).value_or(-1);
if (coreId < 0) {
return ReplyErrno(EILSEQ);
}
// Is the core off?
if (m_attachedCoreList & BIT(coreId)) {
return ReplyOk();
} else {
return ReplyErrno(ESRCH);
}
}
GDB_DEFINE_QUERY_HANDLER(CurrentThreadId)
{
GDB_CHECK_NO_CMD_DATA();
return SendFormattedPacket("QC%x", 1 + currentCoreCtx->GetCoreId());
}
GDB_DEFINE_QUERY_HANDLER(fThreadInfo)
{
GDB_CHECK_NO_CMD_DATA();
// We have made our GDB packet big enough to list all the thread ids (coreIds + 1 for each coreId)
char *buf = GetInPlaceOutputBuffer();
size_t n = 0;
for (int coreId: util::BitsOf{m_attachedCoreList}) {
n += sprintf(buf + n, "%lx,", 1u + coreId);
}
// Remove trailing comma
--n;
return SendStreamData(std::string_view{buf, n}, 0, n, true);
}
GDB_DEFINE_QUERY_HANDLER(sThreadInfo)
{
GDB_CHECK_NO_CMD_DATA();
// We have made our GDB packet big enough to list all the thread ids (coreIds + 1 for each coreId) in fThreadInfo
// Note: we assume GDB doesn't accept notifications during the sequence transfer...
return SendPacket("l");
}
GDB_DEFINE_QUERY_HANDLER(ThreadEvents)
{
if (m_commandData.size() != 1) {
return ReplyErrno(EILSEQ);
}
switch (m_commandData[0]) {
case '0':
m_catchThreadEvents = false;
return ReplyOk();
case '1':
m_catchThreadEvents = true;
return ReplyOk();
default:
return ReplyErrno(EILSEQ);
}
}
GDB_DEFINE_QUERY_HANDLER(ThreadExtraInfo)
{
int coreId = ParseConvertExactlyOneTid(m_commandData).value_or(-1);
if (coreId < 0) {
return ReplyErrno(EILSEQ);
}
size_t n = sprintf(m_workBuffer, "TODO");
return SendHexPacket(m_workBuffer, n);
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "hvisor_gdb_context.hpp"
namespace ams::hvisor::gdb {
int ConvertTidToCoreId(unsigned long tid);
std::optional<int> ParseConvertExactlyOneTid(std::string_view str);
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
/*
* This file is part of Luma3DS.
* Copyright (C) 2016-2019 Aurora Wright, TuxSH
*
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
*/
#include "hvisor_gdb_defines_internal.hpp"
#include "hvisor_gdb_packet_data.hpp"
namespace ams::hvisor::gdb {
GDB_DEFINE_HANDLER(VerboseCommand)
{
// Extract name
char delim = ':';
size_t delimPos = m_commandData.find_first_of(";:");
std::string_view cmdName = m_commandData;
if (delimPos != std::string_view::npos) {
delim = m_commandData[delimPos];
cmdName.remove_suffix(cmdName.size() - delimPos);
m_commandData.remove_prefix(delimPos + 1);
}
if (cmdName == "Cont?") {
GDB_VERBOSE_HANDLER(ContinueSupported)();
} else if (cmdName == "Cont") {
GDB_VERBOSE_HANDLER(Continue)();
} else if (cmdName == "CtrlC") {
GDB_VERBOSE_HANDLER(CtrlC)();
} else if (cmdName == "MustReplyEmpty") {
return HandleUnsupported();
} else if (cmdName == "Stopped") {
return GDB_VERBOSE_HANDLER(Stopped)();
} else {
return HandleUnsupported(); // No handler found!
}
}
GDB_DEFINE_VERBOSE_HANDLER(ContinueSupported)
{
return SendPacket("vCont;c;C;s;S;t;r");
}
}

View File

@@ -0,0 +1,143 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
/*
* This file is part of Luma3DS.
* Copyright (C) 2016-2019 Aurora Wright, TuxSH
*
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
*/
#include "hvisor_gdb_defines_internal.hpp"
#include "hvisor_gdb_packet_data.hpp"
namespace {
std::string_view GenerateTargetXml(char *buf)
{
int pos;
const char *hdr = "<?xml version=\"1.0\"?><!DOCTYPE feature SYSTEM \"gdb-target.dtd\"><target>";
const char *cpuDescBegin = "<feature name=\"org.gnu.gdb.aarch64.core\">";
const char *cpuDescEnd =
"<reg name=\"sp\" bitsize=\"64\" type=\"data_ptr\"/>"
"<reg name=\"pc\" bitsize=\"64\" type=\"code_ptr\"/>"
"<reg name=\"cpsr\" bitsize=\"32\"/></feature>";
const char *fpuDescBegin =
"<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=\"v8u\" type=\"uint16\" count=\"8\"/>"
"<vector id=\"v8i\" type=\"int16\" 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=\"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>";
const char *fpuDescEnd = "<reg name=\"fpsr\" bitsize=\"32\"/>\r\n<reg name=\"fpcr\" bitsize=\"32\"/>\r\n</feature>";
const char *footer = "</target>";
std::strcpy(buf, hdr);
// CPU registers
std::strcat(buf, cpuDescBegin);
pos = static_cast<int>(std::strlen(buf));
for (u32 i = 0; i < 31; i++) {
pos += std::sprintf(buf + pos, "<reg name=\"x%u\" bitsize=\"64\"/>", i);
}
std::strcat(buf, cpuDescEnd);
std::strcat(buf, fpuDescBegin);
pos = static_cast<int>(std::strlen(buf));
for (u32 i = 0; i < 32; i++) {
pos += std::sprintf(buf + pos, "<reg name=\"v%u\" bitsize=\"128\" type=\"aarch64v\"/>", i);
}
std::strcat(buf, fpuDescEnd);
std::strcat(buf, footer);
return std::string_view{buf};
}
}
namespace ams::hvisor::gdb {
GDB_DEFINE_XFER_HANDLER(Features)
{
if (write || annex != "target.xml") {
return ReplyEmpty();
}
// Generate the target xml on-demand
// This is a bit whack, we rightfully assume that GDB won't sent any other command during the stream transfer
if (m_targetXml.empty()) {
m_targetXml = GenerateTargetXml(m_workBuffer);
}
int n = SendStreamData(m_targetXml, offset, length, false);
// Transfer ended
if(offset + length >= m_targetXml.size()) {
m_targetXml = {};
}
return n;
}
GDB_DEFINE_QUERY_HANDLER(Xfer)
{
// e.g. qXfer:features:read:annex:offset,length
// Split
auto [cmd, directionStr, annex, offsetlen] = SplitString<4>(m_commandData, ':');
if (offsetlen.empty()) {
return ReplyErrno(EILSEQ);
}
// Check direction
bool isWrite;
if (directionStr == "read") {
isWrite = false;
} else if (directionStr == "write") {
isWrite = true;
} else {
return ReplyErrno(EILSEQ);
}
// Get offset and length
auto [nread, off, len] = ParseHexIntegerList<2>(offsetlen, isWrite ? ':' : '\0');
if (nread == 0) {
return ReplyErrno(EILSEQ);
}
// Get data/nothing
m_commandData = offsetlen;
m_commandData.remove_prefix(nread);
// Run command
if (cmd == "features") {
return GDB_XFER_HANDLER(Features)(isWrite, annex, off, len);
} else {
return HandleUnsupported();
}
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#include "hvisor_core_context.hpp"
#include "cpu/hvisor_cpu_instructions.hpp"
namespace ams::hvisor {
std::array<CoreContext, MAX_CORE> CoreContext::instances{};
std::atomic<u32> CoreContext::activeCoreMask{};
bool CoreContext::coldboot = true;
void CoreContext::InitializeCoreInstance(u32 coreId, bool isBootCore, u64 argument)
{
CoreContext &instance = instances[coreId];
instance.m_coreId = coreId;
instance.m_bootCore = isBootCore;
instance.m_kernelArgument = argument;
if (isBootCore && instance.m_kernelEntrypoint == 0) {
instance.m_kernelEntrypoint = initialKernelEntrypoint;
}
currentCoreCtx = &instance;
cpu::dmb();
}
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "defines.hpp"
namespace ams::hvisor {
struct ExceptionStackFrame;
class CoreContext;
register CoreContext *currentCoreCtx asm("x18");
class alignas(64) CoreContext final {
// This should be 64-byte big
NON_COPYABLE(CoreContext);
NON_MOVEABLE(CoreContext);
private:
static std::array<CoreContext, MAX_CORE> instances;
static std::atomic<u32> activeCoreMask;
static bool coldboot; // "coldboot" to be 'true' on init & thus not in BSS
// start.s
static uintptr_t initialKernelEntrypoint;
private:
ExceptionStackFrame *m_guestFrame = nullptr;
u64 m_kernelArgument = 0;
uintptr_t m_kernelEntrypoint = 0;
u32 m_coreId = 0;
bool m_bootCore = false;
// Debug features
bool m_wasPaused = false;
uintptr_t m_steppingRangeStartAddr = 0;
uintptr_t m_steppingRangeEndAddr = 0;
// Timer stuff
u64 m_totalTimeInHypervisor = 0;
u64 m_emulPtimerCval = 0;
private:
constexpr CoreContext() = default;
public:
static void InitializeCoreInstance(u32 coreId, bool isBootCore, u64 argument);
static CoreContext &GetInstanceFor(u32 coreId) { return instances[coreId]; }
static u32 GetActiveCoreMask() { return activeCoreMask.load(); }
static u32 SetCurrentCoreActive()
{
activeCoreMask |= BIT(currentCoreCtx->m_coreId);
}
static bool IsColdboot() { return coldboot; }
public:
constexpr ExceptionStackFrame *GetGuestFrame() const { return m_guestFrame; }
constexpr void SetGuestFrame(ExceptionStackFrame *frame) { m_guestFrame = frame; }
constexpr u64 GetKernelArgument() const { return m_kernelArgument; }
constexpr u64 GetKernelEntrypoint() const { return m_kernelEntrypoint; }
constexpr u32 GetCoreId() const { return m_coreId; }
constexpr bool IsBootCore() const { return m_bootCore; }
constexpr u64 SetKernelEntrypoint(uintptr_t ep, bool warmboot = false)
{
if (warmboot) {
// No race possible, only possible transition is 1->0 and we only really check IsColdboot() at init time
// And CPU_SUSPEND should only be called with only one core left.
coldboot = false;
}
m_kernelEntrypoint = ep;
}
constexpr bool WasPaused() const { return m_wasPaused; }
constexpr void SetPausedFlag(bool wasPaused) { m_wasPaused = wasPaused; }
constexpr auto GetSteppingRange() const
{
return std::tuple{m_steppingRangeStartAddr, m_steppingRangeEndAddr};
}
constexpr void SetSteppingRange(uintptr_t startAddr, uintptr_t endAddr)
{
m_steppingRangeStartAddr = startAddr;
m_steppingRangeEndAddr = endAddr;
}
constexpr u64 GetTotalTimeInHypervisor() const { return m_totalTimeInHypervisor; }
constexpr void IncrementTotalTimeInHypervisor(u64 timeDelta) { m_totalTimeInHypervisor += timeDelta; }
constexpr u64 GetEmulPtimerCval() const { return m_emulPtimerCval; }
constexpr void SetEmulPtimerCval(u64 cval) { m_emulPtimerCval = cval; }
};
}

Some files were not shown because too many files have changed in this diff Show More