Compare commits
201 Commits
access_log
...
0.14.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee5a095c1a | ||
|
|
3726def6ec | ||
|
|
29358dc593 | ||
|
|
a82914d58e | ||
|
|
0508b5d31b | ||
|
|
235c070a37 | ||
|
|
82f757459e | ||
|
|
b2b47da8c1 | ||
|
|
bc364029b2 | ||
|
|
ac9832c5ce | ||
|
|
b65707efd9 | ||
|
|
98ca5e2927 | ||
|
|
91bbdf2baf | ||
|
|
3fd4002bc9 | ||
|
|
5d462c626c | ||
|
|
8cd81b3092 | ||
|
|
5c4fbf5c67 | ||
|
|
3afd723b92 | ||
|
|
e1f3bb10a5 | ||
|
|
bea550ebce | ||
|
|
85f533e6c4 | ||
|
|
c9f8252577 | ||
|
|
325802e29d | ||
|
|
4c3c910774 | ||
|
|
b88e09de5b | ||
|
|
b143f1e05f | ||
|
|
3289b45408 | ||
|
|
91fa0d651f | ||
|
|
9bc2c0c0c5 | ||
|
|
96c3dfee14 | ||
|
|
51084c0837 | ||
|
|
0993ae0685 | ||
|
|
e4b30f4022 | ||
|
|
f6f43300e0 | ||
|
|
1ffe08672d | ||
|
|
f866f37cdc | ||
|
|
008bfc27f3 | ||
|
|
7352d87b20 | ||
|
|
9ddb4194b3 | ||
|
|
3f17a34b67 | ||
|
|
5de853b662 | ||
|
|
36a3909a24 | ||
|
|
8922bbd108 | ||
|
|
b6cb561c47 | ||
|
|
2db6760461 | ||
|
|
bb7f320f7f | ||
|
|
94a170c437 | ||
|
|
7cf0432569 | ||
|
|
39b22cee8c | ||
|
|
3fb3beeaff | ||
|
|
4bb9ef061a | ||
|
|
f70ee67753 | ||
|
|
787964f7e7 | ||
|
|
b1f38be3ae | ||
|
|
9dc3e025fc | ||
|
|
4ac94e9179 | ||
|
|
1448068b69 | ||
|
|
0af44d2875 | ||
|
|
a4d376cc0f | ||
|
|
840ab0785c | ||
|
|
583899ede3 | ||
|
|
2ad9927a88 | ||
|
|
893b046111 | ||
|
|
3a01fd1ebc | ||
|
|
1b9acc4a6a | ||
|
|
cbecda2a27 | ||
|
|
5ecc80a5f6 | ||
|
|
695b82b945 | ||
|
|
fd9b986938 | ||
|
|
9231646f33 | ||
|
|
418de7b0dc | ||
|
|
46935fea80 | ||
|
|
11d5353827 | ||
|
|
240e6227e9 | ||
|
|
1d4d637818 | ||
|
|
23eed522d3 | ||
|
|
c622539b95 | ||
|
|
d87a9f011c | ||
|
|
28aab09b5d | ||
|
|
3917ecad46 | ||
|
|
ab96255a5d | ||
|
|
185baa7c4d | ||
|
|
eecf59dd00 | ||
|
|
afe7e41de8 | ||
|
|
632a75eee7 | ||
|
|
7aa3120f60 | ||
|
|
81db43932d | ||
|
|
51311a7332 | ||
|
|
8759cb4da3 | ||
|
|
fb6e85b291 | ||
|
|
3cf793f87e | ||
|
|
3265927ed7 | ||
|
|
0c82709af4 | ||
|
|
058f223b97 | ||
|
|
43ad4eb794 | ||
|
|
dea1235e12 | ||
|
|
266001ded4 | ||
|
|
ea6000b372 | ||
|
|
4fca870f2f | ||
|
|
26df56cd87 | ||
|
|
5b8a20dbf7 | ||
|
|
94f9cc1626 | ||
|
|
36eb78a3ce | ||
|
|
1c5b58ce66 | ||
|
|
0bd14b7114 | ||
|
|
75e6999668 | ||
|
|
5fa59ec888 | ||
|
|
fae2daf77c | ||
|
|
8d507aa5a1 | ||
|
|
a0cc22302c | ||
|
|
01a7606f95 | ||
|
|
9c4c058307 | ||
|
|
9beb05da50 | ||
|
|
2ca6772475 | ||
|
|
0d3aa13f70 | ||
|
|
863515a3b5 | ||
|
|
fe035736ca | ||
|
|
cfddb75398 | ||
|
|
04f325cf5a | ||
|
|
b35380a942 | ||
|
|
93be2ffcba | ||
|
|
9f79710cb7 | ||
|
|
ef1763334b | ||
|
|
d9e9fbe3c2 | ||
|
|
ff022115ca | ||
|
|
3d2eb8e903 | ||
|
|
65b42497d7 | ||
|
|
46bd03c06d | ||
|
|
9482fafabd | ||
|
|
762db93f4a | ||
|
|
5c3cd7d610 | ||
|
|
48a38847c2 | ||
|
|
9382ff0939 | ||
|
|
f71ad5ac2c | ||
|
|
fef8a4918e | ||
|
|
72afe34cf9 | ||
|
|
51dd668c7c | ||
|
|
c01753370a | ||
|
|
33434e21ef | ||
|
|
18698bf1d3 | ||
|
|
57867d6ced | ||
|
|
35c1959131 | ||
|
|
a4e3eae4b6 | ||
|
|
84d8634938 | ||
|
|
095fc6b996 | ||
|
|
bc767742f7 | ||
|
|
e2e5a2ccc2 | ||
|
|
a8d4b10b17 | ||
|
|
3c8d8161cc | ||
|
|
4a7ce9dd75 | ||
|
|
ca9327a120 | ||
|
|
f37eda6b86 | ||
|
|
c72bdec328 | ||
|
|
4a767c9082 | ||
|
|
c8f71007ec | ||
|
|
4ff10f3ce0 | ||
|
|
d52655eaf1 | ||
|
|
1b429918de | ||
|
|
b29dc76b20 | ||
|
|
4db9d95958 | ||
|
|
2ec4ed3568 | ||
|
|
4e226b523e | ||
|
|
9fa6d12586 | ||
|
|
804aa0e55d | ||
|
|
9d57783aa8 | ||
|
|
a510a1138d | ||
|
|
4e667bc7c1 | ||
|
|
84b1be1d58 | ||
|
|
1b2203d102 | ||
|
|
4f12449acf | ||
|
|
f4fd4cbbb2 | ||
|
|
be98aaa185 | ||
|
|
7f4c6ae9e7 | ||
|
|
9503aae522 | ||
|
|
78da7422ae | ||
|
|
f52232f0f2 | ||
|
|
16c9c53a4a | ||
|
|
d0d8914f28 | ||
|
|
7400a8ff68 | ||
|
|
a2eb93fde8 | ||
|
|
1a0696f8a3 | ||
|
|
faad5609b9 | ||
|
|
ad551c3b55 | ||
|
|
c2408395d8 | ||
|
|
084dd3232e | ||
|
|
8d6e076b77 | ||
|
|
9bd6916646 | ||
|
|
f168542931 | ||
|
|
852c039f70 | ||
|
|
bffd553b80 | ||
|
|
2d9f5b6942 | ||
|
|
aa2d1e15ab | ||
|
|
0cac10a2f0 | ||
|
|
499f29d2f0 | ||
|
|
9efca346d9 | ||
|
|
9fde97cfdd | ||
|
|
94eb2195d3 | ||
|
|
b08ccd7341 | ||
|
|
0a53c74aad | ||
|
|
70e67da1e1 | ||
|
|
e2b17086d4 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -38,6 +38,9 @@
|
||||
*.x86_64
|
||||
*.hex
|
||||
|
||||
# Deko3d shaders
|
||||
*.dksh
|
||||
|
||||
# Switch Executables
|
||||
*.nso
|
||||
*.nro
|
||||
|
||||
5
Makefile
5
Makefile
@@ -64,6 +64,7 @@ dist-no-debug: all
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000036
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000037
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/010000000000003C
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000042
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/fatal_errors
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/config_templates
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/config
|
||||
@@ -91,11 +92,13 @@ dist-no-debug: all
|
||||
cp stratosphere/creport/creport.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000036/exefs.nsp
|
||||
cp stratosphere/ro/ro.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000037/exefs.nsp
|
||||
cp stratosphere/jpegdec/jpegdec.nsp atmosphere-$(AMSVER)/atmosphere/contents/010000000000003C/exefs.nsp
|
||||
cp stratosphere/pgl/pgl.nsp atmosphere-$(AMSVER)/atmosphere/contents/0100000000000042/exefs.nsp
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000032/flags
|
||||
touch atmosphere-$(AMSVER)/atmosphere/contents/0100000000000032/flags/boot2.flag
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/contents/0100000000000037/flags
|
||||
touch atmosphere-$(AMSVER)/atmosphere/contents/0100000000000037/flags/boot2.flag
|
||||
cp troposphere/reboot_to_payload/reboot_to_payload.nro atmosphere-$(AMSVER)/switch/reboot_to_payload.nro
|
||||
cp troposphere/daybreak/daybreak.nro atmosphere-$(AMSVER)/switch/daybreak.nro
|
||||
cd atmosphere-$(AMSVER); zip -r ../atmosphere-$(AMSVER).zip ./*; cd ../;
|
||||
rm -r atmosphere-$(AMSVER)
|
||||
mkdir out
|
||||
@@ -141,6 +144,8 @@ dist: dist-no-debug
|
||||
cp stratosphere/spl/spl.elf atmosphere-$(AMSVER)-debug/spl.elf
|
||||
cp stratosphere/erpt/erpt.elf atmosphere-$(AMSVER)-debug/erpt.elf
|
||||
cp stratosphere/jpegdec/jpegdec.elf atmosphere-$(AMSVER)-debug/jpegdec.elf
|
||||
cp stratosphere/pgl/pgl.elf atmosphere-$(AMSVER)-debug/pgl.elf
|
||||
cp troposphere/daybreak/daybreak.elf atmosphere-$(AMSVER)-debug/daybreak.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
|
||||
|
||||
@@ -1,4 +1,31 @@
|
||||
# Changelog
|
||||
## 0.14.1
|
||||
+ An issue was fixed in 0.14.0 that would cause a black screen on boot when the INI1's size was not aligned to 8 bytes.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
## 0.14.0
|
||||
+ An API (`ams:su`) was added to allow homebrew to safely install system upgrades or downgrades.
|
||||
+ This is a re-implementation of the logic that `ns` uses to install gamecard system updates.
|
||||
+ Nintendo (and now atmosphère) uses an installation process that can recover no matter where a failure occurs, which should significantly improve the safety of custom system update installation.
|
||||
+ Support was added to `exosphère` for running on Mariko hardware.
|
||||
+ **Please note**: Atmosphère still does not support Mariko, and should not be run on Mariko yet.
|
||||
+ Certain stratosphere components do not handle mariko-specific logic fully correctly yet, and may initialize or interact with hardware incorrectly.
|
||||
+ This will be fixed and support will be added over the remainder of the Summer.
|
||||
+ A homebrew application (`daybreak`) was added that uses the system updater API (with thanks to @Adubbz for both design and implementation).
|
||||
+ `daybreak` is included with atmosphère, and functions as a safer/more accurate equivalent to e.g. ChoiDujourNX.
|
||||
+ Upgrades/downgrades can be installed from a folder containing the update NCAs on the SD card.
|
||||
+ Because the update logic functions identically to Nintendo's, `daybreak` will be safe to use on Mariko when the rest of atmosphère has support.
|
||||
+ **Please note**: Daybreak requires that meta (.cnmt) NCAs have the correct extension `.cnmt.nca`.
|
||||
+ This is because gamecard system update logic uses extension to determine whether to mount the content.
|
||||
+ [Several](https://gist.github.com/HookedBehemoth/df36b5970e1c5b1b512ec7bdd9043c6e) [scripts](https://gist.github.com/antiKk/279966c27fdfd9c7fe63b4ae410f89c4) have been made by community members to automatically rename folders with incorrect extensions.
|
||||
+ A bug was fixed that would cause file-based emummc to throw an error (showing a hexdump) on boot.
|
||||
+ Major thanks to @hexkyz for tracking down and resolving this.
|
||||
+ A number of minor issues were resolved, including:
|
||||
+ fusee now prints information to the screen when an error occurs, instead of getting stuck trying to initialize the display.
|
||||
+ A race condition in Horizon was worked around that could prevent boot under certain circumstances.
|
||||
+ A bug was fixed that would cause atmosphère modules to open ten copies of certain filesystems instead of one.
|
||||
+ This could cause object exhaustion under certain circumstances.
|
||||
+ For those interested in atmosphère's future development plans, the project's [roadmap](https://github.com/Atmosphere-NX/Atmosphere/blob/ac9832c5ce7be5832f6d29f6564a9c03e7efd22f/docs/roadmap.md) was updated.
|
||||
+ General system stability improvements to enhance the user's experience.
|
||||
## 0.13.0
|
||||
+ `exosphère`, atmosphère's secure monitor re-implementation, was completely re-written.
|
||||
+ `exosphère` was the first component authored for the project in early 2018. It is written in C, and in a style very different from the rest of atmosphère's code.
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
# Planned Features
|
||||
atmosphère has a number of features that are either works-in-progress or planned. Please note that while time-estimates are given, they are loose, and things may be completed sooner or later than advertised.
|
||||
|
||||
The following descriptions were last updated on June 29th, 2020.
|
||||
|
||||
## system updater homebrew
|
||||
* **Description**: A user homebrew making use of the new system updater api, so that users can actually use the new api in practice.
|
||||
* **Development Status**: Initial revision complete, pending re-write by Adubbz.
|
||||
* **Completion Time**: June-July 2020
|
||||
The following descriptions were last updated on July 7th, 2020.
|
||||
|
||||
## ams-on-mariko
|
||||
* **Description**: Atmosphere cannot run as-is on Mariko hardware. A large number of changes are needed in many components. Although secure monitor support is complete in exosphere, additional work is needed on the bootloader and stratosphere sides as well. Mariko support will also require further design thought; atmosphere's debugging design heavily relies on reboot-to-payload and (more generally) the ability to perform warmboot bootrom hax at will. This is not possible on Mariko, and will require a new design/software support for whatever solution is chosen.
|
||||
@@ -20,7 +15,7 @@ The following descriptions were last updated on June 29th, 2020.
|
||||
|
||||
## mesosphere
|
||||
* **Description**: mesosphère is a reimplementation of the Horizon operating system's Kernel. It aims to provide an open-source reference for Nintendo's code.
|
||||
* **Development Status**: Under semi-active development by SciresM; temporarily on pause while the System Updater API is completed.
|
||||
* **Development Status**: Under active development by SciresM.
|
||||
* **Estimated Time**: Mid-to-Late 2020
|
||||
|
||||
## tma reimplementation
|
||||
@@ -54,6 +49,10 @@ The following features were previously included under the planned features secti
|
||||
|
||||
Please note that this is not an exhaustive list of features present in atmosphère, and only serves to indicate what from the above has been completed.
|
||||
|
||||
## system updater homebrew
|
||||
* **Description**: A user homebrew making use of the new system updater api, so that users can actually use the new api in practice.
|
||||
* **Completion Time**: July 2020
|
||||
|
||||
## system updater api
|
||||
* **Description**: A planned extension api for stratosphere (tenatively `ams:su`), this will provide an interface for homebrew to safely install system upgrades or downgrades. This will allow for much more easily transitioning safely between different versions of the operating system.
|
||||
* **Completion Time**: June 2020
|
||||
|
||||
@@ -48,7 +48,12 @@ namespace ams::secmon::boot {
|
||||
const auto pmc = MemoryRegionVirtualDevicePmc.GetAddress();
|
||||
|
||||
/* Set the physical address of the warmboot binary to scratch 1. */
|
||||
reg::Write(pmc + APBDEV_PMC_SCRATCH1, static_cast<u32>(MemoryRegionPhysicalDramSecureDataStoreWarmbootFirmware.GetAddress()));
|
||||
if (GetSocType() == fuse::SocType_Mariko) {
|
||||
reg::Write(pmc + APBDEV_PMC_SECURE_SCRATCH119, static_cast<u32>(MemoryRegionPhysicalDramSecureDataStoreWarmbootFirmware.GetAddress()));
|
||||
} else /* if (GetSocType() == fuse::SocType_Erista) */ {
|
||||
reg::Write(pmc + APBDEV_PMC_SCRATCH1, static_cast<u32>(MemoryRegionPhysicalDramSecureDataStoreWarmbootFirmware.GetAddress()));
|
||||
}
|
||||
|
||||
|
||||
/* Configure logging by setting bits 18-19 of scratch 20. */
|
||||
reg::ReadWrite(pmc + APBDEV_PMC_SCRATCH20, REG_BITS_VALUE(18, 2, 0));
|
||||
@@ -66,6 +71,7 @@ namespace ams::secmon::boot {
|
||||
/* The warmboot key as a parameter. The latter is a better solution, but it would be nice to take */
|
||||
/* care of it here. Perhaps we should read the number of anti-downgrade fuses burnt, and translate that */
|
||||
/* to the warmboot key? To be decided during the process of implementing ams-on-mariko support. */
|
||||
reg::Write(pmc + APBDEV_PMC_SECURE_SCRATCH32, 0x129);
|
||||
}
|
||||
|
||||
constinit const u8 DeviceMasterKeySourceKekSource[se::AesBlockSize] = {
|
||||
@@ -92,6 +98,10 @@ namespace ams::secmon::boot {
|
||||
0xF9, 0x37, 0xCF, 0x9A, 0xBD, 0x86, 0xBB, 0xA9, 0x9C, 0x9E, 0x03, 0xC4, 0xFC, 0xBC, 0x3B, 0xCE
|
||||
};
|
||||
|
||||
constinit const u8 MasterKeySource[se::AesBlockSize] = {
|
||||
0xD8, 0xA2, 0x41, 0x0A, 0xC6, 0xC5, 0x90, 0x01, 0xC6, 0x1D, 0x6A, 0x26, 0x7C, 0x51, 0x3F, 0x3C
|
||||
};
|
||||
|
||||
void DeriveMasterKekAndDeviceKeyMariko(bool is_prod) {
|
||||
/* Clear all keyslots other than KEK and SBK in SE1. */
|
||||
for (int i = 0; i < pkg1::AesKeySlot_Count; ++i) {
|
||||
@@ -123,6 +133,14 @@ namespace ams::secmon::boot {
|
||||
}
|
||||
}
|
||||
|
||||
void DeriveMasterKey() {
|
||||
if (GetSocType() == fuse::SocType_Mariko) {
|
||||
se::SetEncryptedAesKey128(pkg1::AesKeySlot_Master, pkg1::AesKeySlot_MasterKek, MasterKeySource, se::AesBlockSize);
|
||||
} else /* if (GetSocType() == fuse::SocType_Erista) */ {
|
||||
/* Nothing to do here; erista bootloader will have derived master key already. */
|
||||
}
|
||||
}
|
||||
|
||||
void SetupRandomKey(int slot, se::KeySlotLockFlags flags) {
|
||||
/* Create an aligned buffer to hold the key. */
|
||||
constexpr size_t KeySize = se::AesBlockSize;
|
||||
@@ -312,6 +330,9 @@ namespace ams::secmon::boot {
|
||||
/* Derive the master keys. */
|
||||
DeriveAllMasterKeys(is_prod, work_block);
|
||||
|
||||
/* Lock the master key as a kek. */
|
||||
se::LockAesKeySlot(pkg1::AesKeySlot_Master, se::KeySlotLockFlags_AllLockKek);
|
||||
|
||||
/* Derive the device master keys. */
|
||||
DeriveAllDeviceMasterKeys(is_prod, work_block);
|
||||
|
||||
@@ -357,7 +378,10 @@ namespace ams::secmon::boot {
|
||||
/* Lock the device key as only usable as a kek. */
|
||||
se::LockAesKeySlot(pkg1::AesKeySlot_Device, se::KeySlotLockFlags_AllLockKek);
|
||||
|
||||
/* Derive all keys. */
|
||||
/* Derive the master key. */
|
||||
DeriveMasterKey();
|
||||
|
||||
/* Derive all other keys. */
|
||||
DeriveAllKeys(is_prod);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,10 +16,24 @@
|
||||
#include <exosphere.hpp>
|
||||
#include "secmon_error.hpp"
|
||||
|
||||
namespace {
|
||||
namespace ams {
|
||||
|
||||
constexpr bool SaveSystemStateForDebug = false;
|
||||
namespace {
|
||||
|
||||
constexpr bool SaveSystemStateForDebug = false;
|
||||
constexpr bool LogSystemStateForDebug = false;
|
||||
|
||||
void LogU64(u64 value) {
|
||||
char buffer[2 * sizeof(value)];
|
||||
for (size_t i = 0; i < sizeof(value); ++i) {
|
||||
buffer[sizeof(buffer) - 1 - (2 * i) - 0] = "0123456789ABCDEF"[(value >> 0) & 0xF];
|
||||
buffer[sizeof(buffer) - 1 - (2 * i) - 1] = "0123456789ABCDEF"[(value >> 4) & 0xF];
|
||||
value >>= 8;
|
||||
}
|
||||
log::SendText(buffer, sizeof(buffer));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
namespace ams::diag {
|
||||
@@ -98,6 +112,57 @@ namespace ams::secmon {
|
||||
util::WaitMicroSeconds(1000);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void LogSystemStateForDebugErrorReboot(u64 lr, u64 sp) {
|
||||
log::SendText("*** Error Reboot ***\n", 21);
|
||||
log::Flush();
|
||||
|
||||
u64 temp_reg;
|
||||
|
||||
__asm__ __volatile__("mrs %0, esr_el3" : "=r"(temp_reg) :: "memory");
|
||||
log::SendText("ESR_EL3: ", 9);
|
||||
LogU64(temp_reg);
|
||||
log::SendText("\n", 1);
|
||||
log::Flush();
|
||||
|
||||
__asm__ __volatile__("mrs %0, elr_el3" : "=r"(temp_reg) :: "memory");
|
||||
log::SendText("ELR_EL3: ", 9);
|
||||
LogU64(temp_reg);
|
||||
log::SendText("\n", 1);
|
||||
log::Flush();
|
||||
|
||||
__asm__ __volatile__("mrs %0, far_el3" : "=r"(temp_reg) :: "memory");
|
||||
log::SendText("FAR_EL3: ", 9);
|
||||
LogU64(temp_reg);
|
||||
log::SendText("\n", 1);
|
||||
log::Flush();
|
||||
|
||||
log::SendText("LR: ", 9);
|
||||
LogU64(lr);
|
||||
log::SendText("\n", 1);
|
||||
log::Flush();
|
||||
|
||||
log::SendText("SP: ", 9);
|
||||
LogU64(sp);
|
||||
log::SendText("\n", 1);
|
||||
log::Flush();
|
||||
|
||||
log::SendText("Stack:\n", 7);
|
||||
log::Flush();
|
||||
|
||||
char buf[2];
|
||||
for (int i = 0; i < 0x100; ++i) {
|
||||
const u8 byte = *(volatile u8 *)(sp + i);
|
||||
buf[0] = "0123456789ABCDEF"[(byte >> 4) & 0xF];
|
||||
buf[1] = "0123456789ABCDEF"[(byte >> 0) & 0xF];
|
||||
log::SendText(buf, 2);
|
||||
log::Flush();
|
||||
if (util::IsAligned(i + 1, 0x10)) {
|
||||
log::SendText("\n", 1);
|
||||
log::Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SetError(pkg1::ErrorInfo info) {
|
||||
@@ -114,6 +179,14 @@ namespace ams::secmon {
|
||||
SaveSystemStateForDebugErrorReboot();
|
||||
}
|
||||
|
||||
if constexpr (LogSystemStateForDebug) {
|
||||
u64 lr, sp;
|
||||
__asm__ __volatile__("mov %0, lr" : "=r"(lr) :: "memory");
|
||||
__asm__ __volatile__("mov %0, sp" : "=r"(sp) :: "memory");
|
||||
|
||||
LogSystemStateForDebugErrorReboot(lr, sp);
|
||||
}
|
||||
|
||||
/* Lockout the security engine. */
|
||||
se::Lockout();
|
||||
|
||||
|
||||
@@ -51,27 +51,27 @@ namespace ams::secmon {
|
||||
|
||||
constinit bool g_is_cold_boot = true;
|
||||
|
||||
constinit const se::StickyBits ExpectedSeStickyBits = {
|
||||
constinit se::StickyBits ExpectedSeStickyBits = {
|
||||
.se_security = (1 << 0), /* SE_HARD_SETTING */
|
||||
.tzram_security = 0,
|
||||
.crypto_security_perkey = (1 << pkg1::AesKeySlot_UserEnd) - 1,
|
||||
.crypto_keytable_access = {
|
||||
(1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 0: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */
|
||||
(1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 1: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */
|
||||
(1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 2: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */
|
||||
(1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 3: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */
|
||||
(1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 4: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */
|
||||
(1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 5: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */
|
||||
(0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 6: Unused keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */
|
||||
(0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 7: Unused keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */
|
||||
(0 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 8: Temp keyslot. UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. KEYUSE, UIVREAD, OIVREAD, KEYREAD disabled. */
|
||||
(0 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 9: SmcTemp keyslot. UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. KEYUSE, UIVREAD, OIVREAD, KEYREAD disabled. */
|
||||
(0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 10: Wrap1 keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */
|
||||
(0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 11: Wrap2 keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */
|
||||
(0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 12: DMaster keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */
|
||||
(0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 13: Master keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */
|
||||
(0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 14: Unused keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */
|
||||
(0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 13: Device keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */
|
||||
(0 << 7) | (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 0: User keyslot. KEY. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */
|
||||
(0 << 7) | (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 1: User keyslot. KEY. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */
|
||||
(0 << 7) | (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 2: User keyslot. KEY. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */
|
||||
(0 << 7) | (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 3: User keyslot. KEY. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */
|
||||
(0 << 7) | (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 4: User keyslot. KEY. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */
|
||||
(0 << 7) | (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 5: User keyslot. KEY. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */
|
||||
(1 << 7) | (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 6: Unused keyslot. KEK. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */
|
||||
(1 << 7) | (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 7: Unused keyslot. KEK. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */
|
||||
(0 << 7) | (0 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 8: Temp keyslot. KEY. UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. KEYUSE, UIVREAD, OIVREAD, KEYREAD disabled. */
|
||||
(0 << 7) | (0 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 9: SmcTemp keyslot. KEY. UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. KEYUSE, UIVREAD, OIVREAD, KEYREAD disabled. */
|
||||
(1 << 7) | (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 10: Wrap1 keyslot. KEK. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */
|
||||
(0 << 7) | (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 11: Wrap2 keyslot. KEY. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */
|
||||
(1 << 7) | (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 12: DMaster keyslot. KEK. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */
|
||||
(1 << 7) | (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 13: Master keyslot. KEK. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */
|
||||
(1 << 7) | (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 14: Unused keyslot. KEK. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */
|
||||
(1 << 7) | (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 13: Device keyslot. KEK. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */
|
||||
},
|
||||
.rsa_security_perkey = 0,
|
||||
.rsa_keytable_access = {
|
||||
@@ -139,6 +139,16 @@ namespace ams::secmon {
|
||||
}
|
||||
|
||||
void VerifySecurityEngineStickyBits() {
|
||||
/* On mariko, an extra sticky bit is set. */
|
||||
if (GetSocType() == fuse::SocType_Mariko) {
|
||||
ExpectedSeStickyBits.se_security |= (1 << 5);
|
||||
} else /* if (GetSocType() == fuse::SocType_Erista) */ {
|
||||
/* Erista does not support DST_KEYTABLE_ONLY, and so all keys will have the bit clear. */
|
||||
for (size_t i = 0; i < util::size(ExpectedSeStickyBits.crypto_keytable_access); ++i) {
|
||||
ExpectedSeStickyBits.crypto_keytable_access[i] &= ~(1 << 7);
|
||||
}
|
||||
}
|
||||
|
||||
if (!se::ValidateStickyBits(ExpectedSeStickyBits)) {
|
||||
SetError(pkg1::ErrorInfo_InvalidSecurityEngineStickyBits);
|
||||
AMS_ABORT("Invalid sticky bits");
|
||||
@@ -938,12 +948,16 @@ namespace ams::secmon {
|
||||
return reg::Read(MC + MC_SECURITY_CFG3) == 0;
|
||||
}
|
||||
|
||||
void SetupLogForBoot() {
|
||||
log::Initialize();
|
||||
log::SendText("OHAYO\n", 6);
|
||||
log::Flush();
|
||||
}
|
||||
|
||||
void LogExitLp0() {
|
||||
/* NOTE: Nintendo only does this on dev, but we will always do it. */
|
||||
if (true /* !pkg1::IsProduction() */) {
|
||||
log::Initialize();
|
||||
log::SendText("OHAYO\n", 6);
|
||||
log::Flush();
|
||||
SetupLogForBoot();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -969,7 +983,7 @@ namespace ams::secmon {
|
||||
InitializeConfigurationContext();
|
||||
|
||||
/* Initialize uart for logging. */
|
||||
log::Initialize();
|
||||
SetupLogForBoot();
|
||||
|
||||
/* Initialize the security engine. */
|
||||
se::Initialize();
|
||||
@@ -1017,12 +1031,16 @@ namespace ams::secmon {
|
||||
|
||||
/* Overwrite keys that we want to be random with random contents. */
|
||||
se::InitializeRandom();
|
||||
se::ConfigureAutomaticContextSave();
|
||||
se::SetRandomKey(pkg1::AesKeySlot_Temporary);
|
||||
se::GenerateSrk();
|
||||
se::SetRandomKey(pkg1::AesKeySlot_TzramSaveKek);
|
||||
|
||||
/* Initialize pmc secure scratch. */
|
||||
pmc::InitializeRandomScratch();
|
||||
if (GetSocType() == fuse::SocType_Erista) {
|
||||
pmc::InitializeRandomScratch();
|
||||
}
|
||||
pmc::LockSecureRegister(pmc::SecureRegister_Srk);
|
||||
|
||||
/* Setup secure registers. */
|
||||
SetupSecureRegisters();
|
||||
|
||||
@@ -262,6 +262,11 @@ namespace ams::secmon::smc {
|
||||
/* Get whether this unit should allow writing to the calibration partition. */
|
||||
args.r[1] = (GetEmummcConfiguration().IsEmummcActive() || GetSecmonConfiguration().AllowWritingToCalibrationBinarySysmmc());
|
||||
break;
|
||||
case ConfigItem::ExosphereEmummcType:
|
||||
/* Get what kind of emummc this unit has active. */
|
||||
/* NOTE: This may return values other than 1 in the future. */
|
||||
args.r[1] = (GetEmummcConfiguration().IsEmummcActive() ? 1 : 0);
|
||||
break;
|
||||
default:
|
||||
return SmcResult::InvalidArgument;
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ namespace ams::secmon::smc {
|
||||
ExosphereHasRcmBugPatch = 65004,
|
||||
ExosphereBlankProdInfo = 65005,
|
||||
ExosphereAllowCalWrites = 65006,
|
||||
ExosphereEmummcType = 65007,
|
||||
};
|
||||
|
||||
SmcResult SmcGetConfigUser(SmcArguments &args);
|
||||
|
||||
@@ -3,7 +3,11 @@ import sys, lz4
|
||||
from struct import unpack as up
|
||||
|
||||
def lz4_compress(data):
|
||||
return lz4.block.compress(data, 'high_compression', store_size=False)
|
||||
try:
|
||||
import lz4.block as block
|
||||
except ImportError:
|
||||
block = lz4.LZ4_compress
|
||||
return block.compress(data, 'high_compression', store_size=False)
|
||||
|
||||
def split_binary(data):
|
||||
A, B, START, BOOT_CODE_START, BOOT_CODE_END, PROGRAM_START, C, D = up('<QQQQQQQQ', data[:0x40])
|
||||
@@ -19,7 +23,7 @@ def split_binary(data):
|
||||
|
||||
def main(argc, argv):
|
||||
if argc != 3:
|
||||
print 'Usage: %s in outdir' % argv[0]
|
||||
print('Usage: %s in outdir' % argv[0])
|
||||
return 1
|
||||
with open(argv[1], 'rb') as f:
|
||||
data = f.read()
|
||||
@@ -30,4 +34,4 @@ def main(argc, argv):
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(len(sys.argv), sys.argv))
|
||||
sys.exit(main(len(sys.argv), sys.argv))
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#ifndef FUSEE_LOG_H
|
||||
#define FUSEE_LOG_H
|
||||
|
||||
#define PRINT_MESSAGE_MAX_LENGTH 512
|
||||
#define PRINT_MESSAGE_MAX_LENGTH 1024
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
|
||||
@@ -17,17 +17,26 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include "utils.h"
|
||||
#include "display/video_fb.h"
|
||||
#include "lib/log.h"
|
||||
|
||||
__attribute__ ((noreturn)) void generic_panic(void) {
|
||||
print(SCREEN_LOG_LEVEL_ERROR, "Panic raised!");
|
||||
|
||||
while (true) {
|
||||
/* Lock. */
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void fatal_error(const char *fmt, ...) {
|
||||
/* Forcefully initialize the screen if logging is disabled. */
|
||||
if (log_get_log_level() == SCREEN_LOG_LEVEL_NONE) {
|
||||
/* Zero-fill the framebuffer and register it as printk provider. */
|
||||
video_init((void *)0xC0000000);
|
||||
|
||||
/* Override the global logging level. */
|
||||
log_set_log_level(SCREEN_LOG_LEVEL_ERROR);
|
||||
}
|
||||
|
||||
/* Display fatal error. */
|
||||
va_list args;
|
||||
print(SCREEN_LOG_LEVEL_ERROR, "Fatal error: ");
|
||||
va_start(args, fmt);
|
||||
|
||||
@@ -19,9 +19,10 @@
|
||||
#include "exception_handlers.h"
|
||||
#include "utils.h"
|
||||
#include "lib/log.h"
|
||||
#include "lib/vsprintf.h"
|
||||
|
||||
#define CODE_DUMP_SIZE 0x30
|
||||
#define STACK_DUMP_SIZE 0x60
|
||||
#define STACK_DUMP_SIZE 0x30
|
||||
|
||||
extern const uint32_t exception_handler_table[];
|
||||
|
||||
@@ -34,6 +35,40 @@ static const char *register_names[] = {
|
||||
"SP", "LR", "PC", "CPSR",
|
||||
};
|
||||
|
||||
/* Adapted from https://gist.github.com/ccbrown/9722406 */
|
||||
static void hexdump(const void* data, size_t size, uintptr_t addrbase, char* strbuf) {
|
||||
const uint8_t *d = (const uint8_t *)data;
|
||||
char ascii[17] = {0};
|
||||
ascii[16] = '\0';
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
if (i % 16 == 0) {
|
||||
strbuf += sprintf(strbuf, "%0*" PRIXPTR ": | ", 2 * sizeof(addrbase), addrbase + i);
|
||||
}
|
||||
strbuf += sprintf(strbuf, "%02X ", d[i]);
|
||||
if (d[i] >= ' ' && d[i] <= '~') {
|
||||
ascii[i % 16] = d[i];
|
||||
} else {
|
||||
ascii[i % 16] = '.';
|
||||
}
|
||||
if ((i+1) % 8 == 0 || i+1 == size) {
|
||||
strbuf += sprintf(strbuf, " ");
|
||||
if ((i+1) % 16 == 0) {
|
||||
strbuf += sprintf(strbuf, "| %s \n", ascii);
|
||||
} else if (i+1 == size) {
|
||||
ascii[(i+1) % 16] = '\0';
|
||||
if ((i+1) % 16 <= 8) {
|
||||
strbuf += sprintf(strbuf, " ");
|
||||
}
|
||||
for (size_t j = (i+1) % 16; j < 16; j++) {
|
||||
strbuf += sprintf(strbuf, " ");
|
||||
}
|
||||
strbuf += sprintf(strbuf, "| %s \n", ascii);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setup_exception_handlers(void) {
|
||||
volatile uint32_t *bpmp_exception_handler_table = (volatile uint32_t *)0x6000F200;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
@@ -44,38 +79,40 @@ void setup_exception_handlers(void) {
|
||||
}
|
||||
|
||||
void exception_handler_main(uint32_t *registers, unsigned int exception_type) {
|
||||
uint8_t code_dump[CODE_DUMP_SIZE];
|
||||
uint8_t stack_dump[STACK_DUMP_SIZE];
|
||||
size_t code_dump_size;
|
||||
size_t stack_dump_size;
|
||||
char exception_log[0x400] = {0};
|
||||
uint8_t code_dump[CODE_DUMP_SIZE] = {0};
|
||||
uint8_t stack_dump[STACK_DUMP_SIZE] = {0};
|
||||
size_t code_dump_size = 0;
|
||||
size_t stack_dump_size = 0;
|
||||
|
||||
uint32_t pc = registers[15];
|
||||
uint32_t cpsr = registers[16];
|
||||
|
||||
uint32_t instr_addr = pc + ((cpsr & 0x20) ? 2 : 4) - CODE_DUMP_SIZE;
|
||||
|
||||
print(SCREEN_LOG_LEVEL_ERROR, "\nSomething went wrong...\n");
|
||||
|
||||
sprintf(exception_log, "An exception occured!\n");
|
||||
|
||||
code_dump_size = safecpy(code_dump, (const void *)instr_addr, CODE_DUMP_SIZE);
|
||||
stack_dump_size = safecpy(stack_dump, (const void *)registers[13], STACK_DUMP_SIZE);
|
||||
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "\nException type: %s\n",
|
||||
exception_names[exception_type]);
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "\nRegisters:\n\n");
|
||||
sprintf(exception_log + strlen(exception_log), "\nException type: %s\n", exception_names[exception_type]);
|
||||
sprintf(exception_log + strlen(exception_log), "\nRegisters:\n");
|
||||
|
||||
/* Print r0 to pc. */
|
||||
for (int i = 0; i < 16; i += 2) {
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "%-7s%08"PRIX32" %-7s%08"PRIX32"\n",
|
||||
sprintf(exception_log + strlen(exception_log), "%-7s%08"PRIX32" %-7s%08"PRIX32"\n",
|
||||
register_names[i], registers[i], register_names[i+1], registers[i+1]);
|
||||
}
|
||||
|
||||
/* Print cpsr. */
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "%-7s%08"PRIX32"\n", register_names[16], registers[16]);
|
||||
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "\nCode dump:\n");
|
||||
hexdump(code_dump, code_dump_size, instr_addr);
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "\nStack dump:\n");
|
||||
hexdump(stack_dump, stack_dump_size, registers[13]);
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "\n");
|
||||
fatal_error("An exception occured!\n");
|
||||
sprintf(exception_log + strlen(exception_log), "%-7s%08"PRIX32"\n", register_names[16], registers[16]);
|
||||
|
||||
/* Print code and stack regions. */
|
||||
sprintf(exception_log + strlen(exception_log), "\nCode dump:\n");
|
||||
hexdump(code_dump, code_dump_size, instr_addr, exception_log + strlen(exception_log));
|
||||
sprintf(exception_log + strlen(exception_log), "\nStack dump:\n");
|
||||
hexdump(stack_dump, stack_dump_size, registers[13], exception_log + strlen(exception_log));
|
||||
sprintf(exception_log + strlen(exception_log), "\n");
|
||||
|
||||
/* Throw fatal error with the full exception log. */
|
||||
fatal_error(exception_log);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#ifndef FUSEE_LOG_H
|
||||
#define FUSEE_LOG_H
|
||||
|
||||
#define PRINT_MESSAGE_MAX_LENGTH 512
|
||||
#define PRINT_MESSAGE_MAX_LENGTH 1024
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
|
||||
@@ -42,6 +42,8 @@ static const char *get_error_desc_str(uint32_t error_desc) {
|
||||
return "SError";
|
||||
case 0x301:
|
||||
return "Bad SVC";
|
||||
case 0xFFD:
|
||||
return "Stack overflow";
|
||||
case 0xFFE:
|
||||
return "std::abort() called";
|
||||
default:
|
||||
|
||||
@@ -106,7 +106,7 @@ void load_stage2(const char *bct0) {
|
||||
}
|
||||
|
||||
if (strlen(config.path) + 1 + sizeof(stage2_args_t) > CHAINLOADER_ARG_DATA_MAX_SIZE) {
|
||||
print(SCREEN_LOG_LEVEL_ERROR, "Stage2's path name is too big!\n");
|
||||
fatal_error("Stage2's path name is too big!\n");
|
||||
}
|
||||
|
||||
if (!check_32bit_address_loadable(config.entrypoint)) {
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "car.h"
|
||||
#include "btn.h"
|
||||
#include "lib/log.h"
|
||||
#include "lib/vsprintf.h"
|
||||
#include "display/video_fb.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
@@ -112,11 +113,11 @@ __attribute__((noreturn)) void fatal_error(const char *fmt, ...) {
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* Override the global logging level. */
|
||||
log_set_log_level(SCREEN_LOG_LEVEL_ERROR);
|
||||
|
||||
/* Display fatal error. */
|
||||
va_list args;
|
||||
print(SCREEN_LOG_LEVEL_ERROR, "Fatal error: ");
|
||||
@@ -137,37 +138,3 @@ __attribute__((noinline)) bool overlaps(uint64_t as, uint64_t ae, uint64_t bs, u
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Adapted from https://gist.github.com/ccbrown/9722406 */
|
||||
void hexdump(const void* data, size_t size, uintptr_t addrbase) {
|
||||
const uint8_t *d = (const uint8_t *)data;
|
||||
char ascii[17];
|
||||
ascii[16] = '\0';
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
if (i % 16 == 0) {
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "%0*" PRIXPTR ": | ", 2 * sizeof(addrbase), addrbase + i);
|
||||
}
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "%02X ", d[i]);
|
||||
if (d[i] >= ' ' && d[i] <= '~') {
|
||||
ascii[i % 16] = d[i];
|
||||
} else {
|
||||
ascii[i % 16] = '.';
|
||||
}
|
||||
if ((i+1) % 8 == 0 || i+1 == size) {
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, " ");
|
||||
if ((i+1) % 16 == 0) {
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "| %s \n", ascii);
|
||||
} else if (i+1 == size) {
|
||||
ascii[(i+1) % 16] = '\0';
|
||||
if ((i+1) % 16 <= 8) {
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, " ");
|
||||
}
|
||||
for (size_t j = (i+1) % 16; j < 16; j++) {
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, " ");
|
||||
}
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "| %s \n", ascii);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,15 +117,12 @@ static inline bool check_32bit_address_range_in_program(uintptr_t addr, size_t s
|
||||
overlaps_a(start, end, __start__, __end__);
|
||||
}
|
||||
|
||||
void hexdump(const void* data, size_t size, uintptr_t addrbase);
|
||||
|
||||
__attribute__((noreturn)) void watchdog_reboot(void);
|
||||
__attribute__((noreturn)) void pmc_reboot(uint32_t scratch0);
|
||||
__attribute__((noreturn)) void reboot_to_self(void);
|
||||
__attribute__((noreturn)) void wait_for_button_and_reboot(void);
|
||||
|
||||
__attribute__((noreturn)) void generic_panic(void);
|
||||
|
||||
__attribute__((noreturn)) void fatal_error(const char *fmt, ...);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -18,67 +18,9 @@
|
||||
#include <string.h>
|
||||
#include "device_partition.h"
|
||||
|
||||
int device_partition_read_data(device_partition_t *devpart, void *dst, uint64_t sector, uint64_t num_sectors)
|
||||
{
|
||||
int rc;
|
||||
if (!devpart->initialized) {
|
||||
rc = devpart->initializer(devpart);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
if ((devpart->read_cipher != NULL) && (devpart->crypto_mode != DevicePartitionCryptoMode_None)) {
|
||||
for (uint64_t i = 0; i < num_sectors; i += devpart->crypto_work_buffer_num_sectors) {
|
||||
uint64_t n = (i + devpart->crypto_work_buffer_num_sectors > num_sectors) ? (num_sectors - i) : devpart->crypto_work_buffer_num_sectors;
|
||||
rc = devpart->reader(devpart, devpart->crypto_work_buffer, sector + i, n);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
rc = devpart->read_cipher(devpart, sector + i, n);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
memcpy(dst + (size_t)(devpart->sector_size * i), devpart->crypto_work_buffer, (size_t)(devpart->sector_size * n));
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
return devpart->reader(devpart, dst, sector, num_sectors);
|
||||
}
|
||||
}
|
||||
|
||||
int device_partition_write_data(device_partition_t *devpart, const void *src, uint64_t sector, uint64_t num_sectors)
|
||||
{
|
||||
int rc;
|
||||
if (!devpart->initialized) {
|
||||
rc = devpart->initializer(devpart);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
if ((devpart->write_cipher != NULL) && (devpart->crypto_mode != DevicePartitionCryptoMode_None)) {
|
||||
for (uint64_t i = 0; i < num_sectors; i += devpart->crypto_work_buffer_num_sectors) {
|
||||
uint64_t n = (i + devpart->crypto_work_buffer_num_sectors > num_sectors) ? (num_sectors - i) : devpart->crypto_work_buffer_num_sectors;
|
||||
memcpy(devpart->crypto_work_buffer, src + (size_t)(devpart->sector_size * i), (size_t)(devpart->sector_size * n));
|
||||
rc = devpart->write_cipher(devpart, sector + i, n);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
rc = devpart->writer(devpart, devpart->crypto_work_buffer, sector + i, n);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
return devpart->writer(devpart, src, sector, num_sectors);
|
||||
}
|
||||
}
|
||||
|
||||
int emu_device_partition_read_data(device_partition_t *devpart, void *dst, uint64_t sector, uint64_t num_sectors, const char *origin_path, int num_parts, uint64_t part_limit)
|
||||
{
|
||||
int device_partition_read_data(device_partition_t *devpart, void *dst, uint64_t sector, uint64_t num_sectors) {
|
||||
int rc = 0;
|
||||
uint64_t target_sector = sector;
|
||||
char target_path[0x300 + 1] = {0};
|
||||
|
||||
/* Perform initialization steps, if necessary. */
|
||||
if (!devpart->initialized) {
|
||||
@@ -88,33 +30,37 @@ int emu_device_partition_read_data(device_partition_t *devpart, void *dst, uint6
|
||||
}
|
||||
}
|
||||
|
||||
/* Prepare the right file path if using file mode. */
|
||||
if (devpart->emu_use_file && (origin_path != NULL)) {
|
||||
/* Handle data in multiple parts, if necessary. */
|
||||
if (num_parts > 0) {
|
||||
int target_part = 0;
|
||||
uint64_t data_offset = sector * devpart->sector_size;
|
||||
/* Handle emulation. */
|
||||
if (devpart->is_emulated) {
|
||||
/* Prepare the right file path if using file mode. */
|
||||
if (devpart->emu_use_file) {
|
||||
int num_parts = devpart->emu_num_parts;
|
||||
uint64_t part_limit = devpart->emu_part_limit;
|
||||
|
||||
if (data_offset >= part_limit) {
|
||||
uint64_t data_offset_aligned = (data_offset + (part_limit - 1)) & ~(part_limit - 1);
|
||||
target_part = (data_offset_aligned == data_offset) ? (data_offset / part_limit) : (data_offset_aligned / part_limit) - 1;
|
||||
target_sector = (data_offset - (target_part * part_limit)) / devpart->sector_size;
|
||||
|
||||
/* Target part is invalid. */
|
||||
if (target_part > num_parts) {
|
||||
return -1;
|
||||
/* Handle data in multiple parts, if necessary. */
|
||||
if (num_parts > 0) {
|
||||
int target_part = 0;
|
||||
char target_path[0x300 + 1] = {0};
|
||||
uint64_t data_offset = sector * devpart->sector_size;
|
||||
|
||||
if (data_offset >= part_limit) {
|
||||
uint64_t data_offset_aligned = (data_offset + (part_limit - 1)) & ~(part_limit - 1);
|
||||
target_part = (data_offset_aligned == data_offset) ? (data_offset / part_limit) : (data_offset_aligned / part_limit) - 1;
|
||||
target_sector = (data_offset - (target_part * part_limit)) / devpart->sector_size;
|
||||
|
||||
/* Target part is invalid. */
|
||||
if (target_part > num_parts) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Treat the path as a folder with each part inside. */
|
||||
snprintf(target_path, sizeof(target_path) - 1, "%s/%02d", devpart->emu_root_path, target_part);
|
||||
|
||||
/* Update the target file path. */
|
||||
strcpy(devpart->emu_file_path, target_path);
|
||||
}
|
||||
|
||||
/* Treat the path as a folder with each part inside. */
|
||||
snprintf(target_path, sizeof(target_path) - 1, "%s/%02d", origin_path, target_part);
|
||||
} else {
|
||||
/* If there are no parts, copy the origin path directly. */
|
||||
strcpy(target_path, origin_path);
|
||||
}
|
||||
|
||||
/* Update the target file path. */
|
||||
devpart->emu_file_path = target_path;
|
||||
}
|
||||
|
||||
/* Read the partition data. */
|
||||
@@ -147,11 +93,9 @@ int emu_device_partition_read_data(device_partition_t *devpart, void *dst, uint6
|
||||
return rc;
|
||||
}
|
||||
|
||||
int emu_device_partition_write_data(device_partition_t *devpart, const void *src, uint64_t sector, uint64_t num_sectors, const char *origin_path, int num_parts, uint64_t part_limit)
|
||||
{
|
||||
int device_partition_write_data(device_partition_t *devpart, const void *src, uint64_t sector, uint64_t num_sectors) {
|
||||
int rc = 0;
|
||||
uint64_t target_sector = sector;
|
||||
char target_path[0x300 + 1] = {0};
|
||||
|
||||
/* Perform initialization steps, if necessary. */
|
||||
if (!devpart->initialized) {
|
||||
@@ -161,33 +105,37 @@ int emu_device_partition_write_data(device_partition_t *devpart, const void *src
|
||||
}
|
||||
}
|
||||
|
||||
/* Prepare the right file path if using file mode. */
|
||||
if (devpart->emu_use_file && (origin_path != NULL)) {
|
||||
/* Handle data in multiple parts, if necessary. */
|
||||
if (num_parts > 0) {
|
||||
int target_part = 0;
|
||||
uint64_t data_offset = sector * devpart->sector_size;
|
||||
/* Handle emulation. */
|
||||
if (devpart->is_emulated) {
|
||||
/* Prepare the right file path if using file mode. */
|
||||
if (devpart->emu_use_file) {
|
||||
int num_parts = devpart->emu_num_parts;
|
||||
uint64_t part_limit = devpart->emu_part_limit;
|
||||
|
||||
if (data_offset >= part_limit) {
|
||||
uint64_t data_offset_aligned = (data_offset + (part_limit - 1)) & ~(part_limit - 1);
|
||||
target_part = (data_offset_aligned == data_offset) ? (data_offset / part_limit) : (data_offset_aligned / part_limit) - 1;
|
||||
target_sector = (data_offset - (target_part * part_limit)) / devpart->sector_size;
|
||||
|
||||
/* Target part is invalid. */
|
||||
if (target_part > num_parts) {
|
||||
return -1;
|
||||
/* Handle data in multiple parts, if necessary. */
|
||||
if (num_parts > 0) {
|
||||
int target_part = 0;
|
||||
char target_path[0x300 + 1] = {0};
|
||||
uint64_t data_offset = sector * devpart->sector_size;
|
||||
|
||||
if (data_offset >= part_limit) {
|
||||
uint64_t data_offset_aligned = (data_offset + (part_limit - 1)) & ~(part_limit - 1);
|
||||
target_part = (data_offset_aligned == data_offset) ? (data_offset / part_limit) : (data_offset_aligned / part_limit) - 1;
|
||||
target_sector = (data_offset - (target_part * part_limit)) / devpart->sector_size;
|
||||
|
||||
/* Target part is invalid. */
|
||||
if (target_part > num_parts) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Treat the path as a folder with each part inside. */
|
||||
snprintf(target_path, sizeof(target_path) - 1, "%s/%02d", devpart->emu_root_path, target_part);
|
||||
|
||||
/* Update the target file path. */
|
||||
strcpy(devpart->emu_file_path, target_path);
|
||||
}
|
||||
|
||||
/* Treat the path as a folder with each part inside. */
|
||||
snprintf(target_path, sizeof(target_path) - 1, "%s/%02d", origin_path, target_part);
|
||||
} else {
|
||||
/* If there are no parts, copy the origin path directly. */
|
||||
strcpy(target_path, origin_path);
|
||||
}
|
||||
|
||||
/* Update the target file path. */
|
||||
devpart->emu_file_path = target_path;
|
||||
}
|
||||
|
||||
/* Write the partition data. */
|
||||
|
||||
@@ -67,13 +67,16 @@ typedef struct device_partition_t {
|
||||
uint8_t __attribute__((aligned(16))) iv[DEVPART_IV_MAX_SIZE]; /* IV. */
|
||||
bool initialized;
|
||||
|
||||
char *emu_file_path; /* Emulated device file path. */
|
||||
/* Emulation only. */
|
||||
bool is_emulated;
|
||||
bool emu_use_file;
|
||||
char emu_root_path[0x100 + 1];
|
||||
char emu_file_path[0x300 + 1];
|
||||
int emu_num_parts;
|
||||
uint64_t emu_part_limit;
|
||||
} device_partition_t;
|
||||
|
||||
int device_partition_read_data(device_partition_t *devpart, void *dst, uint64_t sector, uint64_t num_sectors);
|
||||
int device_partition_write_data(device_partition_t *devpart, const void *src, uint64_t sector, uint64_t num_sectors);
|
||||
int emu_device_partition_read_data(device_partition_t *devpart, void *dst, uint64_t sector, uint64_t num_sectors, const char *origin_path, int num_parts, uint64_t part_limit);
|
||||
int emu_device_partition_write_data(device_partition_t *devpart, const void *src, uint64_t sector, uint64_t num_sectors, const char *origin_path, int num_parts, uint64_t part_limit);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,489 +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 <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/iosupport.h>
|
||||
#include <sys/param.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "emu_dev.h"
|
||||
#include "utils.h"
|
||||
|
||||
static int emudev_open(struct _reent *r, void *fileStruct, const char *path, int flags, int mode);
|
||||
static int emudev_close(struct _reent *r, void *fd);
|
||||
static ssize_t emudev_write(struct _reent *r, void *fd, const char *ptr, size_t len);
|
||||
static ssize_t emudev_read(struct _reent *r, void *fd, char *ptr, size_t len);
|
||||
static off_t emudev_seek(struct _reent *r, void *fd, off_t pos, int whence);
|
||||
static int emudev_fstat(struct _reent *r, void *fd, struct stat *st);
|
||||
static int emudev_stat(struct _reent *r, const char *file, struct stat *st);
|
||||
static int emudev_fsync(struct _reent *r, void *fd);
|
||||
|
||||
typedef struct emudev_device_t {
|
||||
devoptab_t devoptab;
|
||||
|
||||
char origin_path[0x300+1];
|
||||
int num_parts;
|
||||
uint64_t part_limit;
|
||||
uint8_t *tmp_sector;
|
||||
device_partition_t devpart;
|
||||
char name[32+1];
|
||||
char root_path[34+1];
|
||||
bool setup, registered;
|
||||
} emudev_device_t;
|
||||
|
||||
typedef struct emudev_file_t {
|
||||
emudev_device_t *device;
|
||||
int open_flags;
|
||||
uint64_t offset;
|
||||
} emudev_file_t;
|
||||
|
||||
static emudev_device_t g_emudev_devices[EMUDEV_MAX_DEVICES] = {0};
|
||||
|
||||
static devoptab_t g_emudev_devoptab = {
|
||||
.structSize = sizeof(emudev_file_t),
|
||||
.open_r = emudev_open,
|
||||
.close_r = emudev_close,
|
||||
.write_r = emudev_write,
|
||||
.read_r = emudev_read,
|
||||
.seek_r = emudev_seek,
|
||||
.fstat_r = emudev_fstat,
|
||||
.stat_r = emudev_stat,
|
||||
.fsync_r = emudev_fsync,
|
||||
.deviceData = NULL,
|
||||
};
|
||||
|
||||
static emudev_device_t *emudev_find_device(const char *name) {
|
||||
for (size_t i = 0; i < EMUDEV_MAX_DEVICES; i++) {
|
||||
if (g_emudev_devices[i].setup && strcmp(g_emudev_devices[i].name, name) == 0) {
|
||||
return &g_emudev_devices[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int emudev_mount_device(const char *name, const device_partition_t *devpart, const char *origin_path, int num_parts, uint64_t part_limit) {
|
||||
emudev_device_t *device = NULL;
|
||||
|
||||
if (name[0] == '\0' || devpart == NULL) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strlen(name) > 32) {
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
if (emudev_find_device(name) != NULL) {
|
||||
errno = EEXIST; /* Device already exists */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Find an unused slot. */
|
||||
for (size_t i = 0; i < EMUDEV_MAX_DEVICES; i++) {
|
||||
if (!g_emudev_devices[i].setup) {
|
||||
device = &g_emudev_devices[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (device == NULL) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(device, 0, sizeof(emudev_device_t));
|
||||
device->devoptab = g_emudev_devoptab;
|
||||
device->devpart = *devpart;
|
||||
strcpy(device->name, name);
|
||||
strcpy(device->root_path, name);
|
||||
strcat(device->root_path, ":/");
|
||||
|
||||
/* Copy the file path for file mode. */
|
||||
if (devpart->emu_use_file)
|
||||
strcpy(device->origin_path, origin_path);
|
||||
|
||||
device->num_parts = num_parts;
|
||||
device->part_limit = part_limit;
|
||||
|
||||
device->devoptab.name = device->name;
|
||||
device->devoptab.deviceData = device;
|
||||
|
||||
/* Initialize immediately. */
|
||||
int rc = device->devpart.initializer(&device->devpart);
|
||||
if (rc != 0) {
|
||||
errno = rc;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Allocate memory for our intermediate sector. */
|
||||
device->tmp_sector = (uint8_t *)malloc(devpart->sector_size);
|
||||
if (device->tmp_sector == NULL) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
device->setup = true;
|
||||
device->registered = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int emudev_register_device(const char *name) {
|
||||
emudev_device_t *device = emudev_find_device(name);
|
||||
if (device == NULL) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (device->registered) {
|
||||
/* Do nothing if the device is already registered. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (AddDevice(&device->devoptab) == -1) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
} else {
|
||||
device->registered = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int emudev_unregister_device(const char *name) {
|
||||
emudev_device_t *device = emudev_find_device(name);
|
||||
char drname[40];
|
||||
|
||||
if (device == NULL) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!device->registered) {
|
||||
/* Do nothing if the device is not registered. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
strcpy(drname, name);
|
||||
strcat(drname, ":");
|
||||
|
||||
if (RemoveDevice(drname) == -1) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
} else {
|
||||
device->registered = false;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int emudev_unmount_device(const char *name) {
|
||||
int rc;
|
||||
emudev_device_t *device = emudev_find_device(name);
|
||||
|
||||
if (device == NULL) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = emudev_unregister_device(name);
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(device->tmp_sector);
|
||||
device->devpart.finalizer(&device->devpart);
|
||||
memset(device, 0, sizeof(emudev_device_t));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int emudev_unmount_all(void) {
|
||||
for (size_t i = 0; i < EMUDEV_MAX_DEVICES; i++) {
|
||||
int rc = emudev_unmount_device(g_emudev_devices[i].name);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emudev_open(struct _reent *r, void *fileStruct, const char *path, int flags, int mode) {
|
||||
(void)mode;
|
||||
emudev_file_t *f = (emudev_file_t *)fileStruct;
|
||||
emudev_device_t *device = (emudev_device_t *)(r->deviceData);
|
||||
|
||||
/* Only allow "device:/". */
|
||||
if (strcmp(path, device->root_path) != 0) {
|
||||
r->_errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Forbid some flags that we explicitly don't support.*/
|
||||
if (flags & (O_APPEND | O_TRUNC | O_EXCL)) {
|
||||
r->_errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(f, 0, sizeof(emudev_file_t));
|
||||
f->device = device;
|
||||
f->open_flags = flags;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emudev_close(struct _reent *r, void *fd) {
|
||||
(void)r;
|
||||
emudev_file_t *f = (emudev_file_t *)fd;
|
||||
memset(f, 0, sizeof(emudev_file_t));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t emudev_write(struct _reent *r, void *fd, const char *ptr, size_t len) {
|
||||
emudev_file_t *f = (emudev_file_t *)fd;
|
||||
emudev_device_t *device = f->device;
|
||||
size_t sector_size = device->devpart.sector_size;
|
||||
uint64_t sector_begin = f->offset / sector_size;
|
||||
uint64_t sector_end = (f->offset + len + sector_size - 1) / sector_size;
|
||||
uint64_t sector_end_aligned;
|
||||
uint64_t current_sector = sector_begin;
|
||||
const uint8_t *data = (const uint8_t *)ptr;
|
||||
|
||||
int no = 0;
|
||||
|
||||
if (sector_end >= device->devpart.num_sectors) {
|
||||
len = (size_t)(sector_size * device->devpart.num_sectors - f->offset);
|
||||
sector_end = device->devpart.num_sectors;
|
||||
}
|
||||
|
||||
sector_end_aligned = sector_end - ((f->offset + len) % sector_size != 0 ? 1 : 0);
|
||||
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Unaligned at the start, we need to read the sector and incorporate the data. */
|
||||
if (f->offset % sector_size != 0) {
|
||||
size_t nb = (size_t)(len <= (sector_size - (f->offset % sector_size)) ? len : sector_size - (f->offset % sector_size));
|
||||
no = emu_device_partition_read_data(&device->devpart, device->tmp_sector, sector_begin, 1, device->origin_path, device->num_parts, device->part_limit);
|
||||
if (no != 0) {
|
||||
r->_errno = no;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(device->tmp_sector + (f->offset % sector_size), data, nb);
|
||||
|
||||
no = emu_device_partition_write_data(&device->devpart, device->tmp_sector, sector_begin, 1, device->origin_path, device->num_parts, device->part_limit);
|
||||
if (no != 0) {
|
||||
r->_errno = no;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Advance */
|
||||
data += sector_size - (f->offset % sector_size);
|
||||
current_sector++;
|
||||
}
|
||||
|
||||
/* Check if we're already done (otherwise this causes a bug in handling the last sector of the range). */
|
||||
if (current_sector == sector_end) {
|
||||
f->offset += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Write all of the sector-aligned data. */
|
||||
if (current_sector != sector_end_aligned) {
|
||||
no = emu_device_partition_write_data(&device->devpart, data, current_sector, sector_end_aligned - current_sector, device->origin_path, device->num_parts, device->part_limit);
|
||||
if (no != 0) {
|
||||
r->_errno = no;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
data += sector_size * (sector_end_aligned - current_sector);
|
||||
current_sector = sector_end_aligned;
|
||||
|
||||
/* Unaligned at the end, we need to read the sector and incorporate the data. */
|
||||
if (sector_end != sector_end_aligned) {
|
||||
no = emu_device_partition_read_data(&device->devpart, device->tmp_sector, sector_end_aligned, 1, device->origin_path, device->num_parts, device->part_limit);
|
||||
if (no != 0) {
|
||||
r->_errno = no;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(device->tmp_sector, data, (size_t)((f->offset + len) % sector_size));
|
||||
|
||||
no = emu_device_partition_write_data(&device->devpart, device->tmp_sector, sector_end_aligned, 1, device->origin_path, device->num_parts, device->part_limit);
|
||||
if (no != 0) {
|
||||
r->_errno = no;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Advance */
|
||||
data += sector_size - ((f->offset + len) % sector_size);
|
||||
current_sector++;
|
||||
}
|
||||
|
||||
f->offset += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t emudev_read(struct _reent *r, void *fd, char *ptr, size_t len) {
|
||||
emudev_file_t *f = (emudev_file_t *)fd;
|
||||
emudev_device_t *device = f->device;
|
||||
size_t sector_size = device->devpart.sector_size;
|
||||
uint64_t sector_begin = f->offset / sector_size;
|
||||
uint64_t sector_end = (f->offset + len + sector_size - 1) / sector_size;
|
||||
uint64_t sector_end_aligned;
|
||||
uint64_t current_sector = sector_begin;
|
||||
uint8_t *data = (uint8_t *)ptr;
|
||||
|
||||
int no = 0;
|
||||
|
||||
if (sector_end >= device->devpart.num_sectors) {
|
||||
len = (size_t)(sector_size * device->devpart.num_sectors - f->offset);
|
||||
sector_end = device->devpart.num_sectors;
|
||||
}
|
||||
|
||||
sector_end_aligned = sector_end - ((f->offset + len) % sector_size != 0 ? 1 : 0);
|
||||
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Unaligned at the start, we need to read the sector and incorporate the data. */
|
||||
if (f->offset % sector_size != 0) {
|
||||
size_t nb = (size_t)(len <= (sector_size - (f->offset % sector_size)) ? len : sector_size - (f->offset % sector_size));
|
||||
no = emu_device_partition_read_data(&device->devpart, device->tmp_sector, sector_begin, 1, device->origin_path, device->num_parts, device->part_limit);
|
||||
if (no != 0) {
|
||||
r->_errno = no;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(data, device->tmp_sector + (f->offset % sector_size), nb);
|
||||
|
||||
/* Advance */
|
||||
data += sector_size - (f->offset % sector_size);
|
||||
current_sector++;
|
||||
}
|
||||
|
||||
/* Check if we're already done (otherwise this causes a bug in handling the last sector of the range). */
|
||||
if (current_sector == sector_end) {
|
||||
f->offset += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Read all of the sector-aligned data. */
|
||||
if (current_sector != sector_end_aligned) {
|
||||
no = emu_device_partition_read_data(&device->devpart, data, current_sector, sector_end_aligned - current_sector, device->origin_path, device->num_parts, device->part_limit);
|
||||
if (no != 0) {
|
||||
r->_errno = no;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
data += sector_size * (sector_end_aligned - current_sector);
|
||||
current_sector = sector_end_aligned;
|
||||
|
||||
/* Unaligned at the end, we need to read the sector and incorporate the data. */
|
||||
if (sector_end != sector_end_aligned) {
|
||||
no = emu_device_partition_read_data(&device->devpart, device->tmp_sector, sector_end_aligned, 1, device->origin_path, device->num_parts, device->part_limit);
|
||||
if (no != 0) {
|
||||
r->_errno = no;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(data, device->tmp_sector, (size_t)((f->offset + len) % sector_size));
|
||||
|
||||
/* Advance */
|
||||
data += sector_size - ((f->offset + len) % sector_size);
|
||||
current_sector++;
|
||||
}
|
||||
|
||||
f->offset += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
static off_t emudev_seek(struct _reent *r, void *fd, off_t pos, int whence) {
|
||||
emudev_file_t *f = (emudev_file_t *)fd;
|
||||
emudev_device_t *device = f->device;
|
||||
uint64_t off;
|
||||
|
||||
switch (whence) {
|
||||
case SEEK_SET:
|
||||
off = 0;
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
off = f->offset;
|
||||
break;
|
||||
case SEEK_END:
|
||||
off = device->devpart.num_sectors * device->devpart.sector_size;
|
||||
break;
|
||||
default:
|
||||
r->_errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pos < 0 && pos + off < 0) {
|
||||
/* don't allow seek to before the beginning of the file */
|
||||
r->_errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
f->offset = (uint64_t)(pos + off);
|
||||
return (off_t)(pos + off);
|
||||
}
|
||||
|
||||
static void emudev_stat_impl(emudev_device_t *device, struct stat *st) {
|
||||
memset(st, 0, sizeof(struct stat));
|
||||
st->st_size = (off_t)(device->devpart.num_sectors * device->devpart.sector_size);
|
||||
st->st_nlink = 1;
|
||||
|
||||
st->st_blksize = device->devpart.sector_size;
|
||||
st->st_blocks = st->st_size / st->st_blksize;
|
||||
|
||||
st->st_mode = S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
|
||||
}
|
||||
|
||||
static int emudev_fstat(struct _reent *r, void *fd, struct stat *st) {
|
||||
(void)r;
|
||||
emudev_file_t *f = (emudev_file_t *)fd;
|
||||
emudev_device_t *device = f->device;
|
||||
emudev_stat_impl(device, st);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emudev_stat(struct _reent *r, const char *file, struct stat *st) {
|
||||
emudev_device_t *device = (emudev_device_t *)(r->deviceData);
|
||||
if (strcmp(file, device->root_path) != 0) {
|
||||
r->_errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
emudev_stat_impl(device, st);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emudev_fsync(struct _reent *r, void *fd) {
|
||||
/* Nothing to do. */
|
||||
(void)r;
|
||||
(void)fd;
|
||||
return 0;
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "exception_handlers.h"
|
||||
@@ -22,7 +23,7 @@
|
||||
#include "lib/log.h"
|
||||
|
||||
#define CODE_DUMP_SIZE 0x30
|
||||
#define STACK_DUMP_SIZE 0x60
|
||||
#define STACK_DUMP_SIZE 0x30
|
||||
|
||||
extern const uint32_t exception_handler_table[];
|
||||
|
||||
@@ -35,6 +36,40 @@ static const char *register_names[] = {
|
||||
"SP", "LR", "PC", "CPSR",
|
||||
};
|
||||
|
||||
/* Adapted from https://gist.github.com/ccbrown/9722406 */
|
||||
static void hexdump(const void* data, size_t size, uintptr_t addrbase, char* strbuf) {
|
||||
const uint8_t *d = (const uint8_t *)data;
|
||||
char ascii[17] = {0};
|
||||
ascii[16] = '\0';
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
if (i % 16 == 0) {
|
||||
strbuf += sprintf(strbuf, "%0*" PRIXPTR ": | ", 2 * sizeof(addrbase), addrbase + i);
|
||||
}
|
||||
strbuf += sprintf(strbuf, "%02X ", d[i]);
|
||||
if (d[i] >= ' ' && d[i] <= '~') {
|
||||
ascii[i % 16] = d[i];
|
||||
} else {
|
||||
ascii[i % 16] = '.';
|
||||
}
|
||||
if ((i+1) % 8 == 0 || i+1 == size) {
|
||||
strbuf += sprintf(strbuf, " ");
|
||||
if ((i+1) % 16 == 0) {
|
||||
strbuf += sprintf(strbuf, "| %s \n", ascii);
|
||||
} else if (i+1 == size) {
|
||||
ascii[(i+1) % 16] = '\0';
|
||||
if ((i+1) % 16 <= 8) {
|
||||
strbuf += sprintf(strbuf, " ");
|
||||
}
|
||||
for (size_t j = (i+1) % 16; j < 16; j++) {
|
||||
strbuf += sprintf(strbuf, " ");
|
||||
}
|
||||
strbuf += sprintf(strbuf, "| %s \n", ascii);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setup_exception_handlers(void) {
|
||||
volatile uint32_t *bpmp_exception_handler_table = (volatile uint32_t *)0x6000F200;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
@@ -45,38 +80,40 @@ void setup_exception_handlers(void) {
|
||||
}
|
||||
|
||||
void exception_handler_main(uint32_t *registers, unsigned int exception_type) {
|
||||
uint8_t code_dump[CODE_DUMP_SIZE];
|
||||
uint8_t stack_dump[STACK_DUMP_SIZE];
|
||||
size_t code_dump_size;
|
||||
size_t stack_dump_size;
|
||||
char exception_log[0x400] = {0};
|
||||
uint8_t code_dump[CODE_DUMP_SIZE] = {0};
|
||||
uint8_t stack_dump[STACK_DUMP_SIZE] = {0};
|
||||
size_t code_dump_size = 0;
|
||||
size_t stack_dump_size = 0;
|
||||
|
||||
uint32_t pc = registers[15];
|
||||
uint32_t cpsr = registers[16];
|
||||
|
||||
uint32_t instr_addr = pc + ((cpsr & 0x20) ? 2 : 4) - CODE_DUMP_SIZE;
|
||||
|
||||
print(SCREEN_LOG_LEVEL_ERROR, "\nSomething went wrong...\n");
|
||||
|
||||
sprintf(exception_log, "An exception occured!\n");
|
||||
|
||||
code_dump_size = safecpy(code_dump, (const void *)instr_addr, CODE_DUMP_SIZE);
|
||||
stack_dump_size = safecpy(stack_dump, (const void *)registers[13], STACK_DUMP_SIZE);
|
||||
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "\nException type: %s\n",
|
||||
exception_names[exception_type]);
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "\nRegisters:\n\n");
|
||||
sprintf(exception_log + strlen(exception_log), "\nException type: %s\n", exception_names[exception_type]);
|
||||
sprintf(exception_log + strlen(exception_log), "\nRegisters:\n");
|
||||
|
||||
/* Print r0 to pc. */
|
||||
for (int i = 0; i < 16; i += 2) {
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "%-7s%08"PRIX32" %-7s%08"PRIX32"\n",
|
||||
sprintf(exception_log + strlen(exception_log), "%-7s%08"PRIX32" %-7s%08"PRIX32"\n",
|
||||
register_names[i], registers[i], register_names[i+1], registers[i+1]);
|
||||
}
|
||||
|
||||
/* Print cpsr. */
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "%-7s%08"PRIX32"\n", register_names[16], registers[16]);
|
||||
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "\nCode dump:\n");
|
||||
hexdump(code_dump, code_dump_size, instr_addr);
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "\nStack dump:\n");
|
||||
hexdump(stack_dump, stack_dump_size, registers[13]);
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "\n");
|
||||
fatal_error("An exception occurred!\n");
|
||||
sprintf(exception_log + strlen(exception_log), "%-7s%08"PRIX32"\n", register_names[16], registers[16]);
|
||||
|
||||
/* Print code and stack regions. */
|
||||
sprintf(exception_log + strlen(exception_log), "\nCode dump:\n");
|
||||
hexdump(code_dump, code_dump_size, instr_addr, exception_log + strlen(exception_log));
|
||||
sprintf(exception_log + strlen(exception_log), "\nStack dump:\n");
|
||||
hexdump(stack_dump, stack_dump_size, registers[13], exception_log + strlen(exception_log));
|
||||
sprintf(exception_log + strlen(exception_log), "\n");
|
||||
|
||||
/* Throw fatal error with the full exception log. */
|
||||
fatal_error(exception_log);
|
||||
}
|
||||
|
||||
@@ -302,7 +302,6 @@ int fsdev_register_keys(const char *name, unsigned int target_firmware, BisParti
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int fsdev_unmount_all(void) {
|
||||
for (size_t i = 0; i < FF_VOLUMES; i++) {
|
||||
int ret = fsdev_unmount_device(g_fsdev_devices[i].name);
|
||||
|
||||
@@ -112,42 +112,3 @@ int gpt_iterate_through_entries(FILE *disk, size_t sector_size, gpt_entry_iterat
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpt_iterate_through_emu_entries(FILE *disk, size_t sector_size, gpt_emu_entry_iterator_t callback, void *param, const char *origin_path, int num_parts, uint64_t part_limit) {
|
||||
efi_header_t hdr;
|
||||
efi_entry_t entry;
|
||||
size_t offset = 2 * 512; /* Sector #2. */
|
||||
size_t delta;
|
||||
|
||||
/* Get the header. */
|
||||
if (gpt_get_header(&hdr, disk, sector_size) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Seek to the entry table. */
|
||||
if (fseek(disk, sector_size * hdr.entries_first_lba - offset, SEEK_CUR) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
offset = sector_size * hdr.entries_first_lba;
|
||||
delta = hdr.entry_size - sizeof(efi_entry_t);
|
||||
|
||||
/* Iterate through the entries. */
|
||||
for (uint32_t i = 0; i < hdr.entry_count; i++) {
|
||||
if (!fread(&entry, sizeof(efi_entry_t), 1, disk)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (callback(&entry, param, offset, disk, origin_path, num_parts, part_limit) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (delta != 0 && fseek(disk, delta, SEEK_CUR) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
offset += hdr.entry_size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -56,6 +56,5 @@ typedef int (*gpt_emu_entry_iterator_t)(const efi_entry_t *entry, void *param, s
|
||||
|
||||
int gpt_get_header(efi_header_t *out, FILE *disk, size_t sector_size);
|
||||
int gpt_iterate_through_entries(FILE *disk, size_t sector_size, gpt_entry_iterator_t callback, void *param);
|
||||
int gpt_iterate_through_emu_entries(FILE *disk, size_t sector_size, gpt_emu_entry_iterator_t callback, void *param, const char *origin_path, int num_parts, uint64_t part_limit);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -963,6 +963,11 @@ void package2_patch_kernel(void *_kernel, size_t *kernel_size, bool is_sd_kernel
|
||||
}
|
||||
|
||||
if (kernel_info == NULL && is_sd_kernel) {
|
||||
/* If the kernel is mesosphere, patch it. */
|
||||
if (*(volatile uint32_t *)((uintptr_t)_kernel + 4) == 0x3053534D) {
|
||||
*out_ini1 = (void *)((uintptr_t)_kernel + *(volatile uint32_t *)((uintptr_t)_kernel + 8));
|
||||
*(volatile uint64_t *)((uintptr_t)_kernel + 8) = (uint64_t)*kernel_size;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -971,12 +976,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;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#ifndef FUSEE_LOG_H
|
||||
#define FUSEE_LOG_H
|
||||
|
||||
#define PRINT_MESSAGE_MAX_LENGTH 512
|
||||
#define PRINT_MESSAGE_MAX_LENGTH 1024
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
|
||||
@@ -64,7 +64,6 @@
|
||||
#undef u32
|
||||
|
||||
extern const uint8_t warmboot_bin[];
|
||||
extern const uint32_t warmboot_bin_size;
|
||||
|
||||
static const uint8_t retail_pkc_modulus[0x100] = {
|
||||
0xF7, 0x86, 0x47, 0xAB, 0x71, 0x89, 0x81, 0xB5, 0xCF, 0x0C, 0xB0, 0xE8, 0x48, 0xA7, 0xFD, 0xAD,
|
||||
@@ -236,6 +235,8 @@ static uint32_t nxboot_get_specific_target_firmware(uint32_t target_firmware){
|
||||
#define CHECK_NCA(NCA_ID, VERSION) do { if (is_nca_present(NCA_ID)) { return ATMOSPHERE_TARGET_FIRMWARE_##VERSION; } } while(0)
|
||||
|
||||
if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_10_0_0) {
|
||||
CHECK_NCA("5077973537f6735b564dd7475b779f87", 10_1_1); /* Exclusive to China. */
|
||||
CHECK_NCA("fd1faed0ca750700d254c0915b93d506", 10_1_0);
|
||||
CHECK_NCA("34728c771299443420820d8ae490ea41", 10_0_4);
|
||||
CHECK_NCA("5b1df84f88c3334335bbb45d8522cbb4", 10_0_3);
|
||||
CHECK_NCA("e951bc9dedcd54f65ffd83d4d050f9e0", 10_0_2);
|
||||
@@ -649,7 +650,7 @@ uint32_t nxboot_main(void) {
|
||||
} else {
|
||||
emummc_size = get_file_size("atmosphere/emummc.kip");
|
||||
if (emummc_size != 0) {
|
||||
/* Allocate memory for the TSEC firmware. */
|
||||
/* Allocate memory for the emummc KIP. */
|
||||
emummc = memalign(0x100, emummc_size);
|
||||
|
||||
if (emummc == NULL) {
|
||||
|
||||
@@ -36,7 +36,6 @@ static bool g_emmc_device_initialized = false;
|
||||
|
||||
static bool g_fsdev_ready = false;
|
||||
static bool g_rawdev_ready = false;
|
||||
static bool g_emudev_ready = false;
|
||||
|
||||
static bool g_is_emummc = false;
|
||||
|
||||
@@ -356,87 +355,6 @@ static int nxfs_mount_partition_gpt_callback(const efi_entry_t *entry, void *par
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nxfs_mount_emu_partition_gpt_callback(const efi_entry_t *entry, void *param, size_t entry_offset, FILE *disk, const char *origin_path, int num_parts, uint64_t part_limit) {
|
||||
(void)entry_offset;
|
||||
(void)disk;
|
||||
device_partition_t *parent = (device_partition_t *)param;
|
||||
device_partition_t devpart = *parent;
|
||||
char name_buffer[128];
|
||||
const uint16_t *utf16name = entry->name;
|
||||
uint32_t name_len;
|
||||
int rc;
|
||||
|
||||
static const struct {
|
||||
const char *partition_name;
|
||||
const char *mount_point;
|
||||
bool is_fat;
|
||||
bool is_encrypted;
|
||||
bool register_immediately;
|
||||
} known_partitions[] = {
|
||||
{"PRODINFO", "prodinfo", false, true, false},
|
||||
{"PRODINFOF", "prodinfof", true, true, false},
|
||||
{"BCPKG2-1-Normal-Main", "bcpkg21", false, false, true},
|
||||
{"BCPKG2-2-Normal-Sub", "bcpkg22", false, false, false},
|
||||
{"BCPKG2-3-SafeMode-Main", "bcpkg23", false, false, false},
|
||||
{"BCPKG2-4-SafeMode-Sub", "bcpkg24", false, false, false},
|
||||
{"BCPKG2-5-Repair-Main", "bcpkg25", false, false, false},
|
||||
{"BCPKG2-6-Repair-Sub", "bcpkg26", false, false, false},
|
||||
{"SAFE", "safe", true, true, false},
|
||||
{"SYSTEM", "system", true, true, true},
|
||||
{"USER", "user", true, true, false},
|
||||
};
|
||||
|
||||
/* Convert the partition name to ASCII, for comparison. */
|
||||
for (name_len = 0; name_len < sizeof(entry->name) && *utf16name != 0; name_len++) {
|
||||
name_buffer[name_len] = (char)*utf16name++;
|
||||
}
|
||||
name_buffer[name_len] = '\0';
|
||||
|
||||
/* Mount the partition, if we know about it. */
|
||||
for (size_t i = 0; i < sizeof(known_partitions)/sizeof(known_partitions[0]); i++) {
|
||||
if (strcmp(name_buffer, known_partitions[i].partition_name) == 0) {
|
||||
devpart.start_sector += entry->first_lba;
|
||||
devpart.num_sectors = (entry->last_lba + 1) - entry->first_lba;
|
||||
if (parent->num_sectors < devpart.num_sectors) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (known_partitions[i].is_encrypted) {
|
||||
devpart.read_cipher = nxfs_bis_crypto_decrypt;
|
||||
devpart.write_cipher = nxfs_bis_crypto_encrypt;
|
||||
devpart.crypto_mode = DevicePartitionCryptoMode_Xts;
|
||||
}
|
||||
|
||||
if (known_partitions[i].is_fat) {
|
||||
rc = fsdev_mount_device(known_partitions[i].mount_point, &devpart, false);
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
if (known_partitions[i].register_immediately) {
|
||||
rc = fsdev_register_device(known_partitions[i].mount_point);
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rc = emudev_mount_device(known_partitions[i].mount_point, &devpart, origin_path, num_parts, part_limit);
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
if (known_partitions[i].register_immediately) {
|
||||
rc = emudev_register_device(known_partitions[i].mount_point);
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nxfs_mount_sd() {
|
||||
device_partition_t model;
|
||||
int rc;
|
||||
@@ -446,6 +364,7 @@ int nxfs_mount_sd() {
|
||||
model.device_struct = &g_sd_mmcpart;
|
||||
model.start_sector = 0;
|
||||
model.num_sectors = 1u << 30; /* arbitrary numbers of sectors. TODO: find the size of the SD in sectors. */
|
||||
model.is_emulated = false;
|
||||
|
||||
/* Mount the SD card device. */
|
||||
rc = fsdev_mount_device("sdmc", &model, true);
|
||||
@@ -479,6 +398,7 @@ int nxfs_mount_emmc() {
|
||||
model.device_struct = &g_emmc_boot0_mmcpart;
|
||||
model.start_sector = 0;
|
||||
model.num_sectors = 0x184000 / model.sector_size;
|
||||
model.is_emulated = false;
|
||||
|
||||
/* Mount boot0 device. */
|
||||
rc = rawdev_mount_device("boot0", &model, true);
|
||||
@@ -499,6 +419,7 @@ int nxfs_mount_emmc() {
|
||||
model.device_struct = &g_emmc_boot1_mmcpart;
|
||||
model.start_sector = 0;
|
||||
model.num_sectors = 0x80000 / model.sector_size;
|
||||
model.is_emulated = false;
|
||||
|
||||
/* Mount boot1 device. */
|
||||
rc = rawdev_mount_device("boot1", &model, false);
|
||||
@@ -514,6 +435,7 @@ int nxfs_mount_emmc() {
|
||||
model.device_struct = &g_emmc_user_mmcpart;
|
||||
model.start_sector = 0;
|
||||
model.num_sectors = (256ull << 30) / model.sector_size;
|
||||
model.is_emulated = false;
|
||||
|
||||
/* Mount raw NAND device. */
|
||||
rc = rawdev_mount_device("rawnand", &model, false);
|
||||
@@ -558,10 +480,11 @@ int nxfs_mount_emummc_partition(uint64_t emummc_start_sector) {
|
||||
model = g_emummc_devpart_template;
|
||||
model.start_sector = emummc_start_sector + (0x400000 * 0 / model.sector_size);
|
||||
model.num_sectors = 0x400000 / model.sector_size;
|
||||
model.is_emulated = true;
|
||||
model.emu_use_file = false;
|
||||
|
||||
/* Mount emulated boot0 device. */
|
||||
rc = emudev_mount_device("boot0", &model, NULL, 0, 0);
|
||||
rc = rawdev_mount_device("boot0", &model, true);
|
||||
|
||||
/* Failed to mount boot0 device. */
|
||||
if (rc == -1) {
|
||||
@@ -569,7 +492,7 @@ int nxfs_mount_emummc_partition(uint64_t emummc_start_sector) {
|
||||
}
|
||||
|
||||
/* Register emulated boot0 device. */
|
||||
rc = emudev_register_device("boot0");
|
||||
rc = rawdev_register_device("boot0");
|
||||
|
||||
/* Failed to register boot0 device. */
|
||||
if (rc == -1) {
|
||||
@@ -580,10 +503,11 @@ int nxfs_mount_emummc_partition(uint64_t emummc_start_sector) {
|
||||
model = g_emummc_devpart_template;
|
||||
model.start_sector = emummc_start_sector + (0x400000 * 1 / model.sector_size);
|
||||
model.num_sectors = 0x400000 / model.sector_size;
|
||||
model.is_emulated = true;
|
||||
model.emu_use_file = false;
|
||||
|
||||
/* Mount emulated boot1 device. */
|
||||
rc = emudev_mount_device("boot1", &model, NULL, 0, 0);
|
||||
rc = rawdev_mount_device("boot1", &model, false);
|
||||
|
||||
/* Failed to mount boot1. */
|
||||
if (rc == -1) {
|
||||
@@ -596,10 +520,11 @@ int nxfs_mount_emummc_partition(uint64_t emummc_start_sector) {
|
||||
model = g_emummc_devpart_template;
|
||||
model.start_sector = emummc_start_sector + (0x400000 * 2 / model.sector_size);
|
||||
model.num_sectors = (256ull << 30) / model.sector_size;
|
||||
model.is_emulated = true;
|
||||
model.emu_use_file = false;
|
||||
|
||||
/* Mount emulated raw NAND device. */
|
||||
rc = emudev_mount_device("rawnand", &model, NULL, 0, 0);
|
||||
rc = rawdev_mount_device("rawnand", &model, false);
|
||||
|
||||
/* Failed to mount raw NAND. */
|
||||
if (rc == -1) {
|
||||
@@ -607,7 +532,7 @@ int nxfs_mount_emummc_partition(uint64_t emummc_start_sector) {
|
||||
}
|
||||
|
||||
/* Register emulated raw NAND device. */
|
||||
rc = emudev_register_device("rawnand");
|
||||
rc = rawdev_register_device("rawnand");
|
||||
|
||||
/* Failed to register raw NAND device. */
|
||||
if (rc == -1) {
|
||||
@@ -623,14 +548,14 @@ int nxfs_mount_emummc_partition(uint64_t emummc_start_sector) {
|
||||
}
|
||||
|
||||
/* Iterate the GPT and mount each emulated raw NAND partition. */
|
||||
rc = gpt_iterate_through_emu_entries(rawnand, model.sector_size, nxfs_mount_emu_partition_gpt_callback, &model, NULL, 0, 0);
|
||||
rc = gpt_iterate_through_entries(rawnand, model.sector_size, nxfs_mount_partition_gpt_callback, &model);
|
||||
|
||||
/* Close emulated raw NAND device. */
|
||||
fclose(rawnand);
|
||||
|
||||
/* All emulated devices are ready. */
|
||||
if (rc == 0) {
|
||||
g_emudev_ready = true;
|
||||
g_rawdev_ready = true;
|
||||
g_is_emummc = true;
|
||||
}
|
||||
|
||||
@@ -641,10 +566,9 @@ int nxfs_mount_emummc_file(const char *emummc_path, int num_parts, uint64_t part
|
||||
device_partition_t model;
|
||||
int rc;
|
||||
FILE *rawnand;
|
||||
bool is_exfat;
|
||||
char emummc_boot0_path[0x300 + 1] = {0};
|
||||
char emummc_boot1_path[0x300 + 1] = {0};
|
||||
|
||||
|
||||
/* Check if the SD card is EXFAT formatted. */
|
||||
rc = fsdev_is_exfat("sdmc");
|
||||
|
||||
@@ -652,29 +576,20 @@ int nxfs_mount_emummc_file(const char *emummc_path, int num_parts, uint64_t part
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set EXFAT status. */
|
||||
is_exfat = (rc == 1);
|
||||
|
||||
/* Reject single part in FAT32. */
|
||||
/* NOTE: This check has no effect in the current design. */
|
||||
if (!is_exfat && (num_parts < 1)) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
||||
/* We want a folder with the archive bit set. */
|
||||
rc = fsdev_get_attr(emummc_path);
|
||||
|
||||
/* Failed to get file DOS attributes. */
|
||||
if (rc == -1) {
|
||||
return -3;
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
||||
/* Our path is not a directory. */
|
||||
if (!(rc & AM_DIR)) {
|
||||
return -4;
|
||||
return -3;
|
||||
}
|
||||
|
||||
|
||||
/* Check if the archive bit is not set. */
|
||||
if (!(rc & AM_ARC)) {
|
||||
/* Try to set the archive bit. */
|
||||
@@ -682,102 +597,117 @@ int nxfs_mount_emummc_file(const char *emummc_path, int num_parts, uint64_t part
|
||||
|
||||
/* Failed to set file DOS attributes. */
|
||||
if (rc == -1) {
|
||||
return -5;
|
||||
return -4;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Prepare boot0 file path. */
|
||||
snprintf(emummc_boot0_path, sizeof(emummc_boot0_path) - 1, "%s/%s", emummc_path, "boot0");
|
||||
|
||||
/* Setup an emulation template for boot0. */
|
||||
model = g_emummc_devpart_template;
|
||||
model.start_sector = 0;
|
||||
model.num_sectors = 0x400000 / model.sector_size;
|
||||
model.is_emulated = true;
|
||||
model.emu_use_file = true;
|
||||
|
||||
/* Prepare boot0 file path. */
|
||||
snprintf(emummc_boot0_path, sizeof(emummc_boot0_path) - 1, "%s/%s", emummc_path, "boot0");
|
||||
model.emu_num_parts = 0;
|
||||
model.emu_part_limit = 0;
|
||||
strcpy(model.emu_root_path, emummc_path);
|
||||
strcpy(model.emu_file_path, emummc_boot0_path);
|
||||
|
||||
/* Mount emulated boot0 device. */
|
||||
rc = emudev_mount_device("boot0", &model, emummc_boot0_path, 0, 0);
|
||||
rc = rawdev_mount_device("boot0", &model, true);
|
||||
|
||||
/* Failed to mount boot0 device. */
|
||||
if (rc == -1) {
|
||||
return -6;
|
||||
return -5;
|
||||
}
|
||||
|
||||
|
||||
/* Register emulated boot0 device. */
|
||||
rc = emudev_register_device("boot0");
|
||||
rc = rawdev_register_device("boot0");
|
||||
|
||||
/* Failed to register boot0 device. */
|
||||
if (rc == -1) {
|
||||
return -7;
|
||||
return -6;
|
||||
}
|
||||
|
||||
|
||||
/* Prepare boot1 file path. */
|
||||
snprintf(emummc_boot1_path, sizeof(emummc_boot1_path) - 1, "%s/%s", emummc_path, "boot1");
|
||||
|
||||
/* Setup an emulation template for boot1. */
|
||||
model = g_emummc_devpart_template;
|
||||
model.start_sector = 0;
|
||||
model.num_sectors = 0x400000 / model.sector_size;
|
||||
model.is_emulated = true;
|
||||
model.emu_use_file = true;
|
||||
|
||||
/* Prepare boot1 file path. */
|
||||
snprintf(emummc_boot1_path, sizeof(emummc_boot1_path) - 1, "%s/%s", emummc_path, "boot1");
|
||||
|
||||
model.emu_num_parts = 0;
|
||||
model.emu_part_limit = 0;
|
||||
strcpy(model.emu_root_path, emummc_path);
|
||||
strcpy(model.emu_file_path, emummc_boot1_path);
|
||||
|
||||
/* Mount emulated boot1 device. */
|
||||
rc = emudev_mount_device("boot1", &model, emummc_boot1_path, 0, 0);
|
||||
rc = rawdev_mount_device("boot1", &model, false);
|
||||
|
||||
/* Failed to mount boot1. */
|
||||
if (rc == -1) {
|
||||
return -8;
|
||||
return -7;
|
||||
}
|
||||
|
||||
|
||||
/* Register emulated boot1 device. */
|
||||
rc = emudev_register_device("boot1");
|
||||
rc = rawdev_register_device("boot1");
|
||||
|
||||
/* Failed to register boot1 device. */
|
||||
if (rc == -1) {
|
||||
return -9;
|
||||
return -8;
|
||||
}
|
||||
|
||||
|
||||
/* Setup a template for raw NAND. */
|
||||
model = g_emummc_devpart_template;
|
||||
model.start_sector = 0;
|
||||
model.num_sectors = (256ull << 30) / model.sector_size;
|
||||
model.is_emulated = true;
|
||||
model.emu_use_file = true;
|
||||
model.emu_num_parts = num_parts;
|
||||
model.emu_part_limit = part_limit;
|
||||
strcpy(model.emu_root_path, emummc_path);
|
||||
strcpy(model.emu_file_path, emummc_path);
|
||||
|
||||
/* Mount emulated raw NAND device from single or multiple parts. */
|
||||
rc = emudev_mount_device("rawnand", &model, emummc_path, num_parts, part_limit);
|
||||
rc = rawdev_mount_device("rawnand", &model, false);
|
||||
|
||||
/* Failed to mount raw NAND. */
|
||||
if (rc == -1) {
|
||||
return -10;
|
||||
return -9;
|
||||
}
|
||||
|
||||
|
||||
/* Register emulated raw NAND device. */
|
||||
rc = emudev_register_device("rawnand");
|
||||
rc = rawdev_register_device("rawnand");
|
||||
|
||||
/* Failed to register raw NAND device. */
|
||||
if (rc == -1) {
|
||||
return -11;
|
||||
return -10;
|
||||
}
|
||||
|
||||
|
||||
/* Open emulated raw NAND device. */
|
||||
rawnand = fopen("rawnand:/", "rb");
|
||||
|
||||
/* Failed to open emulated raw NAND device. */
|
||||
if (rawnand == NULL) {
|
||||
return -12;
|
||||
return -11;
|
||||
}
|
||||
|
||||
|
||||
/* Iterate the GPT and mount each emulated raw NAND partition. */
|
||||
rc = gpt_iterate_through_emu_entries(rawnand, model.sector_size, nxfs_mount_emu_partition_gpt_callback, &model, emummc_path, num_parts, part_limit);
|
||||
rc = gpt_iterate_through_entries(rawnand, model.sector_size, nxfs_mount_partition_gpt_callback, &model);
|
||||
|
||||
/* Close emulated raw NAND device. */
|
||||
fclose(rawnand);
|
||||
|
||||
/* All emulated devices are ready. */
|
||||
if (rc == 0) {
|
||||
g_emudev_ready = true;
|
||||
g_rawdev_ready = true;
|
||||
g_is_emummc = true;
|
||||
}
|
||||
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -805,18 +735,6 @@ int nxfs_unmount_emmc() {
|
||||
return rc;
|
||||
}
|
||||
|
||||
int nxfs_unmount_emummc() {
|
||||
int rc = 0;
|
||||
|
||||
/* Unmount all emulated devices. */
|
||||
if (g_emudev_ready) {
|
||||
rc = emudev_unmount_all();
|
||||
g_emudev_ready = false;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int nxfs_init() {
|
||||
int rc;
|
||||
|
||||
@@ -832,5 +750,5 @@ int nxfs_init() {
|
||||
}
|
||||
|
||||
int nxfs_end() {
|
||||
return ((nxfs_unmount_sd() || nxfs_unmount_emmc() || nxfs_unmount_emummc()) ? -1 : 0);
|
||||
return ((nxfs_unmount_sd() || nxfs_unmount_emmc()) ? -1 : 0);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
#include "fs_dev.h"
|
||||
#include "raw_dev.h"
|
||||
#include "emu_dev.h"
|
||||
|
||||
int nxfs_init();
|
||||
int nxfs_end();
|
||||
|
||||
@@ -77,9 +77,6 @@ static rawdev_device_t *rawdev_find_device(const char *name) {
|
||||
|
||||
int rawdev_mount_device(const char *name, const device_partition_t *devpart, bool initialize_immediately) {
|
||||
rawdev_device_t *device = NULL;
|
||||
char drname[40];
|
||||
strcpy(drname, name);
|
||||
strcat(drname, ":");
|
||||
|
||||
if (name[0] == '\0' || devpart == NULL) {
|
||||
errno = EINVAL;
|
||||
@@ -205,6 +202,19 @@ int rawdev_unmount_device(const char *name) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rawdev_register_keys(const char *name, unsigned int target_firmware, BisPartition part) {
|
||||
rawdev_device_t *device = rawdev_find_device(name);
|
||||
|
||||
if (device == NULL) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
derive_bis_key(device->devpart.keys, part, target_firmware);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rawdev_unmount_all(void) {
|
||||
for (size_t i = 0; i < RAWDEV_MAX_DEVICES; i++) {
|
||||
int rc = rawdev_unmount_device(g_rawdev_devices[i].name);
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "device_partition.h"
|
||||
#include "key_derivation.h"
|
||||
|
||||
#define RAWDEV_MAX_DEVICES 16
|
||||
|
||||
@@ -29,6 +30,8 @@ int rawdev_register_device(const char *name);
|
||||
int rawdev_unregister_device(const char *name);
|
||||
int rawdev_unmount_device(const char *name); /* also unregisters. */
|
||||
|
||||
int rawdev_register_keys(const char *name, unsigned int target_firmware, BisPartition part);
|
||||
|
||||
int rawdev_unmount_all(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -51,7 +51,6 @@ 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;
|
||||
|
||||
static emummc_fs_ver_t g_fs_ver = FS_VER_1_0_0;
|
||||
|
||||
|
||||
@@ -157,12 +157,18 @@ __attribute__ ((noreturn)) void generic_panic(void) {
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void fatal_error(const char *fmt, ...) {
|
||||
/* Override the global logging level. */
|
||||
log_set_log_level(SCREEN_LOG_LEVEL_ERROR);
|
||||
|
||||
/* Display fatal error. */
|
||||
va_list args;
|
||||
print(SCREEN_LOG_LEVEL_ERROR, "Fatal error: ");
|
||||
va_start(args, fmt);
|
||||
vprint(SCREEN_LOG_LEVEL_ERROR, fmt, args);
|
||||
va_end(args);
|
||||
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "\n Press POWER to reboot.\n");
|
||||
|
||||
/* Wait for button and reboot. */
|
||||
wait_for_button_and_reboot();
|
||||
}
|
||||
|
||||
@@ -174,37 +180,3 @@ __attribute__((noinline)) bool overlaps(uint64_t as, uint64_t ae, uint64_t bs, u
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Adapted from https://gist.github.com/ccbrown/9722406 */
|
||||
void hexdump(const void* data, size_t size, uintptr_t addrbase) {
|
||||
const uint8_t *d = (const uint8_t *)data;
|
||||
char ascii[17];
|
||||
ascii[16] = '\0';
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
if (i % 16 == 0) {
|
||||
printf("%0*" PRIXPTR ": | ", 2 * sizeof(addrbase), addrbase + i);
|
||||
}
|
||||
printf("%02X ", d[i]);
|
||||
if (d[i] >= ' ' && d[i] <= '~') {
|
||||
ascii[i % 16] = d[i];
|
||||
} else {
|
||||
ascii[i % 16] = '.';
|
||||
}
|
||||
if ((i+1) % 8 == 0 || i+1 == size) {
|
||||
printf(" ");
|
||||
if ((i+1) % 16 == 0) {
|
||||
printf("| %s \n", ascii);
|
||||
} else if (i+1 == size) {
|
||||
ascii[(i+1) % 16] = '\0';
|
||||
if ((i+1) % 16 <= 8) {
|
||||
printf(" ");
|
||||
}
|
||||
for (size_t j = (i+1) % 16; j < 16; j++) {
|
||||
printf(" ");
|
||||
}
|
||||
printf("| %s \n", ascii);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,8 +118,6 @@ static inline bool check_32bit_address_range_in_program(uintptr_t addr, size_t s
|
||||
overlaps_a(start, end, __start__, __end__);
|
||||
}
|
||||
|
||||
void hexdump(const void* data, size_t size, uintptr_t addrbase);
|
||||
|
||||
__attribute__((noreturn)) void watchdog_reboot(void);
|
||||
__attribute__((noreturn)) void pmc_reboot(uint32_t scratch0);
|
||||
__attribute__((noreturn)) void reboot_to_fusee_primary(void);
|
||||
@@ -129,8 +127,6 @@ __attribute__((noreturn)) void wait_for_button_and_reboot(void);
|
||||
void wait_for_button(void);
|
||||
|
||||
__attribute__((noreturn)) void generic_panic(void);
|
||||
|
||||
__attribute__((noreturn)) void fatal_error(const char *fmt, ...);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[subrepo]
|
||||
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
||||
branch = master
|
||||
commit = 89f8944f8025c0557a18e64ceca7f65ab3090d82
|
||||
parent = 29f1eb807066bdb551f8b1f7352c97e5ac62d436
|
||||
commit = cac5957d3f4b1417cf76a83cf704a14a254dd4dc
|
||||
parent = 3726def6ecc547e64912ddb050737ebd296366e7
|
||||
method = merge
|
||||
cmdver = 0.4.1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
ATMOSPHERE_LIBRARIES := libmesosphere libstratosphere
|
||||
ATMOSPHERE_LIBRARIES := libmesosphere libstratosphere libexosphere
|
||||
|
||||
TOPTARGETS := all clean
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ export ATMOSPHERE_DEFINES := -DATMOSPHERE
|
||||
export ATMOSPHERE_SETTINGS := -fPIE -g
|
||||
export ATMOSPHERE_CFLAGS := -Wall -ffunction-sections -fdata-sections -fno-strict-aliasing -fwrapv \
|
||||
-fno-asynchronous-unwind-tables -fno-unwind-tables -fno-stack-protector \
|
||||
-Wno-format-truncation
|
||||
-Wno-format-truncation -Wno-format-zero-length -Wno-stringop-truncation
|
||||
|
||||
export ATMOSPHERE_CXXFLAGS := -fno-rtti -fno-exceptions -std=gnu++20
|
||||
export ATMOSPHERE_ASFLAGS :=
|
||||
|
||||
@@ -12,7 +12,7 @@ export CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
|
||||
export CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit
|
||||
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)/$(notdir $(TOPDIR)).specs -fno-asynchronous-unwind-tables -fno-unwind-tables -nostdlib -nostartfiles -g $(SETTINGS) -Wl,-Map,$(notdir $*.map) -Wl,-z,relro,-z,now
|
||||
|
||||
export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \
|
||||
-Wl,--wrap,__cxa_throw \
|
||||
|
||||
@@ -114,6 +114,7 @@
|
||||
#define APBDEV_PMC_SECURE_SCRATCH113 (0xB1C)
|
||||
#define APBDEV_PMC_SECURE_SCRATCH114 (0xB20)
|
||||
#define APBDEV_PMC_SECURE_SCRATCH115 (0xB24)
|
||||
#define APBDEV_PMC_SECURE_SCRATCH119 (0xB34)
|
||||
|
||||
|
||||
#define PMC_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (APBDEV_PMC, NAME)
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace ams::clkrst {
|
||||
reg::ReadWrite(g_register_address + param.clk_enb_offset, REG_BITS_VALUE(param.index, 1, 0));
|
||||
|
||||
/* Set the clock source. */
|
||||
if (param.clk_src != 0) {
|
||||
if (param.clk_src_offset != 0) {
|
||||
reg::Write(g_register_address + param.clk_src_offset, (param.clk_src << 29) | (param.clk_div << 0));
|
||||
}
|
||||
|
||||
@@ -89,11 +89,11 @@ namespace ams::clkrst {
|
||||
}
|
||||
|
||||
void EnableUartBClock() {
|
||||
EnableClock(UartAClock);
|
||||
EnableClock(UartBClock);
|
||||
}
|
||||
|
||||
void EnableUartCClock() {
|
||||
EnableClock(UartAClock);
|
||||
EnableClock(UartCClock);
|
||||
}
|
||||
|
||||
void EnableActmonClock() {
|
||||
|
||||
@@ -48,12 +48,14 @@ namespace ams::log {
|
||||
clkrst::EnableUartAClock();
|
||||
} else if constexpr (UartLogPort == uart::Port_LeftJoyCon) {
|
||||
/* Logging to left joy-con (e.g. with Joyless). */
|
||||
pinmux::SetupUartB();
|
||||
clkrst::EnableUartBClock();
|
||||
} else if constexpr (UartLogPort == uart::Port_RightJoyCon) {
|
||||
/* Logging to right joy-con (e.g. with Joyless). */
|
||||
static_assert(uart::Port_LeftJoyCon == uart::Port_C);
|
||||
pinmux::SetupUartC();
|
||||
clkrst::EnableUartCClock();
|
||||
} else if constexpr (UartLogPort == uart::Port_RightJoyCon) {
|
||||
/* Logging to right joy-con (e.g. with Joyless). */
|
||||
static_assert(uart::Port_RightJoyCon == uart::Port_B);
|
||||
pinmux::SetupUartB();
|
||||
clkrst::EnableUartBClock();
|
||||
} else {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace ams::pinmux {
|
||||
/* Get the registers. */
|
||||
const uintptr_t PINMUX = g_pinmux_address;
|
||||
|
||||
/* Configure Uart-B. */
|
||||
/* Configure Uart-C. */
|
||||
reg::Write(PINMUX + PINMUX_AUX_UART3_TX, PINMUX_REG_BITS_ENUM(AUX_UART3_PM, UARTC),
|
||||
PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE),
|
||||
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
|
||||
@@ -114,13 +114,13 @@ namespace ams::pinmux {
|
||||
|
||||
reg::Write(PINMUX + PINMUX_AUX_UART3_RX, PINMUX_REG_BITS_ENUM(AUX_UART3_PM, UARTC),
|
||||
PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE),
|
||||
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
|
||||
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, TRISTATE),
|
||||
PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE),
|
||||
PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE),
|
||||
PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE));
|
||||
|
||||
reg::Write(PINMUX + PINMUX_AUX_UART3_RTS, PINMUX_REG_BITS_ENUM(AUX_UART3_PM, UARTC),
|
||||
PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE),
|
||||
PINMUX_REG_BITS_ENUM(AUX_PUPD, PULL_DOWN),
|
||||
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
|
||||
PINMUX_REG_BITS_ENUM(AUX_E_INPUT, DISABLE),
|
||||
PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE),
|
||||
@@ -128,13 +128,16 @@ namespace ams::pinmux {
|
||||
|
||||
reg::Write(PINMUX + PINMUX_AUX_UART3_CTS, PINMUX_REG_BITS_ENUM(AUX_UART3_PM, UARTC),
|
||||
PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE),
|
||||
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
|
||||
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, TRISTATE),
|
||||
PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE),
|
||||
PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE),
|
||||
PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE));
|
||||
|
||||
/* Configure GPIO for Uart-C. */
|
||||
reg::ReadWrite(g_gpio_address + 0x00C, REG_BITS_VALUE(1, 4, 0));
|
||||
reg::ReadWrite(g_gpio_address + 0x118, REG_BITS_VALUE(0, 1, 1));
|
||||
reg::Read(g_gpio_address + 0x118);
|
||||
reg::ReadWrite(g_gpio_address + 0x00C, REG_BITS_VALUE(1, 1, 0));
|
||||
reg::Read(g_gpio_address + 0x00C);
|
||||
}
|
||||
|
||||
void SetupI2c1() {
|
||||
|
||||
@@ -33,11 +33,11 @@ namespace ams::uart {
|
||||
}
|
||||
|
||||
void WaitSymbols(int baud, u32 num) {
|
||||
util::WaitMicroSeconds(util::DivideUp(1'000'000, baud) * num);
|
||||
util::WaitMicroSeconds(util::DivideUp(num * 1'000'000, baud));
|
||||
}
|
||||
|
||||
void WaitCycles(int baud, u32 num) {
|
||||
util::WaitMicroSeconds(util::DivideUp(1'000'000, 16 * baud) * num);
|
||||
util::WaitMicroSeconds(util::DivideUp(num * 1'000'000, 16 * baud));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void WaitFifoNotFull(volatile UartRegisters *uart) {
|
||||
@@ -60,26 +60,6 @@ namespace ams::uart {
|
||||
|
||||
constexpr inline u32 LockBit = (1 << 6);
|
||||
|
||||
void Lock(volatile UartRegisters *reg) {
|
||||
while (true) {
|
||||
if (reg->mie != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
reg->irda_csr = LockBit;
|
||||
|
||||
if (reg->mie == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
reg->irda_csr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Unlock(volatile UartRegisters *reg) {
|
||||
reg->irda_csr = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SetRegisterAddress(uintptr_t address) {
|
||||
@@ -97,7 +77,13 @@ namespace ams::uart {
|
||||
constexpr u32 UartClock = 408000000;
|
||||
const u32 divisor = (UartClock + (baud_rate * 16) / 2) / (baud_rate * 16);
|
||||
|
||||
/* Disable DLAB and all interrupts. */
|
||||
/* Wait for idle state. */
|
||||
WaitIdle(uart, UART_VENDOR_STATE_TX_IDLE);
|
||||
|
||||
/* Wait 100 us. */
|
||||
util::WaitMicroSeconds(100);
|
||||
|
||||
/* Disable interrupts. */
|
||||
uart->lcr = uart->lcr & ~UART_LCR_DLAB;
|
||||
uart->ier = 0;
|
||||
uart->mcr = 0;
|
||||
@@ -128,8 +114,8 @@ namespace ams::uart {
|
||||
/* Wait for idle state. */
|
||||
WaitIdle(uart, UART_VENDOR_STATE_TX_IDLE | UART_VENDOR_STATE_RX_IDLE);
|
||||
|
||||
/* Set scratch register to 0. */
|
||||
uart->spr = 0;
|
||||
/* Wait 100 us. */
|
||||
util::WaitMicroSeconds(100);
|
||||
}
|
||||
|
||||
void SendText(Port port, const void *data, size_t size) {
|
||||
@@ -139,10 +125,6 @@ namespace ams::uart {
|
||||
/* Get pointer to data. */
|
||||
const u8 *p = static_cast<const u8 *>(data);
|
||||
|
||||
/* Lock the uart registers. */
|
||||
Lock(uart);
|
||||
ON_SCOPE_EXIT { Unlock(uart); };
|
||||
|
||||
/* Send each byte. */
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
WaitFifoNotFull(uart);
|
||||
|
||||
@@ -23,6 +23,17 @@
|
||||
|
||||
namespace ams::kern::arch::arm64::init {
|
||||
|
||||
inline void ClearPhysicalMemory(KPhysicalAddress address, size_t size) {
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(size, sizeof(u64)));
|
||||
|
||||
/* This Physical Address -> void * conversion is valid, because this is init page table code. */
|
||||
/* The MMU is necessarily not yet turned on, if we are creating an initial page table. */
|
||||
volatile u64 *ptr = reinterpret_cast<volatile u64 *>(GetInteger(address));
|
||||
for (size_t i = 0; i < size / sizeof(u64); ++i) {
|
||||
ptr[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
class KInitialPageTable {
|
||||
public:
|
||||
class IPageAllocator {
|
||||
@@ -61,9 +72,7 @@ namespace ams::kern::arch::arm64::init {
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE void ClearNewPageTable(KPhysicalAddress address) {
|
||||
/* This Physical Address -> void * conversion is valid, because this is page table code. */
|
||||
/* 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);
|
||||
ClearPhysicalMemory(address, PageSize);
|
||||
}
|
||||
private:
|
||||
size_t NOINLINE GetBlockCount(KVirtualAddress virt_addr, size_t size, size_t block_size) {
|
||||
@@ -705,7 +714,7 @@ namespace ams::kern::arch::arm64::init {
|
||||
this->state.next_address += PageSize;
|
||||
}
|
||||
|
||||
std::memset(reinterpret_cast<void *>(allocated), 0, PageSize);
|
||||
ClearPhysicalMemory(allocated, PageSize);
|
||||
return allocated;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,10 @@ namespace ams::kern::arch::arm64::cpu {
|
||||
EnsureInstructionConsistency();
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void Yield() {
|
||||
__asm__ __volatile__("yield" ::: "memory");
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void SwitchProcess(u64 ttbr, u32 proc_id) {
|
||||
SetTtbr0El1(ttbr);
|
||||
ContextIdRegisterAccessor(0).SetProcId(proc_id).Store();
|
||||
@@ -149,6 +153,25 @@ namespace ams::kern::arch::arm64::cpu {
|
||||
return true;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool CanAccessAtomic(KProcessAddress 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;
|
||||
}
|
||||
|
||||
return (par >> (BITSIZEOF(par) - BITSIZEOF(u8))) == 0xFF;
|
||||
}
|
||||
|
||||
/* Synchronization helpers. */
|
||||
NOINLINE void SynchronizeAllCores();
|
||||
|
||||
@@ -173,7 +196,7 @@ namespace ams::kern::arch::arm64::cpu {
|
||||
|
||||
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");
|
||||
__asm__ __volatile__("tlbi aside1is, %[value]" :: [value]"r"(value) : "memory");
|
||||
EnsureInstructionConsistency();
|
||||
}
|
||||
|
||||
|
||||
@@ -56,26 +56,69 @@ namespace ams::kern::arch::arm64::cpu {
|
||||
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(OslarEl1, oslar_el1)
|
||||
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(TpidrEl0, tpidr_el0)
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(TpidrRoEl0, tpidrro_el0)
|
||||
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(EsrEl1, esr_el1)
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(ElrEl1, elr_el1)
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(EsrEl1, esr_el1)
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(SpsrEl1, spsr_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)
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(MdscrEl1, mdscr_el1)
|
||||
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(CpacrEl1, cpacr_el1)
|
||||
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(ContextidrEl1, contextidr_el1)
|
||||
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(CntkCtlEl1, cntkctl_el1)
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(CntpCtlEl0, cntp_ctl_el0)
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(CntpCvalEl0, cntp_cval_el0)
|
||||
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(Daif, daif)
|
||||
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(SpEl0, sp_el0)
|
||||
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(IdAa64Dfr0El1, id_aa64dfr0_el1)
|
||||
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmcrEl0, pmcr_el0)
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmUserEnrEl0, pmuserenr_el0)
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmcCntrEl0, pmccntr_el0)
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmSelrEl0, pmselr_el0)
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmcCfiltrEl0, pmccfiltr_el0)
|
||||
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmIntEnSetEl1, pmintenset_el1)
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmCntEnSetEl0, pmcntenset_el0)
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmOvsSetEl0, pmovsset_el0)
|
||||
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmIntEnClrEl1, pmintenclr_el1)
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmCntEnClrEl0, pmcntenclr_el0)
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmOvsClrEl0, pmovsclr_el0)
|
||||
|
||||
#define FOR_I_IN_0_TO_30(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__) \
|
||||
HANDLER(16, ## __VA_ARGS__) HANDLER(17, ## __VA_ARGS__) HANDLER(18, ## __VA_ARGS__) HANDLER(19, ## __VA_ARGS__) \
|
||||
HANDLER(20, ## __VA_ARGS__) HANDLER(21, ## __VA_ARGS__) HANDLER(22, ## __VA_ARGS__) HANDLER(23, ## __VA_ARGS__) \
|
||||
HANDLER(24, ## __VA_ARGS__) HANDLER(25, ## __VA_ARGS__) HANDLER(26, ## __VA_ARGS__) HANDLER(27, ## __VA_ARGS__) \
|
||||
HANDLER(28, ## __VA_ARGS__) HANDLER(29, ## __VA_ARGS__) HANDLER(30, ## __VA_ARGS__)
|
||||
|
||||
#define MESOSPHERE_CPU_DEFINE_PMEV_ACCESSORS(ID, ...) \
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmevCntr##ID##El0, pmevcntr##ID##_el0) \
|
||||
MESOSPHERE_CPU_DEFINE_SYSREG_ACCESSORS(PmevTyper##ID##El0, pmevtyper##ID##_el0)
|
||||
|
||||
FOR_I_IN_0_TO_30(MESOSPHERE_CPU_DEFINE_PMEV_ACCESSORS)
|
||||
|
||||
#undef MESOSPHERE_CPU_DEFINE_PMEV_ACCESSORS
|
||||
#undef FOR_I_IN_0_TO_30
|
||||
|
||||
#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__) \
|
||||
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) \
|
||||
@@ -158,6 +201,15 @@ namespace ams::kern::arch::arm64::cpu {
|
||||
const size_t shift_value = this->GetBits(16, 6);
|
||||
return size_t(1) << (size_t(64) - shift_value);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool GetEpd0() const {
|
||||
return this->GetBits(7, 1) != 0;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE decltype(auto) SetEpd0(bool set) {
|
||||
this->SetBit(7, set);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(ArchitecturalFeatureAccessControl) {
|
||||
@@ -189,6 +241,10 @@ namespace ams::kern::arch::arm64::cpu {
|
||||
constexpr ALWAYS_INLINE size_t GetNumBreakpoints() const {
|
||||
return this->GetBits(12, 4);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE size_t GetNumContextAwareBreakpoints() const {
|
||||
return this->GetBits(28, 4);
|
||||
}
|
||||
};
|
||||
|
||||
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(MonitorDebugSystemControl) {
|
||||
@@ -387,6 +443,27 @@ namespace ams::kern::arch::arm64::cpu {
|
||||
/* TODO: Other bitfield accessors? */
|
||||
};
|
||||
|
||||
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(PerformanceMonitorsControl) {
|
||||
public:
|
||||
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS(PerformanceMonitorsControl, pmcr_el0)
|
||||
public:
|
||||
constexpr ALWAYS_INLINE u64 GetN() const {
|
||||
return this->GetBits(11, 5);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE decltype(auto) SetEventCounterReset(bool en) {
|
||||
this->SetBit(1, en);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE decltype(auto) SetCycleCounterReset(bool en) {
|
||||
this->SetBit(2, en);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* TODO: Other bitfield accessors? */
|
||||
};
|
||||
|
||||
#undef FOR_I_IN_0_TO_15
|
||||
#undef MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS_FUNCTIONS
|
||||
#undef MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS
|
||||
|
||||
@@ -30,6 +30,38 @@ namespace ams::kern::arch::arm64 {
|
||||
class KDebug final : public KAutoObjectWithSlabHeapAndContainer<KDebug, KDebugBase> {
|
||||
MESOSPHERE_AUTOOBJECT_TRAITS(KDebug, KSynchronizationObject);
|
||||
public:
|
||||
explicit KDebug() { /* ... */ }
|
||||
virtual ~KDebug() { /* ... */ }
|
||||
|
||||
static void PostDestroy(uintptr_t arg) { /* ... */ }
|
||||
public:
|
||||
virtual Result GetThreadContextImpl(ams::svc::ThreadContext *out, KThread *thread, u32 context_flags) override;
|
||||
virtual Result SetThreadContextImpl(const ams::svc::ThreadContext &ctx, KThread *thread, u32 context_flags) override;
|
||||
private:
|
||||
Result GetFpuContext(ams::svc::ThreadContext *out, KThread *thread, u32 context_flags);
|
||||
Result SetFpuContext(const ams::svc::ThreadContext &ctx, KThread *thread, u32 context_flags);
|
||||
public:
|
||||
static uintptr_t GetProgramCounter(const KThread &thread);
|
||||
static void SetPreviousProgramCounter();
|
||||
|
||||
static Result BreakIfAttached(ams::svc::BreakReason break_reason, uintptr_t address, size_t size);
|
||||
static Result SetHardwareBreakPoint(ams::svc::HardwareBreakPointRegisterName name, u64 flags, u64 value);
|
||||
|
||||
static constexpr bool IsBreakInstruction(u32 insn, u32 psr) {
|
||||
constexpr u32 BreakInstructionAarch64 = 0xE7FFFFFF;
|
||||
constexpr u32 BreakInstructionAarch32 = 0xE7FFDEFE;
|
||||
constexpr u32 BreakInstructionThumb32 = 0xB68E;
|
||||
if ((psr & 0x10) == 0) {
|
||||
return insn == BreakInstructionAarch64;
|
||||
} else {
|
||||
if ((psr & 0x20) == 0) {
|
||||
return insn == BreakInstructionAarch32;
|
||||
} else {
|
||||
return insn == BreakInstructionThumb32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: This is a placeholder definition. */
|
||||
};
|
||||
|
||||
|
||||
@@ -22,9 +22,26 @@ namespace ams::kern::arch::arm64 {
|
||||
u64 x[(30 - 0) + 1];
|
||||
u64 sp;
|
||||
u64 pc;
|
||||
u64 psr;
|
||||
u32 psr;
|
||||
u32 write;
|
||||
u64 tpidr;
|
||||
u64 reserved;
|
||||
|
||||
constexpr void GetSvcThreadContext(ams::svc::LastThreadContext *out) const {
|
||||
if ((this->psr & 0x10) == 0) {
|
||||
/* aarch64 thread. */
|
||||
out->fp = this->x[29];
|
||||
out->sp = this->sp;
|
||||
out->lr = this->x[30];
|
||||
out->pc = this->pc;
|
||||
} else {
|
||||
/* aarch32 thread. */
|
||||
out->fp = this->x[11];
|
||||
out->sp = this->x[13];
|
||||
out->lr = this->x[14];
|
||||
out->pc = this->pc;
|
||||
}
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(KExceptionContext) == 0x120);
|
||||
|
||||
|
||||
@@ -112,17 +112,17 @@ namespace ams::kern::arch::arm64 {
|
||||
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];
|
||||
u32 isenabler[NumLocalInterrupts / 32];
|
||||
u32 ipriorityr[NumLocalInterrupts / 4];
|
||||
u32 itargetsr[NumLocalInterrupts / 4];
|
||||
u32 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];
|
||||
u32 isenabler[NumGlobalInterrupts / 32];
|
||||
u32 ipriorityr[NumGlobalInterrupts / 4];
|
||||
u32 itargetsr[NumGlobalInterrupts / 4];
|
||||
u32 icfgr[NumGlobalInterrupts / 16];
|
||||
};
|
||||
|
||||
enum PriorityLevel : u8 {
|
||||
@@ -142,6 +142,11 @@ namespace ams::kern::arch::arm64 {
|
||||
|
||||
void Initialize(s32 core_id);
|
||||
void Finalize(s32 core_id);
|
||||
|
||||
void SaveCoreLocal(LocalState *state) const;
|
||||
void SaveGlobal(GlobalState *state) const;
|
||||
void RestoreCoreLocal(const LocalState *state) const;
|
||||
void RestoreGlobal(const GlobalState *state) const;
|
||||
public:
|
||||
u32 GetIrq() const {
|
||||
return this->gicc->iar;
|
||||
@@ -213,12 +218,10 @@ namespace ams::kern::arch::arm64 {
|
||||
this->gicc->eoir = irq;
|
||||
}
|
||||
|
||||
bool IsInterruptDefined(s32 irq) {
|
||||
bool IsInterruptDefined(s32 irq) const {
|
||||
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);
|
||||
|
||||
@@ -67,10 +67,21 @@ namespace ams::kern::arch::arm64 {
|
||||
NOINLINE void Initialize(s32 core_id);
|
||||
NOINLINE void Finalize(s32 core_id);
|
||||
|
||||
bool IsInterruptDefined(s32 irq) {
|
||||
NOINLINE void Save(s32 core_id);
|
||||
NOINLINE void Restore(s32 core_id);
|
||||
|
||||
bool IsInterruptDefined(s32 irq) const {
|
||||
return this->interrupt_controller.IsInterruptDefined(irq);
|
||||
}
|
||||
|
||||
bool IsGlobal(s32 irq) const {
|
||||
return this->interrupt_controller.IsGlobal(irq);
|
||||
}
|
||||
|
||||
bool IsLocal(s32 irq) const {
|
||||
return this->interrupt_controller.IsLocal(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);
|
||||
|
||||
|
||||
@@ -182,25 +182,31 @@ namespace ams::kern::arch::arm64 {
|
||||
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 MapL1Blocks(KProcessAddress virt_addr, KPhysicalAddress phys_addr, size_t num_pages, PageTableEntry entry_template, PageLinkedList *page_list, bool reuse_ll);
|
||||
Result MapL2Blocks(KProcessAddress virt_addr, KPhysicalAddress phys_addr, size_t num_pages, PageTableEntry entry_template, PageLinkedList *page_list, bool reuse_ll);
|
||||
Result MapL3Blocks(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:
|
||||
return this->MapL1Blocks(virt_addr, phys_addr, num_pages, entry_template, page_list, reuse_ll);
|
||||
case L2ContiguousBlockSize:
|
||||
entry_template.SetContiguous(true);
|
||||
[[fallthrough]];
|
||||
#ifdef ATMOSPHERE_BOARD_NINTENDO_NX
|
||||
case L2TegraSmmuBlockSize:
|
||||
#endif
|
||||
case L2BlockSize:
|
||||
case L3BlockSize:
|
||||
break;
|
||||
case L2ContiguousBlockSize:
|
||||
return this->MapL2Blocks(virt_addr, phys_addr, num_pages, entry_template, page_list, reuse_ll);
|
||||
case L3ContiguousBlockSize:
|
||||
entry_template.SetContiguous(true);
|
||||
break;
|
||||
[[fallthrough]];
|
||||
case L3BlockSize:
|
||||
return this->MapL3Blocks(virt_addr, phys_addr, num_pages, entry_template, page_list, reuse_ll);
|
||||
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);
|
||||
|
||||
@@ -106,6 +106,8 @@ namespace ams::kern::arch::arm64 {
|
||||
NOINLINE void InitializeForProcess(void *tb, KVirtualAddress start, KVirtualAddress end);
|
||||
L1PageTableEntry *Finalize();
|
||||
|
||||
void Dump(uintptr_t start, size_t size) const;
|
||||
|
||||
bool BeginTraversal(TraversalEntry *out_entry, TraversalContext *out_context, KProcessAddress address) const;
|
||||
bool ContinueTraversal(TraversalEntry *out_entry, TraversalContext *context) const;
|
||||
|
||||
|
||||
@@ -44,6 +44,10 @@ namespace ams::kern::arch::arm64 {
|
||||
return this->page_table.SetProcessMemoryPermission(addr, size, perm);
|
||||
}
|
||||
|
||||
Result SetMemoryAttribute(KProcessAddress addr, size_t size, u32 mask, u32 attr) {
|
||||
return this->page_table.SetMemoryAttribute(addr, size, mask, attr);
|
||||
}
|
||||
|
||||
Result SetHeapSize(KProcessAddress *out, size_t size) {
|
||||
return this->page_table.SetHeapSize(out, size);
|
||||
}
|
||||
@@ -56,6 +60,34 @@ namespace ams::kern::arch::arm64 {
|
||||
return this->page_table.QueryInfo(out_info, out_page_info, addr);
|
||||
}
|
||||
|
||||
Result QueryPhysicalAddress(ams::svc::PhysicalMemoryInfo *out, KProcessAddress address) const {
|
||||
return this->page_table.QueryPhysicalAddress(out, address);
|
||||
}
|
||||
|
||||
Result QueryStaticMapping(KProcessAddress *out, KPhysicalAddress address, size_t size) const {
|
||||
return this->page_table.QueryStaticMapping(out, address, size);
|
||||
}
|
||||
|
||||
Result QueryIoMapping(KProcessAddress *out, KPhysicalAddress address, size_t size) const {
|
||||
return this->page_table.QueryIoMapping(out, address, size);
|
||||
}
|
||||
|
||||
Result MapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
|
||||
return this->page_table.MapMemory(dst_address, src_address, size);
|
||||
}
|
||||
|
||||
Result UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
|
||||
return this->page_table.UnmapMemory(dst_address, src_address, size);
|
||||
}
|
||||
|
||||
Result MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
|
||||
return this->page_table.MapCodeMemory(dst_address, src_address, size);
|
||||
}
|
||||
|
||||
Result UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) {
|
||||
return this->page_table.UnmapCodeMemory(dst_address, src_address, size);
|
||||
}
|
||||
|
||||
Result MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) {
|
||||
return this->page_table.MapIo(phys_addr, size, perm);
|
||||
}
|
||||
@@ -72,6 +104,10 @@ namespace ams::kern::arch::arm64 {
|
||||
return this->page_table.MapPageGroup(addr, pg, state, perm);
|
||||
}
|
||||
|
||||
Result UnmapPageGroup(KProcessAddress address, const KPageGroup &pg, KMemoryState state) {
|
||||
return this->page_table.UnmapPageGroup(address, pg, state);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -80,6 +116,10 @@ namespace ams::kern::arch::arm64 {
|
||||
return this->page_table.MapPages(out_addr, num_pages, state, perm);
|
||||
}
|
||||
|
||||
Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state, KMemoryPermission perm) {
|
||||
return this->page_table.MapPages(address, num_pages, state, perm);
|
||||
}
|
||||
|
||||
Result UnmapPages(KProcessAddress addr, size_t num_pages, KMemoryState state) {
|
||||
return this->page_table.UnmapPages(addr, num_pages, state);
|
||||
}
|
||||
@@ -88,11 +128,123 @@ namespace ams::kern::arch::arm64 {
|
||||
return this->page_table.MakeAndOpenPageGroup(out, address, num_pages, state_mask, state, perm_mask, perm, attr_mask, attr);
|
||||
}
|
||||
|
||||
Result MakeAndOpenPageGroupContiguous(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.MakeAndOpenPageGroupContiguous(out, address, num_pages, state_mask, state, perm_mask, perm, attr_mask, attr);
|
||||
}
|
||||
|
||||
Result InvalidateProcessDataCache(KProcessAddress address, size_t size) {
|
||||
return this->page_table.InvalidateProcessDataCache(address, size);
|
||||
}
|
||||
|
||||
Result ReadDebugMemory(void *buffer, KProcessAddress address, size_t size) {
|
||||
return this->page_table.ReadDebugMemory(buffer, address, size);
|
||||
}
|
||||
|
||||
Result WriteDebugMemory(KProcessAddress address, const void *buffer, size_t size) {
|
||||
return this->page_table.WriteDebugMemory(address, buffer, size);
|
||||
}
|
||||
|
||||
Result LockForDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned) {
|
||||
return this->page_table.LockForDeviceAddressSpace(out, address, size, perm, is_aligned);
|
||||
}
|
||||
|
||||
Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size) {
|
||||
return this->page_table.UnlockForDeviceAddressSpace(address, size);
|
||||
}
|
||||
|
||||
Result LockForIpcUserBuffer(KPhysicalAddress *out, KProcessAddress address, size_t size) {
|
||||
return this->page_table.LockForIpcUserBuffer(out, address, size);
|
||||
}
|
||||
|
||||
Result UnlockForIpcUserBuffer(KProcessAddress address, size_t size) {
|
||||
return this->page_table.UnlockForIpcUserBuffer(address, size);
|
||||
}
|
||||
|
||||
Result LockForTransferMemory(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm) {
|
||||
return this->page_table.LockForTransferMemory(out, address, size, perm);
|
||||
}
|
||||
|
||||
Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup &pg) {
|
||||
return this->page_table.UnlockForTransferMemory(address, size, pg);
|
||||
}
|
||||
|
||||
Result LockForCodeMemory(KPageGroup *out, KProcessAddress address, size_t size) {
|
||||
return this->page_table.LockForCodeMemory(out, address, size);
|
||||
}
|
||||
|
||||
Result UnlockForCodeMemory(KProcessAddress address, size_t size, const KPageGroup &pg) {
|
||||
return this->page_table.UnlockForCodeMemory(address, size, pg);
|
||||
}
|
||||
|
||||
Result CopyMemoryFromLinearToUser(KProcessAddress dst_addr, size_t size, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr) {
|
||||
return this->page_table.CopyMemoryFromLinearToUser(dst_addr, size, src_addr, src_state_mask, src_state, src_test_perm, src_attr_mask, src_attr);
|
||||
}
|
||||
|
||||
Result CopyMemoryFromLinearToKernel(KProcessAddress dst_addr, size_t size, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr) {
|
||||
return this->page_table.CopyMemoryFromLinearToKernel(dst_addr, size, src_addr, src_state_mask, src_state, src_test_perm, src_attr_mask, src_attr);
|
||||
}
|
||||
|
||||
Result CopyMemoryFromUserToLinear(KProcessAddress dst_addr, size_t size, u32 dst_state_mask, u32 dst_state, KMemoryPermission dst_test_perm, u32 dst_attr_mask, u32 dst_attr, KProcessAddress src_addr) {
|
||||
return this->page_table.CopyMemoryFromUserToLinear(dst_addr, size, dst_state_mask, dst_state, dst_test_perm, dst_attr_mask, dst_attr, src_addr);
|
||||
}
|
||||
|
||||
Result CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, size_t size, u32 dst_state_mask, u32 dst_state, KMemoryPermission dst_test_perm, u32 dst_attr_mask, u32 dst_attr, KProcessAddress src_addr) {
|
||||
return this->page_table.CopyMemoryFromKernelToLinear(dst_addr, size, dst_state_mask, dst_state, dst_test_perm, dst_attr_mask, dst_attr, src_addr);
|
||||
}
|
||||
|
||||
Result CopyMemoryFromHeapToHeap(KProcessPageTable &dst_page_table, KProcessAddress dst_addr, size_t size, u32 dst_state_mask, u32 dst_state, KMemoryPermission dst_test_perm, u32 dst_attr_mask, u32 dst_attr, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr) {
|
||||
return this->page_table.CopyMemoryFromHeapToHeap(dst_page_table.page_table, dst_addr, size, dst_state_mask, dst_state, dst_test_perm, dst_attr_mask, dst_attr, src_addr, src_state_mask, src_state, src_test_perm, src_attr_mask, src_attr);
|
||||
}
|
||||
|
||||
Result CopyMemoryFromHeapToHeapWithoutCheckDestination(KProcessPageTable &dst_page_table, KProcessAddress dst_addr, size_t size, u32 dst_state_mask, u32 dst_state, KMemoryPermission dst_test_perm, u32 dst_attr_mask, u32 dst_attr, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr) {
|
||||
return this->page_table.CopyMemoryFromHeapToHeapWithoutCheckDestination(dst_page_table.page_table, dst_addr, size, dst_state_mask, dst_state, dst_test_perm, dst_attr_mask, dst_attr, src_addr, src_state_mask, src_state, src_test_perm, src_attr_mask, src_attr);
|
||||
}
|
||||
|
||||
Result SetupForIpc(KProcessAddress *out_dst_addr, size_t size, KProcessAddress src_addr, KProcessPageTable &src_page_table, KMemoryPermission test_perm, KMemoryState dst_state, bool send) {
|
||||
return this->page_table.SetupForIpc(out_dst_addr, size, src_addr, src_page_table.page_table, test_perm, dst_state, send);
|
||||
}
|
||||
|
||||
Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state, KProcess *server_process) {
|
||||
return this->page_table.CleanupForIpcServer(address, size, dst_state, server_process);
|
||||
}
|
||||
|
||||
Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state) {
|
||||
return this->page_table.CleanupForIpcClient(address, size, dst_state);
|
||||
}
|
||||
|
||||
Result MapPhysicalMemory(KProcessAddress address, size_t size) {
|
||||
return this->page_table.MapPhysicalMemory(address, size);
|
||||
}
|
||||
|
||||
Result UnmapPhysicalMemory(KProcessAddress address, size_t size) {
|
||||
return this->page_table.UnmapPhysicalMemory(address, size);
|
||||
}
|
||||
|
||||
Result MapPhysicalMemoryUnsafe(KProcessAddress address, size_t size) {
|
||||
return this->page_table.MapPhysicalMemoryUnsafe(address, size);
|
||||
}
|
||||
|
||||
Result UnmapPhysicalMemoryUnsafe(KProcessAddress address, size_t size) {
|
||||
return this->page_table.UnmapPhysicalMemoryUnsafe(address, size);
|
||||
}
|
||||
|
||||
void DumpTable() const {
|
||||
return this->page_table.DumpTable();
|
||||
}
|
||||
|
||||
void DumpMemoryBlocks() const {
|
||||
return this->page_table.DumpMemoryBlocks();
|
||||
}
|
||||
|
||||
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 IsInAliasRegion(KProcessAddress addr, size_t size) const { return this->page_table.IsInAliasRegion(addr, size); }
|
||||
bool IsInUnsafeAliasRegion(KProcessAddress addr, size_t size) const { return this->page_table.IsInUnsafeAliasRegion(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(); }
|
||||
@@ -109,8 +261,11 @@ namespace ams::kern::arch::arm64 {
|
||||
size_t GetKernelMapRegionSize() const { return this->page_table.GetKernelMapRegionSize(); }
|
||||
size_t GetAliasCodeRegionSize() const { return this->page_table.GetAliasCodeRegionSize(); }
|
||||
|
||||
size_t GetNormalMemorySize() const { return this->page_table.GetNormalMemorySize(); }
|
||||
|
||||
u32 GetAllocateOption() const { return this->page_table.GetAllocateOption(); }
|
||||
|
||||
KPhysicalAddress GetHeapPhysicalAddress(KVirtualAddress address) const {
|
||||
/* TODO: Better way to convert address type? */
|
||||
return this->page_table.GetHeapPhysicalAddress(address);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <mesosphere/kern_select_cpu.hpp>
|
||||
|
||||
namespace ams::kern::arch::arm64 {
|
||||
|
||||
template<typename T>
|
||||
concept SlabHeapNode = requires (T &t) {
|
||||
{ t.next } -> std::convertible_to<T *>;
|
||||
};
|
||||
|
||||
template<typename T> requires SlabHeapNode<T>
|
||||
ALWAYS_INLINE T *AllocateFromSlabAtomic(T **head) {
|
||||
u32 tmp;
|
||||
T *node, *next;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"1:\n"
|
||||
" ldaxr %[node], [%[head]]\n"
|
||||
" cbz %[node], 2f\n"
|
||||
" ldr %[next], [%[node]]\n"
|
||||
" stlxr %w[tmp], %[next], [%[head]]\n"
|
||||
" cbnz %w[tmp], 1b\n"
|
||||
" b 3f\n"
|
||||
"2:\n"
|
||||
" clrex\n"
|
||||
"3:\n"
|
||||
: [tmp]"=&r"(tmp), [node]"=&r"(node), [next]"=&r"(next), [head]"+&r"(head)
|
||||
:
|
||||
: "cc", "memory"
|
||||
);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
template<typename T> requires SlabHeapNode<T>
|
||||
ALWAYS_INLINE void FreeToSlabAtomic(T **head, T *node) {
|
||||
u32 tmp;
|
||||
T *next;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"1:\n"
|
||||
" ldaxr %[next], [%[head]]\n"
|
||||
" str %[next], [%[node]]\n"
|
||||
" stlxr %w[tmp], %[node], [%[head]]\n"
|
||||
" cbnz %w[tmp], 1b\n"
|
||||
"2:\n"
|
||||
: [tmp]"=&r"(tmp), [node]"+&r"(node), [next]"=&r"(next), [head]"+&r"(head)
|
||||
:
|
||||
: "cc", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,9 +23,9 @@ namespace ams::kern::arch::arm64 {
|
||||
class KSupervisorPageTable {
|
||||
private:
|
||||
KPageTable page_table;
|
||||
u64 ttbr0[cpu::NumCores];
|
||||
u64 ttbr0_identity[cpu::NumCores];
|
||||
public:
|
||||
constexpr KSupervisorPageTable() : page_table(), ttbr0() { /* ... */ }
|
||||
constexpr KSupervisorPageTable() : page_table(), ttbr0_identity() { /* ... */ }
|
||||
|
||||
NOINLINE void Initialize(s32 core_id);
|
||||
|
||||
@@ -41,8 +41,6 @@ namespace ams::kern::arch::arm64 {
|
||||
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);
|
||||
}
|
||||
@@ -62,6 +60,8 @@ namespace ams::kern::arch::arm64 {
|
||||
bool GetPhysicalAddress(KPhysicalAddress *out, KProcessAddress address) const {
|
||||
return this->page_table.GetPhysicalAddress(out, address);
|
||||
}
|
||||
|
||||
constexpr u64 GetIdentityMapTtbr0(s32 core_id) const { return this->ttbr0_identity[core_id]; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -66,7 +66,21 @@ namespace ams::kern::arch::arm64 {
|
||||
|
||||
static void FpuContextSwitchHandler(KThread *thread);
|
||||
|
||||
/* TODO: More methods (especially FPU management) */
|
||||
u32 GetFpcr() const { return this->fpcr; }
|
||||
u32 GetFpsr() const { return this->fpsr; }
|
||||
|
||||
void SetFpcr(u32 v) { this->fpcr = v; }
|
||||
void SetFpsr(u32 v) { this->fpsr = v; }
|
||||
|
||||
void CloneFpuStatus();
|
||||
|
||||
void SetFpuRegisters(const u128 *v, bool is_64_bit);
|
||||
|
||||
const u128 *GetFpuRegisters() const { return this->fpu_registers; }
|
||||
public:
|
||||
static void OnThreadTerminating(const KThread *thread);
|
||||
};
|
||||
|
||||
void GetUserContext(ams::svc::ThreadContext *out, const KThread *thread);
|
||||
|
||||
}
|
||||
@@ -39,6 +39,10 @@ namespace ams::kern::arch::arm64 {
|
||||
static bool ClearMemoryAligned64Bit(void *dst, size_t size);
|
||||
static bool ClearMemorySize32Bit(void *dst);
|
||||
|
||||
static bool UpdateLockAtomic(u32 *out, u32 *address, u32 if_zero, u32 new_orr_mask);
|
||||
static bool UpdateIfEqualAtomic(s32 *out, s32 *address, s32 compare_value, s32 new_value);
|
||||
static bool DecrementIfLessThanAtomic(s32 *out, s32 *address, s32 compare);
|
||||
|
||||
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);
|
||||
|
||||
@@ -35,6 +35,14 @@ namespace ams::kern::board::nintendo::nx {
|
||||
u32 hs_attached_value;
|
||||
u32 hs_detached_value;
|
||||
private:
|
||||
static ALWAYS_INLINE bool IsHeapVirtualAddress(KVirtualAddress addr) {
|
||||
return KMemoryLayout::IsHeapVirtualAddress(nullptr, addr);
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE bool IsHeapPhysicalAddress(KPhysicalAddress addr) {
|
||||
return KMemoryLayout::IsHeapPhysicalAddress(nullptr, addr);
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE KVirtualAddress GetHeapVirtualAddress(KPhysicalAddress addr) {
|
||||
return KPageTable::GetHeapVirtualAddress(addr);
|
||||
}
|
||||
@@ -53,7 +61,30 @@ namespace ams::kern::board::nintendo::nx {
|
||||
public:
|
||||
constexpr KDevicePageTable() : tables(), table_asids(), attached_device(), attached_value(), detached_value(), hs_attached_value(), hs_detached_value() { /* ... */ }
|
||||
|
||||
Result Initialize(u64 space_address, u64 space_size);
|
||||
void Finalize();
|
||||
|
||||
Result Attach(ams::svc::DeviceName device_name, u64 space_address, u64 space_size);
|
||||
Result Detach(ams::svc::DeviceName device_name);
|
||||
|
||||
Result Map(size_t *out_mapped_size, const KPageGroup &pg, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool refresh_mappings);
|
||||
Result Unmap(const KPageGroup &pg, KDeviceVirtualAddress device_address);
|
||||
private:
|
||||
Result MapDevicePage(size_t *out_mapped_size, s32 &num_pt, s32 max_pt, KPhysicalAddress phys_addr, u64 size, KDeviceVirtualAddress address, ams::svc::MemoryPermission device_perm);
|
||||
|
||||
Result MapImpl(size_t *out_mapped_size, s32 &num_pt, s32 max_pt, const KPageGroup &pg, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm);
|
||||
void UnmapImpl(KDeviceVirtualAddress address, u64 size, bool force);
|
||||
|
||||
bool IsFree(KDeviceVirtualAddress address, u64 size) const;
|
||||
Result MakePageGroup(KPageGroup *out, KDeviceVirtualAddress address, u64 size) const;
|
||||
bool Compare(const KPageGroup &pg, KDeviceVirtualAddress device_address) const;
|
||||
public:
|
||||
static void Initialize();
|
||||
|
||||
static void Lock();
|
||||
static void Unlock();
|
||||
static void Sleep();
|
||||
static void Wakeup();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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 {
|
||||
|
||||
constexpr inline size_t MainMemorySize = 4_GB;
|
||||
|
||||
}
|
||||
@@ -48,7 +48,7 @@ namespace ams::kern::board::nintendo::nx {
|
||||
|
||||
/* 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 Result ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value);
|
||||
|
||||
static ALWAYS_INLINE u32 ReadRegisterPrivileged(ams::svc::PhysicalAddress address) {
|
||||
u32 v;
|
||||
@@ -67,6 +67,11 @@ namespace ams::kern::board::nintendo::nx {
|
||||
|
||||
/* User access. */
|
||||
static void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args);
|
||||
|
||||
/* Secure Memory. */
|
||||
static size_t CalculateRequiredSecureMemorySize(size_t size, u32 pool);
|
||||
static Result AllocateSecureMemory(KVirtualAddress *out, size_t size, u32 pool);
|
||||
static void FreeSecureMemory(KVirtualAddress address, size_t size, u32 pool);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
#include <mesosphere/kern_common.hpp>
|
||||
#include <mesosphere/svc/kern_svc_k_user_pointer.hpp>
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
@@ -26,6 +27,12 @@ namespace ams::kern {
|
||||
|
||||
static NOINLINE void Printf(const char *format, ...) __attribute__((format(printf, 1, 2)));
|
||||
static NOINLINE void VPrintf(const char *format, ::std::va_list vl);
|
||||
|
||||
static NOINLINE Result PrintUserString(ams::kern::svc::KUserPointer<const char *> user_str, size_t len);
|
||||
|
||||
/* Functionality for preserving across sleep. */
|
||||
static NOINLINE void Save();
|
||||
static NOINLINE void Restore();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -22,9 +22,9 @@ namespace ams::kern {
|
||||
struct KAddressSpaceInfo {
|
||||
public:
|
||||
enum Type {
|
||||
Type_32Bit = 0,
|
||||
Type_Small64Bit = 1,
|
||||
Type_Large64Bit = 2,
|
||||
Type_MapSmall = 0,
|
||||
Type_MapLarge = 1,
|
||||
Type_Map39Bit = 2,
|
||||
Type_Heap = 3,
|
||||
Type_Stack = 4,
|
||||
Type_Alias = 5,
|
||||
|
||||
@@ -127,6 +127,7 @@ namespace ams::kern {
|
||||
u32 cur_ref_count = this->ref_count.load(std::memory_order_acquire);
|
||||
do {
|
||||
if (AMS_UNLIKELY(cur_ref_count == 0)) {
|
||||
MESOSPHERE_AUDIT(cur_ref_count != 0);
|
||||
return false;
|
||||
}
|
||||
MESOSPHERE_ABORT_UNLESS(cur_ref_count < cur_ref_count + 1);
|
||||
@@ -177,10 +178,12 @@ namespace ams::kern {
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
template<typename T> requires std::derived_from<T, KAutoObject>
|
||||
class KScopedAutoObject {
|
||||
static_assert(std::is_base_of<KAutoObject, T>::value);
|
||||
NON_COPYABLE(KScopedAutoObject);
|
||||
private:
|
||||
template<typename U>
|
||||
friend class KScopedAutoObject;
|
||||
private:
|
||||
T *obj;
|
||||
private:
|
||||
@@ -202,12 +205,28 @@ namespace ams::kern {
|
||||
this->obj = nullptr;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE KScopedAutoObject(KScopedAutoObject &&rhs) {
|
||||
this->obj = rhs.obj;
|
||||
rhs.obj = nullptr;
|
||||
template<typename U> requires (std::derived_from<T, U> || std::derived_from<U, T>)
|
||||
constexpr ALWAYS_INLINE KScopedAutoObject(KScopedAutoObject<U> &&rhs) {
|
||||
if constexpr (std::derived_from<U, T>) {
|
||||
/* Upcast. */
|
||||
this->obj = rhs.obj;
|
||||
rhs.obj = nullptr;
|
||||
} else {
|
||||
/* Downcast. */
|
||||
T *derived = nullptr;
|
||||
if (rhs.obj != nullptr) {
|
||||
derived = rhs.obj->template DynamicCast<T *>();
|
||||
if (derived == nullptr) {
|
||||
rhs.obj->Close();
|
||||
}
|
||||
}
|
||||
|
||||
this->obj = derived;
|
||||
rhs.obj = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE KScopedAutoObject &operator=(KScopedAutoObject &&rhs) {
|
||||
constexpr ALWAYS_INLINE KScopedAutoObject<T> &operator=(KScopedAutoObject<T> &&rhs) {
|
||||
rhs.Swap(*this);
|
||||
return *this;
|
||||
}
|
||||
@@ -221,6 +240,8 @@ namespace ams::kern {
|
||||
|
||||
constexpr ALWAYS_INLINE T *GetPointerUnsafe() { return this->obj; }
|
||||
|
||||
constexpr ALWAYS_INLINE T *ReleasePointerUnsafe() { T *ret = this->obj; this->obj = nullptr; return ret; }
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsNull() const { return this->obj == nullptr; }
|
||||
constexpr ALWAYS_INLINE bool IsNotNull() const { return this->obj != nullptr; }
|
||||
};
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace ams::kern {
|
||||
class KAutoObjectWithListContainer {
|
||||
NON_COPYABLE(KAutoObjectWithListContainer);
|
||||
NON_MOVEABLE(KAutoObjectWithListContainer);
|
||||
private:
|
||||
public:
|
||||
using ListType = util::IntrusiveRedBlackTreeMemberTraits<&KAutoObjectWithList::list_node>::TreeType<KAutoObjectWithList>;
|
||||
public:
|
||||
class ListAccessor : public KScopedLightLock {
|
||||
|
||||
@@ -221,6 +221,12 @@ namespace ams::kern {
|
||||
data[id / BitsPerWord] &= ~(1ul << (id % BitsPerWord));
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE bool GetSvcAllowedImpl(u8 *data, u32 id) {
|
||||
constexpr size_t BitsPerWord = BITSIZEOF(*data);
|
||||
MESOSPHERE_ASSERT(id < svc::SvcId_Count);
|
||||
return (data[id / BitsPerWord] & (1ul << (id % BitsPerWord))) != 0;
|
||||
}
|
||||
|
||||
bool SetSvcAllowed(u32 id) {
|
||||
if (id < BITSIZEOF(this->svc_access_flags)) {
|
||||
SetSvcAllowedImpl(this->svc_access_flags, id);
|
||||
@@ -230,10 +236,10 @@ namespace ams::kern {
|
||||
}
|
||||
}
|
||||
|
||||
bool SetInterruptAllowed(u32 id) {
|
||||
bool SetInterruptPermitted(u32 id) {
|
||||
constexpr size_t BitsPerWord = BITSIZEOF(this->irq_access_flags[0]);
|
||||
if (id < BITSIZEOF(this->irq_access_flags)) {
|
||||
this->irq_access_flags[id / BitsPerWord] = (1ul << (id % BitsPerWord));
|
||||
this->irq_access_flags[id / BitsPerWord] |= (1ul << (id % BitsPerWord));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -253,10 +259,12 @@ namespace ams::kern {
|
||||
|
||||
Result SetCapability(const util::BitPack32 cap, u32 &set_flags, u32 &set_svc, KProcessPageTable *page_table);
|
||||
Result SetCapabilities(const u32 *caps, s32 num_caps, KProcessPageTable *page_table);
|
||||
Result SetCapabilities(svc::KUserPointer<const u32 *> user_caps, s32 num_caps, KProcessPageTable *page_table);
|
||||
public:
|
||||
constexpr KCapabilities() = default;
|
||||
|
||||
Result Initialize(const u32 *caps, s32 num_caps, KProcessPageTable *page_table);
|
||||
Result Initialize(svc::KUserPointer<const u32 *> user_caps, s32 num_caps, KProcessPageTable *page_table);
|
||||
|
||||
constexpr u64 GetCoreMask() const { return this->core_mask; }
|
||||
constexpr u64 GetPriorityMask() const { return this->priority_mask; }
|
||||
@@ -264,17 +272,88 @@ namespace ams::kern {
|
||||
|
||||
ALWAYS_INLINE void CopySvcPermissionsTo(KThread::StackParameters &sp) const {
|
||||
static_assert(sizeof(svc_access_flags) == sizeof(sp.svc_permission));
|
||||
/* Copy permissions. */
|
||||
std::memcpy(sp.svc_permission, this->svc_access_flags, sizeof(this->svc_access_flags));
|
||||
|
||||
/* Clear specific SVCs based on our state. */
|
||||
ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException);
|
||||
ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_SynchronizePreemptionState);
|
||||
if (sp.is_preemption_state_pinned) {
|
||||
if (sp.is_pinned) {
|
||||
ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_GetInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: Member functions. */
|
||||
ALWAYS_INLINE void CopyPinnedSvcPermissionsTo(KThread::StackParameters &sp) const {
|
||||
static_assert(sizeof(svc_access_flags) == sizeof(sp.svc_permission));
|
||||
/* Clear all permissions. */
|
||||
std::memset(sp.svc_permission, 0, sizeof(this->svc_access_flags));
|
||||
|
||||
/* Set specific SVCs based on our state. */
|
||||
SetSvcAllowedImpl(sp.svc_permission, svc::SvcId_SynchronizePreemptionState);
|
||||
if (GetSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException)) {
|
||||
SetSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException);
|
||||
SetSvcAllowedImpl(sp.svc_permission, svc::SvcId_GetInfo);
|
||||
}
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void CopyUnpinnedSvcPermissionsTo(KThread::StackParameters &sp) const {
|
||||
static_assert(sizeof(svc_access_flags) == sizeof(sp.svc_permission));
|
||||
/* Get whether we have access to return from exception. */
|
||||
const bool return_from_exception = GetSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException);
|
||||
|
||||
/* Copy permissions. */
|
||||
std::memcpy(sp.svc_permission, this->svc_access_flags, sizeof(this->svc_access_flags));
|
||||
|
||||
/* Clear/Set specific SVCs based on our state. */
|
||||
ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException);
|
||||
ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_SynchronizePreemptionState);
|
||||
if (return_from_exception) {
|
||||
SetSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException);
|
||||
}
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void CopyEnterExceptionSvcPermissionsTo(KThread::StackParameters &sp) {
|
||||
static_assert(sizeof(svc_access_flags) == sizeof(sp.svc_permission));
|
||||
|
||||
/* Set ReturnFromException if allowed. */
|
||||
if (GetSvcAllowedImpl(this->svc_access_flags, svc::SvcId_ReturnFromException)) {
|
||||
SetSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException);
|
||||
}
|
||||
|
||||
/* Set GetInfo if allowed. */
|
||||
if (GetSvcAllowedImpl(this->svc_access_flags, svc::SvcId_GetInfo)) {
|
||||
SetSvcAllowedImpl(sp.svc_permission, svc::SvcId_GetInfo);
|
||||
}
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void CopyLeaveExceptionSvcPermissionsTo(KThread::StackParameters &sp) {
|
||||
static_assert(sizeof(svc_access_flags) == sizeof(sp.svc_permission));
|
||||
|
||||
/* Clear ReturnFromException. */
|
||||
ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException);
|
||||
|
||||
/* If pinned, clear GetInfo. */
|
||||
if (sp.is_pinned) {
|
||||
ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_GetInfo);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool IsPermittedInterrupt(u32 id) const {
|
||||
constexpr size_t BitsPerWord = BITSIZEOF(this->irq_access_flags[0]);
|
||||
if (id < BITSIZEOF(this->irq_access_flags)) {
|
||||
return (this->irq_access_flags[id / BitsPerWord] & (1ul << (id % BitsPerWord))) != 0;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool IsPermittedDebug() const {
|
||||
return this->debug_capabilities.Get<DebugFlags::AllowDebug>();
|
||||
}
|
||||
|
||||
constexpr bool CanForceDebug() const {
|
||||
return this->debug_capabilities.Get<DebugFlags::ForceDebug>();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
namespace ams::kern {
|
||||
|
||||
class KPort;
|
||||
class KSession;
|
||||
class KClientSession;
|
||||
class KLightSession;
|
||||
class KLightClientSession;
|
||||
|
||||
class KClientPort final : public KSynchronizationObject {
|
||||
MESOSPHERE_AUTOOBJECT_TRAITS(KClientPort, KSynchronizationObject);
|
||||
@@ -33,6 +37,8 @@ namespace ams::kern {
|
||||
virtual ~KClientPort() { /* ... */ }
|
||||
|
||||
void Initialize(KPort *parent, s32 max_sessions);
|
||||
void OnSessionFinalized();
|
||||
void OnServerClosed();
|
||||
|
||||
constexpr const KPort *GetParent() const { return this->parent; }
|
||||
|
||||
@@ -42,7 +48,8 @@ namespace ams::kern {
|
||||
virtual void Destroy() override;
|
||||
virtual bool IsSignaled() const override;
|
||||
|
||||
/* TODO: More of KClientPort. */
|
||||
Result CreateSession(KClientSession **out);
|
||||
Result CreateLightSession(KLightClientSession **out);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -34,11 +34,15 @@ namespace ams::kern {
|
||||
this->parent = parent;
|
||||
}
|
||||
|
||||
virtual void Destroy() override;
|
||||
static void PostDestroy(uintptr_t arg) { /* ... */ }
|
||||
|
||||
constexpr const KSession *GetParent() const { return this->parent; }
|
||||
constexpr KSession *GetParent() const { return this->parent; }
|
||||
|
||||
/* TODO: More of KClientSession. */
|
||||
Result SendSyncRequest(uintptr_t address, size_t size);
|
||||
Result SendAsyncRequest(KWritableEvent *event, uintptr_t address, size_t size);
|
||||
|
||||
void OnServerClosed();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -22,8 +22,35 @@ namespace ams::kern {
|
||||
|
||||
class KCodeMemory final : public KAutoObjectWithSlabHeapAndContainer<KCodeMemory, KAutoObjectWithList> {
|
||||
MESOSPHERE_AUTOOBJECT_TRAITS(KCodeMemory, KAutoObject);
|
||||
private:
|
||||
TYPED_STORAGE(KPageGroup) page_group;
|
||||
KProcess *owner;
|
||||
KProcessAddress address;
|
||||
KLightLock lock;
|
||||
bool is_initialized;
|
||||
bool is_owner_mapped;
|
||||
bool is_mapped;
|
||||
public:
|
||||
/* TODO: This is a placeholder definition. */
|
||||
explicit KCodeMemory() : owner(nullptr), address(Null<KProcessAddress>), is_initialized(false), is_owner_mapped(false), is_mapped(false) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
virtual ~KCodeMemory() { /* ... */ }
|
||||
|
||||
Result Initialize(KProcessAddress address, size_t size);
|
||||
virtual void Finalize() override;
|
||||
|
||||
Result Map(KProcessAddress address, size_t size);
|
||||
Result Unmap(KProcessAddress address, size_t size);
|
||||
Result MapToOwner(KProcessAddress address, size_t size, ams::svc::MemoryPermission perm);
|
||||
Result UnmapFromOwner(KProcessAddress address, size_t size);
|
||||
|
||||
virtual bool IsInitialized() const override { return this->is_initialized; }
|
||||
static void PostDestroy(uintptr_t arg) { /* ... */ }
|
||||
|
||||
KProcess *GetOwner() const { return this->owner; }
|
||||
KProcessAddress GetSourceAddress() { return this->address; }
|
||||
size_t GetSize() const { return this->is_initialized ? GetReference(this->page_group).GetNumPages() * PageSize : 0; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -20,26 +20,11 @@
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
struct KConditionVariableComparator {
|
||||
static constexpr ALWAYS_INLINE int Compare(const KThread &lhs, const KThread &rhs) {
|
||||
const uintptr_t l_key = lhs.GetConditionVariableKey();
|
||||
const uintptr_t r_key = rhs.GetConditionVariableKey();
|
||||
|
||||
if (l_key < r_key) {
|
||||
/* Sort first by key */
|
||||
return -1;
|
||||
} else if (l_key == r_key && lhs.GetPriority() < rhs.GetPriority()) {
|
||||
/* And then by priority. */
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
extern KThread g_cv_arbiter_compare_thread;
|
||||
|
||||
class KConditionVariable {
|
||||
public:
|
||||
using ThreadTree = util::IntrusiveRedBlackTreeMemberTraits<&KThread::condvar_arbiter_tree_node>::TreeType<KConditionVariableComparator>;
|
||||
using ThreadTree = typename KThread::ConditionVariableThreadTreeType;
|
||||
private:
|
||||
ThreadTree tree;
|
||||
public:
|
||||
@@ -52,18 +37,20 @@ namespace ams::kern {
|
||||
/* Condition variable. */
|
||||
void Signal(uintptr_t cv_key, s32 count);
|
||||
Result Wait(KProcessAddress addr, uintptr_t key, u32 value, s64 timeout);
|
||||
|
||||
ALWAYS_INLINE void BeforeUpdatePriority(KThread *thread) {
|
||||
MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread());
|
||||
|
||||
this->tree.erase(this->tree.iterator_to(*thread));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void AfterUpdatePriority(KThread *thread) {
|
||||
MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread());
|
||||
|
||||
this->tree.insert(*thread);
|
||||
}
|
||||
private:
|
||||
KThread *SignalImpl(KThread *thread);
|
||||
};
|
||||
|
||||
ALWAYS_INLINE void BeforeUpdatePriority(KConditionVariable::ThreadTree *tree, KThread *thread) {
|
||||
MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread());
|
||||
|
||||
tree->erase(tree->iterator_to(*thread));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void AfterUpdatePriority(KConditionVariable::ThreadTree *tree, KThread *thread) {
|
||||
MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread());
|
||||
|
||||
tree->insert(*thread);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace ams::kern {
|
||||
KInterruptTaskManager *interrupt_task_manager;
|
||||
s32 core_id;
|
||||
void *exception_stack_top;
|
||||
ams::svc::ThreadLocalRegion *tlr;
|
||||
};
|
||||
static_assert(std::is_standard_layout<KCurrentContext>::value && std::is_trivially_destructible<KCurrentContext>::value);
|
||||
static_assert(sizeof(KCurrentContext) <= cpu::DataCacheLineSize);
|
||||
@@ -80,6 +81,10 @@ namespace ams::kern {
|
||||
return impl::GetCurrentContext().core_id;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE ams::svc::ThreadLocalRegion *GetCurrentThreadLocalRegion() {
|
||||
return impl::GetCurrentContext().tlr;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void SetCurrentThread(KThread *new_thread) {
|
||||
impl::GetCurrentContext().current_thread = new_thread;
|
||||
}
|
||||
@@ -88,4 +93,8 @@ namespace ams::kern {
|
||||
impl::GetCurrentContext().current_process = new_process;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void SetCurrentThreadLocalRegion(void *address) {
|
||||
impl::GetCurrentContext().tlr = static_cast<ams::svc::ThreadLocalRegion *>(address);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,12 +16,68 @@
|
||||
#pragma once
|
||||
#include <mesosphere/kern_common.hpp>
|
||||
#include <mesosphere/kern_k_synchronization_object.hpp>
|
||||
#include <mesosphere/kern_k_process.hpp>
|
||||
#include <mesosphere/kern_k_event_info.hpp>
|
||||
#include <mesosphere/kern_k_light_lock.hpp>
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
class KDebugBase : public KSynchronizationObject {
|
||||
protected:
|
||||
using DebugEventList = util::IntrusiveListBaseTraits<KEventInfo>::ListType;
|
||||
private:
|
||||
DebugEventList event_info_list;
|
||||
u32 continue_flags;
|
||||
KProcess *process;
|
||||
KLightLock lock;
|
||||
KProcess::State old_process_state;
|
||||
public:
|
||||
/* TODO: This is a placeholder definition. */
|
||||
explicit KDebugBase() { /* ... */ }
|
||||
virtual ~KDebugBase() { /* ... */ }
|
||||
protected:
|
||||
bool Is64Bit() const;
|
||||
public:
|
||||
void Initialize();
|
||||
|
||||
Result Attach(KProcess *process);
|
||||
Result BreakProcess();
|
||||
Result TerminateProcess();
|
||||
|
||||
Result ContinueDebug(const u32 flags, const u64 *thread_ids, size_t num_thread_ids);
|
||||
|
||||
Result QueryMemoryInfo(ams::svc::MemoryInfo *out_memory_info, ams::svc::PageInfo *out_page_info, KProcessAddress address);
|
||||
Result ReadMemory(KProcessAddress buffer, KProcessAddress address, size_t size);
|
||||
Result WriteMemory(KProcessAddress buffer, KProcessAddress address, size_t size);
|
||||
|
||||
Result GetThreadContext(ams::svc::ThreadContext *out, u64 thread_id, u32 context_flags);
|
||||
Result SetThreadContext(const ams::svc::ThreadContext &ctx, u64 thread_id, u32 context_flags);
|
||||
|
||||
virtual Result GetThreadContextImpl(ams::svc::ThreadContext *out, KThread *thread, u32 context_flags) = 0;
|
||||
virtual Result SetThreadContextImpl(const ams::svc::ThreadContext &ctx, KThread *thread, u32 context_flags) = 0;
|
||||
|
||||
Result GetRunningThreadInfo(ams::svc::LastThreadContext *out_context, u64 *out_thread_id);
|
||||
|
||||
Result GetDebugEventInfo(ams::svc::lp64::DebugEventInfo *out);
|
||||
Result GetDebugEventInfo(ams::svc::ilp32::DebugEventInfo *out);
|
||||
|
||||
KScopedAutoObject<KProcess> GetProcess();
|
||||
private:
|
||||
void PushDebugEvent(ams::svc::DebugEvent event, uintptr_t param0 = 0, uintptr_t param1 = 0, uintptr_t param2 = 0, uintptr_t param3 = 0, uintptr_t param4 = 0);
|
||||
void EnqueueDebugEventInfo(KEventInfo *info);
|
||||
|
||||
template<typename T> requires (std::same_as<T, ams::svc::lp64::DebugEventInfo> || std::same_as<T, ams::svc::ilp32::DebugEventInfo>)
|
||||
Result GetDebugEventInfoImpl(T *out);
|
||||
public:
|
||||
virtual void OnFinalizeSynchronizationObject() override;
|
||||
virtual bool IsSignaled() const override;
|
||||
private:
|
||||
static Result ProcessDebugEvent(ams::svc::DebugEvent event, uintptr_t param0, uintptr_t param1, uintptr_t param2, uintptr_t param3, uintptr_t param4);
|
||||
public:
|
||||
static Result OnDebugEvent(ams::svc::DebugEvent event, uintptr_t param0 = 0, uintptr_t param1 = 0, uintptr_t param2 = 0, uintptr_t param3 = 0, uintptr_t param4 = 0);
|
||||
static Result OnExitProcess(KProcess *process);
|
||||
static Result OnTerminateProcess(KProcess *process);
|
||||
static Result OnExitThread(KThread *thread);
|
||||
static KEventInfo *CreateDebugEvent(ams::svc::DebugEvent event, uintptr_t param0, uintptr_t param1, uintptr_t param2, uintptr_t param3, uintptr_t param4, u64 thread_id);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -23,10 +23,39 @@ namespace ams::kern {
|
||||
|
||||
class KDeviceAddressSpace final : public KAutoObjectWithSlabHeapAndContainer<KDeviceAddressSpace, KAutoObjectWithList> {
|
||||
MESOSPHERE_AUTOOBJECT_TRAITS(KDeviceAddressSpace, KAutoObject);
|
||||
private:
|
||||
KLightLock lock;
|
||||
KDevicePageTable table;
|
||||
u64 space_address;
|
||||
u64 space_size;
|
||||
bool is_initialized;
|
||||
public:
|
||||
constexpr KDeviceAddressSpace() : lock(), table(), space_address(), space_size(), is_initialized() { /* ... */ }
|
||||
virtual ~KDeviceAddressSpace() { /* ... */ }
|
||||
|
||||
Result Initialize(u64 address, u64 size);
|
||||
virtual void Finalize() override;
|
||||
|
||||
virtual bool IsInitialized() const override { return this->is_initialized; }
|
||||
static void PostDestroy(uintptr_t arg) { /* ... */ }
|
||||
|
||||
Result Attach(ams::svc::DeviceName device_name);
|
||||
Result Detach(ams::svc::DeviceName device_name);
|
||||
|
||||
Result Map(size_t *out_mapped_size, KProcessPageTable *page_table, KProcessAddress process_address, size_t size, u64 device_address, ams::svc::MemoryPermission device_perm, bool refresh_mappings) {
|
||||
return this->Map(out_mapped_size, page_table, process_address, size, device_address, device_perm, false, refresh_mappings);
|
||||
}
|
||||
|
||||
Result MapAligned(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, u64 device_address, ams::svc::MemoryPermission device_perm) {
|
||||
size_t dummy;
|
||||
return this->Map(std::addressof(dummy), page_table, process_address, size, device_address, device_perm, true, false);
|
||||
}
|
||||
|
||||
Result Unmap(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, u64 device_address);
|
||||
private:
|
||||
Result Map(size_t *out_mapped_size, KProcessPageTable *page_table, KProcessAddress process_address, size_t size, u64 device_address, ams::svc::MemoryPermission device_perm, bool is_aligned, bool refresh_mappings);
|
||||
public:
|
||||
static void Initialize();
|
||||
|
||||
/* TODO: This is a placeholder definition. */
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -18,13 +18,38 @@
|
||||
#include <mesosphere/kern_k_auto_object.hpp>
|
||||
#include <mesosphere/kern_slab_helpers.hpp>
|
||||
#include <mesosphere/kern_k_readable_event.hpp>
|
||||
#include <mesosphere/kern_k_writable_event.hpp>
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
class KEvent final : public KAutoObjectWithSlabHeapAndContainer<KEvent, KAutoObjectWithList> {
|
||||
MESOSPHERE_AUTOOBJECT_TRAITS(KEvent, KAutoObject);
|
||||
private:
|
||||
KReadableEvent readable_event;
|
||||
KWritableEvent writable_event;
|
||||
KProcess *owner;
|
||||
bool initialized;
|
||||
public:
|
||||
/* TODO: This is a placeholder definition. */
|
||||
constexpr KEvent()
|
||||
: readable_event(), writable_event(), owner(), initialized()
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
virtual ~KEvent() { /* ... */ }
|
||||
|
||||
void Initialize();
|
||||
virtual void Finalize() override;
|
||||
|
||||
virtual bool IsInitialized() const override { return this->initialized; }
|
||||
virtual uintptr_t GetPostDestroyArgument() const override { return reinterpret_cast<uintptr_t>(this->owner); }
|
||||
|
||||
static void PostDestroy(uintptr_t arg);
|
||||
|
||||
virtual KProcess *GetOwner() const override { return this->owner; }
|
||||
|
||||
KReadableEvent &GetReadableEvent() { return this->readable_event; }
|
||||
KWritableEvent &GetWritableEvent() { return this->writable_event; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -21,7 +21,50 @@ namespace ams::kern {
|
||||
|
||||
class KEventInfo : public KSlabAllocated<KEventInfo>, public util::IntrusiveListBaseNode<KEventInfo> {
|
||||
public:
|
||||
/* TODO: This is a placeholder definition. */
|
||||
struct InfoCreateThread {
|
||||
u32 thread_id;
|
||||
uintptr_t tls_address;
|
||||
uintptr_t entrypoint;
|
||||
};
|
||||
|
||||
struct InfoExitProcess {
|
||||
ams::svc::ProcessExitReason reason;
|
||||
};
|
||||
|
||||
struct InfoExitThread {
|
||||
ams::svc::ThreadExitReason reason;
|
||||
};
|
||||
|
||||
struct InfoException {
|
||||
ams::svc::DebugException exception_type;
|
||||
s32 exception_data_count;
|
||||
uintptr_t exception_address;
|
||||
uintptr_t exception_data[4];
|
||||
};
|
||||
|
||||
struct InfoSystemCall {
|
||||
s64 tick;
|
||||
s32 id;
|
||||
};
|
||||
public:
|
||||
ams::svc::DebugEvent event;
|
||||
u32 thread_id;
|
||||
u32 flags;
|
||||
bool is_attached;
|
||||
bool continue_flag;
|
||||
bool ignore_continue;
|
||||
bool close_once;
|
||||
union {
|
||||
InfoCreateThread create_thread;
|
||||
InfoExitProcess exit_process;
|
||||
InfoExitThread exit_thread;
|
||||
InfoException exception;
|
||||
InfoSystemCall system_call;
|
||||
} info;
|
||||
KThread *debug_thread;
|
||||
public:
|
||||
explicit KEventInfo() : is_attached(), continue_flag(), ignore_continue() { /* ... */ }
|
||||
~KEventInfo() { /* ... */ }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -126,20 +126,7 @@ namespace ams::kern {
|
||||
NOINLINE bool Remove(ams::svc::Handle handle);
|
||||
|
||||
template<typename T = KAutoObject>
|
||||
ALWAYS_INLINE KScopedAutoObject<T> GetObject(ams::svc::Handle handle) const {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
|
||||
/* Handle pseudo-handles. */
|
||||
if constexpr (std::is_base_of<T, KProcess>::value) {
|
||||
if (handle == ams::svc::PseudoHandle::CurrentProcess) {
|
||||
return GetCurrentProcessPointer();
|
||||
}
|
||||
} else if constexpr (std::is_base_of<T, KThread>::value) {
|
||||
if (handle == ams::svc::PseudoHandle::CurrentThread) {
|
||||
return GetCurrentThreadPointer();
|
||||
}
|
||||
}
|
||||
|
||||
ALWAYS_INLINE KScopedAutoObject<T> GetObjectWithoutPseudoHandle(ams::svc::Handle handle) const {
|
||||
/* Lock and look up in table. */
|
||||
KScopedDisableDispatch dd;
|
||||
KScopedSpinLock lk(this->lock);
|
||||
@@ -147,25 +134,33 @@ namespace ams::kern {
|
||||
if constexpr (std::is_same<T, KAutoObject>::value) {
|
||||
return this->GetObjectImpl(handle);
|
||||
} else {
|
||||
return this->GetObjectImpl(handle)->DynamicCast<T*>();
|
||||
if (auto *obj = this->GetObjectImpl(handle); obj != nullptr) {
|
||||
return obj->DynamicCast<T*>();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T = KAutoObject>
|
||||
ALWAYS_INLINE KScopedAutoObject<T> GetObjectForIpc(ams::svc::Handle handle) const {
|
||||
static_assert(!std::is_base_of<KInterruptEvent, T>::value);
|
||||
ALWAYS_INLINE KScopedAutoObject<T> GetObject(ams::svc::Handle handle) const {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
|
||||
/* Handle pseudo-handles. */
|
||||
if constexpr (std::is_base_of<T, KProcess>::value) {
|
||||
if constexpr (std::derived_from<KProcess, T>) {
|
||||
if (handle == ams::svc::PseudoHandle::CurrentProcess) {
|
||||
return GetCurrentProcessPointer();
|
||||
}
|
||||
} else if constexpr (std::is_base_of<T, KThread>::value) {
|
||||
} else if constexpr (std::derived_from<KThread, T>) {
|
||||
if (handle == ams::svc::PseudoHandle::CurrentThread) {
|
||||
return GetCurrentThreadPointer();
|
||||
}
|
||||
}
|
||||
|
||||
return this->template GetObjectWithoutPseudoHandle<T>(handle);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE KScopedAutoObject<KAutoObject> GetObjectForIpcWithoutPseudoHandle(ams::svc::Handle handle) const {
|
||||
/* Lock and look up in table. */
|
||||
KScopedDisableDispatch dd;
|
||||
KScopedSpinLock lk(this->lock);
|
||||
@@ -174,11 +169,20 @@ namespace ams::kern {
|
||||
if (obj->DynamicCast<KInterruptEvent *>() != nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
if constexpr (std::is_same<T, KAutoObject>::value) {
|
||||
return obj;
|
||||
} else {
|
||||
return obj->DynamicCast<T*>();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE KScopedAutoObject<KAutoObject> GetObjectForIpc(ams::svc::Handle handle, KThread *cur_thread) const {
|
||||
/* Handle pseudo-handles. */
|
||||
if (handle == ams::svc::PseudoHandle::CurrentProcess) {
|
||||
return static_cast<KAutoObject *>(static_cast<void *>(cur_thread->GetOwnerProcess()));
|
||||
}
|
||||
if (handle == ams::svc::PseudoHandle::CurrentThread) {
|
||||
return static_cast<KAutoObject *>(cur_thread);
|
||||
}
|
||||
|
||||
return GetObjectForIpcWithoutPseudoHandle(handle);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE KScopedAutoObject<KAutoObject> GetObjectByIndex(ams::svc::Handle *out_handle, size_t index) const {
|
||||
@@ -203,6 +207,49 @@ namespace ams::kern {
|
||||
static_assert(std::is_base_of<KAutoObject, T>::value);
|
||||
return this->Register(handle, obj, obj->GetTypeObj().GetClassToken());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ALWAYS_INLINE bool GetMultipleObjects(T **out, const ams::svc::Handle *handles, size_t num_handles) const {
|
||||
/* Try to convert and open all the handles. */
|
||||
size_t num_opened;
|
||||
{
|
||||
/* Lock the table. */
|
||||
KScopedDisableDispatch dd;
|
||||
KScopedSpinLock lk(this->lock);
|
||||
for (num_opened = 0; num_opened < num_handles; num_opened++) {
|
||||
/* Get the current handle. */
|
||||
const auto cur_handle = handles[num_opened];
|
||||
|
||||
/* Get the object for the current handle. */
|
||||
KAutoObject *cur_object = this->GetObjectImpl(cur_handle);
|
||||
if (AMS_UNLIKELY(cur_object == nullptr)) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Cast the current object to the desired type. */
|
||||
T *cur_t = cur_object->DynamicCast<T*>();
|
||||
if (AMS_UNLIKELY(cur_t == nullptr)) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Open a reference to the current object. */
|
||||
cur_t->Open();
|
||||
out[num_opened] = cur_t;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we converted every object, succeed. */
|
||||
if (AMS_LIKELY(num_opened == num_handles)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If we didn't convert entry object, close the ones we opened. */
|
||||
for (size_t i = 0; i < num_opened; i++) {
|
||||
out[i]->Close();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
private:
|
||||
NOINLINE Result Add(ams::svc::Handle *out_handle, KAutoObject *obj, u16 type);
|
||||
NOINLINE void Register(ams::svc::Handle handle, KAutoObject *obj, u16 type);
|
||||
@@ -310,49 +357,6 @@ namespace ams::kern {
|
||||
*out_handle = EncodeHandle(index, entry->GetLinearId());
|
||||
return entry->GetObject();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ALWAYS_INLINE bool GetMultipleObjects(T **out, const ams::svc::Handle *handles, size_t num_handles) const {
|
||||
/* Try to convert and open all the handles. */
|
||||
size_t num_opened;
|
||||
{
|
||||
/* Lock the table. */
|
||||
KScopedDisableDispatch dd;
|
||||
KScopedSpinLock lk(this->lock);
|
||||
for (num_opened = 0; num_opened < num_handles; num_opened++) {
|
||||
/* Get the current handle. */
|
||||
const auto cur_handle = handles[num_opened];
|
||||
|
||||
/* Get the object for the current handle. */
|
||||
KAutoObject *cur_object = this->GetObjectImpl(cur_handle);
|
||||
if (AMS_UNLIKELY(cur_object == nullptr)) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Cast the current object to the desired type. */
|
||||
T *cur_t = cur_object->DynamicCast<T*>();
|
||||
if (AMS_UNLIKELY(cur_t == nullptr)) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Open a reference to the current object. */
|
||||
cur_t->Open();
|
||||
out[num_opened] = cur_t;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we converted every object, succeed. */
|
||||
if (AMS_LIKELY(num_opened == num_handles)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If we didn't convert entry object, close the ones we opened. */
|
||||
for (size_t i = 0; i < num_opened; i++) {
|
||||
out[i]->Close();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -26,13 +26,40 @@ namespace ams::kern {
|
||||
|
||||
class KInterruptEvent final : public KAutoObjectWithSlabHeapAndContainer<KInterruptEvent, KReadableEvent> {
|
||||
MESOSPHERE_AUTOOBJECT_TRAITS(KInterruptEvent, KReadableEvent);
|
||||
private:
|
||||
KInterruptEventTask *task;
|
||||
s32 interrupt_id;
|
||||
bool is_initialized;
|
||||
public:
|
||||
/* TODO: This is a placeholder definition. */
|
||||
constexpr KInterruptEvent() : task(nullptr), interrupt_id(-1), is_initialized(false) { /* ... */ }
|
||||
virtual ~KInterruptEvent() { /* ... */ }
|
||||
|
||||
Result Initialize(int32_t interrupt_name, ams::svc::InterruptType type);
|
||||
virtual void Finalize() override;
|
||||
|
||||
virtual Result Reset() override;
|
||||
|
||||
virtual bool IsInitialized() const override { return this->is_initialized; }
|
||||
|
||||
static void PostDestroy(uintptr_t arg) { /* ... */ }
|
||||
|
||||
constexpr s32 GetInterruptId() const { return this->interrupt_id; }
|
||||
};
|
||||
|
||||
class KInterruptEventTask : public KSlabAllocated<KInterruptEventTask>, public KInterruptTask {
|
||||
private:
|
||||
KInterruptEvent *event;
|
||||
s32 interrupt_id;
|
||||
public:
|
||||
/* TODO: This is a placeholder definition. */
|
||||
constexpr KInterruptEventTask() : event(nullptr), interrupt_id(-1) { /* ... */ }
|
||||
~KInterruptEventTask() { /* ... */ }
|
||||
|
||||
virtual KInterruptTask *OnInterrupt(s32 interrupt_id) override;
|
||||
virtual void DoTask() override;
|
||||
|
||||
void Unregister();
|
||||
public:
|
||||
static Result Register(KInterruptEventTask **out, s32 interrupt_id, bool level, KInterruptEvent *event);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -49,8 +49,6 @@ namespace ams::kern {
|
||||
|
||||
NOINLINE void Initialize();
|
||||
void EnqueueTask(KInterruptTask *task);
|
||||
|
||||
/* TODO: Actually implement KInterruptTaskManager. This is a placeholder. */
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -34,11 +34,14 @@ namespace ams::kern {
|
||||
this->parent = parent;
|
||||
}
|
||||
|
||||
virtual void Destroy() override;
|
||||
static void PostDestroy(uintptr_t arg) { /* ... */ }
|
||||
|
||||
constexpr const KLightSession *GetParent() const { return this->parent; }
|
||||
|
||||
/* TODO: More of KLightClientSession. */
|
||||
Result SendSyncRequest(u32 *data);
|
||||
|
||||
void OnServerClosed();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -31,15 +31,18 @@ namespace ams::kern {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
|
||||
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer());
|
||||
const uintptr_t cur_thread_tag = (cur_thread | 1);
|
||||
|
||||
while (true) {
|
||||
uintptr_t old_tag = this->tag.load(std::memory_order_relaxed);
|
||||
|
||||
while (!this->tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : old_tag | 1, std::memory_order_acquire)) {
|
||||
/* ... */
|
||||
if ((old_tag | 1) == cur_thread_tag) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ((old_tag == 0) || ((old_tag | 1) == (cur_thread | 1))) {
|
||||
if ((old_tag == 0) || ((old_tag | 1) == cur_thread_tag)) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -52,9 +55,11 @@ namespace ams::kern {
|
||||
|
||||
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer());
|
||||
uintptr_t expected = cur_thread;
|
||||
if (!this->tag.compare_exchange_weak(expected, 0, std::memory_order_release)) {
|
||||
this->UnlockSlowPath(cur_thread);
|
||||
}
|
||||
do {
|
||||
if (expected != cur_thread) {
|
||||
return this->UnlockSlowPath(cur_thread);
|
||||
}
|
||||
} while (!this->tag.compare_exchange_weak(expected, 0, std::memory_order_release));
|
||||
}
|
||||
|
||||
void LockSlowPath(uintptr_t owner, uintptr_t cur_thread);
|
||||
|
||||
@@ -35,13 +35,22 @@ namespace ams::kern {
|
||||
constexpr KLightServerSession() : parent(), request_queue(), server_queue(), current_request(), server_thread() { /* ... */ }
|
||||
virtual ~KLightServerSession() { /* ... */ }
|
||||
|
||||
void Initialize(KLightSession *parent);
|
||||
void Initialize(KLightSession *parent) {
|
||||
/* Set member variables. */
|
||||
this->parent = parent;
|
||||
}
|
||||
|
||||
virtual void Destroy() override;
|
||||
static void PostDestroy(uintptr_t arg) { /* ... */ }
|
||||
|
||||
constexpr const KLightSession *GetParent() const { return this->parent; }
|
||||
|
||||
/* TODO: More of KLightServerSession. */
|
||||
Result OnRequest(KThread *request_thread);
|
||||
Result ReplyAndReceive(u32 *data);
|
||||
|
||||
void OnClientClosed();
|
||||
private:
|
||||
void CleanupRequests();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -34,6 +34,9 @@ namespace ams::kern {
|
||||
ClientClosed = 2,
|
||||
ServerClosed = 3,
|
||||
};
|
||||
public:
|
||||
static constexpr size_t DataSize = sizeof(u32) * 7;
|
||||
static constexpr u32 ReplyFlag = (1u << (BITSIZEOF(u32) - 1));
|
||||
private:
|
||||
KLightServerSession server;
|
||||
KLightClientSession client;
|
||||
@@ -51,12 +54,21 @@ namespace ams::kern {
|
||||
|
||||
virtual ~KLightSession() { /* ... */ }
|
||||
|
||||
void Initialize(KClientPort *client_port, uintptr_t name);
|
||||
virtual void Finalize() override;
|
||||
|
||||
virtual bool IsInitialized() const override { return this->initialized; }
|
||||
virtual uintptr_t GetPostDestroyArgument() const override { return reinterpret_cast<uintptr_t>(this->process); }
|
||||
|
||||
static void PostDestroy(uintptr_t arg);
|
||||
|
||||
/* TODO: This is a placeholder definition. */
|
||||
void OnServerClosed();
|
||||
void OnClientClosed();
|
||||
|
||||
bool IsServerClosed() const { return this->state != State::Normal; }
|
||||
bool IsClientClosed() const { return this->state != State::Normal; }
|
||||
|
||||
Result OnRequest(KThread *request_thread) { return this->server.OnRequest(request_thread); }
|
||||
|
||||
KLightClientSession &GetClientSession() { return this->client; }
|
||||
KLightServerSession &GetServerSession() { return this->server; }
|
||||
|
||||
@@ -155,6 +155,8 @@ namespace ams::kern {
|
||||
KMemoryPermission_UserReadExecute = KMemoryPermission_UserRead | KMemoryPermission_UserExecute,
|
||||
|
||||
KMemoryPermission_UserMask = ams::svc::MemoryPermission_Read | ams::svc::MemoryPermission_Write | ams::svc::MemoryPermission_Execute,
|
||||
|
||||
KMemoryPermission_IpcLockChangeMask = KMemoryPermission_NotMapped | KMemoryPermission_UserReadWrite,
|
||||
};
|
||||
|
||||
constexpr KMemoryPermission ConvertToKMemoryPermission(ams::svc::MemoryPermission perm) {
|
||||
@@ -170,6 +172,10 @@ namespace ams::kern {
|
||||
KMemoryAttribute_IpcLocked = ams::svc::MemoryAttribute_IpcLocked,
|
||||
KMemoryAttribute_DeviceShared = ams::svc::MemoryAttribute_DeviceShared,
|
||||
KMemoryAttribute_Uncached = ams::svc::MemoryAttribute_Uncached,
|
||||
|
||||
KMemoryAttribute_AnyLocked = 0x80,
|
||||
|
||||
KMemoryAttribute_SetMask = KMemoryAttribute_Uncached,
|
||||
};
|
||||
|
||||
struct KMemoryInfo {
|
||||
@@ -213,6 +219,26 @@ namespace ams::kern {
|
||||
constexpr uintptr_t GetLastAddress() const {
|
||||
return this->GetEndAddress() - 1;
|
||||
}
|
||||
|
||||
constexpr u16 GetIpcLockCount() const {
|
||||
return this->ipc_lock_count;
|
||||
}
|
||||
|
||||
constexpr KMemoryState GetState() const {
|
||||
return this->state;
|
||||
}
|
||||
|
||||
constexpr KMemoryPermission GetPermission() const {
|
||||
return this->perm;
|
||||
}
|
||||
|
||||
constexpr KMemoryPermission GetOriginalPermission() const {
|
||||
return this->original_perm;
|
||||
}
|
||||
|
||||
constexpr KMemoryAttribute GetAttribute() const {
|
||||
return this->attribute;
|
||||
}
|
||||
};
|
||||
|
||||
class KMemoryBlock : public util::IntrusiveRedBlackTreeBaseNode<KMemoryBlock> {
|
||||
@@ -256,6 +282,22 @@ namespace ams::kern {
|
||||
return this->GetEndAddress() - 1;
|
||||
}
|
||||
|
||||
constexpr u16 GetIpcLockCount() const {
|
||||
return this->ipc_lock_count;
|
||||
}
|
||||
|
||||
constexpr KMemoryPermission GetPermission() const {
|
||||
return this->perm;
|
||||
}
|
||||
|
||||
constexpr KMemoryPermission GetOriginalPermission() const {
|
||||
return this->original_perm;
|
||||
}
|
||||
|
||||
constexpr KMemoryAttribute GetAttribute() const {
|
||||
return this->attribute;
|
||||
}
|
||||
|
||||
constexpr KMemoryInfo GetMemoryInfo() const {
|
||||
return {
|
||||
.address = GetInteger(this->GetAddress()),
|
||||
@@ -352,6 +394,66 @@ namespace ams::kern {
|
||||
this->address = addr;
|
||||
this->num_pages -= block->num_pages;
|
||||
}
|
||||
|
||||
constexpr void ShareToDevice(KMemoryPermission new_perm) {
|
||||
/* We must either be shared or have a zero lock count. */
|
||||
MESOSPHERE_ASSERT((this->attribute & KMemoryAttribute_DeviceShared) == KMemoryAttribute_DeviceShared || this->device_use_count == 0);
|
||||
|
||||
/* Share. */
|
||||
const u16 new_count = ++this->device_use_count;
|
||||
MESOSPHERE_ABORT_UNLESS(new_count > 0);
|
||||
|
||||
this->attribute = static_cast<KMemoryAttribute>(this->attribute | KMemoryAttribute_DeviceShared);
|
||||
}
|
||||
|
||||
constexpr void UnshareToDevice(KMemoryPermission new_perm) {
|
||||
/* We must be shared. */
|
||||
MESOSPHERE_ASSERT((this->attribute & KMemoryAttribute_DeviceShared) == KMemoryAttribute_DeviceShared);
|
||||
|
||||
/* Unhare. */
|
||||
const u16 old_count = this->device_use_count--;
|
||||
MESOSPHERE_ABORT_UNLESS(old_count > 0);
|
||||
|
||||
if (old_count == 1) {
|
||||
this->attribute = static_cast<KMemoryAttribute>(this->attribute & ~KMemoryAttribute_DeviceShared);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void LockForIpc(KMemoryPermission new_perm) {
|
||||
/* We must either be locked or have a zero lock count. */
|
||||
MESOSPHERE_ASSERT((this->attribute & KMemoryAttribute_IpcLocked) == KMemoryAttribute_IpcLocked || this->ipc_lock_count == 0);
|
||||
|
||||
/* Lock. */
|
||||
const u16 new_lock_count = ++this->ipc_lock_count;
|
||||
MESOSPHERE_ABORT_UNLESS(new_lock_count > 0);
|
||||
|
||||
/* If this is our first lock, update our permissions. */
|
||||
if (new_lock_count == 1) {
|
||||
MESOSPHERE_ASSERT(this->original_perm == KMemoryPermission_None);
|
||||
MESOSPHERE_ASSERT((this->perm | new_perm | KMemoryPermission_NotMapped) == (this->perm | KMemoryPermission_NotMapped));
|
||||
MESOSPHERE_ASSERT((this->perm & KMemoryPermission_UserExecute) != KMemoryPermission_UserExecute || (new_perm == KMemoryPermission_UserRead));
|
||||
this->original_perm = this->perm;
|
||||
this->perm = static_cast<KMemoryPermission>((new_perm & KMemoryPermission_IpcLockChangeMask) | (this->original_perm & ~KMemoryPermission_IpcLockChangeMask));
|
||||
}
|
||||
this->attribute = static_cast<KMemoryAttribute>(this->attribute | KMemoryAttribute_IpcLocked);
|
||||
}
|
||||
|
||||
constexpr void UnlockForIpc(KMemoryPermission new_perm) {
|
||||
/* We must be locked. */
|
||||
MESOSPHERE_ASSERT((this->attribute & KMemoryAttribute_IpcLocked) == KMemoryAttribute_IpcLocked);
|
||||
|
||||
/* Unlock. */
|
||||
const u16 old_lock_count = this->ipc_lock_count--;
|
||||
MESOSPHERE_ABORT_UNLESS(old_lock_count > 0);
|
||||
|
||||
/* If this is our last unlock, update our permissions. */
|
||||
if (old_lock_count == 1) {
|
||||
MESOSPHERE_ASSERT(this->original_perm != KMemoryPermission_None);
|
||||
this->perm = this->original_perm;
|
||||
this->original_perm = KMemoryPermission_None;
|
||||
this->attribute = static_cast<KMemoryAttribute>(this->attribute & ~KMemoryAttribute_IpcLocked);
|
||||
}
|
||||
}
|
||||
};
|
||||
static_assert(std::is_trivially_destructible<KMemoryBlock>::value);
|
||||
|
||||
|
||||
@@ -96,6 +96,8 @@ namespace ams::kern {
|
||||
void Update(KMemoryBlockManagerUpdateAllocator *allocator, KProcessAddress address, size_t num_pages, KMemoryState state, KMemoryPermission perm, KMemoryAttribute attr);
|
||||
void UpdateLock(KMemoryBlockManagerUpdateAllocator *allocator, KProcessAddress address, size_t num_pages, void (KMemoryBlock::*lock_func)(KMemoryPermission new_perm), KMemoryPermission perm);
|
||||
|
||||
void UpdateIfMatch(KMemoryBlockManagerUpdateAllocator *allocator, KProcessAddress address, size_t num_pages, KMemoryState test_state, KMemoryPermission test_perm, KMemoryAttribute test_attr, KMemoryState state, KMemoryPermission perm, KMemoryAttribute attr);
|
||||
|
||||
iterator FindIterator(KProcessAddress address) const {
|
||||
return this->memory_block_tree.find(KMemoryBlock(address, 1, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None));
|
||||
}
|
||||
|
||||
@@ -17,6 +17,12 @@
|
||||
#include <mesosphere/kern_common.hpp>
|
||||
#include <mesosphere/init/kern_init_page_table_select.hpp>
|
||||
|
||||
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
||||
#include <mesosphere/board/nintendo/nx/kern_k_memory_layout.board.nintendo_nx.hpp>
|
||||
#else
|
||||
#error "Unknown board for KMemoryLayout"
|
||||
#endif
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
constexpr size_t KernelAslrAlignment = 2_MB;
|
||||
@@ -268,6 +274,16 @@ namespace ams::kern {
|
||||
MESOSPHERE_INIT_ABORT();
|
||||
}
|
||||
|
||||
iterator TryFindFirstRegionByType(u32 type_id) {
|
||||
for (auto it = this->begin(); it != this->end(); it++) {
|
||||
if (it->GetType() == type_id) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
|
||||
return this->end();
|
||||
}
|
||||
|
||||
iterator FindFirstDerivedRegion(u32 type_id) {
|
||||
for (auto it = this->begin(); it != this->end(); it++) {
|
||||
if (it->IsDerivedFrom(type_id)) {
|
||||
@@ -277,6 +293,16 @@ namespace ams::kern {
|
||||
MESOSPHERE_INIT_ABORT();
|
||||
}
|
||||
|
||||
iterator TryFindFirstDerivedRegion(u32 type_id) {
|
||||
for (auto it = this->begin(); it != this->end(); it++) {
|
||||
if (it->IsDerivedFrom(type_id)) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
|
||||
return this->end();
|
||||
}
|
||||
|
||||
DerivedRegionExtents GetDerivedRegionExtents(u32 type_id) const {
|
||||
DerivedRegionExtents extents;
|
||||
|
||||
@@ -504,6 +530,33 @@ namespace ams::kern {
|
||||
return *GetVirtualLinearMemoryRegionTree().FindContainingRegion(GetInteger(address));
|
||||
}
|
||||
|
||||
static NOINLINE const KMemoryRegion *TryGetKernelTraceBufferRegion() {
|
||||
auto &tree = GetPhysicalMemoryRegionTree();
|
||||
if (KMemoryRegionTree::const_iterator it = tree.TryFindFirstDerivedRegion(KMemoryRegionType_KernelTraceBuffer); it != tree.end()) {
|
||||
return std::addressof(*it);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static NOINLINE const KMemoryRegion *TryGetOnMemoryBootImageRegion() {
|
||||
auto &tree = GetPhysicalMemoryRegionTree();
|
||||
if (KMemoryRegionTree::const_iterator it = tree.TryFindFirstDerivedRegion(KMemoryRegionType_OnMemoryBootImage); it != tree.end()) {
|
||||
return std::addressof(*it);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static NOINLINE const KMemoryRegion *TryGetDTBRegion() {
|
||||
auto &tree = GetPhysicalMemoryRegionTree();
|
||||
if (KMemoryRegionTree::const_iterator it = tree.TryFindFirstDerivedRegion(KMemoryRegionType_DTB); it != tree.end()) {
|
||||
return std::addressof(*it);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static NOINLINE bool IsHeapPhysicalAddress(const KMemoryRegion **out, KPhysicalAddress address, const KMemoryRegion *hint = nullptr) {
|
||||
auto &tree = GetPhysicalLinearMemoryRegionTree();
|
||||
KMemoryRegionTree::const_iterator it = tree.end();
|
||||
@@ -546,6 +599,48 @@ namespace ams::kern {
|
||||
return false;
|
||||
}
|
||||
|
||||
static NOINLINE bool IsLinearMappedPhysicalAddress(const KMemoryRegion **out, KPhysicalAddress address, const KMemoryRegion *hint = nullptr) {
|
||||
auto &tree = GetPhysicalLinearMemoryRegionTree();
|
||||
KMemoryRegionTree::const_iterator it = tree.end();
|
||||
if (hint != nullptr) {
|
||||
it = tree.iterator_to(*hint);
|
||||
}
|
||||
if (it == tree.end() || !it->Contains(GetInteger(address))) {
|
||||
it = tree.FindContainingRegion(GetInteger(address));
|
||||
}
|
||||
if (it != tree.end() && it->IsDerivedFrom(KMemoryRegionAttr_LinearMapped)) {
|
||||
if (out) {
|
||||
*out = std::addressof(*it);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static NOINLINE bool IsLinearMappedPhysicalAddress(const KMemoryRegion **out, KPhysicalAddress address, size_t size, const KMemoryRegion *hint = nullptr) {
|
||||
auto &tree = GetPhysicalLinearMemoryRegionTree();
|
||||
KMemoryRegionTree::const_iterator it = tree.end();
|
||||
if (hint != nullptr) {
|
||||
it = tree.iterator_to(*hint);
|
||||
}
|
||||
if (it == tree.end() || !it->Contains(GetInteger(address))) {
|
||||
it = tree.FindContainingRegion(GetInteger(address));
|
||||
}
|
||||
if (it != tree.end() && it->IsDerivedFrom(KMemoryRegionAttr_LinearMapped)) {
|
||||
const uintptr_t last_address = GetInteger(address) + size - 1;
|
||||
do {
|
||||
if (last_address <= it->GetLastAddress()) {
|
||||
if (out) {
|
||||
*out = std::addressof(*it);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
it++;
|
||||
} while (it != tree.end() && it->IsDerivedFrom(KMemoryRegionAttr_LinearMapped));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static NOINLINE bool IsHeapVirtualAddress(const KMemoryRegion **out, KVirtualAddress address, const KMemoryRegion *hint = nullptr) {
|
||||
auto &tree = GetVirtualLinearMemoryRegionTree();
|
||||
KMemoryRegionTree::const_iterator it = tree.end();
|
||||
|
||||
@@ -53,6 +53,12 @@ namespace ams::kern {
|
||||
class Impl {
|
||||
private:
|
||||
using RefCount = u16;
|
||||
public:
|
||||
static size_t CalculateMetadataOverheadSize(size_t region_size);
|
||||
|
||||
static constexpr size_t CalculateOptimizedProcessOverheadSize(size_t region_size) {
|
||||
return (util::AlignUp((region_size / PageSize), BITSIZEOF(u64)) / BITSIZEOF(u64)) * sizeof(u64);
|
||||
}
|
||||
private:
|
||||
KPageHeap heap;
|
||||
RefCount *page_reference_counts;
|
||||
@@ -68,11 +74,19 @@ namespace ams::kern {
|
||||
KVirtualAddress AllocateBlock(s32 index, bool random) { return this->heap.AllocateBlock(index, random); }
|
||||
void Free(KVirtualAddress addr, size_t num_pages) { this->heap.Free(addr, num_pages); }
|
||||
|
||||
void TrackAllocationForOptimizedProcess(KVirtualAddress block, size_t num_pages);
|
||||
void InitializeOptimizedMemory() { std::memset(GetVoidPointer(this->metadata_region), 0, CalculateOptimizedProcessOverheadSize(this->heap.GetSize())); }
|
||||
|
||||
void TrackUnoptimizedAllocation(KVirtualAddress block, size_t num_pages);
|
||||
size_t TrackOptimizedAllocation(KVirtualAddress block, size_t num_pages);
|
||||
|
||||
size_t ProcessOptimizedAllocation(bool *out_any_new, KVirtualAddress block, size_t num_pages, u8 fill_pattern);
|
||||
|
||||
constexpr Pool GetPool() const { return this->pool; }
|
||||
constexpr size_t GetSize() const { return this->heap.GetSize(); }
|
||||
constexpr KVirtualAddress GetEndAddress() const { return this->heap.GetEndAddress(); }
|
||||
|
||||
size_t GetFreeSize() const { return this->heap.GetFreeSize(); }
|
||||
|
||||
constexpr void SetNext(Impl *n) { this->next = n; }
|
||||
constexpr void SetPrev(Impl *n) { this->prev = n; }
|
||||
constexpr Impl *GetNext() const { return this->next; }
|
||||
@@ -125,8 +139,6 @@ namespace ams::kern {
|
||||
this->Free(this->heap.GetAddress() + free_start * PageSize, free_count);
|
||||
}
|
||||
}
|
||||
public:
|
||||
static size_t CalculateMetadataOverheadSize(size_t region_size);
|
||||
};
|
||||
private:
|
||||
KLightLock pool_locks[Pool_Count];
|
||||
@@ -153,7 +165,7 @@ namespace ams::kern {
|
||||
}
|
||||
}
|
||||
|
||||
Result AllocatePageGroupImpl(KPageGroup *out, size_t num_pages, Pool pool, Direction dir, bool optimize, bool random);
|
||||
Result AllocatePageGroupImpl(KPageGroup *out, size_t num_pages, Pool pool, Direction dir, bool unoptimized, bool random);
|
||||
public:
|
||||
KMemoryManager()
|
||||
: pool_locks(), pool_managers_head(), pool_managers_tail(), managers(), num_managers(), optimized_process_ids(), has_optimized_process()
|
||||
@@ -163,8 +175,12 @@ namespace ams::kern {
|
||||
|
||||
NOINLINE void Initialize(KVirtualAddress metadata_region, size_t metadata_region_size);
|
||||
|
||||
NOINLINE Result InitializeOptimizedMemory(u64 process_id, Pool pool);
|
||||
NOINLINE void FinalizeOptimizedMemory(u64 process_id, Pool pool);
|
||||
|
||||
NOINLINE KVirtualAddress AllocateContinuous(size_t num_pages, size_t align_pages, u32 option);
|
||||
NOINLINE Result Allocate(KPageGroup *out, size_t num_pages, u32 option);
|
||||
NOINLINE Result AllocateForProcess(KPageGroup *out, size_t num_pages, u32 option, u64 process_id, u8 fill_pattern);
|
||||
|
||||
void Open(KVirtualAddress address, size_t num_pages) {
|
||||
/* Repeatedly open references until we've done so for all pages. */
|
||||
@@ -204,6 +220,23 @@ namespace ams::kern {
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
size_t GetFreeSize() {
|
||||
size_t total = 0;
|
||||
for (size_t i = 0; i < this->num_managers; i++) {
|
||||
total += this->managers[i].GetFreeSize();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
size_t GetFreeSize(Pool pool) {
|
||||
constexpr Direction GetSizeDirection = Direction_FromFront;
|
||||
size_t total = 0;
|
||||
for (auto *manager = this->GetFirstManager(pool, GetSizeDirection); manager != nullptr; manager = this->GetNextManager(manager, GetSizeDirection)) {
|
||||
total += manager->GetFreeSize();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
public:
|
||||
static size_t CalculateMetadataOverheadSize(size_t region_size) {
|
||||
return Impl::CalculateMetadataOverheadSize(region_size);
|
||||
|
||||
@@ -49,6 +49,11 @@ namespace ams::kern {
|
||||
|
||||
return Delete(obj.GetPointerUnsafe(), name);
|
||||
}
|
||||
|
||||
template<typename Derived> requires std::derived_from<Derived, KAutoObject>
|
||||
static KScopedAutoObject<Derived> Find(const char *name) {
|
||||
return Find(name);
|
||||
}
|
||||
private:
|
||||
static KScopedAutoObject<KAutoObject> FindImpl(const char *name);
|
||||
|
||||
|
||||
@@ -140,11 +140,14 @@ namespace ams::kern {
|
||||
constexpr size_t GetSize() const { return this->heap_size; }
|
||||
constexpr KVirtualAddress GetEndAddress() const { return this->GetAddress() + this->GetSize(); }
|
||||
constexpr size_t GetPageOffset(KVirtualAddress block) const { return (block - this->GetAddress()) / PageSize; }
|
||||
constexpr size_t GetPageOffsetToEnd(KVirtualAddress block) const { return (this->GetEndAddress() - block) / PageSize; }
|
||||
|
||||
void Initialize(KVirtualAddress heap_address, size_t heap_size, KVirtualAddress metadata_address, size_t metadata_size) {
|
||||
return Initialize(heap_address, heap_size, metadata_address, metadata_size, MemoryBlockPageShifts, NumMemoryBlockPageShifts);
|
||||
}
|
||||
|
||||
size_t GetFreeSize() const { return this->GetNumFreePages() * PageSize; }
|
||||
|
||||
void UpdateUsedSize() {
|
||||
this->used_size = this->heap_size - (this->GetNumFreePages() * PageSize);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,6 @@ namespace ams::kern {
|
||||
OperationType_Unmap = 2,
|
||||
OperationType_ChangePermissions = 3,
|
||||
OperationType_ChangePermissionsAndRefresh = 4,
|
||||
/* TODO: perm/attr operations */
|
||||
};
|
||||
|
||||
static constexpr size_t MaxPhysicalMapAlignment = 1_GB;
|
||||
@@ -134,7 +133,7 @@ namespace ams::kern {
|
||||
KProcessAddress code_region_start;
|
||||
KProcessAddress code_region_end;
|
||||
size_t max_heap_size;
|
||||
size_t max_physical_memory_size;
|
||||
size_t mapped_physical_memory_size;
|
||||
size_t mapped_unsafe_physical_memory;
|
||||
mutable KLightLock general_lock;
|
||||
mutable KLightLock map_physical_memory_lock;
|
||||
@@ -157,7 +156,7 @@ namespace ams::kern {
|
||||
address_space_start(), address_space_end(), heap_region_start(), heap_region_end(), current_heap_end(),
|
||||
alias_region_start(), alias_region_end(), stack_region_start(), stack_region_end(), kernel_map_region_start(),
|
||||
kernel_map_region_end(), alias_code_region_start(), alias_code_region_end(), code_region_start(), code_region_end(),
|
||||
max_heap_size(), max_physical_memory_size(),mapped_unsafe_physical_memory(), general_lock(), map_physical_memory_lock(),
|
||||
max_heap_size(), mapped_physical_memory_size(), mapped_unsafe_physical_memory(), general_lock(), map_physical_memory_lock(),
|
||||
impl(), memory_block_manager(), allocate_option(), address_space_width(), is_kernel(), enable_aslr(), memory_block_slab_manager(),
|
||||
block_info_manager(), cached_physical_linear_region(), cached_physical_heap_region(), cached_virtual_heap_region(),
|
||||
heap_fill_value(), ipc_fill_value(), stack_fill_value()
|
||||
@@ -181,6 +180,15 @@ namespace ams::kern {
|
||||
return this->address_space_start <= addr && addr < addr + size && addr + size - 1 <= this->address_space_end - 1;
|
||||
}
|
||||
|
||||
constexpr bool IsInAliasRegion(KProcessAddress addr, size_t size) const {
|
||||
return this->Contains(addr, size) && this->alias_region_start <= addr && addr + size - 1 <= this->alias_region_end - 1;
|
||||
}
|
||||
|
||||
bool IsInUnsafeAliasRegion(KProcessAddress addr, size_t size) const {
|
||||
/* Even though Unsafe physical memory is KMemoryState_Normal, it must be mapped inside the alias code region. */
|
||||
return this->CanContain(addr, size, KMemoryState_AliasCode);
|
||||
}
|
||||
|
||||
KProcessAddress GetRegionAddress(KMemoryState state) const;
|
||||
size_t GetRegionSize(KMemoryState state) const;
|
||||
bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const;
|
||||
@@ -194,12 +202,30 @@ namespace ams::kern {
|
||||
|
||||
bool IsLockedByCurrentThread() const { return this->general_lock.IsLockedByCurrentThread(); }
|
||||
|
||||
bool IsLinearMappedPhysicalAddress(KPhysicalAddress phys_addr) {
|
||||
MESOSPHERE_ASSERT(this->IsLockedByCurrentThread());
|
||||
|
||||
return KMemoryLayout::IsLinearMappedPhysicalAddress(std::addressof(this->cached_physical_linear_region), phys_addr, this->cached_physical_linear_region);
|
||||
}
|
||||
|
||||
bool IsLinearMappedPhysicalAddress(KPhysicalAddress phys_addr, size_t size) {
|
||||
MESOSPHERE_ASSERT(this->IsLockedByCurrentThread());
|
||||
|
||||
return KMemoryLayout::IsLinearMappedPhysicalAddress(std::addressof(this->cached_physical_linear_region), phys_addr, size, this->cached_physical_linear_region);
|
||||
}
|
||||
|
||||
bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr) {
|
||||
MESOSPHERE_ASSERT(this->IsLockedByCurrentThread());
|
||||
|
||||
return KMemoryLayout::IsHeapPhysicalAddress(std::addressof(this->cached_physical_heap_region), phys_addr, this->cached_physical_heap_region);
|
||||
}
|
||||
|
||||
bool IsHeapPhysicalAddressForFinalize(KPhysicalAddress phys_addr) {
|
||||
MESOSPHERE_ASSERT(!this->IsLockedByCurrentThread());
|
||||
|
||||
return KMemoryLayout::IsHeapPhysicalAddress(std::addressof(this->cached_physical_heap_region), phys_addr, this->cached_physical_heap_region);
|
||||
}
|
||||
|
||||
bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr, size_t size) {
|
||||
MESOSPHERE_ASSERT(this->IsLockedByCurrentThread());
|
||||
|
||||
@@ -225,13 +251,21 @@ namespace ams::kern {
|
||||
constexpr size_t GetNumGuardPages() const { return this->IsKernel() ? 1 : 4; }
|
||||
ALWAYS_INLINE KProcessAddress FindFreeArea(KProcessAddress region_start, size_t region_num_pages, size_t num_pages, size_t alignment, size_t offset, size_t guard_pages) const;
|
||||
|
||||
Result CheckMemoryStateContiguous(KProcessAddress addr, size_t size, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr) const;
|
||||
|
||||
Result CheckMemoryState(const KMemoryInfo &info, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr) const;
|
||||
Result CheckMemoryState(KMemoryState *out_state, KMemoryPermission *out_perm, KMemoryAttribute *out_attr, KProcessAddress addr, size_t size, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr, u32 ignore_attr = DefaultMemoryIgnoreAttr) const;
|
||||
Result CheckMemoryState(KProcessAddress addr, size_t size, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr, u32 ignore_attr = DefaultMemoryIgnoreAttr) const {
|
||||
return this->CheckMemoryState(nullptr, nullptr, nullptr, addr, size, state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr);
|
||||
}
|
||||
|
||||
Result LockMemoryAndOpen(KPageGroup *out_pg, KPhysicalAddress *out_paddr, KProcessAddress addr, size_t size, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr, KMemoryPermission new_perm, u32 lock_attr);
|
||||
Result UnlockMemory(KProcessAddress addr, size_t size, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr, KMemoryPermission new_perm, u32 lock_attr, const KPageGroup *pg);
|
||||
|
||||
Result QueryInfoImpl(KMemoryInfo *out_info, ams::svc::PageInfo *out_page, KProcessAddress address) const;
|
||||
|
||||
Result QueryMappingImpl(KProcessAddress *out, KPhysicalAddress address, size_t size, KMemoryState state) const;
|
||||
|
||||
Result AllocateAndMapPagesImpl(PageLinkedList *page_list, KProcessAddress address, size_t num_pages, const KPageProperties properties);
|
||||
Result MapPageGroupImpl(PageLinkedList *page_list, KProcessAddress address, const KPageGroup &pg, const KPageProperties properties, bool reuse_ll);
|
||||
|
||||
@@ -239,6 +273,10 @@ namespace ams::kern {
|
||||
bool IsValidPageGroup(const KPageGroup &pg, KProcessAddress addr, size_t num_pages);
|
||||
|
||||
NOINLINE Result MapPages(KProcessAddress *out_addr, size_t num_pages, size_t alignment, KPhysicalAddress phys_addr, bool is_pa_valid, KProcessAddress region_start, size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
|
||||
|
||||
Result SetupForIpcClient(PageLinkedList *page_list, KProcessAddress address, size_t size, KMemoryPermission test_perm, KMemoryState dst_state);
|
||||
Result SetupForIpcServer(KProcessAddress *out_addr, size_t size, KProcessAddress src_addr, KMemoryPermission test_perm, KMemoryState dst_state, KPageTableBase &src_page_table, bool send);
|
||||
Result CleanupForIpcClientOnServerSetupFailure(PageLinkedList *page_list, KProcessAddress address, size_t size, KMemoryPermission test_perm);
|
||||
public:
|
||||
bool GetPhysicalAddress(KPhysicalAddress *out, KProcessAddress virt_addr) const {
|
||||
return this->GetImpl().GetPhysicalAddress(out, virt_addr);
|
||||
@@ -248,9 +286,17 @@ namespace ams::kern {
|
||||
|
||||
Result SetMemoryPermission(KProcessAddress addr, size_t size, ams::svc::MemoryPermission perm);
|
||||
Result SetProcessMemoryPermission(KProcessAddress addr, size_t size, ams::svc::MemoryPermission perm);
|
||||
Result SetMemoryAttribute(KProcessAddress addr, size_t size, u32 mask, u32 attr);
|
||||
Result SetHeapSize(KProcessAddress *out, size_t size);
|
||||
Result SetMaxHeapSize(size_t size);
|
||||
Result QueryInfo(KMemoryInfo *out_info, ams::svc::PageInfo *out_page_info, KProcessAddress addr) const;
|
||||
Result QueryPhysicalAddress(ams::svc::PhysicalMemoryInfo *out, KProcessAddress address) const;
|
||||
Result QueryStaticMapping(KProcessAddress *out, KPhysicalAddress address, size_t size) const { return this->QueryMappingImpl(out, address, size, KMemoryState_Static); }
|
||||
Result QueryIoMapping(KProcessAddress *out, KPhysicalAddress address, size_t size) const { return this->QueryMappingImpl(out, address, size, KMemoryState_Io); }
|
||||
Result MapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
|
||||
Result UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
|
||||
Result MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
|
||||
Result UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size);
|
||||
Result MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm);
|
||||
Result MapStatic(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm);
|
||||
Result MapRegion(KMemoryRegionType region_type, KMemoryPermission perm);
|
||||
@@ -267,12 +313,62 @@ namespace ams::kern {
|
||||
return this->MapPages(out_addr, num_pages, PageSize, Null<KPhysicalAddress>, false, this->GetRegionAddress(state), this->GetRegionSize(state) / PageSize, state, perm);
|
||||
}
|
||||
|
||||
Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state, KMemoryPermission perm);
|
||||
Result UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state);
|
||||
|
||||
Result MapPageGroup(KProcessAddress *out_addr, const KPageGroup &pg, KProcessAddress region_start, size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
|
||||
Result MapPageGroup(KProcessAddress address, const KPageGroup &pg, KMemoryState state, KMemoryPermission perm);
|
||||
Result UnmapPageGroup(KProcessAddress address, const KPageGroup &pg, KMemoryState 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);
|
||||
Result MakeAndOpenPageGroupContiguous(KPageGroup *out, KProcessAddress address, size_t num_pages, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr);
|
||||
|
||||
Result InvalidateProcessDataCache(KProcessAddress address, size_t size);
|
||||
|
||||
Result ReadDebugMemory(void *buffer, KProcessAddress address, size_t size);
|
||||
Result WriteDebugMemory(KProcessAddress address, const void *buffer, size_t size);
|
||||
|
||||
Result LockForDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned);
|
||||
Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size);
|
||||
Result LockForIpcUserBuffer(KPhysicalAddress *out, KProcessAddress address, size_t size);
|
||||
Result UnlockForIpcUserBuffer(KProcessAddress address, size_t size);
|
||||
|
||||
Result LockForTransferMemory(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm);
|
||||
Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup &pg);
|
||||
Result LockForCodeMemory(KPageGroup *out, KProcessAddress address, size_t size);
|
||||
Result UnlockForCodeMemory(KProcessAddress address, size_t size, const KPageGroup &pg);
|
||||
|
||||
Result CopyMemoryFromLinearToUser(KProcessAddress dst_addr, size_t size, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr);
|
||||
Result CopyMemoryFromLinearToKernel(KProcessAddress dst_addr, size_t size, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr);
|
||||
Result CopyMemoryFromUserToLinear(KProcessAddress dst_addr, size_t size, u32 dst_state_mask, u32 dst_state, KMemoryPermission dst_test_perm, u32 dst_attr_mask, u32 dst_attr, KProcessAddress src_addr);
|
||||
Result CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, size_t size, u32 dst_state_mask, u32 dst_state, KMemoryPermission dst_test_perm, u32 dst_attr_mask, u32 dst_attr, KProcessAddress src_addr);
|
||||
Result CopyMemoryFromHeapToHeap(KPageTableBase &dst_page_table, KProcessAddress dst_addr, size_t size, u32 dst_state_mask, u32 dst_state, KMemoryPermission dst_test_perm, u32 dst_attr_mask, u32 dst_attr, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr);
|
||||
Result CopyMemoryFromHeapToHeapWithoutCheckDestination(KPageTableBase &dst_page_table, KProcessAddress dst_addr, size_t size, u32 dst_state_mask, u32 dst_state, KMemoryPermission dst_test_perm, u32 dst_attr_mask, u32 dst_attr, KProcessAddress src_addr, u32 src_state_mask, u32 src_state, KMemoryPermission src_test_perm, u32 src_attr_mask, u32 src_attr);
|
||||
|
||||
Result SetupForIpc(KProcessAddress *out_dst_addr, size_t size, KProcessAddress src_addr, KPageTableBase &src_page_table, KMemoryPermission test_perm, KMemoryState dst_state, bool send);
|
||||
Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state, KProcess *server_process);
|
||||
Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state);
|
||||
|
||||
Result MapPhysicalMemory(KProcessAddress address, size_t size);
|
||||
Result UnmapPhysicalMemory(KProcessAddress address, size_t size);
|
||||
|
||||
Result MapPhysicalMemoryUnsafe(KProcessAddress address, size_t size);
|
||||
Result UnmapPhysicalMemoryUnsafe(KProcessAddress address, size_t size);
|
||||
|
||||
void DumpTable() const {
|
||||
KScopedLightLock lk(this->general_lock);
|
||||
this->GetImpl().Dump(GetInteger(this->address_space_start), this->address_space_end - this->address_space_start);
|
||||
}
|
||||
|
||||
void DumpMemoryBlocks() const {
|
||||
KScopedLightLock lk(this->general_lock);
|
||||
this->DumpMemoryBlocksLocked();
|
||||
}
|
||||
|
||||
void DumpMemoryBlocksLocked() const {
|
||||
MESOSPHERE_ASSERT(this->IsLockedByCurrentThread());
|
||||
this->memory_block_manager.DumpBlocks();
|
||||
}
|
||||
public:
|
||||
KProcessAddress GetAddressSpaceStart() const { return this->address_space_start; }
|
||||
KProcessAddress GetHeapRegionStart() const { return this->heap_region_start; }
|
||||
@@ -287,29 +383,38 @@ namespace ams::kern {
|
||||
size_t GetStackRegionSize() const { return this->stack_region_end - this->stack_region_start; }
|
||||
size_t GetKernelMapRegionSize() const { return this->kernel_map_region_end - this->kernel_map_region_start; }
|
||||
size_t GetAliasCodeRegionSize() const { return this->alias_code_region_end - this->alias_code_region_start; }
|
||||
|
||||
size_t GetNormalMemorySize() const {
|
||||
/* Lock the table. */
|
||||
KScopedLightLock lk(this->general_lock);
|
||||
|
||||
return (this->current_heap_end - this->heap_region_start) + this->mapped_physical_memory_size;
|
||||
}
|
||||
|
||||
u32 GetAllocateOption() const { return this->allocate_option; }
|
||||
public:
|
||||
static ALWAYS_INLINE KVirtualAddress GetLinearVirtualAddress(KPhysicalAddress addr) {
|
||||
static ALWAYS_INLINE KVirtualAddress GetLinearMappedVirtualAddress(KPhysicalAddress addr) {
|
||||
return KMemoryLayout::GetLinearVirtualAddress(addr);
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE KPhysicalAddress GetLinearPhysicalAddress(KVirtualAddress addr) {
|
||||
static ALWAYS_INLINE KPhysicalAddress GetLinearMappedPhysicalAddress(KVirtualAddress addr) {
|
||||
return KMemoryLayout::GetLinearPhysicalAddress(addr);
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE KVirtualAddress GetHeapVirtualAddress(KPhysicalAddress addr) {
|
||||
return GetLinearVirtualAddress(addr);
|
||||
return GetLinearMappedVirtualAddress(addr);
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE KPhysicalAddress GetHeapPhysicalAddress(KVirtualAddress addr) {
|
||||
return GetLinearPhysicalAddress(addr);
|
||||
return GetLinearMappedPhysicalAddress(addr);
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE KVirtualAddress GetPageTableVirtualAddress(KPhysicalAddress addr) {
|
||||
return GetLinearVirtualAddress(addr);
|
||||
return GetLinearMappedVirtualAddress(addr);
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE KPhysicalAddress GetPageTablePhysicalAddress(KVirtualAddress addr) {
|
||||
return GetLinearPhysicalAddress(addr);
|
||||
return GetLinearMappedPhysicalAddress(addr);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
class KServerSession;
|
||||
class KLightServerSession;
|
||||
|
||||
class KPort final : public KAutoObjectWithSlabHeapAndContainer<KPort, KAutoObjectWithList> {
|
||||
MESOSPHERE_AUTOOBJECT_TRAITS(KPort, KAutoObject);
|
||||
private:
|
||||
@@ -50,7 +53,8 @@ namespace ams::kern {
|
||||
uintptr_t GetName() const { return this->name; }
|
||||
bool IsLight() const { return this->is_light; }
|
||||
|
||||
/* TODO: More of KPort */
|
||||
Result EnqueueSession(KServerSession *session);
|
||||
Result EnqueueSession(KLightServerSession *session);
|
||||
|
||||
KClientPort &GetClientPort() { return this->client; }
|
||||
KServerPort &GetServerPort() { return this->server; }
|
||||
|
||||
@@ -400,7 +400,7 @@ namespace ams::kern {
|
||||
|
||||
/* Remove from the suggested queue and add to the scheduled queue for the new core. */
|
||||
if (new_core >= 0) {
|
||||
this->suggested_queue.Remove(priority, prev_core, member);
|
||||
this->suggested_queue.Remove(priority, new_core, member);
|
||||
if (to_front) {
|
||||
this->scheduled_queue.PushFront(priority, new_core, member);
|
||||
} else {
|
||||
|
||||
@@ -48,6 +48,8 @@ namespace ams::kern {
|
||||
};
|
||||
|
||||
using ThreadList = util::IntrusiveListMemberTraits<&KThread::process_list_node>::ListType;
|
||||
|
||||
static constexpr size_t AslrAlignment = KernelAslrAlignment;
|
||||
private:
|
||||
using SharedMemoryInfoList = util::IntrusiveListBaseTraits<KSharedMemoryInfo>::ListType;
|
||||
using TLPTree = util::IntrusiveRedBlackTreeBaseTraits<KThreadLocalPage>::TreeType<KThreadLocalPage>;
|
||||
@@ -64,7 +66,7 @@ namespace ams::kern {
|
||||
size_t system_resource_num_pages{};
|
||||
size_t memory_release_hint{};
|
||||
State state{};
|
||||
KLightLock lock{};
|
||||
KLightLock state_lock{};
|
||||
KLightLock list_lock{};
|
||||
KConditionVariable cond_var{};
|
||||
KAddressArbiter address_arbiter{};
|
||||
@@ -118,38 +120,127 @@ namespace ams::kern {
|
||||
KPageTableManager page_table_manager{};
|
||||
private:
|
||||
Result Initialize(const ams::svc::CreateProcessParameter ¶ms);
|
||||
|
||||
void StartTermination();
|
||||
void FinishTermination();
|
||||
public:
|
||||
KProcess() { /* ... */ }
|
||||
virtual ~KProcess() { /* ... */ }
|
||||
|
||||
Result Initialize(const ams::svc::CreateProcessParameter ¶ms, const KPageGroup &pg, const u32 *caps, s32 num_caps, KResourceLimit *res_limit, KMemoryManager::Pool pool);
|
||||
Result Initialize(const ams::svc::CreateProcessParameter ¶ms, svc::KUserPointer<const u32 *> caps, s32 num_caps, KResourceLimit *res_limit, KMemoryManager::Pool pool);
|
||||
void Exit();
|
||||
|
||||
constexpr const char *GetName() const { return this->name; }
|
||||
|
||||
constexpr ams::svc::ProgramId GetProgramId() const { return this->program_id; }
|
||||
|
||||
constexpr u64 GetProcessId() const { return this->process_id; }
|
||||
|
||||
constexpr State GetState() const { return this->state; }
|
||||
|
||||
constexpr u64 GetCoreMask() const { return this->capabilities.GetCoreMask(); }
|
||||
constexpr u64 GetPriorityMask() const { return this->capabilities.GetPriorityMask(); }
|
||||
|
||||
constexpr s32 GetIdealCoreId() const { return this->ideal_core_id; }
|
||||
constexpr void SetIdealCoreId(s32 core_id) { this->ideal_core_id = core_id; }
|
||||
|
||||
constexpr bool CheckThreadPriority(s32 prio) const { return ((1ul << prio) & this->GetPriorityMask()) != 0; }
|
||||
|
||||
constexpr u32 GetCreateProcessFlags() const { return this->flags; }
|
||||
|
||||
constexpr bool Is64Bit() const { return this->flags & ams::svc::CreateProcessFlag_Is64Bit; }
|
||||
|
||||
constexpr KProcessAddress GetEntryPoint() const { return this->code_address; }
|
||||
|
||||
constexpr bool IsSuspended() const {
|
||||
return this->is_suspended;
|
||||
constexpr KMemoryManager::Pool GetMemoryPool() const { return this->memory_pool; }
|
||||
|
||||
constexpr u64 GetRandomEntropy(size_t i) const { return this->entropy[i]; }
|
||||
|
||||
constexpr bool IsApplication() const { return this->is_application; }
|
||||
|
||||
constexpr bool IsSuspended() const { return this->is_suspended; }
|
||||
constexpr void SetSuspended(bool suspended) { this->is_suspended = suspended; }
|
||||
|
||||
Result Terminate();
|
||||
|
||||
constexpr bool IsTerminated() const {
|
||||
return this->state == State_Terminated;
|
||||
}
|
||||
|
||||
KThread *GetPreemptionStatePinnedThread(s32 core_id) const {
|
||||
constexpr bool IsAttachedToDebugger() const {
|
||||
return this->attached_object != nullptr;
|
||||
}
|
||||
|
||||
constexpr bool IsPermittedInterrupt(int32_t interrupt_id) const {
|
||||
return this->capabilities.IsPermittedInterrupt(interrupt_id);
|
||||
}
|
||||
|
||||
constexpr bool IsPermittedDebug() const {
|
||||
return this->capabilities.IsPermittedDebug();
|
||||
}
|
||||
|
||||
constexpr bool CanForceDebug() const {
|
||||
return this->capabilities.CanForceDebug();
|
||||
}
|
||||
|
||||
u32 GetAllocateOption() const { return this->page_table.GetAllocateOption(); }
|
||||
|
||||
ThreadList &GetThreadList() { return this->thread_list; }
|
||||
const ThreadList &GetThreadList() const { return this->thread_list; }
|
||||
|
||||
constexpr void *GetDebugObject() const { return this->attached_object; }
|
||||
KProcess::State SetDebugObject(void *debug_object);
|
||||
void ClearDebugObject(KProcess::State state);
|
||||
|
||||
bool EnterJitDebug(ams::svc::DebugEvent event, ams::svc::DebugException exception, uintptr_t param1 = 0, uintptr_t param2 = 0, uintptr_t param3 = 0, uintptr_t param4 = 0);
|
||||
|
||||
KEventInfo *GetJitDebugInfo();
|
||||
void ClearJitDebugInfo();
|
||||
|
||||
bool EnterUserException();
|
||||
bool LeaveUserException();
|
||||
bool ReleaseUserException(KThread *thread);
|
||||
|
||||
KThread *GetPinnedThread(s32 core_id) const {
|
||||
MESOSPHERE_ASSERT(0 <= core_id && core_id < static_cast<s32>(cpu::NumCores));
|
||||
return this->pinned_threads[core_id];
|
||||
}
|
||||
|
||||
void PinThread(s32 core_id, KThread *thread) {
|
||||
MESOSPHERE_ASSERT(0 <= core_id && core_id < static_cast<s32>(cpu::NumCores));
|
||||
MESOSPHERE_ASSERT(thread != nullptr);
|
||||
MESOSPHERE_ASSERT(this->pinned_threads[core_id] == nullptr);
|
||||
this->pinned_threads[core_id] = thread;
|
||||
}
|
||||
|
||||
void UnpinThread(s32 core_id, KThread *thread) {
|
||||
MESOSPHERE_ASSERT(0 <= core_id && core_id < static_cast<s32>(cpu::NumCores));
|
||||
MESOSPHERE_ASSERT(thread != nullptr);
|
||||
MESOSPHERE_ASSERT(this->pinned_threads[core_id] == thread);
|
||||
this->pinned_threads[core_id] = nullptr;
|
||||
}
|
||||
|
||||
void CopySvcPermissionsTo(KThread::StackParameters &sp) {
|
||||
this->capabilities.CopySvcPermissionsTo(sp);
|
||||
}
|
||||
|
||||
void CopyPinnedSvcPermissionsTo(KThread::StackParameters &sp) {
|
||||
this->capabilities.CopyPinnedSvcPermissionsTo(sp);
|
||||
}
|
||||
|
||||
void CopyUnpinnedSvcPermissionsTo(KThread::StackParameters &sp) {
|
||||
this->capabilities.CopyUnpinnedSvcPermissionsTo(sp);
|
||||
}
|
||||
|
||||
void CopyEnterExceptionSvcPermissionsTo(KThread::StackParameters &sp) {
|
||||
this->capabilities.CopyEnterExceptionSvcPermissionsTo(sp);
|
||||
}
|
||||
|
||||
void CopyLeaveExceptionSvcPermissionsTo(KThread::StackParameters &sp) {
|
||||
this->capabilities.CopyLeaveExceptionSvcPermissionsTo(sp);
|
||||
}
|
||||
|
||||
constexpr KResourceLimit *GetResourceLimit() const { return this->resource_limit; }
|
||||
|
||||
bool ReserveResource(ams::svc::LimitableResource which, s64 value);
|
||||
@@ -157,27 +248,114 @@ namespace ams::kern {
|
||||
void ReleaseResource(ams::svc::LimitableResource which, s64 value);
|
||||
void ReleaseResource(ams::svc::LimitableResource which, s64 value, s64 hint);
|
||||
|
||||
constexpr KLightLock &GetStateLock() { return this->state_lock; }
|
||||
constexpr KLightLock &GetListLock() { return this->list_lock; }
|
||||
|
||||
constexpr KProcessPageTable &GetPageTable() { return this->page_table; }
|
||||
constexpr const KProcessPageTable &GetPageTable() const { return this->page_table; }
|
||||
|
||||
constexpr KHandleTable &GetHandleTable() { return this->handle_table; }
|
||||
constexpr const KHandleTable &GetHandleTable() const { return this->handle_table; }
|
||||
|
||||
KWaitObject *GetWaitObjectPointer() { return std::addressof(this->wait_object); }
|
||||
|
||||
size_t GetUsedUserPhysicalMemorySize() const;
|
||||
size_t GetTotalUserPhysicalMemorySize() const;
|
||||
size_t GetUsedNonSystemUserPhysicalMemorySize() const;
|
||||
size_t GetTotalNonSystemUserPhysicalMemorySize() const;
|
||||
|
||||
Result AddSharedMemory(KSharedMemory *shmem, KProcessAddress address, size_t size);
|
||||
void RemoveSharedMemory(KSharedMemory *shmem, KProcessAddress address, size_t size);
|
||||
|
||||
Result CreateThreadLocalRegion(KProcessAddress *out);
|
||||
Result DeleteThreadLocalRegion(KProcessAddress addr);
|
||||
void *GetThreadLocalRegionPointer(KProcessAddress addr);
|
||||
|
||||
constexpr KProcessAddress GetProcessLocalRegionAddress() const { return this->plr_address; }
|
||||
|
||||
void AddCpuTime(s64 diff) { this->cpu_time += diff; }
|
||||
|
||||
constexpr s64 GetScheduledCount() const { return this->schedule_count; }
|
||||
void IncrementScheduledCount() { ++this->schedule_count; }
|
||||
|
||||
void IncrementThreadCount();
|
||||
void DecrementThreadCount();
|
||||
|
||||
size_t GetTotalSystemResourceSize() const { return this->system_resource_num_pages * PageSize; }
|
||||
size_t GetUsedSystemResourceSize() const {
|
||||
if (this->system_resource_num_pages == 0) {
|
||||
return 0;
|
||||
}
|
||||
return this->dynamic_page_manager.GetUsed() * PageSize;
|
||||
}
|
||||
|
||||
void SetRunningThread(s32 core, KThread *thread, u64 idle_count) {
|
||||
this->running_threads[core] = thread;
|
||||
this->running_thread_idle_counts[core] = idle_count;
|
||||
}
|
||||
|
||||
void ClearRunningThread(KThread *thread) {
|
||||
for (size_t i = 0; i < util::size(this->running_threads); ++i) {
|
||||
if (this->running_threads[i] == thread) {
|
||||
this->running_threads[i] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr KThread *GetRunningThread(s32 core) const { return this->running_threads[core]; }
|
||||
constexpr u64 GetRunningThreadIdleCount(s32 core) const { return this->running_thread_idle_counts[core]; }
|
||||
|
||||
void RegisterThread(KThread *thread);
|
||||
void UnregisterThread(KThread *thread);
|
||||
|
||||
Result Run(s32 priority, size_t stack_size);
|
||||
|
||||
void SetPreemptionState();
|
||||
Result Reset();
|
||||
|
||||
void SetDebugBreak() {
|
||||
if (this->state == State_RunningAttached) {
|
||||
this->ChangeState(State_DebugBreak);
|
||||
}
|
||||
}
|
||||
|
||||
void SetAttached() {
|
||||
if (this->state == State_DebugBreak) {
|
||||
this->ChangeState(State_RunningAttached);
|
||||
}
|
||||
}
|
||||
|
||||
Result SetActivity(ams::svc::ProcessActivity activity);
|
||||
|
||||
void PinCurrentThread();
|
||||
|
||||
Result SignalToAddress(KProcessAddress address) {
|
||||
return this->cond_var.SignalToAddress(address);
|
||||
}
|
||||
|
||||
Result WaitForAddress(ams::svc::Handle handle, KProcessAddress address, u32 tag) {
|
||||
return this->cond_var.WaitForAddress(handle, address, tag);
|
||||
}
|
||||
|
||||
void SignalConditionVariable(uintptr_t cv_key, int32_t count) {
|
||||
return this->cond_var.Signal(cv_key, count);
|
||||
}
|
||||
|
||||
Result WaitConditionVariable(KProcessAddress address, uintptr_t cv_key, u32 tag, s64 ns) {
|
||||
return this->cond_var.Wait(address, cv_key, tag, ns);
|
||||
}
|
||||
|
||||
Result SignalAddressArbiter(uintptr_t address, ams::svc::SignalType signal_type, s32 value, s32 count) {
|
||||
return this->address_arbiter.SignalToAddress(address, signal_type, value, count);
|
||||
}
|
||||
|
||||
Result WaitAddressArbiter(uintptr_t address, ams::svc::ArbitrationType arb_type, s32 value, s64 timeout) {
|
||||
return this->address_arbiter.WaitForAddress(address, arb_type, value, timeout);
|
||||
}
|
||||
|
||||
Result GetThreadList(s32 *out_num_threads, ams::kern::svc::KUserPointer<u64 *> out_thread_ids, s32 max_out_count);
|
||||
|
||||
static KProcess *GetProcessFromId(u64 process_id);
|
||||
static Result GetProcessList(s32 *out_num_processes, ams::kern::svc::KUserPointer<u64 *> out_process_ids, s32 max_out_count);
|
||||
|
||||
static void Switch(KProcess *cur_process, KProcess *next_process) {
|
||||
/* Set the current process pointer. */
|
||||
|
||||
@@ -70,6 +70,11 @@ namespace ams::kern {
|
||||
NOINLINE void Initialize(KThread *idle_thread);
|
||||
NOINLINE void Activate();
|
||||
|
||||
ALWAYS_INLINE void SetInterruptTaskRunnable() {
|
||||
this->state.interrupt_task_thread_runnable = true;
|
||||
this->state.needs_scheduling = true;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void RequestScheduleOnInterrupt() {
|
||||
SetSchedulerUpdateNeeded();
|
||||
|
||||
@@ -77,6 +82,22 @@ namespace ams::kern {
|
||||
this->ScheduleOnInterrupt();
|
||||
}
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u64 GetIdleCount() const {
|
||||
return this->state.idle_count;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE KThread *GetIdleThread() const {
|
||||
return this->idle_thread;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE KThread *GetPreviousThread() const {
|
||||
return this->prev_thread;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE s64 GetLastContextSwitchTime() const {
|
||||
return this->last_context_switch_time;
|
||||
}
|
||||
private:
|
||||
/* Static private API. */
|
||||
static ALWAYS_INLINE bool IsSchedulerUpdateNeeded() { return s_scheduler_update_needed; }
|
||||
@@ -85,13 +106,13 @@ namespace ams::kern {
|
||||
static ALWAYS_INLINE KSchedulerPriorityQueue &GetPriorityQueue() { return s_priority_queue; }
|
||||
|
||||
static NOINLINE u64 UpdateHighestPriorityThreadsImpl();
|
||||
|
||||
static NOINLINE void InterruptTaskThreadToRunnable();
|
||||
public:
|
||||
/* Static public API. */
|
||||
static ALWAYS_INLINE bool CanSchedule() { return GetCurrentThread().GetDisableDispatchCount() == 0; }
|
||||
static ALWAYS_INLINE bool IsSchedulerLockedByCurrentThread() { return s_scheduler_lock.IsLockedByCurrentThread(); }
|
||||
|
||||
static NOINLINE void SetInterruptTaskThreadRunnable();
|
||||
|
||||
static ALWAYS_INLINE void DisableScheduling() {
|
||||
MESOSPHERE_ASSERT(GetCurrentThread().GetDisableDispatchCount() >= 0);
|
||||
GetCurrentThread().DisableDispatch();
|
||||
@@ -116,12 +137,20 @@ namespace ams::kern {
|
||||
}
|
||||
}
|
||||
|
||||
static NOINLINE void ClearPreviousThread(KThread *thread);
|
||||
|
||||
static NOINLINE void PinCurrentThread(KProcess *cur_process);
|
||||
static NOINLINE void UnpinCurrentThread(KProcess *cur_process);
|
||||
|
||||
static NOINLINE void OnThreadStateChanged(KThread *thread, KThread::ThreadState old_state);
|
||||
static NOINLINE void OnThreadPriorityChanged(KThread *thread, s32 old_priority);
|
||||
static NOINLINE void OnThreadAffinityMaskChanged(KThread *thread, const KAffinityMask &old_affinity, s32 old_core);
|
||||
|
||||
/* TODO: Yield operations */
|
||||
static NOINLINE void RotateScheduledQueue(s32 priority, s32 core_id);
|
||||
|
||||
static NOINLINE void YieldWithoutCoreMigration();
|
||||
static NOINLINE void YieldWithCoreMigration();
|
||||
static NOINLINE void YieldToAnyThread();
|
||||
private:
|
||||
/* Instanced private API. */
|
||||
void ScheduleImpl();
|
||||
|
||||
@@ -38,6 +38,11 @@ namespace ams::kern {
|
||||
virtual ~KServerPort() { /* ... */ }
|
||||
|
||||
void Initialize(KPort *parent);
|
||||
void EnqueueSession(KServerSession *session);
|
||||
void EnqueueSession(KLightServerSession *session);
|
||||
|
||||
KServerSession *AcceptSession();
|
||||
KLightServerSession *AcceptLightSession();
|
||||
|
||||
constexpr const KPort *GetParent() const { return this->parent; }
|
||||
|
||||
@@ -46,11 +51,8 @@ namespace ams::kern {
|
||||
/* Overridden virtual functions. */
|
||||
virtual void Destroy() override;
|
||||
virtual bool IsSignaled() const override;
|
||||
|
||||
/* TODO: More of KClientPort. */
|
||||
private:
|
||||
void CleanupSessions();
|
||||
/* TODO: This is a placeholder definition. */
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -36,13 +36,23 @@ namespace ams::kern {
|
||||
constexpr KServerSession() : parent(), request_list(), current_request(), lock() { /* ... */ }
|
||||
virtual ~KServerSession() { /* ... */ }
|
||||
|
||||
void Initialize(KSession *parent);
|
||||
virtual void Destroy() override;
|
||||
|
||||
void Initialize(KSession *p) { this->parent = p; }
|
||||
|
||||
constexpr const KSession *GetParent() const { return this->parent; }
|
||||
|
||||
virtual bool IsSignaled() const override { MESOSPHERE_UNIMPLEMENTED(); }
|
||||
virtual bool IsSignaled() const override;
|
||||
|
||||
/* TODO: More of KServerSession. */
|
||||
Result OnRequest(KSessionRequest *request);
|
||||
|
||||
Result ReceiveRequest(uintptr_t message, uintptr_t buffer_size, KPhysicalAddress message_paddr);
|
||||
Result SendReply(uintptr_t message, uintptr_t buffer_size, KPhysicalAddress message_paddr);
|
||||
|
||||
void OnClientClosed();
|
||||
private:
|
||||
bool IsSignaledImpl() const;
|
||||
void CleanupRequests();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -51,12 +51,21 @@ namespace ams::kern {
|
||||
|
||||
virtual ~KSession() { /* ... */ }
|
||||
|
||||
void Initialize(KClientPort *client_port, uintptr_t name);
|
||||
virtual void Finalize() override;
|
||||
|
||||
virtual bool IsInitialized() const override { return this->initialized; }
|
||||
virtual uintptr_t GetPostDestroyArgument() const override { return reinterpret_cast<uintptr_t>(this->process); }
|
||||
|
||||
static void PostDestroy(uintptr_t arg);
|
||||
|
||||
/* TODO: This is a placeholder definition. */
|
||||
void OnServerClosed();
|
||||
void OnClientClosed();
|
||||
|
||||
bool IsServerClosed() const { return this->state != State::Normal; }
|
||||
bool IsClientClosed() const { return this->state != State::Normal; }
|
||||
|
||||
Result OnRequest(KSessionRequest *request) { return this->server.OnRequest(request); }
|
||||
|
||||
KClientSession &GetClientSession() { return this->client; }
|
||||
KServerSession &GetServerSession() { return this->server; }
|
||||
|
||||
@@ -17,13 +17,209 @@
|
||||
#include <mesosphere/kern_common.hpp>
|
||||
#include <mesosphere/kern_k_auto_object.hpp>
|
||||
#include <mesosphere/kern_slab_helpers.hpp>
|
||||
#include <mesosphere/kern_k_writable_event.hpp>
|
||||
#include <mesosphere/kern_k_thread.hpp>
|
||||
#include <mesosphere/kern_k_process.hpp>
|
||||
#include <mesosphere/kern_k_memory_block.hpp>
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
class KSessionRequest final : public KSlabAllocated<KSessionRequest>, public KAutoObject, public util::IntrusiveListBaseNode<KSessionRequest> {
|
||||
MESOSPHERE_AUTOOBJECT_TRAITS(KSessionRequest, KAutoObject);
|
||||
public:
|
||||
/* TODO: This is a placeholder definition. */
|
||||
class SessionMappings {
|
||||
private:
|
||||
static constexpr size_t NumStaticMappings = 8;
|
||||
|
||||
class Mapping {
|
||||
private:
|
||||
KProcessAddress client_address;
|
||||
KProcessAddress server_address;
|
||||
size_t size;
|
||||
KMemoryState state;
|
||||
public:
|
||||
constexpr void Set(KProcessAddress c, KProcessAddress s, size_t sz, KMemoryState st) {
|
||||
this->client_address = c;
|
||||
this->server_address = s;
|
||||
this->size = sz;
|
||||
this->state = st;
|
||||
}
|
||||
|
||||
constexpr KProcessAddress GetClientAddress() const { return this->client_address; }
|
||||
constexpr KProcessAddress GetServerAddress() const { return this->server_address; }
|
||||
constexpr size_t GetSize() const { return this->size; }
|
||||
constexpr KMemoryState GetMemoryState() const { return this->state; }
|
||||
};
|
||||
private:
|
||||
Mapping static_mappings[NumStaticMappings];
|
||||
Mapping *mappings;
|
||||
u8 num_send;
|
||||
u8 num_recv;
|
||||
u8 num_exch;
|
||||
public:
|
||||
constexpr explicit SessionMappings() : static_mappings(), mappings(), num_send(), num_recv(), num_exch() { /* ... */ }
|
||||
|
||||
void Initialize() { /* ... */ }
|
||||
void Finalize();
|
||||
|
||||
constexpr size_t GetSendCount() const { return this->num_send; }
|
||||
constexpr size_t GetReceiveCount() const { return this->num_recv; }
|
||||
constexpr size_t GetExchangeCount() const { return this->num_exch; }
|
||||
|
||||
Result PushSend(KProcessAddress client, KProcessAddress server, size_t size, KMemoryState state);
|
||||
Result PushReceive(KProcessAddress client, KProcessAddress server, size_t size, KMemoryState state);
|
||||
Result PushExchange(KProcessAddress client, KProcessAddress server, size_t size, KMemoryState state);
|
||||
|
||||
constexpr KProcessAddress GetSendClientAddress(size_t i) const { return GetSendMapping(i).GetClientAddress(); }
|
||||
constexpr KProcessAddress GetSendServerAddress(size_t i) const { return GetSendMapping(i).GetServerAddress(); }
|
||||
constexpr size_t GetSendSize(size_t i) const { return GetSendMapping(i).GetSize(); }
|
||||
constexpr KMemoryState GetSendMemoryState(size_t i) const { return GetSendMapping(i).GetMemoryState(); }
|
||||
|
||||
constexpr KProcessAddress GetReceiveClientAddress(size_t i) const { return GetReceiveMapping(i).GetClientAddress(); }
|
||||
constexpr KProcessAddress GetReceiveServerAddress(size_t i) const { return GetReceiveMapping(i).GetServerAddress(); }
|
||||
constexpr size_t GetReceiveSize(size_t i) const { return GetReceiveMapping(i).GetSize(); }
|
||||
constexpr KMemoryState GetReceiveMemoryState(size_t i) const { return GetReceiveMapping(i).GetMemoryState(); }
|
||||
|
||||
constexpr KProcessAddress GetExchangeClientAddress(size_t i) const { return GetExchangeMapping(i).GetClientAddress(); }
|
||||
constexpr KProcessAddress GetExchangeServerAddress(size_t i) const { return GetExchangeMapping(i).GetServerAddress(); }
|
||||
constexpr size_t GetExchangeSize(size_t i) const { return GetExchangeMapping(i).GetSize(); }
|
||||
constexpr KMemoryState GetExchangeMemoryState(size_t i) const { return GetExchangeMapping(i).GetMemoryState(); }
|
||||
private:
|
||||
Result PushMap(KProcessAddress client, KProcessAddress server, size_t size, KMemoryState state, size_t index);
|
||||
|
||||
constexpr const Mapping &GetSendMapping(size_t i) const {
|
||||
MESOSPHERE_ASSERT(i < this->num_send);
|
||||
|
||||
const size_t index = i;
|
||||
if (index < NumStaticMappings) {
|
||||
return this->static_mappings[index];
|
||||
} else {
|
||||
return this->mappings[index - NumStaticMappings];
|
||||
}
|
||||
}
|
||||
|
||||
constexpr const Mapping &GetReceiveMapping(size_t i) const {
|
||||
MESOSPHERE_ASSERT(i < this->num_recv);
|
||||
|
||||
const size_t index = this->num_send + i;
|
||||
if (index < NumStaticMappings) {
|
||||
return this->static_mappings[index];
|
||||
} else {
|
||||
return this->mappings[index - NumStaticMappings];
|
||||
}
|
||||
}
|
||||
|
||||
constexpr const Mapping &GetExchangeMapping(size_t i) const {
|
||||
MESOSPHERE_ASSERT(i < this->num_exch);
|
||||
|
||||
const size_t index = this->num_send + this->num_recv + i;
|
||||
if (index < NumStaticMappings) {
|
||||
return this->static_mappings[index];
|
||||
} else {
|
||||
return this->mappings[index - NumStaticMappings];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
private:
|
||||
SessionMappings mappings;
|
||||
KThread *thread;
|
||||
KProcess *server;
|
||||
KWritableEvent *event;
|
||||
uintptr_t address;
|
||||
size_t size;
|
||||
public:
|
||||
constexpr KSessionRequest() : mappings(), thread(), server(), event(), address(), size() { /* ... */ }
|
||||
virtual ~KSessionRequest() { /* ... */ }
|
||||
|
||||
static KSessionRequest *Create() {
|
||||
KSessionRequest *req = KSessionRequest::Allocate();
|
||||
if (req != nullptr) {
|
||||
KAutoObject::Create(req);
|
||||
}
|
||||
return req;
|
||||
}
|
||||
|
||||
virtual void Destroy() override {
|
||||
this->Finalize();
|
||||
KSessionRequest::Free(this);
|
||||
}
|
||||
|
||||
void Initialize(KWritableEvent *event, uintptr_t address, size_t size) {
|
||||
this->mappings.Initialize();
|
||||
|
||||
this->thread = std::addressof(GetCurrentThread());
|
||||
this->event = event;
|
||||
this->address = address;
|
||||
this->size = size;
|
||||
|
||||
this->thread->Open();
|
||||
if (this->event != nullptr) {
|
||||
this->event->Open();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void Finalize() override {
|
||||
this->mappings.Finalize();
|
||||
|
||||
if (this->thread) {
|
||||
this->thread->Close();
|
||||
}
|
||||
if (this->event) {
|
||||
this->event->Close();
|
||||
}
|
||||
if (this->server) {
|
||||
this->server->Close();
|
||||
}
|
||||
}
|
||||
|
||||
static void PostDestroy(uintptr_t arg) { /* ... */ }
|
||||
|
||||
constexpr KThread *GetThread() const { return this->thread; }
|
||||
constexpr KWritableEvent *GetEvent() const { return this->event; }
|
||||
constexpr uintptr_t GetAddress() const { return this->address; }
|
||||
constexpr size_t GetSize() const { return this->size; }
|
||||
constexpr KProcess *GetServerProcess() const { return this->server; }
|
||||
|
||||
void SetServerProcess(KProcess *process) {
|
||||
this->server = process;
|
||||
this->server->Open();
|
||||
}
|
||||
|
||||
constexpr void ClearThread() { this->thread = nullptr; }
|
||||
constexpr void ClearEvent() { this->event = nullptr; }
|
||||
|
||||
constexpr size_t GetSendCount() const { return this->mappings.GetSendCount(); }
|
||||
constexpr size_t GetReceiveCount() const { return this->mappings.GetReceiveCount(); }
|
||||
constexpr size_t GetExchangeCount() const { return this->mappings.GetExchangeCount(); }
|
||||
|
||||
Result PushSend(KProcessAddress client, KProcessAddress server, size_t size, KMemoryState state) {
|
||||
return this->mappings.PushSend(client, server, size, state);
|
||||
}
|
||||
|
||||
Result PushReceive(KProcessAddress client, KProcessAddress server, size_t size, KMemoryState state) {
|
||||
return this->mappings.PushReceive(client, server, size, state);
|
||||
}
|
||||
|
||||
Result PushExchange(KProcessAddress client, KProcessAddress server, size_t size, KMemoryState state) {
|
||||
return this->mappings.PushExchange(client, server, size, state);
|
||||
}
|
||||
|
||||
constexpr KProcessAddress GetSendClientAddress(size_t i) const { return this->mappings.GetSendClientAddress(i); }
|
||||
constexpr KProcessAddress GetSendServerAddress(size_t i) const { return this->mappings.GetSendServerAddress(i); }
|
||||
constexpr size_t GetSendSize(size_t i) const { return this->mappings.GetSendSize(i); }
|
||||
constexpr KMemoryState GetSendMemoryState(size_t i) const { return this->mappings.GetSendMemoryState(i); }
|
||||
|
||||
constexpr KProcessAddress GetReceiveClientAddress(size_t i) const { return this->mappings.GetReceiveClientAddress(i); }
|
||||
constexpr KProcessAddress GetReceiveServerAddress(size_t i) const { return this->mappings.GetReceiveServerAddress(i); }
|
||||
constexpr size_t GetReceiveSize(size_t i) const { return this->mappings.GetReceiveSize(i); }
|
||||
constexpr KMemoryState GetReceiveMemoryState(size_t i) const { return this->mappings.GetReceiveMemoryState(i); }
|
||||
|
||||
constexpr KProcessAddress GetExchangeClientAddress(size_t i) const { return this->mappings.GetExchangeClientAddress(i); }
|
||||
constexpr KProcessAddress GetExchangeServerAddress(size_t i) const { return this->mappings.GetExchangeServerAddress(i); }
|
||||
constexpr size_t GetExchangeSize(size_t i) const { return this->mappings.GetExchangeSize(i); }
|
||||
constexpr KMemoryState GetExchangeMemoryState(size_t i) const { return this->mappings.GetExchangeMemoryState(i); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -17,13 +17,43 @@
|
||||
#include <mesosphere/kern_common.hpp>
|
||||
#include <mesosphere/kern_k_auto_object.hpp>
|
||||
#include <mesosphere/kern_slab_helpers.hpp>
|
||||
#include <mesosphere/kern_select_page_table.hpp>
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
class KProcess;
|
||||
class KResourceLimit;
|
||||
|
||||
class KSharedMemory final : public KAutoObjectWithSlabHeapAndContainer<KSharedMemory, KAutoObjectWithList> {
|
||||
MESOSPHERE_AUTOOBJECT_TRAITS(KSharedMemory, KAutoObject);
|
||||
private:
|
||||
KPageGroup page_group;
|
||||
KResourceLimit *resource_limit;
|
||||
u64 owner_process_id;
|
||||
ams::svc::MemoryPermission owner_perm;
|
||||
ams::svc::MemoryPermission remote_perm;
|
||||
bool is_initialized;
|
||||
public:
|
||||
/* TODO: This is a placeholder definition. */
|
||||
explicit KSharedMemory()
|
||||
: page_group(std::addressof(Kernel::GetBlockInfoManager())), resource_limit(nullptr), owner_process_id(std::numeric_limits<u64>::max()),
|
||||
owner_perm(ams::svc::MemoryPermission_None), remote_perm(ams::svc::MemoryPermission_None), is_initialized(false)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
virtual ~KSharedMemory() { /* ... */ }
|
||||
|
||||
Result Initialize(KProcess *owner, size_t size, ams::svc::MemoryPermission own_perm, ams::svc::MemoryPermission rem_perm);
|
||||
virtual void Finalize() override;
|
||||
|
||||
virtual bool IsInitialized() const override { return this->is_initialized; }
|
||||
static void PostDestroy(uintptr_t arg) { /* ... */ }
|
||||
|
||||
Result Map(KProcessPageTable *table, KProcessAddress address, size_t size, KProcess *process, ams::svc::MemoryPermission map_perm);
|
||||
Result Unmap(KProcessPageTable *table, KProcessAddress address, size_t size, KProcess *process);
|
||||
|
||||
u64 GetOwnerProcessId() const { return this->owner_process_id; }
|
||||
size_t GetSize() const { return this->page_group.GetNumPages() * PageSize; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,21 @@
|
||||
#include <mesosphere/kern_common.hpp>
|
||||
#include <mesosphere/kern_k_typed_address.hpp>
|
||||
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
|
||||
#include <mesosphere/arch/arm64/kern_k_slab_heap_impl.hpp>
|
||||
namespace ams::kern {
|
||||
using ams::kern::arch::arm64::AllocateFromSlabAtomic;
|
||||
using ams::kern::arch::arm64::FreeToSlabAtomic;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#error "Unknown architecture for KSlabHeapImpl"
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
namespace ams::kern {
|
||||
|
||||
namespace impl {
|
||||
@@ -29,8 +44,8 @@ namespace ams::kern {
|
||||
Node *next;
|
||||
};
|
||||
private:
|
||||
std::atomic<Node *> head;
|
||||
size_t obj_size;
|
||||
Node * head;
|
||||
size_t obj_size;
|
||||
public:
|
||||
constexpr KSlabHeapImpl() : head(nullptr), obj_size(0) { MESOSPHERE_ASSERT_THIS(); }
|
||||
|
||||
@@ -50,15 +65,7 @@ namespace ams::kern {
|
||||
void *Allocate() {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
|
||||
Node *ret = this->head.load();
|
||||
|
||||
do {
|
||||
if (AMS_UNLIKELY(ret == nullptr)) {
|
||||
break;
|
||||
}
|
||||
} while (!this->head.compare_exchange_weak(ret, ret->next));
|
||||
|
||||
return ret;
|
||||
return AllocateFromSlabAtomic(std::addressof(this->head));
|
||||
}
|
||||
|
||||
void Free(void *obj) {
|
||||
@@ -66,10 +73,7 @@ namespace ams::kern {
|
||||
|
||||
Node *node = reinterpret_cast<Node *>(obj);
|
||||
|
||||
Node *cur_head = this->head.load();
|
||||
do {
|
||||
node->next = cur_head;
|
||||
} while (!this->head.compare_exchange_weak(cur_head, node));
|
||||
return FreeToSlabAtomic(std::addressof(this->head), node);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -42,8 +42,8 @@ namespace ams::kern {
|
||||
virtual bool IsSignaled() const = 0;
|
||||
virtual void DebugWaiters();
|
||||
|
||||
iterator AddWaiterThread(KThread *thread);
|
||||
iterator RemoveWaiterThread(iterator it);
|
||||
iterator RegisterWaitingThread(KThread *thread);
|
||||
iterator UnregisterWaitingThread(iterator it);
|
||||
|
||||
iterator begin();
|
||||
iterator end();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user