Compare commits

..

173 Commits

Author SHA1 Message Date
TuxSH
56bf2defa4 thermosphere: fix watchpoint creation, fix wp&bp allocation 2020-02-04 19:12:25 +00:00
TuxSH
833d88bb90 thermosphere: rewrite watchpoints.c 2020-02-04 19:12:25 +00:00
TuxSH
f2edaee5b6 thermosphere: suppress potential unused variable warnings 2020-02-04 19:12:25 +00:00
TuxSH
8fbe4385c6 thermosphere: reduce gdb work buf to least acceptable limit 2020-02-04 19:12:25 +00:00
TuxSH
559d254a79 thermosphere: the fpu cache is only being really modified by gdb anyway 2020-02-04 19:12:25 +00:00
TuxSH
57f2aca2fd thermosphere: I wish ld wasn't dumb (also, bugfix). This saves 4K 2020-02-04 19:12:25 +00:00
TuxSH
20737569ce thermosphere: rewhoops 2020-02-04 19:12:25 +00:00
TuxSH
84ee01797a thermosphere: fix software breakpoints 2020-02-04 19:12:24 +00:00
TuxSH
307d2361ff thermosphere: gdb: fix IsThreadAlive 2020-02-04 19:12:24 +00:00
TuxSH
7935c8b1ad thermosphère: fix deadlock 2020-02-04 19:12:24 +00:00
TuxSH
c12a32c540 thermosphere: fix sending bug when handling ctrl-c 2020-02-04 19:12:24 +00:00
TuxSH
50efbe1840 thermosphere: fix continue logic for full-stop & some refactoring 2020-02-04 19:12:24 +00:00
TuxSH
ade27f084f thermosphere: rewrite condition in debugManagerDoPauseCores 2020-02-04 19:12:24 +00:00
TuxSH
e18cc528a8 thermosphere: fix bug in debug.c 2020-02-04 19:12:24 +00:00
TuxSH
ab02bd6cfe thermosphere: oops 2020-02-04 19:12:24 +00:00
TuxSH
94c04af758 thermosphere: better self-debug fault reporting 2020-02-04 19:12:24 +00:00
TuxSH
aca1e86f45 thermosphere: fix bug in exceptionReturnPreprocess 2020-02-04 19:12:24 +00:00
TuxSH
95ea2f4f95 thermosphere: forgot compiler barrier in get_sysreg 2020-02-04 19:12:24 +00:00
TuxSH
316166d8de thermosphere: fix gdb/regs.c assertions 2020-02-04 19:12:24 +00:00
TuxSH
97d548cc94 thermopshere: GDB_ParseExceptionFrame: fix format error 2020-02-04 19:12:24 +00:00
TuxSH
65713df11c thermosphere: fix bug where x0 isn't saved 2020-02-04 19:12:24 +00:00
TuxSH
57a4a3457e thermosphere: fix thread reporting logic, etc 2020-02-04 19:12:24 +00:00
TuxSH
fe3badcdf7 thermosphere: fix reporting logic of initial break event 2020-02-04 19:12:24 +00:00
TuxSH
18ca273107 thermosphere: fix target.xml generation 2020-02-04 19:12:24 +00:00
TuxSH
2915aed461 thermosphere: fix irq buffer overflow 2020-02-04 19:12:24 +00:00
TuxSH
6457dce5b1 thermosphere: add more debugging strings 2020-02-04 19:12:24 +00:00
TuxSH
eff433f62c thermosphere: gdb: fix a few bugs 2020-02-04 19:12:24 +00:00
TuxSH
86eaa7777b thermosphere: pl011: fix uartSetInterruptStatus
We don't need to forcefully clear the line level
2020-02-04 19:12:24 +00:00
TuxSH
116d0703f4 thermosphere: actually report the debug events 2020-02-04 19:12:24 +00:00
TuxSH
fded7fd692 thermosphere: gdb: fix GDB_SendStopReply 2020-02-04 19:12:24 +00:00
TuxSH
3190d971a1 thermosphere: pause at start, some cleanup, etc. 2020-02-04 19:12:24 +00:00
TuxSH
b929d76009 thermosphere: libc: fix missing macro 2020-02-04 19:12:24 +00:00
TuxSH
6b546a2716 thermosphere: qemu: make serial go through a socket 2020-02-04 19:12:24 +00:00
TuxSH
26461cd7a9 thermosphere: oops 2020-02-04 19:12:24 +00:00
TuxSH
680a768178 thermosphere: reduce usage of nonvolatile memory by around 4KB 2020-02-04 19:12:24 +00:00
TuxSH
1fd2cdb664 thermosphere: gdb: add debugManagerInit 2020-02-04 19:12:23 +00:00
TuxSH
057d3e5e1c thermosphere: gdb: add missing command list entries, fix warnings again 2020-02-04 19:12:23 +00:00
TuxSH
6becc5dc8a thermosphere: add src/gdb to build list, fix subsequent warnings and errors 2020-02-04 19:12:23 +00:00
TuxSH
55c92b3ab1 thermosphere: fix break/continue (?), fix attach/detach 2020-02-04 19:12:23 +00:00
TuxSH
598b0b4b56 thermosphere: gdb: remove currentThreadId; migrate rx irq 2020-02-04 19:12:23 +00:00
TuxSH
5a29fd17fe thermosphere: gdb: add core_on and core_off handling 2020-02-04 19:12:23 +00:00
TuxSH
ced0b32556 thermosphere: forgot to call exceptionReturnPreprocess in start.s 2020-02-04 19:12:23 +00:00
TuxSH
997d1a5b0a thermosphere: gdb: properly handle vStopped ack sequence 2020-02-04 19:12:23 +00:00
TuxSH
8dc536cc5b thermosphere: gdb/debug: avoid pause/unpause race condition in vCont + bugfix 2020-02-04 19:12:23 +00:00
TuxSH
5dd8e3c129 thermosphere: gdb: fix parsing errors in vCont and hex decode 2020-02-04 19:12:23 +00:00
TuxSH
b08c1e34b1 thermosphere: gdb add break & vCont handling 2020-02-04 19:12:23 +00:00
TuxSH
ba9b99713f thermosphere: GDB_TrySignalDebugEvent, do nothing if not attached 2020-02-04 19:12:23 +00:00
TuxSH
52c3397b19 thermosphere: add structural changes needed for range step 2020-02-04 19:12:23 +00:00
TuxSH
cba5c08bbc thermosphere: resend debug event if not handled 2020-02-04 19:12:23 +00:00
TuxSH
adc6962d99 thermosphere: impl. debug event dispatching, vStopped, "?" 2020-02-04 19:12:23 +00:00
TuxSH
d7ffcfc5d5 thermosphere: debug manager wip 2020-02-04 19:12:23 +00:00
TuxSH
f69ef02096 thermosphere: gdb: remove server, rewrite data processing in gdb/context and gdb/net 2020-02-04 19:12:23 +00:00
TuxSH
5dc54d8764 thermosphere: optimize barrier and core_ctx 2020-02-04 19:12:23 +00:00
TuxSH
b168b0c2eb thermopshere: add spinlock try lock 2020-02-04 19:12:23 +00:00
TuxSH
65205f74da thermosphere: rewrite gdb/reg 2020-02-04 19:12:23 +00:00
TuxSH
abc699aa3d thermosphere: rework fpu register handling 2020-02-04 19:12:23 +00:00
TuxSH
e3961f225c thermosphere: introduce "ENSURE" 2020-02-04 19:12:23 +00:00
TuxSH
d0821a3f50 thermosphere: small spinlock improvements 2020-02-04 19:12:22 +00:00
TuxSH
f4e7425b27 thermosphere: refactor gdb/thread 2020-02-04 19:12:22 +00:00
TuxSH
8b28e6f107 thermosphere: gdb: target xml + various refactoring 2020-02-04 19:12:22 +00:00
TuxSH
3ef785ef21 thermosphere: rewrite gdb/mem 2020-02-04 19:12:22 +00:00
TuxSH
a501f0b4a2 thermosphere: gdb/net: reduce stack/memory usage by using memmove 2020-02-04 19:12:22 +00:00
TuxSH
559b54a319 thermopshere: gdb: rewrite stop point handling 2020-02-04 19:12:22 +00:00
TuxSH
55be6773fd thermosphere: retrieve wp direction 2020-02-04 19:12:22 +00:00
TuxSH
e723415e44 thermosphere: some gdb/debug refactor 2020-02-04 19:12:22 +00:00
TuxSH
c66b70b1a2 thermopshère: rewrite some gdb/net functions 2020-02-04 19:12:22 +00:00
TuxSH
b545295f32 thermosphere: add esr_el2 to exception frame 2020-02-04 19:12:22 +00:00
TuxSH
7bf92888a4 thermosphere: allow each core to pause itself in a lock-free manner & fix bugs 2020-02-04 19:12:22 +00:00
TuxSH
63f5255a3d thermosphere: wip gdb 2020-02-04 19:12:22 +00:00
TuxSH
e4b2745e7c thermosphere: copy paste lots of gdb luma files (but don't build them yet) 2020-02-04 19:12:22 +00:00
TuxSH
d80299d9ce thermosphere: minor changes 2020-02-04 19:12:21 +00:00
TuxSH
0014991378 thermosphere: incl pattern utils 2020-02-04 19:12:21 +00:00
TuxSH
cfdf1e7ec6 thermosphere: unfuck sw breakpoint logic 2020-02-04 19:12:21 +00:00
TuxSH
09bb173757 thermosphere: uninline recursive lock funcs 2020-02-04 19:12:21 +00:00
TuxSH
2d32a812b7 thermosphere: implement reading and writing guest memory 2020-02-04 19:12:21 +00:00
TuxSH
aebdb2a774 thermosphere: we expose a GICv2, not a GICv1 2020-02-04 19:12:21 +00:00
TuxSH
f943b8e94f thermosphere: use ish instead of sy in most places 2020-02-04 19:12:21 +00:00
TuxSH
cb38236bf0 thermosphere: fix fmt.c "l" handling 2020-02-04 19:12:21 +00:00
TuxSH
7553580b64 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
2020-02-04 19:12:21 +00:00
TuxSH
5eb2d79996 thermosphere: disable interrupts in debugPauseCores 2020-02-04 19:12:21 +00:00
TuxSH
1c707d9ded thermosphere: rewrite debug pause & fix single step state machine 2020-02-04 19:12:21 +00:00
TuxSH
2753b6cf8f thermosphere: trap set/way dcache access
note: qemu does not implement the trap
2020-02-04 19:12:21 +00:00
TuxSH
114cdc5aa4 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)
2020-02-04 19:12:21 +00:00
TuxSH
fbdd941061 thermosphere: add debug pause logic 2020-02-04 19:12:21 +00:00
TuxSH
3e7e658594 thermosphere: add common asm macros 2020-02-04 19:12:21 +00:00
TuxSH
84a2dc4ad9 thermosphere: add fpu regs save/restore 2020-02-04 19:12:21 +00:00
TuxSH
c085a67150 thermosphere: add cctx->userFrame 2020-02-04 19:12:21 +00:00
TuxSH
674f3d0fc9 thermosphere: fix ptimer time freezing (again) 2020-02-04 19:12:21 +00:00
TuxSH
e5f6440c3f thermosphere: properly implement guest timer stuff 2020-02-04 19:12:21 +00:00
TuxSH
3b542e749f thermosphere: add TransportInterface abstraction layer 2020-02-04 19:12:21 +00:00
TuxSH
26bda4f32d thermosphere: refactor tegra uart code, etc. 2020-02-04 19:12:21 +00:00
TuxSH
a552c254e0 thermosphere: pl011 uart refactor 2020-02-04 19:12:20 +00:00
TuxSH
57548e67fb thermosphere: fix pl101 uart reg definitions 2020-02-04 19:12:20 +00:00
TuxSH
edb942a032 thermosphere: add proper memory/instruction barriers for breakpoint stuff 2020-02-04 19:12:20 +00:00
TuxSH
0dd5f1f6d4 thermosphere: add hypervisor timer code 2020-02-04 19:12:20 +00:00
TuxSH
4d8a07943c thermosphere: qemu: get rid of arm tf
qemu impls psci anyway
2020-02-04 19:12:20 +00:00
TuxSH
f19c67435a thermosphere: refactor exception handlers & add stolen time/emulated ptimer logic 2020-02-04 19:12:20 +00:00
TuxSH
2f999497df thermosphere: rewrite sysreg trapping code, add skeleton code for timer val trap handling; support A32 EL1 once again 2020-02-04 19:12:20 +00:00
TuxSH
a67d682c10 thermosphere: don't trap memory register writes/don't migrate sw breakpoints
Makes no sense on a system with ASLR
2020-02-04 19:12:20 +00:00
TuxSH
2219494675 thermosphere: vgic: largely reduce the number of mmio accesses
since we have to use 64 bits for VirqState anyway
2020-02-04 19:12:20 +00:00
TuxSH
d560330a9d thermosphere: make the pending virq list ordering stable 2020-02-04 19:12:20 +00:00
TuxSH
3424e0bf71 thermosphere: fix wrong icfgr shift; fix list handling bug 2020-02-04 19:12:20 +00:00
TuxSH
7d30fce54c thermosphere: vgic: fix OOB accesses, fix icfgr and itargetsr handling
qemu actually allows SPIs to use the N-N model
2020-02-04 19:12:20 +00:00
TuxSH
81a3b4fff5 thermosphere: fix is/ic registers usage; fix offset calculation 2020-02-04 19:12:20 +00:00
TuxSH
d43d1af62a thermosphere: fix truncation in vgicCleanupPendingList 2020-02-04 19:12:20 +00:00
TuxSH
7573d1ad3e thermosphere: honor irq config for ppis 2020-02-04 19:12:20 +00:00
TuxSH
5f83df2599 thermosphere: yikes 2020-02-04 19:12:20 +00:00
TuxSH
aeca48503b thermosphere: use strict volatile bitfields just in case 2020-02-04 19:12:20 +00:00
TuxSH
0fb5f81e8a thermosphere: vgic: fix critical bug in vgicUpdateState, add more checks
Yikes.
2020-02-04 19:12:20 +00:00
TuxSH
b0d258209c thermosphere: add CFI where needed, add PANIC macro, etc. 2020-02-04 19:12:20 +00:00
TuxSH
c365fff119 thermosphere: vgic: mostly fix vSGI handling, remove unimplementable/unused stuff + bugfixes
Still somewhat broken, though
2020-02-04 19:12:20 +00:00
TuxSH
0b532a0dfb thermosphere: fix guest access to irq 25, etc; we don't need to raise VI manually
See Armv8a TRM "Virtual IRQ exception"
2020-02-04 19:12:20 +00:00
TuxSH
1345aef693 thermosphere: add PPI definitions 2020-02-04 19:12:20 +00:00
TuxSH
eda6a8d8d6 vgic: fix multiple bugs 2020-02-04 19:12:20 +00:00
TuxSH
f75f584f2f thermosphere: fix various vgic bugs; fix register access OOB bug (xzr) 2020-02-04 19:12:20 +00:00
TuxSH
62fe082cd4 thermosphere: vgic: fix enabled state of virqs 2020-02-04 19:12:20 +00:00
TuxSH
6cef320bc1 thermosphere: fix multiple bugs 2020-02-04 19:12:19 +00:00
TuxSH
e7b351ddb8 thermosphere: vgic code draft 2020-02-04 19:12:19 +00:00
TuxSH
9787bca325 thermosphere: also trap GICH (to deny access) 2020-02-04 19:12:19 +00:00
TuxSH
bb1ba5308d thermosphere: handle stage2 data aborts, trap gicd accesses 2020-02-04 19:12:19 +00:00
TuxSH
442f4ef9ef thermosphere: implement stop point broadcast 2020-02-04 19:12:19 +00:00
TuxSH
3af20ff7a2 thermopshere: add "execute function" sgi 2020-02-04 19:12:19 +00:00
TuxSH
322d796004 thermosphere: barrier & active core mask 2020-02-04 19:12:19 +00:00
TuxSH
c34df08ed9 thermosphere: handle physical IRQs 2020-02-04 19:12:19 +00:00
TuxSH
62fd2cd94d thermosphere: add gicv2 register definitions 2020-02-04 19:12:19 +00:00
TuxSH
e71974085e thermosphere: sw breakpoint code, etc. 2020-02-04 19:12:19 +00:00
TuxSH
577daaebf0 thermosphere: remove breakpoint/watchpoint reg dump functions 2020-02-04 19:12:19 +00:00
TuxSH
aad18182f4 thermosphere: add watchpoint + watchpoint merging code 2020-02-04 19:12:19 +00:00
TuxSH
0435b73f63 thermosphere: refactor crt0 + watchpoint init 2020-02-04 19:12:19 +00:00
TuxSH
bd93b01e57 thermosphere: add actual breakpoint code 2020-02-04 19:12:19 +00:00
TuxSH
88218f606c thermosphere: add breakpoint/watchpoint enable/reset code 2020-02-04 19:12:19 +00:00
TuxSH
5081174d27 thermopshere: refactor & fix single-stepping code 2020-02-04 19:12:19 +00:00
TuxSH
731d50a3a3 thermopshere: refactor jump-to-kernel ,add single-step code
not working under qemu yet though
2020-02-04 19:12:19 +00:00
TuxSH
9c9f6c04cc thermosphere: add spinlock code 2020-02-04 19:12:19 +00:00
TuxSH
0c0d9f5335 thermometer: yeet most a32 support code 👌 2020-02-04 19:12:19 +00:00
TuxSH
7f9c80abec thermosphere: impl stage2 translation 2020-02-04 19:12:19 +00:00
TuxSH
c33d2ee369 thermosphere: rework linkscrips, use discardable sections, better sp pivot on crash 2020-02-04 19:12:19 +00:00
TuxSH
0b1ab362c6 thermosphere: add shadow page table hooks
note: HCR.TVM not supported by qemu yet
2020-02-04 19:12:19 +00:00
TuxSH
823b2c8a6d thermosphere: enable EL2 stage1 translation (doesn't take much space)
Identity map using 1GB L1 blocks
2020-02-04 19:12:19 +00:00
TuxSH
a35b3ff982 thermosphere: fix x18 init, etc. 2020-02-04 19:12:19 +00:00
TuxSH
7f094044b2 thermosphere: add semihosting support & load a kernel using it when needed
basically host i/o
2020-02-04 19:12:19 +00:00
TuxSH
6cbf5628d4 thermosphere: seriaLog => debugLog, add DEBUG macro 2020-02-04 19:12:19 +00:00
TuxSH
abe524fd79 thermosphere: cpu_on hook & skeleton for other PSCI functions 2020-02-04 19:12:19 +00:00
TuxSH
2bc1dc5ac2 thermosphere: add smc trap handler 2020-02-04 19:12:18 +00:00
TuxSH
a47ad0b155 thermosphere: use adrp 2020-02-04 19:12:18 +00:00
TuxSH
e6db007a22 thermosphere: enable traps, works around qemu brk bug 2020-02-04 19:12:18 +00:00
TuxSH
ebef9b92e4 thermosphere: Fix wrong register allocation 2020-02-04 19:12:18 +00:00
TuxSH
af80d5816b thermosphere: unfuck qemu JIT, fix exc. handling bug, add cache funcs 2020-02-04 19:12:18 +00:00
TuxSH
1f767fcce9 thermosphere: use x18 but qemu shits the bed 2020-02-04 19:12:18 +00:00
TuxSH
3769493300 thermosphere: add core_ctx.c/h 2020-02-04 19:12:18 +00:00
TuxSH
eeee49404d fml coke spilled all over this laptop's keyboard 2020-02-04 19:12:18 +00:00
TuxSH
df38a00b86 thermosphere: sysreg stuff, continued 2020-02-04 19:12:18 +00:00
TuxSH
53785b2281 thermosphere: add remaining sysreg passthrough stuff 2020-02-04 19:12:18 +00:00
TuxSH
07039902f7 thermosphere: more sysreg code 2020-02-04 19:12:18 +00:00
TuxSH
e06131c114 thermosphere: add some basic sysreg trapping code 2020-02-04 19:12:18 +00:00
TuxSH
001cd7a7b0 thermosphere: proper uart_reset impl for uart-b 2020-02-04 19:12:18 +00:00
TuxSH
24f0af9e02 thermosphere: fix uart fifo init/flushing 2020-02-04 19:12:18 +00:00
TuxSH
36911b1365 thermosphere: rebase, fix some bugs
uart now works except for fifo flush
2020-02-04 19:12:18 +00:00
TuxSH
532907c9e7 thermosphere: add more sysreg stuff & start writing trap stuff 2020-02-04 19:12:18 +00:00
TuxSH
fe0c3835b6 thermosphere: add sysreg list 2020-02-04 19:12:18 +00:00
TuxSH
11c1d926e2 thermosphere: add hypercall support... even if unused 2020-02-04 19:12:18 +00:00
TuxSH
f2d22ccdef thermosphere: add ExceptionSyndromeRegister definition 2020-02-04 19:12:18 +00:00
TuxSH
114cd464e8 thermosphere: start exception handling 2020-02-04 19:12:18 +00:00
TuxSH
8e73bdef4c thermosphere: add qemu support 2020-02-04 19:12:18 +00:00
TuxSH
02c27a482a thermosphere: uart fixes/ still not working 2020-02-04 19:12:18 +00:00
TuxSH
3c2ff2933a thermosphere: rebase, doesn't work 2020-02-04 19:12:18 +00:00
TuxSH
c326492464 thermosphere: uart refactor, now it doesn't work at all 2020-02-04 19:12:18 +00:00
TuxSH
20e5689f04 thermosphere: set correct gpio config for uart (thanks @hexkyz) 2020-02-04 19:12:18 +00:00
TuxSH
1d225ed5f8 thermosphere: attempt to output to uart-c 2020-02-04 19:12:18 +00:00
TuxSH
acae18365b thermosphere: fix bugs:
- missing barriers after setting elr/spsr
- .text.start* matching .text.startup (which contains main, thanks @fincs)
2020-02-04 19:12:18 +00:00
TuxSH
b124c4383e thermosphere: attempt to run 2020-02-04 19:12:18 +00:00
TuxSH
5bc923ea00 thermosphere: "write" placeholder code 2020-02-04 19:12:18 +00:00
TuxSH
2e2976efba thermosphere: remove legacy code 2020-02-04 19:12:18 +00:00
1342 changed files with 22077 additions and 99595 deletions

1
.gitignore vendored
View File

@@ -64,7 +64,6 @@ dkms.conf
# Distribution files
*.tgz
*.zip
*.bz2
# IDA binaries
*.id0

View File

@@ -58,7 +58,6 @@ dist-no-debug: all
mkdir atmosphere-$(AMSVER)/switch
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000008
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/010000000000000D
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/010000000000002B
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000032
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000034
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000036
@@ -83,7 +82,6 @@ dist-no-debug: all
cp -r config_templates/hbl_html atmosphere-$(AMSVER)/atmosphere/hbl_html
cp stratosphere/boot2/boot2.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000008/exefs.nsp
cp stratosphere/dmnt/dmnt.nsp atmosphere-$(AMSVER)/atmosphere/contents/010000000000000D/exefs.nsp
cp stratosphere/erpt/erpt.nsp atmosphere-$(AMSVER)/atmosphere/contents/010000000000002B/exefs.nsp
cp stratosphere/eclct.stub/eclct.stub.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000032/exefs.nsp
cp stratosphere/fatal/fatal.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000034/exefs.nsp
cp stratosphere/creport/creport.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000036/exefs.nsp
@@ -135,7 +133,6 @@ dist: dist-no-debug
cp stratosphere/ro/ro.elf atmosphere-$(AMSVER)-debug/ro.elf
cp stratosphere/sm/sm.elf atmosphere-$(AMSVER)-debug/sm.elf
cp stratosphere/spl/spl.elf atmosphere-$(AMSVER)-debug/spl.elf
cp stratosphere/erpt/erpt.elf atmosphere-$(AMSVER)-debug/erpt.elf
cd atmosphere-$(AMSVER)-debug; zip -r ../atmosphere-$(AMSVER)-debug.zip ./*; cd ../;
rm -r atmosphere-$(AMSVER)-debug
mv atmosphere-$(AMSVER)-debug.zip out/atmosphere-$(AMSVER)-debug.zip

View File

@@ -28,7 +28,6 @@ You can find a copy of the license in the [LICENSE file](LICENSE).
Exemptions:
* The [yuzu emulator project](https://github.com/yuzu-emu/yuzu) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the Atmosphère project as GPLv2 or later.
* [Nintendo](https://github.com/Nintendo) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the Atmosphère project under the Zero-Clause BSD license.
Credits
=====

View File

@@ -18,5 +18,3 @@ enable_user_pmu_access = 0
[stratosphere]
; To force-enable nogc, add nogc = 1
; To force-disable nogc, add nogc = 0
; To opt in to using Atmosphere's NCM reimplementation, add enable_ncm = 1

View File

@@ -1,82 +1,4 @@
# Changelog
## 0.11.1
+ A bug was fixed that could cause owls to flicker under certain circumstances.
+ For those interested in technical details, in 10.0.0 kernelldr/kernel no longer set cpuactlr_el1, assuming that it was set correctly by the secure monitor.
+ However, exosphere did not set cpuactlr_el1. This meant that the register held the reset value going into boot.
+ This caused a variety of highly erratic symptoms, including causing basically any game to crash seemingly randomly.
+ A number of other major inaccuracies in exosphere were corrected.
+ General system stability improvements to enhance the user's experience.
## 0.11.0
+ Support was added for 10.0.0.
+ Exosphere has been updated to reflect the new key import semantics in 10.0.0.
+ kernel_ldr now implements physical ASLR for the kernel's backing pages.
+ Loader, NCM, and PM have been updated to reflect the changes Nintendo made in 10.0.0.
+ Creport was updated to use the new `pgl` service to terminate processes instead of `ns:dev`.
+ A reimplementation of the `erpt` (error reports) system module was added.
+ In previous versions of Atmosphere, a majority of error reports were prevented via a combination of custom creport, fatal, and stubbed eclct.
+ However, error reports were still generated via some system actions.
+ Most notably, any time the error applet appeared, an error report was generated.
+ By default, atmosphere disabled the *uploading* of error reports, but going online in OFW after an error report occurred in Atmosphere could lead to undesirable telemetry.
+ Atmosphere's `erpt` reimplementation allows the system to interact with existing error reports as expected.
+ However, all new error reports are instead saved to the sd card (`/atmosphere/erpt_reports`), and are not committed to the system savegame.
+ Users curious about what kind of telemetry is being prevented can view the reports as they're generated in there.
+ Reports are saved as msgpack (as this is what Nintendo uses).
+ Please note, not all telemetry is disabled. Play reports and System reports will continue to function unmodified.
+ With atmosphere's `erpt` implementation, homebrew can now use the native error applet to display errors without worrying about generating undesirable telemetry.
+ libstratosphere and libvapours received a number of improvements.
+ With thanks to @Adubbz for his work, the NCM namespace now has client code.
+ This lays the groundwork for first-class system update/downgrade homebrew support in the near future.
+ In particular, code implementing the os namespace is significantly more accurate.
+ In addition, Nintendo's allocators were implemented, allowing for identical memory efficiency versus Nintendo's implementations.
+ General system stability improvements to enhance the user's experience.
## 0.10.5
+ Changes were made to the way fs.mitm builds images when providing a layeredfs romfs.
+ Building romfs metadata previously had a memory cost of about ~4-5x the file table size.
+ This caused games that have particularly enormous file metadata tables (> 4 MB) to exhaust fs.mitm's 16 MB memory pool.
+ The code that creates romfs images was thus changed to be significantly more memory efficient, again.
+ Memory requirements have been lowered from ~4x file table size to ~2x file table size + 0.5 MB.
+ There is a slight speed penalty to this, but testing on Football Manager 2020 only took an extra ~1.5 seconds for the game to boot with many modded files.
+ This shouldn't be noticeable thanks to the async changes made in 0.10.2.
+ If you encounter a game that exhausts ams.mitm's memory (crashing it) when loading layeredfs mods, please contact SciresM.
+ Romfs building can be made even more memory efficient, but unless games show up with even more absurdly huge file tables it seems not worth the speed trade-off.
+ A bug was fixed that caused Atmosphere's fatal error context to not dump TLS for certain processes.
+ General system stability improvements to enhance the user's experience.
## 0.10.4
+ With major thanks to @Adubbz for his work, the NCM system module has now been re-implemented.
+ This was a major stepping stone towards the goal of having implementations everything in the Switch's package1/package2 firmware.
+ This also lays the groundwork for libstratosphere providing bindings for changing the installed version of the Switch's OS.
+ **Please Note**: The NCM implementation will initially be opt-in.
+ The Atmosphere team is confident in our NCM implementation (and we have tested it on every firmware version).
+ That said, this is our first system module that manages NAND savegames -- and caution is a habit.
+ We do not anticipate any issues that didn't come up in testing, so this is just our being particularly careful.
+ Users interested in opting in to using our implementation should set `stratosphere!ncm_enabled = 1` in BCT.ini.
+ In the unlikely event that any issues are encountered, please report them to @SciresM.
+ The NCM implementation will stop being opt-in in a future update, after thorough testing has been done in practice.
+ A bug was fixed in emummc that caused Nintendo path to be corrupted on 1.0.0.
+ This manifested as the emummc folder being created inside the virtual NAND instead of the SD card.
+ It's unlikely there are any negative consequences to this in practice.
+ If you want to be truly sure, you can re-clone sysmmc before updating a 1.0.0 emummc to latest firmware.
+ Stratosphere system modules now use new Nintendo-style FS bindings instead of stdio.
+ This saves a modest amount of memory due to leaner code, and greatly increases the accuracy of several components.
+ These bindings will make it easier for other system modules using libstratosphere to interact with the filesystem.
+ This also lays the groundwork for changes necessary to support per-emummc Atmosphere folders in a future update.
+ Atmosphere's fatal error context now dumps 0x100 of TLS.
+ This will make it much easier to fix bugs when an error report is dumped for whatever caused the crash.
+ General system stability improvements to enhance the user's experience.
## 0.10.3
+ Support was added for 9.2.0.
+ Support was added for redirecting manual html content for games.
+ This works like normal layeredfs, replacing content placed in `/atmosphere/contents/<program id>/manual_html/`.
+ This allows for game mods/translations to provide custom manual content, if they so choose.
+ A number of improvements were made to Atmosphere's memory usage, including:
+ `fatal` now uses STB instead of freetype for rendering.
+ This saves around 1 MB of memory, and makes our fatal substantially leaner than Nintendo's.
+ `sm` no longer wastes 2 MiB unnecessarily.
+ fusee/sept's sdmmc access now better matches official behavior.
+ This improves compatibility with some SD cards.
+ `ro` has been updated to reflect changes made in 9.1.0.
+ The temporary auto-migration added in 0.10.0 has been removed, since the transitionary period is well over.
+ General system stability improvements to enhance the user's experience.
## 0.10.2
+ hbl configuration was made more flexible.
+ Up to eight specific program ids can now be specified to have their own override keys.

View File

@@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/m4xw/emuMMC
branch = develop
commit = b168ddf5fbb31013ff529a4859110c82b11eb361
parent = c07f54f3709a4710e0aead6c91139fa0893b5e5c
commit = bd81a674a946c30b566e1732a95c18f19b701558
parent = 6ee525201ccef107c61d81ba73c891e3eb5f0215
method = rebase
cmdver = 0.4.1
cmdver = 0.4.0

View File

@@ -1,21 +1,21 @@
# emuMMC
*A SDMMC driver replacement for Nintendo's Filesystem Services, by **m4xw***
*A SDMMC driver replacement for Nintendo's Filesystem Services, by **m4xw***
### Supported Horizon Versions
**1.0.0 - 10.0.0**
**1.0.0 - 9.1.0**
## Features
* Arbitrary SDMMC backend selection
* Arbitrary SDMMC backend selection
**This allows loading eMMC from SD or even SD from eMMC**
* On the fly hooking / patching, fully self-infesting
* On the fly hooking / patching, fully self-infesting
**Only one payload required for all versions!**
* File-based SDMMC backend support (from SD)
* File-based SDMMC backend support (from SD)
**This allows loading eMMC images from hekate-backups (split or not)**
* SDMMC device based sector offset (*currently eMMC only*)
* SDMMC device based sector offset (*currently eMMC only*)
**Raw partition support for eMMC from SD with less performance overhead**
* Full support for `/Nintendo` folder redirection to a arbitrary path
* Full support for `/Nintendo` folder redirection to a arbitrary path
**No 8 char length restriction!**
* exosphere based context configuration
* exosphere based context configuration
**This includes full support for multiple emuMMC images**
## Compiling

View File

@@ -45,8 +45,6 @@
#include "offsets/900_exfat.h"
#include "offsets/910.h"
#include "offsets/910_exfat.h"
#include "offsets/1000.h"
#include "offsets/1000_exfat.h"
#include "../utils/fatal.h"
#define GET_OFFSET_STRUCT_NAME(vers) g_offsets##vers
@@ -102,8 +100,6 @@ DEFINE_OFFSET_STRUCT(_900);
DEFINE_OFFSET_STRUCT(_900_EXFAT);
DEFINE_OFFSET_STRUCT(_910);
DEFINE_OFFSET_STRUCT(_910_EXFAT);
DEFINE_OFFSET_STRUCT(_1000);
DEFINE_OFFSET_STRUCT(_1000_EXFAT);
const fs_offsets_t *get_fs_offsets(enum FS_VER version) {
switch (version) {
@@ -165,10 +161,6 @@ const fs_offsets_t *get_fs_offsets(enum FS_VER version) {
return &(GET_OFFSET_STRUCT_NAME(_910));
case FS_VER_9_1_0_EXFAT:
return &(GET_OFFSET_STRUCT_NAME(_910_EXFAT));
case FS_VER_10_0_0:
return &(GET_OFFSET_STRUCT_NAME(_1000));
case FS_VER_10_0_0_EXFAT:
return &(GET_OFFSET_STRUCT_NAME(_1000_EXFAT));
default:
fatal_abort(Fatal_UnknownVersion);
}

View File

@@ -65,9 +65,6 @@ enum FS_VER
FS_VER_9_1_0,
FS_VER_9_1_0_EXFAT,
FS_VER_10_0_0,
FS_VER_10_0_0_EXFAT,
FS_VER_MAX,
};

View File

@@ -48,8 +48,10 @@
// Nintendo Paths
#define FS_OFFSET_100_NINTENDO_PATHS \
{ \
{.opcode_reg = 8, .adrp_offset = 0x00032C58, .add_rel_offset = 8}, \
{.opcode_reg = 9, .adrp_offset = 0x00032F40, .add_rel_offset = 8}, \
{.opcode_reg = 9, .adrp_offset = 0x00032C58, .add_rel_offset = 4}, \
{.opcode_reg = 8, .adrp_offset = 0x00032C60, .add_rel_offset = 4}, \
{.opcode_reg = 9, .adrp_offset = 0x00032F3C, .add_rel_offset = 4}, \
{.opcode_reg = 8, .adrp_offset = 0x00032F44, .add_rel_offset = 4}, \
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
}

View File

@@ -1,58 +0,0 @@
/*
* Copyright (c) 2019 m4xw <m4x@m4xw.net>
* Copyright (c) 2019 Atmosphere-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __FS_1000_H__
#define __FS_1000_H__
// Accessor vtable getters
#define FS_OFFSET_1000_SDMMC_ACCESSOR_GC 0x14DC90
#define FS_OFFSET_1000_SDMMC_ACCESSOR_SD 0x14BDA0
#define FS_OFFSET_1000_SDMMC_ACCESSOR_NAND 0x146C20
// Hooks
#define FS_OFFSET_1000_SDMMC_WRAPPER_READ 0x142380
#define FS_OFFSET_1000_SDMMC_WRAPPER_WRITE 0x142460
#define FS_OFFSET_1000_RTLD 0x634
#define FS_OFFSET_1000_RTLD_DESTINATION 0x9C
#define FS_OFFSET_1000_CLKRST_SET_MIN_V_CLK_RATE 0x1415A0
// Misc funcs
#define FS_OFFSET_1000_LOCK_MUTEX 0x28910
#define FS_OFFSET_1000_UNLOCK_MUTEX 0x28960
#define FS_OFFSET_1000_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x1422E0
// Misc Data
#define FS_OFFSET_1000_SD_MUTEX 0xE273E8
#define FS_OFFSET_1000_NAND_MUTEX 0xE22DA0
#define FS_OFFSET_1000_ACTIVE_PARTITION 0xE22DE0
#define FS_OFFSET_1000_SDMMC_DAS_HANDLE 0xE0AB90
// NOPs
#define FS_OFFSET_1000_SD_DAS_INIT 0x151CEC
// Nintendo Paths
#define FS_OFFSET_1000_NINTENDO_PATHS \
{ \
{.opcode_reg = 3, .adrp_offset = 0x0006BBA4, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 3, .adrp_offset = 0x00078520, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 3, .adrp_offset = 0x0007ED0C, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 4, .adrp_offset = 0x0009115C, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
}
#endif // __FS_1000_H__

View File

@@ -1,58 +0,0 @@
/*
* Copyright (c) 2019 m4xw <m4x@m4xw.net>
* Copyright (c) 2019 Atmosphere-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __FS_1000_EXFAT_H__
#define __FS_1000_EXFAT_H__
// Accessor vtable getters
#define FS_OFFSET_1000_EXFAT_SDMMC_ACCESSOR_GC 0x14DC90
#define FS_OFFSET_1000_EXFAT_SDMMC_ACCESSOR_SD 0x14BDA0
#define FS_OFFSET_1000_EXFAT_SDMMC_ACCESSOR_NAND 0x146C20
// Hooks
#define FS_OFFSET_1000_EXFAT_SDMMC_WRAPPER_READ 0x142380
#define FS_OFFSET_1000_EXFAT_SDMMC_WRAPPER_WRITE 0x142460
#define FS_OFFSET_1000_EXFAT_RTLD 0x634
#define FS_OFFSET_1000_EXFAT_RTLD_DESTINATION 0x9C
#define FS_OFFSET_1000_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x1415A0
// Misc funcs
#define FS_OFFSET_1000_EXFAT_LOCK_MUTEX 0x28910
#define FS_OFFSET_1000_EXFAT_UNLOCK_MUTEX 0x28960
#define FS_OFFSET_1000_EXFAT_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x1422E0
// Misc Data
#define FS_OFFSET_1000_EXFAT_SD_MUTEX 0xE353E8
#define FS_OFFSET_1000_EXFAT_NAND_MUTEX 0xE30DA0
#define FS_OFFSET_1000_EXFAT_ACTIVE_PARTITION 0xE30DE0
#define FS_OFFSET_1000_EXFAT_SDMMC_DAS_HANDLE 0xE18B90
// NOPs
#define FS_OFFSET_1000_EXFAT_SD_DAS_INIT 0x151CEC
// Nintendo Paths
#define FS_OFFSET_1000_EXFAT_NINTENDO_PATHS \
{ \
{.opcode_reg = 3, .adrp_offset = 0x0006BBA4, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 3, .adrp_offset = 0x00078520, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 3, .adrp_offset = 0x0007ED0C, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 4, .adrp_offset = 0x0009115C, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
}
#endif // __FS_1000_EXFAT_H__

View File

@@ -36,6 +36,7 @@ sdmmc_storage_t sd_storage;
// init vars
bool custom_driver = true;
extern const volatile emuMMC_ctx_t emuMMC_ctx;
// FS funcs
_sdmmc_accessor_gc sdmmc_accessor_gc;
@@ -343,7 +344,7 @@ uint64_t sdmmc_wrapper_controller_close(int mmc_id)
{
return 0;
}
if (mmc_id == FS_SDMMC_EMMC)
{
// Close file handles and unmount

View File

@@ -48,9 +48,6 @@ extern char __argdata__;
// TODO
static char nintendo_path[0x80] = "Nintendo";
// 1.0.0 requires special path handling because it has separate album and contents paths.
#define FS_100_ALBUM_PATH 0
#define FS_100_CONTENTS_PATH 1
static char nintendo_path_album_100[0x100] = "/Nintendo/Album";
static char nintendo_path_contents_100[0x100] = "/Nintendo/Contents";
@@ -278,18 +275,23 @@ void setup_nintendo_paths(void)
// 1.0.0 needs special handling because it uses two paths.
// Do album path
{
snprintf(nintendo_path_album_100, sizeof(nintendo_path_album_100), "/%s/Album", nintendo_path);
int path_len = snprintf(nintendo_path_album_100, sizeof(nintendo_path_album_100), "/%s/Album", nintendo_path);
intptr_t nintendo_album_path_location = (intptr_t)&nintendo_path_album_100;
uintptr_t fs_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[FS_100_ALBUM_PATH].adrp_offset);
write_adrp_add(fs_offsets->nintendo_paths[FS_100_ALBUM_PATH].opcode_reg, fs_adrp_opcode_location, fs_offsets->nintendo_paths[FS_100_ALBUM_PATH].add_rel_offset, nintendo_album_path_location);
intptr_t album_path_location = nintendo_album_path_location + path_len - 6; // "/Album"
uintptr_t fs_n_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[0].adrp_offset);
uintptr_t fs_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[1].adrp_offset);
write_adrp_add(fs_offsets->nintendo_paths[0].opcode_reg, fs_n_adrp_opcode_location, fs_offsets->nintendo_paths[0].add_rel_offset, nintendo_album_path_location);
write_adrp_add(fs_offsets->nintendo_paths[1].opcode_reg, fs_adrp_opcode_location, fs_offsets->nintendo_paths[1].add_rel_offset, album_path_location);
}
// Do contents path
{
snprintf(nintendo_path_contents_100, sizeof(nintendo_path_contents_100), "/%s/Contents", nintendo_path);
int path_len = snprintf(nintendo_path_contents_100, sizeof(nintendo_path_contents_100), "/%s/Contents", nintendo_path);
intptr_t nintendo_contents_path_location = (intptr_t)&nintendo_path_contents_100;
uintptr_t fs_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[FS_100_CONTENTS_PATH].adrp_offset);
write_adrp_add(fs_offsets->nintendo_paths[FS_100_CONTENTS_PATH].opcode_reg, fs_adrp_opcode_location, fs_offsets->nintendo_paths[FS_100_CONTENTS_PATH].add_rel_offset, nintendo_contents_path_location);
intptr_t contents_path_location = nintendo_contents_path_location + path_len - 9; // "/Contents"
uintptr_t fs_n_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[2].adrp_offset);
uintptr_t fs_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[3].adrp_offset);
write_adrp_add(fs_offsets->nintendo_paths[2].opcode_reg, fs_n_adrp_opcode_location, fs_offsets->nintendo_paths[2].add_rel_offset, nintendo_contents_path_location);
write_adrp_add(fs_offsets->nintendo_paths[3].opcode_reg, fs_adrp_opcode_location, fs_offsets->nintendo_paths[3].add_rel_offset, contents_path_location);
}
}
}

View File

@@ -50,18 +50,8 @@ extern "C" {
* @return Result code.
* @note Syscall number 0x55.
* @warning This is a privileged syscall. Use \ref envIsSyscallHinted to check if it is available.
* @warning Only exists on [10.0.0+]. For older versions use \ref svcLegacyQueryIoMapping.
*/
Result svcQueryIoMapping(u64* virtaddr, u64* out_size, u64 physaddr, u64 size);
/**
* @brief Returns a virtual address mapped to a given IO range.
* @return Result code.
* @note Syscall number 0x55.
* @warning This is a privileged syscall. Use \ref envIsSyscallHinted to check if it is available.
* @warning Only exists on [1.0.0-9.2.0]. For newer versions use \ref svcQueryIoMapping.
*/
Result svcLegacyQueryIoMapping(u64* virtaddr, u64 physaddr, u64 size);
Result svcQueryIoMapping(u64* virtaddr, u64 physaddr, u64 size);
/**
* @brief Attaches a device address space to a device.

View File

@@ -17,15 +17,6 @@
.endm
SVC_BEGIN svcQueryIoMapping
STP X0, X1, [SP, #-16]!
SVC 0x55
LDP X3, X4, [SP], #16
STR X1, [X3]
STR X2, [X4]
RET
SVC_END
SVC_BEGIN svcLegacyQueryIoMapping
STR X0, [SP, #-16]!
SVC 0x55
LDR X2, [SP], #16

View File

@@ -25,12 +25,11 @@ enum FatalReason
Fatal_InvalidAccessor,
Fatal_ReadNoAccessor,
Fatal_WriteNoAccessor,
Fatal_IoMappingLegacy,
Fatal_IoMapping,
Fatal_UnknownVersion,
Fatal_BadResult,
Fatal_GetConfig,
Fatal_CloseAccessor,
Fatal_IoMapping,
Fatal_Max
};

View File

@@ -38,15 +38,8 @@ static inline uintptr_t _GetIoMapping(u64 io_addr, u64 io_size)
u64 vaddr;
u64 aligned_addr = (io_addr & ~0xFFFul);
u64 aligned_size = io_size + (io_addr - aligned_addr);
if (emuMMC_ctx.fs_ver >= FS_VER_10_0_0) {
u64 out_size;
if (svcQueryIoMapping(&vaddr, &out_size, aligned_addr, aligned_size) != 0) {
fatal_abort(Fatal_IoMapping);
}
} else {
if (svcLegacyQueryIoMapping(&vaddr, aligned_addr, aligned_size) != 0) {
fatal_abort(Fatal_IoMappingLegacy);
}
if (svcQueryIoMapping(&vaddr, aligned_addr, aligned_size) != 0) {
fatal_abort(Fatal_IoMapping);
}
return (uintptr_t)(vaddr + (io_addr - aligned_addr));
}

View File

@@ -19,7 +19,6 @@
#define _UTIL_H_
#include "types.h"
#include "../emuMMC/emummc_ctx.h"
intptr_t QueryIoMapping(u64 addr, u64 size);
#define byte_swap_32(num) ((num >> 24) & 0xff) | ((num << 8) & 0xff0000) | \
@@ -38,6 +37,4 @@ void usleep(u64 ticks);
void msleep(u64 milliseconds);
void exec_cfg(u32 *base, const cfg_op_t *ops, u32 num_ops);
extern volatile emuMMC_ctx_t emuMMC_ctx;
#endif

View File

@@ -315,7 +315,7 @@ void setup_current_core_state(void) {
uint64_t temp_reg;
/* Setup system registers. */
SET_SYSREG(spsr_el3, 0b1111 << 6 | 0b0101); /* use EL2h+DAIF set initially, may be overwritten later. Not in official code */
SET_SYSREG(spsr_el3, 0b1111 << 6 | 0b1001); /* use EL2h+DAIF set initially, may be overwritten later. Not in official code */
SET_SYSREG(actlr_el3, 0x73ull);
SET_SYSREG(actlr_el2, 0x73ull);

View File

@@ -263,20 +263,19 @@ uint32_t fuse_get_5x_key_generation(void) {
/* Returns the fuse version expected for the firmware. */
uint32_t fuse_get_expected_fuse_version(uint32_t target_firmware) {
static const uint8_t expected_versions[ATMOSPHERE_TARGET_FIRMWARE_COUNT+1] = {
[ATMOSPHERE_TARGET_FIRMWARE_100] = 1,
[ATMOSPHERE_TARGET_FIRMWARE_200] = 2,
[ATMOSPHERE_TARGET_FIRMWARE_300] = 3,
/* [ATMOSPHERE_TARGET_FIRMWARE_302] = 4, */
[ATMOSPHERE_TARGET_FIRMWARE_400] = 5,
[ATMOSPHERE_TARGET_FIRMWARE_500] = 6,
[ATMOSPHERE_TARGET_FIRMWARE_600] = 7,
[ATMOSPHERE_TARGET_FIRMWARE_620] = 8,
[ATMOSPHERE_TARGET_FIRMWARE_700] = 9,
[ATMOSPHERE_TARGET_FIRMWARE_800] = 9,
[ATMOSPHERE_TARGET_FIRMWARE_810] = 10,
[ATMOSPHERE_TARGET_FIRMWARE_900] = 11,
[ATMOSPHERE_TARGET_FIRMWARE_910] = 12,
[ATMOSPHERE_TARGET_FIRMWARE_1000] = 13,
[ATMOSPHERE_TARGET_FIRMWARE_100] = 1,
[ATMOSPHERE_TARGET_FIRMWARE_200] = 2,
[ATMOSPHERE_TARGET_FIRMWARE_300] = 3,
/* [ATMOSPHERE_TARGET_FIRMWARE_302] = 4, */
[ATMOSPHERE_TARGET_FIRMWARE_400] = 5,
[ATMOSPHERE_TARGET_FIRMWARE_500] = 6,
[ATMOSPHERE_TARGET_FIRMWARE_600] = 7,
[ATMOSPHERE_TARGET_FIRMWARE_620] = 8,
[ATMOSPHERE_TARGET_FIRMWARE_700] = 9,
[ATMOSPHERE_TARGET_FIRMWARE_800] = 9,
[ATMOSPHERE_TARGET_FIRMWARE_810] = 10,
[ATMOSPHERE_TARGET_FIRMWARE_900] = 11,
[ATMOSPHERE_TARGET_FIRMWARE_910] = 12,
};
if (target_firmware > ATMOSPHERE_TARGET_FIRMWARE_COUNT) {

View File

@@ -1,32 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef EXOSPHERE_MC0_H
#define EXOSPHERE_MC0_H
#include <stdint.h>
#include "memory_map.h"
/* Exosphere driver for the Tegra X1 MC0. */
static inline uintptr_t get_mc0_base(void) {
return MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_MC0);
}
#define MC0_BASE (get_mc0_base())
#define MAKE_MC0_REG(n) MAKE_REG32(MC0_BASE + n)
#endif

View File

@@ -1,32 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef EXOSPHERE_MC0_H
#define EXOSPHERE_MC0_H
#include <stdint.h>
#include "memory_map.h"
/* Exosphere driver for the Tegra X1 MC1. */
static inline uintptr_t get_mc1_base(void) {
return MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_MC1);
}
#define MC1_BASE (get_mc1_base())
#define MAKE_MC1_REG(n) MAKE_REG32(MC1_BASE + n)
#endif

View File

@@ -13,7 +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/>.
*/
#ifndef EXOSPHERE_MEMORY_MAP_H
#define EXOSPHERE_MEMORY_MAP_H
@@ -48,11 +48,9 @@
#define _MMAPDEV15 ( 0x6000D000ull, 0x1000ull, true ) /* GPIO-1 - GPIO-8 */
#define _MMAPDEV16 ( 0x7000C000ull, 0x1000ull, true ) /* I2C-I2C4 */
#define _MMAPDEV17 ( 0x6000F000ull, 0x1000ull, true ) /* Exception vectors */
#define _MMAPDEV18 ( 0x7001C000ull, 0x1000ull, true ) /* MC0 */
#define _MMAPDEV19 ( 0x7001D000ull, 0x1000ull, true ) /* MC1 */
#define _MMAPDEV20 ( 0x00000000ull, 0x1000ull, true ) /* AMS irampage, NOT mapped at startup */
#define _MMAPDEV21 ( 0x00000000ull, 0x1000ull, true ) /* AMS userpage, NOT mapped at startup */
#define _MMAPDEV22 ( 0x40038000ull, 0x1000ull, true ) /* DEBUG: IRAM */
#define _MMAPDEV18 ( 0x00000000ull, 0x1000ull, true ) /* AMS irampage, NOT mapped at startup */
#define _MMAPDEV19 ( 0x00000000ull, 0x1000ull, true ) /* AMS userpage, NOT mapped at startup */
#define _MMAPDEV20 ( 0x40038000ull, 0x5000ull, true ) /* DEBUG: IRAM */
/* MMIO 7.0.0+. (addr). */
#define _MMAPDEV7X0 ( 0x50041000ull ) /* ARM Interrupt Distributor */
@@ -73,11 +71,9 @@
#define _MMAPDEV7X15 ( 0x6000D000ull ) /* GPIO-1 - GPIO-8 */
#define _MMAPDEV7X16 ( 0x7000C000ull ) /* I2C-I2C4 */
#define _MMAPDEV7X17 ( 0x6000F000ull ) /* Exception vectors */
#define _MMAPDEV7X18 ( 0x7001C000ull ) /* MC0 */
#define _MMAPDEV7X19 ( 0x7001D000ull ) /* MC1 */
#define _MMAPDEV7X20 ( 0x00000000ull ) /* AMS irampage, NOT mapped at startup */
#define _MMAPDEV7X21 ( 0x00000000ull ) /* AMS userpage, NOT mapped at startup */
#define _MMAPDEV7X22 ( 0x40038000ull ) /* DEBUG: IRAM */
#define _MMAPDEV7X18 ( 0x00000000ull ) /* AMS irampage, NOT mapped at startup */
#define _MMAPDEV7X19 ( 0x00000000ull ) /* AMS userpage, NOT mapped at startup */
#define _MMAPDEV7X20 ( 0x40038000ull ) /* DEBUG: IRAM */
/* LP0 entry ram segments (addr, size, additional attributes) */
#define _MMAPLP0ES0 ( 0x40020000ull, 0x10000ull, MMU_PTE_BLOCK_NS | ATTRIB_MEMTYPE_DEVICE ) /* Encrypted TZRAM */
@@ -137,12 +133,10 @@
#define MMIO_DEVID_GPIO 15
#define MMIO_DEVID_DTV_I2C234 16
#define MMIO_DEVID_EXCEPTION_VECTORS 17
#define MMIO_DEVID_MC0 18
#define MMIO_DEVID_MC1 19
#define MMIO_DEVID_AMS_IRAM_PAGE 20
#define MMIO_DEVID_AMS_USER_PAGE 21
#define MMIO_DEVID_DEBUG_IRAM 22
#define MMIO_DEVID_MAX 23
#define MMIO_DEVID_AMS_IRAM_PAGE 18
#define MMIO_DEVID_AMS_USER_PAGE 19
#define MMIO_DEVID_DEBUG_IRAM 20
#define MMIO_DEVID_MAX 21
#define LP0_ENTRY_RAM_SEGMENT_ID_ENCRYPTED_TZRAM 0
#define LP0_ENTRY_RAM_SEGMENT_ID_LP0_ENTRY_CODE 1

View File

@@ -149,7 +149,6 @@ static void setup_se(void) {
case ATMOSPHERE_TARGET_FIRMWARE_810:
case ATMOSPHERE_TARGET_FIRMWARE_900:
case ATMOSPHERE_TARGET_FIRMWARE_910:
case ATMOSPHERE_TARGET_FIRMWARE_1000:
derive_new_device_keys(KEYSLOT_SWITCH_5XNEWDEVICEKEYGENKEY);
break;
}
@@ -339,7 +338,7 @@ static bool validate_package2_metadata(package2_meta_t *metadata) {
/* Perform version checks. */
/* We will be compatible with all package2s released before current, but not newer ones. */
if (metadata->version_max >= PACKAGE2_MINVER_THEORETICAL && metadata->version_min < PACKAGE2_MAXVER_1000_CURRENT) {
if (metadata->version_max >= PACKAGE2_MINVER_THEORETICAL && metadata->version_min < PACKAGE2_MAXVER_910_CURRENT) {
return true;
}
@@ -467,7 +466,6 @@ static void copy_warmboot_bin_to_dram() {
case ATMOSPHERE_TARGET_FIRMWARE_810:
case ATMOSPHERE_TARGET_FIRMWARE_900:
case ATMOSPHERE_TARGET_FIRMWARE_910:
case ATMOSPHERE_TARGET_FIRMWARE_1000:
warmboot_src = (uint8_t *)0x4003E000;
break;
}
@@ -553,9 +551,6 @@ void load_package2(coldboot_crt0_reloc_list_t *reloc_list) {
case ATMOSPHERE_TARGET_FIRMWARE_910:
MAKE_REG32(PMC_BASE + 0x360) = 0x18C;
break;
case ATMOSPHERE_TARGET_FIRMWARE_1000:
MAKE_REG32(PMC_BASE + 0x360) = 0x1AD;
break;
}
}

View File

@@ -73,8 +73,7 @@ static inline uintptr_t get_nx_bootloader_mailbox_base(unsigned int targetfw) {
#define PACKAGE2_MAXVER_700_800 0xA
#define PACKAGE2_MAXVER_810 0xB
#define PACKAGE2_MAXVER_900 0xC
#define PACKAGE2_MAXVER_910_920 0xD
#define PACKAGE2_MAXVER_1000_CURRENT 0xE
#define PACKAGE2_MAXVER_910_CURRENT 0xD
#define PACKAGE2_MINVER_100 0x3
#define PACKAGE2_MINVER_200 0x4
@@ -87,8 +86,7 @@ static inline uintptr_t get_nx_bootloader_mailbox_base(unsigned int targetfw) {
#define PACKAGE2_MINVER_700_800 0xB
#define PACKAGE2_MINVER_810 0xC
#define PACKAGE2_MINVER_900 0xD
#define PACKAGE2_MINVER_910_920 0xE
#define PACKAGE2_MINVER_1000_CURRENT 0xF
#define PACKAGE2_MINVER_910_CURRENT 0xE
typedef struct {
union {

View File

@@ -1,20 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "rsa_common.h"
/* Instantiate the shared RSA data inside a single translation unit. */
rsa_shared_data_t g_rsa_shared_data = {};

View File

@@ -1,36 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef EXOSPHERE_RSA_COMMON_H
#define EXOSPHERE_RSA_COMMON_H
#include <stdint.h>
typedef union {
struct {
uint8_t user_data[0x100];
} storage_exp_mod;
struct {
uint32_t master_key_rev;
uint32_t type;
uint64_t expected_label_hash[4];
} unwrap_titlekey;
} rsa_shared_data_t __attribute__((aligned(4)));
_Static_assert(sizeof(rsa_shared_data_t) == 0x100);
extern rsa_shared_data_t g_rsa_shared_data;
#endif

View File

@@ -13,7 +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/>.
*/
#include <string.h>
#include "utils.h"
@@ -47,25 +47,24 @@ void ll_init(volatile se_ll_t *ll, void *buffer, size_t size) {
ll->addr_info.address = 0;
ll->addr_info.size = 0;
}
flush_dcache_range((uint8_t *)ll, (uint8_t *)ll + sizeof(*ll));
}
void set_security_engine_callback(unsigned int (*callback)(void)) {
/* Set the callback. */
g_se_callback = callback;
if (callback == NULL || g_se_callback != NULL) {
generic_panic();
}
/* Enable SE Interrupt firing for async op. */
se_get_regs()->SE_INT_ENABLE = 0x10;
g_se_callback = callback;
}
/* Fires on Security Engine operation completion. */
void se_operation_completed(void) {
se_get_regs()->SE_INT_ENABLE = 0;
unsigned int (*callback)(void) = g_se_callback;
if (callback != NULL) {
if (g_se_callback != NULL) {
g_se_callback();
g_se_callback = NULL;
callback();
}
}
@@ -104,7 +103,7 @@ void se_validate_stored_vector(void) {
uint8_t calc_vector[0x10];
se_generate_test_vector(calc_vector);
/* Ensure nobody's messed with the security engine while we slept. */
if (memcmp(calc_vector, g_se_stored_test_vector, 0x10) != 0) {
generic_panic();
@@ -123,7 +122,7 @@ void se_generate_stored_vector(void) {
/* Set the flags for an AES keyslot. */
void set_aes_keyslot_flags(unsigned int keyslot, unsigned int flags) {
volatile tegra_se_t *se = se_get_regs();
if (keyslot >= KEYSLOT_AES_MAX) {
generic_panic();
}
@@ -142,7 +141,7 @@ void set_aes_keyslot_flags(unsigned int keyslot, unsigned int flags) {
/* Set the flags for an RSA keyslot. */
void set_rsa_keyslot_flags(unsigned int keyslot, unsigned int flags) {
volatile tegra_se_t *se = se_get_regs();
if (keyslot >= KEYSLOT_RSA_MAX) {
generic_panic();
}
@@ -161,7 +160,7 @@ void set_rsa_keyslot_flags(unsigned int keyslot, unsigned int flags) {
void clear_aes_keyslot(unsigned int keyslot) {
volatile tegra_se_t *se = se_get_regs();
if (keyslot >= KEYSLOT_AES_MAX) {
generic_panic();
}
@@ -175,7 +174,7 @@ void clear_aes_keyslot(unsigned int keyslot) {
void clear_rsa_keyslot(unsigned int keyslot) {
volatile tegra_se_t *se = se_get_regs();
if (keyslot >= KEYSLOT_RSA_MAX) {
generic_panic();
}
@@ -195,7 +194,7 @@ void clear_rsa_keyslot(unsigned int keyslot) {
void set_aes_keyslot(unsigned int keyslot, const void *key, size_t key_size) {
volatile tegra_se_t *se = se_get_regs();
if (keyslot >= KEYSLOT_AES_MAX || key_size > KEYSIZE_AES_MAX) {
generic_panic();
}
@@ -208,7 +207,7 @@ void set_aes_keyslot(unsigned int keyslot, const void *key, size_t key_size) {
void set_rsa_keyslot(unsigned int keyslot, const void *modulus, size_t modulus_size, const void *exponent, size_t exp_size) {
volatile tegra_se_t *se = se_get_regs();
if (keyslot >= KEYSLOT_RSA_MAX || modulus_size > KEYSIZE_RSA_MAX || exp_size > KEYSIZE_RSA_MAX) {
generic_panic();
}
@@ -229,7 +228,7 @@ void set_rsa_keyslot(unsigned int keyslot, const void *modulus, size_t modulus_
void set_aes_keyslot_iv(unsigned int keyslot, const void *iv, size_t iv_size) {
volatile tegra_se_t *se = se_get_regs();
if (keyslot >= KEYSLOT_AES_MAX || iv_size > 0x10) {
generic_panic();
}
@@ -242,7 +241,7 @@ void set_aes_keyslot_iv(unsigned int keyslot, const void *iv, size_t iv_size) {
void clear_aes_keyslot_iv(unsigned int keyslot) {
volatile tegra_se_t *se = se_get_regs();
if (keyslot >= KEYSLOT_AES_MAX) {
generic_panic();
}
@@ -261,7 +260,7 @@ void set_se_ctr(const void *ctr) {
void decrypt_data_into_keyslot(unsigned int keyslot_dst, unsigned int keyslot_src, const void *wrapped_key, size_t wrapped_key_size) {
volatile tegra_se_t *se = se_get_regs();
if (keyslot_dst >= KEYSLOT_AES_MAX || keyslot_src >= KEYSIZE_AES_MAX || wrapped_key_size > KEYSIZE_AES_MAX) {
generic_panic();
}
@@ -277,7 +276,7 @@ void decrypt_data_into_keyslot(unsigned int keyslot_dst, unsigned int keyslot_sr
void se_aes_crypt_insecure_internal(unsigned int keyslot, uint32_t out_ll_paddr, uint32_t in_ll_paddr, size_t size, unsigned int crypt_config, bool encrypt, unsigned int (*callback)(void)) {
volatile tegra_se_t *se = se_get_regs();
if (keyslot >= KEYSLOT_AES_MAX) {
generic_panic();
}
@@ -305,6 +304,9 @@ void se_aes_crypt_insecure_internal(unsigned int keyslot, uint32_t out_ll_paddr,
/* Set the callback, for after the async operation. */
set_security_engine_callback(callback);
/* Enable SE Interrupt firing for async op. */
se->SE_INT_ENABLE = 0x10;
/* Setup Input/Output lists */
se->SE_IN_LL_ADDR = in_ll_paddr;
se->SE_OUT_LL_ADDR = out_ll_paddr;
@@ -336,7 +338,7 @@ void se_aes_cbc_decrypt_insecure(unsigned int keyslot, uint32_t out_ll_paddr, ui
se_aes_crypt_insecure_internal(keyslot, out_ll_paddr, in_ll_paddr, size, 0x66, false, callback);
}
void se_exp_mod(unsigned int keyslot, const void *buf, size_t size, unsigned int (*callback)(void)) {
void se_exp_mod(unsigned int keyslot, void *buf, size_t size, unsigned int (*callback)(void)) {
volatile tegra_se_t *se = se_get_regs();
uint8_t stack_buf[KEYSIZE_RSA_MAX];
@@ -346,7 +348,7 @@ void se_exp_mod(unsigned int keyslot, const void *buf, size_t size, unsigned int
/* Endian swap the input. */
for (size_t i = 0; i < size; i++) {
stack_buf[i] = *((const uint8_t *)buf + size - i - 1);
stack_buf[i] = *((uint8_t *)buf + size - i - 1);
}
se->SE_CONFIG = (ALG_RSA | DST_RSAREG);
@@ -356,6 +358,9 @@ void se_exp_mod(unsigned int keyslot, const void *buf, size_t size, unsigned int
set_security_engine_callback(callback);
/* Enable SE interrupt firing for async op. */
se->SE_INT_ENABLE = 0x10;
flush_dcache_range(stack_buf, stack_buf + KEYSIZE_RSA_MAX);
trigger_se_rsa_op(stack_buf, size);
@@ -463,7 +468,7 @@ bool se_rsa2048_pss_verify(const void *signature, size_t signature_size, const v
void trigger_se_rsa_op(void *buf, size_t size) {
volatile tegra_se_t *se = se_get_regs();
se_ll_t in_ll;
ll_init(&in_ll, (void *)buf, size);
/* Set the input LL. */
@@ -486,19 +491,19 @@ void trigger_se_blocking_op(unsigned int op, void *dst, size_t dst_size, const v
ll_init(&in_ll, (void *)src, src_size);
ll_init(&out_ll, dst, dst_size);
__dsb_sy();
/* Set the LLs. */
se->SE_IN_LL_ADDR = (uint32_t) get_physical_address(&in_ll);
se->SE_OUT_LL_ADDR = (uint32_t) get_physical_address(&out_ll);
/* Set registers for operation. */
se->SE_ERR_STATUS = se->SE_ERR_STATUS;
se->SE_INT_STATUS = se->SE_INT_STATUS;
se->SE_OPERATION = op;
(void)(se->SE_OPERATION);
__dsb_ish();
while (!(se->SE_INT_STATUS & 0x10)) { /* Wait a while */ }
@@ -533,7 +538,7 @@ void se_perform_aes_block_operation(void *dst, size_t dst_size, const void *src,
void se_aes_ctr_crypt(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size, const void *ctr, size_t ctr_size) {
volatile tegra_se_t *se = se_get_regs();
if (keyslot >= KEYSLOT_AES_MAX || ctr_size != 0x10) {
generic_panic();
}
@@ -543,7 +548,7 @@ void se_aes_ctr_crypt(unsigned int keyslot, void *dst, size_t dst_size, const vo
}
if (dst_size) {
flush_dcache_range((uint8_t *)dst, (uint8_t *)dst + dst_size);
}
}
unsigned int num_blocks = src_size >> 4;
@@ -571,12 +576,12 @@ void se_aes_ctr_crypt(unsigned int keyslot, void *dst, size_t dst_size, const vo
if (dst_size) {
flush_dcache_range((uint8_t *)dst, (uint8_t *)dst + dst_size);
}
}
}
void se_aes_ecb_encrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size, unsigned int config_high) {
volatile tegra_se_t *se = se_get_regs();
if (keyslot >= KEYSLOT_AES_MAX || dst_size != 0x10 || src_size != 0x10) {
generic_panic();
}
@@ -601,7 +606,7 @@ void se_aes_256_ecb_encrypt_block(unsigned int keyslot, void *dst, size_t dst_si
void se_aes_ecb_decrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size) {
volatile tegra_se_t *se = se_get_regs();
if (keyslot >= KEYSLOT_AES_MAX || dst_size != 0x10 || src_size != 0x10) {
generic_panic();
}
@@ -627,15 +632,15 @@ void shift_left_xor_rb(uint8_t *key) {
void se_compute_aes_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, const void *data, size_t data_size, unsigned int config_high) {
volatile tegra_se_t *se = se_get_regs();
if (keyslot >= KEYSLOT_AES_MAX) {
generic_panic();
}
if (data_size) {
flush_dcache_range((uint8_t *)data, (uint8_t *)data + data_size);
}
/* Generate the derived key, to be XOR'd with final output block. */
uint8_t derived_key[0x10] = {0};
se_aes_ecb_encrypt_block(keyslot, derived_key, sizeof(derived_key), derived_key, sizeof(derived_key), config_high);
@@ -647,7 +652,7 @@ void se_compute_aes_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, con
se->SE_CONFIG = (ALG_AES_ENC | DST_HASHREG) | (config_high << 16);
se->SE_CRYPTO_CONFIG = (keyslot << 24) | (0x145);
clear_aes_keyslot_iv(keyslot);
unsigned int num_blocks = (data_size + 0xF) >> 4;
/* Handle aligned blocks. */
if (num_blocks > 1) {
@@ -655,7 +660,7 @@ void se_compute_aes_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, con
trigger_se_blocking_op(OP_START, NULL, 0, data, data_size);
se->SE_CRYPTO_CONFIG |= 0x80;
}
/* Create final block. */
uint8_t last_block[0x10] = {0};
if (data_size & 0xF) {
@@ -664,11 +669,11 @@ void se_compute_aes_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, con
} else if (data_size >= 0x10) {
memcpy(last_block, data + data_size - 0x10, 0x10);
}
for (unsigned int i = 0; i < 0x10; i++) {
last_block[i] ^= derived_key[i];
}
/* Perform last operation. */
se->SE_CRYPTO_LAST_BLOCK = 0;
flush_dcache_range(last_block, last_block + sizeof(last_block));
@@ -689,11 +694,11 @@ void se_compute_aes_256_cmac(unsigned int keyslot, void *cmac, size_t cmac_size,
void se_aes_256_cbc_encrypt(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size, const void *iv) {
volatile tegra_se_t *se = se_get_regs();
if (keyslot >= KEYSLOT_AES_MAX || src_size < 0x10) {
generic_panic();
}
se->SE_CONFIG = (ALG_AES_ENC | DST_MEMORY) | (0x202 << 16);
se->SE_CRYPTO_CONFIG = (keyslot << 24) | 0x144;
set_aes_keyslot_iv(keyslot, iv, 0x10);
@@ -704,7 +709,7 @@ void se_aes_256_cbc_encrypt(unsigned int keyslot, void *dst, size_t dst_size, co
/* SHA256 Implementation. */
void se_calculate_sha256(void *dst, const void *src, size_t src_size) {
volatile tegra_se_t *se = se_get_regs();
/* Setup config for SHA256, size = BITS(src_size) */
se->SE_CONFIG = (ENCMODE_SHA256 | ALG_SHA | DST_HASHREG);
se->SE_SHA_CONFIG = 1;
@@ -716,7 +721,7 @@ void se_calculate_sha256(void *dst, const void *src, size_t src_size) {
se->SE_SHA_MSG_LEFT[1] = 0;
se->SE_SHA_MSG_LEFT[2] = 0;
se->SE_SHA_MSG_LEFT[3] = 0;
/* Trigger the operation. */
trigger_se_blocking_op(OP_START, NULL, 0, src, src_size);
@@ -729,7 +734,7 @@ void se_calculate_sha256(void *dst, const void *src, size_t src_size) {
/* RNG API */
void se_initialize_rng(unsigned int keyslot) {
volatile tegra_se_t *se = se_get_regs();
if (keyslot >= KEYSLOT_AES_MAX) {
generic_panic();
}
@@ -749,7 +754,7 @@ void se_initialize_rng(unsigned int keyslot) {
void se_generate_random(unsigned int keyslot, void *dst, size_t size) {
volatile tegra_se_t *se = se_get_regs();
if (keyslot >= KEYSLOT_AES_MAX) {
generic_panic();
}
@@ -772,7 +777,7 @@ void se_generate_random(unsigned int keyslot, void *dst, size_t size) {
/* SE context save API. */
void se_set_in_context_save_mode(bool is_context_save_mode) {
volatile tegra_se_t *se = se_get_regs();
uint32_t val = se->SE_SE_SECURITY;
if (is_context_save_mode) {
val |= 0x10000;
@@ -786,7 +791,7 @@ void se_set_in_context_save_mode(bool is_context_save_mode) {
void se_generate_random_key(unsigned int dst_keyslot, unsigned int rng_keyslot) {
volatile tegra_se_t *se = se_get_regs();
if (dst_keyslot >= KEYSLOT_AES_MAX || rng_keyslot >= KEYSLOT_AES_MAX) {
generic_panic();
}
@@ -796,7 +801,7 @@ void se_generate_random_key(unsigned int dst_keyslot, unsigned int rng_keyslot)
se->SE_CRYPTO_CONFIG = (rng_keyslot << 24) | 0x108;
se->SE_RNG_CONFIG = 4;
se->SE_CRYPTO_LAST_BLOCK = 0;
/* Generate low part of key. */
se->SE_CRYPTO_KEYTABLE_DST = (dst_keyslot << 8);
trigger_se_blocking_op(OP_START, NULL, 0, NULL, 0);
@@ -807,7 +812,7 @@ void se_generate_random_key(unsigned int dst_keyslot, unsigned int rng_keyslot)
void se_generate_srk(unsigned int srkgen_keyslot) {
volatile tegra_se_t *se = se_get_regs();
se->SE_CONFIG = (ALG_RNG | DST_SRK);
se->SE_CRYPTO_CONFIG = (srkgen_keyslot << 24) | 0x108;
se->SE_RNG_CONFIG = 6;
@@ -842,24 +847,24 @@ void se_save_context(unsigned int srkgen_keyslot, unsigned int rng_keyslot, void
/* Generate the SRK (context save encryption key). */
se_generate_random_key(srkgen_keyslot, rng_keyslot);
se_generate_srk(srkgen_keyslot);
flush_dcache_range(work_buf, work_buf + 0x10);
se_generate_random(rng_keyslot, work_buf, 0x10);
flush_dcache_range(work_buf, work_buf + 0x10);
/* Save random initial block. */
se->SE_CONFIG = (ALG_AES_ENC | DST_MEMORY);
se->SE_CTX_SAVE_CONFIG = (CTX_SAVE_SRC_MEM);
se->SE_CRYPTO_LAST_BLOCK = 0;
se_encrypt_with_srk(dst, 0x10, work_buf, 0x10);
/* Save Sticky Bits. */
for (unsigned int i = 0; i < 0x2; i++) {
se->SE_CTX_SAVE_CONFIG = (CTX_SAVE_SRC_STICKY_BITS) | (i << CTX_SAVE_STICKY_BIT_INDEX_SHIFT);
se->SE_CRYPTO_LAST_BLOCK = 0;
se_encrypt_with_srk(dst + 0x10 + (i * 0x10), 0x10, NULL, 0);
}
/* Save AES Key Table. */
for (unsigned int i = 0; i < KEYSLOT_AES_MAX; i++) {
se->SE_CTX_SAVE_CONFIG = (CTX_SAVE_SRC_KEYTABLE_AES) | (i << CTX_SAVE_KEY_INDEX_SHIFT) | (CTX_SAVE_KEY_LOW_BITS);
@@ -869,21 +874,21 @@ void se_save_context(unsigned int srkgen_keyslot, unsigned int rng_keyslot, void
se->SE_CRYPTO_LAST_BLOCK = 0;
se_encrypt_with_srk(dst + 0x40 + (i * 0x20), 0x10, NULL, 0);
}
/* Save AES Original IVs. */
for (unsigned int i = 0; i < KEYSLOT_AES_MAX; i++) {
se->SE_CTX_SAVE_CONFIG = (CTX_SAVE_SRC_KEYTABLE_AES) | (i << CTX_SAVE_KEY_INDEX_SHIFT) | (CTX_SAVE_KEY_ORIGINAL_IV);
se->SE_CRYPTO_LAST_BLOCK = 0;
se_encrypt_with_srk(dst + 0x230 + (i * 0x10), 0x10, NULL, 0);
}
/* Save AES Updated IVs */
for (unsigned int i = 0; i < KEYSLOT_AES_MAX; i++) {
se->SE_CTX_SAVE_CONFIG = (CTX_SAVE_SRC_KEYTABLE_AES) | (i << CTX_SAVE_KEY_INDEX_SHIFT) | (CTX_SAVE_KEY_UPDATED_IV);
se->SE_CRYPTO_LAST_BLOCK = 0;
se_encrypt_with_srk(dst + 0x330 + (i * 0x10), 0x10, NULL, 0);
}
/* Save RSA Keytable. */
uint8_t *rsa_ctx_out = (uint8_t *)dst + 0x430;
for (unsigned int rsa_key = 0; rsa_key < KEYSLOT_RSA_MAX; rsa_key++) {
@@ -896,13 +901,13 @@ void se_save_context(unsigned int srkgen_keyslot, unsigned int rng_keyslot, void
}
}
}
/* Save "Known Pattern. " */
static const uint8_t context_save_known_pattern[0x10] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
se->SE_CTX_SAVE_CONFIG = (CTX_SAVE_SRC_MEM);
se->SE_CRYPTO_LAST_BLOCK = 0;
se_encrypt_with_srk(dst + 0x830, 0x10, context_save_known_pattern, 0x10);
/* Save SRK into PMC registers. */
se->SE_CTX_SAVE_CONFIG = (CTX_SAVE_SRC_SRK);
se->SE_CRYPTO_LAST_BLOCK = 0;

View File

@@ -13,7 +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/>.
*/
#ifndef EXOSPHERE_SE_H
#define EXOSPHERE_SE_H
@@ -213,7 +213,7 @@ void se_aes_256_cbc_encrypt(unsigned int keyslot, void *dst, size_t dst_size, co
void se_calculate_sha256(void *dst, const void *src, size_t src_size);
/* RSA API */
void se_exp_mod(unsigned int keyslot, const void *buf, size_t size, unsigned int (*callback)(void));
void se_exp_mod(unsigned int keyslot, void *buf, size_t size, unsigned int (*callback)(void));
void se_get_exp_mod_output(void *buf, size_t size);
void se_synchronous_exp_mod(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size);
bool se_rsa2048_pss_verify(const void *signature, size_t signature_size, const void *modulus, size_t modulus_size, const void *data, size_t data_size);

View File

@@ -25,8 +25,6 @@
#include "synchronization.h"
#include "masterkey.h"
#include "mc.h"
#include "mc0.h"
#include "mc1.h"
#include "memory_map.h"
#include "pmc.h"
#include "randomcache.h"
@@ -190,7 +188,6 @@ void set_version_specific_smcs(void) {
case ATMOSPHERE_TARGET_FIRMWARE_810:
case ATMOSPHERE_TARGET_FIRMWARE_900:
case ATMOSPHERE_TARGET_FIRMWARE_910:
case ATMOSPHERE_TARGET_FIRMWARE_1000:
/* No more LoadSecureExpModKey. */
g_smc_user_table[0xE].handler = NULL;
g_smc_user_table[0xC].id = 0xC300D60C;
@@ -436,18 +433,19 @@ uint32_t smc_get_result(smc_args_t *args) {
}
uint32_t smc_exp_mod_get_result(void *buf, uint64_t size) {
uint32_t res = get_exp_mod_result();
if (res == 0) {
if (size == 0x100) {
se_get_exp_mod_output(buf, 0x100);
/* smc_exp_mod is done now. */
clear_user_smc_in_progress();
res = 0;
} else {
res = 2;
}
if (get_exp_mod_done() != 1) {
return 3;
}
return res;
if (size != 0x100) {
return 2;
}
se_get_exp_mod_output(buf, 0x100);
/* smc_exp_mod is done now. */
clear_user_smc_in_progress();
return 0;
}
uint32_t smc_exp_mod(smc_args_t *args) {
@@ -510,31 +508,30 @@ uint32_t smc_unwrap_rsa_oaep_wrapped_titlekey_get_result(void *buf, uint64_t siz
uint8_t aes_wrapped_titlekey[0x10];
uint8_t titlekey[0x10];
uint64_t sealed_titlekey[2];
uint32_t res = get_exp_mod_result();
if (res == 0) {
if (size == 0x10) {
se_get_exp_mod_output(rsa_wrapped_titlekey, 0x100);
if (tkey_rsa_oaep_unwrap(aes_wrapped_titlekey, 0x10, rsa_wrapped_titlekey, 0x100) == 0x10) {
tkey_aes_unwrap(titlekey, 0x10, aes_wrapped_titlekey, 0x10);
seal_titlekey(sealed_titlekey, 0x10, titlekey, 0x10);
p_sealed_key[0] = sealed_titlekey[0];
p_sealed_key[1] = sealed_titlekey[1];
res = 0;
} else {
/* Failed to extract RSA OAEP wrapped key. */
res = 2;
}
/* smc_unwrap_rsa_oaep_wrapped_titlekey is done now. */
clear_user_smc_in_progress();
} else {
res = 2;
}
if (get_exp_mod_done() != 1) {
return 3;
}
return res;
if (size != 0x10) {
return 2;
}
se_get_exp_mod_output(rsa_wrapped_titlekey, 0x100);
if (tkey_rsa_oaep_unwrap(aes_wrapped_titlekey, 0x10, rsa_wrapped_titlekey, 0x100) != 0x10) {
/* Failed to extract RSA OAEP wrapped key. */
clear_user_smc_in_progress();
return 2;
}
tkey_aes_unwrap(titlekey, 0x10, aes_wrapped_titlekey, 0x10);
seal_titlekey(sealed_titlekey, 0x10, titlekey, 0x10);
p_sealed_key[0] = sealed_titlekey[0];
p_sealed_key[1] = sealed_titlekey[1];
/* smc_unwrap_rsa_oaep_wrapped_titlekey is done now. */
clear_user_smc_in_progress();
return 0;
}
uint32_t smc_unwrap_rsa_oaep_wrapped_titlekey(smc_args_t *args) {
@@ -619,7 +616,7 @@ uint32_t smc_read_write_register(smc_args_t *args) {
}
/* Check for PMC registers. */
if (0x7000E400 <= address && address <= 0x7000EFFF) {
static const uint8_t pmc_whitelist[0x28] = {
const uint8_t pmc_whitelist[0x28] = {
0xB9, 0xF9, 0x07, 0x00, 0x00, 0x00, 0x80, 0x03,
0x00, 0x00, 0x00, 0x17, 0x00, 0xC4, 0x07, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x00,
@@ -635,83 +632,39 @@ uint32_t smc_read_write_register(smc_args_t *args) {
} else {
return 2;
}
} else {
if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_500) {
static const uint8_t mc_whitelist_5x[0xD00/(sizeof(uint32_t) * 8)] = {
0x9F, 0x31, 0x30, 0x00, 0xF0, 0xFF, 0xF7, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x40, 0x73, 0x3E, 0x2F, 0x00, 0x00, 0x6E,
0x30, 0x05, 0x06, 0xB0, 0x71, 0xC8, 0x43, 0x04,
0x80, 0xFF, 0x08, 0x80, 0x03, 0x38, 0x8E, 0x1F,
0xC8, 0xFF, 0xFF, 0x00, 0x0E, 0x00, 0x00, 0x00,
0xF0, 0x1F, 0x00, 0x30, 0xF0, 0x03, 0x03, 0x30,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x31, 0x00, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xE4, 0xFF, 0xFF, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x0F,
0x01, 0x00, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00
};
static const uint8_t mc01_whitelist_5x[0xC00/(sizeof(uint32_t) * 8)] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xCD, 0xFE, 0xC0, 0xFE, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00,
};
static const struct {
uint32_t phys_addr;
uint32_t size;
uint64_t virt_addr;
const uint8_t *whitelist;
} register_whitelists[3] = {
{ MMIO_GET_DEVICE_PA(MMIO_DEVID_MC), sizeof(mc_whitelist_5x) * (sizeof(uint32_t) * 8), MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_MC), mc_whitelist_5x },
{ MMIO_GET_DEVICE_PA(MMIO_DEVID_MC0), sizeof(mc01_whitelist_5x) * (sizeof(uint32_t) * 8), MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_MC0), mc01_whitelist_5x },
{ MMIO_GET_DEVICE_PA(MMIO_DEVID_MC1), sizeof(mc01_whitelist_5x) * (sizeof(uint32_t) * 8), MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_MC1), mc01_whitelist_5x },
};
for (unsigned int which = 0; which < 3; which++) {
if (register_whitelists[which].phys_addr <= address && address < register_whitelists[which].phys_addr + register_whitelists[which].size) {
uint32_t offset = (uint32_t)(address - register_whitelists[which].phys_addr);
uint32_t wl_ind = (offset >> 5);
/* If address is whitelisted, allow write. */
if (register_whitelists[which].whitelist[wl_ind] & (1 << ((offset >> 2) & 0x7))) {
p_mmio = (volatile uint32_t *)(register_whitelists[which].virt_addr + offset);
}
break;
}
}
} else if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) {
if (MMIO_GET_DEVICE_PA(MMIO_DEVID_MC) <= address && address < MMIO_GET_DEVICE_PA(MMIO_DEVID_MC) + 0xD00) {
/* Memory Controller RW supported only on 4.0.0+ */
static const uint8_t mc_whitelist[0x68] = {
0x9F, 0x31, 0x30, 0x00, 0xF0, 0xFF, 0xF7, 0x01,
0xCD, 0xFE, 0xC0, 0xFE, 0x00, 0x00, 0x00, 0x00,
0x03, 0x40, 0x73, 0x3E, 0x2F, 0x00, 0x00, 0x6E,
0x30, 0x05, 0x06, 0xB0, 0x71, 0xC8, 0x43, 0x04,
0x80, 0x1F, 0x08, 0x80, 0x03, 0x00, 0x0E, 0x00,
0x08, 0x00, 0xE0, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x30, 0xF0, 0x03, 0x03, 0x30,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x31, 0x00, 0x40, 0x00, 0x00,
0x00, 0x03, 0x00, 0x00, 0xE4, 0xFF, 0xFF, 0x01,
0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xFE, 0x0F,
0x01, 0x00, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00
};
uint32_t offset = (uint32_t)(address - MMIO_GET_DEVICE_PA(MMIO_DEVID_MC));
uint32_t wl_ind = (offset >> 5);
/* If address is whitelisted, allow write. */
if (mc_whitelist[wl_ind] & (1 << ((offset >> 2) & 0x7))) {
p_mmio = (volatile uint32_t *)(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_MC) + offset);
}
} else if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400 && MMIO_GET_DEVICE_PA(MMIO_DEVID_MC) <= address &&
address < MMIO_GET_DEVICE_PA(MMIO_DEVID_MC) + MMIO_GET_DEVICE_SIZE(MMIO_DEVID_MC)) {
/* Memory Controller RW supported only on 4.0.0+ */
const uint8_t mc_whitelist[0x68] = {
0x9F, 0x31, 0x30, 0x00, 0xF0, 0xFF, 0xF7, 0x01,
0xCD, 0xFE, 0xC0, 0xFE, 0x00, 0x00, 0x00, 0x00,
0x03, 0x40, 0x73, 0x3E, 0x2F, 0x00, 0x00, 0x6E,
0x30, 0x05, 0x06, 0xB0, 0x71, 0xC8, 0x43, 0x04,
0x80, 0x1F, 0x08, 0x80, 0x03, 0x00, 0x0E, 0x00,
0x08, 0x00, 0xE0, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x30, 0xF0, 0x03, 0x03, 0x30,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x31, 0x00, 0x40, 0x00, 0x00,
0x00, 0x03, 0x00, 0x00, 0xE4, 0xFF, 0xFF, 0x01,
0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xFE, 0x0F,
0x01, 0x00, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00
};
uint32_t offset = (uint32_t)(address - 0x70019000);
uint32_t wl_ind = (offset >> 5);
/* If address is whitelisted, allow write. */
if (wl_ind < sizeof(mc_whitelist) && (mc_whitelist[wl_ind] & (1 << ((offset >> 2) & 0x7)))) {
p_mmio = (volatile uint32_t *)(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_MC) + offset);
} else {
/* These addresses are not allowed by the whitelist. */
/* They correspond to SMMU DISABLE for the BPMP, and for APB-DMA. */
/* However, smcReadWriteRegister returns 0 for these addresses despite not actually performing the write. */
/* This is "probably" to fuck with hackers who got access to smcReadWriteRegister and are trying to get */
/* control of the BPMP for jamais vu etc., since there's no other reason to return 0 despite failure. */
if (address == 0x7001923C || address == 0x70019298) {
return 0;
}
return 2;
}
}
@@ -730,16 +683,9 @@ uint32_t smc_read_write_register(smc_args_t *args) {
/* Return old value. */
args->X[1] = old_value;
return 0;
} else if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400 && (address == 0x7001923C || address == 0x70019298)) {
/* These addresses are not allowed by the whitelist. */
/* They correspond to SMMU DISABLE for the BPMP, and for APB-DMA. */
/* However, smcReadWriteRegister returns 0 for these addresses despite not actually performing the write. */
/* This is "probably" to fuck with hackers who got access to smcReadWriteRegister and are trying to get */
/* control of the BPMP for jamais vu etc., since there's no other reason to return 0 despite failure. */
return 0;
} else {
return 2;
}
return 2;
}

View File

@@ -34,93 +34,12 @@
/* Globals. */
static bool g_crypt_aes_done = false;
static uint32_t g_exp_mod_result = 0;
static bool g_exp_mod_done = false;
static __attribute__((aligned(4))) uint8_t g_imported_exponents[4][0x100];
static __attribute__((aligned(4))) uint8_t g_imported_moduli[4][0x100];
static bool g_is_modulus_verified[4];
static __attribute__((aligned(4))) const uint8_t g_rsa_public_key[4] = { 0x00, 0x01, 0x00, 0x01 };
static __attribute__((aligned(4))) const uint8_t g_rsa_test_vector[0x100] = {
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D',
'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D'
};
static uint32_t g_test_exp_mod_keyslot = 0;
static uint32_t g_test_exp_mod_usecase = 0;
static bool g_test_exp_mod_in_progress = false;
static uint8_t g_imported_exponents[4][0x100];
static uint8_t g_rsausecase_to_cryptousecase[5] = {1, 2, 3, 5, 6};
static void import_rsa_exponent(unsigned int which, const uint8_t *exponent, uint64_t size) {
g_is_modulus_verified[which] = false;
for (unsigned int i = 0; i < 0x100; i++) {
g_imported_exponents[which][i] = exponent[i];
g_imported_moduli[which][i] = 0;
}
}
static void import_rsa_modulus(unsigned int which, const uint8_t *modulus, uint64_t size) {
uint64_t clamped_size = 0x100;
if (size <= 0x100) {
clamped_size = size;
}
if (clamped_size != 0) {
/* The official secure monitor implements this via bit-fiddling, */
/* and to prevent accidental inaccuracy we will too. */
/* It's probably done to prevent errors on negative sizes. */
uint64_t remaining = 0x100;
if (size != 0x100 && (~size >= ~0xFFFFFFFFFFFFFEFFULL)) {
remaining = size;
}
memcpy(&g_imported_moduli[which][0], modulus, remaining);
}
}
static bool load_imported_rsa_keypair(unsigned int keyslot, unsigned int which) {
if (!g_is_modulus_verified[which]) {
return false;
}
set_rsa_keyslot(keyslot, g_imported_moduli[which], 0x100, g_imported_exponents[which], 0x100);
return true;
}
static void test_rsa_modulus_public(unsigned int which, unsigned int keyslot, const uint8_t *modulus, uint64_t modulus_size, unsigned int (*callback)(void)) {
import_rsa_modulus(which, modulus, modulus_size);
set_rsa_keyslot(keyslot, modulus, modulus_size, g_rsa_public_key, 0x4);
se_exp_mod(keyslot, g_rsa_test_vector, 0x100, callback);
}
static void test_rsa_modulus_private(unsigned int which, unsigned int keyslot, unsigned int (*callback)(void)) {
uint8_t exponentiated_data[0x100];
se_get_exp_mod_output(exponentiated_data, sizeof(exponentiated_data));
set_rsa_keyslot(keyslot, g_imported_moduli[which], 0x100, g_imported_exponents[which], 0x100);
se_exp_mod(keyslot, exponentiated_data, 0x100, callback);
}
static void validate_rsa_result(unsigned int which) {
char result[0x100];
se_get_exp_mod_output(result, sizeof(result));
if (memcmp(result, g_rsa_test_vector, sizeof(result)) == 0) {
g_is_modulus_verified[which] = true;
}
}
static bool is_user_keyslot_valid(unsigned int keyslot) {
switch (exosphere_get_target_firmware()) {
case ATMOSPHERE_TARGET_FIRMWARE_100:
@@ -136,45 +55,27 @@ static bool is_user_keyslot_valid(unsigned int keyslot) {
case ATMOSPHERE_TARGET_FIRMWARE_810:
case ATMOSPHERE_TARGET_FIRMWARE_900:
case ATMOSPHERE_TARGET_FIRMWARE_910:
case ATMOSPHERE_TARGET_FIRMWARE_1000:
default:
return keyslot <= 5;
}
}
void set_exp_mod_result(uint32_t result) {
g_exp_mod_result = result;
void set_exp_mod_done(bool done) {
g_exp_mod_done = done;
}
uint32_t get_exp_mod_result(void) {
return g_exp_mod_result;
bool get_exp_mod_done(void) {
return g_exp_mod_done;
}
uint32_t exp_mod_done_handler(void) {
set_exp_mod_result(0);
set_exp_mod_done(true);
se_trigger_interrupt();
return 0;
}
static uint32_t test_exp_mod_done_handler(void) {
if (g_test_exp_mod_in_progress) {
g_test_exp_mod_in_progress = false;
test_rsa_modulus_private(g_test_exp_mod_usecase, g_test_exp_mod_keyslot, test_exp_mod_done_handler);
} else {
validate_rsa_result(g_test_exp_mod_usecase);
if (load_imported_rsa_keypair(g_test_exp_mod_keyslot, g_test_exp_mod_usecase)) {
se_exp_mod(g_test_exp_mod_keyslot, g_rsa_shared_data.storage_exp_mod.user_data, 0x100, exp_mod_done_handler);
} else {
set_exp_mod_result(2);
se_trigger_interrupt();
}
}
return 0;
}
uint32_t user_exp_mod(smc_args_t *args) {
uint8_t modulus[0x100];
uint8_t exponent[0x100];
@@ -207,8 +108,7 @@ uint32_t user_exp_mod(smc_args_t *args) {
return 2;
}
set_exp_mod_result(3);
set_exp_mod_done(false);
/* Hardcode RSA keyslot 0. */
set_rsa_keyslot(0, modulus, 0x100, exponent, exponent_size);
se_exp_mod(0, input, 0x100, exp_mod_done_handler);
@@ -750,21 +650,10 @@ uint32_t user_secure_exp_mod(smc_args_t *args) {
return 2;
}
set_exp_mod_result(3);
set_exp_mod_done(false);
/* Hardcode RSA keyslot 0. */
if (exosphere_get_target_firmware() < ATMOSPHERE_TARGET_FIRMWARE_1000) {
set_rsa_keyslot(0, modulus, 0x100, g_imported_exponents[exponent_id], 0x100);
se_exp_mod(0, input, 0x100, exp_mod_done_handler);
} else if (load_imported_rsa_keypair(0, exponent_id)) {
se_exp_mod(0, input, 0x100, exp_mod_done_handler);
} else {
memcpy(g_rsa_shared_data.storage_exp_mod.user_data, input, 0x100);
g_test_exp_mod_keyslot = 0;
g_test_exp_mod_usecase = exponent_id;
g_test_exp_mod_in_progress = true;
test_rsa_modulus_public(exponent_id, 0, modulus, 0x100, test_exp_mod_done_handler);
}
set_rsa_keyslot(0, modulus, 0x100, g_imported_exponents[exponent_id], 0x100);
se_exp_mod(0, input, 0x100, exp_mod_done_handler);
return 0;
}
@@ -811,7 +700,7 @@ uint32_t user_unwrap_rsa_oaep_wrapped_titlekey(smc_args_t *args) {
return 2;
}
set_exp_mod_result(3);
set_exp_mod_done(false);
/* Expected label_hash occupies args->X[3] to args->X[6]. */
tkey_set_expected_label_hash(&args->X[3]);
@@ -990,7 +879,6 @@ uint32_t user_decrypt_or_import_rsa_key(smc_args_t *args) {
}
unsigned int exponent_id;
bool import_modulus;
switch (usecase) {
case 0:
@@ -1000,33 +888,22 @@ uint32_t user_decrypt_or_import_rsa_key(smc_args_t *args) {
return 0;
case 1:
exponent_id = 1;
import_modulus = false;
break;
case 2:
exponent_id = 0;
import_modulus = true;
break;
case 3:
exponent_id = 2;
import_modulus = false;
break;
case 4:
exponent_id = 3;
import_modulus = true;
break;
default:
generic_panic();
}
/* Modulus import isn't implemented on < 10.0.0. */
import_modulus &= (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_1000);
/* Import the key. */
import_rsa_exponent(exponent_id, user_data, 0x100);
if (import_modulus) {
import_rsa_modulus(exponent_id, user_data + 0x100, 0x100);
g_is_modulus_verified[exponent_id] = true;
}
/* Copy key to global. */
memcpy(g_imported_exponents[exponent_id], user_data, 0x100);
return 0;
}

View File

@@ -13,7 +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/>.
*/
#ifndef EXOSPHERE_SMC_USER_H
#define EXOSPHERE_SMC_USER_H
@@ -41,7 +41,7 @@ uint32_t user_decrypt_or_import_rsa_key(smc_args_t *args);
void set_crypt_aes_done(bool done);
bool get_crypt_aes_done(void);
void set_exp_mod_result(uint32_t result);
uint32_t get_exp_mod_result(void);
void set_exp_mod_done(bool done);
bool get_exp_mod_done(void);
#endif

View File

@@ -13,7 +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/>.
*/
#include <stdint.h>
#include <string.h>
@@ -25,10 +25,14 @@
#include "masterkey.h"
#include "se.h"
static uint64_t g_tkey_expected_label_hash[4];
static unsigned int g_tkey_master_key_rev = MASTERKEY_REVISION_MAX;
static unsigned int g_tkey_type = 0;
/* Set the expected db prefix. */
void tkey_set_expected_label_hash(uint64_t *label_hash) {
for (unsigned int i = 0; i < 4; i++) {
g_rsa_shared_data.unwrap_titlekey.expected_label_hash[i] = label_hash[i];
g_tkey_expected_label_hash[i] = label_hash[i];
}
}
@@ -36,7 +40,7 @@ void tkey_set_master_key_rev(unsigned int master_key_rev) {
if (master_key_rev >= MASTERKEY_REVISION_MAX) {
generic_panic();
}
g_rsa_shared_data.unwrap_titlekey.master_key_rev = master_key_rev;
g_tkey_master_key_rev = master_key_rev;
}
static void tkey_validate_type(unsigned int type) {
@@ -47,7 +51,7 @@ static void tkey_validate_type(unsigned int type) {
void tkey_set_type(unsigned int type) {
tkey_validate_type(type);
g_rsa_shared_data.unwrap_titlekey.type = type;
g_tkey_type = type;
}
/* Reference for MGF1 can be found here: https://en.wikipedia.org/wiki/Mask_generation_function#MGF1 */
@@ -112,7 +116,7 @@ size_t tkey_rsa_oaep_unwrap(void *dst, size_t dst_size, void *src, size_t src_si
uint8_t *db = message + 0x21;
/* This will be passed to smc_unwrap_rsa_oaep_wrapped_titlekey. */
uint8_t *expected_label_hash = (uint8_t *)(&g_rsa_shared_data.unwrap_titlekey.expected_label_hash[0]);
uint8_t *expected_label_hash = (uint8_t *)(&g_tkey_expected_label_hash[0]);
/* Unmask the salt. */
calculate_mgf1_and_xor(salt, 0x20, db, 0xDF);
@@ -167,13 +171,13 @@ static const uint8_t titlekek_sources[TITLEKEY_TYPE_MAX+1][0x10] = {
};
void tkey_aes_unwrap(void *dst, size_t dst_size, const void *src, size_t src_size) {
if (g_rsa_shared_data.unwrap_titlekey.master_key_rev >= MASTERKEY_REVISION_MAX || dst_size != 0x10 || src_size != 0x10) {
if (g_tkey_master_key_rev >= MASTERKEY_REVISION_MAX || dst_size != 0x10 || src_size != 0x10) {
generic_panic();
}
/* Generate the appropriate titlekek into keyslot 9. */
unsigned int master_keyslot = mkey_get_keyslot(g_rsa_shared_data.unwrap_titlekey.master_key_rev);
decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, master_keyslot, titlekek_sources[g_rsa_shared_data.unwrap_titlekey.type], 0x10);
unsigned int master_keyslot = mkey_get_keyslot(g_tkey_master_key_rev);
decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, master_keyslot, titlekek_sources[g_tkey_type], 0x10);
/* Unwrap the titlekey using the titlekek. */
se_aes_ecb_decrypt_block(KEYSLOT_SWITCH_TEMPKEY, dst, 0x10, src, 0x10);

View File

@@ -13,12 +13,11 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef EXOSPHERE_TITLEKEY_H
#define EXOSPHERE_TITLEKEY_H
#include <stdint.h>
#include "rsa_common.h"
#define TITLEKEY_TYPE_MAX 0x1

View File

@@ -21,12 +21,12 @@
static inline void uart_wait_cycles(uint32_t baud, uint32_t num)
{
udelay((num * 1000000 + 16 * baud - 1) / (16 * baud));
wait((num * 1000000 + 16 * baud - 1) / (16 * baud));
}
static inline void uart_wait_syms(uint32_t baud, uint32_t num)
{
udelay((num * 1000000 + baud - 1) / baud);
wait((num * 1000000 + baud - 1) / baud);
}
void uart_config(UartDevice dev) {
@@ -34,28 +34,28 @@ void uart_config(UartDevice dev) {
switch (dev) {
case UART_A:
pinmux->uart1_tx = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
pinmux->uart1_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_UP | PINMUX_SELECT_FUNCTION0);
pinmux->uart1_rts = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
pinmux->uart1_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
pinmux->uart1_tx = 0;
pinmux->uart1_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
pinmux->uart1_rts = 0;
pinmux->uart1_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
break;
case UART_B:
pinmux->uart2_tx = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
pinmux->uart2_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
pinmux->uart2_rts = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
pinmux->uart2_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
pinmux->uart2_tx = 0;
pinmux->uart2_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
pinmux->uart2_rts = 0;
pinmux->uart2_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
break;
case UART_C:
pinmux->uart3_tx = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
pinmux->uart3_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
pinmux->uart3_rts = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
pinmux->uart3_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
pinmux->uart3_tx = 0;
pinmux->uart3_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
pinmux->uart3_rts = 0;
pinmux->uart3_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
break;
case UART_D:
pinmux->uart4_tx = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
pinmux->uart4_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
pinmux->uart4_rts = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
pinmux->uart4_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
pinmux->uart4_tx = 0;
pinmux->uart4_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
pinmux->uart4_rts = 0;
pinmux->uart4_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
break;
case UART_E:
/* Unused. */

View File

@@ -13,7 +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/>.
*/
#include "utils.h"
#include "memory_map.h"
#include "mc.h"
@@ -65,7 +65,7 @@ void init_dma_controllers(unsigned int target_firmware) {
MAKE_REG32(0x6000C038) = 0x0;
/* MSELECT_CONFIG_0 |= WRAP_TO_INCR_SLAVE0(APC) | WRAP_TO_INCR_SLAVE1(PCIe) | WRAP_TO_INCR_SLAVE2(GPU) */
MAKE_REG32(0x50060000) = (MAKE_REG32(0x50060000) & 0xC4FFFFFF) | 0x38000000;
MAKE_REG32(0x50060000) |= 0x38000000;
/* AHB_ARBITRATION_DISABLE_0 - Disables USB, USB2, and AHB-DMA from arbitration */
MAKE_REG32(0x6000C004) = 0x40060;
@@ -99,7 +99,7 @@ void init_dma_controllers(unsigned int target_firmware) {
MAKE_REG32(0x60020038) = 0;
/* MSELECT_CONFIG_0 |= WRAP_TO_INCR_SLAVE0(APC) | WRAP_TO_INCR_SLAVE1(PCIe) | WRAP_TO_INCR_SLAVE2(GPU) */
MAKE_REG32(0x50060000) |= (MAKE_REG32(0x50060000) & 0xC4FFFFFF) | 0x38000000;
MAKE_REG32(0x50060000) |= 0x38000000;
/* AHB_ARBITRATION_PRIORITY_CTRL_0 - Select high prio group with prio 7 */
MAKE_REG32(0x6000C008) = 0xE0000001;
@@ -111,14 +111,6 @@ void init_dma_controllers(unsigned int target_firmware) {
void _set_memory_registers_enable_mmu(const uintptr_t ttbr0) {
static const uintptr_t vbar = TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGEMENT_ID_SECMON_EVT) + 0x800;
/*
- Non-cacheable load forwarding enabled
- Disable load-pass DMB.
- NOTE: This and this alone is done via inline asm, due to register argument limits.
*/
static const uint64_t cpuactlr = 0x800000001000000ull;
__asm__ __volatile__("msr s3_1_c15_c2_0, %0" :: "r"(cpuactlr) : "memory", "cc");
/*
- Disable table walk descriptor access prefetch.
@@ -205,12 +197,12 @@ void warmboot_init(void) {
*/
flush_dcache_all();
invalidate_icache_all();
/* On warmboot (not cpu_on) only */
if (VIRT_MC_SECURITY_CFG3 == 0) {
init_dma_controllers(g_exosphere_target_firmware_for_init);
}
/*identity_remap_tzram();*/
/* Nintendo pointlessly fully invalidate the TLB & invalidate the data cache on the modified ranges here */
if (g_exosphere_target_firmware_for_init < ATMOSPHERE_TARGET_FIRMWARE_500) {

View File

@@ -21,58 +21,66 @@
#include "gpio.h"
#include "utils.h"
/* Set GPIO's value. */
static void gpio_register_set(uint32_t pin, bool do_set, uint32_t offset) {
static volatile tegra_gpio_bank_t *gpio_get_bank(uint32_t pin) {
volatile tegra_gpio_t *gpio = gpio_get_regs();
uint32_t bank_number = (pin >> GPIO_BANK_SHIFT);
return &gpio->bank[bank_number];
}
static volatile uint32_t gpio_get_port(uint32_t pin) {
return ((pin >> GPIO_PORT_SHIFT) & GPIO_PORT_MASK);
}
static volatile uint32_t gpio_get_mask(uint32_t pin) {
uint32_t pin_number = (pin & GPIO_PIN_MASK);
return (1 << pin_number);
}
static void gpio_simple_register_set(uint32_t pin, bool should_be_set, uint32_t offset) {
/* Retrieve the register set that corresponds to the given pin and offset. */
volatile uint32_t *cluster = (uint32_t *)((uintptr_t)&gpio->bank[(pin >> GPIO_BANK_SHIFT)] + offset);
uintptr_t cluster_addr = (uintptr_t)gpio_get_bank(pin) + offset;
uint32_t *cluster = (uint32_t *)cluster_addr;
/* Figure out the offset into the cluster, and the mask to be used. */
uint32_t port = ((pin >> GPIO_PORT_SHIFT) & GPIO_PORT_MASK);
uint32_t mask = (1 << (pin & GPIO_PIN_MASK));
uint32_t port = gpio_get_port(pin);
uint32_t mask = gpio_get_mask(pin);
/* Set or clear the bit, as appropriate. */
if (do_set)
if (should_be_set)
cluster[port] |= mask;
else
cluster[port] &= ~mask;
/* Dummy read. */
cluster[port];
(void)cluster[port];
}
/* Get GPIO's value. */
static bool gpio_register_get(uint32_t pin, uint32_t offset) {
volatile tegra_gpio_t *gpio = gpio_get_regs();
static bool gpio_simple_register_get(uint32_t pin, uint32_t offset) {
/* Retrieve the register set that corresponds to the given pin and offset. */
volatile uint32_t *cluster = (uint32_t *)((uintptr_t)&gpio->bank[(pin >> GPIO_BANK_SHIFT)] + offset);
uintptr_t cluster_addr = (uintptr_t)gpio_get_bank(pin) + offset;
uint32_t *cluster = (uint32_t *)cluster_addr;
/* Figure out the offset into the cluster, and the mask to be used. */
uint32_t port = ((pin >> GPIO_PORT_SHIFT) & GPIO_PORT_MASK);
uint32_t mask = (1 << (pin & GPIO_PIN_MASK));
uint32_t port = gpio_get_port(pin);
uint32_t mask = gpio_get_mask(pin);
/* Convert the given value to a boolean. */
return !!(cluster[port] & mask);
}
/* Configure GPIO's mode. */
void gpio_configure_mode(uint32_t pin, uint32_t mode) {
gpio_register_set(pin, mode == GPIO_MODE_GPIO, offsetof(tegra_gpio_bank_t, config));
gpio_simple_register_set(pin, mode == GPIO_MODE_GPIO, offsetof(tegra_gpio_bank_t, config));
}
/* Configure GPIO's direction. */
void gpio_configure_direction(uint32_t pin, uint32_t dir) {
gpio_register_set(pin, dir == GPIO_DIRECTION_OUTPUT, offsetof(tegra_gpio_bank_t, direction));
gpio_simple_register_set(pin, dir == GPIO_DIRECTION_OUTPUT, offsetof(tegra_gpio_bank_t, direction));
}
/* Write to GPIO. */
void gpio_write(uint32_t pin, uint32_t value) {
gpio_register_set(pin, value == GPIO_LEVEL_HIGH, offsetof(tegra_gpio_bank_t, out));
gpio_simple_register_set(pin, value == GPIO_LEVEL_HIGH, offsetof(tegra_gpio_bank_t, out));
}
/* Read from GPIO. */
uint32_t gpio_read(uint32_t pin) {
return gpio_register_get(pin, offsetof(tegra_gpio_bank_t, in));
return gpio_simple_register_get(pin, offsetof(tegra_gpio_bank_t, in));
}

View File

@@ -13,7 +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/>.
*/
#include <stdio.h>
#include "panic.h"
#include "di.h"
@@ -51,10 +51,8 @@ static const char *get_error_desc_str(uint32_t error_desc) {
static void _check_and_display_atmosphere_fatal_error(void) {
/* Check for valid magic. */
if (ATMOSPHERE_FATAL_ERROR_CONTEXT->magic != ATMOSPHERE_REBOOT_TO_FATAL_MAGIC &&
ATMOSPHERE_FATAL_ERROR_CONTEXT->magic != ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_1 &&
ATMOSPHERE_FATAL_ERROR_CONTEXT->magic != ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_0)
{
if (ATMOSPHERE_FATAL_ERROR_CONTEXT->magic != ATMOSPHERE_REBOOT_TO_FATAL_MAGIC &&
ATMOSPHERE_FATAL_ERROR_CONTEXT->magic != ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_0) {
return;
}
@@ -71,10 +69,10 @@ static void _check_and_display_atmosphere_fatal_error(void) {
/* Turn on the backlight after initializing the lfb */
/* to avoid flickering. */
display_backlight(true);
/* Override the global logging level. */
log_set_log_level(SCREEN_LOG_LEVEL_ERROR);
/* Copy fatal error context to the stack. */
atmosphere_fatal_error_ctx ctx = *(ATMOSPHERE_FATAL_ERROR_CONTEXT);
@@ -105,7 +103,7 @@ static void _check_and_display_atmosphere_fatal_error(void) {
void check_and_display_panic(void) {
/* Handle a panic sent via a stratosphere module. */
_check_and_display_atmosphere_fatal_error();
/* We also handle our own panics. */
bool has_panic = ((APBDEV_PMC_RST_STATUS_0 != 0) || (g_panic_code != 0));
uint32_t code = (g_panic_code == 0) ? APBDEV_PMC_SCRATCH200_0 : g_panic_code;
@@ -154,10 +152,10 @@ void check_and_display_panic(void) {
/* Initialize the display. */
display_init();
/* Fill the screen. */
display_color_screen(color);
/* Wait for button and reboot. */
wait_for_button_and_reboot();
} else {

View File

@@ -13,7 +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/>.
*/
#ifndef FUSEE_PANIC_H
#define FUSEE_PANIC_H
@@ -30,7 +30,6 @@
#define AMS_FATAL_ERROR_MAX_STACKTRACE 0x20
#define AMS_FATAL_ERROR_MAX_STACKDUMP 0x100
#define AMS_FATAL_ERROR_TLS_SIZE 0x100
/* Atmosphere reboot-to-fatal-error. */
typedef struct {
@@ -58,13 +57,10 @@ typedef struct {
uint64_t stack_dump_size;
uint64_t stack_trace[AMS_FATAL_ERROR_MAX_STACKTRACE];
uint8_t stack_dump[AMS_FATAL_ERROR_MAX_STACKDUMP];
uint8_t tls[AMS_FATAL_ERROR_TLS_SIZE];
} atmosphere_fatal_error_ctx;
/* "AFE2" */
#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC 0x32454641
/* "AFE1" */
#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_1 0x31454641
#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC 0x31454641
/* "AFE0" */
#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_0 0x30454641

View File

@@ -615,7 +615,7 @@ static void sdmmc_autocal_run(sdmmc_t *sdmmc, SdmmcBusVoltage voltage)
while ((sdmmc->regs->auto_cal_status & SDMMC_AUTOCAL_ACTIVE)) {
/* Ensure we haven't timed out. */
if (get_time_since(timebase) > SDMMC_AUTOCAL_TIMEOUT) {
sdmmc_warn(sdmmc, "Auto-calibration timed out!");
sdmmc_error(sdmmc, "Auto-calibration timed out!");
/* Force a register read to refresh the clock control value. */
sdmmc_get_sd_clock_control(sdmmc);
@@ -1314,9 +1314,9 @@ static int sdmmc_wait_busy(sdmmc_t *sdmmc)
static void sdmmc_intr_enable(sdmmc_t *sdmmc)
{
/* Enable the relevant interrupts and set all error bits. */
sdmmc->regs->int_enable |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT);
/* Set all error bits and enable the relevant interrupts. */
sdmmc->regs->int_enable |= 0x017F0000;
sdmmc->regs->int_enable |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT);
/* Refresh status. */
sdmmc->regs->int_status = sdmmc->regs->int_status;
@@ -1324,35 +1324,34 @@ static void sdmmc_intr_enable(sdmmc_t *sdmmc)
static void sdmmc_intr_disable(sdmmc_t *sdmmc)
{
/* Clear all error bits and disable the relevant interrupts. */
/* Clear all error bits and the interrupts. */
sdmmc->regs->int_enable &= ~(0x017F0000);
sdmmc->regs->int_enable &= ~(TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT);
/* Refresh status. */
sdmmc->regs->int_status = sdmmc->regs->int_status;
}
static int sdmmc_intr_check(sdmmc_t *sdmmc, uint16_t *status_out, uint16_t status_mask)
static bool sdmmc_intr_check_status(sdmmc_t *sdmmc, uint16_t status_mask)
{
uint32_t int_status = sdmmc->regs->int_status;
bool is_masked = (sdmmc->regs->int_status & status_mask);
sdmmc_debug(sdmmc, "INTSTS: %08X", int_status);
/* Mask status. */
if (is_masked)
sdmmc->regs->int_status &= status_mask;
/* Return the status, if necessary. */
if (status_out)
*status_out = (int_status & 0xFFFF);
return is_masked;
}
static bool sdmmc_intr_check_error(sdmmc_t *sdmmc)
{
bool is_error = (sdmmc->regs->int_status & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT);
if (int_status & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT)
{
/* Acknowledge error by refreshing status. */
sdmmc->regs->int_status = int_status;
return -1;
}
else if (int_status & status_mask)
{
/* Mask the status. */
sdmmc->regs->int_status = (int_status & status_mask);
return 1;
}
/* Refresh status. */
if (is_error)
sdmmc->regs->int_status = sdmmc->regs->int_status;
return 0;
return is_error;
}
static int sdmmc_dma_init(sdmmc_t *sdmmc, sdmmc_request_t *req)
@@ -1443,23 +1442,15 @@ static int sdmmc_dma_update(sdmmc_t *sdmmc)
/* Watch over the DMA transfer. */
while (!is_timeout)
{
/* Check interrupts. */
uint16_t intr_status = 0;
int intr_res = sdmmc_intr_check(sdmmc, &intr_status, TEGRA_MMC_NORINTSTS_XFER_COMPLETE | TEGRA_MMC_NORINTSTS_DMA_INTERRUPT);
/* An error has been raised. Reset. */
if (intr_res < 0)
if (sdmmc_intr_check_error(sdmmc))
{
sdmmc_do_sw_reset(sdmmc);
return 0;
}
/* Transfer is over. */
if (intr_status & TEGRA_MMC_NORINTSTS_XFER_COMPLETE)
return 1;
/* We have a DMA interrupt. Restart the transfer where it was interrupted. */
if (intr_status & TEGRA_MMC_NORINTSTS_DMA_INTERRUPT)
if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTS_DMA_INTERRUPT))
{
if (sdmmc->use_adma)
{
@@ -1476,6 +1467,10 @@ static int sdmmc_dma_update(sdmmc_t *sdmmc)
sdmmc->next_dma_addr += 0x80000;
}
/* Transfer is over. */
if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTS_XFER_COMPLETE))
return 1;
/* Keep checking if timeout expired. */
is_timeout = (get_time_since(timebase) > 2000000);
}
@@ -1531,15 +1526,12 @@ static int sdmmc_wait_for_cmd(sdmmc_t *sdmmc)
/* Wait for CMD to finish. */
while (!is_err && !is_timeout) {
/* Check interrupts. */
int intr_res = sdmmc_intr_check(sdmmc, 0, TEGRA_MMC_NORINTSTS_CMD_COMPLETE);
/* Command is done. */
if (intr_res > 0)
if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTS_CMD_COMPLETE))
return 1;
/* Check for any raised errors. */
is_err = (intr_res < 0);
is_err = sdmmc_intr_check_error(sdmmc);
/* Keep checking if timeout expired. */
is_timeout = (get_time_since(timebase) > 2000000);
@@ -1650,7 +1642,6 @@ int sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_command_t *cmd, sdmmc_request_t *req, u
is_dma = true;
dma_blkcnt = sdmmc_dma_init(sdmmc, req);
/* Abort in case initialization failed. */
if (!dma_blkcnt)
{
sdmmc_error(sdmmc, "Failed to initialize the DMA transfer!");
@@ -1678,14 +1669,12 @@ int sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_command_t *cmd, sdmmc_request_t *req, u
/* Save response, if necessary. */
sdmmc_save_response(sdmmc, cmd->flags);
/* Update the DMA request. */
/* Process the DMA request. */
if (req)
{
/* Disable interrupts and abort in case updating failed. */
if (!sdmmc_dma_update(sdmmc))
{
sdmmc_warn(sdmmc, "Failed to update the DMA transfer!");
sdmmc_intr_disable(sdmmc);
sdmmc_error(sdmmc, "Failed to process the DMA transfer!");
return 0;
}
@@ -1851,7 +1840,7 @@ static int sdmmc_send_tuning(sdmmc_t *sdmmc, uint32_t opcode)
while (!is_timeout)
{
/* Buffer Read Ready was asserted. */
if (sdmmc_intr_check(sdmmc, 0, TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY) > 0)
if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY))
{
/* Manually disable the Buffer Read Ready interrupt. */
sdmmc->regs->int_enable &= ~(TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY);

View File

@@ -34,28 +34,28 @@ void uart_config(UartDevice dev) {
switch (dev) {
case UART_A:
pinmux->uart1_tx = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
pinmux->uart1_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_UP | PINMUX_SELECT_FUNCTION0);
pinmux->uart1_rts = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
pinmux->uart1_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
pinmux->uart1_tx = 0;
pinmux->uart1_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
pinmux->uart1_rts = 0;
pinmux->uart1_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
break;
case UART_B:
pinmux->uart2_tx = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
pinmux->uart2_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
pinmux->uart2_rts = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
pinmux->uart2_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
pinmux->uart2_tx = 0;
pinmux->uart2_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
pinmux->uart2_rts = 0;
pinmux->uart2_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
break;
case UART_C:
pinmux->uart3_tx = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
pinmux->uart3_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
pinmux->uart3_rts = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
pinmux->uart3_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
pinmux->uart3_tx = 0;
pinmux->uart3_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
pinmux->uart3_rts = 0;
pinmux->uart3_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
break;
case UART_D:
pinmux->uart4_tx = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
pinmux->uart4_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
pinmux->uart4_rts = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
pinmux->uart4_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
pinmux->uart4_tx = 0;
pinmux->uart4_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
pinmux->uart4_rts = 0;
pinmux->uart4_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
break;
case UART_E:
/* Unused. */

View File

@@ -84,7 +84,7 @@ ifneq ($(BUILD),$(notdir $(CURDIR)))
export OUTPUT := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export KIPDIRS := $(AMS)/stratosphere/loader $(AMS)/stratosphere/ncm $(AMS)/stratosphere/pm $(AMS)/stratosphere/sm $(AMS)/stratosphere/boot $(AMS)/stratosphere/spl $(AMS)/stratosphere/ams_mitm
export KIPDIRS := $(AMS)/stratosphere/loader $(AMS)/stratosphere/pm $(AMS)/stratosphere/sm $(AMS)/stratosphere/boot $(AMS)/stratosphere/spl $(AMS)/stratosphere/ams_mitm
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir)) \
$(AMS)/exosphere $(AMS)/exosphere/lp0fw $(AMS)/exosphere/rebootstub \
@@ -96,7 +96,7 @@ export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
KIPFILES := loader.kip ncm.kip pm.kip sm.kip ams_mitm.kip spl.kip boot.kip
KIPFILES := loader.kip pm.kip sm.kip ams_mitm.kip spl.kip boot.kip
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) fusee-primary.bin \
exosphere.bin lp0fw.bin rebootstub.bin thermosphere.bin splash_screen.bmp \
sept-primary.bin sept-secondary_00.enc sept-secondary_01.enc emummc.kip \
@@ -221,12 +221,12 @@ sept_secondary_01.enc.o sept_secondary_01_enc.h: sept-secondary_01.enc
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(_bin2o)
sept_secondary_dev_00.enc.o sept_secondary_dev_00_enc.h: sept-secondary_dev_00.enc
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(_bin2o)
sept_secondary_dev_01.enc.o sept_secondary_dev_01_enc.h: sept-secondary_dev_01.enc
#---------------------------------------------------------------------------------
@echo $(notdir $<)

View File

@@ -230,8 +230,6 @@ SECTIONS
PROVIDE(__loader_kip_size__ = loader_kip_end - loader_kip);
PROVIDE(__lp0fw_bin_start__ = lp0fw_bin - __start__);
PROVIDE(__lp0fw_bin_size__ = lp0fw_bin_end - lp0fw_bin);
PROVIDE(__ncm_kip_start__ = ncm_kip - __start__);
PROVIDE(__ncm_kip_size__ = ncm_kip_end - ncm_kip);
PROVIDE(__pm_kip_start__ = pm_kip - __start__);
PROVIDE(__pm_kip_size__ = pm_kip_end - pm_kip);
PROVIDE(__rebootstub_bin_start__ = rebootstub_bin - __start__);

View File

@@ -82,9 +82,6 @@ typedef enum {
FS_VER_9_1_0,
FS_VER_9_1_0_EXFAT,
FS_VER_10_0_0,
FS_VER_10_0_0_EXFAT,
FS_VER_MAX,
} emummc_fs_ver_t;

View File

@@ -21,58 +21,66 @@
#include "gpio.h"
#include "utils.h"
/* Set GPIO's value. */
static void gpio_register_set(uint32_t pin, bool do_set, uint32_t offset) {
static volatile tegra_gpio_bank_t *gpio_get_bank(uint32_t pin) {
volatile tegra_gpio_t *gpio = gpio_get_regs();
uint32_t bank_number = (pin >> GPIO_BANK_SHIFT);
return &gpio->bank[bank_number];
}
static volatile uint32_t gpio_get_port(uint32_t pin) {
return ((pin >> GPIO_PORT_SHIFT) & GPIO_PORT_MASK);
}
static volatile uint32_t gpio_get_mask(uint32_t pin) {
uint32_t pin_number = (pin & GPIO_PIN_MASK);
return (1 << pin_number);
}
static void gpio_simple_register_set(uint32_t pin, bool should_be_set, uint32_t offset) {
/* Retrieve the register set that corresponds to the given pin and offset. */
volatile uint32_t *cluster = (uint32_t *)((uintptr_t)&gpio->bank[(pin >> GPIO_BANK_SHIFT)] + offset);
uintptr_t cluster_addr = (uintptr_t)gpio_get_bank(pin) + offset;
uint32_t *cluster = (uint32_t *)cluster_addr;
/* Figure out the offset into the cluster, and the mask to be used. */
uint32_t port = ((pin >> GPIO_PORT_SHIFT) & GPIO_PORT_MASK);
uint32_t mask = (1 << (pin & GPIO_PIN_MASK));
uint32_t port = gpio_get_port(pin);
uint32_t mask = gpio_get_mask(pin);
/* Set or clear the bit, as appropriate. */
if (do_set)
if (should_be_set)
cluster[port] |= mask;
else
cluster[port] &= ~mask;
/* Dummy read. */
cluster[port];
(void)cluster[port];
}
/* Get GPIO's value. */
static bool gpio_register_get(uint32_t pin, uint32_t offset) {
volatile tegra_gpio_t *gpio = gpio_get_regs();
static bool gpio_simple_register_get(uint32_t pin, uint32_t offset) {
/* Retrieve the register set that corresponds to the given pin and offset. */
volatile uint32_t *cluster = (uint32_t *)((uintptr_t)&gpio->bank[(pin >> GPIO_BANK_SHIFT)] + offset);
uintptr_t cluster_addr = (uintptr_t)gpio_get_bank(pin) + offset;
uint32_t *cluster = (uint32_t *)cluster_addr;
/* Figure out the offset into the cluster, and the mask to be used. */
uint32_t port = ((pin >> GPIO_PORT_SHIFT) & GPIO_PORT_MASK);
uint32_t mask = (1 << (pin & GPIO_PIN_MASK));
uint32_t port = gpio_get_port(pin);
uint32_t mask = gpio_get_mask(pin);
/* Convert the given value to a boolean. */
return !!(cluster[port] & mask);
}
/* Configure GPIO's mode. */
void gpio_configure_mode(uint32_t pin, uint32_t mode) {
gpio_register_set(pin, mode == GPIO_MODE_GPIO, offsetof(tegra_gpio_bank_t, config));
gpio_simple_register_set(pin, mode == GPIO_MODE_GPIO, offsetof(tegra_gpio_bank_t, config));
}
/* Configure GPIO's direction. */
void gpio_configure_direction(uint32_t pin, uint32_t dir) {
gpio_register_set(pin, dir == GPIO_DIRECTION_OUTPUT, offsetof(tegra_gpio_bank_t, direction));
gpio_simple_register_set(pin, dir == GPIO_DIRECTION_OUTPUT, offsetof(tegra_gpio_bank_t, direction));
}
/* Write to GPIO. */
void gpio_write(uint32_t pin, uint32_t value) {
gpio_register_set(pin, value == GPIO_LEVEL_HIGH, offsetof(tegra_gpio_bank_t, out));
gpio_simple_register_set(pin, value == GPIO_LEVEL_HIGH, offsetof(tegra_gpio_bank_t, out));
}
/* Read from GPIO. */
uint32_t gpio_read(uint32_t pin) {
return gpio_register_get(pin, offsetof(tegra_gpio_bank_t, in));
return gpio_simple_register_get(pin, offsetof(tegra_gpio_bank_t, in));
}

View File

@@ -417,9 +417,6 @@ static const uint8_t g_fs_hashes[FS_VER_MAX][0x8] = {
"\xB5\xE7\xA6\x4C\x6F\x5C\x4F\xE3", /* FS_VER_9_1_0 */
"\xF1\x96\xD1\x44\xD0\x44\x45\xB6", /* FS_VER_9_1_0_EXFAT */
"\x3E\xEB\xD9\xB7\xBC\xD1\xB5\xE0", /* FS_VER_10_0_0 */
"\x81\x7E\xA2\xB0\xB7\x02\xC1\xF3", /* FS_VER_10_0_0_EXFAT */
};
kip1_header_t *apply_kip_ips_patches(kip1_header_t *kip, size_t kip_size, emummc_fs_ver_t *out_fs_ver) {

View File

@@ -486,77 +486,20 @@ static const instruction_t MAKE_KERNEL_PATCH_NAME(900, proc_id_send)[] = {0xA9BF
static const uint8_t MAKE_KERNEL_PATTERN_NAME(900, proc_id_recv)[] = {0x68, 0x03, 0x40, 0xF9, 0x08, 0x1D, 0x40, 0xF9, 0xE0, 0x03, 0x1B, 0xAA, 0x00, 0x01, 0x3F, 0xD6, 0xE8, 0x03, 0x17, 0x2A, 0xF7, 0x0A, 0x00, 0x11, 0x08, 0xF5, 0x7E, 0xD3};
static const instruction_t MAKE_KERNEL_PATCH_NAME(900, proc_id_recv)[] = {0xA9BF2FEA, 0xF9404BEB, 0x2A1703EA, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF9400368, 0xF9401D08, 0xAA1B03E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0};
/*
stp x10, x11, [sp, #-0x10]!
ldr x11, [sp, #0xC0]
mov w10, w22
lsl x10, x10, #2
ldr x10, [x11, x10]
mov x9, #0x0000ffffffffffff
and x8, x10, x9
mov x9, #0xffff000000000000
and x10, x10, x9
mov x9, #0xfffe000000000000
cmp x10, x9
beq #0x20
stp x8, x9, [sp, #-0x10]!
ldr x8, [x23]
ldr x8, [x8, #0x38]
mov x0, x23
blr x8
ldp x8, x9, [sp],#0x10
mov x8, x0
ldp x10, x11, [sp],#0x10
mov x0, x8
*/
static const uint8_t MAKE_KERNEL_PATTERN_NAME(1000, proc_id_send)[] = {0xE8, 0x02, 0x40, 0xF9, 0x08, 0x1D, 0x40, 0xF9, 0xE0, 0x03, 0x17, 0xAA, 0x00, 0x01, 0x3F, 0xD6, 0x08, 0x4B, 0x36, 0x8B, 0x09, 0xFC, 0x60, 0xD3, 0x00, 0x25, 0x00, 0x29};
static const instruction_t MAKE_KERNEL_PATCH_NAME(1000, proc_id_send)[] = {0xA9BF2FEA, 0xF94063EB, 0x2A1603EA, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF94002E8, 0xF9401D08, 0xAA1703E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0};
/*
stp x10, x11, [sp, #-0x10]!
ldr x11, [sp, #0xC8]
mov w10, w26
lsl x10, x10, #2
ldr x10, [x11, x10]
mov x9, #0x0000ffffffffffff
and x8, x10, x9
mov x9, #0xffff000000000000
and x10, x10, x9
mov x9, #0xfffe000000000000
cmp x10, x9
beq #0x20
stp x8, x9, [sp, #-0x10]!
ldr x8, [x28]
ldr x8, [x8, #0x38]
mov x0, x28
blr x8
ldp x8, x9, [sp],#0x10
mov x8, x0
ldp x10, x11, [sp],#0x10
mov x0, x8
*/
static const uint8_t MAKE_KERNEL_PATTERN_NAME(1000, proc_id_recv)[] = {0x88, 0x03, 0x40, 0xF9, 0x08, 0x1D, 0x40, 0xF9, 0xE0, 0x03, 0x1C, 0xAA, 0x00, 0x01, 0x3F, 0xD6, 0xE8, 0x87, 0x40, 0xF9, 0x08, 0x49, 0x3A, 0x8B, 0x09, 0xFC, 0x60, 0xD3};
static const instruction_t MAKE_KERNEL_PATCH_NAME(1000, proc_id_recv)[] = {0xA9BF2FEA, 0xF94067EB, 0x2A1A03EA, 0xD37EF54A, 0xF86A696A, 0x92FFFFE9, 0x8A090148, 0xD2FFFFE9, 0x8A09014A, 0xD2FFFFC9, 0xEB09015F, 0x54000100, 0xA9BF27E8, 0xF9400388, 0xF9401D08, 0xAA1C03E0, 0xD63F0100, 0xA8C127E8, 0xAA0003E8, 0xA8C12FEA, 0xAA0803E0};
/* svcControlCodeMemory Patches */
/* b.eq -> nop */
static const instruction_t MAKE_KERNEL_PATCH_NAME(500, svc_control_codememory)[] = {MAKE_NOP};
static const instruction_t MAKE_KERNEL_PATCH_NAME(600, svc_control_codememory)[] = {MAKE_NOP};
static const instruction_t MAKE_KERNEL_PATCH_NAME(700, svc_control_codememory)[] = {MAKE_NOP};
static const instruction_t MAKE_KERNEL_PATCH_NAME(800, svc_control_codememory)[] = {MAKE_NOP};
static const instruction_t MAKE_KERNEL_PATCH_NAME(900, svc_control_codememory)[] = {MAKE_NOP};
static const instruction_t MAKE_KERNEL_PATCH_NAME(1000, svc_control_codememory)[] = {MAKE_NOP};
static const instruction_t MAKE_KERNEL_PATCH_NAME(500, svc_control_codememory)[] = {MAKE_NOP};
static const instruction_t MAKE_KERNEL_PATCH_NAME(600, svc_control_codememory)[] = {MAKE_NOP};
static const instruction_t MAKE_KERNEL_PATCH_NAME(700, svc_control_codememory)[] = {MAKE_NOP};
static const instruction_t MAKE_KERNEL_PATCH_NAME(800, svc_control_codememory)[] = {MAKE_NOP};
static const instruction_t MAKE_KERNEL_PATCH_NAME(900, svc_control_codememory)[] = {MAKE_NOP};
static const instruction_t MAKE_KERNEL_PATCH_NAME(500, system_memory_increase)[] = {0x52A3C008}; /* MOV W8, #0x1E000000 */
static const instruction_t MAKE_KERNEL_PATCH_NAME(600, system_memory_increase)[] = {0x52A3B008}; /* MOV W8, #0x1D800000 */
static const instruction_t MAKE_KERNEL_PATCH_NAME(700, system_memory_increase)[] = {0x52A3B008}; /* MOV W8, #0x1D800000 */
static const instruction_t MAKE_KERNEL_PATCH_NAME(800, system_memory_increase)[] = {0x52A3B013}; /* MOV W19, #0x1D800000 */
static const instruction_t MAKE_KERNEL_PATCH_NAME(900, system_memory_increase)[] = {0x52A3B013}; /* MOV W19, #0x1D800000 */
static const instruction_t MAKE_KERNEL_PATCH_NAME(1000, system_memory_increase)[] = {0x52A3B013}; /* MOV W19, #0x1D800000 */
static const instruction_t MAKE_KERNEL_PATCH_NAME(500, system_memory_increase)[] = {0x52A3C008}; /* MOV W8, #0x1E000000 */
static const instruction_t MAKE_KERNEL_PATCH_NAME(600, system_memory_increase)[] = {0x52A3B008}; /* MOV W8, #0x1D800000 */
static const instruction_t MAKE_KERNEL_PATCH_NAME(700, system_memory_increase)[] = {0x52A3B008}; /* MOV W8, #0x1D800000 */
static const instruction_t MAKE_KERNEL_PATCH_NAME(800, system_memory_increase)[] = {0x52A3B013}; /* MOV W19, #0x1D800000 */
static const instruction_t MAKE_KERNEL_PATCH_NAME(900, system_memory_increase)[] = {0x52A3B013}; /* MOV W19, #0x1D800000 */
/* Hook Definitions. */
static const kernel_patch_t g_kernel_patches_100[] = {
@@ -792,35 +735,6 @@ static const kernel_patch_t g_kernel_patches_900[] = {
}
};
static const kernel_patch_t g_kernel_patches_1000[] = {
{ /* Send Message Process ID Patch. */
.pattern_size = 0x1C,
.pattern = MAKE_KERNEL_PATTERN_NAME(1000, proc_id_send),
.pattern_hook_offset = 0x0,
.payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(1000, proc_id_send))/sizeof(instruction_t),
.branch_back_offset = 0x10,
.payload = MAKE_KERNEL_PATCH_NAME(1000, proc_id_send)
},
{ /* Receive Message Process ID Patch. */
.pattern_size = 0x1C,
.pattern = MAKE_KERNEL_PATTERN_NAME(1000, proc_id_recv),
.pattern_hook_offset = 0x0,
.payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(1000, proc_id_recv))/sizeof(instruction_t),
.branch_back_offset = 0x10,
.payload = MAKE_KERNEL_PATCH_NAME(1000, proc_id_recv)
},
{ /* svcControlCodeMemory Patch. */
.payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(1000, svc_control_codememory))/sizeof(instruction_t),
.payload = MAKE_KERNEL_PATCH_NAME(1000, svc_control_codememory),
.patch_offset = 0x45DAC,
},
{ /* System Memory Increase Patch. */
.payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(1000, system_memory_increase))/sizeof(instruction_t),
.payload = MAKE_KERNEL_PATCH_NAME(1000, system_memory_increase),
.patch_offset = 0x66950,
}
};
#define KERNEL_PATCHES(vers) .num_patches = sizeof(g_kernel_patches_##vers)/sizeof(kernel_patch_t), .patches = g_kernel_patches_##vers,
/* Kernel Infos. */
@@ -887,25 +801,6 @@ static const kernel_info_t g_kernel_infos[] = {
.embedded_ini_ptr = 0x180,
.free_code_space_offset = 0x65780,
KERNEL_PATCHES(900)
},
{ /* 9.2.0. */
/* NOTE: 9.2.0 has identical kernel layout to 9.0.0, so patches may be reused. */
.hash = {0x66, 0xE7, 0x73, 0xE7, 0xF5, 0xCB, 0x9B, 0xB8, 0x66, 0xB7, 0xB1, 0x26, 0x23, 0x02, 0x76, 0xF2, 0xD1, 0x60, 0x3E, 0x09, 0x14, 0x19, 0xC2, 0x84, 0xFA, 0x5D, 0x0F, 0x16, 0xA3, 0x65, 0xFA, 0x17},
.hash_offset = 0x1C0,
.hash_size = 0x90000 - 0x1C0,
.embedded_ini_offset = 0x90000,
.embedded_ini_ptr = 0x180,
.free_code_space_offset = 0x65780,
KERNEL_PATCHES(900)
},
{ /* 10.0.0. */
.hash = {0x59, 0x31, 0xE6, 0x46, 0xF7, 0xAA, 0x15, 0x59, 0x78, 0xC7, 0xB3, 0xA5, 0xFA, 0x80, 0xE2, 0xC0, 0xCA, 0x6F, 0x31, 0x97, 0x3D, 0x86, 0x8A, 0x69, 0xF3, 0xBF, 0xE6, 0xE5, 0x61, 0xA7, 0x1B, 0x5B, },
.hash_offset = 0x1B8,
.hash_size = 0x93000 - 0x1B8,
.embedded_ini_offset = 0x93000,
.embedded_ini_ptr = 0x178,
.free_code_space_offset = 0x67790,
KERNEL_PATCHES(1000)
}
};
@@ -950,7 +845,7 @@ const kernel_info_t *get_kernel_info(void *kernel, size_t size) {
return NULL;
}
void package2_patch_kernel(void *_kernel, size_t *kernel_size, bool is_sd_kernel, void **out_ini1, uint32_t target_firmware) {
void package2_patch_kernel(void *_kernel, size_t *kernel_size, bool is_sd_kernel, void **out_ini1) {
const kernel_info_t *kernel_info = get_kernel_info(_kernel, *kernel_size);
*out_ini1 = NULL;
@@ -971,12 +866,6 @@ void package2_patch_kernel(void *_kernel, size_t *kernel_size, bool is_sd_kernel
const uint32_t kernel_ldr_offset = *((volatile uint64_t *)((uintptr_t)_kernel + kernel_info->embedded_ini_ptr + 8));
memcpy((void *)((uintptr_t)_kernel + kernel_ldr_offset), kernel_ldr_bin, kernel_ldr_bin_size);
/* Set target firmware for our kernel loader. */
uint32_t *kldr_u32 = (uint32_t *)((uintptr_t)_kernel + kernel_ldr_offset);
if (kldr_u32[1] == 0x30444C4D) {
kldr_u32[2] = target_firmware;
}
/* Update size. */
*kernel_size = kernel_ldr_offset + kernel_ldr_bin_size;

View File

@@ -19,6 +19,6 @@
#include "utils.h"
void package2_patch_kernel(void *kernel, size_t *kernel_size, bool is_sd_kernel, void **out_ini1, uint32_t target_firmware);
void package2_patch_kernel(void *kernel, size_t *kernel_size, bool is_sd_kernel, void **out_ini1);
#endif

View File

@@ -160,7 +160,6 @@ int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, ui
desired_keyblob = MASTERKEY_REVISION_900;
/* Fallthrough */
case ATMOSPHERE_TARGET_FIRMWARE_910:
case ATMOSPHERE_TARGET_FIRMWARE_1000:
desired_keyblob = MASTERKEY_REVISION_910_CURRENT;
break;
default:

View File

@@ -184,12 +184,6 @@ static int stratosphere_ini_handler(void *user, const char *section, const char
strat_cfg->has_nogc_config = true;
sscanf(value, "%d", &tmp);
strat_cfg->enable_nogc = tmp != 0;
} else if (strcmp(name, STRATOSPHERE_ENABLE_NCM_KEY) == 0) {
sscanf(value, "%d", &tmp);
strat_cfg->ncm_enabled = tmp != 0;
if (strat_cfg->ncm_enabled) {
stratosphere_enable_ncm();
}
} else {
return 0;
}
@@ -232,8 +226,6 @@ static uint32_t nxboot_get_target_firmware(const void *package1loader) {
return ATMOSPHERE_TARGET_FIRMWARE_900;
} else if (memcmp(package1loader_header->build_timestamp, "20191021", 8) == 0) {
return ATMOSPHERE_TARGET_FIRMWARE_910;
} else if (memcmp(package1loader_header->build_timestamp, "20200303", 8) == 0) {
return ATMOSPHERE_TARGET_FIRMWARE_1000;
} else {
fatal_error("[NXBOOT] Unable to identify package1!\n");
}

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");
@@ -87,7 +90,7 @@ void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firm
}
/* Perform any patches we want to the NX kernel. */
package2_patch_kernel(kernel, &kernel_size, is_sd_kernel, (void *)&orig_ini1, target_firmware);
package2_patch_kernel(kernel, &kernel_size, is_sd_kernel, (void *)&orig_ini1);
/* Ensure we know where embedded INI is if present, and we don't if not. */
if ((target_firmware < ATMOSPHERE_TARGET_FIRMWARE_800 && orig_ini1 != NULL) ||
@@ -125,6 +128,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);
@@ -232,7 +238,7 @@ static bool package2_validate_metadata(package2_meta_t *metadata, uint8_t data[]
/* Perform version checks. */
/* We will be compatible with all package2s released before current, but not newer ones. */
if (metadata->version_max >= PACKAGE2_MINVER_THEORETICAL && metadata->version_min < PACKAGE2_MAXVER_1000_CURRENT) {
if (metadata->version_max >= PACKAGE2_MINVER_THEORETICAL && metadata->version_min < PACKAGE2_MAXVER_910_CURRENT) {
return true;
}
@@ -309,12 +315,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) {
@@ -335,7 +338,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;
@@ -347,6 +350,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

@@ -39,8 +39,7 @@
#define PACKAGE2_MAXVER_700_800 0xA
#define PACKAGE2_MAXVER_810 0xB
#define PACKAGE2_MAXVER_900 0xC
#define PACKAGE2_MAXVER_910_920 0xD
#define PACKAGE2_MAXVER_1000_CURRENT 0xE
#define PACKAGE2_MAXVER_910_CURRENT 0xD
#define PACKAGE2_MINVER_100 0x3
#define PACKAGE2_MINVER_200 0x4
@@ -53,10 +52,10 @@
#define PACKAGE2_MINVER_700_800 0xB
#define PACKAGE2_MINVER_810 0xC
#define PACKAGE2_MINVER_900 0xD
#define PACKAGE2_MINVER_910_920 0xE
#define PACKAGE2_MINVER_1000_CURRENT 0xF
#define PACKAGE2_MINVER_910_CURRENT 0xE
#define NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS ((void *)(0xA9800000ull))
#define DRAM_BASE_PHYSICAL (0x80000000)
typedef struct {
union {

View File

@@ -615,7 +615,7 @@ static void sdmmc_autocal_run(sdmmc_t *sdmmc, SdmmcBusVoltage voltage)
while ((sdmmc->regs->auto_cal_status & SDMMC_AUTOCAL_ACTIVE)) {
/* Ensure we haven't timed out. */
if (get_time_since(timebase) > SDMMC_AUTOCAL_TIMEOUT) {
sdmmc_warn(sdmmc, "Auto-calibration timed out!");
sdmmc_error(sdmmc, "Auto-calibration timed out!");
/* Force a register read to refresh the clock control value. */
sdmmc_get_sd_clock_control(sdmmc);
@@ -1314,9 +1314,9 @@ static int sdmmc_wait_busy(sdmmc_t *sdmmc)
static void sdmmc_intr_enable(sdmmc_t *sdmmc)
{
/* Enable the relevant interrupts and set all error bits. */
sdmmc->regs->int_enable |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT);
/* Set all error bits and enable the relevant interrupts. */
sdmmc->regs->int_enable |= 0x017F0000;
sdmmc->regs->int_enable |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT);
/* Refresh status. */
sdmmc->regs->int_status = sdmmc->regs->int_status;
@@ -1324,35 +1324,34 @@ static void sdmmc_intr_enable(sdmmc_t *sdmmc)
static void sdmmc_intr_disable(sdmmc_t *sdmmc)
{
/* Clear all error bits and disable the relevant interrupts. */
/* Clear all error bits and the interrupts. */
sdmmc->regs->int_enable &= ~(0x017F0000);
sdmmc->regs->int_enable &= ~(TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT);
/* Refresh status. */
sdmmc->regs->int_status = sdmmc->regs->int_status;
}
static int sdmmc_intr_check(sdmmc_t *sdmmc, uint16_t *status_out, uint16_t status_mask)
static bool sdmmc_intr_check_status(sdmmc_t *sdmmc, uint16_t status_mask)
{
uint32_t int_status = sdmmc->regs->int_status;
bool is_masked = (sdmmc->regs->int_status & status_mask);
sdmmc_debug(sdmmc, "INTSTS: %08X", int_status);
/* Mask status. */
if (is_masked)
sdmmc->regs->int_status &= status_mask;
/* Return the status, if necessary. */
if (status_out)
*status_out = (int_status & 0xFFFF);
return is_masked;
}
static bool sdmmc_intr_check_error(sdmmc_t *sdmmc)
{
bool is_error = (sdmmc->regs->int_status & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT);
if (int_status & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT)
{
/* Acknowledge error by refreshing status. */
sdmmc->regs->int_status = int_status;
return -1;
}
else if (int_status & status_mask)
{
/* Mask the status. */
sdmmc->regs->int_status = (int_status & status_mask);
return 1;
}
/* Refresh status. */
if (is_error)
sdmmc->regs->int_status = sdmmc->regs->int_status;
return 0;
return is_error;
}
static int sdmmc_dma_init(sdmmc_t *sdmmc, sdmmc_request_t *req)
@@ -1443,23 +1442,15 @@ static int sdmmc_dma_update(sdmmc_t *sdmmc)
/* Watch over the DMA transfer. */
while (!is_timeout)
{
/* Check interrupts. */
uint16_t intr_status = 0;
int intr_res = sdmmc_intr_check(sdmmc, &intr_status, TEGRA_MMC_NORINTSTS_XFER_COMPLETE | TEGRA_MMC_NORINTSTS_DMA_INTERRUPT);
/* An error has been raised. Reset. */
if (intr_res < 0)
if (sdmmc_intr_check_error(sdmmc))
{
sdmmc_do_sw_reset(sdmmc);
return 0;
}
/* Transfer is over. */
if (intr_status & TEGRA_MMC_NORINTSTS_XFER_COMPLETE)
return 1;
/* We have a DMA interrupt. Restart the transfer where it was interrupted. */
if (intr_status & TEGRA_MMC_NORINTSTS_DMA_INTERRUPT)
if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTS_DMA_INTERRUPT))
{
if (sdmmc->use_adma)
{
@@ -1476,6 +1467,10 @@ static int sdmmc_dma_update(sdmmc_t *sdmmc)
sdmmc->next_dma_addr += 0x80000;
}
/* Transfer is over. */
if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTS_XFER_COMPLETE))
return 1;
/* Keep checking if timeout expired. */
is_timeout = (get_time_since(timebase) > 2000000);
}
@@ -1531,15 +1526,12 @@ static int sdmmc_wait_for_cmd(sdmmc_t *sdmmc)
/* Wait for CMD to finish. */
while (!is_err && !is_timeout) {
/* Check interrupts. */
int intr_res = sdmmc_intr_check(sdmmc, 0, TEGRA_MMC_NORINTSTS_CMD_COMPLETE);
/* Command is done. */
if (intr_res > 0)
if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTS_CMD_COMPLETE))
return 1;
/* Check for any raised errors. */
is_err = (intr_res < 0);
is_err = sdmmc_intr_check_error(sdmmc);
/* Keep checking if timeout expired. */
is_timeout = (get_time_since(timebase) > 2000000);
@@ -1650,7 +1642,6 @@ int sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_command_t *cmd, sdmmc_request_t *req, u
is_dma = true;
dma_blkcnt = sdmmc_dma_init(sdmmc, req);
/* Abort in case initialization failed. */
if (!dma_blkcnt)
{
sdmmc_error(sdmmc, "Failed to initialize the DMA transfer!");
@@ -1678,14 +1669,12 @@ int sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_command_t *cmd, sdmmc_request_t *req, u
/* Save response, if necessary. */
sdmmc_save_response(sdmmc, cmd->flags);
/* Update the DMA request. */
/* Process the DMA request. */
if (req)
{
/* Disable interrupts and abort in case updating failed. */
if (!sdmmc_dma_update(sdmmc))
{
sdmmc_warn(sdmmc, "Failed to update the DMA transfer!");
sdmmc_intr_disable(sdmmc);
sdmmc_error(sdmmc, "Failed to process the DMA transfer!");
return 0;
}
@@ -1851,7 +1840,7 @@ static int sdmmc_send_tuning(sdmmc_t *sdmmc, uint32_t opcode)
while (!is_timeout)
{
/* Buffer Read Ready was asserted. */
if (sdmmc_intr_check(sdmmc, 0, TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY) > 0)
if (sdmmc_intr_check_status(sdmmc, TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY))
{
/* Manually disable the Buffer Read Ready interrupt. */
sdmmc->regs->int_enable &= ~(TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY);

View File

@@ -97,18 +97,11 @@ _metadata:
#define CONTENT_TYPE_KLD 9
#define CONTENT_TYPE_KRN 10
#define CONTENT_FLAG_NONE (0 << 0)
#define CONTENT_FLAG0_EXPERIMENTAL (1 << 0)
_content_headers:
/* ams_mitm content header */
.word __ams_mitm_kip_start__
.word __ams_mitm_kip_size__
.byte CONTENT_TYPE_KIP
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word CONTENT_TYPE_KIP
.word 0xCCCCCCCC
.asciz "ams_mitm"
.align 5
@@ -116,10 +109,7 @@ _content_headers:
/* boot content header */
.word __boot_kip_start__
.word __boot_kip_size__
.byte CONTENT_TYPE_KIP
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word CONTENT_TYPE_KIP
.word 0xCCCCCCCC
.asciz "boot"
.align 5
@@ -127,10 +117,7 @@ _content_headers:
/* exosphere content header */
.word __exosphere_bin_start__
.word __exosphere_bin_size__
.byte CONTENT_TYPE_EXO
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word CONTENT_TYPE_EXO
.word 0xCCCCCCCC
.asciz "exosphere"
.align 5
@@ -138,10 +125,7 @@ _content_headers:
/* fusee_primary content header */
.word __fusee_primary_bin_start__
.word __fusee_primary_bin_size__
.byte CONTENT_TYPE_FSP
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word CONTENT_TYPE_FSP
.word 0xCCCCCCCC
.asciz "fusee_primary"
.align 5
@@ -149,21 +133,15 @@ _content_headers:
/* loader content header */
.word __loader_kip_start__
.word __loader_kip_size__
.byte CONTENT_TYPE_KIP
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word CONTENT_TYPE_KIP
.word 0xCCCCCCCC
.asciz "Loader"
.asciz "loader"
.align 5
/* lp0fw content header */
.word __lp0fw_bin_start__
.word __lp0fw_bin_size__
.byte CONTENT_TYPE_WBT
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word CONTENT_TYPE_WBT
.word 0xCCCCCCCC
.asciz "lp0fw"
.align 5
@@ -171,21 +149,15 @@ _content_headers:
/* pm content header */
.word __pm_kip_start__
.word __pm_kip_size__
.byte CONTENT_TYPE_KIP
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word CONTENT_TYPE_KIP
.word 0xCCCCCCCC
.asciz "ProcessManager"
.asciz "pm"
.align 5
/* rebootstub content header */
.word __rebootstub_bin_start__
.word __rebootstub_bin_size__
.byte CONTENT_TYPE_RBT
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word CONTENT_TYPE_RBT
.word 0xCCCCCCCC
.asciz "rebootstub"
.align 5
@@ -193,10 +165,7 @@ _content_headers:
/* sept_primary content header */
.word __sept_primary_bin_start__
.word __sept_primary_bin_size__
.byte CONTENT_TYPE_SP1
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word CONTENT_TYPE_SP1
.word 0xCCCCCCCC
.asciz "sept_primary"
.align 5
@@ -204,10 +173,7 @@ _content_headers:
/* sept_secondary 00 content header */
.word __sept_secondary_00_enc_start__
.word __sept_secondary_00_enc_size__
.byte CONTENT_TYPE_SP2
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word CONTENT_TYPE_SP2
.word 0xCCCCCCCC
.asciz "septsecondary00"
.align 5
@@ -215,10 +181,7 @@ _content_headers:
/* sept_secondary 01 content header */
.word __sept_secondary_01_enc_start__
.word __sept_secondary_01_enc_size__
.byte CONTENT_TYPE_SP2
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word CONTENT_TYPE_SP2
.word 0xCCCCCCCC
.asciz "septsecondary01"
.align 5
@@ -226,10 +189,7 @@ _content_headers:
/* sm content header */
.word __sm_kip_start__
.word __sm_kip_size__
.byte CONTENT_TYPE_KIP
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word CONTENT_TYPE_KIP
.word 0xCCCCCCCC
.asciz "sm"
.align 5
@@ -237,32 +197,15 @@ _content_headers:
/* spl content header */
.word __spl_kip_start__
.word __spl_kip_size__
.byte CONTENT_TYPE_KIP
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word CONTENT_TYPE_KIP
.word 0xCCCCCCCC
.asciz "spl"
.align 5
/* ncm content header */
.word __ncm_kip_start__
.word __ncm_kip_size__
.byte CONTENT_TYPE_KIP
.byte CONTENT_FLAG0_EXPERIMENTAL
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word 0xCCCCCCCC
.asciz "NCM"
.align 5
/* emummc content header */
.word __emummc_kip_start__
.word __emummc_kip_size__
.byte CONTENT_TYPE_EMC
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word CONTENT_TYPE_EMC
.word 0xCCCCCCCC
.asciz "emummc"
.align 5
@@ -270,10 +213,7 @@ _content_headers:
/* kernel_ldr content header */
.word __kernel_ldr_bin_start__
.word __kernel_ldr_bin_size__
.byte CONTENT_TYPE_KLD
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word CONTENT_TYPE_KLD
.word 0xCCCCCCCC
.asciz "kernel_ldr"
.align 5
@@ -281,10 +221,7 @@ _content_headers:
/* splash_screen content header */
.word __splash_screen_bmp_start__
.word __splash_screen_bmp_size__
.byte CONTENT_TYPE_BMP
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.byte CONTENT_FLAG_NONE
.word CONTENT_TYPE_BMP
.word 0xCCCCCCCC
.asciz "splash_screen"
.align 5

View File

@@ -13,7 +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/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
@@ -30,7 +30,6 @@
#define u8 uint8_t
#define u32 uint32_t
#include "loader_kip.h"
#include "ncm_kip.h"
#include "pm_kip.h"
#include "sm_kip.h"
#include "ams_mitm_kip.h"
@@ -48,10 +47,9 @@ static bool g_stratosphere_pm_enabled = true;
static bool g_stratosphere_ams_mitm_enabled = true;
static bool g_stratosphere_spl_enabled = true;
static bool g_stratosphere_boot_enabled = true;
static bool g_stratosphere_ncm_enabled = false;
extern const uint8_t loader_kip[], pm_kip[], sm_kip[], spl_kip[], boot_kip[], ncm_kip[], ams_mitm_kip[];
extern const uint32_t loader_kip_size, pm_kip_size, sm_kip_size, spl_kip_size, boot_kip_size, ncm_kip_size, ams_mitm_kip_size;
extern const uint8_t loader_kip[], pm_kip[], sm_kip[], spl_kip[], boot_kip[], ams_mitm_kip[];
extern const uint32_t loader_kip_size, pm_kip_size, sm_kip_size, spl_kip_size, boot_kip_size, ams_mitm_kip_size;
static emummc_fs_ver_t g_fs_ver = FS_VER_1_0_0;
@@ -59,19 +57,6 @@ emummc_fs_ver_t stratosphere_get_fs_version(void) {
return g_fs_ver;
}
void stratosphere_enable_ncm(void) {
/* The Atmosphere team believes our implementation of NCM to be extremely accurate, */
/* and does not think it likely there is any possibility of undesirable behavior */
/* when using the NCM reimplementation. However, because NCM manages critical save games */
/* the implementation will default to off for some time, until the code has been thoroughly */
/* tested in practice. */
/* PLEASE NOTE: The default behavior will be NCM on in a future atmosphere release, */
/* and this opt-in functionality will be removed at that time. */
g_stratosphere_ncm_enabled = true;
}
/* GCC doesn't consider the size as const... we have to write it ourselves. */
ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
@@ -83,43 +68,38 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
}
size_t size = sizeof(ini1_header_t);
/* Calculate our processes' sizes. */
if (g_stratosphere_loader_enabled) {
size += loader_kip_size;
num_processes++;
}
if (g_stratosphere_pm_enabled) {
size += pm_kip_size;
num_processes++;
}
if (g_stratosphere_sm_enabled) {
size += sm_kip_size;
num_processes++;
}
if (g_stratosphere_spl_enabled) {
size += spl_kip_size;
num_processes++;
}
if (g_stratosphere_ams_mitm_enabled) {
size += ams_mitm_kip_size;
num_processes++;
}
if (g_stratosphere_ncm_enabled) {
size += ncm_kip_size;
num_processes++;
}
if (g_stratosphere_boot_enabled) {
size += boot_kip_size;
num_processes++;
}
g_stratosphere_ini1 = (ini1_header_t *)malloc(size);
if (g_stratosphere_ini1 == NULL) {
@@ -154,11 +134,6 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
data += spl_kip_size;
}
if (g_stratosphere_ncm_enabled) {
memcpy(data, ncm_kip, ncm_kip_size);
data += ncm_kip_size;
}
if (g_stratosphere_ams_mitm_enabled) {
memcpy(data, ams_mitm_kip, ams_mitm_kip_size);
data += ams_mitm_kip_size;
@@ -168,7 +143,7 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
memcpy(data, boot_kip, boot_kip_size);
data += boot_kip_size;
}
return g_stratosphere_ini1;
}
@@ -185,26 +160,26 @@ void stratosphere_free_ini1(void) {
static void try_add_sd_kip(ini1_header_t *ini1, const char *kip_path) {
size_t file_size = get_file_size(kip_path);
if (ini1->size + file_size > PACKAGE2_SIZE_MAX) {
fatal_error("Failed to load %s: INI1 would be too large!\n", kip_path);
}
kip1_header_t kip_header;
if (read_from_file(&kip_header, sizeof(kip_header), kip_path) != sizeof(kip_header) || kip_header.magic != MAGIC_KIP1) {
return;
}
size_t kip_size = kip1_get_size_from_header(&kip_header);
if (kip_size > file_size) {
fatal_error("Failed to load %s: KIP size is corrupt!\n", kip_path);
}
if (read_from_file(ini1->kip_data + ini1->size - sizeof(ini1_header_t), kip_size, kip_path) != kip_size) {
/* TODO: is this error fatal? */
return;
}
ini1->size += kip_size;
ini1->num_processes++;
}
@@ -214,24 +189,24 @@ static kip1_header_t *inject_emummc_kip(kip1_header_t *fs_kip, kip1_header_t *em
size_t fs_kip_size, emummc_kip_size;
fs_kip = kip1_uncompress(fs_kip, &fs_kip_size);
emummc_kip = kip1_uncompress(emummc_kip, &emummc_kip_size);
/* Allocate kip. */
kip1_header_t *injected_kip = calloc(1, fs_kip_size + emummc_kip_size);
if (injected_kip == NULL) {
fatal_error("Failed to allocate memory for emummc kip injection!");
}
memcpy(injected_kip, fs_kip, sizeof(*fs_kip));
const size_t fs_contents_size = kip1_get_size_from_header(fs_kip) - sizeof(kip1_header_t);
const size_t emummc_data_size = emummc_kip->section_headers[3].out_offset + emummc_kip->section_headers[3].out_size;
if (emummc_data_size & 0xFFF) {
fatal_error("Invalid emummc kip!");
}
/* Copy over capabilities. */
memcpy(injected_kip->capabilities, emummc_kip->capabilities, sizeof(emummc_kip->capabilities));
/* Add extra cap for 1.0.0 */
if (stratosphere_get_fs_version() == FS_VER_1_0_0) {
for (size_t i = 0; i < 0x20; i++) {
@@ -242,14 +217,14 @@ static kip1_header_t *inject_emummc_kip(kip1_header_t *fs_kip, kip1_header_t *em
}
}
}
/* Update sections. */
injected_kip->section_headers[0].out_size += emummc_data_size;
injected_kip->section_headers[0].compressed_size += emummc_data_size;
for (size_t i = 1; i < 4; i++) {
injected_kip->section_headers[i].out_offset += emummc_data_size;
}
/* Copy data. */
{
size_t ofs = 0;
@@ -259,22 +234,22 @@ static kip1_header_t *inject_emummc_kip(kip1_header_t *fs_kip, kip1_header_t *em
}
}
memcpy(injected_kip->data + emummc_data_size, fs_kip->data, fs_contents_size);
return injected_kip;
}
ini1_header_t *stratosphere_get_sd_files_ini1(void) {
ini1_header_t *stratosphere_get_sd_files_ini1(void) {
if (g_sd_files_ini1 != NULL) {
return g_sd_files_ini1;
}
/* Allocate space. */
g_sd_files_ini1 = (ini1_header_t *)malloc(PACKAGE2_SIZE_MAX);
g_sd_files_ini1->magic = MAGIC_INI1;
g_sd_files_ini1->size = sizeof(ini1_header_t);
g_sd_files_ini1->num_processes = 0;
g_sd_files_ini1->_0xC = 0;
/* Load all kips from /atmosphere/kips/<*>.kip or /atmosphere/kips/<*>/<*>.kip. */
DIR *kips_dir = opendir("atmosphere/kips");
struct dirent *ent;
@@ -283,15 +258,15 @@ ini1_header_t *stratosphere_get_sd_files_ini1(void) {
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
continue;
}
char kip_path[0x301] = {0};
snprintf(kip_path, 0x300, "atmosphere/kips/%s", ent->d_name);
struct stat kip_stat;
if (stat(kip_path, &kip_stat) == -1) {
continue;
}
if ((kip_stat.st_mode & S_IFMT) == S_IFREG) {
/* If file, add to ini1. */
try_add_sd_kip(g_sd_files_ini1, kip_path);
@@ -304,15 +279,15 @@ ini1_header_t *stratosphere_get_sd_files_ini1(void) {
if (strcmp(sub_ent->d_name, ".") == 0 || strcmp(sub_ent->d_name, "..") == 0) {
continue;
}
/* Reuse kip path variable. */
memset(kip_path, 0, sizeof(kip_path));
snprintf(kip_path, 0x300, "atmosphere/kips/%s/%s", ent->d_name, sub_ent->d_name);
if (stat(kip_path, &kip_stat) == -1) {
continue;
}
if ((kip_stat.st_mode & S_IFMT) == S_IFREG) {
/* If file, add to ini1. */
try_add_sd_kip(g_sd_files_ini1, kip_path);
@@ -324,7 +299,7 @@ ini1_header_t *stratosphere_get_sd_files_ini1(void) {
}
closedir(kips_dir);
}
return g_sd_files_ini1;
}
@@ -369,7 +344,7 @@ ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, size_t num_inis, vo
}
offset += kip1_get_size_from_header(current_kip);
bool already_loaded = false;
for (uint32_t j = 0; j < merged->num_processes; j++) {
if (process_list[j] == current_kip->title_id) {
@@ -380,20 +355,20 @@ ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, size_t num_inis, vo
if (already_loaded) {
continue;
}
print(SCREEN_LOG_LEVEL_MANDATORY, "[NXBOOT]: Loading KIP %08x%08x...\n", (uint32_t)(current_kip->title_id >> 32), (uint32_t)current_kip->title_id);
size_t current_kip_size = kip1_get_size_from_header(current_kip);
if (current_kip_size > remaining_size) {
fatal_error("Not enough space for all the KIP1s!\n");
}
kip1_header_t *patched_kip = apply_kip_ips_patches(current_kip, current_kip_size, &g_fs_ver);
if (current_kip->title_id == FS_TITLE_ID && emummc != NULL) {
patched_kip = inject_emummc_kip(patched_kip != NULL ? patched_kip : current_kip, (kip1_header_t *)emummc);
}
if (patched_kip != NULL) {
size_t patched_kip_size = kip1_get_size_from_header(patched_kip);
if (patched_kip_size > remaining_size) {
@@ -402,9 +377,9 @@ ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, size_t num_inis, vo
memcpy(current_dst_kip, patched_kip, patched_kip_size);
remaining_size -= patched_kip_size;
current_dst_kip += patched_kip_size;
free(patched_kip);
} else {
} else {
memcpy(current_dst_kip, current_kip, current_kip_size);
remaining_size -= current_kip_size;
current_dst_kip += current_kip_size;

View File

@@ -13,7 +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/>.
*/
#ifndef FUSEE_STRATOSPHERE_H
#define FUSEE_STRATOSPHERE_H
@@ -30,8 +30,6 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware);
ini1_header_t *stratosphere_get_sd_files_ini1(void);
void stratosphere_free_ini1(void);
void stratosphere_enable_ncm(void);
emummc_fs_ver_t stratosphere_get_fs_version(void);
ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, unsigned int num_inis, void *emummc, size_t emummc_size);
@@ -39,10 +37,8 @@ ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, unsigned int num_in
typedef struct {
bool has_nogc_config;
bool enable_nogc;
bool ncm_enabled;
} stratosphere_cfg_t;
#define STRATOSPHERE_NOGC_KEY "nogc"
#define STRATOSPHERE_ENABLE_NCM_KEY "enable_ncm"
#endif

View File

@@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
branch = master
commit = 96825c7524c333536dcffadb76341bf599785538
parent = d81a3bdc36ddf3d7b4f3d7fae5cb85afc047b546
commit = 08c9b3cbf85471fe6adb5f42ba9f03357a5f633a
parent = 814c9d1cfb54c192bc1deb140ab476c9f3dd0edf
method = merge
cmdver = 0.4.1
cmdver = 0.4.0

View File

@@ -11,7 +11,6 @@ You can find a copy of the license in the [LICENSE file](LICENSE).
Exemptions:
* The [yuzu emulator project](https://github.com/yuzu-emu/yuzu) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the atmosphere-libs project as GPLv2 or later.
* [Nintendo](https://github.com/Nintendo) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the atmosphere-libs project under the Zero-Clause BSD license.
Credits
=====

View File

@@ -5,7 +5,7 @@ endif
include $(DEVKITPRO)/devkitA64/base_rules
export ATMOSPHERE_DEFINES += -DATMOSPHERE_ARCH_ARM64
export ATMOSPHERE_SETTINGS += -march=armv8-a+crc+crypto -mtp=soft
export ATMOSPHERE_SETTINGS += -march=armv8-a -mtp=soft
export ATMOSPHERE_CFLAGS +=
export ATMOSPHERE_CXXFLAGS +=
export ATMOSPHERE_ASFLAGS +=

View File

@@ -1,4 +1,4 @@
export ATMOSPHERE_DEFINES += -DATMOSPHERE_BOARD_NINTENDO_NX -D__SWITCH__
export ATMOSPHERE_DEFINES += -DATMOSPHERE_BOARD_NINTENDO_SWITCH -D__SWITCH__
export ATMOSPHERE_SETTINGS +=
export ATMOSPHERE_CFLAGS +=
export ATMOSPHERE_CXXFLAGS +=

View File

@@ -24,11 +24,11 @@ export ATMOSPHERE_ASFLAGS :=
ifeq ($(ATMOSPHERE_BOARD),nx-hac-001)
export ATMOSPHERE_ARCH_DIR := arch/arm64
export ATMOSPHERE_BOARD_DIR := board/nintendo/nx
export ATMOSPHERE_BOARD_DIR := board/nintendo/switch
export ATMOSPHERE_OS_DIR := os/horizon
export ATMOSPHERE_ARCH_NAME := arm64
export ATMOSPHERE_BOARD_NAME := nintendo_nx
export ATMOSPHERE_BOARD_NAME := nintendo_switch
export ATMOSPHERE_OS_NAME := horizon
endif
@@ -76,17 +76,14 @@ TARGET := $(notdir $(CURDIR))
BUILD := build
DATA := data
INCLUDES := include
SOURCES ?= $(foreach d,$(filter-out source/arch source/board,$(wildcard source)),$(call DIR_WILDCARD,$d) $d)
GENERAL_SOURCE_DIRS=$1 $(foreach d,$(filter-out $1/arch $1/board $1,$(wildcard $1/*)),$(if $(wildcard $d/.),$(call DIR_WILDCARD,$d) $d,))
SPECIFIC_SOURCE_DIRS=$(if $(wildcard $1/$2/.*),$1/$2 $(call DIR_WILDCARD,$1/$2),)
ALL_SOURCE_DIRS=$(call GENERAL_SOURCE_DIRS,$1) $(call SPECIFIC_SOURCE_DIRS,$1,$(ATMOSPHERE_ARCH_DIR)) $(call SPECIFIC_SOURCE_DIRS,$1,$(ATMOSPHERE_BOARD_DIR)) $(call SPECIFIC_SOURCE_DIRS,$1,$(ATMOSPHERE_OS_DIR))
SOURCES ?= $(call ALL_SOURCE_DIRS,source)
#---------------------------------------------------------------------------------
# Rules for compiling pre-compiled headers
#---------------------------------------------------------------------------------
%.gch: %.hpp
@echo $<
$(CXX) -w -x c++-header -MMD -MP -MF $(DEPSDIR)/$*.d $(CXXFLAGS) -c $< -o $@ $(ERROR_FILTER)
@cp $@ $(<).gch
ifneq ($(strip $(wildcard source/$(ATMOSPHERE_ARCH_DIR)/.*)),)
SOURCES += $(call DIR_WILDCARD,source/$(ATMOSPHERE_ARCH_DIR))
endif
ifneq ($(strip $(wildcard source/$(ATMOSPHERE_BOARD_DIR)/.*)),)
SOURCES += $(call DIR_WILDCARD,source/$(ATMOSPHERE_BOARD_DIR))
endif
ifneq ($(strip $(wildcard source/$(ATMOSPHERE_OS_DIR)/.*)),)
SOURCES += $(call DIR_WILDCARD,source/$(ATMOSPHERE_OS_DIR))
endif

View File

@@ -7,12 +7,12 @@ include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../common.mk
# options for code generation
#---------------------------------------------------------------------------------
export DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_MESOSPHERE
export SETTINGS := $(ATMOSPHERE_SETTINGS) -O2 -mgeneral-regs-only -ffixed-x18 -Werror -fno-non-call-exceptions
export SETTINGS := $(ATMOSPHERE_SETTINGS) -O2 -mgeneral-regs-only -ffixed-x18 -Werror
export CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
export CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit
export CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS)
export ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) $(DEFINES)
export LDFLAGS = -specs=$(TOPDIR)/$(notdir $(TOPDIR)).specs -nostdlib -nostartfiles -g $(SETTINGS) -Wl,-Map,$(notdir $*.map) -Wl,-z,relro,-z,now
export LDFLAGS = -specs=$(TOPDIR)/kernel_ldr.specs -nostdlib -nostartfiles -g $(SETTINGS) -Wl,-Map,$(notdir $*.map)
export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \
-Wl,--wrap,__cxa_throw \
@@ -25,6 +25,7 @@ export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \
-Wl,--wrap,__cxa_call_terminate \
-Wl,--wrap,__gxx_personality_v0 \
-Wl,--wrap,_Unwind_Resume \
-Wl,--wrap,_Unwind_Resume \
-Wl,--wrap,_ZSt19__throw_logic_errorPKc \
-Wl,--wrap,_ZSt20__throw_length_errorPKc \
-Wl,--wrap,_ZNSt11logic_errorC2EPKc

View File

@@ -15,11 +15,11 @@ include $(DEVKITPRO)/libnx/switch_rules
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
export DEFINES = $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_STRATOSPHERE -D_GNU_SOURCE
export SETTINGS = $(ATMOSPHERE_SETTINGS) -O2
export CFLAGS = $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
export CXXFLAGS = $(CFLAGS) $(ATMOSPHERE_CXXFLAGS)
export ASFLAGS = $(ATMOSPHERE_ASFLAGS) $(SETTINGS) $(DEFINES)
export DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_STRATOSPHERE
export SETTINGS := $(ATMOSPHERE_SETTINGS) -O2
export CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
export CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS)
export ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) $(DEFINES)
export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \
-Wl,--wrap,__cxa_throw \
@@ -36,17 +36,17 @@ export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \
-Wl,--wrap,_ZSt20__throw_length_errorPKc \
-Wl,--wrap,_ZNSt11logic_errorC2EPKc
export LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs $(SETTINGS) $(CXXWRAPS) -Wl,-Map,$(notdir $*.map)
export LDFLAGS := -specs=$(DEVKITPRO)/libnx/switch.specs $(SETTINGS) $(CXXWRAPS) -Wl,-Map,$(notdir $*.map)
export LIBS = -lstratosphere -lnx
export LIBS := -lstratosphere -lnx
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
export LIBDIRS = $(PORTLIBS) $(LIBNX) $(ATMOSPHERE_LIBRARIES_DIR)/libvapours $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere
export LIBDIRS := $(PORTLIBS) $(LIBNX) $(ATMOSPHERE_LIBRARIES_DIR)/libvapours $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere
#---------------------------------------------------------------------------------
# stratosphere sysmodules may (but usually do not) have an exefs source dir
#---------------------------------------------------------------------------------
export EXEFS_SRC = exefs_src
export EXEFS_SRC := exefs_src

View File

@@ -6,17 +6,12 @@ include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../config/common.mk
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
#PRECOMPILED_HEADERS := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/include/mesosphere.hpp
PRECOMPILED_HEADERS :=
DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_MESOSPHERE
SETTINGS := $(ATMOSPHERE_SETTINGS) -O2 -mgeneral-regs-only -ffixed-x18 -Werror -fno-non-call-exceptions
SETTINGS := $(ATMOSPHERE_SETTINGS) -O2 -mgeneral-regs-only -ffixed-x18 -Werror
CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit -flto
CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -flto
ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS)
SOURCES += $(call ALL_SOURCE_DIRS,../libvapours/source)
LIBS :=
#---------------------------------------------------------------------------------
@@ -32,7 +27,7 @@ LIBDIRS := $(ATMOSPHERE_LIBRARIES_DIR)/libvapours
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) $(CURDIR)/include \
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \
@@ -69,7 +64,6 @@ endif
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export GCH_FILES := $(foreach hdr,$(PRECOMPILED_HEADERS:.hpp=.gch),$(notdir $(hdr)))
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
@@ -111,15 +105,13 @@ clean:
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d) $(GCH_FILES:.gch=.d)
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(OUTPUT) : $(OFILES)
$(filter-out kern_svc_tables.o, $(OFILES)) : $(GCH_FILES)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------

View File

@@ -11,7 +11,6 @@ You can find a copy of the license in the [LICENSE file](LICENSE).
Exemptions:
* The [yuzu emulator project](https://github.com/yuzu-emu/yuzu) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the libmesosphere project as GPLv2 or later.
* [Nintendo](https://github.com/Nintendo) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the libmesosphere project under the Zero-Clause BSD license.
Credits
=====

View File

@@ -19,70 +19,20 @@
#include <vapours.hpp>
/* First, pull in core macros (panic, etc). */
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_panic.hpp>
#include "mesosphere/kern_panic.hpp"
/* Primitive types. */
#include <mesosphere/kern_k_typed_address.hpp>
#include <mesosphere/kern_initial_process.hpp>
#include <mesosphere/kern_k_exception_context.hpp>
#include "mesosphere/kern_k_typed_address.hpp"
#include "mesosphere/kern_initial_process.hpp"
/* Core pre-initialization includes. */
#include <mesosphere/kern_select_cpu.hpp>
#include <mesosphere/kern_select_system_control.hpp>
#include <mesosphere/kern_k_target_system.hpp>
#include "mesosphere/kern_select_cpu.hpp"
/* Initialization headers. */
#include <mesosphere/init/kern_init_elf.hpp>
#include <mesosphere/init/kern_init_layout.hpp>
#include <mesosphere/init/kern_init_slab_setup.hpp>
#include <mesosphere/init/kern_init_page_table_select.hpp>
#include <mesosphere/init/kern_init_arguments_select.hpp>
#include <mesosphere/kern_k_memory_layout.hpp>
#include "mesosphere/init/kern_init_elf.hpp"
#include "mesosphere/init/kern_init_layout.hpp"
#include "mesosphere/init/kern_init_page_table_select.hpp"
/* Core functionality. */
#include <mesosphere/kern_select_interrupt_manager.hpp>
#include <mesosphere/kern_k_spin_lock.hpp>
#include <mesosphere/kern_k_memory_manager.hpp>
#include <mesosphere/kern_k_interrupt_task_manager.hpp>
#include <mesosphere/kern_k_core_local_region.hpp>
#include <mesosphere/kern_k_slab_heap.hpp>
#include <mesosphere/kern_k_light_lock.hpp>
#include <mesosphere/kern_k_dpc_manager.hpp>
#include <mesosphere/kern_kernel.hpp>
#include <mesosphere/kern_k_page_table_manager.hpp>
#include <mesosphere/kern_select_page_table.hpp>
/* Miscellaneous objects. */
#include <mesosphere/kern_k_shared_memory_info.hpp>
#include <mesosphere/kern_k_event_info.hpp>
/* Auto Objects. */
#include <mesosphere/kern_k_auto_object.hpp>
#include <mesosphere/kern_k_synchronization_object.hpp>
#include <mesosphere/kern_k_readable_event.hpp>
#include <mesosphere/kern_k_handle_table.hpp>
#include <mesosphere/kern_k_event.hpp>
#include <mesosphere/kern_k_interrupt_event.hpp>
#include <mesosphere/kern_k_light_session.hpp>
#include <mesosphere/kern_k_session.hpp>
#include <mesosphere/kern_k_session_request.hpp>
#include <mesosphere/kern_k_port.hpp>
#include <mesosphere/kern_k_shared_memory.hpp>
#include <mesosphere/kern_k_transfer_memory.hpp>
#include <mesosphere/kern_k_code_memory.hpp>
#include <mesosphere/kern_k_device_address_space.hpp>
#include <mesosphere/kern_select_debug.hpp>
#include <mesosphere/kern_k_process.hpp>
#include <mesosphere/kern_k_resource_limit.hpp>
#include <mesosphere/kern_k_synchronization.hpp>
/* More Miscellaneous objects. */
#include <mesosphere/kern_k_object_name.hpp>
#include <mesosphere/kern_k_scoped_resource_reservation.hpp>
/* Supervisor Calls. */
#include <mesosphere/kern_svc.hpp>
/* Main functionality. */
#include <mesosphere/kern_main.hpp>
#include "mesosphere/kern_select_interrupts.hpp"
#include "mesosphere/kern_select_k_system_control.hpp"

View File

@@ -1,34 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
namespace ams::kern::init {
struct KInitArguments {
u64 ttbr0;
u64 ttbr1;
u64 tcr;
u64 mair;
u64 cpuactlr;
u64 cpuectlr;
u64 sctlr;
u64 sp;
u64 entrypoint;
u64 argument;
u64 setup_function;
};
}

View File

@@ -15,13 +15,173 @@
*/
#pragma once
#include <vapours.hpp>
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_panic.hpp>
#include <mesosphere/kern_k_typed_address.hpp>
#include <mesosphere/kern_select_cpu.hpp>
#include <mesosphere/arch/arm64/kern_k_page_table_entry.hpp>
#include <mesosphere/kern_select_system_control.hpp>
#include "../kern_cpu.hpp"
namespace ams::kern::init {
constexpr size_t PageSize = 0x1000;
constexpr size_t L1BlockSize = 0x40000000;
constexpr size_t L2BlockSize = 0x200000;
constexpr size_t L2ContiguousBlockSize = 0x10 * L2BlockSize;
constexpr size_t L3BlockSize = 0x1000;
constexpr size_t L3ContiguousBlockSize = 0x10 * L3BlockSize;
class PageTableEntry {
public:
enum Permission : u64 {
Permission_KernelRWX = ((0ul << 53) | (1ul << 54) | (0ul << 6)),
Permission_KernelRX = ((0ul << 53) | (1ul << 54) | (2ul << 6)),
Permission_KernelR = ((1ul << 53) | (1ul << 54) | (2ul << 6)),
Permission_KernelRW = ((1ul << 53) | (1ul << 54) | (0ul << 6)),
Permission_UserRX = ((1ul << 53) | (0ul << 54) | (3ul << 6)),
Permission_UserR = ((1ul << 53) | (1ul << 54) | (3ul << 6)),
Permission_UserRW = ((1ul << 53) | (1ul << 54) | (1ul << 6)),
};
enum Shareable : u64 {
Shareable_NonShareable = (0 << 8),
Shareable_OuterShareable = (2 << 8),
Shareable_InnerShareable = (3 << 8),
};
/* Official attributes are: */
/* 0x00, 0x04, 0xFF, 0x44. 4-7 are unused. */
enum PageAttribute : u64 {
PageAttribute_Device_nGnRnE = (0 << 2),
PageAttribute_Device_nGnRE = (1 << 2),
PageAttribute_NormalMemory = (2 << 2),
PageAttribute_NormalMemoryNotCacheable = (3 << 2),
};
enum AccessFlag : u64 {
AccessFlag_NotAccessed = (0 << 10),
AccessFlag_Accessed = (1 << 10),
};
protected:
u64 attributes;
public:
/* Take in a raw attribute. */
constexpr ALWAYS_INLINE PageTableEntry(u64 attr) : attributes(attr) { /* ... */ }
/* Extend a previous attribute. */
constexpr ALWAYS_INLINE PageTableEntry(const PageTableEntry &rhs, u64 new_attr) : attributes(rhs.attributes | new_attr) { /* ... */ }
/* Construct a new attribute. */
constexpr ALWAYS_INLINE PageTableEntry(Permission perm, PageAttribute p_a, Shareable share)
: attributes(static_cast<u64>(perm) | static_cast<u64>(AccessFlag_Accessed) | static_cast<u64>(p_a) | static_cast<u64>(share))
{
/* ... */
}
protected:
constexpr ALWAYS_INLINE u64 GetBits(size_t offset, size_t count) const {
return (this->attributes >> offset) & ((1ul << count) - 1);
}
constexpr ALWAYS_INLINE u64 SelectBits(size_t offset, size_t count) const {
return this->attributes & (((1ul << count) - 1) << offset);
}
public:
constexpr ALWAYS_INLINE bool IsUserExecuteNever() const { return this->GetBits(54, 1) != 0; }
constexpr ALWAYS_INLINE bool IsPrivilegedExecuteNever() const { return this->GetBits(53, 1) != 0; }
constexpr ALWAYS_INLINE bool IsContiguous() const { return this->GetBits(52, 1) != 0; }
constexpr ALWAYS_INLINE AccessFlag GetAccessFlag() const { return static_cast<AccessFlag>(this->GetBits(10, 1)); }
constexpr ALWAYS_INLINE Shareable GetShareable() const { return static_cast<Shareable>(this->GetBits(8, 2)); }
constexpr ALWAYS_INLINE PageAttribute GetPageAttribute() const { return static_cast<PageAttribute>(this->GetBits(2, 3)); }
constexpr ALWAYS_INLINE bool IsNonSecure() const { return this->GetBits(5, 1) != 0; }
constexpr ALWAYS_INLINE bool IsBlock() const { return this->GetBits(0, 2) == 0x1; }
constexpr ALWAYS_INLINE bool IsTable() const { return this->GetBits(0, 2) == 0x3; }
/* Should not be called except by derived classes. */
constexpr ALWAYS_INLINE u64 GetRawAttributes() const {
return this->attributes;
}
};
static_assert(sizeof(PageTableEntry) == sizeof(u64));
constexpr PageTableEntry InvalidPageTableEntry = PageTableEntry(0);
constexpr size_t MaxPageTableEntries = PageSize / sizeof(PageTableEntry);
class L1PageTableEntry : public PageTableEntry {
public:
constexpr ALWAYS_INLINE L1PageTableEntry(KPhysicalAddress phys_addr, bool pxn)
: PageTableEntry((0x3ul << 60) | (static_cast<u64>(pxn) << 59) | GetInteger(phys_addr) | 0x3)
{
/* ... */
}
constexpr ALWAYS_INLINE L1PageTableEntry(KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig)
: PageTableEntry(attr, (static_cast<u64>(contig) << 52) | GetInteger(phys_addr) | 0x1)
{
/* ... */
}
constexpr ALWAYS_INLINE KPhysicalAddress GetBlock() const {
return this->SelectBits(30, 18);
}
constexpr ALWAYS_INLINE KPhysicalAddress GetTable() const {
return this->SelectBits(12, 36);
}
constexpr ALWAYS_INLINE bool IsCompatibleWithAttribute(const PageTableEntry &rhs, bool contig) const {
/* Check whether this has the same permission/etc as the desired attributes. */
return L1PageTableEntry(this->GetBlock(), rhs, contig).GetRawAttributes() == this->GetRawAttributes();
}
};
class L2PageTableEntry : public PageTableEntry {
public:
constexpr ALWAYS_INLINE L2PageTableEntry(KPhysicalAddress phys_addr, bool pxn)
: PageTableEntry((0x3ul << 60) | (static_cast<u64>(pxn) << 59) | GetInteger(phys_addr) | 0x3)
{
/* ... */
}
constexpr ALWAYS_INLINE L2PageTableEntry(KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig)
: PageTableEntry(attr, (static_cast<u64>(contig) << 52) | GetInteger(phys_addr) | 0x1)
{
/* ... */
}
constexpr ALWAYS_INLINE KPhysicalAddress GetBlock() const {
return this->SelectBits(21, 27);
}
constexpr ALWAYS_INLINE KPhysicalAddress GetTable() const {
return this->SelectBits(12, 36);
}
constexpr ALWAYS_INLINE bool IsCompatibleWithAttribute(const PageTableEntry &rhs, bool contig) const {
/* Check whether this has the same permission/etc as the desired attributes. */
return L2PageTableEntry(this->GetBlock(), rhs, contig).GetRawAttributes() == this->GetRawAttributes();
}
};
class L3PageTableEntry : public PageTableEntry {
public:
constexpr ALWAYS_INLINE L3PageTableEntry(KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig)
: PageTableEntry(attr, (static_cast<u64>(contig) << 52) | GetInteger(phys_addr) | 0x3)
{
/* ... */
}
constexpr ALWAYS_INLINE bool IsBlock() const { return this->GetBits(0, 2) == 0x3; }
constexpr ALWAYS_INLINE KPhysicalAddress GetBlock() const {
return this->SelectBits(12, 36);
}
constexpr ALWAYS_INLINE bool IsCompatibleWithAttribute(const PageTableEntry &rhs, bool contig) const {
/* Check whether this has the same permission/etc as the desired attributes. */
return L3PageTableEntry(this->GetBlock(), rhs, contig).GetRawAttributes() == this->GetRawAttributes();
}
};
namespace ams::kern::arch::arm64::init {
class KInitialPageTable {
public:
@@ -30,14 +190,10 @@ namespace ams::kern::arch::arm64::init {
virtual KPhysicalAddress Allocate() { return Null<KPhysicalAddress>; }
virtual void Free(KPhysicalAddress phys_addr) { /* Nothing to do here. */ (void)(phys_addr); }
};
struct NoClear{};
private:
KPhysicalAddress l1_table;
public:
constexpr ALWAYS_INLINE KInitialPageTable(KPhysicalAddress l1, NoClear) : l1_table(l1) { /* ... */ }
constexpr ALWAYS_INLINE KInitialPageTable(KPhysicalAddress l1) : KInitialPageTable(l1, NoClear{}) {
constexpr ALWAYS_INLINE KInitialPageTable(KPhysicalAddress l1) : l1_table(l1) {
ClearNewPageTable(this->l1_table);
}
@@ -65,226 +221,12 @@ namespace ams::kern::arch::arm64::init {
/* The MMU is necessarily not yet turned on, if we are creating an initial page table. */
std::memset(reinterpret_cast<void *>(GetInteger(address)), 0, PageSize);
}
private:
size_t NOINLINE GetBlockCount(KVirtualAddress virt_addr, size_t size, size_t block_size) {
const KVirtualAddress end_virt_addr = virt_addr + size;
size_t count = 0;
while (virt_addr < end_virt_addr) {
L1PageTableEntry *l1_entry = GetL1Entry(this->l1_table, virt_addr);
/* If an L1 block is mapped or we're empty, advance by L1BlockSize. */
if (l1_entry->IsBlock() || l1_entry->IsEmpty()) {
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L1BlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(static_cast<size_t>(end_virt_addr - virt_addr) >= L1BlockSize);
virt_addr += L1BlockSize;
if (l1_entry->IsBlock() && block_size == L1BlockSize) {
count++;
}
continue;
}
/* Non empty and non-block must be table. */
MESOSPHERE_INIT_ABORT_UNLESS(l1_entry->IsTable());
/* Table, so check if we're mapped in L2. */
L2PageTableEntry *l2_entry = GetL2Entry(l1_entry, virt_addr);
if (l2_entry->IsBlock() || l2_entry->IsEmpty()) {
const size_t advance_size = (l2_entry->IsBlock() && l2_entry->IsContiguous()) ? L2ContiguousBlockSize : L2BlockSize;
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), advance_size));
MESOSPHERE_INIT_ABORT_UNLESS(static_cast<size_t>(end_virt_addr - virt_addr) >= advance_size);
virt_addr += advance_size;
if (l2_entry->IsBlock() && block_size == advance_size) {
count++;
}
continue;
}
/* Non empty and non-block must be table. */
MESOSPHERE_INIT_ABORT_UNLESS(l2_entry->IsTable());
/* Table, so check if we're mapped in L3. */
L3PageTableEntry *l3_entry = GetL3Entry(l2_entry, virt_addr);
/* L3 must be block or empty. */
MESOSPHERE_INIT_ABORT_UNLESS(l3_entry->IsBlock() || l3_entry->IsEmpty());
const size_t advance_size = (l3_entry->IsBlock() && l3_entry->IsContiguous()) ? L3ContiguousBlockSize : L3BlockSize;
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), advance_size));
MESOSPHERE_INIT_ABORT_UNLESS(static_cast<size_t>(end_virt_addr - virt_addr) >= advance_size);
virt_addr += advance_size;
if (l3_entry->IsBlock() && block_size == advance_size) {
count++;
}
}
return count;
}
KVirtualAddress NOINLINE GetBlockByIndex(KVirtualAddress virt_addr, size_t size, size_t block_size, size_t index) {
const KVirtualAddress end_virt_addr = virt_addr + size;
size_t count = 0;
while (virt_addr < end_virt_addr) {
L1PageTableEntry *l1_entry = GetL1Entry(this->l1_table, virt_addr);
/* If an L1 block is mapped or we're empty, advance by L1BlockSize. */
if (l1_entry->IsBlock() || l1_entry->IsEmpty()) {
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L1BlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(static_cast<size_t>(end_virt_addr - virt_addr) >= L1BlockSize);
if (l1_entry->IsBlock() && block_size == L1BlockSize) {
if ((count++) == index) {
return virt_addr;
}
}
virt_addr += L1BlockSize;
continue;
}
/* Non empty and non-block must be table. */
MESOSPHERE_INIT_ABORT_UNLESS(l1_entry->IsTable());
/* Table, so check if we're mapped in L2. */
L2PageTableEntry *l2_entry = GetL2Entry(l1_entry, virt_addr);
if (l2_entry->IsBlock() || l2_entry->IsEmpty()) {
const size_t advance_size = (l2_entry->IsBlock() && l2_entry->IsContiguous()) ? L2ContiguousBlockSize : L2BlockSize;
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), advance_size));
MESOSPHERE_INIT_ABORT_UNLESS(static_cast<size_t>(end_virt_addr - virt_addr) >= advance_size);
if (l2_entry->IsBlock() && block_size == advance_size) {
if ((count++) == index) {
return virt_addr;
}
}
virt_addr += advance_size;
continue;
}
/* Non empty and non-block must be table. */
MESOSPHERE_INIT_ABORT_UNLESS(l2_entry->IsTable());
/* Table, so check if we're mapped in L3. */
L3PageTableEntry *l3_entry = GetL3Entry(l2_entry, virt_addr);
/* L3 must be block or empty. */
MESOSPHERE_INIT_ABORT_UNLESS(l3_entry->IsBlock() || l3_entry->IsEmpty());
const size_t advance_size = (l3_entry->IsBlock() && l3_entry->IsContiguous()) ? L3ContiguousBlockSize : L3BlockSize;
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), advance_size));
MESOSPHERE_INIT_ABORT_UNLESS(static_cast<size_t>(end_virt_addr - virt_addr) >= advance_size);
if (l3_entry->IsBlock() && block_size == advance_size) {
if ((count++) == index) {
return virt_addr;
}
}
virt_addr += advance_size;
}
return Null<KVirtualAddress>;
}
PageTableEntry *GetMappingEntry(KVirtualAddress virt_addr, size_t block_size) {
L1PageTableEntry *l1_entry = GetL1Entry(this->l1_table, virt_addr);
if (l1_entry->IsBlock()) {
MESOSPHERE_INIT_ABORT_UNLESS(block_size == L1BlockSize);
return l1_entry;
}
MESOSPHERE_INIT_ABORT_UNLESS(l1_entry->IsTable());
/* Table, so check if we're mapped in L2. */
L2PageTableEntry *l2_entry = GetL2Entry(l1_entry, virt_addr);
if (l2_entry->IsBlock()) {
const size_t real_size = (l2_entry->IsContiguous()) ? L2ContiguousBlockSize : L2BlockSize;
MESOSPHERE_INIT_ABORT_UNLESS(real_size == block_size);
return l2_entry;
}
MESOSPHERE_INIT_ABORT_UNLESS(l2_entry->IsTable());
/* Table, so check if we're mapped in L3. */
L3PageTableEntry *l3_entry = GetL3Entry(l2_entry, virt_addr);
/* L3 must be block. */
MESOSPHERE_INIT_ABORT_UNLESS(l3_entry->IsBlock());
const size_t real_size = (l3_entry->IsContiguous()) ? L3ContiguousBlockSize : L3BlockSize;
MESOSPHERE_INIT_ABORT_UNLESS(real_size == block_size);
return l3_entry;
}
void NOINLINE SwapBlocks(KVirtualAddress src_virt_addr, KVirtualAddress dst_virt_addr, size_t block_size, bool do_copy) {
static_assert(L2ContiguousBlockSize / L2BlockSize == L3ContiguousBlockSize / L3BlockSize);
const bool contig = (block_size == L2ContiguousBlockSize || block_size == L3ContiguousBlockSize);
const size_t num_mappings = contig ? L2ContiguousBlockSize / L2BlockSize : 1;
/* Unmap the source. */
PageTableEntry *src_entry = this->GetMappingEntry(src_virt_addr, block_size);
const auto src_saved = *src_entry;
for (size_t i = 0; i < num_mappings; i++) {
*src_entry = InvalidPageTableEntry;
}
/* Unmap the target. */
PageTableEntry *dst_entry = this->GetMappingEntry(dst_virt_addr, block_size);
const auto dst_saved = *dst_entry;
for (size_t i = 0; i < num_mappings; i++) {
*dst_entry = InvalidPageTableEntry;
}
/* Invalidate the entire tlb. */
cpu::DataSynchronizationBarrierInnerShareable();
cpu::InvalidateEntireTlb();
/* Copy data, if we should. */
const u64 negative_block_size_for_mask = static_cast<u64>(-static_cast<s64>(block_size));
const u64 offset_mask = negative_block_size_for_mask & ((1ul << 36) - 1);
const KVirtualAddress copy_src_addr = KVirtualAddress(src_saved.GetRawAttributesUnsafeForSwap() & offset_mask);
const KVirtualAddress copy_dst_addr = KVirtualAddress(dst_saved.GetRawAttributesUnsafeForSwap() & offset_mask);
if (block_size && do_copy) {
u8 tmp[0x100];
for (size_t ofs = 0; ofs < block_size; ofs += sizeof(tmp)) {
std::memcpy(tmp, GetVoidPointer(copy_src_addr + ofs), sizeof(tmp));
std::memcpy(GetVoidPointer(copy_src_addr + ofs), GetVoidPointer(copy_dst_addr + ofs), sizeof(tmp));
std::memcpy(GetVoidPointer(copy_dst_addr + ofs), tmp, sizeof(tmp));
}
}
/* Swap the mappings. */
const u64 attr_preserve_mask = (negative_block_size_for_mask | 0xFFFF000000000000ul) ^ ((1ul << 36) - 1);
const size_t shift_for_contig = contig ? 4 : 0;
size_t advanced_size = 0;
const u64 src_attr_val = src_saved.GetRawAttributesUnsafeForSwap() & attr_preserve_mask;
const u64 dst_attr_val = dst_saved.GetRawAttributesUnsafeForSwap() & attr_preserve_mask;
for (size_t i = 0; i < num_mappings; i++) {
reinterpret_cast<u64 *>(src_entry)[i] = GetInteger(copy_dst_addr + (advanced_size >> shift_for_contig)) | src_attr_val;
reinterpret_cast<u64 *>(dst_entry)[i] = GetInteger(copy_src_addr + (advanced_size >> shift_for_contig)) | dst_attr_val;
advanced_size += block_size;
}
cpu::DataSynchronizationBarrierInnerShareable();
}
void NOINLINE PhysicallyRandomize(KVirtualAddress virt_addr, size_t size, size_t block_size, bool do_copy) {
const size_t block_count = this->GetBlockCount(virt_addr, size, block_size);
if (block_count > 1) {
for (size_t cur_block = 0; cur_block < block_count; cur_block++) {
const size_t target_block = KSystemControl::Init::GenerateRandomRange(cur_block, block_count - 1);
if (cur_block != target_block) {
const KVirtualAddress cur_virt_addr = this->GetBlockByIndex(virt_addr, size, block_size, cur_block);
const KVirtualAddress target_virt_addr = this->GetBlockByIndex(virt_addr, size, block_size, target_block);
MESOSPHERE_INIT_ABORT_UNLESS(cur_virt_addr != Null<KVirtualAddress>);
MESOSPHERE_INIT_ABORT_UNLESS(target_virt_addr != Null<KVirtualAddress>);
this->SwapBlocks(cur_virt_addr, target_virt_addr, block_size, do_copy);
}
}
}
}
public:
void NOINLINE Map(KVirtualAddress virt_addr, size_t size, KPhysicalAddress phys_addr, const PageTableEntry &attr, IPageAllocator &allocator) {
/* Ensure that addresses and sizes are page aligned. */
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), PageSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(phys_addr), PageSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(size, PageSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), PageSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(phys_addr), PageSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, PageSize));
/* Iteratively map pages until the requested region is mapped. */
while (size > 0) {
@@ -367,37 +309,10 @@ namespace ams::kern::arch::arm64::init {
}
}
KPhysicalAddress GetPhysicalAddress(KVirtualAddress virt_addr) const {
/* Get the L1 entry. */
const L1PageTableEntry *l1_entry = GetL1Entry(this->l1_table, virt_addr);
if (l1_entry->IsBlock()) {
return l1_entry->GetBlock() + (GetInteger(virt_addr) & (L1BlockSize - 1));
}
MESOSPHERE_INIT_ABORT_UNLESS(l1_entry->IsTable());
/* Get the L2 entry. */
const L2PageTableEntry *l2_entry = GetL2Entry(l1_entry, virt_addr);
if (l2_entry->IsBlock()) {
return l2_entry->GetBlock() + (GetInteger(virt_addr) & (L2BlockSize - 1));
}
MESOSPHERE_INIT_ABORT_UNLESS(l2_entry->IsTable());
/* Get the L3 entry. */
const L3PageTableEntry *l3_entry = GetL3Entry(l2_entry, virt_addr);
MESOSPHERE_INIT_ABORT_UNLESS(l3_entry->IsBlock());
return l3_entry->GetBlock() + (GetInteger(virt_addr) & (L3BlockSize - 1));
}
bool IsFree(KVirtualAddress virt_addr, size_t size) {
/* Ensure that addresses and sizes are page aligned. */
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), PageSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(size, PageSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), PageSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, PageSize));
const KVirtualAddress end_virt_addr = virt_addr + size;
while (virt_addr < end_virt_addr) {
@@ -445,8 +360,8 @@ namespace ams::kern::arch::arm64::init {
cpu::DataSynchronizationBarrierInnerShareable();
/* Ensure that addresses and sizes are page aligned. */
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), PageSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(size, PageSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), PageSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, PageSize));
/* Iteratively reprotect pages until the requested region is reprotected. */
while (size > 0) {
@@ -456,9 +371,9 @@ namespace ams::kern::arch::arm64::init {
if (l1_entry->IsBlock()) {
/* Ensure that we are allowed to have an L1 block here. */
const KPhysicalAddress block = l1_entry->GetBlock();
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L1BlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(size, L1BlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(l1_entry->IsCompatibleWithAttribute(attr_before, false));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L1BlockSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, L1BlockSize));
MESOSPHERE_ABORT_UNLESS(l1_entry->IsCompatibleWithAttribute(attr_before, false));
/* Invalidate the existing L1 block. */
*static_cast<PageTableEntry *>(l1_entry) = InvalidPageTableEntry;
@@ -474,7 +389,7 @@ namespace ams::kern::arch::arm64::init {
}
/* Not a block, so we must be a table. */
MESOSPHERE_INIT_ABORT_UNLESS(l1_entry->IsTable());
MESOSPHERE_ABORT_UNLESS(l1_entry->IsTable());
L2PageTableEntry *l2_entry = GetL2Entry(l1_entry, virt_addr);
if (l2_entry->IsBlock()) {
@@ -482,14 +397,14 @@ namespace ams::kern::arch::arm64::init {
if (l2_entry->IsContiguous()) {
/* Ensure that we are allowed to have a contiguous L2 block here. */
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L2ContiguousBlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(block), L2ContiguousBlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(size, L2ContiguousBlockSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L2ContiguousBlockSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(block), L2ContiguousBlockSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, L2ContiguousBlockSize));
/* Invalidate the existing contiguous L2 block. */
for (size_t i = 0; i < L2ContiguousBlockSize / L2BlockSize; i++) {
/* Ensure that the entry is valid. */
MESOSPHERE_INIT_ABORT_UNLESS(l2_entry[i].IsCompatibleWithAttribute(attr_before, true));
MESOSPHERE_ABORT_UNLESS(l2_entry[i].IsCompatibleWithAttribute(attr_before, true));
static_cast<PageTableEntry *>(l2_entry)[i] = InvalidPageTableEntry;
}
cpu::DataSynchronizationBarrierInnerShareable();
@@ -504,10 +419,10 @@ namespace ams::kern::arch::arm64::init {
size -= L2ContiguousBlockSize;
} else {
/* Ensure that we are allowed to have an L2 block here. */
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L2BlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(block), L2BlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(size, L2BlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(l2_entry->IsCompatibleWithAttribute(attr_before, false));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L2BlockSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(block), L2BlockSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, L2BlockSize));
MESOSPHERE_ABORT_UNLESS(l2_entry->IsCompatibleWithAttribute(attr_before, false));
/* Invalidate the existing L2 block. */
*static_cast<PageTableEntry *>(l2_entry) = InvalidPageTableEntry;
@@ -525,23 +440,23 @@ namespace ams::kern::arch::arm64::init {
}
/* Not a block, so we must be a table. */
MESOSPHERE_INIT_ABORT_UNLESS(l2_entry->IsTable());
MESOSPHERE_ABORT_UNLESS(l2_entry->IsTable());
/* We must have a mapped l3 entry to reprotect. */
L3PageTableEntry *l3_entry = GetL3Entry(l2_entry, virt_addr);
MESOSPHERE_INIT_ABORT_UNLESS(l3_entry->IsBlock());
MESOSPHERE_ABORT_UNLESS(l3_entry->IsBlock());
const KPhysicalAddress block = l3_entry->GetBlock();
if (l3_entry->IsContiguous()) {
/* Ensure that we are allowed to have a contiguous L3 block here. */
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L3ContiguousBlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(block), L3ContiguousBlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(size, L3ContiguousBlockSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L3ContiguousBlockSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(block), L3ContiguousBlockSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, L3ContiguousBlockSize));
/* Invalidate the existing contiguous L3 block. */
for (size_t i = 0; i < L3ContiguousBlockSize / L3BlockSize; i++) {
/* Ensure that the entry is valid. */
MESOSPHERE_INIT_ABORT_UNLESS(l3_entry[i].IsCompatibleWithAttribute(attr_before, true));
MESOSPHERE_ABORT_UNLESS(l3_entry[i].IsCompatibleWithAttribute(attr_before, true));
static_cast<PageTableEntry *>(l3_entry)[i] = InvalidPageTableEntry;
}
cpu::DataSynchronizationBarrierInnerShareable();
@@ -556,10 +471,10 @@ namespace ams::kern::arch::arm64::init {
size -= L3ContiguousBlockSize;
} else {
/* Ensure that we are allowed to have an L3 block here. */
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L3BlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(GetInteger(block), L3BlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(size, L3BlockSize));
MESOSPHERE_INIT_ABORT_UNLESS(l3_entry->IsCompatibleWithAttribute(attr_before, false));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), L3BlockSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(block), L3BlockSize));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, L3BlockSize));
MESOSPHERE_ABORT_UNLESS(l3_entry->IsCompatibleWithAttribute(attr_before, false));
/* Invalidate the existing L3 block. */
*static_cast<PageTableEntry *>(l3_entry) = InvalidPageTableEntry;
@@ -578,67 +493,6 @@ namespace ams::kern::arch::arm64::init {
cpu::DataSynchronizationBarrierInnerShareable();
}
void PhysicallyRandomize(KVirtualAddress virt_addr, size_t size, bool do_copy) {
this->PhysicallyRandomize(virt_addr, size, L1BlockSize, do_copy);
this->PhysicallyRandomize(virt_addr, size, L2ContiguousBlockSize, do_copy);
this->PhysicallyRandomize(virt_addr, size, L2BlockSize, do_copy);
this->PhysicallyRandomize(virt_addr, size, L3ContiguousBlockSize, do_copy);
this->PhysicallyRandomize(virt_addr, size, L3BlockSize, do_copy);
}
};
class KInitialPageAllocator : public KInitialPageTable::IPageAllocator {
public:
struct State {
uintptr_t next_address;
uintptr_t free_bitmap;
};
private:
State state;
public:
constexpr ALWAYS_INLINE KInitialPageAllocator() : state{} { /* ... */ }
ALWAYS_INLINE void Initialize(uintptr_t address) {
this->state.next_address = address + BITSIZEOF(this->state.free_bitmap) * PageSize;
this->state.free_bitmap = ~uintptr_t();
}
ALWAYS_INLINE void InitializeFromState(uintptr_t state_val) {
if (kern::GetTargetFirmware() >= kern::TargetFirmware_10_0_0) {
this->state = *reinterpret_cast<State *>(state_val);
} else {
this->state.next_address = state_val;
this->state.free_bitmap = 0;
}
}
ALWAYS_INLINE void GetFinalState(State *out) {
*out = this->state;
this->state = {};
}
public:
virtual KPhysicalAddress Allocate() override {
MESOSPHERE_INIT_ABORT_UNLESS(this->state.next_address != Null<uintptr_t>);
uintptr_t allocated = this->state.next_address;
if (this->state.free_bitmap != 0) {
u64 index;
uintptr_t mask;
do {
index = KSystemControl::Init::GenerateRandomRange(0, BITSIZEOF(this->state.free_bitmap) - 1);
mask = (static_cast<uintptr_t>(1) << index);
} while ((this->state.free_bitmap & mask) == 0);
this->state.free_bitmap &= ~mask;
allocated = this->state.next_address - ((BITSIZEOF(this->state.free_bitmap) - index) * PageSize);
} else {
this->state.next_address += PageSize;
}
std::memset(reinterpret_cast<void *>(allocated), 0, PageSize);
return allocated;
}
/* No need to override free. The default does nothing, and so would we. */
};
}

View File

@@ -15,27 +15,9 @@
*/
#pragma once
#include <vapours.hpp>
#include <mesosphere/arch/arm64/kern_cpu_system_registers.hpp>
#include <mesosphere/kern_select_userspace_memory_access.hpp>
#include "kern_cpu_system_registers.hpp"
namespace ams::kern::arch::arm64::cpu {
#if defined(ATMOSPHERE_CPU_ARM_CORTEX_A57) || defined(ATMOSPHERE_CPU_ARM_CORTEX_A53)
constexpr inline size_t InstructionCacheLineSize = 0x40;
constexpr inline size_t DataCacheLineSize = 0x40;
constexpr inline size_t NumPerformanceCounters = 6;
#else
#error "Unknown CPU for cache line sizes"
#endif
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
constexpr inline size_t NumCores = 4;
#else
#error "Unknown Board for cpu::NumCores"
#endif
/* Initialization. */
NOINLINE void InitializeInterruptThreads(s32 core_id);
namespace ams::kern::arm64::cpu {
/* Helpers for managing memory state. */
ALWAYS_INLINE void DataSynchronizationBarrier() {
@@ -64,154 +46,13 @@ namespace ams::kern::arch::arm64::cpu {
EnsureInstructionConsistency();
}
ALWAYS_INLINE void SwitchProcess(u64 ttbr, u32 proc_id) {
SetTtbr0El1(ttbr);
ContextIdRegisterAccessor(0).SetProcId(proc_id).Store();
InstructionMemoryBarrier();
}
/* Performance counter helpers. */
ALWAYS_INLINE u64 GetCycleCounter() {
return cpu::GetPmcCntrEl0();
}
ALWAYS_INLINE u32 GetPerformanceCounter(s32 n) {
u64 counter = 0;
if (n < static_cast<s32>(NumPerformanceCounters)) {
switch (n) {
case 0:
counter = cpu::GetPmevCntr0El0();
break;
case 1:
counter = cpu::GetPmevCntr1El0();
break;
case 2:
counter = cpu::GetPmevCntr2El0();
break;
case 3:
counter = cpu::GetPmevCntr3El0();
break;
case 4:
counter = cpu::GetPmevCntr4El0();
break;
case 5:
counter = cpu::GetPmevCntr5El0();
break;
default:
break;
}
}
return static_cast<u32>(counter);
}
/* Helper for address access. */
ALWAYS_INLINE bool GetPhysicalAddressWritable(KPhysicalAddress *out, KVirtualAddress addr, bool privileged = false) {
const uintptr_t va = GetInteger(addr);
if (privileged) {
__asm__ __volatile__("at s1e1w, %[va]" :: [va]"r"(va) : "memory");
} else {
__asm__ __volatile__("at s1e0w, %[va]" :: [va]"r"(va) : "memory");
}
InstructionMemoryBarrier();
u64 par = GetParEl1();
if (par & 0x1) {
return false;
}
if (out) {
*out = KPhysicalAddress((par & 0xFFFFFFFFF000ull) | (va & 0xFFFull));
}
return true;
}
ALWAYS_INLINE bool GetPhysicalAddressReadable(KPhysicalAddress *out, KVirtualAddress addr, bool privileged = false) {
const uintptr_t va = GetInteger(addr);
if (privileged) {
__asm__ __volatile__("at s1e1r, %[va]" :: [va]"r"(va) : "memory");
} else {
__asm__ __volatile__("at s1e0r, %[va]" :: [va]"r"(va) : "memory");
}
InstructionMemoryBarrier();
u64 par = GetParEl1();
if (par & 0x1) {
return false;
}
if (out) {
*out = KPhysicalAddress((par & 0xFFFFFFFFF000ull) | (va & 0xFFFull));
}
return true;
}
/* Synchronization helpers. */
NOINLINE void SynchronizeAllCores();
/* Cache management helpers. */
void ClearPageToZeroImpl(void *);
void FlushEntireDataCacheSharedForInit();
void FlushEntireDataCacheLocalForInit();
void FlushEntireDataCache();
Result InvalidateDataCache(void *addr, size_t size);
Result StoreDataCache(const void *addr, size_t size);
Result FlushDataCache(const void *addr, size_t size);
Result InvalidateInstructionCache(void *addr, size_t size);
ALWAYS_INLINE void ClearPageToZero(void *page) {
MESOSPHERE_ASSERT(util::IsAligned(reinterpret_cast<uintptr_t>(page), PageSize));
MESOSPHERE_ASSERT(page != nullptr);
ClearPageToZeroImpl(page);
}
ALWAYS_INLINE void InvalidateTlbByAsid(u32 asid) {
const u64 value = (static_cast<u64>(asid) << 48);
__asm__ __volatile__("tlbi aside1is, %[value]" :: [value]"r"(static_cast<u64>(value) << 48) : "memory");
EnsureInstructionConsistency();
}
ALWAYS_INLINE void InvalidateTlbByAsidAndVa(u32 asid, KProcessAddress virt_addr) {
const u64 value = (static_cast<u64>(asid) << 48) | ((GetInteger(virt_addr) >> 12) & 0xFFFFFFFFFFFul);
__asm__ __volatile__("tlbi aside1is, %[value]" :: [value]"r"(value) : "memory");
EnsureInstructionConsistency();
}
void FlushEntireDataCacheShared();
void FlushEntireDataCacheLocal();
ALWAYS_INLINE void InvalidateEntireTlb() {
__asm__ __volatile__("tlbi vmalle1is" ::: "memory");
EnsureInstructionConsistency();
}
ALWAYS_INLINE void InvalidateEntireTlbDataOnly() {
__asm__ __volatile__("tlbi vmalle1is" ::: "memory");
DataSynchronizationBarrier();
}
ALWAYS_INLINE void InvalidateTlbByVaDataOnly(KProcessAddress virt_addr) {
const u64 value = ((GetInteger(virt_addr) >> 12) & 0xFFFFFFFFFFFul);
__asm__ __volatile__("tlbi vaae1is, %[value]" :: [value]"r"(value) : "memory");
DataSynchronizationBarrier();
}
ALWAYS_INLINE uintptr_t GetCoreLocalRegionAddress() {
register uintptr_t x18 asm("x18");
__asm__ __volatile__("" : [x18]"=r"(x18));
return x18;
}
ALWAYS_INLINE void SetCoreLocalRegionAddress(uintptr_t value) {
register uintptr_t x18 asm("x18") = value;
__asm__ __volatile__("":: [x18]"r"(x18));
SetTpidrEl1(value);
}
ALWAYS_INLINE void SwitchThreadLocalRegion(uintptr_t tlr) {
cpu::SetTpidrRoEl0(tlr);
}
}

View File

@@ -16,7 +16,7 @@
#pragma once
#include <vapours.hpp>
namespace ams::kern::arch::arm64::cpu {
namespace ams::kern::arm64::cpu {
#define MESOSPHERE_CPU_GET_SYSREG(name) \
({ \
@@ -37,14 +37,8 @@ namespace ams::kern::arch::arm64::cpu {
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(Ttbr0El1, ttbr0_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(Ttbr1El1, ttbr1_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(TcrEl1, tcr_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(MairEl1, mair_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(TpidrEl1, tpidr_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(VbarEl1, vbar_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(FarEl1, far_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(ParEl1, par_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(TcrEl1, tcr_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(SctlrEl1, sctlr_el1)
@@ -52,215 +46,21 @@ namespace ams::kern::arch::arm64::cpu {
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(CpuEctlrEl1, s3_1_c15_c2_1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(CsselrEl1, csselr_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(CcsidrEl1, ccsidr_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(OslarEl1, oslar_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(TpidrRoEl0, tpidrro_el0)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(EsrEl1, esr_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(Afsr0El1, afsr0_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(Afsr1El1, afsr1_el1)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmUserEnrEl0, pmuserenr_el0)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmcCntrEl0, pmccntr_el0)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmevCntr0El0, pmevcntr0_el0)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmevCntr1El0, pmevcntr1_el0)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmevCntr2El0, pmevcntr2_el0)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmevCntr3El0, pmevcntr3_el0)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmevCntr4El0, pmevcntr4_el0)
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmevCntr5El0, pmevcntr5_el0)
#define FOR_I_IN_0_TO_15(HANDLER, ...) \
HANDLER(0, ## __VA_ARGS__) HANDLER(1, ## __VA_ARGS__) HANDLER(2, ## __VA_ARGS__) HANDLER(3, ## __VA_ARGS__) \
HANDLER(4, ## __VA_ARGS__) HANDLER(5, ## __VA_ARGS__) HANDLER(6, ## __VA_ARGS__) HANDLER(7, ## __VA_ARGS__) \
HANDLER(8, ## __VA_ARGS__) HANDLER(9, ## __VA_ARGS__) HANDLER(10, ## __VA_ARGS__) HANDLER(11, ## __VA_ARGS__) \
HANDLER(12, ## __VA_ARGS__) HANDLER(13, ## __VA_ARGS__) HANDLER(14, ## __VA_ARGS__) HANDLER(15, ## __VA_ARGS__) \
#define MESOSPHERE_CPU_DEFINE_DBG_SYSREG_ACCESSORS(ID, ...) \
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(DbgWcr##ID##El1, dbgwcr##ID##_el1) \
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(DbgWvr##ID##El1, dbgwvr##ID##_el1) \
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(DbgBcr##ID##El1, dbgbcr##ID##_el1) \
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(DbgBvr##ID##El1, dbgbvr##ID##_el1)
FOR_I_IN_0_TO_15(MESOSPHERE_CPU_DEFINE_DBG_SYSREG_ACCESSORS)
#undef MESOSPHERE_CPU_DEFINE_DBG_SYSREG_ACCESSORS
/* Base class for register accessors. */
class GenericRegisterAccessorBase {
NON_COPYABLE(GenericRegisterAccessorBase);
NON_MOVEABLE(GenericRegisterAccessorBase);
class GenericRegisterAccessor {
private:
u64 value;
public:
constexpr ALWAYS_INLINE GenericRegisterAccessorBase(u64 v) : value(v) { /* ... */ }
ALWAYS_INLINE GenericRegisterAccessor(u64 v) : value(v) { /* ... */ }
protected:
constexpr ALWAYS_INLINE u64 GetValue() const {
return this->value;
}
constexpr ALWAYS_INLINE u64 GetBits(size_t offset, size_t count) const {
return (this->value >> offset) & ((1ul << count) - 1);
}
constexpr ALWAYS_INLINE void SetBits(size_t offset, size_t count, u64 value) {
const u64 mask = ((1ul << count) - 1) << offset;
this->value &= ~mask;
this->value |= (value & (mask >> offset)) << offset;
}
constexpr ALWAYS_INLINE void SetBitsDirect(size_t offset, size_t count, u64 value) {
const u64 mask = ((1ul << count) - 1) << offset;
this->value &= ~mask;
this->value |= (value & mask);
}
constexpr ALWAYS_INLINE void SetBit(size_t offset, bool enabled) {
const u64 mask = 1ul << offset;
if (enabled) {
this->value |= mask;
} else {
this->value &= ~mask;
}
}
};
template<typename Derived>
class GenericRegisterAccessor : public GenericRegisterAccessorBase {
public:
constexpr ALWAYS_INLINE GenericRegisterAccessor(u64 v) : GenericRegisterAccessorBase(v) { /* ... */ }
protected:
ALWAYS_INLINE void Store() const {
static_cast<const Derived *>(this)->Store();
}
};
#define MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(name) class name##RegisterAccessor : public GenericRegisterAccessor<name##RegisterAccessor>
#define MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(accessor, reg_name) \
ALWAYS_INLINE accessor##RegisterAccessor() : GenericRegisterAccessor(MESOSPHERE_CPU_GET_SYSREG(reg_name)) { /* ... */ } \
constexpr ALWAYS_INLINE accessor##RegisterAccessor(u64 v) : GenericRegisterAccessor(v) { /* ... */ } \
\
ALWAYS_INLINE void Store() { const u64 v = this->GetValue(); MESOSPHERE_CPU_SET_SYSREG(reg_name, v); }
/* Accessors. */
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(MemoryAccessIndirection) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(MemoryAccessIndirection, mair_el1)
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(TranslationControl) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(TranslationControl, tcr_el1)
constexpr ALWAYS_INLINE size_t GetT1Size() const {
const size_t shift_value = this->GetBits(16, 6);
return size_t(1) << (size_t(64) - shift_value);
}
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(ArchitecturalFeatureAccessControl) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(ArchitecturalFeatureAccessControl, cpacr_el1)
constexpr ALWAYS_INLINE decltype(auto) SetFpEnabled(bool en) {
if (en) {
this->SetBits(20, 2, 0x3);
} else {
this->SetBits(20, 2, 0x0);
}
return *this;
}
constexpr ALWAYS_INLINE bool IsFpEnabled() {
return this->GetBits(20, 2) != 0;
}
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(DebugFeature) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(DebugFeature, id_aa64dfr0_el1)
constexpr ALWAYS_INLINE size_t GetNumWatchpoints() const {
return this->GetBits(20, 4);
}
constexpr ALWAYS_INLINE size_t GetNumBreakpoints() const {
return this->GetBits(12, 4);
}
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(MonitorDebugSystemControl) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(MonitorDebugSystemControl, mdscr_el1)
constexpr ALWAYS_INLINE bool GetMde() const {
return this->GetBits(15, 1) != 0;
}
constexpr ALWAYS_INLINE size_t GetTdcc() const {
return this->GetBits(12, 1) != 0;
}
constexpr ALWAYS_INLINE decltype(auto) SetMde(bool set) {
this->SetBit(15, set);
return *this;
}
constexpr ALWAYS_INLINE decltype(auto) SetTdcc(bool set) {
this->SetBit(12, set);
return *this;
}
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(MultiprocessorAffinity) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(MultiprocessorAffinity, mpidr_el1)
constexpr ALWAYS_INLINE u64 GetAff0() const {
return this->GetBits(0, 8);
}
constexpr ALWAYS_INLINE u64 GetAff1() const {
return this->GetBits(8, 8);
}
constexpr ALWAYS_INLINE u64 GetAff2() const {
return this->GetBits(16, 8);
}
constexpr ALWAYS_INLINE u64 GetAff3() const {
return this->GetBits(32, 8);
}
constexpr ALWAYS_INLINE u64 GetCpuOnArgument() const {
constexpr u64 Mask = 0x000000FF00FFFF00ul;
return this->GetValue() & Mask;
}
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(ThreadId) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(ThreadId, tpidr_el1)
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(OsLockAccess) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(OsLockAccess, oslar_el1)
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(ContextId) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(ContextId, contextidr_el1)
constexpr ALWAYS_INLINE decltype(auto) SetProcId(u32 proc_id) {
this->SetBits(0, BITSIZEOF(proc_id), proc_id);
return *this;
}
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(MainId) {
/* Special code for main id register. */
class MainIdRegisterAccessor : public GenericRegisterAccessor {
public:
enum class Implementer {
ArmLimited = 0x41,
@@ -270,7 +70,7 @@ namespace ams::kern::arch::arm64::cpu {
CortexA57 = 0xD07,
};
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(MainId, midr_el1)
ALWAYS_INLINE MainIdRegisterAccessor() : GenericRegisterAccessor(MESOSPHERE_CPU_GET_SYSREG(midr_el1)) { /* ... */ }
public:
constexpr ALWAYS_INLINE Implementer GetImplementer() const {
return static_cast<Implementer>(this->GetBits(24, 8));
@@ -293,69 +93,10 @@ namespace ams::kern::arch::arm64::cpu {
}
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(SystemControl) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(SystemControl, sctlr_el1)
constexpr ALWAYS_INLINE decltype(auto) SetWxn(bool en) {
this->SetBit(19, en);
return *this;
}
};
/* Accessors for timer registers. */
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(CounterTimerKernelControl) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(CounterTimerKernelControl, cntkctl_el1)
constexpr ALWAYS_INLINE decltype(auto) SetEl0PctEn(bool en) {
this->SetBit(0, en);
return *this;
}
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(CounterTimerPhysicalTimerControl) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(CounterTimerPhysicalTimerControl, cntp_ctl_el0)
constexpr ALWAYS_INLINE decltype(auto) SetEnable(bool en) {
this->SetBit(0, en);
return *this;
}
constexpr ALWAYS_INLINE decltype(auto) SetIMask(bool en) {
this->SetBit(1, en);
return *this;
}
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(CounterTimerPhysicalTimerCompareValue) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(CounterTimerPhysicalTimerCompareValue, cntp_cval_el0)
constexpr ALWAYS_INLINE u64 GetCompareValue() {
return this->GetValue();
}
constexpr ALWAYS_INLINE decltype(auto) SetCompareValue(u64 value) {
this->SetBits(0, BITSIZEOF(value), value);
return *this;
}
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(CounterTimerPhysicalCountValue) {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(CounterTimerPhysicalCountValue, cntpct_el0)
constexpr ALWAYS_INLINE u64 GetCount() {
return this->GetValue();
}
};
/* Accessors for cache registers. */
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(CacheLineId) {
class CacheLineIdAccessor : public GenericRegisterAccessor {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(CacheLineId, clidr_el1)
ALWAYS_INLINE CacheLineIdAccessor() : GenericRegisterAccessor(MESOSPHERE_CPU_GET_SYSREG(clidr_el1)) { /* ... */ }
public:
constexpr ALWAYS_INLINE int GetLevelsOfCoherency() const {
return static_cast<int>(this->GetBits(24, 3));
@@ -368,9 +109,9 @@ namespace ams::kern::arch::arm64::cpu {
/* TODO: Other bitfield accessors? */
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(CacheSizeId) {
class CacheSizeIdAccessor : public GenericRegisterAccessor {
public:
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(CacheSizeId, ccsidr_el1)
ALWAYS_INLINE CacheSizeIdAccessor() : GenericRegisterAccessor(MESOSPHERE_CPU_GET_SYSREG(ccsidr_el1)) { /* ... */ }
public:
constexpr ALWAYS_INLINE int GetNumberOfSets() const {
return static_cast<int>(this->GetBits(13, 15));
@@ -387,9 +128,6 @@ namespace ams::kern::arch::arm64::cpu {
/* TODO: Other bitfield accessors? */
};
#undef FOR_I_IN_0_TO_15
#undef MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS
#undef MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS
#undef MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS
#undef MESOSPHERE_CPU_GET_SYSREG
#undef MESOSPHERE_CPU_SET_SYSREG

View File

@@ -1,31 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
namespace ams::kern::arch::arm64 {
struct KExceptionContext {
u64 x[(30 - 0) + 1];
u64 sp;
u64 pc;
u64 psr;
u64 tpidr;
u64 reserved;
};
static_assert(sizeof(KExceptionContext) == 0x120);
}

View File

@@ -1,94 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_select_cpu.hpp>
#include <mesosphere/kern_k_hardware_timer_base.hpp>
namespace ams::kern::arch::arm64 {
namespace impl {
class KHardwareTimerInterruptTask;
}
class KHardwareTimer : public KHardwareTimerBase {
public:
constexpr KHardwareTimer() : KHardwareTimerBase() { /* ... */ }
public:
/* Public API. */
NOINLINE void Initialize(s32 core_id);
NOINLINE void Finalize();
static s64 GetTick() {
return GetCount();
}
void RegisterAbsoluteTask(KTimerTask *task, s64 task_time) {
KScopedDisableDispatch dd;
KScopedSpinLock lk(this->GetLock());
if (this->RegisterAbsoluteTaskImpl(task, task_time)) {
SetCompareValue(task_time);
EnableInterrupt();
}
}
private:
friend class impl::KHardwareTimerInterruptTask;
NOINLINE void DoInterruptTask();
private:
/* Hardware register accessors. */
static ALWAYS_INLINE void InitializeGlobalTimer() {
/* Set kernel control. */
cpu::CounterTimerKernelControlRegisterAccessor(0).SetEl0PctEn(true).Store();
/* Disable the physical timer. */
cpu::CounterTimerPhysicalTimerControlRegisterAccessor(0).SetEnable(false).SetIMask(false).Store();
/* Set the compare value to the maximum. */
cpu::CounterTimerPhysicalTimerCompareValueRegisterAccessor(0).SetCompareValue(std::numeric_limits<u64>::max()).Store();
/* Enable the physical timer, with interrupt masked. */
cpu::CounterTimerPhysicalTimerControlRegisterAccessor(0).SetEnable(true).SetIMask(true).Store();
}
static ALWAYS_INLINE void EnableInterrupt() {
cpu::CounterTimerPhysicalTimerControlRegisterAccessor(0).SetEnable(true).SetIMask(false).Store();
}
static ALWAYS_INLINE void DisableInterrupt() {
cpu::CounterTimerPhysicalTimerControlRegisterAccessor(0).SetEnable(true).SetIMask(true).Store();
}
static ALWAYS_INLINE void StopTimer() {
/* Set the compare value to the maximum. */
cpu::CounterTimerPhysicalTimerCompareValueRegisterAccessor(0).SetCompareValue(std::numeric_limits<u64>::max()).Store();
/* Disable the physical timer. */
cpu::CounterTimerPhysicalTimerControlRegisterAccessor(0).SetEnable(false).SetIMask(false).Store();
}
static ALWAYS_INLINE s64 GetCount() {
return cpu::CounterTimerPhysicalCountValueRegisterAccessor().GetCount();
}
static ALWAYS_INLINE void SetCompareValue(s64 value) {
cpu::CounterTimerPhysicalTimerCompareValueRegisterAccessor(0).SetCompareValue(static_cast<u64>(value)).Store();
}
};
}

View File

@@ -1,275 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <mesosphere/kern_select_cpu.hpp>
#include <mesosphere/kern_k_typed_address.hpp>
namespace ams::kern::arch::arm64 {
struct GicDistributor {
u32 ctlr;
u32 typer;
u32 iidr;
u32 reserved_0x0c;
u32 statusr;
u32 reserved_0x14[3];
u32 impldef_0x20[8];
u32 setspi_nsr;
u32 reserved_0x44;
u32 clrspi_nsr;
u32 reserved_0x4c;
u32 setspi_sr;
u32 reserved_0x54;
u32 clrspi_sr;
u32 reserved_0x5c[9];
u32 igroupr[32];
u32 isenabler[32];
u32 icenabler[32];
u32 ispendr[32];
u32 icpendr[32];
u32 isactiver[32];
u32 icactiver[32];
union {
u8 bytes[1020];
u32 words[255];
} ipriorityr;
u32 _0x7fc;
union {
u8 bytes[1020];
u32 words[255];
} itargetsr;
u32 _0xbfc;
u32 icfgr[64];
u32 igrpmodr[32];
u32 _0xd80[32];
u32 nsacr[64];
u32 sgir;
u32 _0xf04[3];
u32 cpendsgir[4];
u32 spendsgir[4];
u32 reserved_0xf30[52];
static constexpr size_t SgirCpuTargetListShift = 16;
enum SgirTargetListFilter : u32 {
SgirTargetListFilter_CpuTargetList = (0 << 24),
SgirTargetListFilter_Others = (1 << 24),
SgirTargetListFilter_Self = (2 << 24),
SgirTargetListFilter_Reserved = (3 << 24),
};
};
static_assert(std::is_pod<GicDistributor>::value);
static_assert(sizeof(GicDistributor) == 0x1000);
struct GicCpuInterface {
u32 ctlr;
u32 pmr;
u32 bpr;
u32 iar;
u32 eoir;
u32 rpr;
u32 hppir;
u32 abpr;
u32 aiar;
u32 aeoir;
u32 ahppir;
u32 statusr;
u32 reserved_30[4];
u32 impldef_40[36];
u32 apr[4];
u32 nsapr[4];
u32 reserved_f0[3];
u32 iidr;
u32 reserved_100[960];
u32 dir;
u32 _0x1004[1023];
};
static_assert(std::is_pod<GicCpuInterface>::value);
static_assert(sizeof(GicCpuInterface) == 0x2000);
struct KInterruptController {
NON_COPYABLE(KInterruptController);
NON_MOVEABLE(KInterruptController);
public:
static constexpr s32 NumSoftwareInterrupts = 16;
static constexpr s32 NumLocalInterrupts = NumSoftwareInterrupts + 16;
static constexpr s32 NumGlobalInterrupts = 988;
static constexpr s32 NumInterrupts = NumLocalInterrupts + NumGlobalInterrupts;
static constexpr s32 NumPriorityLevels = 4;
public:
struct LocalState {
u32 local_isenabler[NumLocalInterrupts / 32];
u32 local_ipriorityr[NumLocalInterrupts / 4];
u32 local_targetsr[NumLocalInterrupts / 4];
u32 local_icfgr[NumLocalInterrupts / 16];
};
struct GlobalState {
u32 global_isenabler[NumGlobalInterrupts / 32];
u32 global_ipriorityr[NumGlobalInterrupts / 4];
u32 global_targetsr[NumGlobalInterrupts / 4];
u32 global_icfgr[NumGlobalInterrupts / 16];
};
enum PriorityLevel : u8 {
PriorityLevel_High = 0,
PriorityLevel_Low = NumPriorityLevels - 1,
PriorityLevel_Timer = 1,
PriorityLevel_Scheduler = 2,
};
private:
static inline u32 s_mask[cpu::NumCores];
private:
volatile GicDistributor *gicd;
volatile GicCpuInterface *gicc;
public:
constexpr KInterruptController() : gicd(nullptr), gicc(nullptr) { /* ... */ }
void Initialize(s32 core_id);
void Finalize(s32 core_id);
public:
u32 GetIrq() const {
return this->gicc->iar;
}
static constexpr s32 ConvertRawIrq(u32 irq) {
return (irq == 0x3FF) ? -1 : (irq & 0x3FF);
}
void Enable(s32 irq) const {
this->gicd->isenabler[irq / BITSIZEOF(u32)] = (1u << (irq % BITSIZEOF(u32)));
}
void Disable(s32 irq) const {
this->gicd->icenabler[irq / BITSIZEOF(u32)] = (1u << (irq % BITSIZEOF(u32)));
}
void Clear(s32 irq) const {
this->gicd->icpendr[irq / BITSIZEOF(u32)] = (1u << (irq % BITSIZEOF(u32)));
}
void SetTarget(s32 irq, s32 core_id) const {
this->gicd->itargetsr.bytes[irq] |= GetGicMask(core_id);
}
void ClearTarget(s32 irq, s32 core_id) const {
this->gicd->itargetsr.bytes[irq] &= ~GetGicMask(core_id);
}
void SetPriorityLevel(s32 irq, s32 level) const {
MESOSPHERE_ASSERT(PriorityLevel_High <= level && level <= PriorityLevel_Low);
this->gicd->ipriorityr.bytes[irq] = ToGicPriorityValue(level);
}
s32 GetPriorityLevel(s32 irq) const {
return FromGicPriorityValue(this->gicd->ipriorityr.bytes[irq]);
}
void SetPriorityLevel(s32 level) const {
MESOSPHERE_ASSERT(PriorityLevel_High <= level && level <= PriorityLevel_Low);
this->gicc->pmr = ToGicPriorityValue(level);
}
void SetEdge(s32 irq) const {
u32 cfg = this->gicd->icfgr[irq / (BITSIZEOF(u32) / 2)];
cfg &= ~(0x3 << (2 * (irq % (BITSIZEOF(u32) / 2))));
cfg |= (0x2 << (2 * (irq % (BITSIZEOF(u32) / 2))));
this->gicd->icfgr[irq / (BITSIZEOF(u32) / 2)] = cfg;
}
void SetLevel(s32 irq) const {
u32 cfg = this->gicd->icfgr[irq / (BITSIZEOF(u32) / 2)];
cfg &= ~(0x3 << (2 * (irq % (BITSIZEOF(u32) / 2))));
cfg |= (0x0 << (2 * (irq % (BITSIZEOF(u32) / 2))));
this->gicd->icfgr[irq / (BITSIZEOF(u32) / 2)] = cfg;
}
void SendInterProcessorInterrupt(s32 irq, u64 core_mask) {
MESOSPHERE_ASSERT(IsSoftware(irq));
this->gicd->sgir = GetCpuTargetListMask(irq, core_mask);
}
void SendInterProcessorInterrupt(s32 irq) {
MESOSPHERE_ASSERT(IsSoftware(irq));
this->gicd->sgir = GicDistributor::SgirTargetListFilter_Others | irq;
}
void EndOfInterrupt(u32 irq) const {
this->gicc->eoir = irq;
}
bool IsInterruptDefined(s32 irq) {
const s32 num_interrupts = std::min(32 + 32 * (this->gicd->typer & 0x1F), static_cast<u32>(NumInterrupts));
return (0 <= irq && irq < num_interrupts);
}
/* TODO: Implement more KInterruptController functionality. */
public:
static constexpr ALWAYS_INLINE bool IsSoftware(s32 id) {
MESOSPHERE_ASSERT(0 <= id && id < NumInterrupts);
return id < NumSoftwareInterrupts;
}
static constexpr ALWAYS_INLINE bool IsLocal(s32 id) {
MESOSPHERE_ASSERT(0 <= id && id < NumInterrupts);
return id < NumLocalInterrupts;
}
static constexpr ALWAYS_INLINE bool IsGlobal(s32 id) {
MESOSPHERE_ASSERT(0 <= id && id < NumInterrupts);
return NumLocalInterrupts <= id;
}
static constexpr size_t GetGlobalInterruptIndex(s32 id) {
MESOSPHERE_ASSERT(IsGlobal(id));
return id - NumLocalInterrupts;
}
static constexpr size_t GetLocalInterruptIndex(s32 id) {
MESOSPHERE_ASSERT(IsLocal(id));
return id;
}
private:
static constexpr size_t PriorityShift = BITSIZEOF(u8) - __builtin_ctz(NumPriorityLevels);
static_assert(PriorityShift < BITSIZEOF(u8));
static constexpr ALWAYS_INLINE u8 ToGicPriorityValue(s32 level) {
return (level << PriorityShift) | ((1 << PriorityShift) - 1);
}
static constexpr ALWAYS_INLINE s32 FromGicPriorityValue(u8 priority) {
return (priority >> PriorityShift) & (NumPriorityLevels - 1);
}
static constexpr ALWAYS_INLINE s32 GetCpuTargetListMask(s32 irq, u64 core_mask) {
MESOSPHERE_ASSERT(IsSoftware(irq));
MESOSPHERE_ASSERT(core_mask < (1ul << cpu::NumCores));
return GicDistributor::SgirTargetListFilter_CpuTargetList | irq | (static_cast<u16>(core_mask) << GicDistributor::SgirCpuTargetListShift);
}
static ALWAYS_INLINE s32 GetGicMask(s32 core_id) {
return s_mask[core_id];
}
ALWAYS_INLINE void SetGicMask(s32 core_id) const {
s_mask[core_id] = this->gicd->itargetsr.bytes[0];
}
NOINLINE void SetupInterruptLines(s32 core_id) const;
};
}

View File

@@ -1,130 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <mesosphere/kern_select_cpu.hpp>
#include <mesosphere/kern_k_spin_lock.hpp>
#include <mesosphere/kern_k_interrupt_task.hpp>
#include <mesosphere/kern_select_interrupt_controller.hpp>
namespace ams::kern::arch::arm64 {
class KInterruptManager {
NON_COPYABLE(KInterruptManager);
NON_MOVEABLE(KInterruptManager);
private:
struct KCoreLocalInterruptEntry {
KInterruptHandler *handler;
bool manually_cleared;
bool needs_clear;
u8 priority;
constexpr KCoreLocalInterruptEntry()
: handler(nullptr), manually_cleared(false), needs_clear(false), priority(KInterruptController::PriorityLevel_Low)
{
/* ... */
}
};
struct KGlobalInterruptEntry {
KInterruptHandler *handler;
bool manually_cleared;
bool needs_clear;
constexpr KGlobalInterruptEntry() : handler(nullptr), manually_cleared(false), needs_clear(false) { /* ... */ }
};
private:
static KSpinLock s_lock;
static std::array<KGlobalInterruptEntry, KInterruptController::NumGlobalInterrupts> s_global_interrupts;
static KInterruptController::GlobalState s_global_state;
static bool s_global_state_saved;
private:
KCoreLocalInterruptEntry core_local_interrupts[KInterruptController::NumLocalInterrupts];
KInterruptController interrupt_controller;
KInterruptController::LocalState local_state;
bool local_state_saved;
private:
static ALWAYS_INLINE KSpinLock &GetLock() { return s_lock; }
static ALWAYS_INLINE KGlobalInterruptEntry &GetGlobalInterruptEntry(s32 irq) { return s_global_interrupts[KInterruptController::GetGlobalInterruptIndex(irq)]; }
ALWAYS_INLINE KCoreLocalInterruptEntry &GetLocalInterruptEntry(s32 irq) { return this->core_local_interrupts[KInterruptController::GetLocalInterruptIndex(irq)]; }
bool OnHandleInterrupt();
public:
constexpr KInterruptManager() : core_local_interrupts(), interrupt_controller(), local_state(), local_state_saved(false) { /* ... */ }
NOINLINE void Initialize(s32 core_id);
NOINLINE void Finalize(s32 core_id);
bool IsInterruptDefined(s32 irq) {
return this->interrupt_controller.IsInterruptDefined(irq);
}
NOINLINE Result BindHandler(KInterruptHandler *handler, s32 irq, s32 core_id, s32 priority, bool manual_clear, bool level);
NOINLINE Result UnbindHandler(s32 irq, s32 core);
NOINLINE Result ClearInterrupt(s32 irq);
NOINLINE Result ClearInterrupt(s32 irq, s32 core_id);
ALWAYS_INLINE void SendInterProcessorInterrupt(s32 irq, u64 core_mask) {
this->interrupt_controller.SendInterProcessorInterrupt(irq, core_mask);
}
ALWAYS_INLINE void SendInterProcessorInterrupt(s32 irq) {
this->interrupt_controller.SendInterProcessorInterrupt(irq);
}
static void HandleInterrupt(bool user_mode);
/* Implement more KInterruptManager functionality. */
private:
Result BindGlobal(KInterruptHandler *handler, s32 irq, s32 core_id, s32 priority, bool manual_clear, bool level);
Result BindLocal(KInterruptHandler *handler, s32 irq, s32 priority, bool manual_clear);
Result UnbindGlobal(s32 irq);
Result UnbindLocal(s32 irq);
Result ClearGlobal(s32 irq);
Result ClearLocal(s32 irq);
public:
static ALWAYS_INLINE u32 DisableInterrupts() {
u64 intr_state;
__asm__ __volatile__("mrs %[intr_state], daif\n"
"msr daifset, #2"
: [intr_state]"=r"(intr_state)
:: "memory");
return intr_state;
}
static ALWAYS_INLINE u32 EnableInterrupts() {
u64 intr_state;
__asm__ __volatile__("mrs %[intr_state], daif\n"
"msr daifclr, #2"
: [intr_state]"=r"(intr_state)
:: "memory");
return intr_state;
}
static ALWAYS_INLINE void RestoreInterrupts(u32 intr_state) {
u64 cur_state;
__asm__ __volatile__("mrs %[cur_state], daif" : [cur_state]"=r"(cur_state));
__asm__ __volatile__("msr daif, %[intr_state]" :: [intr_state]"r"((cur_state & ~0x80ul) | (intr_state & 0x80)));
}
static ALWAYS_INLINE bool AreInterruptsEnabled() {
u64 intr_state;
__asm__ __volatile__("mrs %[intr_state], daif" : [intr_state]"=r"(intr_state));
return (intr_state & 0x80) == 0;
}
};
}

View File

@@ -1,48 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
namespace ams::kern::arch::arm64 {
namespace interrupt_name {
enum KInterruptName : s32 {
/* SGIs */
KInterruptName_ThreadTerminate = 4,
KInterruptName_CacheOperation = 5,
KInterruptName_Scheduler = 6,
KInterruptName_PerformanceCounter = 8,
/* PPIs */
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
KInterruptName_VirtualMaintenance = 25,
KInterruptName_HypervisorTimer = 26,
KInterruptName_VirtualTimer = 27,
KInterruptName_LegacyNFiq = 38,
KInterruptName_SecurePhysicalTimer = 29,
KInterruptName_NonSecurePhysicalTimer = 30,
KInterruptName_LegacyNIrq = 31,
#endif
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
KInterruptName_MemoryController = 109,
#endif
};
};
}

View File

@@ -1,275 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_select_cpu.hpp>
#include <mesosphere/kern_k_page_table_base.hpp>
#include <mesosphere/kern_k_page_group.hpp>
#include <mesosphere/kern_k_page_table_manager.hpp>
namespace ams::kern::arch::arm64 {
class KPageTable : public KPageTableBase {
NON_COPYABLE(KPageTable);
NON_MOVEABLE(KPageTable);
public:
using TraversalEntry = KPageTableImpl::TraversalEntry;
using TraversalContext = KPageTableImpl::TraversalContext;
enum BlockType {
BlockType_L3Block,
BlockType_L3ContiguousBlock,
BlockType_L2Block,
#ifdef ATMOSPHERE_BOARD_NINTENDO_NX
BlockType_L2TegraSmmuBlock,
#endif
BlockType_L2ContiguousBlock,
BlockType_L1Block,
BlockType_Count,
};
static_assert(L3BlockSize == PageSize);
static constexpr size_t ContiguousPageSize = L3ContiguousBlockSize;
#ifdef ATMOSPHERE_BOARD_NINTENDO_NX
static constexpr size_t L2TegraSmmuBlockSize = 2 * L2BlockSize;
#endif
static constexpr size_t BlockSizes[BlockType_Count] = {
[BlockType_L3Block] = L3BlockSize,
[BlockType_L3ContiguousBlock] = L3ContiguousBlockSize,
[BlockType_L2Block] = L2BlockSize,
#ifdef ATMOSPHERE_BOARD_NINTENDO_NX
[BlockType_L2TegraSmmuBlock] = L2TegraSmmuBlockSize,
#endif
[BlockType_L2ContiguousBlock] = L2ContiguousBlockSize,
[BlockType_L1Block] = L1BlockSize,
};
static constexpr BlockType GetMaxBlockType() {
return BlockType_L1Block;
}
static constexpr size_t GetBlockSize(BlockType type) {
return BlockSizes[type];
}
static constexpr BlockType GetBlockType(size_t size) {
switch (size) {
case L3BlockSize: return BlockType_L3Block;
case L3ContiguousBlockSize: return BlockType_L3ContiguousBlock;
case L2BlockSize: return BlockType_L2Block;
#ifdef ATMOSPHERE_BOARD_NINTENDO_NX
case L2TegraSmmuBlockSize: return BlockType_L2TegraSmmuBlock;
#endif
case L2ContiguousBlockSize: return BlockType_L2ContiguousBlock;
case L1BlockSize: return BlockType_L1Block;
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
}
}
static constexpr size_t GetSmallerAlignment(size_t alignment) {
MESOSPHERE_ASSERT(alignment > L3BlockSize);
return KPageTable::GetBlockSize(static_cast<KPageTable::BlockType>(KPageTable::GetBlockType(alignment) - 1));
}
static constexpr size_t GetLargerAlignment(size_t alignment) {
MESOSPHERE_ASSERT(alignment < L1BlockSize);
return KPageTable::GetBlockSize(static_cast<KPageTable::BlockType>(KPageTable::GetBlockType(alignment) + 1));
}
private:
KPageTableManager *manager;
u64 ttbr;
u8 asid;
protected:
virtual Result Operate(PageLinkedList *page_list, KProcessAddress virt_addr, size_t num_pages, KPhysicalAddress phys_addr, bool is_pa_valid, const KPageProperties properties, OperationType operation, bool reuse_ll) override;
virtual Result Operate(PageLinkedList *page_list, KProcessAddress virt_addr, size_t num_pages, const KPageGroup &page_group, const KPageProperties properties, OperationType operation, bool reuse_ll) override;
virtual void FinalizeUpdate(PageLinkedList *page_list) override;
KPageTableManager &GetPageTableManager() const { return *this->manager; }
private:
constexpr PageTableEntry GetEntryTemplate(const KPageProperties properties) const {
/* Set basic attributes. */
PageTableEntry entry;
entry.SetPrivilegedExecuteNever(true);
entry.SetAccessFlag(PageTableEntry::AccessFlag_Accessed);
entry.SetShareable(PageTableEntry::Shareable_InnerShareable);
if (!this->IsKernel()) {
entry.SetGlobal(false);
}
/* Set page attribute. */
if (properties.io) {
MESOSPHERE_ABORT_UNLESS(!properties.uncached);
MESOSPHERE_ABORT_UNLESS((properties.perm & (KMemoryPermission_KernelExecute | KMemoryPermission_UserExecute)) == 0);
entry.SetPageAttribute(PageTableEntry::PageAttribute_Device_nGnRnE)
.SetUserExecuteNever(true);
} else if (properties.uncached) {
MESOSPHERE_ABORT_UNLESS((properties.perm & (KMemoryPermission_KernelExecute | KMemoryPermission_UserExecute)) == 0);
entry.SetPageAttribute(PageTableEntry::PageAttribute_NormalMemoryNotCacheable);
} else {
entry.SetPageAttribute(PageTableEntry::PageAttribute_NormalMemory);
}
/* Set user execute never bit. */
if (properties.perm != KMemoryPermission_UserReadExecute) {
MESOSPHERE_ABORT_UNLESS((properties.perm & (KMemoryPermission_KernelExecute | KMemoryPermission_UserExecute)) == 0);
entry.SetUserExecuteNever(true);
}
/* Set can be contiguous. */
entry.SetContiguousAllowed(!properties.non_contiguous);
/* Set AP[1] based on perm. */
switch (properties.perm & KMemoryPermission_UserReadWrite) {
case KMemoryPermission_UserReadWrite:
case KMemoryPermission_UserRead:
entry.SetUserAccessible(true);
break;
case KMemoryPermission_KernelReadWrite:
case KMemoryPermission_KernelRead:
entry.SetUserAccessible(false);
break;
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
}
/* Set AP[2] based on perm. */
switch (properties.perm & KMemoryPermission_UserReadWrite) {
case KMemoryPermission_UserReadWrite:
case KMemoryPermission_KernelReadWrite:
entry.SetReadOnly(false);
break;
case KMemoryPermission_KernelRead:
case KMemoryPermission_UserRead:
entry.SetReadOnly(true);
break;
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
}
return entry;
}
public:
constexpr KPageTable() : KPageTableBase(), manager(), ttbr(), asid() { /* ... */ }
static NOINLINE void Initialize(s32 core_id);
ALWAYS_INLINE void Activate(u32 proc_id) {
cpu::DataSynchronizationBarrier();
cpu::SwitchProcess(this->ttbr, proc_id);
}
NOINLINE Result InitializeForKernel(void *table, KVirtualAddress start, KVirtualAddress end);
NOINLINE Result InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager);
Result Finalize();
private:
Result Map(KProcessAddress virt_addr, KPhysicalAddress phys_addr, size_t num_pages, PageTableEntry entry_template, PageLinkedList *page_list, bool reuse_ll);
Result Unmap(KProcessAddress virt_addr, size_t num_pages, PageLinkedList *page_list, bool force, bool reuse_ll);
Result Map(KProcessAddress virt_addr, KPhysicalAddress phys_addr, size_t num_pages, PageTableEntry entry_template, size_t page_size, PageLinkedList *page_list, bool reuse_ll) {
switch (page_size) {
case L1BlockSize:
#ifdef ATMOSPHERE_BOARD_NINTENDO_NX
case L2TegraSmmuBlockSize:
#endif
case L2BlockSize:
case L3BlockSize:
break;
case L2ContiguousBlockSize:
case L3ContiguousBlockSize:
entry_template.SetContiguous(true);
break;
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
}
return this->Map(virt_addr, phys_addr, num_pages, entry_template, page_list, reuse_ll);
}
Result MapContiguous(KProcessAddress virt_addr, KPhysicalAddress phys_addr, size_t num_pages, PageTableEntry entry_template, PageLinkedList *page_list, bool reuse_ll);
Result MapGroup(KProcessAddress virt_addr, const KPageGroup &pg, size_t num_pages, PageTableEntry entry_template, PageLinkedList *page_list, bool reuse_ll);
bool MergePages(KProcessAddress virt_addr, PageLinkedList *page_list);
ALWAYS_INLINE Result SeparatePagesImpl(KProcessAddress virt_addr, size_t block_size, PageLinkedList *page_list, bool reuse_ll);
Result SeparatePages(KProcessAddress virt_addr, size_t block_size, PageLinkedList *page_list, bool reuse_ll);
Result ChangePermissions(KProcessAddress virt_addr, size_t num_pages, PageTableEntry entry_template, bool refresh_mapping, PageLinkedList *page_list, bool reuse_ll);
static void PteDataSynchronizationBarrier() {
cpu::DataSynchronizationBarrierInnerShareable();
}
static void ClearPageTable(KVirtualAddress table) {
cpu::ClearPageToZero(GetVoidPointer(table));
}
void OnTableUpdated() const {
cpu::InvalidateTlbByAsid(this->asid);
}
void OnKernelTableUpdated() const {
cpu::InvalidateEntireTlbDataOnly();
}
void OnKernelTableSinglePageUpdated(KProcessAddress virt_addr) const {
cpu::InvalidateTlbByVaDataOnly(virt_addr);
}
void NoteUpdated() const {
cpu::DataSynchronizationBarrier();
if (this->IsKernel()) {
this->OnKernelTableUpdated();
} else {
this->OnTableUpdated();
}
}
void NoteSingleKernelPageUpdated(KProcessAddress virt_addr) const {
MESOSPHERE_ASSERT(this->IsKernel());
cpu::DataSynchronizationBarrier();
this->OnKernelTableSinglePageUpdated(virt_addr);
}
KVirtualAddress AllocatePageTable(PageLinkedList *page_list, bool reuse_ll) const {
KVirtualAddress table = this->GetPageTableManager().Allocate();
if (table == Null<KVirtualAddress>) {
if (reuse_ll && page_list->Peek()) {
table = KVirtualAddress(reinterpret_cast<uintptr_t>(page_list->Pop()));
} else {
return Null<KVirtualAddress>;
}
}
ClearPageTable(table);
MESOSPHERE_ASSERT(this->GetPageTableManager().GetRefCount(table) == 0);
return table;
}
void FreePageTable(PageLinkedList *page_list, KVirtualAddress table) const {
MESOSPHERE_ASSERT(this->GetPageTableManager().IsInPageTableHeap(table));
MESOSPHERE_ASSERT(this->GetPageTableManager().GetRefCount(table) == 0);
page_list->Push(table);
}
};
}

View File

@@ -1,299 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_select_cpu.hpp>
#include <mesosphere/kern_k_typed_address.hpp>
namespace ams::kern::arch::arm64 {
constexpr size_t L1BlockSize = 1_GB;
constexpr size_t L1ContiguousBlockSize = 0x10 * L1BlockSize;
constexpr size_t L2BlockSize = 2_MB;
constexpr size_t L2ContiguousBlockSize = 0x10 * L2BlockSize;
constexpr size_t L3BlockSize = PageSize;
constexpr size_t L3ContiguousBlockSize = 0x10 * L3BlockSize;
class PageTableEntry {
public:
struct InvalidTag{};
enum ExtensionTag : u64 {
ExtensionTag_IsValidBit = (1ul << 56),
ExtensionTag_IsValid = (ExtensionTag_IsValidBit | (1ul << 0)),
ExtensionTag_IsBlockMask = (ExtensionTag_IsValidBit | (1ul << 1)),
};
enum Permission : u64 {
Permission_KernelRWX = ((0ul << 53) | (1ul << 54) | (0ul << 6)),
Permission_KernelRX = ((0ul << 53) | (1ul << 54) | (2ul << 6)),
Permission_KernelR = ((1ul << 53) | (1ul << 54) | (2ul << 6)),
Permission_KernelRW = ((1ul << 53) | (1ul << 54) | (0ul << 6)),
Permission_UserRX = ((1ul << 53) | (0ul << 54) | (3ul << 6)),
Permission_UserR = ((1ul << 53) | (1ul << 54) | (3ul << 6)),
Permission_UserRW = ((1ul << 53) | (1ul << 54) | (1ul << 6)),
};
enum Shareable : u64 {
Shareable_NonShareable = (0 << 8),
Shareable_OuterShareable = (2 << 8),
Shareable_InnerShareable = (3 << 8),
};
/* Official attributes are: */
/* 0x00, 0x04, 0xFF, 0x44. 4-7 are unused. */
enum PageAttribute : u64 {
PageAttribute_Device_nGnRnE = (0 << 2),
PageAttribute_Device_nGnRE = (1 << 2),
PageAttribute_NormalMemory = (2 << 2),
PageAttribute_NormalMemoryNotCacheable = (3 << 2),
};
enum AccessFlag : u64 {
AccessFlag_NotAccessed = (0 << 10),
AccessFlag_Accessed = (1 << 10),
};
enum Type : u64 {
Type_None = 0x0,
Type_L1Block = 0x1,
Type_L1Table = 0x3,
Type_L2Block = 0x1,
Type_L2Table = 0x3,
Type_L3Block = 0x3,
};
enum ContigType : u64 {
ContigType_NotContiguous = (0x0ul << 52),
ContigType_Contiguous = (0x1ul << 52),
};
protected:
u64 attributes;
public:
/* Take in a raw attribute. */
constexpr ALWAYS_INLINE PageTableEntry() : attributes() { /* ... */ }
constexpr ALWAYS_INLINE PageTableEntry(u64 attr) : attributes(attr) { /* ... */ }
constexpr ALWAYS_INLINE PageTableEntry(InvalidTag) : attributes(0) { /* ... */ }
/* Extend a previous attribute. */
constexpr ALWAYS_INLINE PageTableEntry(const PageTableEntry &rhs, u64 new_attr) : attributes(rhs.attributes | new_attr) { /* ... */ }
/* Construct a new attribute. */
constexpr ALWAYS_INLINE PageTableEntry(Permission perm, PageAttribute p_a, Shareable share)
: attributes(static_cast<u64>(perm) | static_cast<u64>(AccessFlag_Accessed) | static_cast<u64>(p_a) | static_cast<u64>(share) | static_cast<u64>(ExtensionTag_IsValid))
{
/* ... */
}
protected:
constexpr ALWAYS_INLINE u64 GetBits(size_t offset, size_t count) const {
return (this->attributes >> offset) & ((1ul << count) - 1);
}
constexpr ALWAYS_INLINE u64 SelectBits(size_t offset, size_t count) const {
return this->attributes & (((1ul << count) - 1) << offset);
}
constexpr ALWAYS_INLINE void SetBits(size_t offset, size_t count, u64 value) {
const u64 mask = ((1ul << count) - 1) << offset;
this->attributes &= ~mask;
this->attributes |= (value & (mask >> offset)) << offset;
}
constexpr ALWAYS_INLINE void SetBitsDirect(size_t offset, size_t count, u64 value) {
const u64 mask = ((1ul << count) - 1) << offset;
this->attributes &= ~mask;
this->attributes |= (value & mask);
}
constexpr ALWAYS_INLINE void SetBit(size_t offset, bool enabled) {
const u64 mask = 1ul << offset;
if (enabled) {
this->attributes |= mask;
} else {
this->attributes &= ~mask;
}
}
public:
constexpr ALWAYS_INLINE bool IsContiguousAllowed() const { return this->GetBits(55, 1) != 0; }
constexpr ALWAYS_INLINE bool IsUserExecuteNever() const { return this->GetBits(54, 1) != 0; }
constexpr ALWAYS_INLINE bool IsPrivilegedExecuteNever() const { return this->GetBits(53, 1) != 0; }
constexpr ALWAYS_INLINE bool IsContiguous() const { return this->GetBits(52, 1) != 0; }
constexpr ALWAYS_INLINE bool IsGlobal() const { return this->GetBits(11, 1) == 0; }
constexpr ALWAYS_INLINE AccessFlag GetAccessFlag() const { return static_cast<AccessFlag>(this->GetBits(10, 1)); }
constexpr ALWAYS_INLINE Shareable GetShareable() const { return static_cast<Shareable>(this->GetBits(8, 2)); }
constexpr ALWAYS_INLINE PageAttribute GetPageAttribute() const { return static_cast<PageAttribute>(this->GetBits(2, 3)); }
constexpr ALWAYS_INLINE bool IsReadOnly() const { return this->GetBits(7, 1) != 0; }
constexpr ALWAYS_INLINE bool IsUserAccessible() const { return this->GetBits(6, 1) != 0; }
constexpr ALWAYS_INLINE bool IsNonSecure() const { return this->GetBits(5, 1) != 0; }
constexpr ALWAYS_INLINE bool IsBlock() const { return (this->attributes & ExtensionTag_IsBlockMask) == ExtensionTag_IsValidBit; }
constexpr ALWAYS_INLINE bool IsTable() const { return this->GetBits(0, 2) == 0x3; }
constexpr ALWAYS_INLINE bool IsEmpty() const { return this->GetBits(0, 2) == 0x0; }
constexpr ALWAYS_INLINE decltype(auto) SetContiguousAllowed(bool en) { this->SetBit(55, !en); return *this; }
constexpr ALWAYS_INLINE decltype(auto) SetUserExecuteNever(bool en) { this->SetBit(54, en); return *this; }
constexpr ALWAYS_INLINE decltype(auto) SetPrivilegedExecuteNever(bool en) { this->SetBit(53, en); return *this; }
constexpr ALWAYS_INLINE decltype(auto) SetContiguous(bool en) { this->SetBit(52, en); return *this; }
constexpr ALWAYS_INLINE decltype(auto) SetGlobal(bool en) { this->SetBit(11, !en); return *this; }
constexpr ALWAYS_INLINE decltype(auto) SetAccessFlag(AccessFlag f) { this->SetBitsDirect(10, 1, f); return *this; }
constexpr ALWAYS_INLINE decltype(auto) SetShareable(Shareable s) { this->SetBitsDirect(8, 2, s); return *this; }
constexpr ALWAYS_INLINE decltype(auto) SetReadOnly(bool en) { this->SetBit(7, en); return *this; }
constexpr ALWAYS_INLINE decltype(auto) SetUserAccessible(bool en) { this->SetBit(6, en); return *this; }
constexpr ALWAYS_INLINE decltype(auto) SetPageAttribute(PageAttribute a) { this->SetBitsDirect(2, 3, a); return *this; }
constexpr ALWAYS_INLINE u64 GetEntryTemplate() const {
constexpr u64 Mask = (0xFFF0000000000FFFul & ~u64(0x3ul | (0x1ul << 52)));
return this->attributes & Mask;
}
constexpr ALWAYS_INLINE bool Is(u64 attr) const {
return this->attributes == attr;
}
constexpr ALWAYS_INLINE u64 GetRawAttributesUnsafeForSwap() const {
return this->attributes;
}
protected:
constexpr ALWAYS_INLINE u64 GetRawAttributes() const {
return this->attributes;
}
};
static_assert(sizeof(PageTableEntry) == sizeof(u64));
constexpr inline PageTableEntry InvalidPageTableEntry = PageTableEntry(PageTableEntry::InvalidTag{});
constexpr inline size_t MaxPageTableEntries = PageSize / sizeof(PageTableEntry);
class L1PageTableEntry : public PageTableEntry {
public:
constexpr ALWAYS_INLINE L1PageTableEntry(InvalidTag) : PageTableEntry(InvalidTag{}) { /* ... */ }
constexpr ALWAYS_INLINE L1PageTableEntry(KPhysicalAddress phys_addr, bool pxn)
: PageTableEntry((0x3ul << 60) | (static_cast<u64>(pxn) << 59) | GetInteger(phys_addr) | 0x3)
{
/* ... */
}
constexpr ALWAYS_INLINE L1PageTableEntry(KPhysicalAddress phys_addr, bool is_kernel, bool pxn)
: PageTableEntry(((is_kernel ? 0x3ul : 0) << 60) | (static_cast<u64>(pxn) << 59) | GetInteger(phys_addr) | 0x3)
{
/* ... */
}
constexpr ALWAYS_INLINE L1PageTableEntry(KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig)
: PageTableEntry(attr, (static_cast<u64>(contig) << 52) | GetInteger(phys_addr) | PageTableEntry::ExtensionTag_IsValid)
{
/* ... */
}
constexpr ALWAYS_INLINE KPhysicalAddress GetBlock() const {
return this->SelectBits(30, 18);
}
constexpr ALWAYS_INLINE KPhysicalAddress GetTable() const {
return this->SelectBits(12, 36);
}
constexpr ALWAYS_INLINE bool GetTable(KPhysicalAddress &out) const {
if (this->IsTable()) {
out = this->GetTable();
return true;
} else {
return false;
}
}
constexpr ALWAYS_INLINE bool IsCompatibleWithAttribute(const PageTableEntry &rhs, bool contig) const {
/* Check whether this has the same permission/etc as the desired attributes. */
return L1PageTableEntry(this->GetBlock(), rhs, contig).GetRawAttributes() == this->GetRawAttributes();
}
};
class L2PageTableEntry : public PageTableEntry {
public:
constexpr ALWAYS_INLINE L2PageTableEntry(InvalidTag) : PageTableEntry(InvalidTag{}) { /* ... */ }
constexpr ALWAYS_INLINE L2PageTableEntry(KPhysicalAddress phys_addr, bool pxn)
: PageTableEntry((0x3ul << 60) | (static_cast<u64>(pxn) << 59) | GetInteger(phys_addr) | 0x3)
{
/* ... */
}
constexpr ALWAYS_INLINE L2PageTableEntry(KPhysicalAddress phys_addr, bool is_kernel, bool pxn)
: PageTableEntry(((is_kernel ? 0x3ul : 0) << 60) | (static_cast<u64>(pxn) << 59) | GetInteger(phys_addr) | 0x3)
{
/* ... */
}
constexpr ALWAYS_INLINE L2PageTableEntry(KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig)
: PageTableEntry(attr, (static_cast<u64>(contig) << 52) | GetInteger(phys_addr) | PageTableEntry::ExtensionTag_IsValid)
{
/* ... */
}
constexpr ALWAYS_INLINE KPhysicalAddress GetBlock() const {
return this->SelectBits(21, 27);
}
constexpr ALWAYS_INLINE KPhysicalAddress GetTable() const {
return this->SelectBits(12, 36);
}
constexpr ALWAYS_INLINE bool GetTable(KPhysicalAddress &out) const {
if (this->IsTable()) {
out = this->GetTable();
return true;
} else {
return false;
}
}
constexpr ALWAYS_INLINE bool IsCompatibleWithAttribute(const PageTableEntry &rhs, bool contig) const {
/* Check whether this has the same permission/etc as the desired attributes. */
return L2PageTableEntry(this->GetBlock(), rhs, contig).GetRawAttributes() == this->GetRawAttributes();
}
};
class L3PageTableEntry : public PageTableEntry {
public:
constexpr ALWAYS_INLINE L3PageTableEntry(InvalidTag) : PageTableEntry(InvalidTag{}) { /* ... */ }
constexpr ALWAYS_INLINE L3PageTableEntry(KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig)
: PageTableEntry(attr, (static_cast<u64>(contig) << 52) | GetInteger(phys_addr) | 0x2 | PageTableEntry::ExtensionTag_IsValid)
{
/* ... */
}
constexpr ALWAYS_INLINE bool IsBlock() const { return (GetRawAttributes() & ExtensionTag_IsBlockMask) == ExtensionTag_IsBlockMask; }
constexpr ALWAYS_INLINE KPhysicalAddress GetBlock() const {
return this->SelectBits(12, 36);
}
constexpr ALWAYS_INLINE bool IsCompatibleWithAttribute(const PageTableEntry &rhs, bool contig) const {
/* Check whether this has the same permission/etc as the desired attributes. */
return L3PageTableEntry(this->GetBlock(), rhs, contig).GetRawAttributes() == this->GetRawAttributes();
}
};
constexpr inline L1PageTableEntry InvalidL1PageTableEntry = L1PageTableEntry(PageTableEntry::InvalidTag{});
constexpr inline L2PageTableEntry InvalidL2PageTableEntry = L2PageTableEntry(PageTableEntry::InvalidTag{});
constexpr inline L3PageTableEntry InvalidL3PageTableEntry = L3PageTableEntry(PageTableEntry::InvalidTag{});
}

View File

@@ -1,115 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_select_cpu.hpp>
#include <mesosphere/kern_k_typed_address.hpp>
#include <mesosphere/kern_k_memory_layout.hpp>
#include <mesosphere/arch/arm64/kern_k_page_table_entry.hpp>
namespace ams::kern::arch::arm64 {
class KPageTableImpl {
NON_COPYABLE(KPageTableImpl);
NON_MOVEABLE(KPageTableImpl);
public:
struct TraversalEntry {
KPhysicalAddress phys_addr;
size_t block_size;
};
struct TraversalContext {
const L1PageTableEntry *l1_entry;
const L2PageTableEntry *l2_entry;
const L3PageTableEntry *l3_entry;
};
private:
static constexpr size_t PageBits = __builtin_ctzll(PageSize);
static constexpr size_t NumLevels = 3;
static constexpr size_t LevelBits = 9;
static_assert(NumLevels > 0);
template<size_t Offset, size_t Count>
static constexpr ALWAYS_INLINE u64 GetBits(u64 value) {
return (value >> Offset) & ((1ul << Count) - 1);
}
template<size_t Offset, size_t Count>
constexpr ALWAYS_INLINE u64 SelectBits(u64 value) {
return value & (((1ul << Count) - 1) << Offset);
}
static constexpr ALWAYS_INLINE uintptr_t GetL0Index(KProcessAddress addr) { return GetBits<PageBits + LevelBits * (NumLevels - 0), LevelBits>(GetInteger(addr)); }
static constexpr ALWAYS_INLINE uintptr_t GetL1Index(KProcessAddress addr) { return GetBits<PageBits + LevelBits * (NumLevels - 1), LevelBits>(GetInteger(addr)); }
static constexpr ALWAYS_INLINE uintptr_t GetL2Index(KProcessAddress addr) { return GetBits<PageBits + LevelBits * (NumLevels - 2), LevelBits>(GetInteger(addr)); }
static constexpr ALWAYS_INLINE uintptr_t GetL3Index(KProcessAddress addr) { return GetBits<PageBits + LevelBits * (NumLevels - 3), LevelBits>(GetInteger(addr)); }
static constexpr ALWAYS_INLINE uintptr_t GetL1Offset(KProcessAddress addr) { return GetBits<0, PageBits + LevelBits * (NumLevels - 1)>(GetInteger(addr)); }
static constexpr ALWAYS_INLINE uintptr_t GetL2Offset(KProcessAddress addr) { return GetBits<0, PageBits + LevelBits * (NumLevels - 2)>(GetInteger(addr)); }
static constexpr ALWAYS_INLINE uintptr_t GetL3Offset(KProcessAddress addr) { return GetBits<0, PageBits + LevelBits * (NumLevels - 3)>(GetInteger(addr)); }
static constexpr ALWAYS_INLINE uintptr_t GetContiguousL1Offset(KProcessAddress addr) { return GetBits<0, PageBits + LevelBits * (NumLevels - 1) + 4>(GetInteger(addr)); }
static constexpr ALWAYS_INLINE uintptr_t GetContiguousL2Offset(KProcessAddress addr) { return GetBits<0, PageBits + LevelBits * (NumLevels - 2) + 4>(GetInteger(addr)); }
static constexpr ALWAYS_INLINE uintptr_t GetContiguousL3Offset(KProcessAddress addr) { return GetBits<0, PageBits + LevelBits * (NumLevels - 3) + 4>(GetInteger(addr)); }
static ALWAYS_INLINE KVirtualAddress GetPageTableVirtualAddress(KPhysicalAddress addr) {
return KMemoryLayout::GetLinearVirtualAddress(addr);
}
ALWAYS_INLINE bool ExtractL1Entry(TraversalEntry *out_entry, TraversalContext *out_context, const L1PageTableEntry *l1_entry, KProcessAddress virt_addr) const;
ALWAYS_INLINE bool ExtractL2Entry(TraversalEntry *out_entry, TraversalContext *out_context, const L2PageTableEntry *l2_entry, KProcessAddress virt_addr) const;
ALWAYS_INLINE bool ExtractL3Entry(TraversalEntry *out_entry, TraversalContext *out_context, const L3PageTableEntry *l3_entry, KProcessAddress virt_addr) const;
private:
L1PageTableEntry *table;
bool is_kernel;
u32 num_entries;
public:
ALWAYS_INLINE KVirtualAddress GetTableEntry(KVirtualAddress table, size_t index) const {
return table + index * sizeof(PageTableEntry);
}
ALWAYS_INLINE L1PageTableEntry *GetL1Entry(KProcessAddress address) const {
return GetPointer<L1PageTableEntry>(GetTableEntry(KVirtualAddress(this->table), GetL1Index(address) & (this->num_entries - 1)));
}
ALWAYS_INLINE L2PageTableEntry *GetL2EntryFromTable(KVirtualAddress table, KProcessAddress address) const {
return GetPointer<L2PageTableEntry>(GetTableEntry(table, GetL2Index(address)));
}
ALWAYS_INLINE L2PageTableEntry *GetL2Entry(const L1PageTableEntry *entry, KProcessAddress address) const {
return GetL2EntryFromTable(KMemoryLayout::GetLinearVirtualAddress(entry->GetTable()), address);
}
ALWAYS_INLINE L3PageTableEntry *GetL3EntryFromTable(KVirtualAddress table, KProcessAddress address) const {
return GetPointer<L3PageTableEntry>(GetTableEntry(table, GetL3Index(address)));
}
ALWAYS_INLINE L3PageTableEntry *GetL3Entry(const L2PageTableEntry *entry, KProcessAddress address) const {
return GetL3EntryFromTable(KMemoryLayout::GetLinearVirtualAddress(entry->GetTable()), address);
}
public:
constexpr KPageTableImpl() : table(), is_kernel(), num_entries() { /* ... */ }
NOINLINE void InitializeForKernel(void *tb, KVirtualAddress start, KVirtualAddress end);
NOINLINE void InitializeForProcess(void *tb, KVirtualAddress start, KVirtualAddress end);
L1PageTableEntry *Finalize();
bool BeginTraversal(TraversalEntry *out_entry, TraversalContext *out_context, KProcessAddress address) const;
bool ContinueTraversal(TraversalEntry *out_entry, TraversalContext *context) const;
bool GetPhysicalAddress(KPhysicalAddress *out, KProcessAddress virt_addr) const;
};
}

View File

@@ -1,122 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/arch/arm64/kern_k_page_table.hpp>
namespace ams::kern::arch::arm64 {
class KProcessPageTable {
private:
KPageTable page_table;
public:
constexpr KProcessPageTable() : page_table() { /* ... */ }
void Activate(u64 id) {
/* Activate the page table with the specified contextidr. */
this->page_table.Activate(id);
}
Result Initialize(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager) {
return this->page_table.InitializeForProcess(id, as_type, enable_aslr, from_back, pool, code_address, code_size, mem_block_slab_manager, block_info_manager, pt_manager);
}
void Finalize() { this->page_table.Finalize(); }
Result SetMemoryPermission(KProcessAddress addr, size_t size, ams::svc::MemoryPermission perm) {
return this->page_table.SetMemoryPermission(addr, size, perm);
}
Result SetProcessMemoryPermission(KProcessAddress addr, size_t size, ams::svc::MemoryPermission perm) {
return this->page_table.SetProcessMemoryPermission(addr, size, perm);
}
Result SetHeapSize(KProcessAddress *out, size_t size) {
return this->page_table.SetHeapSize(out, size);
}
Result SetMaxHeapSize(size_t size) {
return this->page_table.SetMaxHeapSize(size);
}
Result QueryInfo(KMemoryInfo *out_info, ams::svc::PageInfo *out_page_info, KProcessAddress addr) const {
return this->page_table.QueryInfo(out_info, out_page_info, addr);
}
Result MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) {
return this->page_table.MapIo(phys_addr, size, perm);
}
Result MapStatic(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) {
return this->page_table.MapStatic(phys_addr, size, perm);
}
Result MapRegion(KMemoryRegionType region_type, KMemoryPermission perm) {
return this->page_table.MapRegion(region_type, perm);
}
Result MapPageGroup(KProcessAddress addr, const KPageGroup &pg, KMemoryState state, KMemoryPermission perm) {
return this->page_table.MapPageGroup(addr, pg, state, perm);
}
Result MapPages(KProcessAddress *out_addr, size_t num_pages, size_t alignment, KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) {
return this->page_table.MapPages(out_addr, num_pages, alignment, phys_addr, state, perm);
}
Result MapPages(KProcessAddress *out_addr, size_t num_pages, KMemoryState state, KMemoryPermission perm) {
return this->page_table.MapPages(out_addr, num_pages, state, perm);
}
Result UnmapPages(KProcessAddress addr, size_t num_pages, KMemoryState state) {
return this->page_table.UnmapPages(addr, num_pages, state);
}
Result MakeAndOpenPageGroup(KPageGroup *out, KProcessAddress address, size_t num_pages, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr) {
return this->page_table.MakeAndOpenPageGroup(out, address, num_pages, state_mask, state, perm_mask, perm, attr_mask, attr);
}
bool GetPhysicalAddress(KPhysicalAddress *out, KProcessAddress address) const {
return this->page_table.GetPhysicalAddress(out, address);
}
bool Contains(KProcessAddress addr, size_t size) const { return this->page_table.Contains(addr, size); }
bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const { return this->page_table.CanContain(addr, size, state); }
KProcessAddress GetAddressSpaceStart() const { return this->page_table.GetAddressSpaceStart(); }
KProcessAddress GetHeapRegionStart() const { return this->page_table.GetHeapRegionStart(); }
KProcessAddress GetAliasRegionStart() const { return this->page_table.GetAliasRegionStart(); }
KProcessAddress GetStackRegionStart() const { return this->page_table.GetStackRegionStart(); }
KProcessAddress GetKernelMapRegionStart() const { return this->page_table.GetKernelMapRegionStart(); }
KProcessAddress GetAliasCodeRegionStart() const { return this->page_table.GetAliasCodeRegionStart(); }
size_t GetAddressSpaceSize() const { return this->page_table.GetAddressSpaceSize(); }
size_t GetHeapRegionSize() const { return this->page_table.GetHeapRegionSize(); }
size_t GetAliasRegionSize() const { return this->page_table.GetAliasRegionSize(); }
size_t GetStackRegionSize() const { return this->page_table.GetStackRegionSize(); }
size_t GetKernelMapRegionSize() const { return this->page_table.GetKernelMapRegionSize(); }
size_t GetAliasCodeRegionSize() const { return this->page_table.GetAliasCodeRegionSize(); }
KPhysicalAddress GetHeapPhysicalAddress(KVirtualAddress address) const {
/* TODO: Better way to convert address type? */
return this->page_table.GetHeapPhysicalAddress(address);
}
KBlockInfoManager *GetBlockInfoManager() {
return this->page_table.GetBlockInfoManager();
}
};
}

View File

@@ -1,111 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <mesosphere/kern_select_cpu.hpp>
namespace ams::kern::arch::arm64 {
class KNotAlignedSpinLock {
private:
u32 packed_tickets;
public:
constexpr KNotAlignedSpinLock() : packed_tickets(0) { /* ... */ }
void Lock() {
u32 tmp0, tmp1;
__asm__ __volatile__(
" prfm pstl1keep, %[packed_tickets]\n"
"1:\n"
" ldaxr %w[tmp0], %[packed_tickets]\n"
" add %w[tmp0], %w[tmp0], #0x10000\n"
" stxr %w[tmp1], %w[tmp0], %[packed_tickets]\n"
" cbnz %w[tmp1], 1b\n"
" \n"
" and %w[tmp1], %w[tmp0], #0xFFFF\n"
" cmp %w[tmp1], %w[tmp0], lsr #16\n"
" b.eq done"
" sevl\n"
"2:\n"
" wfe\n"
" ldaxrh %w[tmp1], %[packed_tickets]\n"
" cmp %w[tmp1], %w[tmp0], lsr #16\n"
" b.ne 2b\n"
"done:\n"
: [tmp0]"=&r"(tmp0), [tmp1]"=&r"(tmp1), [packed_tickets]"+Q"(this->packed_tickets)
:
: "cc", "memory"
);
}
void Unlock() {
const u32 value = this->packed_tickets + 1;
__asm__ __volatile__(
" stlrh %w[value], %[packed_tickets]\n"
: [packed_tickets]"+Q"(this->packed_tickets)
: [value]"r"(value)
: "memory"
);
}
};
static_assert(sizeof(KNotAlignedSpinLock) == sizeof(u32));
class KAlignedSpinLock {
private:
alignas(cpu::DataCacheLineSize) u16 current_ticket;
alignas(cpu::DataCacheLineSize) u16 next_ticket;
public:
constexpr KAlignedSpinLock() : current_ticket(0), next_ticket(0) { /* ... */ }
void Lock() {
u32 tmp0, tmp1, got_lock;
__asm__ __volatile__(
" prfm pstl1keep, %[next_ticket]\n"
"1:\n"
" ldaxrh %w[tmp0], %[next_ticket]\n"
" add %w[tmp1], %w[tmp0], #0x1\n"
" stxrh %w[got_lock], %w[tmp1], %[next_ticket]\n"
" cbnz %w[got_lock], 1b\n"
" \n"
" sevl\n"
"2:\n"
" wfe\n"
" ldaxrh %w[tmp1], %[current_ticket]\n"
" cmp %w[tmp1], %w[tmp0]\n"
" b.ne 2b\n"
: [tmp0]"=&r"(tmp0), [tmp1]"=&r"(tmp1), [got_lock]"=&r"(got_lock), [next_ticket]"+Q"(this->next_ticket)
: [current_ticket]"Q"(this->current_ticket)
: "cc", "memory"
);
}
void Unlock() {
const u32 value = this->current_ticket + 1;
__asm__ __volatile__(
" stlrh %w[value], %[current_ticket]\n"
: [current_ticket]"+Q"(this->current_ticket)
: [value]"r"(value)
: "memory"
);
}
};
static_assert(sizeof(KAlignedSpinLock) == 2 * cpu::DataCacheLineSize);
using KSpinLock = KAlignedSpinLock;
}

View File

@@ -1,67 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_select_cpu.hpp>
#include <mesosphere/arch/arm64/kern_k_page_table.hpp>
namespace ams::kern::arch::arm64 {
class KSupervisorPageTable {
private:
KPageTable page_table;
u64 ttbr0[cpu::NumCores];
public:
constexpr KSupervisorPageTable() : page_table(), ttbr0() { /* ... */ }
NOINLINE void Initialize(s32 core_id);
void Activate() {
/* Activate, using process id = 0xFFFFFFFF */
this->page_table.Activate(0xFFFFFFFF);
}
void ActivateForInit() {
this->Activate();
/* Invalidate entire TLB. */
cpu::InvalidateEntireTlb();
}
void Finalize(s32 core_id);
Result MapPages(KProcessAddress *out_addr, size_t num_pages, size_t alignment, KPhysicalAddress phys_addr, KProcessAddress region_start, size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
return this->page_table.MapPages(out_addr, num_pages, alignment, phys_addr, region_start, region_num_pages, state, perm);
}
Result UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state) {
return this->page_table.UnmapPages(address, num_pages, state);
}
Result MapPageGroup(KProcessAddress *out_addr, const KPageGroup &pg, KProcessAddress region_start, size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
return this->page_table.MapPageGroup(out_addr, pg, region_start, region_num_pages, state, perm);
}
Result UnmapPageGroup(KProcessAddress address, const KPageGroup &pg, KMemoryState state) {
return this->page_table.UnmapPageGroup(address, pg, state);
}
bool GetPhysicalAddress(KPhysicalAddress *out, KProcessAddress address) const {
return this->page_table.GetPhysicalAddress(out, address);
}
};
}

View File

@@ -1,72 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_typed_address.hpp>
namespace ams::kern {
class KThread;
}
namespace ams::kern::arch::arm64 {
class KThreadContext {
public:
static constexpr size_t NumCalleeSavedRegisters = (29 - 19) + 1;
static constexpr size_t NumFpuRegisters = 32;
private:
union {
u64 registers[NumCalleeSavedRegisters];
struct {
u64 x19;
u64 x20;
u64 x21;
u64 x22;
u64 x23;
u64 x24;
u64 x25;
u64 x26;
u64 x27;
u64 x28;
u64 x29;
};
} callee_saved;
u64 lr;
u64 sp;
u64 cpacr;
u64 fpcr;
u64 fpsr;
alignas(0x10) u128 fpu_registers[NumFpuRegisters];
bool locked;
private:
static void RestoreFpuRegisters64(const KThreadContext &);
static void RestoreFpuRegisters32(const KThreadContext &);
public:
constexpr explicit KThreadContext() : callee_saved(), lr(), sp(), cpacr(), fpcr(), fpsr(), fpu_registers(), locked() { /* ... */ }
Result Initialize(KVirtualAddress u_pc, KVirtualAddress k_sp, KVirtualAddress u_sp, uintptr_t arg, bool is_user, bool is_64_bit, bool is_main);
Result Finalize();
void SetArguments(uintptr_t arg0, uintptr_t arg1);
static void FpuContextSwitchHandler(KThread *thread);
/* TODO: More methods (especially FPU management) */
};
}

View File

@@ -1,58 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
namespace ams::kern::arch::arm64 {
void UserspaceAccessFunctionAreaBegin();
class UserspaceAccess {
public:
static bool CopyMemoryFromUser(void *dst, const void *src, size_t size);
static bool CopyMemoryFromUserAligned32Bit(void *dst, const void *src, size_t size);
static bool CopyMemoryFromUserAligned64Bit(void *dst, const void *src, size_t size);
static bool CopyMemoryFromUserSize32Bit(void *dst, const void *src);
static s32 CopyStringFromUser(void *dst, const void *src, size_t size);
static bool CopyMemoryToUser(void *dst, const void *src, size_t size);
static bool CopyMemoryToUserAligned32Bit(void *dst, const void *src, size_t size);
static bool CopyMemoryToUserAligned64Bit(void *dst, const void *src, size_t size);
static bool CopyMemoryToUserSize32Bit(void *dst, const void *src);
static s32 CopyStringToUser(void *dst, const void *src, size_t size);
static bool ClearMemory(void *dst, size_t size);
static bool ClearMemoryAligned32Bit(void *dst, size_t size);
static bool ClearMemoryAligned64Bit(void *dst, size_t size);
static bool ClearMemorySize32Bit(void *dst);
static bool StoreDataCache(uintptr_t start, uintptr_t end);
static bool FlushDataCache(uintptr_t start, uintptr_t end);
static bool InvalidateDataCache(uintptr_t start, uintptr_t end);
static bool InvalidateInstructionCache(uintptr_t start, uintptr_t end);
static bool ReadIoMemory32Bit(void *dst, const void *src, size_t size);
static bool ReadIoMemory16Bit(void *dst, const void *src, size_t size);
static bool ReadIoMemory8Bit(void *dst, const void *src, size_t size);
static bool WriteIoMemory32Bit(void *dst, const void *src, size_t size);
static bool WriteIoMemory16Bit(void *dst, const void *src, size_t size);
static bool WriteIoMemory8Bit(void *dst, const void *src, size_t size);
};
void UserspaceAccessFunctionAreaEnd();
}

View File

@@ -1,59 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_page_group.hpp>
#include <mesosphere/kern_k_memory_manager.hpp>
#include <mesosphere/kern_select_page_table.hpp>
namespace ams::kern::board::nintendo::nx {
using KDeviceVirtualAddress = u64;
class KDevicePageTable {
private:
static constexpr size_t TableCount = 4;
private:
KVirtualAddress tables[TableCount];
u8 table_asids[TableCount];
u64 attached_device;
u32 attached_value;
u32 detached_value;
u32 hs_attached_value;
u32 hs_detached_value;
private:
static ALWAYS_INLINE KVirtualAddress GetHeapVirtualAddress(KPhysicalAddress addr) {
return KPageTable::GetHeapVirtualAddress(addr);
}
static ALWAYS_INLINE KPhysicalAddress GetHeapPhysicalAddress(KVirtualAddress addr) {
return KPageTable::GetHeapPhysicalAddress(addr);
}
static ALWAYS_INLINE KVirtualAddress GetPageTableVirtualAddress(KPhysicalAddress addr) {
return KPageTable::GetPageTableVirtualAddress(addr);
}
static ALWAYS_INLINE KPhysicalAddress GetPageTablePhysicalAddress(KVirtualAddress addr) {
return KPageTable::GetPageTablePhysicalAddress(addr);
}
public:
constexpr KDevicePageTable() : tables(), table_asids(), attached_device(), attached_value(), detached_value(), hs_attached_value(), hs_detached_value() { /* ... */ }
static void Initialize();
};
}

View File

@@ -1,71 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
namespace ams::kern::board::nintendo::nx {
class KSystemControl {
public:
class Init {
public:
/* Initialization. */
static size_t GetIntendedMemorySize();
static KPhysicalAddress GetKernelPhysicalBaseAddress(uintptr_t base_address);
static bool ShouldIncreaseThreadResourceLimit();
static void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg);
static size_t GetApplicationPoolSize();
static size_t GetAppletPoolSize();
static size_t GetMinimumNonSecureSystemPoolSize();
/* Randomness. */
static void GenerateRandomBytes(void *dst, size_t size);
static u64 GenerateRandomRange(u64 min, u64 max);
};
public:
/* Initialization. */
static NOINLINE void InitializePhase1();
static NOINLINE void InitializePhase2();
static NOINLINE u32 GetInitialProcessBinaryPool();
/* Randomness. */
static void GenerateRandomBytes(void *dst, size_t size);
static u64 GenerateRandomRange(u64 min, u64 max);
/* Privileged Access. */
static void ReadWriteRegisterPrivileged(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value);
static void ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value);
static ALWAYS_INLINE u32 ReadRegisterPrivileged(ams::svc::PhysicalAddress address) {
u32 v;
ReadWriteRegisterPrivileged(std::addressof(v), address, 0x00000000u, 0);
return v;
}
static ALWAYS_INLINE void WriteRegisterPrivileged(ams::svc::PhysicalAddress address, u32 value) {
u32 v;
ReadWriteRegisterPrivileged(std::addressof(v), address, 0xFFFFFFFFu, value);
}
/* Power management. */
static void SleepSystem();
static NORETURN void StopSystem();
/* User access. */
static void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args);
};
}

View File

@@ -13,25 +13,26 @@
* 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/psc/sf/psc_sf_i_pm_module.hpp>
namespace ams::psc::sf {
namespace ams::kern {
class IPmService : public ams::sf::IServiceObject {
protected:
enum class CommandId {
Initialize = 0,
class KSystemControl {
public:
class Init {
public:
/* Initialization. */
static KPhysicalAddress GetKernelPhysicalBaseAddress(uintptr_t base_address);
static bool ShouldIncreaseThreadResourceLimit();
/* Randomness. */
static void GenerateRandomBytes(void *dst, size_t size);
static u64 GenerateRandomRange(u64 min, u64 max);
};
public:
/* Actual commands. */
virtual Result Initialize(ams::sf::Out<std::shared_ptr<psc::sf::IPmModule>> out) = 0;
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(Initialize),
};
/* Panic. */
static NORETURN void StopSystem();
};
}

View File

@@ -17,21 +17,23 @@
#include <vapours.hpp>
#ifdef ATMOSPHERE_ARCH_ARM64
#include <mesosphere/init/kern_init_elf64.hpp>
namespace ams::kern::init::Elf {
using namespace ams::kern::init::Elf::Elf64;
enum RelocationType {
R_ARCHITECTURE_RELATIVE = 0x403, /* Real name R_AARCH64_RELATIVE */
};
}
#include "kern_init_elf64.hpp"
#else
#error "Unknown Architecture"
#endif
namespace ams::kern::init::Elf {
#ifdef ATMOSPHERE_ARCH_ARM64
using namespace ams::kern::init::Elf::Elf64;
enum RelocationType {
R_ARCHITECTURE_RELATIVE = 0x403, /* Real name R_AARCH64_RELATIVE */
};
#else
#error "Unknown Architecture"
#endif
/* API to apply relocations or call init array. */
void ApplyRelocations(uintptr_t base_address, const Dyn *dynamic);
void CallInitArrayFuncs(uintptr_t init_array_start, uintptr_t init_array_end);

View File

@@ -27,7 +27,7 @@ namespace ams::kern::init {
u32 rw_end_offset;
u32 bss_offset;
u32 bss_end_offset;
u32 ini_load_offset;
u32 ini_end_offset;
u32 dynamic_offset;
u32 init_array_offset;
u32 init_array_end_offset;

View File

@@ -14,16 +14,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#ifdef ATMOSPHERE_ARCH_ARM64
#include <mesosphere/arch/arm64/init/kern_k_init_page_table.hpp>
namespace ams::kern::init {
using ams::kern::arch::arm64::PageTableEntry;
using ams::kern::arch::arm64::init::KInitialPageTable;
using ams::kern::arch::arm64::init::KInitialPageAllocator;
}
#include "../arch/arm64/init/kern_k_init_page_table.hpp"
#else
#error "Unknown architecture for KInitialPageTable"
#endif

View File

@@ -1,46 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <mesosphere/kern_k_slab_heap.hpp>
namespace ams::kern::init {
struct KSlabResourceCounts {
size_t num_KProcess;
size_t num_KThread;
size_t num_KEvent;
size_t num_KInterruptEvent;
size_t num_KPort;
size_t num_KSharedMemory;
size_t num_KTransferMemory;
size_t num_KCodeMemory;
size_t num_KDeviceAddressSpace;
size_t num_KSession;
size_t num_KLightSession;
size_t num_KObjectName;
size_t num_KResourceLimit;
size_t num_KDebug;
};
NOINLINE void InitializeSlabResourceCounts();
const KSlabResourceCounts &GetSlabResourceCounts();
size_t CalculateTotalSlabHeapSize();
NOINLINE void InitializeKPageBufferSlabHeap();
NOINLINE void InitializeSlabHeaps();
}

View File

@@ -1,56 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::kern {
constexpr size_t PageSize = 4_KB;
enum TargetFirmware : u32 {
TargetFirmware_1_0_0 = ATMOSPHERE_TARGET_FIRMWARE_100,
TargetFirmware_2_0_0 = ATMOSPHERE_TARGET_FIRMWARE_200,
TargetFirmware_3_0_0 = ATMOSPHERE_TARGET_FIRMWARE_300,
TargetFirmware_4_0_0 = ATMOSPHERE_TARGET_FIRMWARE_400,
TargetFirmware_5_0_0 = ATMOSPHERE_TARGET_FIRMWARE_500,
TargetFirmware_6_0_0 = ATMOSPHERE_TARGET_FIRMWARE_600,
TargetFirmware_6_2_0 = ATMOSPHERE_TARGET_FIRMWARE_620,
TargetFirmware_7_0_0 = ATMOSPHERE_TARGET_FIRMWARE_700,
TargetFirmware_8_0_0 = ATMOSPHERE_TARGET_FIRMWARE_800,
TargetFirmware_8_1_0 = ATMOSPHERE_TARGET_FIRMWARE_810,
TargetFirmware_9_0_0 = ATMOSPHERE_TARGET_FIRMWARE_900,
TargetFirmware_9_1_0 = ATMOSPHERE_TARGET_FIRMWARE_910,
TargetFirmware_10_0_0 = ATMOSPHERE_TARGET_FIRMWARE_1000,
};
TargetFirmware GetTargetFirmware();
}
#if 1 || defined(AMS_BUILD_FOR_AUDITING)
#define MESOSPHERE_BUILD_FOR_AUDITING
#endif
#if defined(MESOSPHERE_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING)
#define MESOSPHERE_BUILD_FOR_DEBUGGING
#endif
#ifdef MESOSPHERE_BUILD_FOR_DEBUGGING
#define MESOSPHERE_ENABLE_ASSERTIONS
#define MESOSPHERE_ENABLE_DEBUG_PRINT
#endif
#include <mesosphere/svc/kern_svc_results.hpp>

View File

@@ -1,54 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
namespace ams::kern {
class KDebugLog {
private:
static NOINLINE void VSNPrintf(char *dst, const size_t dst_size, const char *format, ::std::va_list vl);
public:
static NOINLINE void Initialize();
static NOINLINE void Printf(const char *format, ...) __attribute__((format(printf, 1, 2)));
static NOINLINE void VPrintf(const char *format, ::std::va_list vl);
};
}
#ifndef MESOSPHERE_DEBUG_LOG_SELECTED
#ifdef ATMOSPHERE_BOARD_NINTENDO_NX
#define MESOSPHERE_DEBUG_LOG_USE_UART_C
#else
#error "Unknown board for Default Debug Log Source"
#endif
#define MESOSPHERE_DEBUG_LOG_SELECTED
#endif
#define MESOSPHERE_RELEASE_LOG(fmt, ...) ::ams::kern::KDebugLog::Printf((fmt), ## __VA_ARGS__)
#define MESOSPHERE_RELEASE_VLOG(fmt, vl) ::ams::kern::KDebugLog::VPrintf((fmt), (vl))
#ifdef MESOSPHERE_ENABLE_DEBUG_PRINT
#define MESOSPHERE_LOG(fmt, ...) MESOSPHERE_RELEASE_LOG((fmt), ## __VA_ARGS__)
#define MESOSPHERE_VLOG(fmt, vl) MESOSPHERE_RELEASE_VLOG((fmt), (vl))
#else
#define MESOSPHERE_LOG(fmt, ...) do { MESOSPHERE_UNUSED(fmt); MESOSPHERE_UNUSED(__VA_ARGS__); } while (0)
#define MESOSPHERE_VLOG(fmt, vl) do { MESOSPHERE_UNUSED(fmt); MESOSPHERE_UNUSED(vl); } while (0)
#endif

View File

@@ -14,8 +14,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_initial_process_reader.hpp>
#include <vapours.hpp>
#include "kern_panic.hpp"
namespace ams::kern {
@@ -29,10 +29,4 @@ namespace ams::kern {
u32 reserved;
};
NOINLINE void CopyInitialProcessBinaryToKernelMemory();
NOINLINE void CreateAndRunInitialProcesses();
u64 GetInitialProcessIdMin();
u64 GetInitialProcessIdMax();
}

View File

@@ -1,61 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_condition_variable.hpp>
namespace ams::kern {
class KAddressArbiter {
public:
using ThreadTree = KConditionVariable::ThreadTree;
private:
ThreadTree tree;
public:
constexpr KAddressArbiter() : tree() { /* ... */ }
Result SignalToAddress(uintptr_t addr, ams::svc::SignalType type, s32 value, s32 count) {
switch (type) {
case ams::svc::SignalType_Signal:
return this->Signal(addr, count);
case ams::svc::SignalType_SignalAndIncrementIfEqual:
return this->SignalAndIncrementIfEqual(addr, value, count);
case ams::svc::SignalType_SignalAndModifyByWaitingCountIfEqual:
return this->SignalAndModifyByWaitingCountIfEqual(addr, value, count);
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
}
}
Result WaitForAddress(uintptr_t addr, ams::svc::ArbitrationType type, s32 value, s64 timeout) {
switch (type) {
case ams::svc::ArbitrationType_WaitIfLessThan:
return this->WaitIfLessThan(addr, value, false, timeout);
case ams::svc::ArbitrationType_DecrementAndWaitIfLessThan:
return this->WaitIfLessThan(addr, value, true, timeout);
case ams::svc::ArbitrationType_WaitIfEqual:
return this->WaitIfEqual(addr, value, timeout);
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
}
}
private:
Result Signal(uintptr_t addr, s32 count);
Result SignalAndIncrementIfEqual(uintptr_t addr, s32 value, s32 count);
Result SignalAndModifyByWaitingCountIfEqual(uintptr_t addr, s32 value, s32 count);
Result WaitIfLessThan(uintptr_t addr, s32 value, bool decrement, s64 timeout);
Result WaitIfEqual(uintptr_t addr, s32 value, s64 timeout);
};
}

View File

@@ -1,51 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
namespace ams::kern {
struct KAddressSpaceInfo {
public:
enum Type {
Type_32Bit = 0,
Type_Small64Bit = 1,
Type_Large64Bit = 2,
Type_Heap = 3,
Type_Stack = 4,
Type_Alias = 5,
Type_Count,
};
private:
size_t bit_width;
size_t address;
size_t size;
Type type;
public:
static uintptr_t GetAddressSpaceStart(size_t width, Type type);
static size_t GetAddressSpaceSize(size_t width, Type type);
constexpr KAddressSpaceInfo(size_t bw, size_t a, size_t s, Type t) : bit_width(bw), address(a), size(s), type(t) { /* ... */ }
constexpr size_t GetWidth() const { return this->bit_width; }
constexpr size_t GetAddress() const { return this->address; }
constexpr size_t GetSize() const { return this->size; }
constexpr Type GetType() const { return this->type; }
};
}

View File

@@ -1,61 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_select_cpu.hpp>
namespace ams::kern {
class KAffinityMask {
private:
static constexpr u64 AllowedAffinityMask = (1ul << cpu::NumCores) - 1;
private:
u64 mask;
private:
static constexpr ALWAYS_INLINE u64 GetCoreBit(s32 core) {
MESOSPHERE_ASSERT(0 <= core && core < static_cast<s32>(cpu::NumCores));
return (1ul << core);
}
public:
constexpr ALWAYS_INLINE KAffinityMask() : mask(0) { MESOSPHERE_ASSERT_THIS(); }
constexpr ALWAYS_INLINE u64 GetAffinityMask() const { return this->mask; }
constexpr ALWAYS_INLINE void SetAffinityMask(u64 new_mask) {
MESOSPHERE_ASSERT((new_mask & ~AllowedAffinityMask) == 0);
this->mask = new_mask;
}
constexpr ALWAYS_INLINE bool GetAffinity(s32 core) const {
return this->mask & GetCoreBit(core);
}
constexpr ALWAYS_INLINE void SetAffinity(s32 core, bool set) {
MESOSPHERE_ASSERT(0 <= core && core < static_cast<s32>(cpu::NumCores));
if (set) {
this->mask |= GetCoreBit(core);
} else {
this->mask &= ~GetCoreBit(core);
}
}
constexpr ALWAYS_INLINE void SetAll() {
this->mask = AllowedAffinityMask;
}
};
}

View File

@@ -1,232 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_typed_address.hpp>
#include <mesosphere/kern_k_class_token.hpp>
namespace ams::kern {
class KProcess;
#define MESOSPHERE_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \
NON_COPYABLE(CLASS); \
NON_MOVEABLE(CLASS); \
private: \
friend class ::ams::kern::KClassTokenGenerator; \
static constexpr inline auto ObjectType = ::ams::kern::KClassTokenGenerator::ObjectType::CLASS; \
static constexpr inline const char * const TypeName = #CLASS; \
static constexpr inline ClassTokenType ClassToken() { return ::ams::kern::ClassToken<CLASS>; } \
public: \
using BaseClass = BASE_CLASS; \
static constexpr ALWAYS_INLINE TypeObj GetStaticTypeObj() { \
constexpr ClassTokenType Token = ClassToken(); \
return TypeObj(TypeName, Token); \
} \
static constexpr ALWAYS_INLINE const char *GetStaticTypeName() { return TypeName; } \
virtual TypeObj GetTypeObj() const { return GetStaticTypeObj(); } \
virtual const char *GetTypeName() { return GetStaticTypeName(); } \
private:
class KAutoObject {
protected:
class TypeObj {
private:
const char *name;
ClassTokenType class_token;
public:
constexpr explicit TypeObj(const char *n, ClassTokenType tok) : name(n), class_token(tok) { /* ... */ }
constexpr ALWAYS_INLINE const char *GetName() const { return this->name; }
constexpr ALWAYS_INLINE ClassTokenType GetClassToken() const { return this->class_token; }
constexpr ALWAYS_INLINE bool operator==(const TypeObj &rhs) {
return this->GetClassToken() == rhs.GetClassToken();
}
constexpr ALWAYS_INLINE bool operator!=(const TypeObj &rhs) {
return this->GetClassToken() != rhs.GetClassToken();
}
constexpr ALWAYS_INLINE bool IsDerivedFrom(const TypeObj &rhs) {
return (this->GetClassToken() | rhs.GetClassToken()) == this->GetClassToken();
}
};
private:
MESOSPHERE_AUTOOBJECT_TRAITS(KAutoObject, KAutoObject);
private:
std::atomic<u32> ref_count;
public:
static KAutoObject *Create(KAutoObject *ptr);
public:
constexpr ALWAYS_INLINE explicit KAutoObject() : ref_count(0) { MESOSPHERE_ASSERT_THIS(); }
virtual ~KAutoObject() { MESOSPHERE_ASSERT_THIS(); }
/* Destroy is responsible for destroying the auto object's resources when ref_count hits zero. */
virtual void Destroy() { MESOSPHERE_ASSERT_THIS(); }
/* Finalize is responsible for cleaning up resource, but does not destroy the object. */
virtual void Finalize() { MESOSPHERE_ASSERT_THIS(); }
virtual KProcess *GetOwner() const { return nullptr; }
u32 GetReferenceCount() const {
return this->ref_count;
}
ALWAYS_INLINE bool IsDerivedFrom(const TypeObj &rhs) const {
return this->GetTypeObj().IsDerivedFrom(rhs);
}
ALWAYS_INLINE bool IsDerivedFrom(const KAutoObject &rhs) const {
return this->IsDerivedFrom(rhs.GetTypeObj());
}
template<typename Derived>
ALWAYS_INLINE Derived DynamicCast() {
static_assert(std::is_pointer<Derived>::value);
using DerivedType = typename std::remove_pointer<Derived>::type;
if (AMS_LIKELY(this->IsDerivedFrom(DerivedType::GetStaticTypeObj()))) {
return static_cast<Derived>(this);
} else {
return nullptr;
}
}
template<typename Derived>
ALWAYS_INLINE const Derived DynamicCast() const {
static_assert(std::is_pointer<Derived>::value);
using DerivedType = typename std::remove_pointer<Derived>::type;
if (AMS_LIKELY(this->IsDerivedFrom(DerivedType::GetStaticTypeObj()))) {
return static_cast<Derived>(this);
} else {
return nullptr;
}
}
ALWAYS_INLINE bool Open() {
MESOSPHERE_ASSERT_THIS();
/* Atomically increment the reference count, only if it's positive. */
u32 cur_ref_count = this->ref_count.load(std::memory_order_acquire);
do {
if (AMS_UNLIKELY(cur_ref_count == 0)) {
return false;
}
MESOSPHERE_ABORT_UNLESS(cur_ref_count < cur_ref_count + 1);
} while (!this->ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count + 1, std::memory_order_relaxed));
return true;
}
ALWAYS_INLINE void Close() {
MESOSPHERE_ASSERT_THIS();
/* Atomically decrement the reference count, not allowing it to become negative. */
u32 cur_ref_count = this->ref_count.load(std::memory_order_acquire);
do {
MESOSPHERE_ABORT_UNLESS(cur_ref_count > 0);
} while (!this->ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1, std::memory_order_relaxed));
/* If ref count hits zero, destroy the object. */
if (cur_ref_count - 1 == 0) {
this->Destroy();
}
}
};
class KAutoObjectWithListContainer;
class KAutoObjectWithList : public KAutoObject {
private:
friend class KAutoObjectWithListContainer;
private:
util::IntrusiveRedBlackTreeNode list_node;
public:
static ALWAYS_INLINE int Compare(const KAutoObjectWithList &lhs, const KAutoObjectWithList &rhs) {
const u64 lid = lhs.GetId();
const u64 rid = rhs.GetId();
if (lid < rid) {
return -1;
} else if (lid > rid) {
return 1;
} else {
return 0;
}
}
public:
virtual u64 GetId() const {
return reinterpret_cast<u64>(this);
}
};
template<typename T>
class KScopedAutoObject {
static_assert(std::is_base_of<KAutoObject, T>::value);
NON_COPYABLE(KScopedAutoObject);
private:
T *obj;
private:
constexpr ALWAYS_INLINE void Swap(KScopedAutoObject &rhs) {
/* TODO: C++20 constexpr std::swap */
T *tmp = rhs.obj;
rhs.obj = this->obj;
this->obj = tmp;
}
public:
constexpr ALWAYS_INLINE KScopedAutoObject() : obj(nullptr) { /* ... */ }
constexpr ALWAYS_INLINE KScopedAutoObject(T *o) : obj(o) {
if (this->obj != nullptr) {
this->obj->Open();
}
}
ALWAYS_INLINE ~KScopedAutoObject() {
if (this->obj != nullptr) {
this->obj->Close();
}
this->obj = nullptr;
}
constexpr ALWAYS_INLINE KScopedAutoObject(KScopedAutoObject &&rhs) {
this->obj = rhs.obj;
rhs.obj = nullptr;
}
constexpr ALWAYS_INLINE KScopedAutoObject &operator=(KScopedAutoObject &&rhs) {
rhs.Swap(*this);
return *this;
}
constexpr ALWAYS_INLINE T *operator->() { return this->obj; }
constexpr ALWAYS_INLINE T &operator*() { return *this->obj; }
constexpr ALWAYS_INLINE void Reset(T *o) {
KScopedAutoObject(o).Swap(*this);
}
constexpr ALWAYS_INLINE T *GetPointerUnsafe() { return this->obj; }
constexpr ALWAYS_INLINE bool IsNull() const { return this->obj == nullptr; }
constexpr ALWAYS_INLINE bool IsNotNull() const { return this->obj != nullptr; }
};
}

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