Compare commits

..

308 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
Michael Scire
408b81b881 docs: fix missing meme (important) 2021-02-02 12:17:00 -08:00
Michael Scire
d854b94382 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "90d85295"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "90d85295"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2021-02-02 12:16:49 -08:00
Michael Scire
63440cab18 ams: bump version to 0.18.0 2021-02-02 12:15:53 -08:00
Michael Scire
4fa585a23f docs: expand dns_mitm wildcard wording 2021-02-02 11:41:49 -08:00
Michael Scire
e521ae805d dns.mitm: add documentation 2021-02-02 11:41:49 -08:00
Michael Scire
6950989552 dns.mitm: support % in hosts file as stand-in for environment identifier 2021-02-02 11:41:49 -08:00
Michael Scire
ffbdf29c10 dns.mitm: fix ABORT_UNLESS -> ABORT 2021-02-02 11:41:49 -08:00
Michael Scire
135d42ffee dns.mitm: hint = {} fix in options 2021-02-02 11:41:49 -08:00
Michael Scire
1306d03136 dns.mitm: add GetAddrInfo redir, AtmosphereReloadHostsFile, debug logging control 2021-02-02 11:41:49 -08:00
Michael Scire
97aa209c43 dns.mitm: fix inverted hostname detection 2021-02-02 11:41:49 -08:00
Michael Scire
4ce2a6deb3 meso: update for new fatal encoding 2021-02-02 11:41:49 -08:00
Michael Scire
e45d8cd7d8 ams: take three tries to edit a structure 2021-02-02 11:41:49 -08:00
Michael Scire
79211e1159 ams: afsr0 -> 32 bits in fatal error 2021-02-02 11:41:49 -08:00
Michael Scire
ca8e8ce487 ams: fix fatal error context 2021-02-02 11:41:49 -08:00
Michael Scire
b30311be65 dns.mitm: support wildcards in hosts 2021-02-02 11:41:49 -08:00
Michael Scire
6694d8c2d9 dns.mitm: edit default behavior/hosts filenames 2021-02-02 11:41:49 -08:00
Michael Scire
658389fc60 dns.mitm: fix issue in host file parsing 2021-02-02 11:41:49 -08:00
Michael Scire
d6477cf024 dns.mitm: parse redirections from hosts file 2021-02-02 11:41:49 -08:00
Michael Scire
400f5142ee dns: implement GetHostByName redirection (backend TODO) 2021-02-02 11:41:49 -08:00
Michael Scire
8bf8df43e2 dns: skeleton passthrough mitm 2021-02-02 11:41:49 -08:00
Michael Scire
a28c60970f kern: fix scheduler update semantics in KLightLock::LockSlowPath 2021-01-28 17:47:26 -08:00
Michael Scire
b5f72b9f20 fssystem: fix bug in BufferedStorage 2021-01-28 14:28:45 -08:00
Michael Scire
8d10584a51 result: always prefer diag::Abort to fatal 2021-01-28 00:15:24 -08:00
Michael Scire
366c265ab2 dd: fix include, note gcc-11 TODO 2021-01-27 23:41:35 -08:00
Michael Scire
42964992d1 dmnt: ro:dmnt available on all sysvers 2021-01-27 14:44:25 -08:00
Michael Scire
4a82d9bf28 sm: supercede ams extension via DetachClient 2021-01-27 14:36:15 -08:00
Michael Scire
4190281b2f ams: communicate status to libnx 2021-01-27 14:20:30 -08:00
Michael Scire
48830d190f timespan: explicitly require TimeSpanType as is_pod 2021-01-25 08:27:42 -08:00
Michael Scire
3389aaefc3 strat: update for revised libnx weak alloc funcs 2021-01-22 03:52:10 -08:00
Michael Scire
f8f987aa8d boot: remove references to memalign/malloc 2021-01-20 23:39:31 -08:00
Michael Scire
e87e146112 ams_mitm: update for new sf semantics 2021-01-20 23:39:31 -08:00
Michael Scire
5751bcc117 dmnt: update for new sf semantics 2021-01-20 23:39:31 -08:00
Michael Scire
eb1e979257 creport: update for new sf semantics 2021-01-20 23:39:31 -08:00
Michael Scire
204539664b boot2: update for new sf semantics 2021-01-20 23:39:31 -08:00
Michael Scire
f5c6736431 boot: update for new sf-semantics 2021-01-20 23:39:31 -08:00
Michael Scire
d00ebaa28a fatal: update screen task to use native window directly 2021-01-20 23:39:31 -08:00
Michael Scire
402e4d1adb fatal: wip (pending libnx pr) update for new sf semantics 2021-01-20 23:39:31 -08:00
Michael Scire
ed7c0605f9 jpegdec: note libjpeg-turbo TODO 2021-01-20 23:39:31 -08:00
Michael Scire
c848a830ee libstrat: move weak HasLaunchedBootProgram to non-lto object file 2021-01-20 23:39:31 -08:00
Michael Scire
dbe8add4f4 loader: fix failure-to-early-return in launch record management 2021-01-20 23:39:31 -08:00
Michael Scire
e4e278bb3d util::unique_lock, update loader to new sf semantics 2021-01-20 23:39:31 -08:00
Michael Scire
3761f80592 string_view: remove now unecessary comment 2021-01-20 23:39:31 -08:00
Michael Scire
9878c18e47 ncm: update for new sf semantics 2021-01-20 23:39:31 -08:00
Michael Scire
3bb94aa146 util::string_view, update pgl for new sf semantics 2021-01-20 23:39:31 -08:00
Michael Scire
83c04fa5d7 pm: update for new sf semantics 2021-01-20 23:39:31 -08:00
Michael Scire
dc4ee1a5bc erpt: update for new sf semantics 2021-01-20 23:39:31 -08:00
Michael Scire
5191f0e305 ro: reduce memory usage by excising (unused) std::malloc 2021-01-20 23:39:31 -08:00
Michael Scire
170034aed3 ro: update for new sf semantics 2021-01-20 23:39:31 -08:00
Michael Scire
21236020cb sm, spl: update to use new sf semantics 2021-01-20 23:39:31 -08:00
Michael Scire
f06de12bea libstrat: convert to experimental new (super-accurate) sf allocation semantics 2021-01-20 23:39:31 -08:00
250 changed files with 24355 additions and 3165 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

@@ -42,6 +42,16 @@
; enabled or disabled.
; 0 = Disabled (not debug mode), 1 = Enabled (debug mode)
; enable_am_debug_mode = u8!0x0
; Controls whether dns.mitm is enabled
; 0 = Disabled, 1 = Enabled
; enable_dns_mitm = u8!0x1
; Controls whether dns.mitm uses the default redirections in addition to
; whatever is specified in the user's hosts file.
; 0 = Disabled (use hosts file contents), 1 = Enabled (use defaults and hosts file contents)
; add_defaults_to_dns_hosts = u8!0x1
; Controls whether dns.mitm logs to the sd card for debugging
; 0 = Disabled, 1 = Enabled
; enable_dns_mitm_debug_log = u8!0x0
[hbloader]
; Controls the size of the homebrew heap when running as applet.
; If set to zero, all available applet memory is used as heap.

View File

@@ -1,4 +1,30 @@
# 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.
+ By default atmosphère redirects resolution requests for official telemetry servers to a loopback address.
+ Documentation on how to configure `dns.mitm` to meet your more specific needs may be found [here](https://github.com/Atmosphere-NX/Atmosphere/blob/master/docs/features/dns_mitm.md).
+ The service framework API (`sf`) was refactored to be more accurate to official logic and greatly reduce memory requirements.
+ The comparison of atmosphère module memory usage versus Nintendo's found [here](https://github.com/Atmosphere-NX/Atmosphere/wiki/Memory-Comparisons) was updated to reflect this.
+ **Please Note**: If you are a developer using the libstratosphere service APIs, some updating may be required. Contact SciresM#0524 on discord for assistance if required.
+ A number of deprecations were removed, following a general codebase cleanup:
+ The `sm` extension to not unregister services on connection close was superseded by official opt-in logic in 11.0.0, and has been removed in favor of official logic.
+ This should have zero impact on users.
+ The temporary `hid-mitm` added in 0.9.0 has finally been removed, following over a year of deprecation.
+ There shouldn't be any homebrew in use still affected by this, but the situation will be monitored.
+ If this is somehow still a real issue, an unaffiliated hid mitm sysmodule providing the same functionality can be created and released, separate from atmosphère itself.
+ Several issues were fixed, and usability and stability were improved.
## 0.17.1
+ A number of atmosphère's modules were using more memory than their Nintendo equivalent's in 0.17.0; a number of code generatio tweaks have been applied to fix this across the board.
+ A detailed comparison of atmosphère module memory usage versus Nintendo's was made and can be found [here](https://github.com/Atmosphere-NX/Atmosphere/wiki/Memory-Comparisons).

View File

@@ -33,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).

53
docs/features/dns_mitm.md Normal file
View File

@@ -0,0 +1,53 @@
# DNS.mitm
As of 0.18.0, atmosphère provides a mechanism for redirecting DNS resolution requests.
By default, atmosphère redirects resolution requests for official telemetry servers, redirecting them to a loopback address.
## Hosts files
DNS.mitm can be configured through the usage of a slightly-extended `hosts` file format, which is parsed only once on system startup.
In particular, hosts files parsed by DNS.mitm have the following extensions to the usual format:
+ `*` 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
Atmosphère will try to read hosts from the following file paths, in order, stopping once it successfully performs a file read:
+ (emummc only) `/atmosphere/hosts/emummc_%04lx.txt`, formatted with the emummc's id number (see `emummc.ini`).
+ (emummc only) `/atmosphere/hosts/emummc.txt`.
+ (sysmmc only) `/atmosphere/hosts/sysmmc.txt`.
+ `/atmosphere/hosts/default.txt`
If `/atmosphere/hosts/default.txt` does not exist, atmosphère will create it to contain the defaults.
### Atmosphère defaults
By default, atmosphère's default redirections are parsed **in addition to** the contents of the loaded hosts file.
This is equivalent to thinking of the loaded hosts file as having the atmosphère defaults prepended to it.
This setting is considered desirable, because it minimizes the telemetry risks if a user forgets to update a custom hosts file on a system update which changes the telemetry servers.
This behavior can be opted-out from by setting `atmosphere!add_defaults_to_dns_hosts = u8!0x0` in `system_settings.ini`.
The current default redirections are:
```
# Nintendo telemetry servers
127.0.0.1 receive-%.dg.srv.nintendo.net receive-%.er.srv.nintendo.net
```
## Debugging
On startup (or on hosts file re-parse), DNS.mitm will log both what hosts file it selected and the contents of all redirections it parses to `/atmosphere/logs/dns_mitm_startup.log`.
In addition, if the user sets `atmosphere!enable_dns_mitm_debug_log = u8!0x1` in `system_settings.ini`, DNS.mitm will log all requests to GetHostByName/GetAddrInfo to `/atmosphere/logs/dns_mitm_debug.log`. All redirections will be noted when they occur.
## Opting-out of DNS.mitm entirely
If you wish to disable DNS.mitm entirely, `system_settings.ini` can be edited to set `atmosphere!enable_dns_mitm = u8!0x0`.

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 = 6c11c07e2a7f03952a4e70eb89b47bf528de39c6
parent = 9e104bb83f1302e9f126542fbf57c7f666aae953
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

@@ -314,12 +314,12 @@ namespace ams::kern::board::nintendo::nx {
g_secure_applet_memory_used = false;
}
u64 GetVersionIdentifier() {
u64 value = kern::GetTargetFirmware();
value |= static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MICRO) << 32;
value |= static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MINOR) << 40;
value |= static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MAJOR) << 48;
value |= static_cast<u64>('M') << 56;
u32 GetVersionIdentifier() {
u32 value = 0;
value |= static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MICRO) << 0;
value |= static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MINOR) << 8;
value |= static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MAJOR) << 16;
value |= static_cast<u64>('M') << 24;
return value;
}
@@ -584,8 +584,8 @@ namespace ams::kern::board::nintendo::nx {
f_ctx->module_base = KMemoryLayout::GetKernelCodeRegionExtents().GetAddress();
/* Set afsr1. */
f_ctx->afsr0 = 0;
f_ctx->afsr1 = GetVersionIdentifier();
f_ctx->afsr0 = GetVersionIdentifier();
f_ctx->afsr1 = static_cast<u32>(kern::GetTargetFirmware());
/* Set efsr/far. */
f_ctx->far = cpu::GetFarEl1();

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

@@ -43,7 +43,6 @@ namespace ams::kern {
if (owner_thread->IsSuspended()) {
owner_thread->ContinueIfHasKernelWaiters();
KScheduler::SetSchedulerUpdateNeeded();
}
}
@@ -53,6 +52,7 @@ namespace ams::kern {
KThread *owner_thread = cur_thread->GetLockOwner();
if (AMS_UNLIKELY(owner_thread)) {
owner_thread->RemoveWaiter(cur_thread);
KScheduler::SetSchedulerUpdateNeeded();
}
}
}

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

@@ -61,6 +61,7 @@
#include <stratosphere/ncm.hpp>
#include <stratosphere/nim.hpp>
#include <stratosphere/ns.hpp>
#include <stratosphere/nsd.hpp>
#include <stratosphere/patcher.hpp>
#include <stratosphere/pcv.hpp>
#include <stratosphere/pgl.hpp>
@@ -74,9 +75,11 @@
#include <stratosphere/settings.hpp>
#include <stratosphere/sf.hpp>
#include <stratosphere/sm.hpp>
#include <stratosphere/socket.hpp>
#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

@@ -22,6 +22,9 @@ namespace ams::emummc {
/* Get whether emummc is active. */
bool IsActive();
/* Get the active emummc id. */
u32 GetActiveId();
/* Get Nintendo redirection path. */
const char *GetNintendoDirPath();

View File

@@ -113,6 +113,9 @@ namespace ams::impl {
AMS_DEFINE_SYSTEM_THREAD(21, erpt, Main);
AMS_DEFINE_SYSTEM_THREAD(21, erpt, IpcServer);
/* socket. */
AMS_DEFINE_SYSTEM_THREAD(29, socket, ResolverIpcServer);
/* jpegdec. */
AMS_DEFINE_SYSTEM_THREAD(21, jpegdec, Main);

View File

@@ -13,15 +13,15 @@
* 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_types.hpp>
namespace ams::dd {
using ProcessHandle = ::Handle;
/* TODO gcc-11: using MemoryPermission = os::MemoryPermission; using enum os::MemoryPermission; */
enum MemoryPermission {
MemoryPermission_None = 0,
MemoryPermission_ReadOnly = (1u << 0),

View File

@@ -0,0 +1,18 @@
/*
* 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/nsd/nsd_types.hpp>
#include <stratosphere/nsd/impl/device/nsd_device.hpp>

View File

@@ -13,24 +13,12 @@
* 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.hpp>
#include <stratosphere/nsd/nsd_types.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::nsd::impl::device {
/* FIXME: print via UART */
const EnvironmentIdentifier &GetEnvironmentIdentifierFromSettings();
va_end(list);
}

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.hpp>
namespace ams::nsd {
struct EnvironmentIdentifier {
static constexpr size_t Size = 8;
char value[Size];
constexpr friend bool operator==(const EnvironmentIdentifier &lhs, const EnvironmentIdentifier &rhs) {
return util::Strncmp(lhs.value, rhs.value, Size) == 0;
}
constexpr friend bool operator!=(const EnvironmentIdentifier &lhs, const EnvironmentIdentifier &rhs) {
return !(lhs == rhs);
}
};
constexpr inline const EnvironmentIdentifier EnvironmentIdentifierOfProductDevice = {"lp1"};
constexpr inline const EnvironmentIdentifier EnvironmentIdentifierOfNotProductDevice = {"dd1"};
}

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,22 @@
/*
* 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 <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stratosphere/socket/socket_types.hpp>
#include <stratosphere/socket/socket_errno.hpp>
#include <stratosphere/socket/socket_api.hpp>

View File

@@ -0,0 +1,31 @@
/*
* 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/socket/socket_types.hpp>
#include <stratosphere/socket/socket_errno.hpp>
namespace ams::socket {
Errno GetLastError();
void SetLastError(Errno err);
u32 InetHtonl(u32 host);
u16 InetHtons(u16 host);
u32 InetNtohl(u32 net);
u16 InetNtohs(u16 net);
}

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 <vapours.hpp>
namespace ams::socket {
enum class Errno : u32 {
ESuccess = 0,
/* ... */
ENoSpc = 28,
/* ... */
};
enum class HErrno : s32 {
Netdb_Internal = -1,
Netdb_Success = 0,
Host_Not_Found = 1,
Try_Again = 2,
No_Recovery = 3,
No_Data = 4,
No_Address = No_Data,
};
enum class AiErrno : u32 {
EAi_Success = 0,
/* ... */
};
constexpr inline bool operator!(Errno e) { return e == Errno::ESuccess; }
constexpr inline bool operator!(HErrno e) { return e == HErrno::Netdb_Success; }
constexpr inline bool operator!(AiErrno e) { return e == AiErrno::EAi_Success; }
}

View File

@@ -0,0 +1,144 @@
/*
* 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>
namespace ams::socket {
using InAddrT = u32;
using InPortT = u16;
using SockLenT = u32;
using NfdsT = u64;
using FdMask = u64;
constexpr inline unsigned int FdSetSize = 0x400;
template<u32 A, u32 B, u32 C, u32 D>
constexpr inline InAddrT EncodeInAddr = util::ConvertToBigEndian(InAddrT{(A << 24) | (B << 16) | (C << 8) | (D << 0)});
constexpr inline InAddrT InAddr_Any = EncodeInAddr< 0, 0, 0, 0>;
constexpr inline InAddrT InAddr_Broadcast = EncodeInAddr<255, 255, 255, 255>;
constexpr inline InAddrT InAddr_None = EncodeInAddr<255, 255, 255, 255>;
constexpr inline InAddrT InAddr_Loopback = EncodeInAddr<127, 0, 0, 1>;
enum class Protocol : s32 {
IpProto_Ip = 0,
IpProto_Icmp = 1,
IpProto_Tcp = 6,
IpProto_Udp = 17,
IpProto_UdpLite = 136,
IpProto_Raw = 255,
IpProto_Max = 256,
};
enum class Type : u32 {
Sock_Default = 0,
Sock_Stream = 1,
Sock_Dgram = 2,
Sock_Raw = 3,
Sock_SeqPacket = 5,
Sock_NonBlock = 0x20000000,
};
enum class Family : u8 {
Af_Unspec = 0,
Pf_Unspec = Af_Unspec,
Af_Inet = 2,
Pf_Inet = Af_Inet,
Af_Route = 17,
Pf_Route = Af_Route,
Af_Link = 18,
Pf_Link = Af_Link,
Af_Inet6 = 28,
Pf_Inet6 = Af_Inet6,
Af_Max = 42,
Pf_Max = Af_Max
};
struct HostEnt {
char *h_name;
char **h_aliases;
Family h_addrtype;
int h_length;
char **h_addr_list;
};
struct InAddr {
InAddrT s_addr;
};
enum class AddrInfoFlag : u32 {
Ai_None = (0 << 0),
Ai_Passive = (1 << 0),
Ai_CanonName = (1 << 1),
Ai_NumericHost = (1 << 2),
Ai_NumericServ = (1 << 3),
Ai_AddrConfig = (1 << 10),
};
struct SockAddr {
u8 sa_len;
Family sa_family;
char sa_data[14];
};
struct SockAddrIn {
u8 sin_len;
Family sin_family;
InPortT sin_port;
InAddr sin_addr;
u8 sin_zero[8];
};
static_assert(sizeof(SockAddr) == sizeof(SockAddrIn));
struct AddrInfo {
AddrInfoFlag ai_flags;
Family ai_family;
Type ai_socktype;
Protocol ai_protocol;
SockLenT ai_addrlen;
SockAddr *ai_addr;
char *ai_canonname;
AddrInfo *ai_next;
};
#define AMS_SOCKET_IMPL_DEFINE_ENUM_OPERATORS(__ENUM__) \
constexpr inline __ENUM__ operator | (__ENUM__ lhs, __ENUM__ rhs) { return static_cast<__ENUM__>(static_cast<std::underlying_type_t<__ENUM__>>(lhs) | static_cast<std::underlying_type_t<__ENUM__>>(rhs)); } \
constexpr inline __ENUM__ operator |=(__ENUM__ &lhs, __ENUM__ rhs) { return lhs = lhs | rhs; } \
constexpr inline __ENUM__ operator & (__ENUM__ lhs, __ENUM__ rhs) { return static_cast<__ENUM__>(static_cast<std::underlying_type_t<__ENUM__>>(lhs) & static_cast<std::underlying_type_t<__ENUM__>>(rhs)); } \
constexpr inline __ENUM__ operator &=(__ENUM__ &lhs, __ENUM__ rhs) { return lhs = lhs & rhs; } \
constexpr inline __ENUM__ operator ^ (__ENUM__ lhs, __ENUM__ rhs) { return static_cast<__ENUM__>(static_cast<std::underlying_type_t<__ENUM__>>(lhs) ^ static_cast<std::underlying_type_t<__ENUM__>>(rhs)); } \
constexpr inline __ENUM__ operator ^=(__ENUM__ &lhs, __ENUM__ rhs) { return lhs = lhs ^ rhs; } \
constexpr inline __ENUM__ operator ~ (__ENUM__ e) { return static_cast<__ENUM__>(~static_cast<std::underlying_type_t<__ENUM__>>(e)); }
AMS_SOCKET_IMPL_DEFINE_ENUM_OPERATORS(Type)
AMS_SOCKET_IMPL_DEFINE_ENUM_OPERATORS(AddrInfoFlag)
#undef AMS_SOCKET_IMPL_DEFINE_ENUM_OPERATORS
}

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

@@ -57,10 +57,10 @@ namespace ams::emummc {
};
/* Globals. */
os::Mutex g_lock(false);
ExosphereConfig g_exo_config;
bool g_is_emummc;
bool g_has_cached;
constinit os::SdkMutex g_lock;
constinit ExosphereConfig g_exo_config;
constinit bool g_is_emummc;
constinit bool g_has_cached;
/* Helpers. */
void CacheValues() {
@@ -110,6 +110,12 @@ namespace ams::emummc {
return g_is_emummc;
}
/* Get the active emummc id. */
u32 GetActiveId() {
CacheValues();
return g_exo_config.base_cfg.id;
}
/* Get Nintendo redirection path. */
const char *GetNintendoDirPath() {
CacheValues();

View File

@@ -73,11 +73,11 @@ namespace ams {
ams_ctx.sp = ctx->sp.x;
ams_ctx.pc = ctx->pc.x;
ams_ctx.pstate = ctx->pstate;
ams_ctx.afsr0 = ctx->afsr0;
ams_ctx.afsr1 = (static_cast<u64>(::ams::exosphere::GetVersion(ATMOSPHERE_RELEASE_VERSION)) << 32) | static_cast<u64>(hos::GetVersion());
ams_ctx.afsr0 = static_cast<u32>(::ams::exosphere::GetVersion(ATMOSPHERE_RELEASE_VERSION));
if (svc::IsKernelMesosphere()) {
ams_ctx.afsr1 |= (static_cast<u64>('M') << (BITSIZEOF(u64) - BITSIZEOF(u8)));
ams_ctx.afsr0 |= (static_cast<u32>('M') << (BITSIZEOF(u32) - BITSIZEOF(u8)));
}
ams_ctx.afsr1 = static_cast<u32>(hos::GetVersion());
ams_ctx.far = ctx->far.x;
ams_ctx.report_identifier = armGetSystemTick();

View File

@@ -899,7 +899,7 @@ namespace ams::fssystem::save {
const s64 cur_offset_end = offset + *size;
size_t cur_size = 0;
if (!util::IsAligned(offset, this->block_size)) {
if (!util::IsAligned(cur_offset_end, this->block_size)) {
const s64 aligned_size = cur_offset_end - util::AlignDown(cur_offset_end, this->block_size);
cur_size = std::min(aligned_size, static_cast<s64>(*size));
} else if (*size < this->block_size) {

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

@@ -76,7 +76,7 @@ namespace ams::hos {
const u32 major = (hos_version_val >> 24) & 0xFF;
const u32 minor = (hos_version_val >> 16) & 0xFF;
const u32 micro = (hos_version_val >> 8) & 0xFF;
hosversionSet(MAKEHOSVERSION(major, minor, micro));
hosversionSet((BIT(31)) | (MAKEHOSVERSION(major, minor, micro)));
}
}

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

@@ -0,0 +1,49 @@
/*
* 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>
namespace ams::nsd::impl::device {
namespace {
constexpr const char SettingsName[] = "nsd";
constexpr const char SettingsKey[] = "environment_identifier";
constinit os::SdkMutex g_environment_identifier_lock;
constinit EnvironmentIdentifier g_environment_identifier = {};
constinit bool g_determined_environment_identifier = false;
}
const EnvironmentIdentifier &GetEnvironmentIdentifierFromSettings() {
std::scoped_lock lk(g_environment_identifier_lock);
if (!g_determined_environment_identifier) {
/* Check size. */
AMS_ABORT_UNLESS(settings::fwdbg::GetSettingsItemValueSize(SettingsName, SettingsKey) != 0);
/* Get value. */
const size_t size = settings::fwdbg::GetSettingsItemValue(g_environment_identifier.value, EnvironmentIdentifier::Size, SettingsName, SettingsKey);
AMS_ABORT_UNLESS(size < EnvironmentIdentifier::Size);
AMS_ABORT_UNLESS(g_environment_identifier == EnvironmentIdentifierOfProductDevice || g_environment_identifier == EnvironmentIdentifierOfNotProductDevice);
g_determined_environment_identifier = true;
}
return g_environment_identifier;
}
}

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

@@ -15,25 +15,12 @@
*/
#include <stratosphere.hpp>
namespace ams::result {
extern bool CallFatalOnResultAssertion;
}
namespace ams::result::impl {
NORETURN WEAK_SYMBOL void OnResultAbort(const char *file, int line, const char *func, const char *expr, Result result) {
/* Assert that we should call fatal on result assertion. */
/* If we shouldn't fatal, this will abort(); */
/* If we should, we'll continue onwards. */
if (!ams::result::CallFatalOnResultAssertion) {
::ams::diag::AbortImpl(file, line, func, expr, result.GetValue(), "Result Abort: %203d-%04d", result.GetModule(), result.GetDescription());
}
/* TODO: ams::fatal:: */
fatalThrow(result.GetValue());
::ams::diag::AbortImpl(file, line, func, expr, result.GetValue(), "Result Abort: %203d-%04d", result.GetModule(), result.GetDescription());
AMS_INFINITE_LOOP();
__builtin_unreachable();
}
NORETURN WEAK_SYMBOL void OnResultAbort(Result result) {
@@ -41,16 +28,9 @@ namespace ams::result::impl {
}
NORETURN WEAK_SYMBOL void OnResultAssertion(const char *file, int line, const char *func, const char *expr, Result result) {
/* Assert that we should call fatal on result assertion. */
/* If we shouldn't fatal, this will assert(); */
/* If we should, we'll continue onwards. */
if (!ams::result::CallFatalOnResultAssertion) {
::ams::diag::AssertionFailureImpl(file, line, func, expr, result.GetValue(), "Result Assertion: %203d-%04d", result.GetModule(), result.GetDescription());
}
/* TODO: ams::fatal:: */
fatalThrow(result.GetValue());
::ams::diag::AssertionFailureImpl(file, line, func, expr, result.GetValue(), "Result Assertion: %203d-%04d", result.GetModule(), result.GetDescription());
AMS_INFINITE_LOOP();
__builtin_unreachable();
}
NORETURN WEAK_SYMBOL void OnResultAssertion(Result result) {

View File

@@ -28,6 +28,11 @@ static Result _smAtmosphereCmdInServiceNameNoOut(SmServiceName name, Service *sr
return serviceDispatchIn(srv, cmd_id, name);
}
static Result _smAtmosphereDetachClient(Service *srv) {
u64 pid_placeholder = 0;
return serviceDispatchIn(srv, 4, pid_placeholder, .in_send_pid = true);
}
Result smAtmosphereHasService(bool *out, SmServiceName name) {
return _smAtmosphereCmdHas(out, name, 65100);
}
@@ -81,6 +86,10 @@ Result smAtmosphereOpenSession(Service *out) {
}
void smAtmosphereCloseSession(Service *srv) {
Result rc = _smAtmosphereDetachClient(srv);
if (R_FAILED(rc)) {
svcBreak(BreakReason_Panic, (uintptr_t)&rc, sizeof(rc));
}
serviceClose(srv);
}

View File

@@ -30,7 +30,10 @@ namespace ams::sm::impl {
std::scoped_lock lk(GetUserSessionMutex());
{
R_ABORT_UNLESS(smInitialize());
ON_SCOPE_EXIT { smExit(); };
ON_SCOPE_EXIT {
R_ABORT_UNLESS(smDetachClient());
smExit();
};
return f();
}

View File

@@ -0,0 +1,25 @@
/*
* 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::socket::impl {
void *Alloc(size_t size);
void *Calloc(size_t num, size_t size);
void Free(void *ptr);
}

View File

@@ -0,0 +1,28 @@
/*
* 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>
namespace ams::socket::impl {
Errno GetLastError();
void SetLastError(Errno err);
u32 InetHtonl(u32 host);
u16 InetHtons(u16 host);
u32 InetNtohl(u32 net);
u16 InetNtohs(u16 net);
}

View File

@@ -0,0 +1,83 @@
/*
* 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 "socket_api.hpp"
#include "socket_allocator.hpp"
namespace ams::socket::impl {
void *Alloc(size_t size) {
/* TODO: expheap, heap generation. */
return ams::Malloc(size);
}
void *Calloc(size_t num, size_t size) {
if (void *ptr = Alloc(size * num); ptr != nullptr) {
std::memset(ptr, 0, size * num);
return ptr;
} else {
return nullptr;
}
}
void Free(void *ptr) {
/* TODO: expheap, heap generation. */
return ams::Free(ptr);
}
Errno GetLastError() {
/* TODO: check that client library is initialized. */
return static_cast<Errno>(errno);
}
void SetLastError(Errno err) {
/* TODO: check that client library is initialized. */
errno = static_cast<int>(err);
}
u32 InetHtonl(u32 host) {
if constexpr (util::IsBigEndian()) {
return host;
} else {
return util::SwapBytes(host);
}
}
u16 InetHtons(u16 host) {
if constexpr (util::IsBigEndian()) {
return host;
} else {
return util::SwapBytes(host);
}
}
u32 InetNtohl(u32 net) {
if constexpr (util::IsBigEndian()) {
return net;
} else {
return util::SwapBytes(net);
}
}
u16 InetNtohs(u16 net) {
if constexpr (util::IsBigEndian()) {
return net;
} else {
return util::SwapBytes(net);
}
}
}

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/>.
*/
#include <stratosphere.hpp>
#include "impl/socket_api.hpp"
namespace ams::socket {
Errno GetLastError() {
return impl::GetLastError();
}
void SetLastError(Errno err) {
return impl::SetLastError(err);
}
u32 InetHtonl(u32 host) {
return impl::InetHtonl(host);
}
u16 InetHtons(u16 host) {
return impl::InetHtons(host);
}
u32 InetNtohl(u32 net) {
return impl::InetNtohl(net);
}
u16 InetNtohs(u16 net) {
return impl::InetNtohs(net);
}
}

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

@@ -16,7 +16,7 @@
#pragma once
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 0
#define ATMOSPHERE_RELEASE_VERSION_MINOR 17
#define ATMOSPHERE_RELEASE_VERSION_MINOR 18
#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

@@ -17,6 +17,7 @@
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
#include <vapours/util/util_type_traits.hpp>
#include <chrono>
namespace ams {
@@ -54,6 +55,7 @@ namespace ams {
constexpr ALWAYS_INLINE friend TimeSpanType operator+(const TimeSpanType &lhs, const TimeSpanType &rhs) { TimeSpanType r(lhs); return r += rhs; }
constexpr ALWAYS_INLINE friend TimeSpanType operator-(const TimeSpanType &lhs, const TimeSpanType &rhs) { TimeSpanType r(lhs); return r -= rhs; }
};
static_assert(util::is_pod<TimeSpanType>::value);
class TimeSpan {
private:

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

@@ -0,0 +1,28 @@
/*
* 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 Element, typename Compare = std::less<Element>, size_t BufferAlignment = 8>
class FixedSet : public ::ams::util::FixedTree<Element, Compare, const Element, BufferAlignment> {
/* ... */
};
}

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

@@ -31,7 +31,7 @@ namespace ams::util {
}
template<typename T>
int Strncmp(const T *lhs, const T *rhs, int count) {
constexpr int Strncmp(const T *lhs, const T *rhs, int count) {
AMS_ASSERT(lhs != nullptr);
AMS_ASSERT(rhs != nullptr);
AMS_ABORT_UNLESS(count >= 0);
@@ -50,7 +50,7 @@ namespace ams::util {
}
template<typename T>
int Strnicmp(const T *lhs, const T *rhs, int count) {
constexpr int Strnicmp(const T *lhs, const T *rhs, int count) {
AMS_ASSERT(lhs != nullptr);
AMS_ASSERT(rhs != nullptr);
AMS_ABORT_UNLESS(count >= 0);

View File

@@ -47,6 +47,36 @@ namespace ams::mitm::fs {
return fsFsCreateFile(&g_sd_filesystem, path, size, option);
}
Result DeleteSdFile(const char *path) {
R_TRY(EnsureSdInitialized());
return fsFsDeleteFile(&g_sd_filesystem, path);
}
bool HasSdFile(const char *path) {
if (R_FAILED(EnsureSdInitialized())) {
return false;
}
FsDirEntryType type;
if (R_FAILED(fsFsGetEntryType(&g_sd_filesystem, path, &type))) {
return false;
}
return type == FsDirEntryType_File;
}
bool HasAtmosphereSdFile(const char *path) {
char fixed_path[ams::fs::EntryNameLengthMax + 1];
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);
return HasSdFile(fixed_path);
}
Result DeleteAtmosphereSdFile(const char *path) {
char fixed_path[ams::fs::EntryNameLengthMax + 1];
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);
return DeleteSdFile(fixed_path);
}
Result CreateAtmosphereSdFile(const char *path, s64 size, s32 option) {
char fixed_path[ams::fs::EntryNameLengthMax + 1];
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);

View File

@@ -22,6 +22,7 @@ namespace ams::mitm::fs {
void OpenGlobalSdCardFileSystem();
/* Utilities. */
Result DeleteAtmosphereSdFile(const char *path);
Result CreateSdFile(const char *path, s64 size, s32 option);
Result CreateAtmosphereSdFile(const char *path, s64 size, s32 option);
Result OpenSdFile(FsFile *out, const char *path, u32 mode);
@@ -30,6 +31,9 @@ namespace ams::mitm::fs {
Result OpenAtmosphereSdRomfsFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode);
Result OpenAtmosphereRomfsFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode, FsFileSystem *fs);
bool HasSdFile(const char *path);
bool HasAtmosphereSdFile(const char *path);
Result CreateSdDirectory(const char *path);
Result CreateAtmosphereSdDirectory(const char *path);
Result OpenSdDirectory(FsDir *out, const char *path, u32 mode);

View File

@@ -38,20 +38,15 @@ extern "C" {
u64 __nx_exception_stack_size = sizeof(__nx_exception_stack);
void __libnx_exception_handler(ThreadExceptionDump *ctx);
void *__libnx_thread_alloc(size_t size);
void __libnx_thread_free(void *mem);
void *__libnx_alloc(size_t size);
void *__libnx_aligned_alloc(size_t alignment, size_t size);
void __libnx_free(void *mem);
}
namespace ams {
ncm::ProgramId CurrentProgramId = ncm::AtmosphereProgramId::Mitm;
namespace result {
bool CallFatalOnResultAssertion = false;
}
/* Override. */
void ExceptionHandler(FatalErrorContext *ctx) {
/* We're bpc-mitm (or ams_mitm, anyway), so manually reboot to fatal error. */
@@ -110,15 +105,28 @@ void __appExit(void) {
fsExit();
}
void *__libnx_thread_alloc(size_t size) {
AMS_ABORT("__libnx_thread_alloc was called");
void *__libnx_alloc(size_t size) {
AMS_ABORT("__libnx_alloc was called");
}
void __libnx_thread_free(void *mem) {
AMS_ABORT("__libnx_thread_free was called");
void *__libnx_aligned_alloc(size_t alignment, size_t size) {
AMS_ABORT("__libnx_aligned_alloc was called");
}
void __libnx_free(void *mem) {
AMS_ABORT("__libnx_free was called");
}
int main(int argc, char **argv) {
/* Register "ams" port, use up its session. */
{
svc::Handle ams_port;
R_ABORT_UNLESS(svc::ManageNamedPort(std::addressof(ams_port), "ams", 1));
svc::Handle ams_session;
R_ABORT_UNLESS(svc::ConnectToNamedPort(std::addressof(ams_session), "ams"));
}
/* Launch all mitm modules in sequence. */
mitm::LaunchAllModules();

View File

@@ -22,6 +22,7 @@
#include "bpc_mitm/bpcmitm_module.hpp"
#include "bpc_mitm/bpc_ams_module.hpp"
#include "ns_mitm/nsmitm_module.hpp"
#include "dns_mitm/dnsmitm_module.hpp"
#include "sysupdater/sysupdater_module.hpp"
namespace ams::mitm {
@@ -34,6 +35,7 @@ namespace ams::mitm {
ModuleId_BpcMitm,
ModuleId_BpcAms,
ModuleId_NsMitm,
ModuleId_DnsMitm,
ModuleId_Sysupdater,
ModuleId_Count,
@@ -66,6 +68,7 @@ namespace ams::mitm {
GetModuleDefinition<bpc::MitmModule>(),
GetModuleDefinition<bpc_ams::MitmModule>(),
GetModuleDefinition<ns::MitmModule>(),
GetModuleDefinition<socket::resolver::MitmModule>(),
GetModuleDefinition<sysupdater::MitmModule>(),
};

View File

@@ -0,0 +1,77 @@
/*
* 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 "../amsmitm_fs_utils.hpp"
#include "dnsmitm_debug.hpp"
namespace ams::mitm::socket::resolver {
namespace {
constinit os::SdkMutex g_log_mutex;
constinit bool g_log_enabled;
constinit ::FsFile g_log_file;
constinit s64 g_log_ofs;
constinit char g_log_buf[0x400];
}
void InitializeDebug(bool enable_log) {
{
std::scoped_lock lk(g_log_mutex);
g_log_enabled = enable_log;
if (g_log_enabled) {
/* Create the logs directory. */
mitm::fs::CreateAtmosphereSdDirectory("/logs");
/* Create the log file. */
mitm::fs::CreateAtmosphereSdFile("/logs/dns_mitm_debug.log", 0, ams::fs::CreateOption_None);
/* Open the log file. */
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(g_log_file), "/logs/dns_mitm_debug.log", ams::fs::OpenMode_ReadWrite | ams::fs::OpenMode_AllowAppend));
/* Get the current log offset. */
R_ABORT_UNLESS(::fsFileGetSize(std::addressof(g_log_file), std::addressof(g_log_ofs)));
}
}
/* Start a new log. */
LogDebug("\n---\n");
}
void LogDebug(const char *fmt, ...) {
std::scoped_lock lk(g_log_mutex);
if (g_log_enabled) {
int len = 0;
{
std::va_list vl;
va_start(vl, fmt);
len = util::VSNPrintf(g_log_buf, sizeof(g_log_buf), fmt, vl);
va_end(vl);
}
R_ABORT_UNLESS(::fsFileWrite(std::addressof(g_log_file), g_log_ofs, g_log_buf, len, FsWriteOption_Flush));
g_log_ofs += len;
}
}
}

View File

@@ -0,0 +1,25 @@
/*
* 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::mitm::socket::resolver {
void InitializeDebug(bool enable_log);
void LogDebug(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
}

View File

@@ -0,0 +1,399 @@
/*
* 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 "../amsmitm_fs_utils.hpp"
#include "dnsmitm_debug.hpp"
#include "dnsmitm_host_redirection.hpp"
#include "socket_allocator.hpp"
namespace ams::mitm::socket::resolver {
namespace {
/* https://github.com/clibs/wildcardcmp */
constexpr int wildcardcmp(const char *pattern, const char *string) {
const char *w = nullptr; /* last `*` */
const char *s = nullptr; /* last checked char */
/* malformed */
if (!pattern || !string) return 0;
/* loop 1 char at a time */
while (1) {
if (!*string) {
if (!*pattern) return 1;
if ('*' == *pattern) return 1;
if (!*s) return 0;
string = s++;
pattern = w;
continue;
} else {
if (*pattern != *string) {
if ('*' == *pattern) {
w = ++pattern;
s = string;
/* "*" -> "foobar" */
if (*pattern) continue;
return 1;
} else if (w) {
string++;
/* "*ooba*" -> "foobar" */
continue;
}
return 0;
}
}
string++;
pattern++;
}
return 1;
}
constexpr const char DefaultHostsFile[] =
"# Nintendo telemetry servers\n"
"127.0.0.1 receive-%.dg.srv.nintendo.net receive-%.er.srv.nintendo.net\n";
constinit os::SdkMutex g_redirection_lock;
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] = {};
void ParseHostsFile(const char *file_data) {
/* Get the environment identifier from settings. */
const auto env = ams::nsd::impl::device::GetEnvironmentIdentifierFromSettings();
const auto env_len = std::strlen(env.value);
/* Parse the file. */
enum class State {
IgnoredLine,
BeginLine,
Ip1,
IpDot1,
Ip2,
IpDot2,
Ip3,
IpDot3,
Ip4,
WhiteSpace,
HostName,
};
ams::socket::InAddrT current_address;
char current_hostname[0x200];
u32 work;
State state = State::BeginLine;
for (const char *cur = file_data; *cur != '\x00'; ++cur) {
const char c = *cur;
switch (state) {
case State::IgnoredLine:
if (c == '\n') {
state = State::BeginLine;
}
break;
case State::BeginLine:
if (std::isdigit(static_cast<unsigned char>(c))) {
current_address = 0;
work = static_cast<u32>(c - '0');
state = State::Ip1;
} else if (c == '\n') {
state = State::BeginLine;
} else {
state = State::IgnoredLine;
}
break;
case State::Ip1:
if (std::isdigit(static_cast<unsigned char>(c))) {
work *= 10;
work += static_cast<u32>(c - '0');
} else if (c == '.') {
current_address |= (work & 0xFF) << 0;
work = 0;
state = State::IpDot1;
} else {
state = State::IgnoredLine;
}
break;
case State::IpDot1:
if (std::isdigit(static_cast<unsigned char>(c))) {
work = static_cast<u32>(c - '0');
state = State::Ip2;
} else {
state = State::IgnoredLine;
}
break;
case State::Ip2:
if (std::isdigit(static_cast<unsigned char>(c))) {
work *= 10;
work += static_cast<u32>(c - '0');
} else if (c == '.') {
current_address |= (work & 0xFF) << 8;
work = 0;
state = State::IpDot2;
} else {
state = State::IgnoredLine;
}
break;
case State::IpDot2:
if (std::isdigit(static_cast<unsigned char>(c))) {
work = static_cast<u32>(c - '0');
state = State::Ip3;
} else {
state = State::IgnoredLine;
}
break;
case State::Ip3:
if (std::isdigit(static_cast<unsigned char>(c))) {
work *= 10;
work += static_cast<u32>(c - '0');
} else if (c == '.') {
current_address |= (work & 0xFF) << 16;
work = 0;
state = State::IpDot3;
} else {
state = State::IgnoredLine;
}
break;
case State::IpDot3:
if (std::isdigit(static_cast<unsigned char>(c))) {
work = static_cast<u32>(c - '0');
state = State::Ip4;
} else {
state = State::IgnoredLine;
}
break;
case State::Ip4:
if (std::isdigit(static_cast<unsigned char>(c))) {
work *= 10;
work += static_cast<u32>(c - '0');
} else if (c == ' ' || c == '\t') {
current_address |= (work & 0xFF) << 24;
work = 0;
state = State::WhiteSpace;
} else {
state = State::IgnoredLine;
}
break;
case State::WhiteSpace:
if (c == '\n') {
state = State::BeginLine;
} else if (c != ' ' && c != '\r' && c != '\t') {
if (c == '%') {
std::memcpy(current_hostname, env.value, env_len);
work = env_len;
} else {
current_hostname[0] = c;
work = 1;
}
state = State::HostName;
}
break;
case State::HostName:
if (c == ' ' || c == '\r' || c == '\n' || c == '\t') {
AMS_ABORT_UNLESS(work < sizeof(current_hostname));
current_hostname[work] = '\x00';
AddRedirection(current_hostname, current_address);
work = 0;
if (c == '\n') {
state = State::BeginLine;
} else {
state = State::WhiteSpace;
}
} else if (c == '%') {
AMS_ABORT_UNLESS(work < sizeof(current_hostname) - env_len);
std::memcpy(current_hostname + work, env.value, env_len);
work += env_len;
} else {
AMS_ABORT_UNLESS(work < sizeof(current_hostname) - 1);
current_hostname[work++] = c;
}
}
}
if (state == State::HostName) {
AMS_ABORT_UNLESS(work < sizeof(current_hostname));
current_hostname[work] = '\x00';
AddRedirection(current_hostname, current_address);
}
}
void Log(::FsFile &f, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
void Log(::FsFile &f, const char *fmt, ...) {
char log_buf[0x100];
int len = 0;
{
std::va_list vl;
va_start(vl, fmt);
len = util::VSNPrintf(log_buf, sizeof(log_buf), fmt, vl);
va_end(vl);
}
s64 ofs;
R_ABORT_UNLESS(::fsFileGetSize(std::addressof(f), std::addressof(ofs)));
R_ABORT_UNLESS(::fsFileWrite(std::addressof(f), ofs, log_buf, len, FsWriteOption_Flush));
}
const char *SelectHostsFile(::FsFile &log_file) {
Log(log_file, "Selecting hosts file...\n");
const bool is_emummc = emummc::IsActive();
const u32 emummc_id = emummc::GetActiveId();
util::SNPrintf(g_specific_emummc_hosts_path, sizeof(g_specific_emummc_hosts_path), "/hosts/emummc_%04x.txt", emummc_id);
if (is_emummc) {
if (mitm::fs::HasAtmosphereSdFile(g_specific_emummc_hosts_path)) {
return g_specific_emummc_hosts_path;
}
Log(log_file, "Skipping %s because it does not exist...\n", g_specific_emummc_hosts_path);
if (mitm::fs::HasAtmosphereSdFile("/hosts/emummc.txt")) {
return "/hosts/emummc.txt";
}
Log(log_file, "Skipping %s because it does not exist...\n", "/hosts/emummc.txt");
} else {
if (mitm::fs::HasAtmosphereSdFile("/hosts/sysmmc.txt")) {
return "/hosts/sysmmc.txt";
}
Log(log_file, "Skipping %s because it does not exist...\n", "/hosts/sysmmc.txt");
}
return "/hosts/default.txt";
}
bool ShouldAddDefaultResolverRedirections() {
u8 en = 0;
if (settings::fwdbg::GetSettingsItemValue(std::addressof(en), sizeof(en), "atmosphere", "add_defaults_to_dns_hosts") == sizeof(en)) {
return (en != 0);
}
return false;
}
}
void InitializeResolverRedirections() {
/* Get whether we should add defaults. */
const bool add_defaults = ShouldAddDefaultResolverRedirections();
/* Acquire exclusive access to the map. */
std::scoped_lock lk(g_redirection_lock);
/* Clear the redirections map. */
g_redirection_list.clear();
/* Open log file. */
::FsFile log_file;
mitm::fs::DeleteAtmosphereSdFile("/logs/dns_mitm_startup.log");
mitm::fs::CreateAtmosphereSdDirectory("/logs");
R_ABORT_UNLESS(mitm::fs::CreateAtmosphereSdFile("/logs/dns_mitm_startup.log", 0, ams::fs::CreateOption_None));
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(log_file), "/logs/dns_mitm_startup.log", ams::fs::OpenMode_ReadWrite | ams::fs::OpenMode_AllowAppend));
ON_SCOPE_EXIT { ::fsFileClose(std::addressof(log_file)); };
Log(log_file, "DNS Mitm:\n");
/* If a default hosts file doesn't exist on the sd card, create one. */
if (!mitm::fs::HasAtmosphereSdFile("/hosts/default.txt")) {
Log(log_file, "Creating /hosts/default.txt because it does not exist.\n");
mitm::fs::CreateAtmosphereSdDirectory("/hosts");
R_ABORT_UNLESS(mitm::fs::CreateAtmosphereSdFile("/hosts/default.txt", sizeof(DefaultHostsFile) - 1, ams::fs::CreateOption_None));
::FsFile default_file;
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(default_file), "/hosts/default.txt", ams::fs::OpenMode_ReadWrite));
R_ABORT_UNLESS(::fsFileWrite(std::addressof(default_file), 0, DefaultHostsFile, sizeof(DefaultHostsFile) - 1, ::FsWriteOption_Flush));
::fsFileClose(std::addressof(default_file));
}
/* If we should, add the defaults. */
if (add_defaults) {
Log(log_file, "Adding defaults to redirection list.\n");
ParseHostsFile(DefaultHostsFile);
}
/* Select the hosts file. */
const char *hosts_path = SelectHostsFile(log_file);
Log(log_file, "Selected %s\n", hosts_path);
/* Load the hosts file. */
{
char *hosts_file_data = nullptr;
ON_SCOPE_EXIT { if (hosts_file_data != nullptr) { ams::Free(hosts_file_data); } };
{
::FsFile hosts_file;
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(hosts_file), hosts_path, ams::fs::OpenMode_Read));
ON_SCOPE_EXIT { ::fsFileClose(std::addressof(hosts_file)); };
/* Get the hosts file size. */
s64 hosts_size;
R_ABORT_UNLESS(::fsFileGetSize(std::addressof(hosts_file), std::addressof(hosts_size)));
/* Validate we can read the file. */
AMS_ABORT_UNLESS(0 <= hosts_size && hosts_size < 0x8000);
/* Read the data. */
hosts_file_data = static_cast<char *>(ams::Malloc(0x8000));
AMS_ABORT_UNLESS(hosts_file_data != nullptr);
u64 br;
R_ABORT_UNLESS(::fsFileRead(std::addressof(hosts_file), 0, hosts_file_data, hosts_size, ::FsReadOption_None, std::addressof(br)));
AMS_ABORT_UNLESS(br == static_cast<u64>(hosts_size));
/* Null-terminate. */
hosts_file_data[hosts_size] = '\x00';
}
/* Parse the hosts file. */
ParseHostsFile(hosts_file_data);
}
/* Print the redirections. */
Log(log_file, "Redirections:\n");
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);
}
}
bool GetRedirectedHostByName(ams::socket::InAddrT *out, const char *hostname) {
std::scoped_lock lk(g_redirection_lock);
for (const auto &[host, address] : g_redirection_list) {
if (wildcardcmp(host.c_str(), hostname)) {
*out = address;
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,25 @@
/*
* 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::mitm::socket::resolver {
void InitializeResolverRedirections();
bool GetRedirectedHostByName(ams::socket::InAddrT *out, const char *hostname);
}

View File

@@ -0,0 +1,137 @@
/*
* 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 "../amsmitm_initialization.hpp"
#include "dnsmitm_module.hpp"
#include "dnsmitm_debug.hpp"
#include "dnsmitm_resolver_impl.hpp"
#include "dnsmitm_host_redirection.hpp"
namespace ams::mitm::socket::resolver {
namespace {
enum PortIndex {
PortIndex_Mitm,
PortIndex_Count,
};
constexpr sm::ServiceName DnsMitmServiceName = sm::ServiceName::Encode("sfdnsres");
constexpr size_t MaxSessions = 30;
using ServerOptions = sf::hipc::DefaultServerManagerOptions;
class ServerManager final : public sf::hipc::ServerManager<PortIndex_Count, ServerOptions, MaxSessions> {
private:
virtual Result OnNeedsToAccept(int port_index, Server *server) override;
};
ServerManager g_server_manager;
Result ServerManager::OnNeedsToAccept(int port_index, Server *server) {
/* Acknowledge the mitm session. */
std::shared_ptr<::Service> fsrv;
sm::MitmProcessInfo client_info;
server->AcknowledgeMitmSession(std::addressof(fsrv), std::addressof(client_info));
switch (port_index) {
case PortIndex_Mitm:
return this->AcceptMitmImpl(server, sf::CreateSharedObjectEmplaced<IResolver, ResolverImpl>(decltype(fsrv)(fsrv), client_info), fsrv);
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
constexpr size_t TotalThreads = 8;
static_assert(TotalThreads >= 1, "TotalThreads");
constexpr size_t NumExtraThreads = TotalThreads - 1;
constexpr size_t ThreadStackSize = mitm::ModuleTraits<socket::resolver::MitmModule>::StackSize;
alignas(os::MemoryPageSize) u8 g_extra_thread_stacks[NumExtraThreads][ThreadStackSize];
os::ThreadType g_extra_threads[NumExtraThreads];
void LoopServerThread(void *arg) {
/* Loop forever, servicing our services. */
g_server_manager.LoopProcess();
}
void ProcessForServerOnAllThreads() {
/* Initialize threads. */
if constexpr (NumExtraThreads > 0) {
const s32 priority = os::GetThreadCurrentPriority(os::GetCurrentThread());
for (size_t i = 0; i < NumExtraThreads; i++) {
R_ABORT_UNLESS(os::CreateThread(g_extra_threads + i, LoopServerThread, nullptr, g_extra_thread_stacks[i], ThreadStackSize, priority));
}
}
/* Start extra threads. */
if constexpr (NumExtraThreads > 0) {
for (size_t i = 0; i < NumExtraThreads; i++) {
os::StartThread(g_extra_threads + i);
}
}
/* Loop this thread. */
LoopServerThread(nullptr);
/* Wait for extra threads to finish. */
if constexpr (NumExtraThreads > 0) {
for (size_t i = 0; i < NumExtraThreads; i++) {
os::WaitThread(g_extra_threads + i);
}
}
}
bool ShouldMitmDns() {
u8 en = 0;
if (settings::fwdbg::GetSettingsItemValue(std::addressof(en), sizeof(en), "atmosphere", "enable_dns_mitm") == sizeof(en)) {
return (en != 0);
}
return false;
}
bool ShouldEnableDebugLog() {
u8 en = 0;
if (settings::fwdbg::GetSettingsItemValue(std::addressof(en), sizeof(en), "atmosphere", "enable_dns_mitm_debug_log") == sizeof(en)) {
return (en != 0);
}
return false;
}
}
void MitmModule::ThreadFunction(void *arg) {
/* Wait until initialization is complete. */
mitm::WaitInitialized();
/* If we shouldn't mitm dns, don't do anything at all. */
if (!ShouldMitmDns()) {
return;
}
/* Initialize debug. */
resolver::InitializeDebug(ShouldEnableDebugLog());
/* Initialize redirection map. */
resolver::InitializeResolverRedirections();
/* Create mitm servers. */
R_ABORT_UNLESS((g_server_manager.RegisterMitmServer<ResolverImpl>(PortIndex_Mitm, DnsMitmServiceName)));
/* Loop forever, servicing our services. */
ProcessForServerOnAllThreads();
}
}

View File

@@ -0,0 +1,24 @@
/*
* 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>
#include "../amsmitm_module.hpp"
namespace ams::mitm::socket::resolver {
DEFINE_MITM_MODULE_CLASS(0x2000, AMS_GET_SYSTEM_THREAD_PRIORITY(socket, ResolverIpcServer) - 1);
}

View File

@@ -0,0 +1,200 @@
/*
* 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 "dnsmitm_resolver_impl.hpp"
#include "dnsmitm_debug.hpp"
#include "dnsmitm_host_redirection.hpp"
#include "serializer/serializer.hpp"
#include "sfdnsres_shim.h"
namespace ams::mitm::socket::resolver {
ssize_t SerializeRedirectedHostEnt(u8 * const dst, size_t dst_size, const char *hostname, ams::socket::InAddrT redirect_addr) {
struct in_addr addr = { .s_addr = redirect_addr };
struct in_addr *addr_list[2] = { &addr, nullptr };
struct hostent ent = {
.h_name = const_cast<char *>(hostname),
.h_aliases = nullptr,
.h_addrtype = AF_INET,
.h_length = sizeof(u32),
.h_addr_list = (char **)addr_list,
};
const auto result = serializer::DNSSerializer::ToBuffer(dst, dst_size, ent);
AMS_ABORT_UNLESS(result >= 0);
return result;
}
ssize_t SerializeRedirectedAddrInfo(u8 * const dst, size_t dst_size, const char *hostname, ams::socket::InAddrT redirect_addr, u16 redirect_port, const struct addrinfo *hint) {
struct addrinfo ai = {
.ai_flags = 0,
.ai_family = AF_UNSPEC,
.ai_socktype = 0,
.ai_protocol = 0,
.ai_addrlen = 0,
.ai_canonname = nullptr,
.ai_next = nullptr,
};
if (hint != nullptr) {
ai = *hint;
}
switch (ai.ai_family) {
case AF_UNSPEC: ai.ai_family = AF_INET; break;
case AF_INET: ai.ai_family = AF_INET; break;
case AF_INET6: AMS_ABORT("Redirected INET6 not supported"); break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
if (ai.ai_socktype == 0) {
ai.ai_socktype = SOCK_STREAM;
}
if (ai.ai_protocol == 0) {
ai.ai_protocol = IPPROTO_TCP;
}
const struct sockaddr_in sin = {
.sin_family = AF_INET,
.sin_port = ams::socket::InetHtons(redirect_port),
.sin_addr = { .s_addr = redirect_addr },
.sin_zero = {},
};
ai.ai_addrlen = sizeof(sin);
ai.ai_addr = (struct sockaddr *)(std::addressof(sin));
const auto result = serializer::DNSSerializer::ToBuffer(dst, dst_size, ai);
AMS_ABORT_UNLESS(result >= 0);
return result;
}
Result ResolverImpl::GetHostByNameRequest(u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &name, sf::Out<u32> out_host_error, sf::Out<u32> out_errno, const sf::OutBuffer &out_hostent, sf::Out<u32> out_size) {
const char *hostname = reinterpret_cast<const char *>(name.GetPointer());
LogDebug("[%016lx]: GetHostByNameRequest(%s)\n", this->client_info.program_id.value, hostname);
ams::socket::InAddrT redirect_addr = {};
R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession());
LogDebug("[%016lx]: Redirecting %s to %u.%u.%u.%u\n", this->client_info.program_id.value, hostname, (redirect_addr >> 0) & 0xFF, (redirect_addr >> 8) & 0xFF, (redirect_addr >> 16) & 0xFF, (redirect_addr >> 24) & 0xFF);
const auto size = SerializeRedirectedHostEnt(out_hostent.GetPointer(), out_hostent.GetSize(), hostname, redirect_addr);
*out_host_error = 0;
*out_errno = 0;
*out_size = size;
return ResultSuccess();
}
Result ResolverImpl::GetAddrInfoRequest(u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutBuffer &out_addrinfo, sf::Out<u32> out_errno, sf::Out<s32> out_retval, sf::Out<u32> out_size) {
const char *hostname = reinterpret_cast<const char *>(node.GetPointer());
LogDebug("[%016lx]: GetAddrInfoRequest(%s, %s)\n", this->client_info.program_id.value, reinterpret_cast<const char *>(node.GetPointer()), reinterpret_cast<const char *>(srv.GetPointer()));
ams::socket::InAddrT redirect_addr = {};
R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession());
u16 port = 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);
const bool use_hint = serialized_hint.GetPointer() != nullptr;
struct addrinfo hint = {};
if (use_hint) {
AMS_ABORT_UNLESS(serializer::DNSSerializer::FromBuffer(hint, serialized_hint.GetPointer(), serialized_hint.GetSize()) >= 0);
}
ON_SCOPE_EXIT { if (use_hint) { serializer::FreeAddrInfo(hint); } };
const auto size = SerializeRedirectedAddrInfo(out_addrinfo.GetPointer(), out_addrinfo.GetSize(), hostname, redirect_addr, port, use_hint ? std::addressof(hint) : nullptr);
*out_retval = 0;
*out_errno = 0;
*out_size = size;
return ResultSuccess();
}
Result ResolverImpl::GetHostByNameRequestWithOptions(const sf::ClientProcessId &client_pid, const sf::InAutoSelectBuffer &name, const sf::OutAutoSelectBuffer &out_hostent, sf::Out<u32> out_size, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno) {
const char *hostname = reinterpret_cast<const char *>(name.GetPointer());
LogDebug("[%016lx]: GetHostByNameRequestWithOptions(%s)\n", this->client_info.program_id.value, hostname);
ams::socket::InAddrT redirect_addr = {};
R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession());
LogDebug("[%016lx]: Redirecting %s to %u.%u.%u.%u\n", this->client_info.program_id.value, hostname, (redirect_addr >> 0) & 0xFF, (redirect_addr >> 8) & 0xFF, (redirect_addr >> 16) & 0xFF, (redirect_addr >> 24) & 0xFF);
const auto size = SerializeRedirectedHostEnt(out_hostent.GetPointer(), out_hostent.GetSize(), hostname, redirect_addr);
*out_host_error = 0;
*out_errno = 0;
*out_size = size;
return ResultSuccess();
}
Result ResolverImpl::GetAddrInfoRequestWithOptions(const sf::ClientProcessId &client_pid, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutAutoSelectBuffer &out_addrinfo, sf::Out<u32> out_size, sf::Out<s32> out_retval, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno) {
const char *hostname = reinterpret_cast<const char *>(node.GetPointer());
LogDebug("[%016lx]: GetAddrInfoRequestWithOptions(%s, %s)\n", this->client_info.program_id.value, hostname, reinterpret_cast<const char *>(srv.GetPointer()));
ams::socket::InAddrT redirect_addr = {};
R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession());
u16 port = 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);
const bool use_hint = serialized_hint.GetPointer() != nullptr;
struct addrinfo hint = {};
if (use_hint) {
AMS_ABORT_UNLESS(serializer::DNSSerializer::FromBuffer(hint, serialized_hint.GetPointer(), serialized_hint.GetSize()) >= 0);
}
ON_SCOPE_EXIT { if (use_hint) { serializer::FreeAddrInfo(hint); } };
const auto size = SerializeRedirectedAddrInfo(out_addrinfo.GetPointer(), out_addrinfo.GetSize(), hostname, redirect_addr, port, use_hint ? std::addressof(hint) : nullptr);
*out_retval = 0;
*out_host_error = 0;
*out_errno = 0;
*out_size = size;
return ResultSuccess();
}
Result ResolverImpl::AtmosphereReloadHostsFile() {
/* Perform a hosts file reload. */
InitializeResolverRedirections();
return ResultSuccess();
}
}

View File

@@ -0,0 +1,52 @@
/*
* 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>
#define AMS_DNS_MITM_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 2, Result, GetHostByNameRequest, (u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &name, sf::Out<u32> out_host_error, sf::Out<u32> out_errno, const sf::OutBuffer &out_hostent, sf::Out<u32> out_size), (cancel_handle, client_pid, use_nsd_resolve, name, out_host_error, out_errno, out_hostent, out_size)) \
AMS_SF_METHOD_INFO(C, H, 6, Result, GetAddrInfoRequest, (u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutBuffer &out_addrinfo, sf::Out<u32> out_errno, sf::Out<s32> out_retval, sf::Out<u32> out_size), (cancel_handle, client_pid, use_nsd_resolve, node, srv, serialized_hint, out_addrinfo, out_errno, out_retval, out_size)) \
AMS_SF_METHOD_INFO(C, H, 10, Result, GetHostByNameRequestWithOptions, (const sf::ClientProcessId &client_pid, const sf::InAutoSelectBuffer &name, const sf::OutAutoSelectBuffer &out_hostent, sf::Out<u32> out_size, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno), (client_pid, name, out_hostent, out_size, options_version, options, num_options, out_host_error, out_errno), hos::Version_5_0_0) \
AMS_SF_METHOD_INFO(C, H, 12, Result, GetAddrInfoRequestWithOptions, (const sf::ClientProcessId &client_pid, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutAutoSelectBuffer &out_addrinfo, sf::Out<u32> out_size, sf::Out<s32> out_retval, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno), (client_pid, node, srv, serialized_hint, out_addrinfo, out_size, out_retval, options_version, options, num_options, out_host_error, out_errno), hos::Version_5_0_0) \
AMS_SF_METHOD_INFO(C, H, 65000, Result, AtmosphereReloadHostsFile, (), ())
AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::socket::resolver, IResolver, AMS_DNS_MITM_INTERFACE_INFO)
namespace ams::mitm::socket::resolver {
class ResolverImpl : public sf::MitmServiceImplBase {
public:
using MitmServiceImplBase::MitmServiceImplBase;
public:
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
/* We will mitm:
* - everything.
*/
return true;
}
public:
/* Overridden commands. */
Result GetHostByNameRequest(u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &name, sf::Out<u32> out_host_error, sf::Out<u32> out_errno, const sf::OutBuffer &out_hostent, sf::Out<u32> out_size);
Result GetAddrInfoRequest(u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutBuffer &out_addrinfo, sf::Out<u32> out_errno, sf::Out<s32> out_retval, sf::Out<u32> out_size);
Result GetHostByNameRequestWithOptions(const sf::ClientProcessId &client_pid, const sf::InAutoSelectBuffer &name, const sf::OutAutoSelectBuffer &out_hostent, sf::Out<u32> out_size, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno);
Result GetAddrInfoRequestWithOptions(const sf::ClientProcessId &client_pid, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutAutoSelectBuffer &out_addrinfo, sf::Out<u32> out_size, sf::Out<s32> out_retval, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno);
/* Extension commands. */
Result AtmosphereReloadHostsFile();
};
static_assert(IsIResolver<ResolverImpl>);
}

View File

@@ -0,0 +1,49 @@
/*
* 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 "../dnsmitm_debug.hpp"
#include "serializer.hpp"
namespace ams::mitm::socket::resolver::serializer {
ssize_t DNSSerializer::CheckToBufferArguments(const u8 *dst, size_t dst_size, size_t required, int error_id) {
if (dst == nullptr) {
return -1;
} else if (dst_size < required) {
return -1;
}
return 0;
}
u32 DNSSerializer::InternalHton(const u32 &v) {
return ams::socket::InetHtonl(v);
}
u16 DNSSerializer::InternalHton(const u16 &v) {
return ams::socket::InetHtons(v);
}
u32 DNSSerializer::InternalNtoh(const u32 &v) {
return ams::socket::InetNtohl(v);
}
u16 DNSSerializer::InternalNtoh(const u16 &v) {
return ams::socket::InetNtohs(v);
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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::mitm::socket::resolver::serializer {
class DNSSerializer {
public:
static ssize_t CheckToBufferArguments(const u8 *dst, size_t dst_size, size_t required, int error_id);
static u32 InternalHton(const u32 &v);
static u16 InternalHton(const u16 &v);
static u32 InternalNtoh(const u32 &v);
static u16 InternalNtoh(const u16 &v);
public:
template<typename T>
static size_t SizeOf(const T &in);
template<typename T>
static size_t SizeOf(const T *in);
template<typename T>
static size_t SizeOf(const T *in, size_t count);
template<typename T>
static size_t SizeOf(const T **arr, u32 &out_count);
template<typename T>
static ssize_t ToBuffer(u8 * const dst, size_t dst_size, const T &in);
template<typename T>
static ssize_t FromBuffer(T &out, const u8 *src, size_t src_size);
template<typename T>
static ssize_t ToBuffer(u8 * const dst, size_t dst_Size, T *in);
template<typename T>
static ssize_t ToBuffer(u8 * const dst, size_t dst_size, T **arr);
template<typename T>
static ssize_t FromBuffer(T *&out, const u8 *src, size_t src_size);
template<typename T>
static ssize_t FromBuffer(T **&out_arr, const u8 *src, size_t src_size);
template<typename T>
static ssize_t ToBuffer(u8 * const dst, size_t dst_size, const T * const arr, size_t count);
template<typename T>
static ssize_t FromBuffer(T * const arr, size_t arr_size, const u8 *src, size_t src_size, size_t count);
};
void FreeHostent(ams::socket::HostEnt &ent);
void FreeHostent(struct hostent &ent);
void FreeAddrInfo(ams::socket::AddrInfo &addr_info);
void FreeAddrInfo(struct addrinfo &addr_info);
}

View File

@@ -0,0 +1,444 @@
/*
* 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 "../dnsmitm_debug.hpp"
#include "../socket_allocator.hpp"
#include "serializer.hpp"
namespace ams::mitm::socket::resolver::serializer {
namespace {
constexpr inline u32 AddrInfoMagic = 0xBEEFCAFE;
template<typename T>
concept IsAddrInfo = std::same_as<T, ams::socket::AddrInfo> || std::same_as<T, struct addrinfo>;
template<typename T> requires IsAddrInfo<T>
using SockAddrType = typename std::conditional<std::same_as<T, ams::socket::AddrInfo>, ams::socket::SockAddr, struct sockaddr>::type;
template<typename T> requires IsAddrInfo<T>
using SockAddrInType = typename std::conditional<std::same_as<T, ams::socket::AddrInfo>, ams::socket::SockAddrIn, struct sockaddr_in>::type;
template<typename T> requires IsAddrInfo<T>
using SockAddrIn6Type = typename std::conditional<std::same_as<T, ams::socket::AddrInfo>, struct sockaddr_in6, struct sockaddr_in6>::type;
template<typename T> requires IsAddrInfo<T>
constexpr bool IsAfInet(const auto ai_family) {
if constexpr (std::same_as<T, ams::socket::AddrInfo>) {
return ai_family == ams::socket::Family::Af_Inet;
} else {
return ai_family == AF_INET;
}
}
template<typename T> requires IsAddrInfo<T>
constexpr bool IsAfInet6(const auto ai_family) {
if constexpr (std::same_as<T, ams::socket::AddrInfo>) {
return ai_family == ams::socket::Family::Af_Inet6;
} else {
return ai_family == AF_INET;
}
}
template<typename T> requires IsAddrInfo<T>
void FreeAddrInfoImpl(T &addr_info) {
T *next = nullptr;
for (T *cur = std::addressof(addr_info); cur != nullptr; cur = next) {
next = cur->ai_next;
if (cur->ai_addr != nullptr) {
if (IsAfInet<T>(cur->ai_family)) {
ams::socket::impl::Free(reinterpret_cast<SockAddrInType<T> *>(cur->ai_addr));
} else if (IsAfInet6<T>(cur->ai_family)) {
ams::socket::impl::Free(reinterpret_cast<SockAddrIn6Type<T> *>(cur->ai_addr));
} else {
ams::socket::impl::Free(cur->ai_addr);
}
cur->ai_addr = nullptr;
}
if (cur->ai_canonname != nullptr) {
ams::socket::impl::Free(cur->ai_canonname);
cur->ai_canonname = nullptr;
}
if (cur != std::addressof(addr_info)) {
ams::socket::impl::Free(cur);
}
}
}
template<typename T> requires IsAddrInfo<T>
size_t AddrInfoSingleSizeOf(const T *addr_info) {
size_t rc = 6 * sizeof(u32);
if (addr_info->ai_addr == nullptr) {
rc += sizeof(u32);
} else if (IsAfInet<T>(addr_info->ai_family)) {
rc += DNSSerializer::SizeOf(*reinterpret_cast<SockAddrInType<T> *>(addr_info->ai_addr));
} else if (IsAfInet6<T>(addr_info->ai_family)) {
rc += DNSSerializer::SizeOf(*reinterpret_cast<SockAddrIn6Type<T> *>(addr_info->ai_addr));
} else if (addr_info->ai_addrlen == 0) {
rc += sizeof(u32);
} else {
rc += addr_info->ai_addrlen;
}
if (addr_info->ai_canonname != nullptr) {
rc += DNSSerializer::SizeOf(static_cast<const char *>(addr_info->ai_canonname));
} else {
rc += sizeof(u8);
}
if (addr_info->ai_next == nullptr) {
rc += sizeof(u32);
}
return rc;
}
template<typename T> requires IsAddrInfo<T>
size_t SizeOfImpl(const T &in) {
size_t rc = 0;
for (const T *addr_info = std::addressof(in); addr_info != nullptr; addr_info = addr_info->ai_next) {
rc += AddrInfoSingleSizeOf(addr_info);
}
return rc;
}
template<typename T> requires IsAddrInfo<T>
ssize_t ToBufferInternalImpl(u8 * const dst, size_t dst_size, const T &addr_info) {
ssize_t rc = -1;
u8 *cur = dst;
{
const u32 value = AddrInfoMagic;
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
return rc;
}
cur += rc;
}
{
const u32 value = static_cast<u32>(addr_info.ai_flags);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
return rc;
}
cur += rc;
}
{
const u32 value = static_cast<u32>(addr_info.ai_family);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
return rc;
}
cur += rc;
}
{
const u32 value = static_cast<u32>(addr_info.ai_socktype);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
return rc;
}
cur += rc;
}
{
const u32 value = static_cast<u32>(addr_info.ai_protocol);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
return rc;
}
cur += rc;
}
{
const u32 value = static_cast<u32>(addr_info.ai_addrlen);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
return rc;
}
cur += rc;
}
{
if (addr_info.ai_addr == nullptr || addr_info.ai_addrlen == 0) {
const u32 value = 0;
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
return rc;
}
} else if (IsAfInet<T>(addr_info.ai_family)) {
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), *reinterpret_cast<SockAddrInType<T> *>(addr_info.ai_addr))) == -1) {
return rc;
}
} else if (IsAfInet6<T>(addr_info.ai_family)) {
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), *reinterpret_cast<SockAddrIn6Type<T> *>(addr_info.ai_addr))) == -1) {
return rc;
}
} else {
if (dst_size - (cur - dst) < addr_info.ai_addrlen) {
rc = -1;
return rc;
}
/* NOTE: This is clearly a nintendo bug, see the accompanying note in FromBufferInternalImpl */
std::memmove(cur, std::addressof(addr_info.ai_addr), addr_info.ai_addrlen);
rc = addr_info.ai_addrlen;
}
cur += rc;
}
{
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), addr_info.ai_canonname)) == -1) {
return rc;
}
cur += rc;
}
if (addr_info.ai_next == nullptr) {
const u32 value = 0;
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
return rc;
}
cur += rc;
}
rc = cur - dst;
return rc;
}
template<typename T> requires IsAddrInfo<T>
ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, const T &in) {
ssize_t rc = -1;
u8 *cur = dst;
std::memset(dst, 0, dst_size);
const size_t required = DNSSerializer::SizeOf(in);
if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, required, __LINE__)) == -1) {
return rc;
}
for (const T *addr_info = std::addressof(in); addr_info != nullptr; addr_info = addr_info->ai_next) {
if ((rc = ToBufferInternalImpl(cur, dst_size, *addr_info)) == -1) {
return rc;
}
cur += rc;
}
rc = cur - dst;
return rc;
}
template<typename T> requires IsAddrInfo<T>
ssize_t FromBufferInternalImpl(T &out, const u8 *src, size_t src_size) {
ssize_t rc = -1;
const u8 *cur = src;
std::memset(std::addressof(out), 0, sizeof(out));
ON_SCOPE_EXIT { if (rc < 0) { FreeAddrInfo(out); } };
u32 tmp_value;
{
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
return rc;
} else if (tmp_value != AddrInfoMagic) {
return rc;
}
cur += rc;
}
{
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.ai_flags = static_cast<decltype(out.ai_flags)>(tmp_value);
cur += rc;
}
{
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.ai_family = static_cast<decltype(out.ai_family)>(tmp_value);
cur += rc;
}
{
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.ai_socktype = static_cast<decltype(out.ai_socktype)>(tmp_value);
cur += rc;
}
{
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.ai_protocol = static_cast<decltype(out.ai_protocol)>(tmp_value);
cur += rc;
}
{
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.ai_addrlen = static_cast<decltype(out.ai_addrlen)>(tmp_value);
cur += rc;
}
{
if (out.ai_addrlen == 0) {
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
return rc;
}
if (tmp_value != 0) {
rc = -1;
return rc;
}
out.ai_addr = nullptr;
} else if (IsAfInet<T>(out.ai_family)) {
out.ai_addr = static_cast<SockAddrType<T> *>(ams::socket::impl::Alloc(sizeof(SockAddrInType<T>)));
if (out.ai_addr == nullptr) {
rc = -1;
return rc;
}
std::memset(out.ai_addr, 0, sizeof(SockAddrInType<T>));
if ((rc = DNSSerializer::FromBuffer(*reinterpret_cast<SockAddrInType<T> *>(out.ai_addr), cur, src_size - (cur - src))) == -1) {
return rc;
}
} else if (IsAfInet6<T>(out.ai_family)) {
out.ai_addr = static_cast<SockAddrType<T> *>(ams::socket::impl::Alloc(sizeof(SockAddrIn6Type<T>)));
if (out.ai_addr == nullptr) {
rc = -1;
return rc;
}
std::memset(out.ai_addr, 0, sizeof(SockAddrIn6Type<T>));
if ((rc = DNSSerializer::FromBuffer(*reinterpret_cast<SockAddrIn6Type<T> *>(out.ai_addr), cur, src_size - (cur - src))) == -1) {
return rc;
}
} else {
out.ai_addr = static_cast<decltype(out.ai_addr)>(ams::socket::impl::Alloc(out.ai_addrlen));
if (out.ai_addr == nullptr) {
rc = -1;
return rc;
}
/* NOTE: This is *clearly* a nintendo bug. */
/* They obviously intend to copy to the buffer they just allocated, but instead they copy to the addrinfo structure itself. */
/* Probably &out.ai_addr instead of &out.ai_addr[0]? Either way, we'll implement what they do, but... */
std::memcpy(std::addressof(out.ai_addr), cur, out.ai_addrlen);
rc = out.ai_addrlen;
}
cur += rc;
}
{
if ((rc = DNSSerializer::FromBuffer(out.ai_canonname, cur, src_size - (cur - src))) == -1) {
return rc;
}
cur += rc;
}
{
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
return rc;
} else if (tmp_value == 0) {
out.ai_next = nullptr;
cur += rc;
} else if (tmp_value == AddrInfoMagic) {
out.ai_next = static_cast<T *>(ams::socket::impl::Alloc(sizeof(T)));
if (out.ai_next == nullptr) {
rc = -1;
return rc;
}
std::memset(out.ai_next, 0, sizeof(T));
} else {
rc = -1;
return rc;
}
}
rc = cur - src;
return rc;
}
template<typename T> requires IsAddrInfo<T>
ssize_t FromBufferImpl(T &out, const u8 *src, size_t src_size) {
ssize_t rc = 0;
const u8 *cur = src;
const size_t required = DNSSerializer::SizeOf(out);
if (src_size < required) {
ams::socket::SetLastError(ams::socket::Errno::ENoSpc);
rc = -1;
return rc;
}
for (T *addr_info = std::addressof(out); addr_info != nullptr; addr_info = addr_info->ai_next) {
if ((rc = FromBufferInternalImpl(*addr_info, cur, src_size - (cur - src))) == -1) {
rc = -1;
return rc;
}
cur += rc;
}
rc = cur - src;
return rc;
}
}
template<> size_t DNSSerializer::SizeOf(const struct addrinfo &in) {
return SizeOfImpl(in);
}
template<> size_t DNSSerializer::SizeOf(const ams::socket::AddrInfo &in) {
return SizeOfImpl(in);
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const ams::socket::AddrInfo &in) {
return ToBufferImpl(dst, dst_size, in);
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const struct addrinfo &in) {
return ToBufferImpl(dst, dst_size, in);
}
template<> ssize_t DNSSerializer::FromBuffer(ams::socket::AddrInfo &out, const u8 *src, size_t src_size) {
return FromBufferImpl(out, src, src_size);
}
template<> ssize_t DNSSerializer::FromBuffer(struct addrinfo &out, const u8 *src, size_t src_size) {
return FromBufferImpl(out, src, src_size);
}
void FreeAddrInfo(ams::socket::AddrInfo &addr_info) {
return FreeAddrInfoImpl(addr_info);
}
void FreeAddrInfo(struct addrinfo &addr_info) {
return FreeAddrInfoImpl(addr_info);
}
}

View File

@@ -0,0 +1,234 @@
/*
* 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 "../dnsmitm_debug.hpp"
#include "../socket_allocator.hpp"
#include "serializer.hpp"
namespace ams::mitm::socket::resolver::serializer {
namespace {
template<typename T>
concept IsHostEnt = std::same_as<T, ams::socket::HostEnt> || std::same_as<T, struct hostent>;
template<typename T> requires IsHostEnt<T>
using InAddrType = typename std::conditional<std::same_as<T, ams::socket::HostEnt>, ams::socket::InAddr, struct in_addr>::type;
template<typename T> requires IsHostEnt<T>
constexpr bool IsAfInet(const auto h_addrtype) {
if constexpr (std::same_as<T, ams::socket::HostEnt>) {
return h_addrtype == ams::socket::Family::Af_Inet;
} else {
return h_addrtype == AF_INET;
}
}
template<typename T> requires IsHostEnt<T>
constexpr bool IsAfInet6(const auto h_addrtype) {
if constexpr (std::same_as<T, ams::socket::HostEnt>) {
return h_addrtype == ams::socket::Family::Af_Inet6;
} else {
return h_addrtype == AF_INET;
}
}
template<typename T> requires IsHostEnt<T>
size_t SizeOfImpl(const T &in) {
size_t rc = 0;
u32 dummy = 0;
rc += DNSSerializer::SizeOf((const char *)(in.h_name));
rc += DNSSerializer::SizeOf((const char **)(in.h_aliases), dummy);
rc += sizeof(u32);
rc += sizeof(u32);
rc += DNSSerializer::SizeOf((const InAddrType<T> **)(in.h_addr_list), dummy);
return rc;
}
template<typename T> requires IsHostEnt<T>
ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, const T &in) {
ssize_t rc = -1;
u8 *cur = dst;
const size_t required = DNSSerializer::SizeOf(in);
if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, required, __LINE__)) == -1) {
return rc;
}
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), in.h_name)) == -1) {
return rc;
}
cur += rc;
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), in.h_aliases)) == -1) {
return rc;
}
cur += rc;
const u16 h_addrtype = static_cast<u16>(in.h_addrtype);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), h_addrtype)) == -1) {
return rc;
}
cur += rc;
const u16 h_length = in.h_length;
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), h_length)) == -1) {
return rc;
}
cur += rc;
if (IsAfInet<T>(in.h_addrtype)) {
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), (InAddrType<T> **)(in.h_addr_list))) == -1) {
return rc;
}
} else if (IsAfInet6<T>(in.h_addrtype)) {
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), (InAddrType<T> **)(in.h_addr_list))) == -1) {
return rc;
}
} else {
const u32 null = 0;
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), null)) == -1) {
return rc;
}
}
cur += rc;
rc = cur - dst;
return rc;
}
template<typename T> requires IsHostEnt<T>
ssize_t FromBufferImpl(T &out, const u8 *src, size_t src_size) {
ssize_t rc = -1;
const u8 *cur = src;
std::memset(std::addressof(out), 0, sizeof(out));
ON_SCOPE_EXIT {
if (rc < 0) {
FreeHostent(out);
}
};
if ((rc = DNSSerializer::FromBuffer(out.h_name, cur, src_size - (cur - src))) == -1) {
return rc;
}
cur += rc;
if ((rc = DNSSerializer::FromBuffer(out.h_aliases, cur, src_size - (cur - src))) == -1) {
return rc;
}
cur += rc;
u16 h_addrtype = 0;
if ((rc = DNSSerializer::FromBuffer(h_addrtype, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.h_addrtype = static_cast<decltype(out.h_addrtype)>(h_addrtype);
cur += rc;
u16 h_length = 0;
if ((rc = DNSSerializer::FromBuffer(h_length, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.h_length = h_length;
cur += rc;
InAddrType<T> **addrs = nullptr;
if ((rc = DNSSerializer::FromBuffer(addrs, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.h_addr_list = (char **)addrs;
cur += rc;
rc = cur - src;
return rc;
}
template<typename T> requires IsHostEnt<T>
void FreeHostentImpl(T &ent) {
if (ent.h_name != nullptr) {
ams::socket::impl::Free(ent.h_name);
ent.h_name = nullptr;
}
if (ent.h_aliases != nullptr) {
u32 i = 0;
for (char *str = ent.h_aliases[i]; str != nullptr; str = ent.h_aliases[++i]) {
ams::socket::impl::Free(str);
ent.h_aliases[i] = nullptr;
}
ams::socket::impl::Free(ent.h_aliases);
ent.h_aliases = nullptr;
}
if (ent.h_addr_list != nullptr) {
AMS_ASSERT(ent.h_length == sizeof(u32));
if (ent.h_length == sizeof(u32)) {
auto **addr_list = reinterpret_cast<InAddrType<T> **>(ent.h_addr_list);
u32 i = 0;
for (auto *addr = addr_list[i]; addr != nullptr; addr = addr_list[++i]) {
ams::socket::impl::Free(addr);
addr_list[i] = nullptr;
}
ams::socket::impl::Free(ent.h_addr_list);
ent.h_addr_list = nullptr;
}
}
std::memset(std::addressof(ent), 0, sizeof(ent));
}
}
template<> size_t DNSSerializer::SizeOf(const struct hostent &in) {
return SizeOfImpl(in);
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const struct hostent &in) {
return ToBufferImpl(dst, dst_size, in);
}
template<> ssize_t DNSSerializer::FromBuffer(struct hostent &out, const u8 *src, size_t src_size) {
return FromBufferImpl(out, src, src_size);
}
template<> size_t DNSSerializer::SizeOf(const ams::socket::HostEnt &in) {
return SizeOfImpl(in);
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const ams::socket::HostEnt &in) {
return ToBufferImpl(dst, dst_size, in);
}
template<> ssize_t DNSSerializer::FromBuffer(ams::socket::HostEnt &out, const u8 *src, size_t src_size) {
return FromBufferImpl(out, src, src_size);
}
void FreeHostent(ams::socket::HostEnt &ent) {
return FreeHostentImpl(ent);
}
void FreeHostent(struct hostent &ent) {
return FreeHostentImpl(ent);
}
}

View File

@@ -0,0 +1,237 @@
/*
* 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 "../dnsmitm_debug.hpp"
#include "../socket_allocator.hpp"
#include "serializer.hpp"
namespace ams::mitm::socket::resolver::serializer {
namespace {
template<typename T>
concept IsInAddr = std::same_as<T, ams::socket::InAddr> || std::same_as<T, struct in_addr>;
template<typename T> requires IsInAddr<T>
size_t SizeOfImpl(const T &in) {
return sizeof(u32);
}
template<typename T> requires IsInAddr<T>
size_t SizeOfImpl(const T **in, u32 &out_count) {
size_t rc = sizeof(u32);
out_count = 0;
if (in != nullptr) {
for (const T ** cur_addr = in; *cur_addr != nullptr; ++cur_addr) {
++out_count;
rc += sizeof(u32);
}
}
return rc;
}
template<typename T> requires IsInAddr<T>
ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, const T &in) {
ssize_t rc = -1;
u8 *cur = dst;
const u32 val = DNSSerializer::InternalHton(in.s_addr);
if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, sizeof(in), __LINE__)) == -1) {
return rc;
}
std::memcpy(cur, std::addressof(val), sizeof(val));
rc += sizeof(val);
return rc;
}
template<typename T> requires IsInAddr<T>
ssize_t FromBufferImpl(T &out, const u8 *src, size_t src_size) {
ssize_t rc = -1;
if (src_size < sizeof(out)) {
return rc;
}
std::memset(std::addressof(out), 0, sizeof(out));
out.s_addr = DNSSerializer::InternalNtoh(*reinterpret_cast<const u32 *>(src));
rc = sizeof(u32);
return rc;
}
template<typename T> requires IsInAddr<T>
ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, T **arr) {
ssize_t rc = -1;
u8 *cur = dst;
if (arr == nullptr && dst_size < sizeof(u32)) {
return rc;
} else if (arr == nullptr) {
const u32 null = 0;
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), null)) == -1) {
return rc;
}
cur += rc;
} else {
u32 count = 0;
for (auto *tmp = arr; *tmp != nullptr; ++tmp) {
++count;
}
if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, (count + 1) * sizeof(**arr), __LINE__)) == -1) {
return rc;
}
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), count)) == -1) {
return rc;
}
cur += rc;
rc = 0;
for (auto i = 0; arr[i] != nullptr; ++i) {
const T addr = *arr[i];
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), addr)) == -1) {
return rc;
}
cur += rc;
}
rc = cur - dst;
}
return rc;
}
template<typename T> requires IsInAddr<T>
ssize_t FromBufferImpl(T **&out, const u8 *src, size_t src_size) {
ssize_t rc = -1;
const u8 *cur = src;
out = nullptr;
ON_SCOPE_EXIT {
if (rc == -1 && out != nullptr) {
for (auto i = 0; out[i] != nullptr; ++i) {
ams::socket::impl::Free(out[i]);
out[i] = nullptr;
}
ams::socket::impl::Free(out);
out = nullptr;
}
};
if (src == nullptr) {
rc = 0;
return rc;
} else if (src_size == 0) {
rc = 0;
return rc;
}
u32 count = 0;
if ((rc = DNSSerializer::FromBuffer(count, cur, src_size)) == -1) {
return rc;
}
cur += rc;
if (count == 0) {
return rc;
}
out = static_cast<T **>(ams::socket::impl::Alloc((count + 1) * sizeof(T *)));
if (out == nullptr) {
rc = -1;
return rc;
}
std::memset(out, 0, (count + 1) * sizeof(T *));
for (u32 i = 0; i < count; ++i) {
out[i] = static_cast<T *>(ams::socket::impl::Alloc(sizeof(T)));
if (out[i] == nullptr) {
rc = -1;
return rc;
}
u32 s_addr = 0;
if ((rc = DNSSerializer::FromBuffer(s_addr, cur, src_size - (cur - src))) == -1) {
return rc;
}
out[i]->s_addr = s_addr;
cur += rc;
}
out[count] = nullptr;
rc = cur - src;
return rc;
}
}
template<> size_t DNSSerializer::SizeOf(const struct in_addr &in) {
return SizeOfImpl(in);
}
template<> size_t DNSSerializer::SizeOf(const ams::socket::InAddr &in) {
return SizeOfImpl(in);
}
template<> size_t DNSSerializer::SizeOf(const struct in_addr **in, u32 &out_count) {
return SizeOfImpl(in, out_count);
}
template<> size_t DNSSerializer::SizeOf(const ams::socket::InAddr **in, u32 &out_count) {
return SizeOfImpl(in, out_count);
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const struct in_addr &in) {
return ToBufferImpl(dst, dst_size, in);
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const ams::socket::InAddr &in) {
return ToBufferImpl(dst, dst_size, in);
}
template<> ssize_t DNSSerializer::FromBuffer(struct in_addr &out, const u8 *src, size_t src_size) {
return FromBufferImpl(out, src, src_size);
}
template<> ssize_t DNSSerializer::FromBuffer(struct ams::socket::InAddr &out, const u8 *src, size_t src_size) {
return FromBufferImpl(out, src, src_size);
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, struct in_addr **arr) {
return ToBufferImpl(dst, dst_size, arr);
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, ams::socket::InAddr **arr) {
return ToBufferImpl(dst, dst_size, arr);
}
template<> ssize_t DNSSerializer::FromBuffer(struct in_addr **&out, const u8 *src, size_t src_size) {
return FromBufferImpl(out, src, src_size);
}
template<> ssize_t DNSSerializer::FromBuffer(struct ams::socket::InAddr **&out, const u8 *src, size_t src_size) {
return FromBufferImpl(out, src, src_size);
}
}

View File

@@ -0,0 +1,74 @@
/*
* 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 "../dnsmitm_debug.hpp"
#include "serializer.hpp"
namespace ams::mitm::socket::resolver::serializer {
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const u16 &in) {
/* Convert the value. */
u8 *cur = dst;
const u16 val = InternalHton(in);
/* Check arguments. */
ssize_t rc = -1;
if ((rc = CheckToBufferArguments(cur, dst_size, sizeof(u16), __LINE__)) == -1) {
return rc;
}
std::memcpy(cur, std::addressof(val), sizeof(u16));
rc += sizeof(u16);
return rc;
}
template<> ssize_t DNSSerializer::FromBuffer(u16 &out, const u8 *src, size_t src_size) {
if (src_size < sizeof(u16)) {
return -1;
}
out = InternalNtoh(*reinterpret_cast<const u16 *>(src));
return sizeof(u16);
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const u32 &in) {
/* Convert the value. */
u8 *cur = dst;
const u32 val = InternalHton(in);
/* Check arguments. */
ssize_t rc = -1;
if ((rc = CheckToBufferArguments(cur, dst_size, sizeof(u32), __LINE__)) == -1) {
return rc;
}
std::memcpy(cur, std::addressof(val), sizeof(u32));
rc += sizeof(u32);
return rc;
}
template<> ssize_t DNSSerializer::FromBuffer(u32 &out, const u8 *src, size_t src_size) {
if (src_size < sizeof(u32)) {
return -1;
}
out = InternalNtoh(*reinterpret_cast<const u32 *>(src));
return sizeof(u32);
}
}

View File

@@ -0,0 +1,141 @@
/*
* 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 "../dnsmitm_debug.hpp"
#include "../socket_allocator.hpp"
#include "serializer.hpp"
namespace ams::mitm::socket::resolver::serializer {
namespace {
template<typename T>
concept IsSockAddrIn = std::same_as<T, ams::socket::SockAddrIn> || std::same_as<T, struct sockaddr_in>;
template<typename T> requires IsSockAddrIn<T>
size_t SizeOfImpl(const T &in) {
size_t rc = 0;
rc += sizeof(u16);
rc += sizeof(u16);
rc += DNSSerializer::SizeOf(in.sin_addr);
rc += sizeof(in.sin_zero);
return rc;
}
template<typename T> requires IsSockAddrIn<T>
ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, const T &in) {
ssize_t rc = -1;
u8 *cur = dst;
if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, sizeof(in), __LINE__)) == -1) {
return rc;
}
const u16 sin_family = static_cast<u16>(in.sin_family);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin_family)) == -1) {
return rc;
}
cur += rc;
const u16 sin_port = static_cast<u16>(in.sin_port);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin_port)) == -1) {
return rc;
}
cur += rc;
const u32 s_addr = static_cast<u32>(in.sin_addr.s_addr);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), s_addr)) == -1) {
return rc;
}
cur += rc;
if (dst_size - (cur - dst) < sizeof(in.sin_zero)) {
rc = -1;
return rc;
}
std::memcpy(cur, in.sin_zero, sizeof(in.sin_zero));
cur += sizeof(in.sin_zero);
rc = cur - dst;
return rc;
}
template<typename T> requires IsSockAddrIn<T>
ssize_t FromBufferImpl(T &out, const u8 *src, size_t src_size) {
ssize_t rc = -1;
const u8 *cur = src;
u16 sin_family;
if ((rc = DNSSerializer::FromBuffer(sin_family, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.sin_family = static_cast<decltype(out.sin_family)>(sin_family);
cur += rc;
u16 sin_port;
if ((rc = DNSSerializer::FromBuffer(sin_port, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.sin_port = static_cast<decltype(out.sin_port)>(sin_port);
cur += rc;
u32 s_addr;
if ((rc = DNSSerializer::FromBuffer(s_addr, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.sin_addr.s_addr = static_cast<decltype(out.sin_addr.s_addr)>(s_addr);
cur += rc;
if (src_size - (cur - src) < sizeof(out.sin_zero)) {
rc = -1;
return rc;
}
std::memcpy(out.sin_zero, cur, sizeof(out.sin_zero));
cur += sizeof(out.sin_zero);
rc = cur - src;
return rc;
}
}
template<> size_t DNSSerializer::SizeOf(const struct sockaddr_in &in) {
return SizeOfImpl(in);
}
template<> size_t DNSSerializer::SizeOf(const ams::socket::SockAddrIn &in) {
return SizeOfImpl(in);
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const struct sockaddr_in &in) {
return ToBufferImpl(dst, dst_size, in);
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const ams::socket::SockAddrIn &in) {
return ToBufferImpl(dst, dst_size, in);
}
template<> ssize_t DNSSerializer::FromBuffer(struct sockaddr_in &out, const u8 *src, size_t src_size) {
return FromBufferImpl(out, src, src_size);
}
template<> ssize_t DNSSerializer::FromBuffer(struct ams::socket::SockAddrIn &out, const u8 *src, size_t src_size) {
return FromBufferImpl(out, src, src_size);
}
}

View File

@@ -0,0 +1,133 @@
/*
* 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 "../dnsmitm_debug.hpp"
#include "../socket_allocator.hpp"
#include "serializer.hpp"
namespace ams::mitm::socket::resolver::serializer {
namespace {
template<typename T>
concept IsSockAddrIn6 = std::same_as<T, struct sockaddr_in6>;
template<typename T> requires IsSockAddrIn6<T>
size_t SizeOfImpl(const T &in) {
size_t rc = 0;
rc += sizeof(u16);
rc += sizeof(u16);
rc += sizeof(u32);
rc += DNSSerializer::SizeOf(in.sin6_addr);
rc += sizeof(u32);
return rc;
}
template<typename T> requires IsSockAddrIn6<T>
ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, const T &in) {
ssize_t rc = -1;
u8 *cur = dst;
if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, sizeof(in), __LINE__)) == -1) {
return rc;
}
const u16 sin6_family = static_cast<u16>(in.sin6_family);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin6_family)) == -1) {
return rc;
}
cur += rc;
const u16 sin6_port = static_cast<u16>(in.sin6_port);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin6_port)) == -1) {
return rc;
}
cur += rc;
const u32 sin6_flowinfo = static_cast<u32>(in.sin6_flowinfo);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin6_flowinfo)) == -1) {
return rc;
}
cur += rc;
std::memcpy(cur, std::addressof(in.sin6_addr), sizeof(in.sin6_addr));
cur += sizeof(in.sin6_addr);
const u32 sin6_scope_id = static_cast<u32>(in.sin6_scope_id);
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin6_scope_id)) == -1) {
return rc;
}
cur += rc;
rc = cur - dst;
return rc;
}
template<typename T> requires IsSockAddrIn6<T>
ssize_t FromBufferImpl(T &out, const u8 *src, size_t src_size) {
ssize_t rc = -1;
const u8 *cur = src;
u16 sin6_family;
if ((rc = DNSSerializer::FromBuffer(sin6_family, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.sin6_family = static_cast<decltype(out.sin6_family)>(sin6_family);
cur += rc;
u16 sin6_port;
if ((rc = DNSSerializer::FromBuffer(sin6_port, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.sin6_port = static_cast<decltype(out.sin6_port)>(sin6_port);
cur += rc;
u32 sin6_flowinfo;
if ((rc = DNSSerializer::FromBuffer(sin6_flowinfo, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.sin6_flowinfo = static_cast<decltype(out.sin6_flowinfo)>(sin6_flowinfo);
cur += rc;
std::memcpy(std::addressof(out.sin6_addr), cur, sizeof(out.sin6_addr));
cur += sizeof(out.sin6_addr);
u32 sin6_scope_id;
if ((rc = DNSSerializer::FromBuffer(sin6_scope_id, cur, src_size - (cur - src))) == -1) {
return rc;
}
out.sin6_scope_id = static_cast<decltype(out.sin6_scope_id)>(sin6_scope_id);
cur += rc;
rc = cur - src;
return rc;
}
}
template<> size_t DNSSerializer::SizeOf(const struct sockaddr_in6 &in) {
return SizeOfImpl(in);
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const struct sockaddr_in6 &in) {
return ToBufferImpl(dst, dst_size, in);
}
template<> ssize_t DNSSerializer::FromBuffer(struct sockaddr_in6 &out, const u8 *src, size_t src_size) {
return FromBufferImpl(out, src, src_size);
}
}

View File

@@ -0,0 +1,180 @@
/*
* 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 "../dnsmitm_debug.hpp"
#include "../socket_allocator.hpp"
#include "serializer.hpp"
namespace ams::mitm::socket::resolver::serializer {
template<> size_t DNSSerializer::SizeOf(const char *str) {
if (str == nullptr) {
return sizeof(char);
}
return std::strlen(str) + 1;
}
template<> size_t DNSSerializer::SizeOf(const char **str, u32 &out_count) {
size_t rc = sizeof(u32);
out_count = 0;
if (str != nullptr) {
for (const char **cur = str; *cur != nullptr; ++cur) {
++out_count;
rc += SizeOf(*cur);
}
}
return rc;
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, char *str) {
ssize_t rc = -1;
u8 *cur = dst;
if (str == nullptr && dst_size == 0) {
return -1;
} else if (str == nullptr) {
*cur = '\x00';
return 1;
} else if ((rc = SizeOf(static_cast<const char *>(str))) == 0) {
*cur = '\x00';
return 1;
} else if (CheckToBufferArguments(cur, dst_size, rc + 1, __LINE__) == -1) {
return -1;
}
std::memmove(cur, str, rc);
return rc;
}
template<> ssize_t DNSSerializer::FromBuffer(char *&out, const u8 *src, size_t src_size) {
size_t len = 0;
if (src == nullptr) {
return 0;
} else if (src_size == 0) {
return 0;
} else if (src_size < (len = SizeOf(reinterpret_cast<const char *>(src)))) {
return 1;
} else if (src[0] == '\x00') {
return 1;
}
out = static_cast<char *>(ams::socket::impl::Alloc(len));
if (out == nullptr) {
return -1;
}
std::memmove(out, src, len);
return len;
}
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, char **str) {
ssize_t rc = -1;
u8 *cur = dst;
u32 count = 0;
if (dst_size == 0) {
return -1;
}
const size_t total_size = SizeOf(const_cast<const char **>(str), count);
AMS_UNUSED(total_size);
if ((rc = CheckToBufferArguments(cur, dst_size, sizeof(u32), __LINE__)) == -1) {
return rc;
} else if ((rc = ToBuffer(cur, dst_size, count)) == -1) {
return rc;
}
cur += rc;
dst_size -= rc;
if (str != nullptr) {
for (char **cur_str = str; *cur_str != nullptr; ++cur_str) {
const auto tmp = ToBuffer(cur, dst_size, *cur_str);
if (tmp == -1) {
return rc;
}
cur += tmp;
dst_size -= tmp;
rc += tmp;
}
}
rc = cur - dst;
return rc;
}
template<> ssize_t DNSSerializer::FromBuffer(char **&out, const u8 *src, size_t src_size) {
ssize_t rc = -1;
const u8 *cur = src;
u32 count = 0;
out = nullptr;
ON_SCOPE_EXIT {
if (rc < 0 && out != nullptr) {
u32 i = 0;
for (char *str = *out; str != nullptr; str = out[++i]) {
ams::socket::impl::Free(str);
}
ams::socket::impl::Free(out);
out = nullptr;
}
};
if (src == nullptr) {
rc = 0;
return rc;
} else if (src_size == 0) {
rc = 0;
return rc;
} else if ((rc = FromBuffer(count, cur, src_size)) == -1) {
rc = -1;
return rc;
}
cur += rc;
out = static_cast<char **>(ams::socket::impl::Alloc((count + 1) * sizeof(char *)));
if (out == nullptr) {
rc = -1;
return rc;
}
std::memset(out, 0, (count + 1) * sizeof(char *));
u32 i;
for (i = 0; i < count; ++i) {
const size_t len = std::strlen(reinterpret_cast<const char *>(cur));
out[i] = static_cast<char *>(ams::socket::impl::Alloc(len + 1));
if (out[i] == nullptr) {
rc = -1;
return rc;
}
std::memmove(out[i], cur, len + 1);
cur += len + 1;
}
out[i] = nullptr;
rc = cur - src;
return rc;
}
}

View File

@@ -0,0 +1,97 @@
/*
* 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 "sfdnsres_shim.h"
#include <stratosphere/sf/sf_mitm_dispatch.h>
/* Command forwarders. */
Result sfdnsresGetHostByNameRequestWithOptionsFwd(Service *s, u64 process_id, const void *name, size_t name_size, void *out_hostent, size_t out_hostent_size, u32 *out_size, u32 options_version, const void *option, size_t option_size, u32 num_options, s32 *out_host_error, s32 *out_errno) {
const struct {
u32 options_version;
u32 num_options;
u64 process_id;
} in = { options_version, num_options, process_id };
struct {
u32 size;
s32 host_error;
s32 errno;
} out;
Result rc = serviceMitmDispatchInOut(s, 10, in, out,
.buffer_attrs = {
SfBufferAttr_HipcAutoSelect | SfBufferAttr_In,
SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out,
SfBufferAttr_HipcAutoSelect | SfBufferAttr_In
},
.buffers = {
{ name, name_size },
{ out_hostent, out_hostent_size },
{ option, option_size }
},
.in_send_pid = true,
.override_pid = process_id,
);
if (R_SUCCEEDED(rc)) {
if (out_size) *out_size = out.size;
if (out_host_error) *out_host_error = out.host_error;
if (out_errno) *out_errno = out.errno;
}
return rc;
}
Result sfdnsresGetAddrInfoRequestWithOptionsFwd(Service *s, u64 process_id, const void *node, size_t node_size, const void *srv, size_t srv_size, const void *hint, size_t hint_size, void *out_ai, size_t out_ai_size, u32 *out_size, s32 *out_rv, u32 options_version, const void *option, size_t option_size, u32 num_options, s32 *out_host_error, s32 *out_errno) {
const struct {
u32 options_version;
u32 num_options;
u64 process_id;
} in = { options_version, num_options, process_id };
struct {
u32 size;
s32 rv;
s32 host_error;
s32 errno;
} out;
Result rc = serviceMitmDispatchInOut(s, 12, in, out,
.buffer_attrs = {
SfBufferAttr_HipcMapAlias | SfBufferAttr_In,
SfBufferAttr_HipcMapAlias | SfBufferAttr_In,
SfBufferAttr_HipcMapAlias | SfBufferAttr_In,
SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out,
SfBufferAttr_HipcAutoSelect | SfBufferAttr_In
},
.buffers = {
{ node, node_size },
{ srv, srv_size },
{ hint, hint_size },
{ out_ai, out_ai_size },
{ option, option_size }
},
.in_send_pid = true,
.override_pid = process_id,
);
if (R_SUCCEEDED(rc)) {
if (out_size) *out_size = out.size;
if (out_rv) *out_rv = out.rv;
if (out_host_error) *out_host_error = out.host_error;
if (out_errno) *out_errno = out.errno;
}
return rc;
}

View File

@@ -0,0 +1,21 @@
/**
* @file sfdnsres_shim.h
* @brief IPC wrapper for dns.mitm.
* @author SciresM
* @copyright libnx Authors
*/
#pragma once
#include <switch.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Command forwarders. */
Result sfdnsresGetHostByNameRequestWithOptionsFwd(Service *s, u64 process_id, const void *name, size_t name_size, void *out_hostent, size_t out_hostent_size, u32 *out_size, u32 options_version, const void *option, size_t option_size, u32 num_options, s32 *out_host_error, s32 *out_errno);
Result sfdnsresGetAddrInfoRequestWithOptionsFwd(Service *s, u64 process_id, const void *node, size_t node_size, const void *srv, size_t srv_size, const void *hint, size_t hint_size, void *out_ai, size_t out_ai_size, u32 *out_size, s32 *out_rv, u32 options_version, const void *option, size_t option_size, u32 num_options, s32 *out_host_error, s32 *out_errno);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,25 @@
/*
* 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::socket::impl {
void *Alloc(size_t size);
void *Calloc(size_t num, size_t size);
void Free(void *ptr);
}

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

@@ -349,6 +349,19 @@ namespace ams::settings::fwdbg {
/* 0 = Disabled (not debug mode), 1 = Enabled (debug mode) */
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_am_debug_mode", "u8!0x0"));
/* Controls whether dns.mitm is enabled. */
/* 0 = Disabled, 1 = Enabled */
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_dns_mitm", "u8!0x1"));
/* Controls whether dns.mitm uses the default redirections in addition to */
/* whatever is specified in the user's hosts file. */
/* 0 = Disabled (use hosts file contents), 1 = Enabled (use defaults and hosts file contents) */
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "add_defaults_to_dns_hosts", "u8!0x1"));
/* Controls whether dns.mitm logs to the sd card for debugging. */
/* 0 = Disabled, 1 = Enabled */
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_dns_mitm_debug_log", "u8!0x0"));
/* Hbloader custom settings. */
/* Controls the size of the homebrew heap when running as applet. */

View File

@@ -49,8 +49,9 @@ extern "C" {
u64 __nx_exception_stack_size = sizeof(__nx_exception_stack);
void __libnx_exception_handler(ThreadExceptionDump *ctx);
void *__libnx_thread_alloc(size_t size);
void __libnx_thread_free(void *mem);
void *__libnx_alloc(size_t size);
void *__libnx_aligned_alloc(size_t alignment, size_t size);
void __libnx_free(void *mem);
}
namespace ams {
@@ -62,12 +63,6 @@ namespace ams {
boot::RebootForFatalError(ctx);
}
namespace result {
bool CallFatalOnResultAssertion = false;
}
}
using namespace ams;
@@ -160,12 +155,16 @@ namespace ams {
}
void *__libnx_thread_alloc(size_t size) {
AMS_ABORT("__libnx_thread_alloc was called");
void *__libnx_alloc(size_t size) {
AMS_ABORT("__libnx_alloc was called");
}
void __libnx_thread_free(void *mem) {
AMS_ABORT("__libnx_thread_free was called");
void *__libnx_aligned_alloc(size_t alignment, size_t size) {
AMS_ABORT("__libnx_aligned_alloc was called");
}
void __libnx_free(void *mem) {
AMS_ABORT("__libnx_free was called");
}
void *operator new(size_t size) {

View File

@@ -33,18 +33,16 @@ extern "C" {
alignas(16) u8 __nx_exception_stack[ams::os::MemoryPageSize];
u64 __nx_exception_stack_size = sizeof(__nx_exception_stack);
void __libnx_exception_handler(ThreadExceptionDump *ctx);
void *__libnx_alloc(size_t size);
void *__libnx_aligned_alloc(size_t alignment, size_t size);
void __libnx_free(void *mem);
}
namespace ams {
ncm::ProgramId CurrentProgramId = ncm::SystemProgramId::Boot2;
namespace result {
bool CallFatalOnResultAssertion = false;
}
}
using namespace ams;
@@ -137,6 +135,18 @@ void operator delete(void *p) {
AMS_ABORT("operator delete(void *) was called");
}
void *__libnx_alloc(size_t size) {
AMS_ABORT("__libnx_alloc was called");
}
void *__libnx_aligned_alloc(size_t alignment, size_t size) {
AMS_ABORT("__libnx_aligned_alloc was called");
}
void __libnx_free(void *mem) {
AMS_ABORT("__libnx_free was called");
}
int main(int argc, char **argv)
{
/* Set thread name. */

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