Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc24a33600 | ||
|
|
9bb5af9823 | ||
|
|
82eab9c8d0 | ||
|
|
3cca3801ca | ||
|
|
03408f404a | ||
|
|
92e7a3ca08 | ||
|
|
b27c7552d2 | ||
|
|
426257d4ae | ||
|
|
7d34d599bb | ||
|
|
067fe2d10f | ||
|
|
4759c2f92c | ||
|
|
ca26d8ce27 | ||
|
|
6c52cc3e26 | ||
|
|
e42d3a3abf | ||
|
|
884844bc23 | ||
|
|
f556db8c89 | ||
|
|
96d15b28c6 | ||
|
|
37f7afb426 | ||
|
|
7dd4e76c1d | ||
|
|
daa0deb1bf | ||
|
|
43bd733f0a | ||
|
|
70367e3e7c | ||
|
|
45f8343659 | ||
|
|
e8dfe04701 | ||
|
|
95d5375158 | ||
|
|
b735bc53c4 | ||
|
|
b4856a2d07 | ||
|
|
ed4491a24f | ||
|
|
93004be59e | ||
|
|
237b513408 | ||
|
|
4c5e980e07 | ||
|
|
08d9de6907 | ||
|
|
6eee3f5fe7 | ||
|
|
4eb3109c93 | ||
|
|
f7fb689412 | ||
|
|
2181adb82b | ||
|
|
40c6733de3 | ||
|
|
c703be86fc | ||
|
|
f3732c72dc | ||
|
|
c7026b9094 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -64,6 +64,7 @@ dkms.conf
|
|||||||
# Distribution files
|
# Distribution files
|
||||||
*.tgz
|
*.tgz
|
||||||
*.zip
|
*.zip
|
||||||
|
*.bz2
|
||||||
|
|
||||||
# IDA binaries
|
# IDA binaries
|
||||||
*.id0
|
*.id0
|
||||||
|
|||||||
@@ -18,3 +18,5 @@ enable_user_pmu_access = 0
|
|||||||
[stratosphere]
|
[stratosphere]
|
||||||
; To force-enable nogc, add nogc = 1
|
; To force-enable nogc, add nogc = 1
|
||||||
; To force-disable nogc, add nogc = 0
|
; To force-disable nogc, add nogc = 0
|
||||||
|
|
||||||
|
; To opt in to using Atmosphere's NCM reimplementation, add enable_ncm = 1
|
||||||
|
|||||||
@@ -1,4 +1,38 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
## 0.10.5
|
||||||
|
+ Changes were made to the way fs.mitm builds images when providing a layeredfs romfs.
|
||||||
|
+ Building romfs metadata previously had a memory cost of about ~4-5x the file table size.
|
||||||
|
+ This caused games that have particularly enormous file metadata tables (> 4 MB) to exhaust fs.mitm's 16 MB memory pool.
|
||||||
|
+ The code that creates romfs images was thus changed to be significantly more memory efficient, again.
|
||||||
|
+ Memory requirements have been lowered from ~4x file table size to ~2x file table size + 0.5 MB.
|
||||||
|
+ There is a slight speed penalty to this, but testing on Football Manager 2020 only took an extra ~1.5 seconds for the game to boot with many modded files.
|
||||||
|
+ This shouldn't be noticeable thanks to the async changes made in 0.10.2.
|
||||||
|
+ If you encounter a game that exhausts ams.mitm's memory (crashing it) when loading layeredfs mods, please contact SciresM.
|
||||||
|
+ Romfs building can be made even more memory efficient, but unless games show up with even more absurdly huge file tables it seems not worth the speed trade-off.
|
||||||
|
+ A bug was fixed that caused Atmosphere's fatal error context to not dump TLS for certain processes.
|
||||||
|
+ General system stability improvements to enhance the user's experience.
|
||||||
|
## 0.10.4
|
||||||
|
+ With major thanks to @Adubbz for his work, the NCM system module has now been re-implemented.
|
||||||
|
+ This was a major stepping stone towards the goal of having implementations everything in the Switch's package1/package2 firmware.
|
||||||
|
+ This also lays the groundwork for libstratosphere providing bindings for changing the installed version of the Switch's OS.
|
||||||
|
+ **Please Note**: The NCM implementation will initially be opt-in.
|
||||||
|
+ The Atmosphere team is confident in our NCM implementation (and we have tested it on every firmware version).
|
||||||
|
+ That said, this is our first system module that manages NAND savegames -- and caution is a habit.
|
||||||
|
+ We do not anticipate any issues that didn't come up in testing, so this is just our being particularly careful.
|
||||||
|
+ Users interested in opting in to using our implementation should set `stratosphere!ncm_enabled = 1` in BCT.ini.
|
||||||
|
+ In the unlikely event that any issues are encountered, please report them to @SciresM.
|
||||||
|
+ The NCM implementation will stop being opt-in in a future update, after thorough testing has been done in practice.
|
||||||
|
+ A bug was fixed in emummc that caused Nintendo path to be corrupted on 1.0.0.
|
||||||
|
+ This manifested as the emummc folder being created inside the virtual NAND instead of the SD card.
|
||||||
|
+ It's unlikely there are any negative consequences to this in practice.
|
||||||
|
+ If you want to be truly sure, you can re-clone sysmmc before updating a 1.0.0 emummc to latest firmware.
|
||||||
|
+ Stratosphere system modules now use new Nintendo-style FS bindings instead of stdio.
|
||||||
|
+ This saves a modest amount of memory due to leaner code, and greatly increases the accuracy of several components.
|
||||||
|
+ These bindings will make it easier for other system modules using libstratosphere to interact with the filesystem.
|
||||||
|
+ This also lays the groundwork for changes necessary to support per-emummc Atmosphere folders in a future update.
|
||||||
|
+ Atmosphere's fatal error context now dumps 0x100 of TLS.
|
||||||
|
+ This will make it much easier to fix bugs when an error report is dumped for whatever caused the crash.
|
||||||
|
+ General system stability improvements to enhance the user's experience.
|
||||||
## 0.10.3
|
## 0.10.3
|
||||||
+ Support was added for 9.2.0.
|
+ Support was added for 9.2.0.
|
||||||
+ Support was added for redirecting manual html content for games.
|
+ Support was added for redirecting manual html content for games.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
[subrepo]
|
[subrepo]
|
||||||
remote = https://github.com/m4xw/emuMMC
|
remote = https://github.com/m4xw/emuMMC
|
||||||
branch = develop
|
branch = develop
|
||||||
commit = bd81a674a946c30b566e1732a95c18f19b701558
|
commit = d12dd5464422029a1e5601916517ec3f1c81d8d0
|
||||||
parent = 6ee525201ccef107c61d81ba73c891e3eb5f0215
|
parent = 259a1a7513236a1de4d373bc6cb99032ede2c626
|
||||||
method = rebase
|
method = rebase
|
||||||
cmdver = 0.4.0
|
cmdver = 0.4.1
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
# emuMMC
|
# emuMMC
|
||||||
*A SDMMC driver replacement for Nintendo's Filesystem Services, by **m4xw***
|
*A SDMMC driver replacement for Nintendo's Filesystem Services, by **m4xw***
|
||||||
|
|
||||||
### Supported Horizon Versions
|
### Supported Horizon Versions
|
||||||
**1.0.0 - 9.1.0**
|
**1.0.0 - 9.1.0**
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
* Arbitrary SDMMC backend selection
|
* Arbitrary SDMMC backend selection
|
||||||
**This allows loading eMMC from SD or even SD from eMMC**
|
**This allows loading eMMC from SD or even SD from eMMC**
|
||||||
* On the fly hooking / patching, fully self-infesting
|
* On the fly hooking / patching, fully self-infesting
|
||||||
**Only one payload required for all versions!**
|
**Only one payload required for all versions!**
|
||||||
* File-based SDMMC backend support (from SD)
|
* File-based SDMMC backend support (from SD)
|
||||||
**This allows loading eMMC images from hekate-backups (split or not)**
|
**This allows loading eMMC images from hekate-backups (split or not)**
|
||||||
* SDMMC device based sector offset (*currently eMMC only*)
|
* SDMMC device based sector offset (*currently eMMC only*)
|
||||||
**Raw partition support for eMMC from SD with less performance overhead**
|
**Raw partition support for eMMC from SD with less performance overhead**
|
||||||
* Full support for `/Nintendo` folder redirection to a arbitrary path
|
* Full support for `/Nintendo` folder redirection to a arbitrary path
|
||||||
**No 8 char length restriction!**
|
**No 8 char length restriction!**
|
||||||
* exosphere based context configuration
|
* exosphere based context configuration
|
||||||
**This includes full support for multiple emuMMC images**
|
**This includes full support for multiple emuMMC images**
|
||||||
|
|
||||||
## Compiling
|
## Compiling
|
||||||
|
|||||||
@@ -48,10 +48,8 @@
|
|||||||
// Nintendo Paths
|
// Nintendo Paths
|
||||||
#define FS_OFFSET_100_NINTENDO_PATHS \
|
#define FS_OFFSET_100_NINTENDO_PATHS \
|
||||||
{ \
|
{ \
|
||||||
{.opcode_reg = 9, .adrp_offset = 0x00032C58, .add_rel_offset = 4}, \
|
{.opcode_reg = 8, .adrp_offset = 0x00032C58, .add_rel_offset = 8}, \
|
||||||
{.opcode_reg = 8, .adrp_offset = 0x00032C60, .add_rel_offset = 4}, \
|
{.opcode_reg = 9, .adrp_offset = 0x00032F40, .add_rel_offset = 8}, \
|
||||||
{.opcode_reg = 9, .adrp_offset = 0x00032F3C, .add_rel_offset = 4}, \
|
|
||||||
{.opcode_reg = 8, .adrp_offset = 0x00032F44, .add_rel_offset = 4}, \
|
|
||||||
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
|
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ extern char __argdata__;
|
|||||||
// TODO
|
// TODO
|
||||||
static char nintendo_path[0x80] = "Nintendo";
|
static char nintendo_path[0x80] = "Nintendo";
|
||||||
|
|
||||||
|
// 1.0.0 requires special path handling because it has separate album and contents paths.
|
||||||
|
#define FS_100_ALBUM_PATH 0
|
||||||
|
#define FS_100_CONTENTS_PATH 1
|
||||||
static char nintendo_path_album_100[0x100] = "/Nintendo/Album";
|
static char nintendo_path_album_100[0x100] = "/Nintendo/Album";
|
||||||
static char nintendo_path_contents_100[0x100] = "/Nintendo/Contents";
|
static char nintendo_path_contents_100[0x100] = "/Nintendo/Contents";
|
||||||
|
|
||||||
@@ -275,23 +278,18 @@ void setup_nintendo_paths(void)
|
|||||||
// 1.0.0 needs special handling because it uses two paths.
|
// 1.0.0 needs special handling because it uses two paths.
|
||||||
// Do album path
|
// Do album path
|
||||||
{
|
{
|
||||||
int path_len = snprintf(nintendo_path_album_100, sizeof(nintendo_path_album_100), "/%s/Album", nintendo_path);
|
snprintf(nintendo_path_album_100, sizeof(nintendo_path_album_100), "/%s/Album", nintendo_path);
|
||||||
intptr_t nintendo_album_path_location = (intptr_t)&nintendo_path_album_100;
|
intptr_t nintendo_album_path_location = (intptr_t)&nintendo_path_album_100;
|
||||||
intptr_t album_path_location = nintendo_album_path_location + path_len - 6; // "/Album"
|
uintptr_t fs_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[FS_100_ALBUM_PATH].adrp_offset);
|
||||||
uintptr_t fs_n_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[0].adrp_offset);
|
write_adrp_add(fs_offsets->nintendo_paths[FS_100_ALBUM_PATH].opcode_reg, fs_adrp_opcode_location, fs_offsets->nintendo_paths[FS_100_ALBUM_PATH].add_rel_offset, nintendo_album_path_location);
|
||||||
uintptr_t fs_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[1].adrp_offset);
|
|
||||||
write_adrp_add(fs_offsets->nintendo_paths[0].opcode_reg, fs_n_adrp_opcode_location, fs_offsets->nintendo_paths[0].add_rel_offset, nintendo_album_path_location);
|
|
||||||
write_adrp_add(fs_offsets->nintendo_paths[1].opcode_reg, fs_adrp_opcode_location, fs_offsets->nintendo_paths[1].add_rel_offset, album_path_location);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do contents path
|
// Do contents path
|
||||||
{
|
{
|
||||||
int path_len = snprintf(nintendo_path_contents_100, sizeof(nintendo_path_contents_100), "/%s/Contents", nintendo_path);
|
snprintf(nintendo_path_contents_100, sizeof(nintendo_path_contents_100), "/%s/Contents", nintendo_path);
|
||||||
intptr_t nintendo_contents_path_location = (intptr_t)&nintendo_path_contents_100;
|
intptr_t nintendo_contents_path_location = (intptr_t)&nintendo_path_contents_100;
|
||||||
intptr_t contents_path_location = nintendo_contents_path_location + path_len - 9; // "/Contents"
|
uintptr_t fs_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[FS_100_CONTENTS_PATH].adrp_offset);
|
||||||
uintptr_t fs_n_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[2].adrp_offset);
|
write_adrp_add(fs_offsets->nintendo_paths[FS_100_CONTENTS_PATH].opcode_reg, fs_adrp_opcode_location, fs_offsets->nintendo_paths[FS_100_CONTENTS_PATH].add_rel_offset, nintendo_contents_path_location);
|
||||||
uintptr_t fs_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[3].adrp_offset);
|
|
||||||
write_adrp_add(fs_offsets->nintendo_paths[2].opcode_reg, fs_n_adrp_opcode_location, fs_offsets->nintendo_paths[2].add_rel_offset, nintendo_contents_path_location);
|
|
||||||
write_adrp_add(fs_offsets->nintendo_paths[3].opcode_reg, fs_adrp_opcode_location, fs_offsets->nintendo_paths[3].add_rel_offset, contents_path_location);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,12 +21,12 @@
|
|||||||
|
|
||||||
static inline void uart_wait_cycles(uint32_t baud, uint32_t num)
|
static inline void uart_wait_cycles(uint32_t baud, uint32_t num)
|
||||||
{
|
{
|
||||||
wait((num * 1000000 + 16 * baud - 1) / (16 * baud));
|
udelay((num * 1000000 + 16 * baud - 1) / (16 * baud));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void uart_wait_syms(uint32_t baud, uint32_t num)
|
static inline void uart_wait_syms(uint32_t baud, uint32_t num)
|
||||||
{
|
{
|
||||||
wait((num * 1000000 + baud - 1) / baud);
|
udelay((num * 1000000 + baud - 1) / baud);
|
||||||
}
|
}
|
||||||
|
|
||||||
void uart_config(UartDevice dev) {
|
void uart_config(UartDevice dev) {
|
||||||
@@ -34,28 +34,28 @@ void uart_config(UartDevice dev) {
|
|||||||
|
|
||||||
switch (dev) {
|
switch (dev) {
|
||||||
case UART_A:
|
case UART_A:
|
||||||
pinmux->uart1_tx = 0;
|
pinmux->uart1_tx = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||||
pinmux->uart1_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
|
pinmux->uart1_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_UP | PINMUX_SELECT_FUNCTION0);
|
||||||
pinmux->uart1_rts = 0;
|
pinmux->uart1_rts = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||||
pinmux->uart1_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
|
pinmux->uart1_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||||
break;
|
break;
|
||||||
case UART_B:
|
case UART_B:
|
||||||
pinmux->uart2_tx = 0;
|
pinmux->uart2_tx = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||||
pinmux->uart2_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
|
pinmux->uart2_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||||
pinmux->uart2_rts = 0;
|
pinmux->uart2_rts = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||||
pinmux->uart2_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
|
pinmux->uart2_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||||
break;
|
break;
|
||||||
case UART_C:
|
case UART_C:
|
||||||
pinmux->uart3_tx = 0;
|
pinmux->uart3_tx = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||||
pinmux->uart3_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
|
pinmux->uart3_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||||
pinmux->uart3_rts = 0;
|
pinmux->uart3_rts = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||||
pinmux->uart3_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
|
pinmux->uart3_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||||
break;
|
break;
|
||||||
case UART_D:
|
case UART_D:
|
||||||
pinmux->uart4_tx = 0;
|
pinmux->uart4_tx = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||||
pinmux->uart4_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
|
pinmux->uart4_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||||
pinmux->uart4_rts = 0;
|
pinmux->uart4_rts = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||||
pinmux->uart4_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
|
pinmux->uart4_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||||
break;
|
break;
|
||||||
case UART_E:
|
case UART_E:
|
||||||
/* Unused. */
|
/* Unused. */
|
||||||
|
|||||||
@@ -21,66 +21,58 @@
|
|||||||
#include "gpio.h"
|
#include "gpio.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
static volatile tegra_gpio_bank_t *gpio_get_bank(uint32_t pin) {
|
/* Set GPIO's value. */
|
||||||
|
static void gpio_register_set(uint32_t pin, bool do_set, uint32_t offset) {
|
||||||
volatile tegra_gpio_t *gpio = gpio_get_regs();
|
volatile tegra_gpio_t *gpio = gpio_get_regs();
|
||||||
uint32_t bank_number = (pin >> GPIO_BANK_SHIFT);
|
|
||||||
|
|
||||||
return &gpio->bank[bank_number];
|
|
||||||
}
|
|
||||||
|
|
||||||
static volatile uint32_t gpio_get_port(uint32_t pin) {
|
|
||||||
return ((pin >> GPIO_PORT_SHIFT) & GPIO_PORT_MASK);
|
|
||||||
}
|
|
||||||
|
|
||||||
static volatile uint32_t gpio_get_mask(uint32_t pin) {
|
|
||||||
uint32_t pin_number = (pin & GPIO_PIN_MASK);
|
|
||||||
return (1 << pin_number);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gpio_simple_register_set(uint32_t pin, bool should_be_set, uint32_t offset) {
|
|
||||||
/* Retrieve the register set that corresponds to the given pin and offset. */
|
/* Retrieve the register set that corresponds to the given pin and offset. */
|
||||||
uintptr_t cluster_addr = (uintptr_t)gpio_get_bank(pin) + offset;
|
volatile uint32_t *cluster = (uint32_t *)((uintptr_t)&gpio->bank[(pin >> GPIO_BANK_SHIFT)] + offset);
|
||||||
uint32_t *cluster = (uint32_t *)cluster_addr;
|
|
||||||
|
|
||||||
/* Figure out the offset into the cluster, and the mask to be used. */
|
/* Figure out the offset into the cluster, and the mask to be used. */
|
||||||
uint32_t port = gpio_get_port(pin);
|
uint32_t port = ((pin >> GPIO_PORT_SHIFT) & GPIO_PORT_MASK);
|
||||||
uint32_t mask = gpio_get_mask(pin);
|
uint32_t mask = (1 << (pin & GPIO_PIN_MASK));
|
||||||
|
|
||||||
/* Set or clear the bit, as appropriate. */
|
/* Set or clear the bit, as appropriate. */
|
||||||
if (should_be_set)
|
if (do_set)
|
||||||
cluster[port] |= mask;
|
cluster[port] |= mask;
|
||||||
else
|
else
|
||||||
cluster[port] &= ~mask;
|
cluster[port] &= ~mask;
|
||||||
|
|
||||||
/* Dummy read. */
|
/* Dummy read. */
|
||||||
(void)cluster[port];
|
cluster[port];
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool gpio_simple_register_get(uint32_t pin, uint32_t offset) {
|
/* Get GPIO's value. */
|
||||||
|
static bool gpio_register_get(uint32_t pin, uint32_t offset) {
|
||||||
|
volatile tegra_gpio_t *gpio = gpio_get_regs();
|
||||||
|
|
||||||
/* Retrieve the register set that corresponds to the given pin and offset. */
|
/* Retrieve the register set that corresponds to the given pin and offset. */
|
||||||
uintptr_t cluster_addr = (uintptr_t)gpio_get_bank(pin) + offset;
|
volatile uint32_t *cluster = (uint32_t *)((uintptr_t)&gpio->bank[(pin >> GPIO_BANK_SHIFT)] + offset);
|
||||||
uint32_t *cluster = (uint32_t *)cluster_addr;
|
|
||||||
|
|
||||||
/* Figure out the offset into the cluster, and the mask to be used. */
|
/* Figure out the offset into the cluster, and the mask to be used. */
|
||||||
uint32_t port = gpio_get_port(pin);
|
uint32_t port = ((pin >> GPIO_PORT_SHIFT) & GPIO_PORT_MASK);
|
||||||
uint32_t mask = gpio_get_mask(pin);
|
uint32_t mask = (1 << (pin & GPIO_PIN_MASK));
|
||||||
|
|
||||||
/* Convert the given value to a boolean. */
|
/* Convert the given value to a boolean. */
|
||||||
return !!(cluster[port] & mask);
|
return !!(cluster[port] & mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Configure GPIO's mode. */
|
||||||
void gpio_configure_mode(uint32_t pin, uint32_t mode) {
|
void gpio_configure_mode(uint32_t pin, uint32_t mode) {
|
||||||
gpio_simple_register_set(pin, mode == GPIO_MODE_GPIO, offsetof(tegra_gpio_bank_t, config));
|
gpio_register_set(pin, mode == GPIO_MODE_GPIO, offsetof(tegra_gpio_bank_t, config));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Configure GPIO's direction. */
|
||||||
void gpio_configure_direction(uint32_t pin, uint32_t dir) {
|
void gpio_configure_direction(uint32_t pin, uint32_t dir) {
|
||||||
gpio_simple_register_set(pin, dir == GPIO_DIRECTION_OUTPUT, offsetof(tegra_gpio_bank_t, direction));
|
gpio_register_set(pin, dir == GPIO_DIRECTION_OUTPUT, offsetof(tegra_gpio_bank_t, direction));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Write to GPIO. */
|
||||||
void gpio_write(uint32_t pin, uint32_t value) {
|
void gpio_write(uint32_t pin, uint32_t value) {
|
||||||
gpio_simple_register_set(pin, value == GPIO_LEVEL_HIGH, offsetof(tegra_gpio_bank_t, out));
|
gpio_register_set(pin, value == GPIO_LEVEL_HIGH, offsetof(tegra_gpio_bank_t, out));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Read from GPIO. */
|
||||||
uint32_t gpio_read(uint32_t pin) {
|
uint32_t gpio_read(uint32_t pin) {
|
||||||
return gpio_simple_register_get(pin, offsetof(tegra_gpio_bank_t, in));
|
return gpio_register_get(pin, offsetof(tegra_gpio_bank_t, in));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "panic.h"
|
#include "panic.h"
|
||||||
#include "di.h"
|
#include "di.h"
|
||||||
@@ -51,8 +51,10 @@ static const char *get_error_desc_str(uint32_t error_desc) {
|
|||||||
|
|
||||||
static void _check_and_display_atmosphere_fatal_error(void) {
|
static void _check_and_display_atmosphere_fatal_error(void) {
|
||||||
/* Check for valid magic. */
|
/* Check for valid magic. */
|
||||||
if (ATMOSPHERE_FATAL_ERROR_CONTEXT->magic != ATMOSPHERE_REBOOT_TO_FATAL_MAGIC &&
|
if (ATMOSPHERE_FATAL_ERROR_CONTEXT->magic != ATMOSPHERE_REBOOT_TO_FATAL_MAGIC &&
|
||||||
ATMOSPHERE_FATAL_ERROR_CONTEXT->magic != ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_0) {
|
ATMOSPHERE_FATAL_ERROR_CONTEXT->magic != ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_1 &&
|
||||||
|
ATMOSPHERE_FATAL_ERROR_CONTEXT->magic != ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_0)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,10 +71,10 @@ static void _check_and_display_atmosphere_fatal_error(void) {
|
|||||||
/* Turn on the backlight after initializing the lfb */
|
/* Turn on the backlight after initializing the lfb */
|
||||||
/* to avoid flickering. */
|
/* to avoid flickering. */
|
||||||
display_backlight(true);
|
display_backlight(true);
|
||||||
|
|
||||||
/* Override the global logging level. */
|
/* Override the global logging level. */
|
||||||
log_set_log_level(SCREEN_LOG_LEVEL_ERROR);
|
log_set_log_level(SCREEN_LOG_LEVEL_ERROR);
|
||||||
|
|
||||||
/* Copy fatal error context to the stack. */
|
/* Copy fatal error context to the stack. */
|
||||||
atmosphere_fatal_error_ctx ctx = *(ATMOSPHERE_FATAL_ERROR_CONTEXT);
|
atmosphere_fatal_error_ctx ctx = *(ATMOSPHERE_FATAL_ERROR_CONTEXT);
|
||||||
|
|
||||||
@@ -103,7 +105,7 @@ static void _check_and_display_atmosphere_fatal_error(void) {
|
|||||||
void check_and_display_panic(void) {
|
void check_and_display_panic(void) {
|
||||||
/* Handle a panic sent via a stratosphere module. */
|
/* Handle a panic sent via a stratosphere module. */
|
||||||
_check_and_display_atmosphere_fatal_error();
|
_check_and_display_atmosphere_fatal_error();
|
||||||
|
|
||||||
/* We also handle our own panics. */
|
/* We also handle our own panics. */
|
||||||
bool has_panic = ((APBDEV_PMC_RST_STATUS_0 != 0) || (g_panic_code != 0));
|
bool has_panic = ((APBDEV_PMC_RST_STATUS_0 != 0) || (g_panic_code != 0));
|
||||||
uint32_t code = (g_panic_code == 0) ? APBDEV_PMC_SCRATCH200_0 : g_panic_code;
|
uint32_t code = (g_panic_code == 0) ? APBDEV_PMC_SCRATCH200_0 : g_panic_code;
|
||||||
@@ -152,10 +154,10 @@ void check_and_display_panic(void) {
|
|||||||
|
|
||||||
/* Initialize the display. */
|
/* Initialize the display. */
|
||||||
display_init();
|
display_init();
|
||||||
|
|
||||||
/* Fill the screen. */
|
/* Fill the screen. */
|
||||||
display_color_screen(color);
|
display_color_screen(color);
|
||||||
|
|
||||||
/* Wait for button and reboot. */
|
/* Wait for button and reboot. */
|
||||||
wait_for_button_and_reboot();
|
wait_for_button_and_reboot();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef FUSEE_PANIC_H
|
#ifndef FUSEE_PANIC_H
|
||||||
#define FUSEE_PANIC_H
|
#define FUSEE_PANIC_H
|
||||||
|
|
||||||
@@ -30,6 +30,7 @@
|
|||||||
|
|
||||||
#define AMS_FATAL_ERROR_MAX_STACKTRACE 0x20
|
#define AMS_FATAL_ERROR_MAX_STACKTRACE 0x20
|
||||||
#define AMS_FATAL_ERROR_MAX_STACKDUMP 0x100
|
#define AMS_FATAL_ERROR_MAX_STACKDUMP 0x100
|
||||||
|
#define AMS_FATAL_ERROR_TLS_SIZE 0x100
|
||||||
|
|
||||||
/* Atmosphere reboot-to-fatal-error. */
|
/* Atmosphere reboot-to-fatal-error. */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -57,10 +58,13 @@ typedef struct {
|
|||||||
uint64_t stack_dump_size;
|
uint64_t stack_dump_size;
|
||||||
uint64_t stack_trace[AMS_FATAL_ERROR_MAX_STACKTRACE];
|
uint64_t stack_trace[AMS_FATAL_ERROR_MAX_STACKTRACE];
|
||||||
uint8_t stack_dump[AMS_FATAL_ERROR_MAX_STACKDUMP];
|
uint8_t stack_dump[AMS_FATAL_ERROR_MAX_STACKDUMP];
|
||||||
|
uint8_t tls[AMS_FATAL_ERROR_TLS_SIZE];
|
||||||
} atmosphere_fatal_error_ctx;
|
} atmosphere_fatal_error_ctx;
|
||||||
|
|
||||||
|
/* "AFE2" */
|
||||||
|
#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC 0x32454641
|
||||||
/* "AFE1" */
|
/* "AFE1" */
|
||||||
#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC 0x31454641
|
#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_1 0x31454641
|
||||||
/* "AFE0" */
|
/* "AFE0" */
|
||||||
#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_0 0x30454641
|
#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC_0 0x30454641
|
||||||
|
|
||||||
|
|||||||
@@ -34,28 +34,28 @@ void uart_config(UartDevice dev) {
|
|||||||
|
|
||||||
switch (dev) {
|
switch (dev) {
|
||||||
case UART_A:
|
case UART_A:
|
||||||
pinmux->uart1_tx = 0;
|
pinmux->uart1_tx = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||||
pinmux->uart1_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
|
pinmux->uart1_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_UP | PINMUX_SELECT_FUNCTION0);
|
||||||
pinmux->uart1_rts = 0;
|
pinmux->uart1_rts = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||||
pinmux->uart1_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
|
pinmux->uart1_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||||
break;
|
break;
|
||||||
case UART_B:
|
case UART_B:
|
||||||
pinmux->uart2_tx = 0;
|
pinmux->uart2_tx = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||||
pinmux->uart2_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
|
pinmux->uart2_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||||
pinmux->uart2_rts = 0;
|
pinmux->uart2_rts = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||||
pinmux->uart2_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
|
pinmux->uart2_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||||
break;
|
break;
|
||||||
case UART_C:
|
case UART_C:
|
||||||
pinmux->uart3_tx = 0;
|
pinmux->uart3_tx = (0 | 0 | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||||
pinmux->uart3_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
|
pinmux->uart3_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||||
pinmux->uart3_rts = 0;
|
pinmux->uart3_rts = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||||
pinmux->uart3_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
|
pinmux->uart3_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_NONE | PINMUX_SELECT_FUNCTION0);
|
||||||
break;
|
break;
|
||||||
case UART_D:
|
case UART_D:
|
||||||
pinmux->uart4_tx = 0;
|
pinmux->uart4_tx = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||||
pinmux->uart4_rx = (PINMUX_INPUT | PINMUX_PULL_UP);
|
pinmux->uart4_rx = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||||
pinmux->uart4_rts = 0;
|
pinmux->uart4_rts = (0 | 0 | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||||
pinmux->uart4_cts = (PINMUX_INPUT | PINMUX_PULL_DOWN);
|
pinmux->uart4_cts = (PINMUX_INPUT | PINMUX_TRISTATE | PINMUX_PULL_DOWN | PINMUX_SELECT_FUNCTION0);
|
||||||
break;
|
break;
|
||||||
case UART_E:
|
case UART_E:
|
||||||
/* Unused. */
|
/* Unused. */
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ ifneq ($(BUILD),$(notdir $(CURDIR)))
|
|||||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||||
export TOPDIR := $(CURDIR)
|
export TOPDIR := $(CURDIR)
|
||||||
|
|
||||||
export KIPDIRS := $(AMS)/stratosphere/loader $(AMS)/stratosphere/pm $(AMS)/stratosphere/sm $(AMS)/stratosphere/boot $(AMS)/stratosphere/spl $(AMS)/stratosphere/ams_mitm
|
export KIPDIRS := $(AMS)/stratosphere/loader $(AMS)/stratosphere/ncm $(AMS)/stratosphere/pm $(AMS)/stratosphere/sm $(AMS)/stratosphere/boot $(AMS)/stratosphere/spl $(AMS)/stratosphere/ams_mitm
|
||||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir)) \
|
$(foreach dir,$(DATA),$(CURDIR)/$(dir)) \
|
||||||
$(AMS)/exosphere $(AMS)/exosphere/lp0fw $(AMS)/exosphere/rebootstub \
|
$(AMS)/exosphere $(AMS)/exosphere/lp0fw $(AMS)/exosphere/rebootstub \
|
||||||
@@ -96,7 +96,7 @@ export DEPSDIR := $(CURDIR)/$(BUILD)
|
|||||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||||
KIPFILES := loader.kip pm.kip sm.kip ams_mitm.kip spl.kip boot.kip
|
KIPFILES := loader.kip ncm.kip pm.kip sm.kip ams_mitm.kip spl.kip boot.kip
|
||||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) fusee-primary.bin \
|
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) fusee-primary.bin \
|
||||||
exosphere.bin lp0fw.bin rebootstub.bin thermosphere.bin splash_screen.bmp \
|
exosphere.bin lp0fw.bin rebootstub.bin thermosphere.bin splash_screen.bmp \
|
||||||
sept-primary.bin sept-secondary_00.enc sept-secondary_01.enc emummc.kip \
|
sept-primary.bin sept-secondary_00.enc sept-secondary_01.enc emummc.kip \
|
||||||
@@ -221,12 +221,12 @@ sept_secondary_01.enc.o sept_secondary_01_enc.h: sept-secondary_01.enc
|
|||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
@echo $(notdir $<)
|
@echo $(notdir $<)
|
||||||
@$(_bin2o)
|
@$(_bin2o)
|
||||||
|
|
||||||
sept_secondary_dev_00.enc.o sept_secondary_dev_00_enc.h: sept-secondary_dev_00.enc
|
sept_secondary_dev_00.enc.o sept_secondary_dev_00_enc.h: sept-secondary_dev_00.enc
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
@echo $(notdir $<)
|
@echo $(notdir $<)
|
||||||
@$(_bin2o)
|
@$(_bin2o)
|
||||||
|
|
||||||
sept_secondary_dev_01.enc.o sept_secondary_dev_01_enc.h: sept-secondary_dev_01.enc
|
sept_secondary_dev_01.enc.o sept_secondary_dev_01_enc.h: sept-secondary_dev_01.enc
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
@echo $(notdir $<)
|
@echo $(notdir $<)
|
||||||
|
|||||||
@@ -230,6 +230,8 @@ SECTIONS
|
|||||||
PROVIDE(__loader_kip_size__ = loader_kip_end - loader_kip);
|
PROVIDE(__loader_kip_size__ = loader_kip_end - loader_kip);
|
||||||
PROVIDE(__lp0fw_bin_start__ = lp0fw_bin - __start__);
|
PROVIDE(__lp0fw_bin_start__ = lp0fw_bin - __start__);
|
||||||
PROVIDE(__lp0fw_bin_size__ = lp0fw_bin_end - lp0fw_bin);
|
PROVIDE(__lp0fw_bin_size__ = lp0fw_bin_end - lp0fw_bin);
|
||||||
|
PROVIDE(__ncm_kip_start__ = ncm_kip - __start__);
|
||||||
|
PROVIDE(__ncm_kip_size__ = ncm_kip_end - ncm_kip);
|
||||||
PROVIDE(__pm_kip_start__ = pm_kip - __start__);
|
PROVIDE(__pm_kip_start__ = pm_kip - __start__);
|
||||||
PROVIDE(__pm_kip_size__ = pm_kip_end - pm_kip);
|
PROVIDE(__pm_kip_size__ = pm_kip_end - pm_kip);
|
||||||
PROVIDE(__rebootstub_bin_start__ = rebootstub_bin - __start__);
|
PROVIDE(__rebootstub_bin_start__ = rebootstub_bin - __start__);
|
||||||
|
|||||||
@@ -21,66 +21,58 @@
|
|||||||
#include "gpio.h"
|
#include "gpio.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
static volatile tegra_gpio_bank_t *gpio_get_bank(uint32_t pin) {
|
/* Set GPIO's value. */
|
||||||
|
static void gpio_register_set(uint32_t pin, bool do_set, uint32_t offset) {
|
||||||
volatile tegra_gpio_t *gpio = gpio_get_regs();
|
volatile tegra_gpio_t *gpio = gpio_get_regs();
|
||||||
uint32_t bank_number = (pin >> GPIO_BANK_SHIFT);
|
|
||||||
|
|
||||||
return &gpio->bank[bank_number];
|
|
||||||
}
|
|
||||||
|
|
||||||
static volatile uint32_t gpio_get_port(uint32_t pin) {
|
|
||||||
return ((pin >> GPIO_PORT_SHIFT) & GPIO_PORT_MASK);
|
|
||||||
}
|
|
||||||
|
|
||||||
static volatile uint32_t gpio_get_mask(uint32_t pin) {
|
|
||||||
uint32_t pin_number = (pin & GPIO_PIN_MASK);
|
|
||||||
return (1 << pin_number);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gpio_simple_register_set(uint32_t pin, bool should_be_set, uint32_t offset) {
|
|
||||||
/* Retrieve the register set that corresponds to the given pin and offset. */
|
/* Retrieve the register set that corresponds to the given pin and offset. */
|
||||||
uintptr_t cluster_addr = (uintptr_t)gpio_get_bank(pin) + offset;
|
volatile uint32_t *cluster = (uint32_t *)((uintptr_t)&gpio->bank[(pin >> GPIO_BANK_SHIFT)] + offset);
|
||||||
uint32_t *cluster = (uint32_t *)cluster_addr;
|
|
||||||
|
|
||||||
/* Figure out the offset into the cluster, and the mask to be used. */
|
/* Figure out the offset into the cluster, and the mask to be used. */
|
||||||
uint32_t port = gpio_get_port(pin);
|
uint32_t port = ((pin >> GPIO_PORT_SHIFT) & GPIO_PORT_MASK);
|
||||||
uint32_t mask = gpio_get_mask(pin);
|
uint32_t mask = (1 << (pin & GPIO_PIN_MASK));
|
||||||
|
|
||||||
/* Set or clear the bit, as appropriate. */
|
/* Set or clear the bit, as appropriate. */
|
||||||
if (should_be_set)
|
if (do_set)
|
||||||
cluster[port] |= mask;
|
cluster[port] |= mask;
|
||||||
else
|
else
|
||||||
cluster[port] &= ~mask;
|
cluster[port] &= ~mask;
|
||||||
|
|
||||||
/* Dummy read. */
|
/* Dummy read. */
|
||||||
(void)cluster[port];
|
cluster[port];
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool gpio_simple_register_get(uint32_t pin, uint32_t offset) {
|
/* Get GPIO's value. */
|
||||||
|
static bool gpio_register_get(uint32_t pin, uint32_t offset) {
|
||||||
|
volatile tegra_gpio_t *gpio = gpio_get_regs();
|
||||||
|
|
||||||
/* Retrieve the register set that corresponds to the given pin and offset. */
|
/* Retrieve the register set that corresponds to the given pin and offset. */
|
||||||
uintptr_t cluster_addr = (uintptr_t)gpio_get_bank(pin) + offset;
|
volatile uint32_t *cluster = (uint32_t *)((uintptr_t)&gpio->bank[(pin >> GPIO_BANK_SHIFT)] + offset);
|
||||||
uint32_t *cluster = (uint32_t *)cluster_addr;
|
|
||||||
|
|
||||||
/* Figure out the offset into the cluster, and the mask to be used. */
|
/* Figure out the offset into the cluster, and the mask to be used. */
|
||||||
uint32_t port = gpio_get_port(pin);
|
uint32_t port = ((pin >> GPIO_PORT_SHIFT) & GPIO_PORT_MASK);
|
||||||
uint32_t mask = gpio_get_mask(pin);
|
uint32_t mask = (1 << (pin & GPIO_PIN_MASK));
|
||||||
|
|
||||||
/* Convert the given value to a boolean. */
|
/* Convert the given value to a boolean. */
|
||||||
return !!(cluster[port] & mask);
|
return !!(cluster[port] & mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Configure GPIO's mode. */
|
||||||
void gpio_configure_mode(uint32_t pin, uint32_t mode) {
|
void gpio_configure_mode(uint32_t pin, uint32_t mode) {
|
||||||
gpio_simple_register_set(pin, mode == GPIO_MODE_GPIO, offsetof(tegra_gpio_bank_t, config));
|
gpio_register_set(pin, mode == GPIO_MODE_GPIO, offsetof(tegra_gpio_bank_t, config));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Configure GPIO's direction. */
|
||||||
void gpio_configure_direction(uint32_t pin, uint32_t dir) {
|
void gpio_configure_direction(uint32_t pin, uint32_t dir) {
|
||||||
gpio_simple_register_set(pin, dir == GPIO_DIRECTION_OUTPUT, offsetof(tegra_gpio_bank_t, direction));
|
gpio_register_set(pin, dir == GPIO_DIRECTION_OUTPUT, offsetof(tegra_gpio_bank_t, direction));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Write to GPIO. */
|
||||||
void gpio_write(uint32_t pin, uint32_t value) {
|
void gpio_write(uint32_t pin, uint32_t value) {
|
||||||
gpio_simple_register_set(pin, value == GPIO_LEVEL_HIGH, offsetof(tegra_gpio_bank_t, out));
|
gpio_register_set(pin, value == GPIO_LEVEL_HIGH, offsetof(tegra_gpio_bank_t, out));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Read from GPIO. */
|
||||||
uint32_t gpio_read(uint32_t pin) {
|
uint32_t gpio_read(uint32_t pin) {
|
||||||
return gpio_simple_register_get(pin, offsetof(tegra_gpio_bank_t, in));
|
return gpio_register_get(pin, offsetof(tegra_gpio_bank_t, in));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -184,6 +184,12 @@ static int stratosphere_ini_handler(void *user, const char *section, const char
|
|||||||
strat_cfg->has_nogc_config = true;
|
strat_cfg->has_nogc_config = true;
|
||||||
sscanf(value, "%d", &tmp);
|
sscanf(value, "%d", &tmp);
|
||||||
strat_cfg->enable_nogc = tmp != 0;
|
strat_cfg->enable_nogc = tmp != 0;
|
||||||
|
} else if (strcmp(name, STRATOSPHERE_ENABLE_NCM_KEY) == 0) {
|
||||||
|
sscanf(value, "%d", &tmp);
|
||||||
|
strat_cfg->ncm_enabled = tmp != 0;
|
||||||
|
if (strat_cfg->ncm_enabled) {
|
||||||
|
stratosphere_enable_ncm();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,11 +97,18 @@ _metadata:
|
|||||||
#define CONTENT_TYPE_KLD 9
|
#define CONTENT_TYPE_KLD 9
|
||||||
#define CONTENT_TYPE_KRN 10
|
#define CONTENT_TYPE_KRN 10
|
||||||
|
|
||||||
|
#define CONTENT_FLAG_NONE (0 << 0)
|
||||||
|
|
||||||
|
#define CONTENT_FLAG0_EXPERIMENTAL (1 << 0)
|
||||||
|
|
||||||
_content_headers:
|
_content_headers:
|
||||||
/* ams_mitm content header */
|
/* ams_mitm content header */
|
||||||
.word __ams_mitm_kip_start__
|
.word __ams_mitm_kip_start__
|
||||||
.word __ams_mitm_kip_size__
|
.word __ams_mitm_kip_size__
|
||||||
.word CONTENT_TYPE_KIP
|
.byte CONTENT_TYPE_KIP
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
.word 0xCCCCCCCC
|
.word 0xCCCCCCCC
|
||||||
.asciz "ams_mitm"
|
.asciz "ams_mitm"
|
||||||
.align 5
|
.align 5
|
||||||
@@ -109,7 +116,10 @@ _content_headers:
|
|||||||
/* boot content header */
|
/* boot content header */
|
||||||
.word __boot_kip_start__
|
.word __boot_kip_start__
|
||||||
.word __boot_kip_size__
|
.word __boot_kip_size__
|
||||||
.word CONTENT_TYPE_KIP
|
.byte CONTENT_TYPE_KIP
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
.word 0xCCCCCCCC
|
.word 0xCCCCCCCC
|
||||||
.asciz "boot"
|
.asciz "boot"
|
||||||
.align 5
|
.align 5
|
||||||
@@ -117,7 +127,10 @@ _content_headers:
|
|||||||
/* exosphere content header */
|
/* exosphere content header */
|
||||||
.word __exosphere_bin_start__
|
.word __exosphere_bin_start__
|
||||||
.word __exosphere_bin_size__
|
.word __exosphere_bin_size__
|
||||||
.word CONTENT_TYPE_EXO
|
.byte CONTENT_TYPE_EXO
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
.word 0xCCCCCCCC
|
.word 0xCCCCCCCC
|
||||||
.asciz "exosphere"
|
.asciz "exosphere"
|
||||||
.align 5
|
.align 5
|
||||||
@@ -125,7 +138,10 @@ _content_headers:
|
|||||||
/* fusee_primary content header */
|
/* fusee_primary content header */
|
||||||
.word __fusee_primary_bin_start__
|
.word __fusee_primary_bin_start__
|
||||||
.word __fusee_primary_bin_size__
|
.word __fusee_primary_bin_size__
|
||||||
.word CONTENT_TYPE_FSP
|
.byte CONTENT_TYPE_FSP
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
.word 0xCCCCCCCC
|
.word 0xCCCCCCCC
|
||||||
.asciz "fusee_primary"
|
.asciz "fusee_primary"
|
||||||
.align 5
|
.align 5
|
||||||
@@ -133,15 +149,21 @@ _content_headers:
|
|||||||
/* loader content header */
|
/* loader content header */
|
||||||
.word __loader_kip_start__
|
.word __loader_kip_start__
|
||||||
.word __loader_kip_size__
|
.word __loader_kip_size__
|
||||||
.word CONTENT_TYPE_KIP
|
.byte CONTENT_TYPE_KIP
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
.word 0xCCCCCCCC
|
.word 0xCCCCCCCC
|
||||||
.asciz "loader"
|
.asciz "Loader"
|
||||||
.align 5
|
.align 5
|
||||||
|
|
||||||
/* lp0fw content header */
|
/* lp0fw content header */
|
||||||
.word __lp0fw_bin_start__
|
.word __lp0fw_bin_start__
|
||||||
.word __lp0fw_bin_size__
|
.word __lp0fw_bin_size__
|
||||||
.word CONTENT_TYPE_WBT
|
.byte CONTENT_TYPE_WBT
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
.word 0xCCCCCCCC
|
.word 0xCCCCCCCC
|
||||||
.asciz "lp0fw"
|
.asciz "lp0fw"
|
||||||
.align 5
|
.align 5
|
||||||
@@ -149,15 +171,21 @@ _content_headers:
|
|||||||
/* pm content header */
|
/* pm content header */
|
||||||
.word __pm_kip_start__
|
.word __pm_kip_start__
|
||||||
.word __pm_kip_size__
|
.word __pm_kip_size__
|
||||||
.word CONTENT_TYPE_KIP
|
.byte CONTENT_TYPE_KIP
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
.word 0xCCCCCCCC
|
.word 0xCCCCCCCC
|
||||||
.asciz "pm"
|
.asciz "ProcessManager"
|
||||||
.align 5
|
.align 5
|
||||||
|
|
||||||
/* rebootstub content header */
|
/* rebootstub content header */
|
||||||
.word __rebootstub_bin_start__
|
.word __rebootstub_bin_start__
|
||||||
.word __rebootstub_bin_size__
|
.word __rebootstub_bin_size__
|
||||||
.word CONTENT_TYPE_RBT
|
.byte CONTENT_TYPE_RBT
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
.word 0xCCCCCCCC
|
.word 0xCCCCCCCC
|
||||||
.asciz "rebootstub"
|
.asciz "rebootstub"
|
||||||
.align 5
|
.align 5
|
||||||
@@ -165,7 +193,10 @@ _content_headers:
|
|||||||
/* sept_primary content header */
|
/* sept_primary content header */
|
||||||
.word __sept_primary_bin_start__
|
.word __sept_primary_bin_start__
|
||||||
.word __sept_primary_bin_size__
|
.word __sept_primary_bin_size__
|
||||||
.word CONTENT_TYPE_SP1
|
.byte CONTENT_TYPE_SP1
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
.word 0xCCCCCCCC
|
.word 0xCCCCCCCC
|
||||||
.asciz "sept_primary"
|
.asciz "sept_primary"
|
||||||
.align 5
|
.align 5
|
||||||
@@ -173,7 +204,10 @@ _content_headers:
|
|||||||
/* sept_secondary 00 content header */
|
/* sept_secondary 00 content header */
|
||||||
.word __sept_secondary_00_enc_start__
|
.word __sept_secondary_00_enc_start__
|
||||||
.word __sept_secondary_00_enc_size__
|
.word __sept_secondary_00_enc_size__
|
||||||
.word CONTENT_TYPE_SP2
|
.byte CONTENT_TYPE_SP2
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
.word 0xCCCCCCCC
|
.word 0xCCCCCCCC
|
||||||
.asciz "septsecondary00"
|
.asciz "septsecondary00"
|
||||||
.align 5
|
.align 5
|
||||||
@@ -181,7 +215,10 @@ _content_headers:
|
|||||||
/* sept_secondary 01 content header */
|
/* sept_secondary 01 content header */
|
||||||
.word __sept_secondary_01_enc_start__
|
.word __sept_secondary_01_enc_start__
|
||||||
.word __sept_secondary_01_enc_size__
|
.word __sept_secondary_01_enc_size__
|
||||||
.word CONTENT_TYPE_SP2
|
.byte CONTENT_TYPE_SP2
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
.word 0xCCCCCCCC
|
.word 0xCCCCCCCC
|
||||||
.asciz "septsecondary01"
|
.asciz "septsecondary01"
|
||||||
.align 5
|
.align 5
|
||||||
@@ -189,7 +226,10 @@ _content_headers:
|
|||||||
/* sm content header */
|
/* sm content header */
|
||||||
.word __sm_kip_start__
|
.word __sm_kip_start__
|
||||||
.word __sm_kip_size__
|
.word __sm_kip_size__
|
||||||
.word CONTENT_TYPE_KIP
|
.byte CONTENT_TYPE_KIP
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
.word 0xCCCCCCCC
|
.word 0xCCCCCCCC
|
||||||
.asciz "sm"
|
.asciz "sm"
|
||||||
.align 5
|
.align 5
|
||||||
@@ -197,15 +237,32 @@ _content_headers:
|
|||||||
/* spl content header */
|
/* spl content header */
|
||||||
.word __spl_kip_start__
|
.word __spl_kip_start__
|
||||||
.word __spl_kip_size__
|
.word __spl_kip_size__
|
||||||
.word CONTENT_TYPE_KIP
|
.byte CONTENT_TYPE_KIP
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
.word 0xCCCCCCCC
|
.word 0xCCCCCCCC
|
||||||
.asciz "spl"
|
.asciz "spl"
|
||||||
.align 5
|
.align 5
|
||||||
|
|
||||||
|
/* ncm content header */
|
||||||
|
.word __ncm_kip_start__
|
||||||
|
.word __ncm_kip_size__
|
||||||
|
.byte CONTENT_TYPE_KIP
|
||||||
|
.byte CONTENT_FLAG0_EXPERIMENTAL
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.word 0xCCCCCCCC
|
||||||
|
.asciz "NCM"
|
||||||
|
.align 5
|
||||||
|
|
||||||
/* emummc content header */
|
/* emummc content header */
|
||||||
.word __emummc_kip_start__
|
.word __emummc_kip_start__
|
||||||
.word __emummc_kip_size__
|
.word __emummc_kip_size__
|
||||||
.word CONTENT_TYPE_EMC
|
.byte CONTENT_TYPE_EMC
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
.word 0xCCCCCCCC
|
.word 0xCCCCCCCC
|
||||||
.asciz "emummc"
|
.asciz "emummc"
|
||||||
.align 5
|
.align 5
|
||||||
@@ -213,7 +270,10 @@ _content_headers:
|
|||||||
/* kernel_ldr content header */
|
/* kernel_ldr content header */
|
||||||
.word __kernel_ldr_bin_start__
|
.word __kernel_ldr_bin_start__
|
||||||
.word __kernel_ldr_bin_size__
|
.word __kernel_ldr_bin_size__
|
||||||
.word CONTENT_TYPE_KLD
|
.byte CONTENT_TYPE_KLD
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
.word 0xCCCCCCCC
|
.word 0xCCCCCCCC
|
||||||
.asciz "kernel_ldr"
|
.asciz "kernel_ldr"
|
||||||
.align 5
|
.align 5
|
||||||
@@ -221,7 +281,10 @@ _content_headers:
|
|||||||
/* splash_screen content header */
|
/* splash_screen content header */
|
||||||
.word __splash_screen_bmp_start__
|
.word __splash_screen_bmp_start__
|
||||||
.word __splash_screen_bmp_size__
|
.word __splash_screen_bmp_size__
|
||||||
.word CONTENT_TYPE_BMP
|
.byte CONTENT_TYPE_BMP
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
|
.byte CONTENT_FLAG_NONE
|
||||||
.word 0xCCCCCCCC
|
.word 0xCCCCCCCC
|
||||||
.asciz "splash_screen"
|
.asciz "splash_screen"
|
||||||
.align 5
|
.align 5
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -30,6 +30,7 @@
|
|||||||
#define u8 uint8_t
|
#define u8 uint8_t
|
||||||
#define u32 uint32_t
|
#define u32 uint32_t
|
||||||
#include "loader_kip.h"
|
#include "loader_kip.h"
|
||||||
|
#include "ncm_kip.h"
|
||||||
#include "pm_kip.h"
|
#include "pm_kip.h"
|
||||||
#include "sm_kip.h"
|
#include "sm_kip.h"
|
||||||
#include "ams_mitm_kip.h"
|
#include "ams_mitm_kip.h"
|
||||||
@@ -47,9 +48,10 @@ static bool g_stratosphere_pm_enabled = true;
|
|||||||
static bool g_stratosphere_ams_mitm_enabled = true;
|
static bool g_stratosphere_ams_mitm_enabled = true;
|
||||||
static bool g_stratosphere_spl_enabled = true;
|
static bool g_stratosphere_spl_enabled = true;
|
||||||
static bool g_stratosphere_boot_enabled = true;
|
static bool g_stratosphere_boot_enabled = true;
|
||||||
|
static bool g_stratosphere_ncm_enabled = false;
|
||||||
|
|
||||||
extern const uint8_t loader_kip[], pm_kip[], sm_kip[], spl_kip[], boot_kip[], ams_mitm_kip[];
|
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, ams_mitm_kip_size;
|
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;
|
static emummc_fs_ver_t g_fs_ver = FS_VER_1_0_0;
|
||||||
|
|
||||||
@@ -57,6 +59,19 @@ emummc_fs_ver_t stratosphere_get_fs_version(void) {
|
|||||||
return g_fs_ver;
|
return g_fs_ver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void stratosphere_enable_ncm(void) {
|
||||||
|
/* The Atmosphere team believes our implementation of NCM to be extremely accurate, */
|
||||||
|
/* and does not think it likely there is any possibility of undesirable behavior */
|
||||||
|
/* when using the NCM reimplementation. However, because NCM manages critical save games */
|
||||||
|
/* the implementation will default to off for some time, until the code has been thoroughly */
|
||||||
|
/* tested in practice. */
|
||||||
|
|
||||||
|
/* PLEASE NOTE: The default behavior will be NCM on in a future atmosphere release, */
|
||||||
|
/* and this opt-in functionality will be removed at that time. */
|
||||||
|
|
||||||
|
g_stratosphere_ncm_enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
/* GCC doesn't consider the size as const... we have to write it ourselves. */
|
/* GCC doesn't consider the size as const... we have to write it ourselves. */
|
||||||
|
|
||||||
ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
|
ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
|
||||||
@@ -68,38 +83,43 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t size = sizeof(ini1_header_t);
|
size_t size = sizeof(ini1_header_t);
|
||||||
|
|
||||||
/* Calculate our processes' sizes. */
|
/* Calculate our processes' sizes. */
|
||||||
if (g_stratosphere_loader_enabled) {
|
if (g_stratosphere_loader_enabled) {
|
||||||
size += loader_kip_size;
|
size += loader_kip_size;
|
||||||
num_processes++;
|
num_processes++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_stratosphere_pm_enabled) {
|
if (g_stratosphere_pm_enabled) {
|
||||||
size += pm_kip_size;
|
size += pm_kip_size;
|
||||||
num_processes++;
|
num_processes++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_stratosphere_sm_enabled) {
|
if (g_stratosphere_sm_enabled) {
|
||||||
size += sm_kip_size;
|
size += sm_kip_size;
|
||||||
num_processes++;
|
num_processes++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_stratosphere_spl_enabled) {
|
if (g_stratosphere_spl_enabled) {
|
||||||
size += spl_kip_size;
|
size += spl_kip_size;
|
||||||
num_processes++;
|
num_processes++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_stratosphere_ams_mitm_enabled) {
|
if (g_stratosphere_ams_mitm_enabled) {
|
||||||
size += ams_mitm_kip_size;
|
size += ams_mitm_kip_size;
|
||||||
num_processes++;
|
num_processes++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (g_stratosphere_ncm_enabled) {
|
||||||
|
size += ncm_kip_size;
|
||||||
|
num_processes++;
|
||||||
|
}
|
||||||
|
|
||||||
if (g_stratosphere_boot_enabled) {
|
if (g_stratosphere_boot_enabled) {
|
||||||
size += boot_kip_size;
|
size += boot_kip_size;
|
||||||
num_processes++;
|
num_processes++;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_stratosphere_ini1 = (ini1_header_t *)malloc(size);
|
g_stratosphere_ini1 = (ini1_header_t *)malloc(size);
|
||||||
|
|
||||||
if (g_stratosphere_ini1 == NULL) {
|
if (g_stratosphere_ini1 == NULL) {
|
||||||
@@ -134,6 +154,11 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
|
|||||||
data += spl_kip_size;
|
data += spl_kip_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (g_stratosphere_ncm_enabled) {
|
||||||
|
memcpy(data, ncm_kip, ncm_kip_size);
|
||||||
|
data += ncm_kip_size;
|
||||||
|
}
|
||||||
|
|
||||||
if (g_stratosphere_ams_mitm_enabled) {
|
if (g_stratosphere_ams_mitm_enabled) {
|
||||||
memcpy(data, ams_mitm_kip, ams_mitm_kip_size);
|
memcpy(data, ams_mitm_kip, ams_mitm_kip_size);
|
||||||
data += ams_mitm_kip_size;
|
data += ams_mitm_kip_size;
|
||||||
@@ -143,7 +168,7 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
|
|||||||
memcpy(data, boot_kip, boot_kip_size);
|
memcpy(data, boot_kip, boot_kip_size);
|
||||||
data += boot_kip_size;
|
data += boot_kip_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return g_stratosphere_ini1;
|
return g_stratosphere_ini1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,26 +185,26 @@ void stratosphere_free_ini1(void) {
|
|||||||
|
|
||||||
static void try_add_sd_kip(ini1_header_t *ini1, const char *kip_path) {
|
static void try_add_sd_kip(ini1_header_t *ini1, const char *kip_path) {
|
||||||
size_t file_size = get_file_size(kip_path);
|
size_t file_size = get_file_size(kip_path);
|
||||||
|
|
||||||
if (ini1->size + file_size > PACKAGE2_SIZE_MAX) {
|
if (ini1->size + file_size > PACKAGE2_SIZE_MAX) {
|
||||||
fatal_error("Failed to load %s: INI1 would be too large!\n", kip_path);
|
fatal_error("Failed to load %s: INI1 would be too large!\n", kip_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
kip1_header_t kip_header;
|
kip1_header_t kip_header;
|
||||||
if (read_from_file(&kip_header, sizeof(kip_header), kip_path) != sizeof(kip_header) || kip_header.magic != MAGIC_KIP1) {
|
if (read_from_file(&kip_header, sizeof(kip_header), kip_path) != sizeof(kip_header) || kip_header.magic != MAGIC_KIP1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t kip_size = kip1_get_size_from_header(&kip_header);
|
size_t kip_size = kip1_get_size_from_header(&kip_header);
|
||||||
if (kip_size > file_size) {
|
if (kip_size > file_size) {
|
||||||
fatal_error("Failed to load %s: KIP size is corrupt!\n", kip_path);
|
fatal_error("Failed to load %s: KIP size is corrupt!\n", kip_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (read_from_file(ini1->kip_data + ini1->size - sizeof(ini1_header_t), kip_size, kip_path) != kip_size) {
|
if (read_from_file(ini1->kip_data + ini1->size - sizeof(ini1_header_t), kip_size, kip_path) != kip_size) {
|
||||||
/* TODO: is this error fatal? */
|
/* TODO: is this error fatal? */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ini1->size += kip_size;
|
ini1->size += kip_size;
|
||||||
ini1->num_processes++;
|
ini1->num_processes++;
|
||||||
}
|
}
|
||||||
@@ -189,24 +214,24 @@ static kip1_header_t *inject_emummc_kip(kip1_header_t *fs_kip, kip1_header_t *em
|
|||||||
size_t fs_kip_size, emummc_kip_size;
|
size_t fs_kip_size, emummc_kip_size;
|
||||||
fs_kip = kip1_uncompress(fs_kip, &fs_kip_size);
|
fs_kip = kip1_uncompress(fs_kip, &fs_kip_size);
|
||||||
emummc_kip = kip1_uncompress(emummc_kip, &emummc_kip_size);
|
emummc_kip = kip1_uncompress(emummc_kip, &emummc_kip_size);
|
||||||
|
|
||||||
/* Allocate kip. */
|
/* Allocate kip. */
|
||||||
kip1_header_t *injected_kip = calloc(1, fs_kip_size + emummc_kip_size);
|
kip1_header_t *injected_kip = calloc(1, fs_kip_size + emummc_kip_size);
|
||||||
if (injected_kip == NULL) {
|
if (injected_kip == NULL) {
|
||||||
fatal_error("Failed to allocate memory for emummc kip injection!");
|
fatal_error("Failed to allocate memory for emummc kip injection!");
|
||||||
}
|
}
|
||||||
memcpy(injected_kip, fs_kip, sizeof(*fs_kip));
|
memcpy(injected_kip, fs_kip, sizeof(*fs_kip));
|
||||||
|
|
||||||
const size_t fs_contents_size = kip1_get_size_from_header(fs_kip) - sizeof(kip1_header_t);
|
const size_t fs_contents_size = kip1_get_size_from_header(fs_kip) - sizeof(kip1_header_t);
|
||||||
|
|
||||||
const size_t emummc_data_size = emummc_kip->section_headers[3].out_offset + emummc_kip->section_headers[3].out_size;
|
const size_t emummc_data_size = emummc_kip->section_headers[3].out_offset + emummc_kip->section_headers[3].out_size;
|
||||||
if (emummc_data_size & 0xFFF) {
|
if (emummc_data_size & 0xFFF) {
|
||||||
fatal_error("Invalid emummc kip!");
|
fatal_error("Invalid emummc kip!");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy over capabilities. */
|
/* Copy over capabilities. */
|
||||||
memcpy(injected_kip->capabilities, emummc_kip->capabilities, sizeof(emummc_kip->capabilities));
|
memcpy(injected_kip->capabilities, emummc_kip->capabilities, sizeof(emummc_kip->capabilities));
|
||||||
|
|
||||||
/* Add extra cap for 1.0.0 */
|
/* Add extra cap for 1.0.0 */
|
||||||
if (stratosphere_get_fs_version() == FS_VER_1_0_0) {
|
if (stratosphere_get_fs_version() == FS_VER_1_0_0) {
|
||||||
for (size_t i = 0; i < 0x20; i++) {
|
for (size_t i = 0; i < 0x20; i++) {
|
||||||
@@ -217,14 +242,14 @@ static kip1_header_t *inject_emummc_kip(kip1_header_t *fs_kip, kip1_header_t *em
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update sections. */
|
/* Update sections. */
|
||||||
injected_kip->section_headers[0].out_size += emummc_data_size;
|
injected_kip->section_headers[0].out_size += emummc_data_size;
|
||||||
injected_kip->section_headers[0].compressed_size += emummc_data_size;
|
injected_kip->section_headers[0].compressed_size += emummc_data_size;
|
||||||
for (size_t i = 1; i < 4; i++) {
|
for (size_t i = 1; i < 4; i++) {
|
||||||
injected_kip->section_headers[i].out_offset += emummc_data_size;
|
injected_kip->section_headers[i].out_offset += emummc_data_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy data. */
|
/* Copy data. */
|
||||||
{
|
{
|
||||||
size_t ofs = 0;
|
size_t ofs = 0;
|
||||||
@@ -234,22 +259,22 @@ static kip1_header_t *inject_emummc_kip(kip1_header_t *fs_kip, kip1_header_t *em
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
memcpy(injected_kip->data + emummc_data_size, fs_kip->data, fs_contents_size);
|
memcpy(injected_kip->data + emummc_data_size, fs_kip->data, fs_contents_size);
|
||||||
|
|
||||||
return injected_kip;
|
return injected_kip;
|
||||||
}
|
}
|
||||||
|
|
||||||
ini1_header_t *stratosphere_get_sd_files_ini1(void) {
|
ini1_header_t *stratosphere_get_sd_files_ini1(void) {
|
||||||
if (g_sd_files_ini1 != NULL) {
|
if (g_sd_files_ini1 != NULL) {
|
||||||
return g_sd_files_ini1;
|
return g_sd_files_ini1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate space. */
|
/* Allocate space. */
|
||||||
g_sd_files_ini1 = (ini1_header_t *)malloc(PACKAGE2_SIZE_MAX);
|
g_sd_files_ini1 = (ini1_header_t *)malloc(PACKAGE2_SIZE_MAX);
|
||||||
g_sd_files_ini1->magic = MAGIC_INI1;
|
g_sd_files_ini1->magic = MAGIC_INI1;
|
||||||
g_sd_files_ini1->size = sizeof(ini1_header_t);
|
g_sd_files_ini1->size = sizeof(ini1_header_t);
|
||||||
g_sd_files_ini1->num_processes = 0;
|
g_sd_files_ini1->num_processes = 0;
|
||||||
g_sd_files_ini1->_0xC = 0;
|
g_sd_files_ini1->_0xC = 0;
|
||||||
|
|
||||||
/* Load all kips from /atmosphere/kips/<*>.kip or /atmosphere/kips/<*>/<*>.kip. */
|
/* Load all kips from /atmosphere/kips/<*>.kip or /atmosphere/kips/<*>/<*>.kip. */
|
||||||
DIR *kips_dir = opendir("atmosphere/kips");
|
DIR *kips_dir = opendir("atmosphere/kips");
|
||||||
struct dirent *ent;
|
struct dirent *ent;
|
||||||
@@ -258,15 +283,15 @@ ini1_header_t *stratosphere_get_sd_files_ini1(void) {
|
|||||||
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
|
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
char kip_path[0x301] = {0};
|
char kip_path[0x301] = {0};
|
||||||
snprintf(kip_path, 0x300, "atmosphere/kips/%s", ent->d_name);
|
snprintf(kip_path, 0x300, "atmosphere/kips/%s", ent->d_name);
|
||||||
|
|
||||||
struct stat kip_stat;
|
struct stat kip_stat;
|
||||||
if (stat(kip_path, &kip_stat) == -1) {
|
if (stat(kip_path, &kip_stat) == -1) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((kip_stat.st_mode & S_IFMT) == S_IFREG) {
|
if ((kip_stat.st_mode & S_IFMT) == S_IFREG) {
|
||||||
/* If file, add to ini1. */
|
/* If file, add to ini1. */
|
||||||
try_add_sd_kip(g_sd_files_ini1, kip_path);
|
try_add_sd_kip(g_sd_files_ini1, kip_path);
|
||||||
@@ -279,15 +304,15 @@ ini1_header_t *stratosphere_get_sd_files_ini1(void) {
|
|||||||
if (strcmp(sub_ent->d_name, ".") == 0 || strcmp(sub_ent->d_name, "..") == 0) {
|
if (strcmp(sub_ent->d_name, ".") == 0 || strcmp(sub_ent->d_name, "..") == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reuse kip path variable. */
|
/* Reuse kip path variable. */
|
||||||
memset(kip_path, 0, sizeof(kip_path));
|
memset(kip_path, 0, sizeof(kip_path));
|
||||||
snprintf(kip_path, 0x300, "atmosphere/kips/%s/%s", ent->d_name, sub_ent->d_name);
|
snprintf(kip_path, 0x300, "atmosphere/kips/%s/%s", ent->d_name, sub_ent->d_name);
|
||||||
|
|
||||||
if (stat(kip_path, &kip_stat) == -1) {
|
if (stat(kip_path, &kip_stat) == -1) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((kip_stat.st_mode & S_IFMT) == S_IFREG) {
|
if ((kip_stat.st_mode & S_IFMT) == S_IFREG) {
|
||||||
/* If file, add to ini1. */
|
/* If file, add to ini1. */
|
||||||
try_add_sd_kip(g_sd_files_ini1, kip_path);
|
try_add_sd_kip(g_sd_files_ini1, kip_path);
|
||||||
@@ -299,7 +324,7 @@ ini1_header_t *stratosphere_get_sd_files_ini1(void) {
|
|||||||
}
|
}
|
||||||
closedir(kips_dir);
|
closedir(kips_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
return g_sd_files_ini1;
|
return g_sd_files_ini1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,7 +369,7 @@ ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, size_t num_inis, vo
|
|||||||
}
|
}
|
||||||
|
|
||||||
offset += kip1_get_size_from_header(current_kip);
|
offset += kip1_get_size_from_header(current_kip);
|
||||||
|
|
||||||
bool already_loaded = false;
|
bool already_loaded = false;
|
||||||
for (uint32_t j = 0; j < merged->num_processes; j++) {
|
for (uint32_t j = 0; j < merged->num_processes; j++) {
|
||||||
if (process_list[j] == current_kip->title_id) {
|
if (process_list[j] == current_kip->title_id) {
|
||||||
@@ -355,20 +380,20 @@ ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, size_t num_inis, vo
|
|||||||
if (already_loaded) {
|
if (already_loaded) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
print(SCREEN_LOG_LEVEL_MANDATORY, "[NXBOOT]: Loading KIP %08x%08x...\n", (uint32_t)(current_kip->title_id >> 32), (uint32_t)current_kip->title_id);
|
print(SCREEN_LOG_LEVEL_MANDATORY, "[NXBOOT]: Loading KIP %08x%08x...\n", (uint32_t)(current_kip->title_id >> 32), (uint32_t)current_kip->title_id);
|
||||||
|
|
||||||
size_t current_kip_size = kip1_get_size_from_header(current_kip);
|
size_t current_kip_size = kip1_get_size_from_header(current_kip);
|
||||||
if (current_kip_size > remaining_size) {
|
if (current_kip_size > remaining_size) {
|
||||||
fatal_error("Not enough space for all the KIP1s!\n");
|
fatal_error("Not enough space for all the KIP1s!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
kip1_header_t *patched_kip = apply_kip_ips_patches(current_kip, current_kip_size, &g_fs_ver);
|
kip1_header_t *patched_kip = apply_kip_ips_patches(current_kip, current_kip_size, &g_fs_ver);
|
||||||
|
|
||||||
if (current_kip->title_id == FS_TITLE_ID && emummc != NULL) {
|
if (current_kip->title_id == FS_TITLE_ID && emummc != NULL) {
|
||||||
patched_kip = inject_emummc_kip(patched_kip != NULL ? patched_kip : current_kip, (kip1_header_t *)emummc);
|
patched_kip = inject_emummc_kip(patched_kip != NULL ? patched_kip : current_kip, (kip1_header_t *)emummc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (patched_kip != NULL) {
|
if (patched_kip != NULL) {
|
||||||
size_t patched_kip_size = kip1_get_size_from_header(patched_kip);
|
size_t patched_kip_size = kip1_get_size_from_header(patched_kip);
|
||||||
if (patched_kip_size > remaining_size) {
|
if (patched_kip_size > remaining_size) {
|
||||||
@@ -377,9 +402,9 @@ ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, size_t num_inis, vo
|
|||||||
memcpy(current_dst_kip, patched_kip, patched_kip_size);
|
memcpy(current_dst_kip, patched_kip, patched_kip_size);
|
||||||
remaining_size -= patched_kip_size;
|
remaining_size -= patched_kip_size;
|
||||||
current_dst_kip += patched_kip_size;
|
current_dst_kip += patched_kip_size;
|
||||||
|
|
||||||
free(patched_kip);
|
free(patched_kip);
|
||||||
} else {
|
} else {
|
||||||
memcpy(current_dst_kip, current_kip, current_kip_size);
|
memcpy(current_dst_kip, current_kip, current_kip_size);
|
||||||
remaining_size -= current_kip_size;
|
remaining_size -= current_kip_size;
|
||||||
current_dst_kip += current_kip_size;
|
current_dst_kip += current_kip_size;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef FUSEE_STRATOSPHERE_H
|
#ifndef FUSEE_STRATOSPHERE_H
|
||||||
#define FUSEE_STRATOSPHERE_H
|
#define FUSEE_STRATOSPHERE_H
|
||||||
|
|
||||||
@@ -30,6 +30,8 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware);
|
|||||||
ini1_header_t *stratosphere_get_sd_files_ini1(void);
|
ini1_header_t *stratosphere_get_sd_files_ini1(void);
|
||||||
void stratosphere_free_ini1(void);
|
void stratosphere_free_ini1(void);
|
||||||
|
|
||||||
|
void stratosphere_enable_ncm(void);
|
||||||
|
|
||||||
emummc_fs_ver_t stratosphere_get_fs_version(void);
|
emummc_fs_ver_t stratosphere_get_fs_version(void);
|
||||||
|
|
||||||
ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, unsigned int num_inis, void *emummc, size_t emummc_size);
|
ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, unsigned int num_inis, void *emummc, size_t emummc_size);
|
||||||
@@ -37,8 +39,10 @@ ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, unsigned int num_in
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
bool has_nogc_config;
|
bool has_nogc_config;
|
||||||
bool enable_nogc;
|
bool enable_nogc;
|
||||||
|
bool ncm_enabled;
|
||||||
} stratosphere_cfg_t;
|
} stratosphere_cfg_t;
|
||||||
|
|
||||||
#define STRATOSPHERE_NOGC_KEY "nogc"
|
#define STRATOSPHERE_NOGC_KEY "nogc"
|
||||||
|
#define STRATOSPHERE_ENABLE_NCM_KEY "enable_ncm"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
[subrepo]
|
[subrepo]
|
||||||
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
||||||
branch = master
|
branch = master
|
||||||
commit = 83aa6133ee2eb43287f8fc373d309a0c99337429
|
commit = 30f3e4c33d2767126c36a51b9259051c6c42d6b5
|
||||||
parent = fd34e2342a66b2aa3062ea7cecaf9728f12ef21a
|
parent = 82eab9c8d0093e4d8907a668e9335981f6f4949b
|
||||||
method = merge
|
method = merge
|
||||||
cmdver = 0.4.1
|
cmdver = 0.4.1
|
||||||
|
|||||||
@@ -15,11 +15,11 @@ include $(DEVKITPRO)/libnx/switch_rules
|
|||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# options for code generation
|
# options for code generation
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
export DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_STRATOSPHERE
|
export DEFINES = $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_STRATOSPHERE -D_GNU_SOURCE
|
||||||
export SETTINGS := $(ATMOSPHERE_SETTINGS) -O2
|
export SETTINGS = $(ATMOSPHERE_SETTINGS) -O2
|
||||||
export CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
|
export CFLAGS = $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
|
||||||
export CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS)
|
export CXXFLAGS = $(CFLAGS) $(ATMOSPHERE_CXXFLAGS)
|
||||||
export ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) $(DEFINES)
|
export ASFLAGS = $(ATMOSPHERE_ASFLAGS) $(SETTINGS) $(DEFINES)
|
||||||
|
|
||||||
export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \
|
export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \
|
||||||
-Wl,--wrap,__cxa_throw \
|
-Wl,--wrap,__cxa_throw \
|
||||||
@@ -36,17 +36,17 @@ export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \
|
|||||||
-Wl,--wrap,_ZSt20__throw_length_errorPKc \
|
-Wl,--wrap,_ZSt20__throw_length_errorPKc \
|
||||||
-Wl,--wrap,_ZNSt11logic_errorC2EPKc
|
-Wl,--wrap,_ZNSt11logic_errorC2EPKc
|
||||||
|
|
||||||
export LDFLAGS := -specs=$(DEVKITPRO)/libnx/switch.specs $(SETTINGS) $(CXXWRAPS) -Wl,-Map,$(notdir $*.map)
|
export LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs $(SETTINGS) $(CXXWRAPS) -Wl,-Map,$(notdir $*.map)
|
||||||
|
|
||||||
export LIBS := -lstratosphere -lnx
|
export LIBS = -lstratosphere -lnx
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# list of directories containing libraries, this must be the top level containing
|
# list of directories containing libraries, this must be the top level containing
|
||||||
# include and lib
|
# include and lib
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
export LIBDIRS := $(PORTLIBS) $(LIBNX) $(ATMOSPHERE_LIBRARIES_DIR)/libvapours $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere
|
export LIBDIRS = $(PORTLIBS) $(LIBNX) $(ATMOSPHERE_LIBRARIES_DIR)/libvapours $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# stratosphere sysmodules may (but usually do not) have an exefs source dir
|
# stratosphere sysmodules may (but usually do not) have an exefs source dir
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
export EXEFS_SRC := exefs_src
|
export EXEFS_SRC = exefs_src
|
||||||
@@ -84,6 +84,10 @@ namespace ams::kern::arch::arm64 {
|
|||||||
return this->page_table.UnmapPages(addr, num_pages, state);
|
return this->page_table.UnmapPages(addr, num_pages, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result MakeAndOpenPageGroup(KPageGroup *out, KProcessAddress address, size_t num_pages, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr) {
|
||||||
|
return this->page_table.MakeAndOpenPageGroup(out, address, num_pages, state_mask, state, perm_mask, perm, attr_mask, attr);
|
||||||
|
}
|
||||||
|
|
||||||
bool GetPhysicalAddress(KPhysicalAddress *out, KProcessAddress address) const {
|
bool GetPhysicalAddress(KPhysicalAddress *out, KProcessAddress address) const {
|
||||||
return this->page_table.GetPhysicalAddress(out, address);
|
return this->page_table.GetPhysicalAddress(out, address);
|
||||||
}
|
}
|
||||||
@@ -96,12 +100,23 @@ namespace ams::kern::arch::arm64 {
|
|||||||
KProcessAddress GetAliasRegionStart() const { return this->page_table.GetAliasRegionStart(); }
|
KProcessAddress GetAliasRegionStart() const { return this->page_table.GetAliasRegionStart(); }
|
||||||
KProcessAddress GetStackRegionStart() const { return this->page_table.GetStackRegionStart(); }
|
KProcessAddress GetStackRegionStart() const { return this->page_table.GetStackRegionStart(); }
|
||||||
KProcessAddress GetKernelMapRegionStart() const { return this->page_table.GetKernelMapRegionStart(); }
|
KProcessAddress GetKernelMapRegionStart() const { return this->page_table.GetKernelMapRegionStart(); }
|
||||||
|
KProcessAddress GetAliasCodeRegionStart() const { return this->page_table.GetAliasCodeRegionStart(); }
|
||||||
|
|
||||||
size_t GetAddressSpaceSize() const { return this->page_table.GetAddressSpaceSize(); }
|
size_t GetAddressSpaceSize() const { return this->page_table.GetAddressSpaceSize(); }
|
||||||
size_t GetHeapRegionSize() const { return this->page_table.GetHeapRegionSize(); }
|
size_t GetHeapRegionSize() const { return this->page_table.GetHeapRegionSize(); }
|
||||||
size_t GetAliasRegionSize() const { return this->page_table.GetAliasRegionSize(); }
|
size_t GetAliasRegionSize() const { return this->page_table.GetAliasRegionSize(); }
|
||||||
size_t GetStackRegionSize() const { return this->page_table.GetStackRegionSize(); }
|
size_t GetStackRegionSize() const { return this->page_table.GetStackRegionSize(); }
|
||||||
size_t GetKernelMapRegionSize() const { return this->page_table.GetKernelMapRegionSize(); }
|
size_t GetKernelMapRegionSize() const { return this->page_table.GetKernelMapRegionSize(); }
|
||||||
|
size_t GetAliasCodeRegionSize() const { return this->page_table.GetAliasCodeRegionSize(); }
|
||||||
|
|
||||||
|
KPhysicalAddress GetHeapPhysicalAddress(KVirtualAddress address) const {
|
||||||
|
/* TODO: Better way to convert address type? */
|
||||||
|
return this->page_table.GetHeapPhysicalAddress(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
KBlockInfoManager *GetBlockInfoManager() {
|
||||||
|
return this->page_table.GetBlockInfoManager();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,9 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
/* Power management. */
|
/* Power management. */
|
||||||
static void SleepSystem();
|
static void SleepSystem();
|
||||||
static NORETURN void StopSystem();
|
static NORETURN void StopSystem();
|
||||||
|
|
||||||
|
/* User access. */
|
||||||
|
static void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -222,6 +222,8 @@ namespace ams::kern {
|
|||||||
KScopedAutoObject(o).Swap(*this);
|
KScopedAutoObject(o).Swap(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr ALWAYS_INLINE T *GetPointerUnsafe() { return this->obj; }
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE bool IsNull() const { return this->obj == nullptr; }
|
constexpr ALWAYS_INLINE bool IsNull() const { return this->obj == nullptr; }
|
||||||
constexpr ALWAYS_INLINE bool IsNotNull() const { return this->obj != nullptr; }
|
constexpr ALWAYS_INLINE bool IsNotNull() const { return this->obj != nullptr; }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <mesosphere/kern_common.hpp>
|
||||||
|
#include <mesosphere/kern_k_synchronization_object.hpp>
|
||||||
|
|
||||||
|
namespace ams::kern {
|
||||||
|
|
||||||
|
class KPort;
|
||||||
|
|
||||||
|
class KClientPort final : public KSynchronizationObject {
|
||||||
|
MESOSPHERE_AUTOOBJECT_TRAITS(KClientPort, KSynchronizationObject);
|
||||||
|
private:
|
||||||
|
std::atomic<s32> num_sessions;
|
||||||
|
std::atomic<s32> peak_sessions;
|
||||||
|
s32 max_sessions;
|
||||||
|
KPort *parent;
|
||||||
|
public:
|
||||||
|
constexpr KClientPort() : num_sessions(), peak_sessions(), max_sessions(), parent() { /* ... */ }
|
||||||
|
virtual ~KClientPort() { /* ... */ }
|
||||||
|
|
||||||
|
void Initialize(KPort *parent, s32 max_sessions);
|
||||||
|
|
||||||
|
constexpr const KPort *GetParent() const { return this->parent; }
|
||||||
|
|
||||||
|
bool IsLight() const;
|
||||||
|
|
||||||
|
/* Overridden virtual functions. */
|
||||||
|
virtual void Destroy() override;
|
||||||
|
virtual bool IsSignaled() const override;
|
||||||
|
|
||||||
|
/* TODO: More of KClientPort. */
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <mesosphere/kern_common.hpp>
|
||||||
|
#include <mesosphere/kern_k_synchronization_object.hpp>
|
||||||
|
|
||||||
|
namespace ams::kern {
|
||||||
|
|
||||||
|
class KSession;
|
||||||
|
|
||||||
|
class KClientSession final : public KAutoObjectWithSlabHeapAndContainer<KClientSession, KAutoObjectWithList> {
|
||||||
|
MESOSPHERE_AUTOOBJECT_TRAITS(KClientSession, KAutoObject);
|
||||||
|
private:
|
||||||
|
KSession *parent;
|
||||||
|
public:
|
||||||
|
constexpr KClientSession() : parent() { /* ... */ }
|
||||||
|
virtual ~KClientSession() { /* ... */ }
|
||||||
|
|
||||||
|
void Initialize(KSession *parent) {
|
||||||
|
/* Set member variables. */
|
||||||
|
this->parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PostDestroy(uintptr_t arg) { /* ... */ }
|
||||||
|
|
||||||
|
constexpr const KSession *GetParent() const { return this->parent; }
|
||||||
|
|
||||||
|
/* TODO: More of KClientSession. */
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -130,11 +130,11 @@ namespace ams::kern {
|
|||||||
MESOSPHERE_ASSERT_THIS();
|
MESOSPHERE_ASSERT_THIS();
|
||||||
|
|
||||||
/* Handle pseudo-handles. */
|
/* Handle pseudo-handles. */
|
||||||
if constexpr (std::is_same<T, KProcess>::value) {
|
if constexpr (std::is_base_of<T, KProcess>::value) {
|
||||||
if (handle == ams::svc::PseudoHandle::CurrentProcess) {
|
if (handle == ams::svc::PseudoHandle::CurrentProcess) {
|
||||||
return GetCurrentProcessPointer();
|
return GetCurrentProcessPointer();
|
||||||
}
|
}
|
||||||
} else if constexpr (std::is_same<T, KThread>::value) {
|
} else if constexpr (std::is_base_of<T, KThread>::value) {
|
||||||
if (handle == ams::svc::PseudoHandle::CurrentThread) {
|
if (handle == ams::svc::PseudoHandle::CurrentThread) {
|
||||||
return GetCurrentThreadPointer();
|
return GetCurrentThreadPointer();
|
||||||
}
|
}
|
||||||
@@ -156,11 +156,11 @@ namespace ams::kern {
|
|||||||
static_assert(!std::is_base_of<KInterruptEvent, T>::value);
|
static_assert(!std::is_base_of<KInterruptEvent, T>::value);
|
||||||
|
|
||||||
/* Handle pseudo-handles. */
|
/* Handle pseudo-handles. */
|
||||||
if constexpr (std::is_same<T, KProcess>::value) {
|
if constexpr (std::is_base_of<T, KProcess>::value) {
|
||||||
if (handle == ams::svc::PseudoHandle::CurrentProcess) {
|
if (handle == ams::svc::PseudoHandle::CurrentProcess) {
|
||||||
return GetCurrentProcessPointer();
|
return GetCurrentProcessPointer();
|
||||||
}
|
}
|
||||||
} else if constexpr (std::is_same<T, KThread>::value) {
|
} else if constexpr (std::is_base_of<T, KThread>::value) {
|
||||||
if (handle == ams::svc::PseudoHandle::CurrentThread) {
|
if (handle == ams::svc::PseudoHandle::CurrentThread) {
|
||||||
return GetCurrentThreadPointer();
|
return GetCurrentThreadPointer();
|
||||||
}
|
}
|
||||||
@@ -201,7 +201,7 @@ namespace ams::kern {
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
ALWAYS_INLINE void Register(ams::svc::Handle handle, T *obj) {
|
ALWAYS_INLINE void Register(ams::svc::Handle handle, T *obj) {
|
||||||
static_assert(std::is_base_of<KAutoObject, T>::value);
|
static_assert(std::is_base_of<KAutoObject, T>::value);
|
||||||
return this->Add(handle, obj, obj->GetTypeObj().GetClassToken());
|
return this->Register(handle, obj, obj->GetTypeObj().GetClassToken());
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
NOINLINE Result Add(ams::svc::Handle *out_handle, KAutoObject *obj, u16 type);
|
NOINLINE Result Add(ams::svc::Handle *out_handle, KAutoObject *obj, u16 type);
|
||||||
@@ -278,7 +278,7 @@ namespace ams::kern {
|
|||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr NOINLINE KAutoObject *GetObjectImpl(ams::svc::Handle handle) const {
|
constexpr ALWAYS_INLINE KAutoObject *GetObjectImpl(ams::svc::Handle handle) const {
|
||||||
MESOSPHERE_ASSERT_THIS();
|
MESOSPHERE_ASSERT_THIS();
|
||||||
|
|
||||||
/* Handles must not have reserved bits set. */
|
/* Handles must not have reserved bits set. */
|
||||||
@@ -293,7 +293,7 @@ namespace ams::kern {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr NOINLINE KAutoObject *GetObjectByIndexImpl(ams::svc::Handle *out_handle, size_t index) const {
|
constexpr ALWAYS_INLINE KAutoObject *GetObjectByIndexImpl(ams::svc::Handle *out_handle, size_t index) const {
|
||||||
MESOSPHERE_ASSERT_THIS();
|
MESOSPHERE_ASSERT_THIS();
|
||||||
|
|
||||||
/* Index must be in bounds. */
|
/* Index must be in bounds. */
|
||||||
@@ -310,6 +310,49 @@ namespace ams::kern {
|
|||||||
*out_handle = EncodeHandle(index, entry->GetLinearId());
|
*out_handle = EncodeHandle(index, entry->GetLinearId());
|
||||||
return entry->GetObject();
|
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;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <mesosphere/kern_common.hpp>
|
||||||
|
#include <mesosphere/kern_k_synchronization_object.hpp>
|
||||||
|
|
||||||
|
namespace ams::kern {
|
||||||
|
|
||||||
|
class KLightSession;
|
||||||
|
|
||||||
|
class KLightClientSession final : public KAutoObjectWithSlabHeapAndContainer<KLightClientSession, KAutoObjectWithList> {
|
||||||
|
MESOSPHERE_AUTOOBJECT_TRAITS(KLightClientSession, KAutoObject);
|
||||||
|
private:
|
||||||
|
KLightSession *parent;
|
||||||
|
public:
|
||||||
|
constexpr KLightClientSession() : parent() { /* ... */ }
|
||||||
|
virtual ~KLightClientSession() { /* ... */ }
|
||||||
|
|
||||||
|
void Initialize(KLightSession *parent) {
|
||||||
|
/* Set member variables. */
|
||||||
|
this->parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PostDestroy(uintptr_t arg) { /* ... */ }
|
||||||
|
|
||||||
|
constexpr const KLightSession *GetParent() const { return this->parent; }
|
||||||
|
|
||||||
|
/* TODO: More of KLightClientSession. */
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <mesosphere/kern_common.hpp>
|
||||||
|
#include <mesosphere/kern_k_synchronization_object.hpp>
|
||||||
|
#include <mesosphere/kern_k_thread.hpp>
|
||||||
|
#include <mesosphere/kern_k_thread_queue.hpp>
|
||||||
|
|
||||||
|
namespace ams::kern {
|
||||||
|
|
||||||
|
class KLightSession;
|
||||||
|
|
||||||
|
class KLightServerSession final : public KAutoObjectWithSlabHeapAndContainer<KLightServerSession, KAutoObjectWithList>, public util::IntrusiveListBaseNode<KLightServerSession> {
|
||||||
|
MESOSPHERE_AUTOOBJECT_TRAITS(KLightServerSession, KAutoObject);
|
||||||
|
private:
|
||||||
|
KLightSession *parent;
|
||||||
|
KThreadQueue request_queue;
|
||||||
|
KThreadQueue server_queue;
|
||||||
|
KThread *current_request;
|
||||||
|
KThread *server_thread;
|
||||||
|
public:
|
||||||
|
constexpr KLightServerSession() : parent(), request_queue(), server_queue(), current_request(), server_thread() { /* ... */ }
|
||||||
|
virtual ~KLightServerSession() { /* ... */ }
|
||||||
|
|
||||||
|
void Initialize(KLightSession *parent);
|
||||||
|
|
||||||
|
static void PostDestroy(uintptr_t arg) { /* ... */ }
|
||||||
|
|
||||||
|
constexpr const KLightSession *GetParent() const { return this->parent; }
|
||||||
|
|
||||||
|
/* TODO: More of KLightServerSession. */
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -16,14 +16,52 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <mesosphere/kern_common.hpp>
|
#include <mesosphere/kern_common.hpp>
|
||||||
#include <mesosphere/kern_k_synchronization_object.hpp>
|
#include <mesosphere/kern_k_synchronization_object.hpp>
|
||||||
|
#include <mesosphere/kern_k_light_server_session.hpp>
|
||||||
|
#include <mesosphere/kern_k_light_client_session.hpp>
|
||||||
#include <mesosphere/kern_slab_helpers.hpp>
|
#include <mesosphere/kern_slab_helpers.hpp>
|
||||||
|
|
||||||
namespace ams::kern {
|
namespace ams::kern {
|
||||||
|
|
||||||
|
class KClientPort;
|
||||||
|
class KProcess;
|
||||||
|
|
||||||
class KLightSession final : public KAutoObjectWithSlabHeapAndContainer<KLightSession, KAutoObjectWithList> {
|
class KLightSession final : public KAutoObjectWithSlabHeapAndContainer<KLightSession, KAutoObjectWithList> {
|
||||||
MESOSPHERE_AUTOOBJECT_TRAITS(KLightSession, KAutoObject);
|
MESOSPHERE_AUTOOBJECT_TRAITS(KLightSession, KAutoObject);
|
||||||
|
private:
|
||||||
|
enum class State : u8 {
|
||||||
|
Invalid = 0,
|
||||||
|
Normal = 1,
|
||||||
|
ClientClosed = 2,
|
||||||
|
ServerClosed = 3,
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
KLightServerSession server;
|
||||||
|
KLightClientSession client;
|
||||||
|
State state;
|
||||||
|
KClientPort *port;
|
||||||
|
uintptr_t name;
|
||||||
|
KProcess *process;
|
||||||
|
bool initialized;
|
||||||
public:
|
public:
|
||||||
|
constexpr KLightSession()
|
||||||
|
: server(), client(), state(State::Invalid), port(), name(), process(), initialized()
|
||||||
|
{
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~KLightSession() { /* ... */ }
|
||||||
|
|
||||||
|
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. */
|
/* TODO: This is a placeholder definition. */
|
||||||
|
|
||||||
|
KLightClientSession &GetClientSession() { return this->client; }
|
||||||
|
KLightServerSession &GetServerSession() { return this->server; }
|
||||||
|
const KLightClientSession &GetClientSession() const { return this->client; }
|
||||||
|
const KLightServerSession &GetServerSession() const { return this->server; }
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,39 @@ namespace ams::kern {
|
|||||||
|
|
||||||
class KObjectName : public KSlabAllocated<KObjectName>, public util::IntrusiveListBaseNode<KObjectName> {
|
class KObjectName : public KSlabAllocated<KObjectName>, public util::IntrusiveListBaseNode<KObjectName> {
|
||||||
public:
|
public:
|
||||||
/* TODO: This is a placeholder definition. */
|
static constexpr size_t NameLengthMax = 12;
|
||||||
|
|
||||||
|
using List = util::IntrusiveListBaseTraits<KObjectName>::ListType;
|
||||||
|
private:
|
||||||
|
char name[NameLengthMax];
|
||||||
|
KAutoObject *object;
|
||||||
|
public:
|
||||||
|
constexpr KObjectName() : name(), object() { /* ... */ }
|
||||||
|
public:
|
||||||
|
static Result NewFromName(KAutoObject *obj, const char *name);
|
||||||
|
static Result Delete(KAutoObject *obj, const char *name);
|
||||||
|
|
||||||
|
static KScopedAutoObject<KAutoObject> Find(const char *name);
|
||||||
|
|
||||||
|
template<typename Derived>
|
||||||
|
static Result Delete(const char *name) {
|
||||||
|
/* Find the object. */
|
||||||
|
KScopedAutoObject obj = Find(name);
|
||||||
|
R_UNLESS(obj.IsNotNull(), svc::ResultNotFound());
|
||||||
|
|
||||||
|
/* Cast the object to the desired type. */
|
||||||
|
Derived *derived = obj->DynamicCast<Derived *>();
|
||||||
|
R_UNLESS(derived != nullptr, svc::ResultNotFound());
|
||||||
|
|
||||||
|
return Delete(obj.GetPointerUnsafe(), name);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
static KScopedAutoObject<KAutoObject> FindImpl(const char *name);
|
||||||
|
|
||||||
|
void Initialize(KAutoObject *obj, const char *name);
|
||||||
|
|
||||||
|
bool MatchesName(const char *name) const;
|
||||||
|
KAutoObject *GetObject() const { return this->object; }
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -191,8 +191,6 @@ namespace ams::kern {
|
|||||||
KPageTableImpl &GetImpl() { return this->impl; }
|
KPageTableImpl &GetImpl() { return this->impl; }
|
||||||
const KPageTableImpl &GetImpl() const { return this->impl; }
|
const KPageTableImpl &GetImpl() const { return this->impl; }
|
||||||
|
|
||||||
KBlockInfoManager *GetBlockInfoManager() const { return this->block_info_manager; }
|
|
||||||
|
|
||||||
bool IsLockedByCurrentThread() const { return this->general_lock.IsLockedByCurrentThread(); }
|
bool IsLockedByCurrentThread() const { return this->general_lock.IsLockedByCurrentThread(); }
|
||||||
|
|
||||||
bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr) {
|
bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr) {
|
||||||
@@ -245,6 +243,8 @@ namespace ams::kern {
|
|||||||
return this->GetImpl().GetPhysicalAddress(out, virt_addr);
|
return this->GetImpl().GetPhysicalAddress(out, virt_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KBlockInfoManager *GetBlockInfoManager() const { return this->block_info_manager; }
|
||||||
|
|
||||||
Result SetMemoryPermission(KProcessAddress addr, size_t size, ams::svc::MemoryPermission perm);
|
Result SetMemoryPermission(KProcessAddress addr, size_t size, ams::svc::MemoryPermission perm);
|
||||||
Result SetProcessMemoryPermission(KProcessAddress addr, size_t size, ams::svc::MemoryPermission perm);
|
Result SetProcessMemoryPermission(KProcessAddress addr, size_t size, ams::svc::MemoryPermission perm);
|
||||||
Result SetHeapSize(KProcessAddress *out, size_t size);
|
Result SetHeapSize(KProcessAddress *out, size_t size);
|
||||||
@@ -270,18 +270,22 @@ namespace ams::kern {
|
|||||||
Result MapPageGroup(KProcessAddress *out_addr, const KPageGroup &pg, KProcessAddress region_start, size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
|
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 MapPageGroup(KProcessAddress address, const KPageGroup &pg, KMemoryState state, KMemoryPermission perm);
|
||||||
Result UnmapPageGroup(KProcessAddress address, const KPageGroup &pg, KMemoryState state);
|
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);
|
||||||
public:
|
public:
|
||||||
KProcessAddress GetAddressSpaceStart() const { return this->address_space_start; }
|
KProcessAddress GetAddressSpaceStart() const { return this->address_space_start; }
|
||||||
KProcessAddress GetHeapRegionStart() const { return this->heap_region_start; }
|
KProcessAddress GetHeapRegionStart() const { return this->heap_region_start; }
|
||||||
KProcessAddress GetAliasRegionStart() const { return this->alias_region_start; }
|
KProcessAddress GetAliasRegionStart() const { return this->alias_region_start; }
|
||||||
KProcessAddress GetStackRegionStart() const { return this->stack_region_start; }
|
KProcessAddress GetStackRegionStart() const { return this->stack_region_start; }
|
||||||
KProcessAddress GetKernelMapRegionStart() const { return this->kernel_map_region_start; }
|
KProcessAddress GetKernelMapRegionStart() const { return this->kernel_map_region_start; }
|
||||||
|
KProcessAddress GetAliasCodeRegionStart() const { return this->alias_code_region_start; }
|
||||||
|
|
||||||
size_t GetAddressSpaceSize() const { return this->address_space_end - this->address_space_start; }
|
size_t GetAddressSpaceSize() const { return this->address_space_end - this->address_space_start; }
|
||||||
size_t GetHeapRegionSize() const { return this->heap_region_end - this->heap_region_start; }
|
size_t GetHeapRegionSize() const { return this->heap_region_end - this->heap_region_start; }
|
||||||
size_t GetAliasRegionSize() const { return this->alias_region_end - this->alias_region_start; }
|
size_t GetAliasRegionSize() const { return this->alias_region_end - this->alias_region_start; }
|
||||||
size_t GetStackRegionSize() const { return this->stack_region_end - this->stack_region_start; }
|
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 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; }
|
||||||
public:
|
public:
|
||||||
static ALWAYS_INLINE KVirtualAddress GetLinearVirtualAddress(KPhysicalAddress addr) {
|
static ALWAYS_INLINE KVirtualAddress GetLinearVirtualAddress(KPhysicalAddress addr) {
|
||||||
return KMemoryLayout::GetLinearVirtualAddress(addr);
|
return KMemoryLayout::GetLinearVirtualAddress(addr);
|
||||||
|
|||||||
@@ -16,14 +16,46 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <mesosphere/kern_common.hpp>
|
#include <mesosphere/kern_common.hpp>
|
||||||
#include <mesosphere/kern_k_synchronization_object.hpp>
|
#include <mesosphere/kern_k_synchronization_object.hpp>
|
||||||
|
#include <mesosphere/kern_k_client_port.hpp>
|
||||||
|
#include <mesosphere/kern_k_server_port.hpp>
|
||||||
#include <mesosphere/kern_slab_helpers.hpp>
|
#include <mesosphere/kern_slab_helpers.hpp>
|
||||||
|
|
||||||
namespace ams::kern {
|
namespace ams::kern {
|
||||||
|
|
||||||
class KPort final : public KAutoObjectWithSlabHeapAndContainer<KPort, KAutoObjectWithList> {
|
class KPort final : public KAutoObjectWithSlabHeapAndContainer<KPort, KAutoObjectWithList> {
|
||||||
MESOSPHERE_AUTOOBJECT_TRAITS(KPort, KAutoObject);
|
MESOSPHERE_AUTOOBJECT_TRAITS(KPort, KAutoObject);
|
||||||
|
private:
|
||||||
|
enum class State : u8 {
|
||||||
|
Invalid = 0,
|
||||||
|
Normal = 1,
|
||||||
|
ClientClosed = 2,
|
||||||
|
ServerClosed = 3,
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
KServerPort server;
|
||||||
|
KClientPort client;
|
||||||
|
uintptr_t name;
|
||||||
|
State state;
|
||||||
|
bool is_light;
|
||||||
public:
|
public:
|
||||||
/* TODO: This is a placeholder definition. */
|
constexpr KPort() : server(), client(), name(), state(State::Invalid), is_light() { /* ... */ }
|
||||||
|
virtual ~KPort() { /* ... */ }
|
||||||
|
|
||||||
|
static void PostDestroy(uintptr_t arg) { /* ... */ }
|
||||||
|
|
||||||
|
void Initialize(s32 max_sessions, bool is_light, uintptr_t name);
|
||||||
|
void OnClientClosed();
|
||||||
|
void OnServerClosed();
|
||||||
|
|
||||||
|
uintptr_t GetName() const { return this->name; }
|
||||||
|
bool IsLight() const { return this->is_light; }
|
||||||
|
|
||||||
|
/* TODO: More of KPort */
|
||||||
|
|
||||||
|
KClientPort &GetClientPort() { return this->client; }
|
||||||
|
KServerPort &GetServerPort() { return this->server; }
|
||||||
|
const KClientPort &GetClientPort() const { return this->client; }
|
||||||
|
const KServerPort &GetServerPort() const { return this->server; }
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <mesosphere/kern_common.hpp>
|
||||||
|
#include <mesosphere/kern_k_synchronization_object.hpp>
|
||||||
|
#include <mesosphere/kern_slab_helpers.hpp>
|
||||||
|
|
||||||
|
namespace ams::kern {
|
||||||
|
|
||||||
|
class KPort;
|
||||||
|
class KServerSession;
|
||||||
|
class KLightServerSession;
|
||||||
|
|
||||||
|
class KServerPort final : public KSynchronizationObject {
|
||||||
|
MESOSPHERE_AUTOOBJECT_TRAITS(KServerPort, KSynchronizationObject);
|
||||||
|
private:
|
||||||
|
using SessionList = util::IntrusiveListBaseTraits<KServerSession>::ListType;
|
||||||
|
using LightSessionList = util::IntrusiveListBaseTraits<KLightServerSession>::ListType;
|
||||||
|
private:
|
||||||
|
SessionList session_list;
|
||||||
|
LightSessionList light_session_list;
|
||||||
|
KPort *parent;
|
||||||
|
public:
|
||||||
|
constexpr KServerPort() : session_list(), light_session_list(), parent() { /* ... */ }
|
||||||
|
virtual ~KServerPort() { /* ... */ }
|
||||||
|
|
||||||
|
void Initialize(KPort *parent);
|
||||||
|
|
||||||
|
constexpr const KPort *GetParent() const { return this->parent; }
|
||||||
|
|
||||||
|
bool IsLight() const;
|
||||||
|
|
||||||
|
/* 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. */
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <mesosphere/kern_common.hpp>
|
||||||
|
#include <mesosphere/kern_k_synchronization_object.hpp>
|
||||||
|
#include <mesosphere/kern_k_session_request.hpp>
|
||||||
|
#include <mesosphere/kern_k_light_lock.hpp>
|
||||||
|
|
||||||
|
namespace ams::kern {
|
||||||
|
|
||||||
|
class KSession;
|
||||||
|
|
||||||
|
class KServerSession final : public KSynchronizationObject, public util::IntrusiveListBaseNode<KServerSession> {
|
||||||
|
MESOSPHERE_AUTOOBJECT_TRAITS(KServerSession, KSynchronizationObject);
|
||||||
|
private:
|
||||||
|
using RequestList = util::IntrusiveListBaseTraits<KSessionRequest>::ListType;
|
||||||
|
private:
|
||||||
|
KSession *parent;
|
||||||
|
RequestList request_list;
|
||||||
|
KSessionRequest *current_request;
|
||||||
|
KLightLock lock;
|
||||||
|
public:
|
||||||
|
constexpr KServerSession() : parent(), request_list(), current_request(), lock() { /* ... */ }
|
||||||
|
virtual ~KServerSession() { /* ... */ }
|
||||||
|
|
||||||
|
void Initialize(KSession *parent);
|
||||||
|
|
||||||
|
constexpr const KSession *GetParent() const { return this->parent; }
|
||||||
|
|
||||||
|
virtual bool IsSignaled() const override { MESOSPHERE_UNIMPLEMENTED(); }
|
||||||
|
|
||||||
|
/* TODO: More of KServerSession. */
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -16,14 +16,52 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <mesosphere/kern_common.hpp>
|
#include <mesosphere/kern_common.hpp>
|
||||||
#include <mesosphere/kern_k_synchronization_object.hpp>
|
#include <mesosphere/kern_k_synchronization_object.hpp>
|
||||||
|
#include <mesosphere/kern_k_server_session.hpp>
|
||||||
|
#include <mesosphere/kern_k_client_session.hpp>
|
||||||
#include <mesosphere/kern_slab_helpers.hpp>
|
#include <mesosphere/kern_slab_helpers.hpp>
|
||||||
|
|
||||||
namespace ams::kern {
|
namespace ams::kern {
|
||||||
|
|
||||||
|
class KClientPort;
|
||||||
|
class KProcess;
|
||||||
|
|
||||||
class KSession final : public KAutoObjectWithSlabHeapAndContainer<KSession, KAutoObjectWithList> {
|
class KSession final : public KAutoObjectWithSlabHeapAndContainer<KSession, KAutoObjectWithList> {
|
||||||
MESOSPHERE_AUTOOBJECT_TRAITS(KSession, KAutoObject);
|
MESOSPHERE_AUTOOBJECT_TRAITS(KSession, KAutoObject);
|
||||||
|
private:
|
||||||
|
enum class State : u8 {
|
||||||
|
Invalid = 0,
|
||||||
|
Normal = 1,
|
||||||
|
ClientClosed = 2,
|
||||||
|
ServerClosed = 3,
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
KServerSession server;
|
||||||
|
KClientSession client;
|
||||||
|
State state;
|
||||||
|
KClientPort *port;
|
||||||
|
uintptr_t name;
|
||||||
|
KProcess *process;
|
||||||
|
bool initialized;
|
||||||
public:
|
public:
|
||||||
|
constexpr KSession()
|
||||||
|
: server(), client(), state(State::Invalid), port(), name(), process(), initialized()
|
||||||
|
{
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~KSession() { /* ... */ }
|
||||||
|
|
||||||
|
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. */
|
/* TODO: This is a placeholder definition. */
|
||||||
|
|
||||||
|
KClientSession &GetClientSession() { return this->client; }
|
||||||
|
KServerSession &GetServerSession() { return this->server; }
|
||||||
|
const KClientSession &GetClientSession() const { return this->client; }
|
||||||
|
const KServerSession &GetServerSession() const { return this->server; }
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ namespace ams::kern {
|
|||||||
#define MESOSPHERE_UNUSED(...) ::ams::kern::UnusedImpl(__VA_ARGS__)
|
#define MESOSPHERE_UNUSED(...) ::ams::kern::UnusedImpl(__VA_ARGS__)
|
||||||
|
|
||||||
#ifdef MESOSPHERE_ENABLE_DEBUG_PRINT
|
#ifdef MESOSPHERE_ENABLE_DEBUG_PRINT
|
||||||
#define MESOSPHERE_PANIC(...) do { ::ams::kern::Panic(__FILE__, __LINE__, __VA_ARGS__); } while(0)
|
#define MESOSPHERE_PANIC(...) do { ::ams::kern::Panic(__FILE__, __LINE__, ## __VA_ARGS__); } while(0)
|
||||||
#else
|
#else
|
||||||
#define MESOSPHERE_PANIC(...) do { MESOSPHERE_UNUSED(__VA_ARGS__); ::ams::kern::Panic(); } while(0)
|
#define MESOSPHERE_PANIC(...) do { MESOSPHERE_UNUSED(__VA_ARGS__); ::ams::kern::Panic(); } while(0)
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -150,6 +150,15 @@ namespace ams::kern::arch::arm64 {
|
|||||||
HandleUserException(context, esr, far, afsr0, afsr1, data);
|
HandleUserException(context, esr, far, afsr0, afsr1, data);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
MESOSPHERE_LOG("Unhandled Exception in Supervisor Mode\n");
|
||||||
|
MESOSPHERE_LOG("Current Process = %s\n", GetCurrentProcess().GetName());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 31; i++) {
|
||||||
|
MESOSPHERE_LOG("X[%02zu] = %016lx\n", i, context->x[i]);
|
||||||
|
}
|
||||||
|
MESOSPHERE_LOG("PC = %016lx\n", context->pc);
|
||||||
|
MESOSPHERE_LOG("SP = %016lx\n", context->sp);
|
||||||
|
|
||||||
MESOSPHERE_PANIC("Unhandled Exception in Supervisor Mode\n");
|
MESOSPHERE_PANIC("Unhandled Exception in Supervisor Mode\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -262,8 +262,8 @@ namespace ams::kern::arch::arm64 {
|
|||||||
R_UNLESS(entry.handler != nullptr, svc::ResultInvalidState());
|
R_UNLESS(entry.handler != nullptr, svc::ResultInvalidState());
|
||||||
|
|
||||||
/* If auto-cleared, we can succeed immediately. */
|
/* If auto-cleared, we can succeed immediately. */
|
||||||
R_UNLESS(entry.manually_cleared, ResultSuccess());
|
R_SUCCEED_IF(!entry.manually_cleared);
|
||||||
R_UNLESS(entry.needs_clear, ResultSuccess());
|
R_SUCCEED_IF(!entry.needs_clear);
|
||||||
|
|
||||||
/* Clear and enable. */
|
/* Clear and enable. */
|
||||||
entry.needs_clear = false;
|
entry.needs_clear = false;
|
||||||
@@ -277,8 +277,8 @@ namespace ams::kern::arch::arm64 {
|
|||||||
R_UNLESS(entry.handler != nullptr, svc::ResultInvalidState());
|
R_UNLESS(entry.handler != nullptr, svc::ResultInvalidState());
|
||||||
|
|
||||||
/* If auto-cleared, we can succeed immediately. */
|
/* If auto-cleared, we can succeed immediately. */
|
||||||
R_UNLESS(entry.manually_cleared, ResultSuccess());
|
R_SUCCEED_IF(!entry.manually_cleared);
|
||||||
R_UNLESS(entry.needs_clear, ResultSuccess());
|
R_SUCCEED_IF(!entry.needs_clear);
|
||||||
|
|
||||||
/* Clear and set priority. */
|
/* Clear and set priority. */
|
||||||
entry.needs_clear = false;
|
entry.needs_clear = false;
|
||||||
|
|||||||
@@ -832,7 +832,7 @@ namespace ams::kern::arch::arm64 {
|
|||||||
L1PageTableEntry *l1_entry = impl.GetL1Entry(virt_addr);
|
L1PageTableEntry *l1_entry = impl.GetL1Entry(virt_addr);
|
||||||
if (l1_entry->IsBlock()) {
|
if (l1_entry->IsBlock()) {
|
||||||
/* If our block size is too big, don't bother. */
|
/* If our block size is too big, don't bother. */
|
||||||
R_UNLESS(block_size < L1BlockSize, ResultSuccess());
|
R_SUCCEED_IF(block_size >= L1BlockSize);
|
||||||
|
|
||||||
/* Get the addresses we're working with. */
|
/* Get the addresses we're working with. */
|
||||||
const KProcessAddress block_virt_addr = util::AlignDown(GetInteger(virt_addr), L1BlockSize);
|
const KProcessAddress block_virt_addr = util::AlignDown(GetInteger(virt_addr), L1BlockSize);
|
||||||
@@ -859,10 +859,10 @@ namespace ams::kern::arch::arm64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* If we don't have an l1 table, we're done. */
|
/* If we don't have an l1 table, we're done. */
|
||||||
R_UNLESS(l1_entry->IsTable(), ResultSuccess());
|
R_SUCCEED_IF(!l1_entry->IsTable());
|
||||||
|
|
||||||
/* We want to separate L2 contiguous blocks into L2 blocks, so check that our size permits that. */
|
/* We want to separate L2 contiguous blocks into L2 blocks, so check that our size permits that. */
|
||||||
R_UNLESS(block_size < L2ContiguousBlockSize, ResultSuccess());
|
R_SUCCEED_IF(block_size >= L2ContiguousBlockSize);
|
||||||
|
|
||||||
L2PageTableEntry *l2_entry = impl.GetL2Entry(l1_entry, virt_addr);
|
L2PageTableEntry *l2_entry = impl.GetL2Entry(l1_entry, virt_addr);
|
||||||
if (l2_entry->IsBlock()) {
|
if (l2_entry->IsBlock()) {
|
||||||
@@ -878,7 +878,7 @@ namespace ams::kern::arch::arm64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* We want to separate L2 blocks into L3 contiguous blocks, so check that our size permits that. */
|
/* We want to separate L2 blocks into L3 contiguous blocks, so check that our size permits that. */
|
||||||
R_UNLESS(block_size < L2BlockSize, ResultSuccess());
|
R_SUCCEED_IF(block_size >= L2BlockSize);
|
||||||
|
|
||||||
/* Get the addresses we're working with. */
|
/* Get the addresses we're working with. */
|
||||||
const KProcessAddress block_virt_addr = util::AlignDown(GetInteger(virt_addr), L2BlockSize);
|
const KProcessAddress block_virt_addr = util::AlignDown(GetInteger(virt_addr), L2BlockSize);
|
||||||
@@ -905,10 +905,10 @@ namespace ams::kern::arch::arm64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* If we don't have an L3 table, we're done. */
|
/* If we don't have an L3 table, we're done. */
|
||||||
R_UNLESS(l2_entry->IsTable(), ResultSuccess());
|
R_SUCCEED_IF(!l2_entry->IsTable());
|
||||||
|
|
||||||
/* We want to separate L3 contiguous blocks into L2 blocks, so check that our size permits that. */
|
/* We want to separate L3 contiguous blocks into L2 blocks, so check that our size permits that. */
|
||||||
R_UNLESS(block_size < L3ContiguousBlockSize, ResultSuccess());
|
R_SUCCEED_IF(block_size >= L3ContiguousBlockSize);
|
||||||
|
|
||||||
/* If we're contiguous, try to separate. */
|
/* If we're contiguous, try to separate. */
|
||||||
L3PageTableEntry *l3_entry = impl.GetL3Entry(l2_entry, virt_addr);
|
L3PageTableEntry *l3_entry = impl.GetL3Entry(l2_entry, virt_addr);
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
const WordType clear_bit = (this->state[i] + 1) ^ (this->state[i]);
|
const WordType clear_bit = (this->state[i] + 1) ^ (this->state[i]);
|
||||||
this->state[i] |= clear_bit;
|
this->state[i] |= clear_bit;
|
||||||
out[num_reserved++] = static_cast<u8>(BitsPerWord * i + BitsPerWord - 1 - ClearLeadingZero(clear_bit));
|
out[num_reserved++] = static_cast<u8>(BitsPerWord * i + BitsPerWord - 1 - ClearLeadingZero(clear_bit));
|
||||||
R_UNLESS(num_reserved != num_desired, ResultSuccess());
|
R_SUCCEED_IF(num_reserved == num_desired);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -327,4 +327,53 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
while (true) { /* ... */ }
|
while (true) { /* ... */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* User access. */
|
||||||
|
void KSystemControl::CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args) {
|
||||||
|
/* Get the function id for the current call. */
|
||||||
|
u64 function_id = args->r[0];
|
||||||
|
|
||||||
|
MESOSPHERE_LOG("CallSecureMonitor(%lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx);\n", args->r[0], args->r[1], args->r[2], args->r[3], args->r[4], args->r[5], args->r[6], args->r[7]);
|
||||||
|
|
||||||
|
/* We'll need to map in pages if arguments are pointers. Prepare page groups to do so. */
|
||||||
|
auto &page_table = GetCurrentProcess().GetPageTable();
|
||||||
|
auto *bim = page_table.GetBlockInfoManager();
|
||||||
|
|
||||||
|
constexpr size_t MaxMappedRegisters = 7;
|
||||||
|
std::array<KPageGroup, MaxMappedRegisters> page_groups = { KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), };
|
||||||
|
|
||||||
|
for (size_t i = 0; i < MaxMappedRegisters; i++) {
|
||||||
|
const size_t reg_id = i + 1;
|
||||||
|
if (function_id & (1ul << (8 + reg_id))) {
|
||||||
|
/* Create and open a new page group for the address. */
|
||||||
|
KVirtualAddress virt_addr = args->r[reg_id];
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(page_table.MakeAndOpenPageGroup(std::addressof(page_groups[i]), util::AlignDown(GetInteger(virt_addr), PageSize), 1, KMemoryState_None, KMemoryState_None, KMemoryPermission_UserReadWrite, KMemoryPermission_UserReadWrite, KMemoryAttribute_None, KMemoryAttribute_None))) {
|
||||||
|
/* Translate the virtual address to a physical address. */
|
||||||
|
const auto it = page_groups[i].begin();
|
||||||
|
MESOSPHERE_ASSERT(it != page_groups[i].end());
|
||||||
|
MESOSPHERE_ASSERT(it->GetNumPages() == 1);
|
||||||
|
|
||||||
|
KPhysicalAddress phys_addr = page_table.GetHeapPhysicalAddress(it->GetAddress());
|
||||||
|
|
||||||
|
args->r[reg_id] = GetInteger(phys_addr) | (GetInteger(virt_addr) & (PageSize - 1));
|
||||||
|
MESOSPHERE_LOG("Mapped arg %zu\n", reg_id);
|
||||||
|
} else {
|
||||||
|
/* If we couldn't map, we should clear the address. */
|
||||||
|
MESOSPHERE_LOG("Failed to map arg %zu\n", reg_id);
|
||||||
|
args->r[reg_id] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Invoke the secure monitor. */
|
||||||
|
smc::CallSecureMonitorFromUser(args);
|
||||||
|
|
||||||
|
MESOSPHERE_LOG("Secure Monitor Returned: (%lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx);\n", args->r[0], args->r[1], args->r[2], args->r[3], args->r[4], args->r[5], args->r[6], args->r[7]);
|
||||||
|
|
||||||
|
/* Make sure that we close any pages that we opened. */
|
||||||
|
for (size_t i = 0; i < MaxMappedRegisters; i++) {
|
||||||
|
page_groups[i].Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -71,6 +71,42 @@ namespace ams::kern::board::nintendo::nx::smc {
|
|||||||
args.x[7] = x7;
|
args.x[7] = x7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CallUserSecureMonitorFunction(ams::svc::lp64::SecureMonitorArguments *args) {
|
||||||
|
/* Load arguments into registers. */
|
||||||
|
register u64 x0 asm("x0") = args->r[0];
|
||||||
|
register u64 x1 asm("x1") = args->r[1];
|
||||||
|
register u64 x2 asm("x2") = args->r[2];
|
||||||
|
register u64 x3 asm("x3") = args->r[3];
|
||||||
|
register u64 x4 asm("x4") = args->r[4];
|
||||||
|
register u64 x5 asm("x5") = args->r[5];
|
||||||
|
register u64 x6 asm("x6") = args->r[6];
|
||||||
|
register u64 x7 asm("x7") = args->r[7];
|
||||||
|
|
||||||
|
/* Actually make the call. */
|
||||||
|
{
|
||||||
|
/* Disable interrupts while making the call. */
|
||||||
|
KScopedInterruptDisable intr_disable;
|
||||||
|
__asm__ __volatile__("smc #0"
|
||||||
|
: "+r"(x0), "+r"(x1), "+r"(x2), "+r"(x3), "+r"(x4), "+r"(x5), "+r"(x6), "+r"(x7)
|
||||||
|
:
|
||||||
|
: "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "cc", "memory"
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Restore the CoreLocalRegion into X18. */
|
||||||
|
cpu::SetCoreLocalRegionAddress(cpu::GetTpidrEl1());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store arguments to output. */
|
||||||
|
args->r[0] = x0;
|
||||||
|
args->r[1] = x1;
|
||||||
|
args->r[2] = x2;
|
||||||
|
args->r[3] = x3;
|
||||||
|
args->r[4] = x4;
|
||||||
|
args->r[5] = x5;
|
||||||
|
args->r[6] = x6;
|
||||||
|
args->r[7] = x7;
|
||||||
|
}
|
||||||
|
|
||||||
void CallPrivilegedSecureMonitorFunctionForInit(SecureMonitorArguments &args) {
|
void CallPrivilegedSecureMonitorFunctionForInit(SecureMonitorArguments &args) {
|
||||||
/* Load arguments into registers. */
|
/* Load arguments into registers. */
|
||||||
register u64 x0 asm("x0") = args.x[0];
|
register u64 x0 asm("x0") = args.x[0];
|
||||||
@@ -188,4 +224,8 @@ namespace ams::kern::board::nintendo::nx::smc {
|
|||||||
while (true) { /* ... */ }
|
while (true) { /* ... */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args) {
|
||||||
|
CallUserSecureMonitorFunction(args);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -91,6 +91,8 @@ namespace ams::kern::board::nintendo::nx::smc {
|
|||||||
|
|
||||||
void NORETURN Panic(u32 color);
|
void NORETURN Panic(u32 color);
|
||||||
|
|
||||||
|
void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args);
|
||||||
|
|
||||||
namespace init {
|
namespace init {
|
||||||
|
|
||||||
void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg);
|
void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg);
|
||||||
|
|||||||
@@ -211,7 +211,9 @@ namespace ams::kern {
|
|||||||
/* Validate this is a capability we can act on. */
|
/* Validate this is a capability we can act on. */
|
||||||
const auto type = GetCapabilityType(cap);
|
const auto type = GetCapabilityType(cap);
|
||||||
R_UNLESS(type != CapabilityType::Invalid, svc::ResultInvalidArgument());
|
R_UNLESS(type != CapabilityType::Invalid, svc::ResultInvalidArgument());
|
||||||
R_UNLESS(type != CapabilityType::Padding, ResultSuccess());
|
|
||||||
|
/* If the type is padding, we have no work to do. */
|
||||||
|
R_SUCCEED_IF(type == CapabilityType::Padding);
|
||||||
|
|
||||||
/* Check that we haven't already processed this capability. */
|
/* Check that we haven't already processed this capability. */
|
||||||
const auto flag = GetCapabilityFlag(type);
|
const auto flag = GetCapabilityFlag(type);
|
||||||
|
|||||||
46
libraries/libmesosphere/source/kern_k_client_port.cpp
Normal file
46
libraries/libmesosphere/source/kern_k_client_port.cpp
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <mesosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::kern {
|
||||||
|
|
||||||
|
void KClientPort::Initialize(KPort *parent, s32 max_sessions) {
|
||||||
|
/* Set member variables. */
|
||||||
|
this->num_sessions = 0;
|
||||||
|
this->peak_sessions = 0;
|
||||||
|
this->parent = parent;
|
||||||
|
this->max_sessions = max_sessions;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KClientPort::IsLight() const {
|
||||||
|
return this->GetParent()->IsLight();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KClientPort::Destroy() {
|
||||||
|
/* Note with our parent that we're closed. */
|
||||||
|
this->parent->OnClientClosed();
|
||||||
|
|
||||||
|
/* Close our reference to our parent. */
|
||||||
|
this->parent->Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KClientPort::IsSignaled() const {
|
||||||
|
/* TODO: Check preconditions later. */
|
||||||
|
MESOSPHERE_ASSERT_THIS();
|
||||||
|
return this->num_sessions < this->max_sessions;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
107
libraries/libmesosphere/source/kern_k_object_name.cpp
Normal file
107
libraries/libmesosphere/source/kern_k_object_name.cpp
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* 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 <mesosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::kern {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/* TODO: C++20 constinit */
|
||||||
|
KLightLock g_object_list_lock;
|
||||||
|
KObjectName::List g_object_list;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void KObjectName::Initialize(KAutoObject *obj, const char *name) {
|
||||||
|
/* Set member variables. */
|
||||||
|
this->object = obj;
|
||||||
|
std::strncpy(this->name, name, sizeof(this->name));
|
||||||
|
this->name[sizeof(this->name) - 1] = '\x00';
|
||||||
|
|
||||||
|
/* Open a reference to the object we hold. */
|
||||||
|
this->object->Open();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KObjectName::MatchesName(const char *name) const {
|
||||||
|
return std::strncmp(this->name, name, sizeof(this->name)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result KObjectName::NewFromName(KAutoObject *obj, const char *name) {
|
||||||
|
/* Create a new object name. */
|
||||||
|
KObjectName *new_name = KObjectName::Allocate();
|
||||||
|
R_UNLESS(new_name != nullptr, svc::ResultOutOfResource());
|
||||||
|
|
||||||
|
/* Initialize the new name. */
|
||||||
|
new_name->Initialize(obj, name);
|
||||||
|
|
||||||
|
/* Check if there's an existing name. */
|
||||||
|
{
|
||||||
|
/* Ensure we have exclusive access to the global list. */
|
||||||
|
KScopedLightLock lk(g_object_list_lock);
|
||||||
|
|
||||||
|
/* If the object doesn't exist, put it into the list. */
|
||||||
|
KScopedAutoObject existing_object = FindImpl(name);
|
||||||
|
if (existing_object.IsNull()) {
|
||||||
|
g_object_list.push_back(*new_name);
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The object already exists, which is an error condition. Perform cleanup. */
|
||||||
|
obj->Close();
|
||||||
|
KObjectName::Free(new_name);
|
||||||
|
return svc::ResultInvalidState();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result KObjectName::Delete(KAutoObject *obj, const char *compare_name) {
|
||||||
|
/* Ensure we have exclusive access to the global list. */
|
||||||
|
KScopedLightLock lk(g_object_list_lock);
|
||||||
|
|
||||||
|
/* Find a matching entry in the list, and delete it. */
|
||||||
|
for (auto &name : g_object_list) {
|
||||||
|
if (name.MatchesName(compare_name) && obj == name.GetObject()) {
|
||||||
|
/* We found a match, clean up its resources. */
|
||||||
|
obj->Close();
|
||||||
|
g_object_list.erase(g_object_list.iterator_to(name));
|
||||||
|
KObjectName::Free(std::addressof(name));
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We didn't find the object in the list. */
|
||||||
|
return svc::ResultNotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
KScopedAutoObject<KAutoObject> KObjectName::Find(const char *name) {
|
||||||
|
/* Ensure we have exclusive access to the global list. */
|
||||||
|
KScopedLightLock lk(g_object_list_lock);
|
||||||
|
|
||||||
|
return FindImpl(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
KScopedAutoObject<KAutoObject> KObjectName::FindImpl(const char *compare_name) {
|
||||||
|
/* Try to find a matching object in the global list. */
|
||||||
|
for (const auto &name : g_object_list) {
|
||||||
|
if (name.MatchesName(compare_name)) {
|
||||||
|
return name.GetObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* There's no matching entry in the list. */
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -38,7 +38,7 @@ namespace ams::kern {
|
|||||||
|
|
||||||
Result KPageGroup::AddBlock(KVirtualAddress addr, size_t num_pages) {
|
Result KPageGroup::AddBlock(KVirtualAddress addr, size_t num_pages) {
|
||||||
/* Succeed immediately if we're adding no pages. */
|
/* Succeed immediately if we're adding no pages. */
|
||||||
R_UNLESS(num_pages != 0, ResultSuccess());
|
R_SUCCEED_IF(num_pages == 0);
|
||||||
|
|
||||||
/* Check for overflow. */
|
/* Check for overflow. */
|
||||||
MESOSPHERE_ASSERT(addr < addr + num_pages * PageSize);
|
MESOSPHERE_ASSERT(addr < addr + num_pages * PageSize);
|
||||||
@@ -46,7 +46,7 @@ namespace ams::kern {
|
|||||||
/* Try to just append to the last block. */
|
/* Try to just append to the last block. */
|
||||||
if (!this->block_list.empty()) {
|
if (!this->block_list.empty()) {
|
||||||
auto it = --(this->block_list.end());
|
auto it = --(this->block_list.end());
|
||||||
R_UNLESS(!it->TryConcatenate(addr, num_pages), ResultSuccess());
|
R_SUCCEED_IF(it->TryConcatenate(addr, num_pages));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate a new block. */
|
/* Allocate a new block. */
|
||||||
|
|||||||
@@ -1020,4 +1020,27 @@ namespace ams::kern {
|
|||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result KPageTableBase::MakeAndOpenPageGroup(KPageGroup *out, KProcessAddress address, size_t num_pages, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr) {
|
||||||
|
/* Ensure that the page group isn't null. */
|
||||||
|
AMS_ASSERT(out != nullptr);
|
||||||
|
|
||||||
|
/* Make sure that the region we're mapping is valid for the table. */
|
||||||
|
const size_t size = num_pages * PageSize;
|
||||||
|
R_UNLESS(this->Contains(address, size), svc::ResultInvalidCurrentMemory());
|
||||||
|
|
||||||
|
/* Lock the table. */
|
||||||
|
KScopedLightLock lk(this->general_lock);
|
||||||
|
|
||||||
|
/* Check if state allows us to create the group. */
|
||||||
|
R_TRY(this->CheckMemoryState(address, size, state_mask | KMemoryState_FlagReferenceCounted, state | KMemoryState_FlagReferenceCounted, perm_mask, perm, attr_mask, attr));
|
||||||
|
|
||||||
|
/* Create a new page group for the region. */
|
||||||
|
R_TRY(this->MakePageGroup(*out, address, num_pages));
|
||||||
|
|
||||||
|
/* Open a new reference to the pages in the group. */
|
||||||
|
out->Open();
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
44
libraries/libmesosphere/source/kern_k_port.cpp
Normal file
44
libraries/libmesosphere/source/kern_k_port.cpp
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <mesosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::kern {
|
||||||
|
|
||||||
|
void KPort::Initialize(s32 max_sessions, bool is_light, uintptr_t name) {
|
||||||
|
/* Open a new reference count to the initialized port. */
|
||||||
|
this->Open();
|
||||||
|
|
||||||
|
/* Create and initialize our server/client pair. */
|
||||||
|
KAutoObject::Create(std::addressof(this->server));
|
||||||
|
KAutoObject::Create(std::addressof(this->client));
|
||||||
|
this->server.Initialize(this);
|
||||||
|
this->client.Initialize(this, max_sessions);
|
||||||
|
|
||||||
|
/* Set our member variables. */
|
||||||
|
this->is_light = is_light;
|
||||||
|
this->name = name;
|
||||||
|
this->state = State::Normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KPort::OnClientClosed() {
|
||||||
|
MESOSPHERE_UNIMPLEMENTED();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KPort::OnServerClosed() {
|
||||||
|
MESOSPHERE_UNIMPLEMENTED();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
96
libraries/libmesosphere/source/kern_k_server_port.cpp
Normal file
96
libraries/libmesosphere/source/kern_k_server_port.cpp
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* 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 <mesosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::kern {
|
||||||
|
|
||||||
|
void KServerPort::Initialize(KPort *parent) {
|
||||||
|
/* Set member variables. */
|
||||||
|
this->parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KServerPort::IsLight() const {
|
||||||
|
return this->GetParent()->IsLight();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KServerPort::CleanupSessions() {
|
||||||
|
/* Ensure our preconditions are met. */
|
||||||
|
MESOSPHERE_ASSERT(this->IsLight() || this->session_list.empty());
|
||||||
|
MESOSPHERE_ASSERT(!this->IsLight() || this->light_session_list.empty());
|
||||||
|
|
||||||
|
/* Cleanup the session list. */
|
||||||
|
while (true) {
|
||||||
|
/* Get the last session in the list */
|
||||||
|
KServerSession *session = nullptr;
|
||||||
|
{
|
||||||
|
KScopedSchedulerLock sl;
|
||||||
|
while (!this->session_list.empty()) {
|
||||||
|
session = std::addressof(this->session_list.front());
|
||||||
|
this->session_list.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close the session. */
|
||||||
|
if (session != nullptr) {
|
||||||
|
session->Close();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cleanup the light session list. */
|
||||||
|
while (true) {
|
||||||
|
/* Get the last session in the list */
|
||||||
|
KLightServerSession *session = nullptr;
|
||||||
|
{
|
||||||
|
KScopedSchedulerLock sl;
|
||||||
|
while (!this->light_session_list.empty()) {
|
||||||
|
session = std::addressof(this->light_session_list.front());
|
||||||
|
this->light_session_list.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close the session. */
|
||||||
|
if (session != nullptr) {
|
||||||
|
session->Close();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void KServerPort::Destroy() {
|
||||||
|
/* Note with our parent that we're closed. */
|
||||||
|
this->parent->OnClientClosed();
|
||||||
|
|
||||||
|
/* Perform necessary cleanup of our session lists. */
|
||||||
|
this->CleanupSessions();
|
||||||
|
|
||||||
|
/* Close our reference to our parent. */
|
||||||
|
this->parent->Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KServerPort::IsSignaled() const {
|
||||||
|
/* TODO: Check preconditions later. */
|
||||||
|
MESOSPHERE_ASSERT_THIS();
|
||||||
|
if (this->IsLight()) {
|
||||||
|
return !this->light_session_list.empty();
|
||||||
|
} else {
|
||||||
|
return this->session_list.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
31
libraries/libmesosphere/source/libc/arch/arm64/asmdefs.h
Normal file
31
libraries/libmesosphere/source/libc/arch/arm64/asmdefs.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Macros for asm code.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019, Arm Limited.
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ASMDEFS_H
|
||||||
|
#define _ASMDEFS_H
|
||||||
|
|
||||||
|
#define ENTRY_ALIGN(name, alignment) \
|
||||||
|
.global name; \
|
||||||
|
.type name,%function; \
|
||||||
|
.align alignment; \
|
||||||
|
name: \
|
||||||
|
.cfi_startproc;
|
||||||
|
|
||||||
|
#define ENTRY(name) ENTRY_ALIGN(name, 6)
|
||||||
|
|
||||||
|
#define ENTRY_ALIAS(name) \
|
||||||
|
.global name; \
|
||||||
|
.type name,%function; \
|
||||||
|
name:
|
||||||
|
|
||||||
|
#define END(name) \
|
||||||
|
.cfi_endproc; \
|
||||||
|
.size name, .-name;
|
||||||
|
|
||||||
|
#define L(l) .L ## l
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
/* memcmp - compare memory
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013, Arm Limited.
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Assumptions:
|
||||||
|
*
|
||||||
|
* ARMv8-a, AArch64, unaligned accesses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "asmdefs.h"
|
||||||
|
|
||||||
|
/* Parameters and result. */
|
||||||
|
#define src1 x0
|
||||||
|
#define src2 x1
|
||||||
|
#define limit x2
|
||||||
|
#define result w0
|
||||||
|
|
||||||
|
/* Internal variables. */
|
||||||
|
#define data1 x3
|
||||||
|
#define data1w w3
|
||||||
|
#define data1h x4
|
||||||
|
#define data2 x5
|
||||||
|
#define data2w w5
|
||||||
|
#define data2h x6
|
||||||
|
#define tmp1 x7
|
||||||
|
#define tmp2 x8
|
||||||
|
|
||||||
|
ENTRY (memcmp)
|
||||||
|
subs limit, limit, 8
|
||||||
|
b.lo L(less8)
|
||||||
|
|
||||||
|
ldr data1, [src1], 8
|
||||||
|
ldr data2, [src2], 8
|
||||||
|
cmp data1, data2
|
||||||
|
b.ne L(return)
|
||||||
|
|
||||||
|
subs limit, limit, 8
|
||||||
|
b.gt L(more16)
|
||||||
|
|
||||||
|
ldr data1, [src1, limit]
|
||||||
|
ldr data2, [src2, limit]
|
||||||
|
b L(return)
|
||||||
|
|
||||||
|
L(more16):
|
||||||
|
ldr data1, [src1], 8
|
||||||
|
ldr data2, [src2], 8
|
||||||
|
cmp data1, data2
|
||||||
|
bne L(return)
|
||||||
|
|
||||||
|
/* Jump directly to comparing the last 16 bytes for 32 byte (or less)
|
||||||
|
strings. */
|
||||||
|
subs limit, limit, 16
|
||||||
|
b.ls L(last_bytes)
|
||||||
|
|
||||||
|
/* We overlap loads between 0-32 bytes at either side of SRC1 when we
|
||||||
|
try to align, so limit it only to strings larger than 128 bytes. */
|
||||||
|
cmp limit, 96
|
||||||
|
b.ls L(loop16)
|
||||||
|
|
||||||
|
/* Align src1 and adjust src2 with bytes not yet done. */
|
||||||
|
and tmp1, src1, 15
|
||||||
|
add limit, limit, tmp1
|
||||||
|
sub src1, src1, tmp1
|
||||||
|
sub src2, src2, tmp1
|
||||||
|
|
||||||
|
/* Loop performing 16 bytes per iteration using aligned src1.
|
||||||
|
Limit is pre-decremented by 16 and must be larger than zero.
|
||||||
|
Exit if <= 16 bytes left to do or if the data is not equal. */
|
||||||
|
.p2align 4
|
||||||
|
L(loop16):
|
||||||
|
ldp data1, data1h, [src1], 16
|
||||||
|
ldp data2, data2h, [src2], 16
|
||||||
|
subs limit, limit, 16
|
||||||
|
ccmp data1, data2, 0, hi
|
||||||
|
ccmp data1h, data2h, 0, eq
|
||||||
|
b.eq L(loop16)
|
||||||
|
|
||||||
|
cmp data1, data2
|
||||||
|
bne L(return)
|
||||||
|
mov data1, data1h
|
||||||
|
mov data2, data2h
|
||||||
|
cmp data1, data2
|
||||||
|
bne L(return)
|
||||||
|
|
||||||
|
/* Compare last 1-16 bytes using unaligned access. */
|
||||||
|
L(last_bytes):
|
||||||
|
add src1, src1, limit
|
||||||
|
add src2, src2, limit
|
||||||
|
ldp data1, data1h, [src1]
|
||||||
|
ldp data2, data2h, [src2]
|
||||||
|
cmp data1, data2
|
||||||
|
bne L(return)
|
||||||
|
mov data1, data1h
|
||||||
|
mov data2, data2h
|
||||||
|
cmp data1, data2
|
||||||
|
|
||||||
|
/* Compare data bytes and set return value to 0, -1 or 1. */
|
||||||
|
L(return):
|
||||||
|
#ifndef __AARCH64EB__
|
||||||
|
rev data1, data1
|
||||||
|
rev data2, data2
|
||||||
|
#endif
|
||||||
|
cmp data1, data2
|
||||||
|
L(ret_eq):
|
||||||
|
cset result, ne
|
||||||
|
cneg result, result, lo
|
||||||
|
ret
|
||||||
|
|
||||||
|
.p2align 4
|
||||||
|
/* Compare up to 8 bytes. Limit is [-8..-1]. */
|
||||||
|
L(less8):
|
||||||
|
adds limit, limit, 4
|
||||||
|
b.lo L(less4)
|
||||||
|
ldr data1w, [src1], 4
|
||||||
|
ldr data2w, [src2], 4
|
||||||
|
cmp data1w, data2w
|
||||||
|
b.ne L(return)
|
||||||
|
sub limit, limit, 4
|
||||||
|
L(less4):
|
||||||
|
adds limit, limit, 4
|
||||||
|
beq L(ret_eq)
|
||||||
|
L(byte_loop):
|
||||||
|
ldrb data1w, [src1], 1
|
||||||
|
ldrb data2w, [src2], 1
|
||||||
|
subs limit, limit, 1
|
||||||
|
ccmp data1w, data2w, 0, ne /* NZCV = 0b0000. */
|
||||||
|
b.eq L(byte_loop)
|
||||||
|
sub result, data1w, data2w
|
||||||
|
ret
|
||||||
|
|
||||||
|
END (memcmp)
|
||||||
@@ -0,0 +1,239 @@
|
|||||||
|
/*
|
||||||
|
* memcpy - copy memory area
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012-2020, Arm Limited.
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Assumptions:
|
||||||
|
*
|
||||||
|
* ARMv8-a, AArch64, unaligned accesses.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "asmdefs.h"
|
||||||
|
|
||||||
|
#define dstin x0
|
||||||
|
#define src x1
|
||||||
|
#define count x2
|
||||||
|
#define dst x3
|
||||||
|
#define srcend x4
|
||||||
|
#define dstend x5
|
||||||
|
#define A_l x6
|
||||||
|
#define A_lw w6
|
||||||
|
#define A_h x7
|
||||||
|
#define B_l x8
|
||||||
|
#define B_lw w8
|
||||||
|
#define B_h x9
|
||||||
|
#define C_l x10
|
||||||
|
#define C_lw w10
|
||||||
|
#define C_h x11
|
||||||
|
#define D_l x12
|
||||||
|
#define D_h x13
|
||||||
|
#define E_l x14
|
||||||
|
#define E_h x15
|
||||||
|
#define F_l x16
|
||||||
|
#define F_h x17
|
||||||
|
#define G_l count
|
||||||
|
#define G_h dst
|
||||||
|
#define H_l src
|
||||||
|
#define H_h srcend
|
||||||
|
#define tmp1 x14
|
||||||
|
|
||||||
|
/* This implementation handles overlaps and supports both memcpy and memmove
|
||||||
|
from a single entry point. It uses unaligned accesses and branchless
|
||||||
|
sequences to keep the code small, simple and improve performance.
|
||||||
|
|
||||||
|
Copies are split into 3 main cases: small copies of up to 32 bytes, medium
|
||||||
|
copies of up to 128 bytes, and large copies. The overhead of the overlap
|
||||||
|
check is negligible since it is only required for large copies.
|
||||||
|
|
||||||
|
Large copies use a software pipelined loop processing 64 bytes per iteration.
|
||||||
|
The destination pointer is 16-byte aligned to minimize unaligned accesses.
|
||||||
|
The loop tail is handled by always copying 64 bytes from the end.
|
||||||
|
*/
|
||||||
|
|
||||||
|
ENTRY (memcpy)
|
||||||
|
ENTRY_ALIAS (memmove)
|
||||||
|
add srcend, src, count
|
||||||
|
add dstend, dstin, count
|
||||||
|
cmp count, 128
|
||||||
|
b.hi L(copy_long)
|
||||||
|
cmp count, 32
|
||||||
|
b.hi L(copy32_128)
|
||||||
|
|
||||||
|
/* Small copies: 0..32 bytes. */
|
||||||
|
cmp count, 16
|
||||||
|
b.lo L(copy16)
|
||||||
|
ldp A_l, A_h, [src]
|
||||||
|
ldp D_l, D_h, [srcend, -16]
|
||||||
|
stp A_l, A_h, [dstin]
|
||||||
|
stp D_l, D_h, [dstend, -16]
|
||||||
|
ret
|
||||||
|
|
||||||
|
/* Copy 8-15 bytes. */
|
||||||
|
L(copy16):
|
||||||
|
tbz count, 3, L(copy8)
|
||||||
|
ldr A_l, [src]
|
||||||
|
ldr A_h, [srcend, -8]
|
||||||
|
str A_l, [dstin]
|
||||||
|
str A_h, [dstend, -8]
|
||||||
|
ret
|
||||||
|
|
||||||
|
.p2align 3
|
||||||
|
/* Copy 4-7 bytes. */
|
||||||
|
L(copy8):
|
||||||
|
tbz count, 2, L(copy4)
|
||||||
|
ldr A_lw, [src]
|
||||||
|
ldr B_lw, [srcend, -4]
|
||||||
|
str A_lw, [dstin]
|
||||||
|
str B_lw, [dstend, -4]
|
||||||
|
ret
|
||||||
|
|
||||||
|
/* Copy 0..3 bytes using a branchless sequence. */
|
||||||
|
L(copy4):
|
||||||
|
cbz count, L(copy0)
|
||||||
|
lsr tmp1, count, 1
|
||||||
|
ldrb A_lw, [src]
|
||||||
|
ldrb C_lw, [srcend, -1]
|
||||||
|
ldrb B_lw, [src, tmp1]
|
||||||
|
strb A_lw, [dstin]
|
||||||
|
strb B_lw, [dstin, tmp1]
|
||||||
|
strb C_lw, [dstend, -1]
|
||||||
|
L(copy0):
|
||||||
|
ret
|
||||||
|
|
||||||
|
.p2align 4
|
||||||
|
/* Medium copies: 33..128 bytes. */
|
||||||
|
L(copy32_128):
|
||||||
|
ldp A_l, A_h, [src]
|
||||||
|
ldp B_l, B_h, [src, 16]
|
||||||
|
ldp C_l, C_h, [srcend, -32]
|
||||||
|
ldp D_l, D_h, [srcend, -16]
|
||||||
|
cmp count, 64
|
||||||
|
b.hi L(copy128)
|
||||||
|
stp A_l, A_h, [dstin]
|
||||||
|
stp B_l, B_h, [dstin, 16]
|
||||||
|
stp C_l, C_h, [dstend, -32]
|
||||||
|
stp D_l, D_h, [dstend, -16]
|
||||||
|
ret
|
||||||
|
|
||||||
|
.p2align 4
|
||||||
|
/* Copy 65..128 bytes. */
|
||||||
|
L(copy128):
|
||||||
|
ldp E_l, E_h, [src, 32]
|
||||||
|
ldp F_l, F_h, [src, 48]
|
||||||
|
cmp count, 96
|
||||||
|
b.ls L(copy96)
|
||||||
|
ldp G_l, G_h, [srcend, -64]
|
||||||
|
ldp H_l, H_h, [srcend, -48]
|
||||||
|
stp G_l, G_h, [dstend, -64]
|
||||||
|
stp H_l, H_h, [dstend, -48]
|
||||||
|
L(copy96):
|
||||||
|
stp A_l, A_h, [dstin]
|
||||||
|
stp B_l, B_h, [dstin, 16]
|
||||||
|
stp E_l, E_h, [dstin, 32]
|
||||||
|
stp F_l, F_h, [dstin, 48]
|
||||||
|
stp C_l, C_h, [dstend, -32]
|
||||||
|
stp D_l, D_h, [dstend, -16]
|
||||||
|
ret
|
||||||
|
|
||||||
|
.p2align 4
|
||||||
|
/* Copy more than 128 bytes. */
|
||||||
|
L(copy_long):
|
||||||
|
/* Use backwards copy if there is an overlap. */
|
||||||
|
sub tmp1, dstin, src
|
||||||
|
cbz tmp1, L(copy0)
|
||||||
|
cmp tmp1, count
|
||||||
|
b.lo L(copy_long_backwards)
|
||||||
|
|
||||||
|
/* Copy 16 bytes and then align dst to 16-byte alignment. */
|
||||||
|
|
||||||
|
ldp D_l, D_h, [src]
|
||||||
|
and tmp1, dstin, 15
|
||||||
|
bic dst, dstin, 15
|
||||||
|
sub src, src, tmp1
|
||||||
|
add count, count, tmp1 /* Count is now 16 too large. */
|
||||||
|
ldp A_l, A_h, [src, 16]
|
||||||
|
stp D_l, D_h, [dstin]
|
||||||
|
ldp B_l, B_h, [src, 32]
|
||||||
|
ldp C_l, C_h, [src, 48]
|
||||||
|
ldp D_l, D_h, [src, 64]!
|
||||||
|
subs count, count, 128 + 16 /* Test and readjust count. */
|
||||||
|
b.ls L(copy64_from_end)
|
||||||
|
|
||||||
|
L(loop64):
|
||||||
|
stp A_l, A_h, [dst, 16]
|
||||||
|
ldp A_l, A_h, [src, 16]
|
||||||
|
stp B_l, B_h, [dst, 32]
|
||||||
|
ldp B_l, B_h, [src, 32]
|
||||||
|
stp C_l, C_h, [dst, 48]
|
||||||
|
ldp C_l, C_h, [src, 48]
|
||||||
|
stp D_l, D_h, [dst, 64]!
|
||||||
|
ldp D_l, D_h, [src, 64]!
|
||||||
|
subs count, count, 64
|
||||||
|
b.hi L(loop64)
|
||||||
|
|
||||||
|
/* Write the last iteration and copy 64 bytes from the end. */
|
||||||
|
L(copy64_from_end):
|
||||||
|
ldp E_l, E_h, [srcend, -64]
|
||||||
|
stp A_l, A_h, [dst, 16]
|
||||||
|
ldp A_l, A_h, [srcend, -48]
|
||||||
|
stp B_l, B_h, [dst, 32]
|
||||||
|
ldp B_l, B_h, [srcend, -32]
|
||||||
|
stp C_l, C_h, [dst, 48]
|
||||||
|
ldp C_l, C_h, [srcend, -16]
|
||||||
|
stp D_l, D_h, [dst, 64]
|
||||||
|
stp E_l, E_h, [dstend, -64]
|
||||||
|
stp A_l, A_h, [dstend, -48]
|
||||||
|
stp B_l, B_h, [dstend, -32]
|
||||||
|
stp C_l, C_h, [dstend, -16]
|
||||||
|
ret
|
||||||
|
|
||||||
|
.p2align 4
|
||||||
|
|
||||||
|
/* Large backwards copy for overlapping copies.
|
||||||
|
Copy 16 bytes and then align dst to 16-byte alignment. */
|
||||||
|
L(copy_long_backwards):
|
||||||
|
ldp D_l, D_h, [srcend, -16]
|
||||||
|
and tmp1, dstend, 15
|
||||||
|
sub srcend, srcend, tmp1
|
||||||
|
sub count, count, tmp1
|
||||||
|
ldp A_l, A_h, [srcend, -16]
|
||||||
|
stp D_l, D_h, [dstend, -16]
|
||||||
|
ldp B_l, B_h, [srcend, -32]
|
||||||
|
ldp C_l, C_h, [srcend, -48]
|
||||||
|
ldp D_l, D_h, [srcend, -64]!
|
||||||
|
sub dstend, dstend, tmp1
|
||||||
|
subs count, count, 128
|
||||||
|
b.ls L(copy64_from_start)
|
||||||
|
|
||||||
|
L(loop64_backwards):
|
||||||
|
stp A_l, A_h, [dstend, -16]
|
||||||
|
ldp A_l, A_h, [srcend, -16]
|
||||||
|
stp B_l, B_h, [dstend, -32]
|
||||||
|
ldp B_l, B_h, [srcend, -32]
|
||||||
|
stp C_l, C_h, [dstend, -48]
|
||||||
|
ldp C_l, C_h, [srcend, -48]
|
||||||
|
stp D_l, D_h, [dstend, -64]!
|
||||||
|
ldp D_l, D_h, [srcend, -64]!
|
||||||
|
subs count, count, 64
|
||||||
|
b.hi L(loop64_backwards)
|
||||||
|
|
||||||
|
/* Write the last iteration and copy 64 bytes from the start. */
|
||||||
|
L(copy64_from_start):
|
||||||
|
ldp G_l, G_h, [src, 48]
|
||||||
|
stp A_l, A_h, [dstend, -16]
|
||||||
|
ldp A_l, A_h, [src, 32]
|
||||||
|
stp B_l, B_h, [dstend, -32]
|
||||||
|
ldp B_l, B_h, [src, 16]
|
||||||
|
stp C_l, C_h, [dstend, -48]
|
||||||
|
ldp C_l, C_h, [src]
|
||||||
|
stp D_l, D_h, [dstend, -64]
|
||||||
|
stp G_l, G_h, [dstin, 48]
|
||||||
|
stp A_l, A_h, [dstin, 32]
|
||||||
|
stp B_l, B_h, [dstin, 16]
|
||||||
|
stp C_l, C_h, [dstin]
|
||||||
|
ret
|
||||||
|
|
||||||
|
END (memcpy)
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
/*
|
||||||
|
* memset - fill memory with a constant byte
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012-2020, Arm Limited.
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Assumptions:
|
||||||
|
*
|
||||||
|
* ARMv8-a, AArch64, Advanced SIMD, unaligned accesses.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "asmdefs.h"
|
||||||
|
|
||||||
|
#define DC_ZVA_THRESHOLD 512
|
||||||
|
|
||||||
|
#define dstin x0
|
||||||
|
#define val x1
|
||||||
|
#define valw w1
|
||||||
|
#define count x2
|
||||||
|
#define dst x3
|
||||||
|
#define dstend x4
|
||||||
|
#define zva_val x5
|
||||||
|
|
||||||
|
ENTRY (memset)
|
||||||
|
|
||||||
|
bfi valw, valw, 8, 8
|
||||||
|
bfi valw, valw, 16, 16
|
||||||
|
bfi val, val, 32, 32
|
||||||
|
|
||||||
|
add dstend, dstin, count
|
||||||
|
|
||||||
|
cmp count, 96
|
||||||
|
b.hi L(set_long)
|
||||||
|
cmp count, 16
|
||||||
|
b.hs L(set_medium)
|
||||||
|
|
||||||
|
/* Set 0..15 bytes. */
|
||||||
|
tbz count, 3, 1f
|
||||||
|
str val, [dstin]
|
||||||
|
str val, [dstend, -8]
|
||||||
|
ret
|
||||||
|
1: tbz count, 2, 2f
|
||||||
|
str valw, [dstin]
|
||||||
|
str valw, [dstend, -4]
|
||||||
|
ret
|
||||||
|
2: cbz count, 3f
|
||||||
|
strb valw, [dstin]
|
||||||
|
tbz count, 1, 3f
|
||||||
|
strh valw, [dstend, -2]
|
||||||
|
3: ret
|
||||||
|
|
||||||
|
/* Set 16..96 bytes. */
|
||||||
|
.p2align 4
|
||||||
|
L(set_medium):
|
||||||
|
stp val, val, [dstin]
|
||||||
|
tbnz count, 6, L(set96)
|
||||||
|
stp val, val, [dstend, -16]
|
||||||
|
tbz count, 5, 1f
|
||||||
|
stp val, val, [dstin, 16]
|
||||||
|
stp val, val, [dstend, -32]
|
||||||
|
1: ret
|
||||||
|
|
||||||
|
.p2align 4
|
||||||
|
/* Set 64..96 bytes. Write 64 bytes from the start and
|
||||||
|
32 bytes from the end. */
|
||||||
|
L(set96):
|
||||||
|
stp val, val, [dstin, 16]
|
||||||
|
stp val, val, [dstin, 32]
|
||||||
|
stp val, val, [dstin, 48]
|
||||||
|
stp val, val, [dstend, -32]
|
||||||
|
stp val, val, [dstend, -16]
|
||||||
|
ret
|
||||||
|
|
||||||
|
.p2align 4
|
||||||
|
L(set_long):
|
||||||
|
stp val, val, [dstin]
|
||||||
|
bic dst, dstin, 15
|
||||||
|
#if DC_ZVA_THRESHOLD
|
||||||
|
cmp count, DC_ZVA_THRESHOLD
|
||||||
|
ccmp val, 0, 0, cs
|
||||||
|
b.eq L(zva_64)
|
||||||
|
#endif
|
||||||
|
/* Small-size or non-zero memset does not use DC ZVA. */
|
||||||
|
sub count, dstend, dst
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Adjust count and bias for loop. By substracting extra 1 from count,
|
||||||
|
* it is easy to use tbz instruction to check whether loop tailing
|
||||||
|
* count is less than 33 bytes, so as to bypass 2 unneccesary stps.
|
||||||
|
*/
|
||||||
|
sub count, count, 64+16+1
|
||||||
|
|
||||||
|
#if DC_ZVA_THRESHOLD
|
||||||
|
/* Align loop on 16-byte boundary, this might be friendly to i-cache. */
|
||||||
|
nop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
1: stp val, val, [dst, 16]
|
||||||
|
stp val, val, [dst, 32]
|
||||||
|
stp val, val, [dst, 48]
|
||||||
|
stp val, val, [dst, 64]!
|
||||||
|
subs count, count, 64
|
||||||
|
b.hs 1b
|
||||||
|
|
||||||
|
tbz count, 5, 1f /* Remaining count is less than 33 bytes? */
|
||||||
|
stp val, val, [dst, 16]
|
||||||
|
stp val, val, [dst, 32]
|
||||||
|
1: stp val, val, [dstend, -32]
|
||||||
|
stp val, val, [dstend, -16]
|
||||||
|
ret
|
||||||
|
|
||||||
|
#if DC_ZVA_THRESHOLD
|
||||||
|
.p2align 4
|
||||||
|
L(zva_64):
|
||||||
|
stp val, val, [dst, 16]
|
||||||
|
stp val, val, [dst, 32]
|
||||||
|
stp val, val, [dst, 48]
|
||||||
|
bic dst, dst, 63
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Previous memory writes might cross cache line boundary, and cause
|
||||||
|
* cache line partially dirty. Zeroing this kind of cache line using
|
||||||
|
* DC ZVA will incur extra cost, for it requires loading untouched
|
||||||
|
* part of the line from memory before zeoring.
|
||||||
|
*
|
||||||
|
* So, write the first 64 byte aligned block using stp to force
|
||||||
|
* fully dirty cache line.
|
||||||
|
*/
|
||||||
|
stp val, val, [dst, 64]
|
||||||
|
stp val, val, [dst, 80]
|
||||||
|
stp val, val, [dst, 96]
|
||||||
|
stp val, val, [dst, 112]
|
||||||
|
|
||||||
|
sub count, dstend, dst
|
||||||
|
/*
|
||||||
|
* Adjust count and bias for loop. By substracting extra 1 from count,
|
||||||
|
* it is easy to use tbz instruction to check whether loop tailing
|
||||||
|
* count is less than 33 bytes, so as to bypass 2 unneccesary stps.
|
||||||
|
*/
|
||||||
|
sub count, count, 128+64+64+1
|
||||||
|
add dst, dst, 128
|
||||||
|
nop
|
||||||
|
|
||||||
|
/* DC ZVA sets 64 bytes each time. */
|
||||||
|
1: dc zva, dst
|
||||||
|
add dst, dst, 64
|
||||||
|
subs count, count, 64
|
||||||
|
b.hs 1b
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write the last 64 byte aligned block using stp to force fully
|
||||||
|
* dirty cache line.
|
||||||
|
*/
|
||||||
|
stp val, val, [dst, 0]
|
||||||
|
stp val, val, [dst, 16]
|
||||||
|
stp val, val, [dst, 32]
|
||||||
|
stp val, val, [dst, 48]
|
||||||
|
|
||||||
|
tbz count, 5, 1f /* Remaining count is less than 33 bytes? */
|
||||||
|
stp val, val, [dst, 64]
|
||||||
|
stp val, val, [dst, 80]
|
||||||
|
1: stp val, val, [dstend, -32]
|
||||||
|
stp val, val, [dstend, -16]
|
||||||
|
ret
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
END (memset)
|
||||||
@@ -61,6 +61,7 @@ QUICKREF
|
|||||||
/*SUPPRESS 20*/
|
/*SUPPRESS 20*/
|
||||||
void *
|
void *
|
||||||
//__inhibit_loop_to_libcall
|
//__inhibit_loop_to_libcall
|
||||||
|
__attribute__((weak))
|
||||||
memmove (void *dst_void,
|
memmove (void *dst_void,
|
||||||
const void *src_void,
|
const void *src_void,
|
||||||
size_t length)
|
size_t length)
|
||||||
@@ -169,6 +170,7 @@ QUICKREF
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
void *
|
void *
|
||||||
|
__attribute__((weak))
|
||||||
memcpy (void * dst0,
|
memcpy (void * dst0,
|
||||||
const void * __restrict src0,
|
const void * __restrict src0,
|
||||||
size_t len0)
|
size_t len0)
|
||||||
@@ -259,6 +261,7 @@ QUICKREF
|
|||||||
#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE)
|
#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE)
|
||||||
|
|
||||||
void *
|
void *
|
||||||
|
__attribute__((weak))
|
||||||
memset (void *m,
|
memset (void *m,
|
||||||
int c,
|
int c,
|
||||||
size_t n)
|
size_t n)
|
||||||
@@ -357,6 +360,7 @@ QUICKREF
|
|||||||
#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE)
|
#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE)
|
||||||
|
|
||||||
int
|
int
|
||||||
|
__attribute__((weak))
|
||||||
memcmp (const void *m1,
|
memcmp (const void *m1,
|
||||||
const void *m2,
|
const void *m2,
|
||||||
size_t n)
|
size_t n)
|
||||||
@@ -417,6 +421,228 @@ memcmp (const void *m1,
|
|||||||
#endif /* not PREFER_SIZE_OVER_SPEED */
|
#endif /* not PREFER_SIZE_OVER_SPEED */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
FUNCTION
|
||||||
|
<<strncpy>>---counted copy string
|
||||||
|
INDEX
|
||||||
|
strncpy
|
||||||
|
SYNOPSIS
|
||||||
|
#include <string.h>
|
||||||
|
char *strncpy(char *restrict <[dst]>, const char *restrict <[src]>,
|
||||||
|
size_t <[length]>);
|
||||||
|
DESCRIPTION
|
||||||
|
<<strncpy>> copies not more than <[length]> characters from the
|
||||||
|
the string pointed to by <[src]> (including the terminating
|
||||||
|
null character) to the array pointed to by <[dst]>. If the
|
||||||
|
string pointed to by <[src]> is shorter than <[length]>
|
||||||
|
characters, null characters are appended to the destination
|
||||||
|
array until a total of <[length]> characters have been
|
||||||
|
written.
|
||||||
|
RETURNS
|
||||||
|
This function returns the initial value of <[dst]>.
|
||||||
|
PORTABILITY
|
||||||
|
<<strncpy>> is ANSI C.
|
||||||
|
<<strncpy>> requires no supporting OS subroutines.
|
||||||
|
QUICKREF
|
||||||
|
strncpy ansi pure
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
/*SUPPRESS 560*/
|
||||||
|
/*SUPPRESS 530*/
|
||||||
|
|
||||||
|
/* Nonzero if either X or Y is not aligned on a "long" boundary. */
|
||||||
|
#define UNALIGNED(X, Y) \
|
||||||
|
(((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1)))
|
||||||
|
|
||||||
|
#if LONG_MAX == 2147483647L
|
||||||
|
#define DETECTNULL(X) (((X) - 0x01010101) & ~(X) & 0x80808080)
|
||||||
|
#else
|
||||||
|
#if LONG_MAX == 9223372036854775807L
|
||||||
|
/* Nonzero if X (a long int) contains a NULL byte. */
|
||||||
|
#define DETECTNULL(X) (((X) - 0x0101010101010101) & ~(X) & 0x8080808080808080)
|
||||||
|
#else
|
||||||
|
#error long int is not a 32bit or 64bit type.
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DETECTNULL
|
||||||
|
#error long int is not a 32bit or 64bit byte
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#undef TOO_SMALL
|
||||||
|
#define TOO_SMALL(LEN) ((LEN) < sizeof (long))
|
||||||
|
|
||||||
|
char *
|
||||||
|
strncpy (char *__restrict dst0,
|
||||||
|
const char *__restrict src0,
|
||||||
|
size_t count)
|
||||||
|
{
|
||||||
|
#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__)
|
||||||
|
char *dscan;
|
||||||
|
const char *sscan;
|
||||||
|
|
||||||
|
dscan = dst0;
|
||||||
|
sscan = src0;
|
||||||
|
while (count > 0)
|
||||||
|
{
|
||||||
|
--count;
|
||||||
|
if ((*dscan++ = *sscan++) == '\0')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
while (count-- > 0)
|
||||||
|
*dscan++ = '\0';
|
||||||
|
|
||||||
|
return dst0;
|
||||||
|
#else
|
||||||
|
char *dst = dst0;
|
||||||
|
const char *src = src0;
|
||||||
|
long *aligned_dst;
|
||||||
|
const long *aligned_src;
|
||||||
|
|
||||||
|
/* If SRC and DEST is aligned and count large enough, then copy words. */
|
||||||
|
if (!UNALIGNED (src, dst) && !TOO_SMALL (count))
|
||||||
|
{
|
||||||
|
aligned_dst = (long*)dst;
|
||||||
|
aligned_src = (long*)src;
|
||||||
|
|
||||||
|
/* SRC and DEST are both "long int" aligned, try to do "long int"
|
||||||
|
sized copies. */
|
||||||
|
while (count >= sizeof (long int) && !DETECTNULL(*aligned_src))
|
||||||
|
{
|
||||||
|
count -= sizeof (long int);
|
||||||
|
*aligned_dst++ = *aligned_src++;
|
||||||
|
}
|
||||||
|
|
||||||
|
dst = (char*)aligned_dst;
|
||||||
|
src = (char*)aligned_src;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (count > 0)
|
||||||
|
{
|
||||||
|
--count;
|
||||||
|
if ((*dst++ = *src++) == '\0')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (count-- > 0)
|
||||||
|
*dst++ = '\0';
|
||||||
|
|
||||||
|
return dst0;
|
||||||
|
#endif /* not PREFER_SIZE_OVER_SPEED */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
FUNCTION
|
||||||
|
<<strncmp>>---character string compare
|
||||||
|
|
||||||
|
INDEX
|
||||||
|
strncmp
|
||||||
|
SYNOPSIS
|
||||||
|
#include <string.h>
|
||||||
|
int strncmp(const char *<[a]>, const char * <[b]>, size_t <[length]>);
|
||||||
|
DESCRIPTION
|
||||||
|
<<strncmp>> compares up to <[length]> characters
|
||||||
|
from the string at <[a]> to the string at <[b]>.
|
||||||
|
RETURNS
|
||||||
|
If <<*<[a]>>> sorts lexicographically after <<*<[b]>>>,
|
||||||
|
<<strncmp>> returns a number greater than zero. If the two
|
||||||
|
strings are equivalent, <<strncmp>> returns zero. If <<*<[a]>>>
|
||||||
|
sorts lexicographically before <<*<[b]>>>, <<strncmp>> returns a
|
||||||
|
number less than zero.
|
||||||
|
PORTABILITY
|
||||||
|
<<strncmp>> is ANSI C.
|
||||||
|
<<strncmp>> requires no supporting OS subroutines.
|
||||||
|
QUICKREF
|
||||||
|
strncmp ansi pure
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
/* Nonzero if either X or Y is not aligned on a "long" boundary. */
|
||||||
|
#define UNALIGNED(X, Y) \
|
||||||
|
(((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1)))
|
||||||
|
|
||||||
|
/* DETECTNULL returns nonzero if (long)X contains a NULL byte. */
|
||||||
|
#if LONG_MAX == 2147483647L
|
||||||
|
#define DETECTNULL(X) (((X) - 0x01010101) & ~(X) & 0x80808080)
|
||||||
|
#else
|
||||||
|
#if LONG_MAX == 9223372036854775807L
|
||||||
|
#define DETECTNULL(X) (((X) - 0x0101010101010101) & ~(X) & 0x8080808080808080)
|
||||||
|
#else
|
||||||
|
#error long int is not a 32bit or 64bit type.
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DETECTNULL
|
||||||
|
#error long int is not a 32bit or 64bit byte
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int
|
||||||
|
strncmp (const char *s1,
|
||||||
|
const char *s2,
|
||||||
|
size_t n)
|
||||||
|
{
|
||||||
|
#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__)
|
||||||
|
if (n == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
while (n-- != 0 && *s1 == *s2)
|
||||||
|
{
|
||||||
|
if (n == 0 || *s1 == '\0')
|
||||||
|
break;
|
||||||
|
s1++;
|
||||||
|
s2++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (*(unsigned char *) s1) - (*(unsigned char *) s2);
|
||||||
|
#else
|
||||||
|
unsigned long *a1;
|
||||||
|
unsigned long *a2;
|
||||||
|
|
||||||
|
if (n == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* If s1 or s2 are unaligned, then compare bytes. */
|
||||||
|
if (!UNALIGNED (s1, s2))
|
||||||
|
{
|
||||||
|
/* If s1 and s2 are word-aligned, compare them a word at a time. */
|
||||||
|
a1 = (unsigned long*)s1;
|
||||||
|
a2 = (unsigned long*)s2;
|
||||||
|
while (n >= sizeof (long) && *a1 == *a2)
|
||||||
|
{
|
||||||
|
n -= sizeof (long);
|
||||||
|
|
||||||
|
/* If we've run out of bytes or hit a null, return zero
|
||||||
|
since we already know *a1 == *a2. */
|
||||||
|
if (n == 0 || DETECTNULL (*a1))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
a1++;
|
||||||
|
a2++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A difference was detected in last few bytes of s1, so search bytewise */
|
||||||
|
s1 = (char*)a1;
|
||||||
|
s2 = (char*)a2;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (n-- > 0 && *s1 == *s2)
|
||||||
|
{
|
||||||
|
/* If we've run out of bytes or hit a null, return zero
|
||||||
|
since we already know *s1 == *s2. */
|
||||||
|
if (n == 0 || *s1 == '\0')
|
||||||
|
return 0;
|
||||||
|
s1++;
|
||||||
|
s2++;
|
||||||
|
}
|
||||||
|
return (*(unsigned char *) s1) - (*(unsigned char *) s2);
|
||||||
|
#endif /* not PREFER_SIZE_OVER_SPEED */
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
#endif
|
#endif
|
||||||
@@ -21,28 +21,112 @@ namespace ams::kern::svc {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
Result GetInfo(u64 *out, ams::svc::InfoType info_type, ams::svc::Handle handle, u64 info_subtype) {
|
||||||
|
MESOSPHERE_LOG("GetInfo(%p, %u, %08x, %lu) was called\n", out, static_cast<u32>(info_type), static_cast<u32>(handle), info_subtype);
|
||||||
|
ON_SCOPE_EXIT{ MESOSPHERE_LOG("GetInfo returned %016lx\n", *out); };
|
||||||
|
|
||||||
|
switch (info_type) {
|
||||||
|
case ams::svc::InfoType_AliasRegionAddress:
|
||||||
|
case ams::svc::InfoType_AliasRegionSize:
|
||||||
|
case ams::svc::InfoType_HeapRegionAddress:
|
||||||
|
case ams::svc::InfoType_HeapRegionSize:
|
||||||
|
case ams::svc::InfoType_AslrRegionAddress:
|
||||||
|
case ams::svc::InfoType_AslrRegionSize:
|
||||||
|
case ams::svc::InfoType_StackRegionAddress:
|
||||||
|
case ams::svc::InfoType_StackRegionSize:
|
||||||
|
{
|
||||||
|
/* These info types don't support non-zero subtypes. */
|
||||||
|
R_UNLESS(info_subtype == 0, svc::ResultInvalidCombination());
|
||||||
|
|
||||||
|
/* Get the process from its handle. */
|
||||||
|
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(handle);
|
||||||
|
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
||||||
|
|
||||||
|
switch (info_type) {
|
||||||
|
case ams::svc::InfoType_AliasRegionAddress:
|
||||||
|
*out = GetInteger(process->GetPageTable().GetAliasRegionStart());
|
||||||
|
break;
|
||||||
|
case ams::svc::InfoType_AliasRegionSize:
|
||||||
|
*out = process->GetPageTable().GetAliasRegionSize();
|
||||||
|
break;
|
||||||
|
case ams::svc::InfoType_HeapRegionAddress:
|
||||||
|
*out = GetInteger(process->GetPageTable().GetHeapRegionStart());
|
||||||
|
break;
|
||||||
|
case ams::svc::InfoType_HeapRegionSize:
|
||||||
|
*out = process->GetPageTable().GetHeapRegionSize();
|
||||||
|
break;
|
||||||
|
case ams::svc::InfoType_AslrRegionAddress:
|
||||||
|
*out = GetInteger(process->GetPageTable().GetAliasCodeRegionStart());
|
||||||
|
break;
|
||||||
|
case ams::svc::InfoType_AslrRegionSize:
|
||||||
|
*out = process->GetPageTable().GetAliasCodeRegionSize();
|
||||||
|
break;
|
||||||
|
case ams::svc::InfoType_StackRegionAddress:
|
||||||
|
*out = GetInteger(process->GetPageTable().GetStackRegionStart());
|
||||||
|
break;
|
||||||
|
case ams::svc::InfoType_StackRegionSize:
|
||||||
|
*out = process->GetPageTable().GetStackRegionSize();
|
||||||
|
break;
|
||||||
|
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return svc::ResultInvalidEnumValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetSystemInfo(u64 *out, ams::svc::SystemInfoType info_type, ams::svc::Handle handle, u64 info_subtype) {
|
||||||
|
MESOSPHERE_LOG("GetSystemInfo(%p, %u, %08x, %lu) was called\n", out, static_cast<u32>(info_type), static_cast<u32>(handle), info_subtype);
|
||||||
|
ON_SCOPE_EXIT{ MESOSPHERE_LOG("GetSystemInfo returned %016lx\n", *out); };
|
||||||
|
|
||||||
|
switch (info_type) {
|
||||||
|
case ams::svc::SystemInfoType_InitialProcessIdRange:
|
||||||
|
{
|
||||||
|
R_UNLESS(handle == ams::svc::InvalidHandle, svc::ResultInvalidHandle());
|
||||||
|
switch (static_cast<ams::svc::InitialProcessIdRangeInfo>(info_subtype)) {
|
||||||
|
case ams::svc::InitialProcessIdRangeInfo_Minimum:
|
||||||
|
MESOSPHERE_ABORT_UNLESS(GetInitialProcessIdMin() <= GetInitialProcessIdMax());
|
||||||
|
*out = GetInitialProcessIdMin();
|
||||||
|
break;
|
||||||
|
case ams::svc::InitialProcessIdRangeInfo_Maximum:
|
||||||
|
MESOSPHERE_ABORT_UNLESS(GetInitialProcessIdMin() <= GetInitialProcessIdMax());
|
||||||
|
*out = GetInitialProcessIdMax();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return svc::ResultInvalidCombination();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return svc::ResultInvalidEnumValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================= 64 ABI ============================= */
|
/* ============================= 64 ABI ============================= */
|
||||||
|
|
||||||
Result GetInfo64(uint64_t *out, ams::svc::InfoType info_type, ams::svc::Handle handle, uint64_t info_subtype) {
|
Result GetInfo64(uint64_t *out, ams::svc::InfoType info_type, ams::svc::Handle handle, uint64_t info_subtype) {
|
||||||
MESOSPHERE_PANIC("Stubbed SvcGetInfo64 was called.");
|
return GetInfo(out, info_type, handle, info_subtype);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetSystemInfo64(uint64_t *out, ams::svc::SystemInfoType info_type, ams::svc::Handle handle, uint64_t info_subtype) {
|
Result GetSystemInfo64(uint64_t *out, ams::svc::SystemInfoType info_type, ams::svc::Handle handle, uint64_t info_subtype) {
|
||||||
MESOSPHERE_PANIC("Stubbed SvcGetSystemInfo64 was called.");
|
return GetSystemInfo(out, info_type, handle, info_subtype);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================= 64From32 ABI ============================= */
|
/* ============================= 64From32 ABI ============================= */
|
||||||
|
|
||||||
Result GetInfo64From32(uint64_t *out, ams::svc::InfoType info_type, ams::svc::Handle handle, uint64_t info_subtype) {
|
Result GetInfo64From32(uint64_t *out, ams::svc::InfoType info_type, ams::svc::Handle handle, uint64_t info_subtype) {
|
||||||
MESOSPHERE_PANIC("Stubbed SvcGetInfo64From32 was called.");
|
return GetInfo(out, info_type, handle, info_subtype);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetSystemInfo64From32(uint64_t *out, ams::svc::SystemInfoType info_type, ams::svc::Handle handle, uint64_t info_subtype) {
|
Result GetSystemInfo64From32(uint64_t *out, ams::svc::SystemInfoType info_type, ams::svc::Handle handle, uint64_t info_subtype) {
|
||||||
MESOSPHERE_PANIC("Stubbed SvcGetSystemInfo64From32 was called.");
|
return GetSystemInfo(out, info_type, handle, info_subtype);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,61 @@ namespace ams::kern::svc {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
Result ManageNamedPort(ams::svc::Handle *out_server_handle, KUserPointer<const char *> user_name, s32 max_sessions) {
|
||||||
|
/* Copy the provided name from user memory to kernel memory. */
|
||||||
|
char name[KObjectName::NameLengthMax] = {};
|
||||||
|
R_TRY(user_name.CopyStringTo(name, sizeof(name)));
|
||||||
|
|
||||||
|
/* Validate that sessions and name are valid. */
|
||||||
|
R_UNLESS(max_sessions >= 0, svc::ResultOutOfRange());
|
||||||
|
R_UNLESS(name[sizeof(name) - 1] == '\x00', svc::ResultOutOfRange());
|
||||||
|
|
||||||
|
if (max_sessions > 0) {
|
||||||
|
MESOSPHERE_LOG("Creating Named Port %s (max sessions = %d)\n", name, max_sessions);
|
||||||
|
/* Get the current handle table. */
|
||||||
|
auto &handle_table = GetCurrentProcess().GetHandleTable();
|
||||||
|
|
||||||
|
/* Create a new port. */
|
||||||
|
KPort *port = KPort::Create();
|
||||||
|
R_UNLESS(port != nullptr, svc::ResultOutOfResource());
|
||||||
|
|
||||||
|
/* Reserve a handle for the server port. */
|
||||||
|
R_TRY(handle_table.Reserve(out_server_handle));
|
||||||
|
auto reserve_guard = SCOPE_GUARD { handle_table.Unreserve(*out_server_handle); };
|
||||||
|
|
||||||
|
/* Initialize the new port. */
|
||||||
|
port->Initialize(max_sessions, false, 0);
|
||||||
|
|
||||||
|
/* Register the port. */
|
||||||
|
KPort::Register(port);
|
||||||
|
|
||||||
|
/* Register the handle in the table. */
|
||||||
|
handle_table.Register(*out_server_handle, std::addressof(port->GetServerPort()));
|
||||||
|
reserve_guard.Cancel();
|
||||||
|
auto register_guard = SCOPE_GUARD { handle_table.Remove(*out_server_handle); };
|
||||||
|
|
||||||
|
/* Create a new object name. */
|
||||||
|
R_TRY(KObjectName::NewFromName(std::addressof(port->GetClientPort()), name));
|
||||||
|
|
||||||
|
/* Perform resource cleanup. */
|
||||||
|
port->GetServerPort().Close();
|
||||||
|
port->GetClientPort().Close();
|
||||||
|
register_guard.Cancel();
|
||||||
|
} else /* if (max_sessions == 0) */ {
|
||||||
|
MESOSPHERE_LOG("Deleting Named Port %s\n", name);
|
||||||
|
|
||||||
|
/* Ensure that this else case is correct. */
|
||||||
|
MESOSPHERE_AUDIT(max_sessions == 0);
|
||||||
|
|
||||||
|
/* If we're closing, there's no server handle. */
|
||||||
|
*out_server_handle = ams::svc::InvalidHandle;
|
||||||
|
|
||||||
|
/* Delete the object. */
|
||||||
|
R_TRY(KObjectName::Delete<KClientPort>(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +90,7 @@ namespace ams::kern::svc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result ManageNamedPort64(ams::svc::Handle *out_server_handle, KUserPointer<const char *> name, int32_t max_sessions) {
|
Result ManageNamedPort64(ams::svc::Handle *out_server_handle, KUserPointer<const char *> name, int32_t max_sessions) {
|
||||||
MESOSPHERE_PANIC("Stubbed SvcManageNamedPort64 was called.");
|
return ManageNamedPort(out_server_handle, name, max_sessions);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ConnectToPort64(ams::svc::Handle *out_handle, ams::svc::Handle port) {
|
Result ConnectToPort64(ams::svc::Handle *out_handle, ams::svc::Handle port) {
|
||||||
@@ -54,7 +108,7 @@ namespace ams::kern::svc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result ManageNamedPort64From32(ams::svc::Handle *out_server_handle, KUserPointer<const char *> name, int32_t max_sessions) {
|
Result ManageNamedPort64From32(ams::svc::Handle *out_server_handle, KUserPointer<const char *> name, int32_t max_sessions) {
|
||||||
MESOSPHERE_PANIC("Stubbed SvcManageNamedPort64From32 was called.");
|
return ManageNamedPort(out_server_handle, name, max_sessions);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ConnectToPort64From32(ams::svc::Handle *out_handle, ams::svc::Handle port) {
|
Result ConnectToPort64From32(ams::svc::Handle *out_handle, ams::svc::Handle port) {
|
||||||
|
|||||||
@@ -21,6 +21,32 @@ namespace ams::kern::svc {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
Result GetProcessId(u64 *out_process_id, ams::svc::Handle handle) {
|
||||||
|
/* Get the object from the handle table. */
|
||||||
|
KScopedAutoObject obj = GetCurrentProcess().GetHandleTable().GetObject<KAutoObject>(handle);
|
||||||
|
R_UNLESS(obj.IsNotNull(), svc::ResultInvalidHandle());
|
||||||
|
|
||||||
|
/* Get the process from the object. */
|
||||||
|
KProcess *process = nullptr;
|
||||||
|
if (obj->IsDerivedFrom(KProcess::GetStaticTypeObj())) {
|
||||||
|
/* The object is a process, so we can use it directly. */
|
||||||
|
process = reinterpret_cast<KProcess *>(obj.GetPointerUnsafe());
|
||||||
|
} else if (obj->IsDerivedFrom(KThread::GetStaticTypeObj())) {
|
||||||
|
/* The object is a thread, so we want to use its parent. */
|
||||||
|
process = reinterpret_cast<KThread *>(obj.GetPointerUnsafe())->GetOwnerProcess();
|
||||||
|
} else if (obj->IsDerivedFrom(KDebug::GetStaticTypeObj())) {
|
||||||
|
/* The object is a debug, so we want to use the process it's attached to. */
|
||||||
|
MESOSPHERE_UNIMPLEMENTED();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure the target process exists. */
|
||||||
|
R_UNLESS(process != nullptr, svc::ResultInvalidHandle());
|
||||||
|
|
||||||
|
/* Get the process id. */
|
||||||
|
*out_process_id = process->GetId();
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -32,7 +58,7 @@ namespace ams::kern::svc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result GetProcessId64(uint64_t *out_process_id, ams::svc::Handle process_handle) {
|
Result GetProcessId64(uint64_t *out_process_id, ams::svc::Handle process_handle) {
|
||||||
MESOSPHERE_PANIC("Stubbed SvcGetProcessId64 was called.");
|
return GetProcessId(out_process_id, process_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetProcessList64(int32_t *out_num_processes, KUserPointer<uint64_t *> out_process_ids, int32_t max_out_count) {
|
Result GetProcessList64(int32_t *out_num_processes, KUserPointer<uint64_t *> out_process_ids, int32_t max_out_count) {
|
||||||
@@ -62,7 +88,7 @@ namespace ams::kern::svc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result GetProcessId64From32(uint64_t *out_process_id, ams::svc::Handle process_handle) {
|
Result GetProcessId64From32(uint64_t *out_process_id, ams::svc::Handle process_handle) {
|
||||||
MESOSPHERE_PANIC("Stubbed SvcGetProcessId64From32 was called.");
|
return GetProcessId(out_process_id, process_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetProcessList64From32(int32_t *out_num_processes, KUserPointer<uint64_t *> out_process_ids, int32_t max_out_count) {
|
Result GetProcessList64From32(int32_t *out_num_processes, KUserPointer<uint64_t *> out_process_ids, int32_t max_out_count) {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ namespace ams::kern::svc {
|
|||||||
/* ============================= 64 ABI ============================= */
|
/* ============================= 64 ABI ============================= */
|
||||||
|
|
||||||
void CallSecureMonitor64(ams::svc::lp64::SecureMonitorArguments *args) {
|
void CallSecureMonitor64(ams::svc::lp64::SecureMonitorArguments *args) {
|
||||||
MESOSPHERE_PANIC("Stubbed SvcCallSecureMonitor64 was called.");
|
KSystemControl::CallSecureMonitorFromUser(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================= 64From32 ABI ============================= */
|
/* ============================= 64From32 ABI ============================= */
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ include $(DEVKITPRO)/libnx/switch_rules
|
|||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# options for code generation
|
# options for code generation
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_STRATOSPHERE
|
DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_STRATOSPHERE -D_GNU_SOURCE
|
||||||
SETTINGS := $(ATMOSPHERE_SETTINGS) -O2
|
SETTINGS := $(ATMOSPHERE_SETTINGS) -O2
|
||||||
CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
|
CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
|
||||||
CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -flto
|
CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -flto
|
||||||
|
|||||||
@@ -22,14 +22,18 @@
|
|||||||
/* Libstratosphere-only utility. */
|
/* Libstratosphere-only utility. */
|
||||||
#include "stratosphere/util.hpp"
|
#include "stratosphere/util.hpp"
|
||||||
|
|
||||||
|
/* Sadly required shims. */
|
||||||
|
#include "stratosphere/svc/svc_stratosphere_shims.hpp"
|
||||||
|
|
||||||
/* Critical modules with no dependencies. */
|
/* Critical modules with no dependencies. */
|
||||||
#include "stratosphere/ams.hpp"
|
#include "stratosphere/ams.hpp"
|
||||||
#include "stratosphere/os.hpp"
|
#include "stratosphere/os.hpp"
|
||||||
#include "stratosphere/dd.hpp"
|
#include "stratosphere/dd.hpp"
|
||||||
#include "stratosphere/lmem.hpp"
|
#include "stratosphere/lmem.hpp"
|
||||||
|
#include "stratosphere/mem.hpp"
|
||||||
|
|
||||||
/* Lots of things depend on NCM, for Program IDs. */
|
/* Pull in all ID definitions from NCM. */
|
||||||
#include "stratosphere/ncm.hpp"
|
#include "stratosphere/ncm/ncm_ids.hpp"
|
||||||
|
|
||||||
/* At this point, just include the rest alphabetically. */
|
/* At this point, just include the rest alphabetically. */
|
||||||
/* TODO: Figure out optimal order. */
|
/* TODO: Figure out optimal order. */
|
||||||
@@ -41,7 +45,9 @@
|
|||||||
#include "stratosphere/hos.hpp"
|
#include "stratosphere/hos.hpp"
|
||||||
#include "stratosphere/kvdb.hpp"
|
#include "stratosphere/kvdb.hpp"
|
||||||
#include "stratosphere/ldr.hpp"
|
#include "stratosphere/ldr.hpp"
|
||||||
|
#include "stratosphere/lr.hpp"
|
||||||
#include "stratosphere/map.hpp"
|
#include "stratosphere/map.hpp"
|
||||||
|
#include "stratosphere/ncm.hpp"
|
||||||
#include "stratosphere/patcher.hpp"
|
#include "stratosphere/patcher.hpp"
|
||||||
#include "stratosphere/pm.hpp"
|
#include "stratosphere/pm.hpp"
|
||||||
#include "stratosphere/reg.hpp"
|
#include "stratosphere/reg.hpp"
|
||||||
@@ -52,7 +58,8 @@
|
|||||||
#include "stratosphere/spl.hpp"
|
#include "stratosphere/spl.hpp"
|
||||||
#include "stratosphere/updater.hpp"
|
#include "stratosphere/updater.hpp"
|
||||||
|
|
||||||
|
|
||||||
/* Include FS last. */
|
/* Include FS last. */
|
||||||
#include "stratosphere/fs.hpp"
|
#include "stratosphere/fs.hpp"
|
||||||
#include "stratosphere/fssrv.hpp"
|
#include "stratosphere/fssrv.hpp"
|
||||||
#include "stratosphere/fssystem.hpp"
|
#include "stratosphere/fssystem.hpp"
|
||||||
@@ -83,12 +83,13 @@ namespace ams {
|
|||||||
struct FatalErrorContext : sf::LargeData, sf::PrefersMapAliasTransferMode {
|
struct FatalErrorContext : sf::LargeData, sf::PrefersMapAliasTransferMode {
|
||||||
static constexpr size_t MaxStackTrace = 0x20;
|
static constexpr size_t MaxStackTrace = 0x20;
|
||||||
static constexpr size_t MaxStackDumpSize = 0x100;
|
static constexpr size_t MaxStackDumpSize = 0x100;
|
||||||
|
static constexpr size_t ThreadLocalSize = 0x100;
|
||||||
static constexpr size_t NumGprs = 29;
|
static constexpr size_t NumGprs = 29;
|
||||||
static constexpr uintptr_t StdAbortMagicAddress = 0x8;
|
static constexpr uintptr_t StdAbortMagicAddress = 0x8;
|
||||||
static constexpr u64 StdAbortMagicValue = 0xA55AF00DDEADCAFEul;
|
static constexpr u64 StdAbortMagicValue = 0xA55AF00DDEADCAFEul;
|
||||||
static constexpr u32 StdAbortErrorDesc = 0xFFE;
|
static constexpr u32 StdAbortErrorDesc = 0xFFE;
|
||||||
static constexpr u32 DataAbortErrorDesc = 0x101;
|
static constexpr u32 DataAbortErrorDesc = 0x101;
|
||||||
static constexpr u32 Magic = util::FourCC<'A', 'F', 'E', '1'>::Code;
|
static constexpr u32 Magic = util::FourCC<'A', 'F', 'E', '2'>::Code;
|
||||||
|
|
||||||
u32 magic;
|
u32 magic;
|
||||||
u32 error_desc;
|
u32 error_desc;
|
||||||
@@ -113,10 +114,11 @@ namespace ams {
|
|||||||
u64 stack_trace_size;
|
u64 stack_trace_size;
|
||||||
u64 stack_dump_size;
|
u64 stack_dump_size;
|
||||||
u64 stack_trace[MaxStackTrace];
|
u64 stack_trace[MaxStackTrace];
|
||||||
u8 stack_dump[MaxStackDumpSize];
|
u8 stack_dump[MaxStackDumpSize];
|
||||||
|
u8 tls[ThreadLocalSize];
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(FatalErrorContext) == 0x350, "sizeof(FatalErrorContext)");
|
static_assert(sizeof(FatalErrorContext) == 0x450, "sizeof(FatalErrorContext)");
|
||||||
static_assert(std::is_pod<FatalErrorContext>::value, "FatalErrorContext");
|
static_assert(std::is_pod<FatalErrorContext>::value, "FatalErrorContext");
|
||||||
|
|
||||||
#ifdef ATMOSPHERE_GIT_BRANCH
|
#ifdef ATMOSPHERE_GIT_BRANCH
|
||||||
|
|||||||
@@ -15,8 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "../os/os_common_types.hpp"
|
#include <stratosphere/os.hpp>
|
||||||
#include "../ncm/ncm_types.hpp"
|
#include <stratosphere/ncm/ncm_ids.hpp>
|
||||||
|
|
||||||
namespace ams::cfg {
|
namespace ams::cfg {
|
||||||
|
|
||||||
|
|||||||
@@ -15,9 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "../os/os_common_types.hpp"
|
#include <stratosphere/os.hpp>
|
||||||
#include "../ncm/ncm_types.hpp"
|
#include <stratosphere/ncm/ncm_ids.hpp>
|
||||||
#include "../sf/sf_buffer_tags.hpp"
|
#include <stratosphere/sf/sf_buffer_tags.hpp>
|
||||||
|
|
||||||
namespace ams::dmnt::cheat {
|
namespace ams::dmnt::cheat {
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <vapours.hpp>
|
#include <vapours.hpp>
|
||||||
#include "../ncm/ncm_types.hpp"
|
#include <stratosphere/ncm/ncm_ids.hpp>
|
||||||
#include "../sf/sf_buffer_tags.hpp"
|
#include <stratosphere/sf/sf_buffer_tags.hpp>
|
||||||
|
|
||||||
namespace ams::fatal {
|
namespace ams::fatal {
|
||||||
|
|
||||||
|
|||||||
@@ -19,11 +19,34 @@
|
|||||||
#include <stratosphere/fs/fsa/fs_ifile.hpp>
|
#include <stratosphere/fs/fsa/fs_ifile.hpp>
|
||||||
#include <stratosphere/fs/fsa/fs_idirectory.hpp>
|
#include <stratosphere/fs/fsa/fs_idirectory.hpp>
|
||||||
#include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
|
#include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
|
||||||
|
#include <stratosphere/fs/impl/fs_filesystem_proxy_type.hpp>
|
||||||
|
#include <stratosphere/fs/fsa/fs_registrar.hpp>
|
||||||
#include <stratosphere/fs/fs_remote_filesystem.hpp>
|
#include <stratosphere/fs/fs_remote_filesystem.hpp>
|
||||||
#include <stratosphere/fs/fs_readonly_filesystem_adapter.hpp>
|
#include <stratosphere/fs/fs_read_only_filesystem.hpp>
|
||||||
#include <stratosphere/fs/fs_istorage.hpp>
|
#include <stratosphere/fs/fs_istorage.hpp>
|
||||||
|
#include <stratosphere/fs/fs_substorage.hpp>
|
||||||
|
#include <stratosphere/fs/fs_memory_storage.hpp>
|
||||||
#include <stratosphere/fs/fs_remote_storage.hpp>
|
#include <stratosphere/fs/fs_remote_storage.hpp>
|
||||||
#include <stratosphere/fs/fs_file_storage.hpp>
|
#include <stratosphere/fs/fs_file_storage.hpp>
|
||||||
#include <stratosphere/fs/fs_query_range.hpp>
|
#include <stratosphere/fs/fs_query_range.hpp>
|
||||||
|
#include <stratosphere/fs/impl/fs_common_mount_name.hpp>
|
||||||
|
#include <stratosphere/fs/fs_mount.hpp>
|
||||||
#include <stratosphere/fs/fs_path_tool.hpp>
|
#include <stratosphere/fs/fs_path_tool.hpp>
|
||||||
#include <stratosphere/fs/fs_path_utils.hpp>
|
#include <stratosphere/fs/fs_path_utils.hpp>
|
||||||
|
#include <stratosphere/fs/fs_filesystem_utils.hpp>
|
||||||
|
#include <stratosphere/fs/fs_romfs_filesystem.hpp>
|
||||||
|
#include <stratosphere/fs/impl/fs_data.hpp>
|
||||||
|
#include <stratosphere/fs/fs_application.hpp>
|
||||||
|
#include <stratosphere/fs/fs_bis.hpp>
|
||||||
|
#include <stratosphere/fs/fs_code.hpp>
|
||||||
|
#include <stratosphere/fs/fs_content.hpp>
|
||||||
|
#include <stratosphere/fs/fs_content_storage.hpp>
|
||||||
|
#include <stratosphere/fs/fs_game_card.hpp>
|
||||||
|
#include <stratosphere/fs/fs_save_data_types.hpp>
|
||||||
|
#include <stratosphere/fs/fs_save_data_management.hpp>
|
||||||
|
#include <stratosphere/fs/fs_save_data_transaction.hpp>
|
||||||
|
#include <stratosphere/fs/fs_device_save_data.hpp>
|
||||||
|
#include <stratosphere/fs/fs_system_save_data.hpp>
|
||||||
|
#include <stratosphere/fs/fs_sd_card.hpp>
|
||||||
|
#include <stratosphere/fs/fs_signed_system_partition.hpp>
|
||||||
|
#include <stratosphere/fs/fs_system_data.hpp>
|
||||||
|
|||||||
@@ -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 "fs_common.hpp"
|
||||||
|
|
||||||
|
namespace ams::fs {
|
||||||
|
|
||||||
|
Result MountApplicationPackage(const char *name, const char *common_path);
|
||||||
|
|
||||||
|
}
|
||||||
58
libraries/libstratosphere/include/stratosphere/fs/fs_bis.hpp
Normal file
58
libraries/libstratosphere/include/stratosphere/fs/fs_bis.hpp
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere/fs/fs_common.hpp>
|
||||||
|
#include <stratosphere/fs/fs_istorage.hpp>
|
||||||
|
|
||||||
|
namespace ams::fs {
|
||||||
|
|
||||||
|
enum class BisPartitionId {
|
||||||
|
/* Boot0 */
|
||||||
|
BootPartition1Root = 0,
|
||||||
|
|
||||||
|
/* Boot1 */
|
||||||
|
BootPartition2Root = 10,
|
||||||
|
|
||||||
|
/* Non-Boot */
|
||||||
|
UserDataRoot = 20,
|
||||||
|
BootConfigAndPackage2Part1 = 21,
|
||||||
|
BootConfigAndPackage2Part2 = 22,
|
||||||
|
BootConfigAndPackage2Part3 = 23,
|
||||||
|
BootConfigAndPackage2Part4 = 24,
|
||||||
|
BootConfigAndPackage2Part5 = 25,
|
||||||
|
BootConfigAndPackage2Part6 = 26,
|
||||||
|
CalibrationBinary = 27,
|
||||||
|
CalibrationFile = 28,
|
||||||
|
SafeMode = 29,
|
||||||
|
User = 30,
|
||||||
|
System = 31,
|
||||||
|
SystemProperEncryption = 32,
|
||||||
|
SystemProperPartition = 33,
|
||||||
|
SignedSystemPartitionOnSafeMode = 34,
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *GetBisMountName(BisPartitionId id);
|
||||||
|
|
||||||
|
Result MountBis(BisPartitionId id, const char *root_path);
|
||||||
|
Result MountBis(const char *name, BisPartitionId id);
|
||||||
|
|
||||||
|
void SetBisRootForHost(BisPartitionId id, const char *root_path);
|
||||||
|
|
||||||
|
Result OpenBisPartition(std::unique_ptr<fs::IStorage> *out, BisPartitionId id);
|
||||||
|
|
||||||
|
Result InvalidateBisCache();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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 "fs_common.hpp"
|
||||||
|
#include <stratosphere/ncm/ncm_ids.hpp>
|
||||||
|
|
||||||
|
namespace ams::fs {
|
||||||
|
|
||||||
|
Result MountCode(const char *name, const char *path, ncm::ProgramId program_id);
|
||||||
|
|
||||||
|
Result MountCodeForAtmosphereWithRedirection(const char *name, const char *path, ncm::ProgramId program_id, bool is_hbl, bool is_specific);
|
||||||
|
Result MountCodeForAtmosphere(const char *name, const char *path, ncm::ProgramId program_id);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -15,13 +15,34 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <vapours.hpp>
|
#include <vapours.hpp>
|
||||||
#include "../os.hpp"
|
#include <stratosphere/os.hpp>
|
||||||
#include "../ncm.hpp"
|
#include <stratosphere/ncm/ncm_ids.hpp>
|
||||||
#include "../sf.hpp"
|
#include <stratosphere/sf.hpp>
|
||||||
|
|
||||||
namespace ams::fs {
|
namespace ams::fs {
|
||||||
|
|
||||||
/* TODO: Better place for this? */
|
struct Int64 {
|
||||||
constexpr inline size_t MountNameLengthMax = 15;
|
u32 low;
|
||||||
|
u32 high;
|
||||||
|
|
||||||
}
|
constexpr ALWAYS_INLINE void Set(s64 v) {
|
||||||
|
this->low = static_cast<u32>((v & static_cast<u64>(0x00000000FFFFFFFFul)) >> 0);
|
||||||
|
this->high = static_cast<u32>((v & static_cast<u64>(0xFFFFFFFF00000000ul)) >> 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr ALWAYS_INLINE s64 Get() const {
|
||||||
|
return (static_cast<s64>(this->high) << 32) | (static_cast<s64>(this->low));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr ALWAYS_INLINE Int64 &operator=(s64 v) {
|
||||||
|
this->Set(v);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr ALWAYS_INLINE operator s64() const {
|
||||||
|
return this->Get();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static_assert(std::is_pod<Int64>::value);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "fs_common.hpp"
|
||||||
|
#include <stratosphere/ncm/ncm_ids.hpp>
|
||||||
|
|
||||||
|
namespace ams::fs {
|
||||||
|
|
||||||
|
enum ContentType {
|
||||||
|
ContentType_Meta = 0,
|
||||||
|
ContentType_Control = 1,
|
||||||
|
ContentType_Manual = 2,
|
||||||
|
ContentType_Logo = 3,
|
||||||
|
ContentType_Data = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
Result MountContent(const char *name, const char *path, ContentType content_type);
|
||||||
|
Result MountContent(const char *name, const char *path, ncm::ProgramId id, ContentType content_type);
|
||||||
|
Result MountContent(const char *name, const char *path, ncm::DataId id, ContentType content_type);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "fs_common.hpp"
|
||||||
|
|
||||||
|
namespace ams::fs {
|
||||||
|
|
||||||
|
enum class ContentStorageId : u32 {
|
||||||
|
System = 0,
|
||||||
|
User = 1,
|
||||||
|
SdCard = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr inline const char * const ContentStorageDirectoryName = "Contents";
|
||||||
|
|
||||||
|
const char *GetContentStorageMountName(ContentStorageId id);
|
||||||
|
|
||||||
|
Result MountContentStorage(ContentStorageId id);
|
||||||
|
Result MountContentStorage(const char *name, ContentStorageId id);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "fs_dbm_rom_types.hpp"
|
||||||
|
#include "fs_dbm_rom_path_tool.hpp"
|
||||||
|
#include "fs_dbm_rom_key_value_storage.hpp"
|
||||||
|
|
||||||
|
namespace ams::fs {
|
||||||
|
|
||||||
|
class HierarchicalRomFileTable {
|
||||||
|
public:
|
||||||
|
using Position = u32;
|
||||||
|
|
||||||
|
struct FindPosition {
|
||||||
|
Position next_dir;
|
||||||
|
Position next_file;
|
||||||
|
};
|
||||||
|
static_assert(std::is_pod<FindPosition>::value);
|
||||||
|
|
||||||
|
using DirectoryInfo = RomDirectoryInfo;
|
||||||
|
using FileInfo = RomFileInfo;
|
||||||
|
|
||||||
|
static constexpr RomFileId ConvertToFileId(Position pos) {
|
||||||
|
return static_cast<RomFileId>(pos);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
static constexpr inline Position InvalidPosition = ~Position();
|
||||||
|
static constexpr inline Position RootPosition = 0;
|
||||||
|
static constexpr inline size_t ReservedDirectoryCount = 1;
|
||||||
|
|
||||||
|
static constexpr RomDirectoryId ConvertToDirectoryId(Position pos) {
|
||||||
|
return static_cast<RomDirectoryId>(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr Position ConvertToPosition(RomDirectoryId id) {
|
||||||
|
return static_cast<Position>(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assert(std::is_same<RomDirectoryId, RomFileId>::value);
|
||||||
|
|
||||||
|
struct RomDirectoryEntry {
|
||||||
|
Position next;
|
||||||
|
Position dir;
|
||||||
|
Position file;
|
||||||
|
};
|
||||||
|
static_assert(std::is_pod<RomDirectoryEntry>::value);
|
||||||
|
|
||||||
|
struct RomFileEntry {
|
||||||
|
Position next;
|
||||||
|
FileInfo info;
|
||||||
|
};
|
||||||
|
static_assert(std::is_pod<RomFileEntry>::value);
|
||||||
|
|
||||||
|
static constexpr inline u32 MaxKeyLength = RomPathTool::MaxPathLength;
|
||||||
|
|
||||||
|
template<typename ImplKeyType, typename ClientKeyType, typename ValueType>
|
||||||
|
class EntryMapTable : public RomKeyValueStorage<ImplKeyType, ValueType, MaxKeyLength> {
|
||||||
|
public:
|
||||||
|
using ImplKey = ImplKeyType;
|
||||||
|
using ClientKey = ClientKeyType;
|
||||||
|
using Value = ValueType;
|
||||||
|
using Position = HierarchicalRomFileTable::Position;
|
||||||
|
using Base = RomKeyValueStorage<ImplKeyType, ValueType, MaxKeyLength>;
|
||||||
|
public:
|
||||||
|
Result Add(Position *out, const ClientKeyType &key, const Value &value) {
|
||||||
|
return Base::AddImpl(out, key.key, key.Hash(), key.name.path, key.name.length * sizeof(RomPathChar), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Get(Position *out_pos, Value *out_val, const ClientKeyType &key) {
|
||||||
|
return Base::GetImpl(out_pos, out_val, key.key, key.Hash(), key.name.path, key.name.length * sizeof(RomPathChar));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetByPosition(ImplKey *out_key, Value *out_val, Position pos) {
|
||||||
|
return Base::GetByPosition(out_key, out_val, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetByPosition(ImplKey *out_key, Value *out_val, void *out_aux, size_t *out_aux_size, Position pos) {
|
||||||
|
return Base::GetByPosition(out_key, out_val, out_aux, out_aux_size, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SetByPosition(Position pos, const Value &value) {
|
||||||
|
return Base::SetByPosition(pos, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RomEntryKey {
|
||||||
|
Position parent;
|
||||||
|
|
||||||
|
bool IsEqual(const RomEntryKey &rhs, const void *aux_lhs, size_t aux_lhs_size, const void *aux_rhs, size_t aux_rhs_size) const {
|
||||||
|
if (this->parent != rhs.parent) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (aux_lhs_size != aux_rhs_size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return RomPathTool::IsEqualPath(reinterpret_cast<const RomPathChar *>(aux_lhs), reinterpret_cast<const RomPathChar *>(aux_rhs), aux_lhs_size / sizeof(RomPathChar));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static_assert(std::is_pod<RomEntryKey>::value);
|
||||||
|
|
||||||
|
struct EntryKey {
|
||||||
|
RomEntryKey key;
|
||||||
|
RomPathTool::RomEntryName name;
|
||||||
|
|
||||||
|
constexpr u32 Hash() const {
|
||||||
|
u32 hash = this->key.parent ^ 123456789;
|
||||||
|
const RomPathChar *name = this->name.path;
|
||||||
|
const RomPathChar *end = name + this->name.length;
|
||||||
|
while (name < end) {
|
||||||
|
const u32 cur = static_cast<u32>(static_cast<std::make_unsigned<RomPathChar>::type>(*(name++)));
|
||||||
|
hash = ((hash >> 5) | (hash << 27)) ^ cur;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static_assert(std::is_pod<EntryKey>::value);
|
||||||
|
|
||||||
|
using DirectoryEntryMapTable = EntryMapTable<RomEntryKey, EntryKey, RomDirectoryEntry>;
|
||||||
|
using FileEntryMapTable = EntryMapTable<RomEntryKey, EntryKey, RomFileEntry>;
|
||||||
|
private:
|
||||||
|
DirectoryEntryMapTable dir_table;
|
||||||
|
FileEntryMapTable file_table;
|
||||||
|
public:
|
||||||
|
static s64 QueryDirectoryEntryStorageSize(u32 count);
|
||||||
|
static s64 QueryDirectoryEntryBucketStorageSize(s64 count);
|
||||||
|
static s64 QueryFileEntryStorageSize(u32 count);
|
||||||
|
static s64 QueryFileEntryBucketStorageSize(s64 count);
|
||||||
|
|
||||||
|
static Result Format(SubStorage dir_bucket, SubStorage file_bucket);
|
||||||
|
public:
|
||||||
|
HierarchicalRomFileTable();
|
||||||
|
|
||||||
|
constexpr u32 GetDirectoryEntryCount() const {
|
||||||
|
return this->dir_table.GetEntryCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr u32 GetFileEntryCount() const {
|
||||||
|
return this->file_table.GetEntryCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Initialize(SubStorage dir_bucket, SubStorage dir_entry, SubStorage file_bucket, SubStorage file_entry);
|
||||||
|
void Finalize();
|
||||||
|
|
||||||
|
Result CreateRootDirectory();
|
||||||
|
Result CreateDirectory(RomDirectoryId *out, const RomPathChar *path, const DirectoryInfo &info);
|
||||||
|
Result CreateFile(RomFileId *out, const RomPathChar *path, const FileInfo &info);
|
||||||
|
Result ConvertPathToDirectoryId(RomDirectoryId *out, const RomPathChar *path);
|
||||||
|
Result ConvertPathToFileId(RomFileId *out, const RomPathChar *path);
|
||||||
|
|
||||||
|
Result GetDirectoryInformation(DirectoryInfo *out, const RomPathChar *path);
|
||||||
|
Result GetDirectoryInformation(DirectoryInfo *out, RomDirectoryId id);
|
||||||
|
|
||||||
|
Result OpenFile(FileInfo *out, const RomPathChar *path);
|
||||||
|
Result OpenFile(FileInfo *out, RomFileId id);
|
||||||
|
|
||||||
|
Result FindOpen(FindPosition *out, const RomPathChar *path);
|
||||||
|
Result FindOpen(FindPosition *out, RomDirectoryId id);
|
||||||
|
|
||||||
|
Result FindNextDirectory(RomPathChar *out, FindPosition *find, size_t length);
|
||||||
|
Result FindNextFile(RomPathChar *out, FindPosition *find, size_t length);
|
||||||
|
|
||||||
|
Result QueryRomFileSystemSize(s64 *out_dir_entry_size, s64 *out_file_entry_size);
|
||||||
|
private:
|
||||||
|
Result GetGrandParent(Position *out_pos, EntryKey *out_dir_key, RomDirectoryEntry *out_dir_entry, Position pos, RomPathTool::RomEntryName name, const RomPathChar *path);
|
||||||
|
|
||||||
|
Result FindParentDirectoryRecursive(Position *out_pos, EntryKey *out_dir_key, RomDirectoryEntry *out_dir_entry, RomPathTool::PathParser *parser, const RomPathChar *path);
|
||||||
|
|
||||||
|
Result FindPathRecursive(EntryKey *out_key, RomDirectoryEntry *out_dir_entry, bool is_dir, const RomPathChar *path);
|
||||||
|
Result FindDirectoryRecursive(EntryKey *out_key, RomDirectoryEntry *out_dir_entry, const RomPathChar *path);
|
||||||
|
Result FindFileRecursive(EntryKey *out_key, RomDirectoryEntry *out_dir_entry, const RomPathChar *path);
|
||||||
|
|
||||||
|
Result CheckSameEntryExists(const EntryKey &key, Result if_exists);
|
||||||
|
|
||||||
|
Result GetDirectoryEntry(Position *out_pos, RomDirectoryEntry *out_entry, const EntryKey &key);
|
||||||
|
Result GetDirectoryEntry(RomDirectoryEntry *out_entry, RomDirectoryId id);
|
||||||
|
|
||||||
|
Result GetFileEntry(Position *out_pos, RomFileEntry *out_entry, const EntryKey &key);
|
||||||
|
Result GetFileEntry(RomFileEntry *out_entry, RomFileId id);
|
||||||
|
|
||||||
|
Result GetDirectoryInformation(DirectoryInfo *out, const EntryKey &key);
|
||||||
|
|
||||||
|
Result OpenFile(FileInfo *out, const EntryKey &key);
|
||||||
|
|
||||||
|
Result FindOpen(FindPosition *out, const EntryKey &key);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,380 @@
|
|||||||
|
/*
|
||||||
|
* 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 "fs_dbm_rom_types.hpp"
|
||||||
|
#include "fs_substorage.hpp"
|
||||||
|
|
||||||
|
namespace ams::fs {
|
||||||
|
|
||||||
|
template<typename KeyType, typename ValueType, size_t MaxAuxiliarySize>
|
||||||
|
class RomKeyValueStorage {
|
||||||
|
public:
|
||||||
|
using Key = KeyType;
|
||||||
|
using Value = ValueType;
|
||||||
|
using Position = u32;
|
||||||
|
using BucketIndex = s64;
|
||||||
|
|
||||||
|
struct FindIndex {
|
||||||
|
BucketIndex ind;
|
||||||
|
Position pos;
|
||||||
|
};
|
||||||
|
static_assert(std::is_pod<FindIndex>::value);
|
||||||
|
private:
|
||||||
|
static constexpr inline Position InvalidPosition = ~Position();
|
||||||
|
|
||||||
|
struct Element {
|
||||||
|
Key key;
|
||||||
|
Value value;
|
||||||
|
Position next;
|
||||||
|
u32 size;
|
||||||
|
};
|
||||||
|
static_assert(std::is_pod<Element>::value);
|
||||||
|
private:
|
||||||
|
s64 bucket_count;
|
||||||
|
SubStorage bucket_storage;
|
||||||
|
SubStorage kv_storage;
|
||||||
|
s64 total_entry_size;
|
||||||
|
u32 entry_count;
|
||||||
|
public:
|
||||||
|
static constexpr s64 QueryBucketStorageSize(s64 num) {
|
||||||
|
return num * sizeof(Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr s64 QueryBucketCount(s64 size) {
|
||||||
|
return size / sizeof(Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr s64 QueryKeyValueStorageSize(u32 num) {
|
||||||
|
return num * sizeof(Element);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Result Format(SubStorage bucket, s64 count) {
|
||||||
|
const Position pos = InvalidPosition;
|
||||||
|
for (s64 i = 0; i < count; i++) {
|
||||||
|
R_TRY(bucket.Write(i * sizeof(pos), std::addressof(pos), sizeof(pos)));
|
||||||
|
}
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
RomKeyValueStorage() : bucket_count(), bucket_storage(), kv_storage(), total_entry_size(), entry_count() { /* ... */ }
|
||||||
|
|
||||||
|
Result Initialize(const SubStorage &bucket, s64 count, const SubStorage &kv) {
|
||||||
|
AMS_ASSERT(count > 0);
|
||||||
|
this->bucket_storage = bucket;
|
||||||
|
this->kv_storage = kv;
|
||||||
|
this->bucket_count = count;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Finalize() {
|
||||||
|
this->bucket_storage = SubStorage();
|
||||||
|
this->kv_storage = SubStorage();
|
||||||
|
this->bucket_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
s64 GetTotalEntrySize() const {
|
||||||
|
return this->total_entry_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetFreeSize(s64 *out) {
|
||||||
|
AMS_ASSERT(out != nullptr);
|
||||||
|
s64 kv_size = 0;
|
||||||
|
R_TRY(this->kv_storage.GetSize(std::addressof(kv_size)));
|
||||||
|
*out = kv_size - this->total_entry_size;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr u32 GetEntryCount() const {
|
||||||
|
return this->entry_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Add(const Key &key, u32 hash_key, const void *aux, size_t aux_size, const Value &value) {
|
||||||
|
AMS_ASSERT(aux != nullptr);
|
||||||
|
AMS_ASSERT(aux_size <= MaxAuxiliarySize);
|
||||||
|
Position pos;
|
||||||
|
return this->AddImpl(std::addressof(pos), key, hash_key, aux, aux_size, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Get(Value *out, const Key &key, u32 hash_key, const void *aux, size_t aux_size) {
|
||||||
|
AMS_ASSERT(aux != nullptr);
|
||||||
|
AMS_ASSERT(aux_size <= MaxAuxiliarySize);
|
||||||
|
Position pos;
|
||||||
|
return this->GetImpl(std::addressof(pos), out, key, hash_key, aux, aux_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FindOpen(FindIndex *out) const {
|
||||||
|
AMS_ASSERT(out != nullptr);
|
||||||
|
|
||||||
|
out->ind = static_cast<BucketIndex>(-1);
|
||||||
|
out->pos = InvalidPosition;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FindNext(Key *out_key, Value *out_val, FindIndex *find) {
|
||||||
|
AMS_ASSERT(out_key != nullptr);
|
||||||
|
AMS_ASSERT(out_val != nullptr);
|
||||||
|
AMS_ASSERT(find != nullptr);
|
||||||
|
|
||||||
|
BucketIndex ind = find->ind;
|
||||||
|
R_UNLESS((ind < this->bucket_count) || ind == static_cast<BucketIndex>(-1), fs::ResultDbmFindKeyFinished());
|
||||||
|
|
||||||
|
s64 kv_size;
|
||||||
|
R_TRY(this->kv_storage.GetSize(std::addressof(kv_size)));
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (find->pos != InvalidPosition) {
|
||||||
|
Element elem;
|
||||||
|
R_TRY(this->ReadKeyValue(std::addressof(elem), find->pos));
|
||||||
|
|
||||||
|
AMS_ASSERT(elem.next == InvalidPosition || elem.next < kv_size);
|
||||||
|
find->pos = elem.next;
|
||||||
|
*out_key = elem.key;
|
||||||
|
*out_val = elem.val;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
ind++;
|
||||||
|
if (ind == this->bucket_count) {
|
||||||
|
find->ind = ind;
|
||||||
|
find->pos = InvalidPosition;
|
||||||
|
return fs::ResultDbmFindKeyFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
Position pos;
|
||||||
|
R_TRY(this->ReadBucket(std::addressof(pos), ind));
|
||||||
|
AMS_ASSERT(pos == InvalidPosition || pos < kv_size);
|
||||||
|
|
||||||
|
if (pos != InvalidPosition) {
|
||||||
|
find->ind = ind;
|
||||||
|
find->pos = pos;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
Result AddImpl(Position *out, const Key &key, u32 hash_key, const void *aux, size_t aux_size, const Value &value) {
|
||||||
|
AMS_ASSERT(out != nullptr);
|
||||||
|
AMS_ASSERT(aux != nullptr);
|
||||||
|
AMS_ASSERT(this->bucket_count > 0);
|
||||||
|
|
||||||
|
{
|
||||||
|
Position pos, prev_pos;
|
||||||
|
Element elem;
|
||||||
|
|
||||||
|
const Result find_res = this->FindImpl(std::addressof(pos), std::addressof(prev_pos), std::addressof(elem), key, hash_key, aux, aux_size);
|
||||||
|
R_UNLESS(R_FAILED(find_res), fs::ResultDbmAlreadyExists());
|
||||||
|
R_UNLESS(fs::ResultDbmKeyNotFound::Includes(find_res), find_res);
|
||||||
|
}
|
||||||
|
|
||||||
|
Position pos;
|
||||||
|
R_TRY(this->AllocateEntry(std::addressof(pos), aux_size));
|
||||||
|
|
||||||
|
Position next_pos;
|
||||||
|
R_TRY(this->LinkEntry(std::addressof(next_pos), pos, hash_key));
|
||||||
|
|
||||||
|
const Element elem = { key, value, next_pos, static_cast<u32>(aux_size) };
|
||||||
|
R_TRY(this->WriteKeyValue(std::addressof(elem), pos, aux, aux_size));
|
||||||
|
|
||||||
|
*out = pos;
|
||||||
|
this->entry_count++;
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetImpl(Position *out_pos, Value *out_val, const Key &key, u32 hash_key, const void *aux, size_t aux_size) {
|
||||||
|
AMS_ASSERT(out_pos != nullptr);
|
||||||
|
AMS_ASSERT(out_val != nullptr);
|
||||||
|
AMS_ASSERT(aux != nullptr);
|
||||||
|
|
||||||
|
Position pos, prev_pos;
|
||||||
|
Element elem;
|
||||||
|
R_TRY(this->FindImpl(std::addressof(pos), std::addressof(prev_pos), std::addressof(elem), key, hash_key, aux, aux_size));
|
||||||
|
|
||||||
|
*out_pos = pos;
|
||||||
|
*out_val = elem.value;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetByPosition(Key *out_key, Value *out_val, Position pos) {
|
||||||
|
AMS_ASSERT(out_key != nullptr);
|
||||||
|
AMS_ASSERT(out_val != nullptr);
|
||||||
|
|
||||||
|
Element elem;
|
||||||
|
R_TRY(this->ReadKeyValue(std::addressof(elem), pos));
|
||||||
|
|
||||||
|
*out_key = elem.key;
|
||||||
|
*out_val = elem.value;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetByPosition(Key *out_key, Value *out_val, void *out_aux, size_t *out_aux_size, Position pos) {
|
||||||
|
AMS_ASSERT(out_key != nullptr);
|
||||||
|
AMS_ASSERT(out_val != nullptr);
|
||||||
|
AMS_ASSERT(out_aux != nullptr);
|
||||||
|
AMS_ASSERT(out_aux_size != nullptr);
|
||||||
|
|
||||||
|
Element elem;
|
||||||
|
R_TRY(this->ReadKeyValue(std::addressof(elem), out_aux, out_aux_size, pos));
|
||||||
|
|
||||||
|
*out_key = elem.key;
|
||||||
|
*out_val = elem.value;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SetByPosition(Position pos, const Value &value) {
|
||||||
|
Element elem;
|
||||||
|
R_TRY(this->ReadKeyValue(std::addressof(elem), pos));
|
||||||
|
elem.value = value;
|
||||||
|
return this->WriteKeyValue(std::addressof(elem), pos, nullptr, 0);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
BucketIndex HashToBucket(u32 hash_key) const {
|
||||||
|
return hash_key % this->bucket_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FindImpl(Position *out_pos, Position *out_prev, Element *out_elem, const Key &key, u32 hash_key, const void *aux, size_t aux_size) {
|
||||||
|
AMS_ASSERT(out_pos != nullptr);
|
||||||
|
AMS_ASSERT(out_prev != nullptr);
|
||||||
|
AMS_ASSERT(out_elem != nullptr);
|
||||||
|
AMS_ASSERT(aux != nullptr);
|
||||||
|
AMS_ASSERT(this->bucket_count > 0);
|
||||||
|
|
||||||
|
*out_pos = 0;
|
||||||
|
*out_prev = 0;
|
||||||
|
|
||||||
|
const BucketIndex ind = HashToBucket(hash_key);
|
||||||
|
|
||||||
|
Position cur;
|
||||||
|
R_TRY(this->ReadBucket(std::addressof(cur), ind));
|
||||||
|
|
||||||
|
s64 kv_size;
|
||||||
|
R_TRY(this->kv_storage.GetSize(std::addressof(kv_size)));
|
||||||
|
AMS_ASSERT(cur == InvalidPosition || cur < kv_size);
|
||||||
|
|
||||||
|
R_UNLESS(cur != InvalidPosition, fs::ResultDbmKeyNotFound());
|
||||||
|
|
||||||
|
u8 *buf = static_cast<u8 *>(::ams::fs::impl::Allocate(MaxAuxiliarySize));
|
||||||
|
R_UNLESS(buf != nullptr, fs::ResultAllocationFailureInDbmRomKeyValueStorage());
|
||||||
|
ON_SCOPE_EXIT { ::ams::fs::impl::Deallocate(buf, MaxAuxiliarySize); };
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
size_t cur_aux_size;
|
||||||
|
R_TRY(this->ReadKeyValue(out_elem, buf, std::addressof(cur_aux_size), cur));
|
||||||
|
|
||||||
|
if (key.IsEqual(out_elem->key, aux, aux_size, buf, cur_aux_size)) {
|
||||||
|
*out_pos = cur;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_prev = cur;
|
||||||
|
cur = out_elem->next;
|
||||||
|
R_UNLESS(cur != InvalidPosition, fs::ResultDbmKeyNotFound());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result AllocateEntry(Position *out, size_t aux_size) {
|
||||||
|
AMS_ASSERT(out != nullptr);
|
||||||
|
|
||||||
|
s64 kv_size;
|
||||||
|
R_TRY(this->kv_storage.GetSize(std::addressof(kv_size)));
|
||||||
|
const size_t end_pos = this->total_entry_size + sizeof(Element) + aux_size;
|
||||||
|
R_UNLESS(end_pos <= static_cast<size_t>(kv_size), fs::ResultDbmKeyFull());
|
||||||
|
|
||||||
|
*out = static_cast<Position>(this->total_entry_size);
|
||||||
|
|
||||||
|
this->total_entry_size = util::AlignUp(static_cast<s64>(end_pos), s64(4));
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result LinkEntry(Position *out, Position pos, u32 hash_key) {
|
||||||
|
AMS_ASSERT(out != nullptr);
|
||||||
|
|
||||||
|
const BucketIndex ind = HashToBucket(hash_key);
|
||||||
|
|
||||||
|
Position next;
|
||||||
|
R_TRY(this->ReadBucket(std::addressof(next), ind));
|
||||||
|
|
||||||
|
s64 kv_size;
|
||||||
|
R_TRY(this->kv_storage.GetSize(std::addressof(kv_size)));
|
||||||
|
AMS_ASSERT(next == InvalidPosition || next < kv_size);
|
||||||
|
|
||||||
|
R_TRY(this->WriteBucket(pos, ind));
|
||||||
|
|
||||||
|
*out = next;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ReadBucket(Position *out, BucketIndex ind) {
|
||||||
|
AMS_ASSERT(out != nullptr);
|
||||||
|
AMS_ASSERT(ind < this->bucket_count);
|
||||||
|
|
||||||
|
const s64 offset = ind * sizeof(Position);
|
||||||
|
return this->bucket_storage.Read(offset, out, sizeof(*out));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result WriteBucket(Position pos, BucketIndex ind) {
|
||||||
|
AMS_ASSERT(ind < this->bucket_count);
|
||||||
|
|
||||||
|
const s64 offset = ind * sizeof(Position);
|
||||||
|
return this->bucket_storage.Write(offset, std::addressof(pos), sizeof(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ReadKeyValue(Element *out, Position pos) {
|
||||||
|
AMS_ASSERT(out != nullptr);
|
||||||
|
|
||||||
|
s64 kv_size;
|
||||||
|
R_TRY(this->kv_storage.GetSize(std::addressof(kv_size)));
|
||||||
|
AMS_ASSERT(pos < kv_size);
|
||||||
|
|
||||||
|
return this->kv_storage.Read(pos, out, sizeof(*out));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ReadKeyValue(Element *out, void *out_aux, size_t *out_aux_size, Position pos) {
|
||||||
|
AMS_ASSERT(out != nullptr);
|
||||||
|
AMS_ASSERT(out_aux != nullptr);
|
||||||
|
AMS_ASSERT(out_aux_size != nullptr);
|
||||||
|
|
||||||
|
R_TRY(this->ReadKeyValue(out, pos));
|
||||||
|
|
||||||
|
*out_aux_size = out->size;
|
||||||
|
if (out->size > 0) {
|
||||||
|
R_TRY(this->kv_storage.Read(pos + sizeof(*out), out_aux, out->size));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result WriteKeyValue(const Element *elem, Position pos, const void *aux, size_t aux_size) {
|
||||||
|
AMS_ASSERT(elem != nullptr);
|
||||||
|
AMS_ASSERT(aux != nullptr);
|
||||||
|
|
||||||
|
s64 kv_size;
|
||||||
|
R_TRY(this->kv_storage.GetSize(std::addressof(kv_size)));
|
||||||
|
AMS_ASSERT(pos < kv_size);
|
||||||
|
|
||||||
|
R_TRY(this->kv_storage.Write(pos, elem, sizeof(*elem)));
|
||||||
|
|
||||||
|
if (aux != nullptr && aux_size > 0) {
|
||||||
|
R_TRY(this->kv_storage.Write(pos + sizeof(*elem), aux, aux_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* 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 "fs_dbm_rom_types.hpp"
|
||||||
|
|
||||||
|
namespace ams::fs {
|
||||||
|
|
||||||
|
namespace RomPathTool {
|
||||||
|
|
||||||
|
constexpr inline u32 MaxPathLength = 0x300;
|
||||||
|
|
||||||
|
struct RomEntryName {
|
||||||
|
size_t length;
|
||||||
|
const RomPathChar *path;
|
||||||
|
};
|
||||||
|
static_assert(std::is_pod<RomEntryName>::value);
|
||||||
|
|
||||||
|
constexpr void InitializeRomEntryName(RomEntryName *entry) {
|
||||||
|
AMS_ABORT_UNLESS(entry != nullptr);
|
||||||
|
entry->length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline bool IsSeparator(RomPathChar c) {
|
||||||
|
return c == RomStringTraits::DirectorySeparator;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline bool IsNullTerminator(RomPathChar c) {
|
||||||
|
return c == RomStringTraits::NullTerminator;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline bool IsDot(RomPathChar c) {
|
||||||
|
return c == RomStringTraits::Dot;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline bool IsCurrentDirectory(const RomEntryName &name) {
|
||||||
|
return name.length == 1 && IsDot(name.path[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline bool IsCurrentDirectory(const RomPathChar *p, size_t length) {
|
||||||
|
AMS_ABORT_UNLESS(p != nullptr);
|
||||||
|
return length == 1 && IsDot(p[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline bool IsCurrentDirectory(const RomPathChar *p) {
|
||||||
|
AMS_ABORT_UNLESS(p != nullptr);
|
||||||
|
return IsDot(p[0]) && IsNullTerminator(p[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline bool IsParentDirectory(const RomEntryName &name) {
|
||||||
|
return name.length == 2 && IsDot(name.path[0]) && IsDot(name.path[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline bool IsParentDirectory(const RomPathChar *p) {
|
||||||
|
AMS_ABORT_UNLESS(p != nullptr);
|
||||||
|
return IsDot(p[0]) && IsDot(p[1]) && IsNullTerminator(p[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline bool IsParentDirectory(const RomPathChar *p, size_t length) {
|
||||||
|
AMS_ABORT_UNLESS(p != nullptr);
|
||||||
|
return length == 2 && IsDot(p[0]) && IsDot(p[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline bool IsEqualPath(const RomPathChar *lhs, const RomPathChar *rhs, size_t length) {
|
||||||
|
AMS_ABORT_UNLESS(lhs != nullptr);
|
||||||
|
AMS_ABORT_UNLESS(rhs != nullptr);
|
||||||
|
return std::strncmp(lhs, rhs, length) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline bool IsEqualName(const RomEntryName &lhs, const RomPathChar *rhs) {
|
||||||
|
AMS_ABORT_UNLESS(rhs != nullptr);
|
||||||
|
if (strnlen(rhs, MaxPathLength) != lhs.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return IsEqualPath(lhs.path, rhs, lhs.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline bool IsEqualName(const RomEntryName &lhs, const RomEntryName &rhs) {
|
||||||
|
if (lhs.length != rhs.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return IsEqualPath(lhs.path, rhs.path, lhs.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetParentDirectoryName(RomEntryName *out, const RomEntryName &cur, const RomPathChar *p);
|
||||||
|
|
||||||
|
class PathParser {
|
||||||
|
private:
|
||||||
|
const RomPathChar *prev_path_start;
|
||||||
|
const RomPathChar *prev_path_end;
|
||||||
|
const RomPathChar *next_path;
|
||||||
|
bool finished;
|
||||||
|
public:
|
||||||
|
constexpr PathParser() : prev_path_start(), prev_path_end(), next_path(), finished() { /* ... */ }
|
||||||
|
|
||||||
|
Result Initialize(const RomPathChar *path);
|
||||||
|
void Finalize();
|
||||||
|
|
||||||
|
bool IsFinished() const;
|
||||||
|
bool IsDirectoryPath() const;
|
||||||
|
|
||||||
|
Result GetAsDirectoryName(RomEntryName *out) const;
|
||||||
|
Result GetAsFileName(RomEntryName *out) const;
|
||||||
|
|
||||||
|
Result GetNextDirectoryName(RomEntryName *out);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* 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 "fs_common.hpp"
|
||||||
|
|
||||||
|
namespace ams::fs {
|
||||||
|
|
||||||
|
using RomPathChar = char;
|
||||||
|
using RomFileId = s32;
|
||||||
|
using RomDirectoryId = s32;
|
||||||
|
|
||||||
|
struct RomFileSystemInformation {
|
||||||
|
s64 size;
|
||||||
|
s64 directory_bucket_offset;
|
||||||
|
s64 directory_bucket_size;
|
||||||
|
s64 directory_entry_offset;
|
||||||
|
s64 directory_entry_size;
|
||||||
|
s64 file_bucket_offset;
|
||||||
|
s64 file_bucket_size;
|
||||||
|
s64 file_entry_offset;
|
||||||
|
s64 file_entry_size;
|
||||||
|
s64 body_offset;
|
||||||
|
};
|
||||||
|
static_assert(std::is_pod<RomFileSystemInformation>::value);
|
||||||
|
static_assert(sizeof(RomFileSystemInformation) == 0x50);
|
||||||
|
|
||||||
|
struct RomDirectoryInfo {
|
||||||
|
/* ... */
|
||||||
|
};
|
||||||
|
static_assert(std::is_pod<RomDirectoryInfo>::value);
|
||||||
|
|
||||||
|
struct RomFileInfo {
|
||||||
|
Int64 offset;
|
||||||
|
Int64 size;
|
||||||
|
};
|
||||||
|
static_assert(std::is_pod<RomFileInfo>::value);
|
||||||
|
|
||||||
|
namespace RomStringTraits {
|
||||||
|
|
||||||
|
constexpr inline char DirectorySeparator = '/';
|
||||||
|
constexpr inline char NullTerminator = '\x00';
|
||||||
|
constexpr inline char Dot = '.';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "fs_common.hpp"
|
||||||
|
#include "fs_save_data_types.hpp"
|
||||||
|
|
||||||
|
namespace ams::fs {
|
||||||
|
|
||||||
|
Result MountDeviceSaveData(const char *name);
|
||||||
|
Result MountDeviceSaveData(const char *name, const ncm::ApplicationId application_id);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -22,4 +22,12 @@ namespace ams::fs {
|
|||||||
|
|
||||||
using DirectoryEntry = ::FsDirectoryEntry;
|
using DirectoryEntry = ::FsDirectoryEntry;
|
||||||
|
|
||||||
|
struct DirectoryHandle {
|
||||||
|
void *handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
Result ReadDirectory(s64 *out_count, DirectoryEntry *out_entries, DirectoryHandle handle, s64 max_entries);
|
||||||
|
Result GetDirectoryEntryCount(s64 *out, DirectoryHandle handle);
|
||||||
|
void CloseDirectory(DirectoryHandle handle);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,4 +60,19 @@ namespace ams::fs {
|
|||||||
|
|
||||||
static_assert(std::is_pod<WriteOption>::value && sizeof(WriteOption) == sizeof(u32));
|
static_assert(std::is_pod<WriteOption>::value && sizeof(WriteOption) == sizeof(u32));
|
||||||
|
|
||||||
|
struct FileHandle {
|
||||||
|
void *handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
Result ReadFile(FileHandle handle, s64 offset, void *buffer, size_t size, const fs::ReadOption &option);
|
||||||
|
Result ReadFile(FileHandle handle, s64 offset, void *buffer, size_t size);
|
||||||
|
Result ReadFile(size_t *out, FileHandle handle, s64 offset, void *buffer, size_t size, const fs::ReadOption &option);
|
||||||
|
Result ReadFile(size_t *out, FileHandle handle, s64 offset, void *buffer, size_t size);
|
||||||
|
Result GetFileSize(s64 *out, FileHandle handle);
|
||||||
|
Result FlushFile(FileHandle handle);
|
||||||
|
Result WriteFile(FileHandle handle, s64 offset, const void *buffer, size_t size, const fs::WriteOption &option);
|
||||||
|
Result SetFileSize(FileHandle handle, s64 size);
|
||||||
|
int GetFileOpenMode(FileHandle handle);
|
||||||
|
void CloseFile(FileHandle handle);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,13 +18,19 @@
|
|||||||
|
|
||||||
namespace ams::fs {
|
namespace ams::fs {
|
||||||
|
|
||||||
|
namespace fsa {
|
||||||
|
|
||||||
|
class IFile;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
enum OpenMode {
|
enum OpenMode {
|
||||||
OpenMode_Read = ::FsOpenMode_Read,
|
OpenMode_Read = (1 << 0),
|
||||||
OpenMode_Write = ::FsOpenMode_Write,
|
OpenMode_Write = (1 << 1),
|
||||||
OpenMode_Append = ::FsOpenMode_Append,
|
OpenMode_AllowAppend = (1 << 2),
|
||||||
|
|
||||||
OpenMode_ReadWrite = (OpenMode_Read | OpenMode_Write),
|
OpenMode_ReadWrite = (OpenMode_Read | OpenMode_Write),
|
||||||
OpenMode_All = (OpenMode_ReadWrite | OpenMode_Append),
|
OpenMode_All = (OpenMode_ReadWrite | OpenMode_AllowAppend),
|
||||||
};
|
};
|
||||||
|
|
||||||
enum OpenDirectoryMode {
|
enum OpenDirectoryMode {
|
||||||
@@ -49,4 +55,27 @@ namespace ams::fs {
|
|||||||
|
|
||||||
using FileTimeStampRaw = ::FsTimeStampRaw;
|
using FileTimeStampRaw = ::FsTimeStampRaw;
|
||||||
|
|
||||||
|
struct FileHandle;
|
||||||
|
struct DirectoryHandle;
|
||||||
|
|
||||||
|
Result CreateFile(const char *path, s64 size);
|
||||||
|
Result CreateFile(const char* path, s64 size, int option);
|
||||||
|
Result DeleteFile(const char *path);
|
||||||
|
Result CreateDirectory(const char *path);
|
||||||
|
Result DeleteDirectory(const char *path);
|
||||||
|
Result DeleteDirectoryRecursively(const char *path);
|
||||||
|
Result RenameFile(const char *old_path, const char *new_path);
|
||||||
|
Result RenameDirectory(const char *old_path, const char *new_path);
|
||||||
|
Result GetEntryType(DirectoryEntryType *out, const char *path);
|
||||||
|
Result OpenFile(FileHandle *out_file, const char *path, int mode);
|
||||||
|
Result OpenDirectory(DirectoryHandle *out_dir, const char *path, int mode);
|
||||||
|
Result CleanDirectoryRecursively(const char *path);
|
||||||
|
Result GetFreeSpaceSize(s64 *out, const char *path);
|
||||||
|
Result GetTotalSpaceSize(s64 *out, const char *path);
|
||||||
|
|
||||||
|
Result SetConcatenationFileAttribute(const char *path);
|
||||||
|
Result GetFileTimeStampRaw(FileTimeStampRaw *out, const char *path);
|
||||||
|
|
||||||
|
Result OpenFile(FileHandle *out, std::unique_ptr<fsa::IFile> &&file, int mode);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* 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 "fs_common.hpp"
|
||||||
|
#include "fs_filesystem.hpp"
|
||||||
|
|
||||||
|
namespace ams::fs {
|
||||||
|
|
||||||
|
/* Common utilities. */
|
||||||
|
Result EnsureDirectoryRecursively(const char *path);
|
||||||
|
Result EnsureParentDirectoryRecursively(const char *path);
|
||||||
|
|
||||||
|
Result HasFile(bool *out, const char *path);
|
||||||
|
Result HasDirectory(bool *out, const char *path);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* 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 "fs_common.hpp"
|
||||||
|
|
||||||
|
namespace ams::fs {
|
||||||
|
|
||||||
|
enum class GameCardPartition {
|
||||||
|
Update = 0,
|
||||||
|
Normal = 1,
|
||||||
|
Secure = 2,
|
||||||
|
Logo = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class GameCardPartitionRaw {
|
||||||
|
NormalReadable,
|
||||||
|
SecureReadable,
|
||||||
|
RootWriteable,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class GameCardAttribute : u8 {
|
||||||
|
AutoBootFlag = (1 << 0),
|
||||||
|
HistoryEraseFlag = (1 << 1),
|
||||||
|
RepairToolFlag = (1 << 2),
|
||||||
|
DifferentRegionCupToTerraDeviceFlag = (1 << 3),
|
||||||
|
DifferentRegionCupToGlobalDeviceFlag = (1 << 4),
|
||||||
|
};
|
||||||
|
|
||||||
|
using GameCardHandle = u32;
|
||||||
|
|
||||||
|
Result GetGameCardHandle(GameCardHandle *out);
|
||||||
|
Result MountGameCardPartition(const char *name, GameCardHandle handle, GameCardPartition partition);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -26,21 +26,15 @@ namespace ams::fs {
|
|||||||
|
|
||||||
virtual Result Read(s64 offset, void *buffer, size_t size) = 0;
|
virtual Result Read(s64 offset, void *buffer, size_t size) = 0;
|
||||||
|
|
||||||
virtual Result Write(s64 offset, const void *buffer, size_t size) {
|
virtual Result Write(s64 offset, const void *buffer, size_t size) = 0;
|
||||||
return fs::ResultUnsupportedOperation();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result Flush() = 0;
|
virtual Result Flush() = 0;
|
||||||
|
|
||||||
virtual Result SetSize(s64 size) {
|
virtual Result SetSize(s64 size) = 0;
|
||||||
return fs::ResultUnsupportedOperation();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result GetSize(s64 *out) = 0;
|
virtual Result GetSize(s64 *out) = 0;
|
||||||
|
|
||||||
virtual Result OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) {
|
virtual Result OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) = 0;
|
||||||
return fs::ResultUnsupportedOperation();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result OperateRange(OperationId op_id, s64 offset, s64 size) {
|
virtual Result OperateRange(OperationId op_id, s64 offset, s64 size) {
|
||||||
return this->OperateRange(nullptr, 0, op_id, offset, size, nullptr, 0);
|
return this->OperateRange(nullptr, 0, op_id, offset, size, nullptr, 0);
|
||||||
@@ -86,21 +80,31 @@ namespace ams::fs {
|
|||||||
|
|
||||||
virtual ~ReadOnlyStorageAdapter() { /* ... */ }
|
virtual ~ReadOnlyStorageAdapter() { /* ... */ }
|
||||||
public:
|
public:
|
||||||
virtual Result Read(s64 offset, void *buffer, size_t size) {
|
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||||
return this->storage->Read(offset, buffer, size);
|
return this->storage->Read(offset, buffer, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result Flush() {
|
virtual Result Flush() override {
|
||||||
return this->storage->Flush();
|
return this->storage->Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result GetSize(s64 *out) {
|
virtual Result GetSize(s64 *out) override {
|
||||||
return this->storage->GetSize(out);
|
return this->storage->GetSize(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override {
|
virtual Result OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override {
|
||||||
return this->storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size);
|
return this->storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||||
|
/* TODO: Better result? Is it possible to get a more specific one? */
|
||||||
|
return fs::ResultUnsupportedOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Result SetSize(s64 size) override {
|
||||||
|
/* TODO: Better result? Is it possible to get a more specific one? */
|
||||||
|
return fs::ResultUnsupportedOperation();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* 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 "fs_common.hpp"
|
||||||
|
|
||||||
|
namespace ams::fs {
|
||||||
|
|
||||||
|
using AllocateFunction = void *(*)(size_t);
|
||||||
|
using DeallocateFunction = void (*)(void *, size_t);
|
||||||
|
|
||||||
|
void SetAllocator(AllocateFunction allocator, DeallocateFunction deallocator);
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
|
||||||
|
void *Allocate(size_t size);
|
||||||
|
void Deallocate(void *ptr, size_t size);
|
||||||
|
|
||||||
|
class Deleter {
|
||||||
|
private:
|
||||||
|
size_t size;
|
||||||
|
public:
|
||||||
|
Deleter() : size() { /* ... */ }
|
||||||
|
explicit Deleter(size_t sz) : size(sz) { /* ... */ }
|
||||||
|
|
||||||
|
void operator()(void *ptr) const {
|
||||||
|
::ams::fs::impl::Deallocate(ptr, this->size);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
std::unique_ptr<T, Deleter> MakeUnique() {
|
||||||
|
static_assert(std::is_pod<T>::value);
|
||||||
|
return std::unique_ptr<T, Deleter>(static_cast<T *>(::ams::fs::impl::Allocate(sizeof(T))), Deleter(sizeof(T)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* 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 "impl/fs_newable.hpp"
|
||||||
|
#include "fs_istorage.hpp"
|
||||||
|
#include "fs_query_range.hpp"
|
||||||
|
|
||||||
|
namespace ams::fs {
|
||||||
|
|
||||||
|
class MemoryStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
|
||||||
|
private:
|
||||||
|
u8 * const buf;
|
||||||
|
const s64 size;
|
||||||
|
public:
|
||||||
|
MemoryStorage(void *b, s64 sz) : buf(static_cast<u8 *>(b)), size(sz) { /* .. */ }
|
||||||
|
public:
|
||||||
|
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||||
|
/* Succeed immediately on zero-sized read. */
|
||||||
|
R_SUCCEED_IF(size == 0);
|
||||||
|
|
||||||
|
/* Validate arguments. */
|
||||||
|
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
|
||||||
|
R_UNLESS(IStorage::IsRangeValid(offset, size, this->size), fs::ResultOutOfRange());
|
||||||
|
|
||||||
|
/* Copy from memory. */
|
||||||
|
std::memcpy(buffer, this->buf + offset, size);
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||||
|
/* Succeed immediately on zero-sized write. */
|
||||||
|
R_SUCCEED_IF(size == 0);
|
||||||
|
|
||||||
|
/* Validate arguments. */
|
||||||
|
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
|
||||||
|
R_UNLESS(IStorage::IsRangeValid(offset, size, this->size), fs::ResultOutOfRange());
|
||||||
|
|
||||||
|
/* Copy to memory. */
|
||||||
|
std::memcpy(this->buf + offset, buffer, size);
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Result Flush() override {
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Result GetSize(s64 *out) override {
|
||||||
|
*out = this->size;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Result SetSize(s64 size) override {
|
||||||
|
return fs::ResultUnsupportedOperationInMemoryStorageA();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Result OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override {
|
||||||
|
switch (op_id) {
|
||||||
|
case OperationId::InvalidateCache:
|
||||||
|
return ResultSuccess();
|
||||||
|
case OperationId::QueryRange:
|
||||||
|
R_UNLESS(dst != nullptr, fs::ResultNullptrArgument());
|
||||||
|
R_UNLESS(dst_size == sizeof(QueryRangeInfo), fs::ResultInvalidSize());
|
||||||
|
reinterpret_cast<QueryRangeInfo *>(dst)->Clear();
|
||||||
|
return ResultSuccess();
|
||||||
|
default:
|
||||||
|
return fs::ResultUnsupportedOperationInMemoryStorageB();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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 "fs_common.hpp"
|
||||||
|
|
||||||
|
namespace ams::fs {
|
||||||
|
|
||||||
|
constexpr inline size_t MountNameLengthMax = 15;
|
||||||
|
|
||||||
|
Result ConvertToFsCommonPath(char *dst, size_t dst_size, const char *src);
|
||||||
|
|
||||||
|
void Unmount(const char *mount_name);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -26,16 +26,25 @@ namespace ams::fs {
|
|||||||
constexpr inline char Dot = '.';
|
constexpr inline char Dot = '.';
|
||||||
constexpr inline char NullTerminator = '\x00';
|
constexpr inline char NullTerminator = '\x00';
|
||||||
|
|
||||||
|
constexpr inline char AlternateDirectorySeparator = '\\';
|
||||||
}
|
}
|
||||||
|
|
||||||
class PathTool {
|
class PathTool {
|
||||||
public:
|
public:
|
||||||
static constexpr const char RootPath[] = "/";
|
static constexpr const char RootPath[] = "/";
|
||||||
public:
|
public:
|
||||||
|
static constexpr inline bool IsAlternateSeparator(char c) {
|
||||||
|
return c == StringTraits::AlternateDirectorySeparator;
|
||||||
|
}
|
||||||
|
|
||||||
static constexpr inline bool IsSeparator(char c) {
|
static constexpr inline bool IsSeparator(char c) {
|
||||||
return c == StringTraits::DirectorySeparator;
|
return c == StringTraits::DirectorySeparator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr inline bool IsAnySeparator(char c) {
|
||||||
|
return IsSeparator(c) || IsAlternateSeparator(c);
|
||||||
|
}
|
||||||
|
|
||||||
static constexpr inline bool IsNullTerminator(char c) {
|
static constexpr inline bool IsNullTerminator(char c) {
|
||||||
return c == StringTraits::NullTerminator;
|
return c == StringTraits::NullTerminator;
|
||||||
}
|
}
|
||||||
@@ -56,6 +65,10 @@ namespace ams::fs {
|
|||||||
return IsWindowsDriveCharacter(p[0]) && IsDriveSeparator(p[1]);
|
return IsWindowsDriveCharacter(p[0]) && IsDriveSeparator(p[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr inline bool IsUnc(const char *p) {
|
||||||
|
return (IsSeparator(p[0]) && IsSeparator(p[1])) || (IsAlternateSeparator(p[0]) && IsAlternateSeparator(p[1]));
|
||||||
|
}
|
||||||
|
|
||||||
static constexpr inline bool IsCurrentDirectory(const char *p) {
|
static constexpr inline bool IsCurrentDirectory(const char *p) {
|
||||||
return IsDot(p[0]) && (IsSeparator(p[1]) || IsNullTerminator(p[1]));
|
return IsDot(p[0]) && (IsSeparator(p[1]) || IsNullTerminator(p[1]));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "fs_common.hpp"
|
#include "fs_common.hpp"
|
||||||
|
#include "fs_file.hpp"
|
||||||
|
|
||||||
namespace ams::fs {
|
namespace ams::fs {
|
||||||
|
|
||||||
@@ -42,4 +43,6 @@ namespace ams::fs {
|
|||||||
using FileQueryRangeInfo = QueryRangeInfo;
|
using FileQueryRangeInfo = QueryRangeInfo;
|
||||||
using StorageQueryRangeInfo = QueryRangeInfo;
|
using StorageQueryRangeInfo = QueryRangeInfo;
|
||||||
|
|
||||||
|
Result QueryRange(QueryRangeInfo *out, FileHandle handle, s64 offset, s64 size);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere/fs/fs_common.hpp>
|
||||||
|
#include <stratosphere/fs/impl/fs_newable.hpp>
|
||||||
|
#include <stratosphere/fs/fsa/fs_ifile.hpp>
|
||||||
|
#include <stratosphere/fs/fsa/fs_idirectory.hpp>
|
||||||
|
#include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
|
||||||
|
|
||||||
|
namespace ams::fs {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class ReadOnlyFile : public fsa::IFile, public impl::Newable {
|
||||||
|
NON_COPYABLE(ReadOnlyFile);
|
||||||
|
NON_MOVEABLE(ReadOnlyFile);
|
||||||
|
private:
|
||||||
|
std::unique_ptr<fsa::IFile> base_file;
|
||||||
|
public:
|
||||||
|
explicit ReadOnlyFile(std::unique_ptr<fsa::IFile> &&f) : base_file(std::move(f)) { /* ... */ }
|
||||||
|
virtual ~ReadOnlyFile() { /* ... */ }
|
||||||
|
private:
|
||||||
|
virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override final {
|
||||||
|
return this->base_file->Read(out, offset, buffer, size, option);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Result GetSizeImpl(s64 *out) override final {
|
||||||
|
return this->base_file->GetSize(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Result FlushImpl() override final {
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override final {
|
||||||
|
return fs::ResultUnsupportedOperationInReadOnlyFileA();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Result SetSizeImpl(s64 size) override final {
|
||||||
|
return fs::ResultUnsupportedOperationInReadOnlyFileA();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Result OperateRangeImpl(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override final {
|
||||||
|
switch (op_id) {
|
||||||
|
case OperationId::InvalidateCache:
|
||||||
|
case OperationId::QueryRange:
|
||||||
|
return this->base_file->OperateRange(dst, dst_size, op_id, offset, size, src, src_size);
|
||||||
|
default:
|
||||||
|
return fs::ResultUnsupportedOperationInReadOnlyFileB();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
|
||||||
|
return this->base_file->GetDomainObjectId();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class ReadOnlyFileSystemTemplate : public fsa::IFileSystem, public impl::Newable {
|
||||||
|
NON_COPYABLE(ReadOnlyFileSystemTemplate);
|
||||||
|
NON_MOVEABLE(ReadOnlyFileSystemTemplate);
|
||||||
|
private:
|
||||||
|
T base_fs;
|
||||||
|
public:
|
||||||
|
explicit ReadOnlyFileSystemTemplate(T &&fs) : base_fs(std::move(fs)) { /* ... */ }
|
||||||
|
virtual ~ReadOnlyFileSystemTemplate() { /* ... */ }
|
||||||
|
private:
|
||||||
|
virtual Result OpenFileImpl(std::unique_ptr<fsa::IFile> *out_file, const char *path, OpenMode mode) override final {
|
||||||
|
/* Only allow opening files with mode = read. */
|
||||||
|
R_UNLESS((mode & fs::OpenMode_All) == fs::OpenMode_Read, fs::ResultInvalidArgument());
|
||||||
|
|
||||||
|
std::unique_ptr<fsa::IFile> base_file;
|
||||||
|
R_TRY(this->base_fs->OpenFile(std::addressof(base_file), path, mode));
|
||||||
|
|
||||||
|
auto read_only_file = std::make_unique<ReadOnlyFile>(std::move(base_file));
|
||||||
|
R_UNLESS(read_only_file != nullptr, fs::ResultAllocationFailureInReadOnlyFileSystemA());
|
||||||
|
|
||||||
|
*out_file = std::move(read_only_file);
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Result OpenDirectoryImpl(std::unique_ptr<fsa::IDirectory> *out_dir, const char *path, OpenDirectoryMode mode) override final {
|
||||||
|
return this->base_fs->OpenDirectory(out_dir, path, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const char *path) override final {
|
||||||
|
return this->base_fs->GetEntryType(out, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Result CommitImpl() override final {
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Result CreateFileImpl(const char *path, s64 size, int flags) override final {
|
||||||
|
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Result DeleteFileImpl(const char *path) override final {
|
||||||
|
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Result CreateDirectoryImpl(const char *path) override final {
|
||||||
|
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Result DeleteDirectoryImpl(const char *path) override final {
|
||||||
|
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override final {
|
||||||
|
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Result RenameFileImpl(const char *old_path, const char *new_path) override final {
|
||||||
|
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override final {
|
||||||
|
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Result CleanDirectoryRecursivelyImpl(const char *path) override final {
|
||||||
|
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) override final {
|
||||||
|
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateB();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) override final {
|
||||||
|
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateB();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Result CommitProvisionallyImpl(s64 counter) override final {
|
||||||
|
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateC();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using ReadOnlyFileSystem = ReadOnlyFileSystemTemplate<std::unique_ptr<::ams::fs::fsa::IFileSystem>>;
|
||||||
|
using ReadOnlyFileSystemShared = ReadOnlyFileSystemTemplate<std::shared_ptr<::ams::fs::fsa::IFileSystem>>;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,152 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <stratosphere/fs/fs_common.hpp>
|
|
||||||
#include <stratosphere/fs/fsa/fs_ifile.hpp>
|
|
||||||
#include <stratosphere/fs/fsa/fs_idirectory.hpp>
|
|
||||||
#include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
|
|
||||||
|
|
||||||
namespace ams::fs {
|
|
||||||
|
|
||||||
class ReadOnlyFileAdapter : public fsa::IFile {
|
|
||||||
NON_COPYABLE(ReadOnlyFileAdapter);
|
|
||||||
private:
|
|
||||||
std::unique_ptr<fsa::IFile> base_file;
|
|
||||||
public:
|
|
||||||
ReadOnlyFileAdapter(fsa::IFile *f) : base_file(f) { /* ... */ }
|
|
||||||
ReadOnlyFileAdapter(std::unique_ptr<fsa::IFile> f) : base_file(std::move(f)) { /* ... */ }
|
|
||||||
virtual ~ReadOnlyFileAdapter() { /* ... */ }
|
|
||||||
public:
|
|
||||||
virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override final {
|
|
||||||
/* Ensure that we can read these extents. */
|
|
||||||
size_t read_size = 0;
|
|
||||||
R_TRY(this->DryRead(std::addressof(read_size), offset, size, option, fs::OpenMode_Read));
|
|
||||||
|
|
||||||
/* Validate preconditions. */
|
|
||||||
AMS_ASSERT(offset >= 0);
|
|
||||||
AMS_ASSERT(buffer != nullptr || size == 0);
|
|
||||||
|
|
||||||
return this->base_file->Read(out, offset, buffer, size, option);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result GetSizeImpl(s64 *out) override final {
|
|
||||||
return this->base_file->GetSize(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result FlushImpl() override final {
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override final {
|
|
||||||
return fs::ResultUnsupportedOperation();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result SetSizeImpl(s64 size) override final {
|
|
||||||
return fs::ResultUnsupportedOperation();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result OperateRangeImpl(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override final {
|
|
||||||
/* TODO: How should this be handled? */
|
|
||||||
return fs::ResultNotImplemented();
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
|
|
||||||
return this->base_file->GetDomainObjectId();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class ReadOnlyFileSystemAdapter : public fsa::IFileSystem {
|
|
||||||
NON_COPYABLE(ReadOnlyFileSystemAdapter);
|
|
||||||
private:
|
|
||||||
std::shared_ptr<fsa::IFileSystem> shared_fs;
|
|
||||||
std::unique_ptr<fsa::IFileSystem> unique_fs;
|
|
||||||
protected:
|
|
||||||
fsa::IFileSystem * const base_fs;
|
|
||||||
public:
|
|
||||||
template<typename T>
|
|
||||||
explicit ReadOnlyFileSystemAdapter(std::shared_ptr<T> fs) : shared_fs(std::move(fs)), base_fs(shared_fs.get()) { static_assert(std::is_base_of<fsa::IFileSystem, T>::value); }
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
explicit ReadOnlyFileSystemAdapter(std::unique_ptr<T> fs) : unique_fs(std::move(fs)), base_fs(unique_fs.get()) { static_assert(std::is_base_of<fsa::IFileSystem, T>::value); }
|
|
||||||
|
|
||||||
virtual ~ReadOnlyFileSystemAdapter() { /* ... */ }
|
|
||||||
public:
|
|
||||||
virtual Result CreateFileImpl(const char *path, s64 size, int flags) override final {
|
|
||||||
return fs::ResultUnsupportedOperation();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result DeleteFileImpl(const char *path) override final {
|
|
||||||
return fs::ResultUnsupportedOperation();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result CreateDirectoryImpl(const char *path) override final {
|
|
||||||
return fs::ResultUnsupportedOperation();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result DeleteDirectoryImpl(const char *path) override final {
|
|
||||||
return fs::ResultUnsupportedOperation();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override final {
|
|
||||||
return fs::ResultUnsupportedOperation();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result RenameFileImpl(const char *old_path, const char *new_path) override final {
|
|
||||||
return fs::ResultUnsupportedOperation();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override final {
|
|
||||||
return fs::ResultUnsupportedOperation();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const char *path) override final {
|
|
||||||
return this->base_fs->GetEntryType(out, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result OpenFileImpl(std::unique_ptr<fsa::IFile> *out_file, const char *path, OpenMode mode) override final {
|
|
||||||
std::unique_ptr<fsa::IFile> f;
|
|
||||||
R_TRY(this->base_fs->OpenFile(std::addressof(f), path, mode));
|
|
||||||
|
|
||||||
*out_file = std::make_unique<ReadOnlyFileAdapter>(std::move(f));
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result OpenDirectoryImpl(std::unique_ptr<fsa::IDirectory> *out_dir, const char *path, OpenDirectoryMode mode) override final {
|
|
||||||
return this->base_fs->OpenDirectory(out_dir, path, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result CommitImpl() override final {
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) {
|
|
||||||
return fs::ResultUnsupportedOperation();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) {
|
|
||||||
return fs::ResultUnsupportedOperation();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result CleanDirectoryRecursivelyImpl(const char *path) {
|
|
||||||
return fs::ResultUnsupportedOperation();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result GetFileTimeStampRawImpl(FileTimeStampRaw *out, const char *path) {
|
|
||||||
return this->base_fs->GetFileTimeStampRaw(out, path);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -14,165 +14,217 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "fs_common.hpp"
|
#include <stratosphere/fs/fs_common.hpp>
|
||||||
#include "fsa/fs_ifile.hpp"
|
#include <stratosphere/fs/impl/fs_newable.hpp>
|
||||||
#include "fsa/fs_idirectory.hpp"
|
#include <stratosphere/fs/fsa/fs_ifile.hpp>
|
||||||
#include "fsa/fs_ifilesystem.hpp"
|
#include <stratosphere/fs/fsa/fs_idirectory.hpp>
|
||||||
|
#include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
|
||||||
|
#include <stratosphere/fs/fs_query_range.hpp>
|
||||||
|
#include <stratosphere/fs/fs_path_tool.hpp>
|
||||||
|
#include <stratosphere/fs/fs_path_utils.hpp>
|
||||||
|
|
||||||
namespace ams::fs {
|
namespace ams::fs {
|
||||||
|
|
||||||
class RemoteFile : public fsa::IFile {
|
class RemoteFile : public fsa::IFile, public impl::Newable {
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<::FsFile> base_file;
|
::FsFile base_file;
|
||||||
public:
|
public:
|
||||||
RemoteFile(::FsFile *f) : base_file(f) { /* ... */ }
|
RemoteFile(const ::FsFile &f) : base_file(f) { /* ... */ }
|
||||||
RemoteFile(std::unique_ptr<::FsFile> f) : base_file(std::move(f)) { /* ... */ }
|
|
||||||
RemoteFile(::FsFile f) {
|
|
||||||
this->base_file = std::make_unique<::FsFile>(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~RemoteFile() { fsFileClose(this->base_file.get()); }
|
virtual ~RemoteFile() { fsFileClose(std::addressof(this->base_file)); }
|
||||||
public:
|
public:
|
||||||
virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override final {
|
virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override final {
|
||||||
return fsFileRead(this->base_file.get(), offset, buffer, size, option.value, out);
|
return fsFileRead(std::addressof(this->base_file), offset, buffer, size, option.value, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result GetSizeImpl(s64 *out) override final {
|
virtual Result GetSizeImpl(s64 *out) override final {
|
||||||
return fsFileGetSize(this->base_file.get(), out);
|
return fsFileGetSize(std::addressof(this->base_file), out);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result FlushImpl() override final {
|
virtual Result FlushImpl() override final {
|
||||||
return fsFileFlush(this->base_file.get());
|
return fsFileFlush(std::addressof(this->base_file));
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override final {
|
virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override final {
|
||||||
return fsFileWrite(this->base_file.get(), offset, buffer, size, option.value);
|
return fsFileWrite(std::addressof(this->base_file), offset, buffer, size, option.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result SetSizeImpl(s64 size) override final {
|
virtual Result SetSizeImpl(s64 size) override final {
|
||||||
return fsFileSetSize(this->base_file.get(), size);
|
return fsFileSetSize(std::addressof(this->base_file), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result OperateRangeImpl(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override final {
|
virtual Result OperateRangeImpl(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override final {
|
||||||
/* TODO: How should this be handled? */
|
R_UNLESS(op_id == OperationId::QueryRange, fs::ResultUnsupportedOperationInFileServiceObjectAdapterA());
|
||||||
return fs::ResultNotImplemented();
|
R_UNLESS(dst_size == sizeof(FileQueryRangeInfo), fs::ResultInvalidSize());
|
||||||
|
|
||||||
|
return fsFileOperateRange(std::addressof(this->base_file), static_cast<::FsOperationId>(op_id), offset, size, reinterpret_cast<::FsRangeInfo *>(dst));
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
|
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
|
||||||
return sf::cmif::DomainObjectId{serviceGetObjectId(&this->base_file->s)};
|
return sf::cmif::DomainObjectId{serviceGetObjectId(const_cast<::Service *>(std::addressof(this->base_file.s)))};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class RemoteDirectory : public fsa::IDirectory {
|
class RemoteDirectory : public fsa::IDirectory, public impl::Newable {
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<::FsDir> base_dir;
|
::FsDir base_dir;
|
||||||
public:
|
public:
|
||||||
RemoteDirectory(::FsDir *d) : base_dir(d) { /* ... */ }
|
RemoteDirectory(const ::FsDir &d) : base_dir(d) { /* ... */ }
|
||||||
RemoteDirectory(std::unique_ptr<::FsDir> d) : base_dir(std::move(d)) { /* ... */ }
|
|
||||||
RemoteDirectory(::FsDir d) {
|
|
||||||
this->base_dir = std::make_unique<::FsDir>(d);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~RemoteDirectory() { fsDirClose(this->base_dir.get()); }
|
virtual ~RemoteDirectory() { fsDirClose(std::addressof(this->base_dir)); }
|
||||||
public:
|
public:
|
||||||
virtual Result ReadImpl(s64 *out_count, DirectoryEntry *out_entries, s64 max_entries) override final {
|
virtual Result ReadImpl(s64 *out_count, DirectoryEntry *out_entries, s64 max_entries) override final {
|
||||||
return fsDirRead(this->base_dir.get(), out_count, max_entries, out_entries);
|
return fsDirRead(std::addressof(this->base_dir), out_count, max_entries, out_entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result GetEntryCountImpl(s64 *out) override final {
|
virtual Result GetEntryCountImpl(s64 *out) override final {
|
||||||
return fsDirGetEntryCount(this->base_dir.get(), out);
|
return fsDirGetEntryCount(std::addressof(this->base_dir), out);
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
|
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
|
||||||
return sf::cmif::DomainObjectId{serviceGetObjectId(&this->base_dir->s)};
|
return sf::cmif::DomainObjectId{serviceGetObjectId(const_cast<::Service *>(std::addressof(this->base_dir.s)))};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class RemoteFileSystem : public fsa::IFileSystem {
|
class RemoteFileSystem : public fsa::IFileSystem, public impl::Newable {
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<::FsFileSystem> base_fs;
|
::FsFileSystem base_fs;
|
||||||
public:
|
public:
|
||||||
RemoteFileSystem(::FsFileSystem *fs) : base_fs(fs) { /* ... */ }
|
RemoteFileSystem(const ::FsFileSystem &fs) : base_fs(fs) { /* ... */ }
|
||||||
RemoteFileSystem(std::unique_ptr<::FsFileSystem> fs) : base_fs(std::move(fs)) { /* ... */ }
|
|
||||||
RemoteFileSystem(::FsFileSystem fs) {
|
|
||||||
this->base_fs = std::make_unique<::FsFileSystem>(fs);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~RemoteFileSystem() { fsFsClose(this->base_fs.get()); }
|
virtual ~RemoteFileSystem() { fsFsClose(std::addressof(this->base_fs)); }
|
||||||
|
private:
|
||||||
|
Result GetPathForServiceObject(fssrv::sf::Path *out_path, const char *path) {
|
||||||
|
/* Copy and null terminate. */
|
||||||
|
std::strncpy(out_path->str, path, sizeof(out_path->str) - 1);
|
||||||
|
out_path->str[sizeof(out_path->str) - 1] = '\x00';
|
||||||
|
|
||||||
|
/* Replace directory separators. */
|
||||||
|
Replace(out_path->str, sizeof(out_path->str) - 1, StringTraits::AlternateDirectorySeparator, StringTraits::DirectorySeparator);
|
||||||
|
|
||||||
|
/* Get lengths. */
|
||||||
|
const auto mount_name_len = PathTool::IsWindowsAbsolutePath(path) ? 2 : 0;
|
||||||
|
const auto rel_path = out_path->str + mount_name_len;
|
||||||
|
const auto max_len = fs::EntryNameLengthMax - mount_name_len;
|
||||||
|
return VerifyPath(rel_path, max_len, max_len);
|
||||||
|
}
|
||||||
public:
|
public:
|
||||||
virtual Result CreateFileImpl(const char *path, s64 size, int flags) override final {
|
virtual Result CreateFileImpl(const char *path, s64 size, int flags) override final {
|
||||||
return fsFsCreateFile(this->base_fs.get(), path, size, flags);
|
fssrv::sf::Path sf_path;
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||||
|
return fsFsCreateFile(std::addressof(this->base_fs), sf_path.str, size, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result DeleteFileImpl(const char *path) override final {
|
virtual Result DeleteFileImpl(const char *path) override final {
|
||||||
return fsFsDeleteFile(this->base_fs.get(), path);
|
fssrv::sf::Path sf_path;
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||||
|
return fsFsDeleteFile(std::addressof(this->base_fs), sf_path.str);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result CreateDirectoryImpl(const char *path) override final {
|
virtual Result CreateDirectoryImpl(const char *path) override final {
|
||||||
return fsFsCreateDirectory(this->base_fs.get(), path);
|
fssrv::sf::Path sf_path;
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||||
|
return fsFsCreateDirectory(std::addressof(this->base_fs), sf_path.str);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result DeleteDirectoryImpl(const char *path) override final {
|
virtual Result DeleteDirectoryImpl(const char *path) override final {
|
||||||
return fsFsDeleteDirectory(this->base_fs.get(), path);
|
fssrv::sf::Path sf_path;
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||||
|
return fsFsDeleteDirectory(std::addressof(this->base_fs), sf_path.str);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override final {
|
virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override final {
|
||||||
return fsFsDeleteDirectoryRecursively(this->base_fs.get(), path);
|
fssrv::sf::Path sf_path;
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||||
|
return fsFsDeleteDirectoryRecursively(std::addressof(this->base_fs), sf_path.str);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result RenameFileImpl(const char *old_path, const char *new_path) override final {
|
virtual Result RenameFileImpl(const char *old_path, const char *new_path) override final {
|
||||||
return fsFsRenameFile(this->base_fs.get(), old_path, new_path);
|
fssrv::sf::Path old_sf_path;
|
||||||
|
fssrv::sf::Path new_sf_path;
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(old_sf_path), old_path));
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(new_sf_path), new_path));
|
||||||
|
return fsFsRenameFile(std::addressof(this->base_fs), old_sf_path.str, new_sf_path.str);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override final {
|
virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override final {
|
||||||
return fsFsRenameDirectory(this->base_fs.get(), old_path, new_path);
|
fssrv::sf::Path old_sf_path;
|
||||||
|
fssrv::sf::Path new_sf_path;
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(old_sf_path), old_path));
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(new_sf_path), new_path));
|
||||||
|
return fsFsRenameDirectory(std::addressof(this->base_fs), old_sf_path.str, new_sf_path.str);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const char *path) override final {
|
virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const char *path) override final {
|
||||||
|
fssrv::sf::Path sf_path;
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||||
|
|
||||||
static_assert(sizeof(::FsDirEntryType) == sizeof(DirectoryEntryType));
|
static_assert(sizeof(::FsDirEntryType) == sizeof(DirectoryEntryType));
|
||||||
return fsFsGetEntryType(this->base_fs.get(), path, reinterpret_cast<::FsDirEntryType *>(out));
|
return fsFsGetEntryType(std::addressof(this->base_fs), sf_path.str, reinterpret_cast<::FsDirEntryType *>(out));
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result OpenFileImpl(std::unique_ptr<fsa::IFile> *out_file, const char *path, OpenMode mode) override final {
|
virtual Result OpenFileImpl(std::unique_ptr<fsa::IFile> *out_file, const char *path, OpenMode mode) override final {
|
||||||
FsFile f;
|
fssrv::sf::Path sf_path;
|
||||||
R_TRY(fsFsOpenFile(this->base_fs.get(), path, mode, &f));
|
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||||
|
|
||||||
*out_file = std::make_unique<RemoteFile>(f);
|
FsFile f;
|
||||||
|
R_TRY(fsFsOpenFile(std::addressof(this->base_fs), sf_path.str, mode, &f));
|
||||||
|
|
||||||
|
auto file = std::make_unique<RemoteFile>(f);
|
||||||
|
R_UNLESS(file != nullptr, fs::ResultAllocationFailureInNew());
|
||||||
|
|
||||||
|
*out_file = std::move(file);
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result OpenDirectoryImpl(std::unique_ptr<fsa::IDirectory> *out_dir, const char *path, OpenDirectoryMode mode) override final {
|
virtual Result OpenDirectoryImpl(std::unique_ptr<fsa::IDirectory> *out_dir, const char *path, OpenDirectoryMode mode) override final {
|
||||||
FsDir d;
|
fssrv::sf::Path sf_path;
|
||||||
R_TRY(fsFsOpenDirectory(this->base_fs.get(), path, mode, &d));
|
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||||
|
|
||||||
*out_dir = std::make_unique<RemoteDirectory>(d);
|
FsDir d;
|
||||||
|
R_TRY(fsFsOpenDirectory(std::addressof(this->base_fs), sf_path.str, mode, &d));
|
||||||
|
|
||||||
|
auto dir = std::make_unique<RemoteDirectory>(d);
|
||||||
|
R_UNLESS(dir != nullptr, fs::ResultAllocationFailureInNew());
|
||||||
|
|
||||||
|
*out_dir = std::move(dir);
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result CommitImpl() override final {
|
virtual Result CommitImpl() override final {
|
||||||
return fsFsCommit(this->base_fs.get());
|
return fsFsCommit(std::addressof(this->base_fs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) {
|
virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) {
|
||||||
return fsFsGetFreeSpace(this->base_fs.get(), path, out);
|
fssrv::sf::Path sf_path;
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||||
|
return fsFsGetFreeSpace(std::addressof(this->base_fs), sf_path.str, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) {
|
virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) {
|
||||||
return fsFsGetTotalSpace(this->base_fs.get(), path, out);
|
fssrv::sf::Path sf_path;
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||||
|
return fsFsGetTotalSpace(std::addressof(this->base_fs), sf_path.str, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result CleanDirectoryRecursivelyImpl(const char *path) {
|
virtual Result CleanDirectoryRecursivelyImpl(const char *path) {
|
||||||
return fsFsCleanDirectoryRecursively(this->base_fs.get(), path);
|
fssrv::sf::Path sf_path;
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||||
|
return fsFsCleanDirectoryRecursively(std::addressof(this->base_fs), sf_path.str);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result GetFileTimeStampRawImpl(FileTimeStampRaw *out, const char *path) {
|
virtual Result GetFileTimeStampRawImpl(FileTimeStampRaw *out, const char *path) {
|
||||||
|
fssrv::sf::Path sf_path;
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||||
static_assert(sizeof(FileTimeStampRaw) == sizeof(::FsTimeStampRaw));
|
static_assert(sizeof(FileTimeStampRaw) == sizeof(::FsTimeStampRaw));
|
||||||
return fsFsGetFileTimeStampRaw(this->base_fs.get(), path, reinterpret_cast<::FsTimeStampRaw *>(out));
|
return fsFsGetFileTimeStampRaw(std::addressof(this->base_fs), sf_path.str, reinterpret_cast<::FsTimeStampRaw *>(out));
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result QueryEntryImpl(char *dst, size_t dst_size, const char *src, size_t src_size, fsa::QueryId query, const char *path) {
|
virtual Result QueryEntryImpl(char *dst, size_t dst_size, const char *src, size_t src_size, fsa::QueryId query, const char *path) {
|
||||||
return fsFsQueryEntry(this->base_fs.get(), dst, dst_size, src, src_size, path, static_cast<FsFileSystemQueryId>(query));
|
fssrv::sf::Path sf_path;
|
||||||
|
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
|
||||||
|
return fsFsQueryEntry(std::addressof(this->base_fs), dst, dst_size, src, src_size, sf_path.str, static_cast<FsFileSystemQueryId>(query));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -16,17 +16,17 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "fs_common.hpp"
|
#include "fs_common.hpp"
|
||||||
#include "fs_istorage.hpp"
|
#include "fs_istorage.hpp"
|
||||||
|
#include "impl/fs_newable.hpp"
|
||||||
|
|
||||||
namespace ams::fs {
|
namespace ams::fs {
|
||||||
|
|
||||||
class RemoteStorage : public IStorage {
|
class RemoteStorage : public IStorage, public impl::Newable {
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<::FsStorage> base_storage;
|
std::unique_ptr<::FsStorage, impl::Deleter> base_storage;
|
||||||
public:
|
public:
|
||||||
RemoteStorage(::FsStorage *s) : base_storage(s) { /* ... */ }
|
RemoteStorage(::FsStorage &s) {
|
||||||
RemoteStorage(std::unique_ptr<::FsStorage> s) : base_storage(std::move(s)) { /* ... */ }
|
this->base_storage = impl::MakeUnique<::FsStorage>();
|
||||||
RemoteStorage(::FsStorage s) {
|
*this->base_storage = s;
|
||||||
this->base_storage = std::make_unique<::FsStorage>(s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~RemoteStorage() { fsStorageClose(this->base_storage.get()); }
|
virtual ~RemoteStorage() { fsStorageClose(this->base_storage.get()); }
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
|
||||||
|
namespace ams::fs {
|
||||||
|
|
||||||
|
union RightsId {
|
||||||
|
u8 data[0x10];
|
||||||
|
u64 data64[2];
|
||||||
|
};
|
||||||
|
static_assert(sizeof(RightsId) == 0x10);
|
||||||
|
static_assert(std::is_pod<RightsId>::value);
|
||||||
|
|
||||||
|
/* Rights ID API */
|
||||||
|
Result GetRightsId(RightsId *out, const char *path);
|
||||||
|
Result GetRightsId(RightsId *out, u8 *out_key_generation, const char *path);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "fs_common.hpp"
|
||||||
|
#include "impl/fs_newable.hpp"
|
||||||
|
#include "fsa/fs_ifile.hpp"
|
||||||
|
#include "fsa/fs_idirectory.hpp"
|
||||||
|
#include "fsa/fs_ifilesystem.hpp"
|
||||||
|
#include "fs_dbm_hierarchical_rom_file_table.hpp"
|
||||||
|
|
||||||
|
namespace ams::fs {
|
||||||
|
|
||||||
|
class RomFsFileSystem : public fsa::IFileSystem, public impl::Newable {
|
||||||
|
NON_COPYABLE(RomFsFileSystem);
|
||||||
|
public:
|
||||||
|
using RomFileTable = HierarchicalRomFileTable;
|
||||||
|
private:
|
||||||
|
RomFileTable rom_file_table;
|
||||||
|
IStorage *base_storage;
|
||||||
|
std::unique_ptr<IStorage> unique_storage;
|
||||||
|
std::unique_ptr<IStorage> dir_bucket_storage;
|
||||||
|
std::unique_ptr<IStorage> dir_entry_storage;
|
||||||
|
std::unique_ptr<IStorage> file_bucket_storage;
|
||||||
|
std::unique_ptr<IStorage> file_entry_storage;
|
||||||
|
s64 entry_size;
|
||||||
|
private:
|
||||||
|
Result GetFileInfo(RomFileTable::FileInfo *out, const char *path);
|
||||||
|
public:
|
||||||
|
static Result GetRequiredWorkingMemorySize(size_t *out, IStorage *storage);
|
||||||
|
public:
|
||||||
|
RomFsFileSystem();
|
||||||
|
virtual ~RomFsFileSystem() override;
|
||||||
|
|
||||||
|
Result Initialize(IStorage *base, void *work, size_t work_size, bool use_cache);
|
||||||
|
Result Initialize(std::unique_ptr<IStorage>&& base, void *work, size_t work_size, bool use_cache);
|
||||||
|
|
||||||
|
IStorage *GetBaseStorage();
|
||||||
|
RomFileTable *GetRomFileTable();
|
||||||
|
Result GetFileBaseOffset(s64 *out, const char *path);
|
||||||
|
public:
|
||||||
|
virtual Result CreateFileImpl(const char *path, s64 size, int flags) override;
|
||||||
|
virtual Result DeleteFileImpl(const char *path) override;
|
||||||
|
virtual Result CreateDirectoryImpl(const char *path) override;
|
||||||
|
virtual Result DeleteDirectoryImpl(const char *path) override;
|
||||||
|
virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override;
|
||||||
|
virtual Result RenameFileImpl(const char *old_path, const char *new_path) override;
|
||||||
|
virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override;
|
||||||
|
virtual Result GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) override;
|
||||||
|
virtual Result OpenFileImpl(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) override;
|
||||||
|
virtual Result OpenDirectoryImpl(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) override;
|
||||||
|
virtual Result CommitImpl() override;
|
||||||
|
virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) override;
|
||||||
|
virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) override;
|
||||||
|
virtual Result CleanDirectoryRecursivelyImpl(const char *path) override;
|
||||||
|
|
||||||
|
/* These aren't accessible as commands. */
|
||||||
|
virtual Result CommitProvisionallyImpl(s64 counter) override;
|
||||||
|
virtual Result RollbackImpl() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* 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 "fs_common.hpp"
|
||||||
|
#include "fs_save_data_types.hpp"
|
||||||
|
|
||||||
|
namespace ams::fs {
|
||||||
|
|
||||||
|
Result DeleteSaveData(SaveDataId id);
|
||||||
|
Result DeleteSaveData(SaveDataSpaceId space_id, SaveDataId id);
|
||||||
|
|
||||||
|
Result GetSaveDataFlags(u32 *out, SaveDataId id);
|
||||||
|
Result GetSaveDataFlags(u32 *out, SaveDataSpaceId space_id, SaveDataId id);
|
||||||
|
Result SetSaveDataFlags(SaveDataId id, SaveDataSpaceId space_id, u32 flags);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "fs_common.hpp"
|
||||||
|
#include "fs_save_data_types.hpp"
|
||||||
|
|
||||||
|
namespace ams::fs {
|
||||||
|
|
||||||
|
Result CommitSaveData(const char *path);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
/*
|
||||||
|
* 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 "fs_common.hpp"
|
||||||
|
|
||||||
|
namespace ams::fs {
|
||||||
|
|
||||||
|
using SaveDataId = u64;
|
||||||
|
using SystemSaveDataId = u64;
|
||||||
|
using SystemBcatSaveDataId = SystemSaveDataId;
|
||||||
|
|
||||||
|
enum class SaveDataSpaceId : u8 {
|
||||||
|
System = 0,
|
||||||
|
User = 1,
|
||||||
|
SdSystem = 2,
|
||||||
|
Temporary = 3,
|
||||||
|
SdUser = 4,
|
||||||
|
|
||||||
|
ProperSystem = 100,
|
||||||
|
SafeMode = 101,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SaveDataType : u8 {
|
||||||
|
System = 0,
|
||||||
|
Account = 1,
|
||||||
|
Bcat = 2,
|
||||||
|
Device = 3,
|
||||||
|
Temporary = 4,
|
||||||
|
Cache = 5,
|
||||||
|
SystemBcat = 6,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SaveDataRank : u8 {
|
||||||
|
Primary = 0,
|
||||||
|
Secondary = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UserId {
|
||||||
|
u64 data[2];
|
||||||
|
};
|
||||||
|
static_assert(std::is_pod<UserId>::value);
|
||||||
|
|
||||||
|
constexpr inline bool operator<(const UserId &lhs, const UserId &rhs) {
|
||||||
|
if (lhs.data[0] < rhs.data[0]) {
|
||||||
|
return true;
|
||||||
|
} else if (lhs.data[0] == rhs.data[0] && lhs.data[1] < rhs.data[1]) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline bool operator==(const UserId &lhs, const UserId &rhs) {
|
||||||
|
return lhs.data[0] == rhs.data[0] && lhs.data[1] == rhs.data[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline bool operator!=(const UserId &lhs, const UserId &rhs) {
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline SystemSaveDataId InvalidSystemSaveDataId = 0;
|
||||||
|
constexpr inline UserId InvalidUserId = {};
|
||||||
|
|
||||||
|
enum SaveDataFlags : u32 {
|
||||||
|
SaveDataFlags_None = (0 << 0),
|
||||||
|
SaveDataFlags_KeepAfterResettingSystemSaveData = (1 << 0),
|
||||||
|
SaveDataFlags_KeepAfterRefurbishment = (1 << 1),
|
||||||
|
SaveDataFlags_KeepAfterResettingSystemSaveDataWithoutUserSaveData = (1 << 2),
|
||||||
|
SaveDataFlags_NeedsSecureDelete = (1 << 3),
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SaveDataCreationInfo {
|
||||||
|
s64 size;
|
||||||
|
s64 journal_size;
|
||||||
|
s64 block_size;
|
||||||
|
u64 owner_id;
|
||||||
|
u32 flags;
|
||||||
|
SaveDataSpaceId space_id;
|
||||||
|
bool pseudo;
|
||||||
|
u8 reserved[0x1A];
|
||||||
|
};
|
||||||
|
static_assert(std::is_pod<SaveDataCreationInfo>::value);
|
||||||
|
static_assert(sizeof(SaveDataCreationInfo) == 0x40);
|
||||||
|
|
||||||
|
struct SaveDataAttribute {
|
||||||
|
ncm::ProgramId program_id;
|
||||||
|
UserId user_id;
|
||||||
|
SystemSaveDataId system_save_data_id;
|
||||||
|
SaveDataType type;
|
||||||
|
SaveDataRank rank;
|
||||||
|
u16 index;
|
||||||
|
u8 reserved[0x1C];
|
||||||
|
|
||||||
|
static constexpr SaveDataAttribute Make(ncm::ProgramId program_id, SaveDataType type, UserId user_id, SystemSaveDataId system_save_data_id, u16 index, SaveDataRank rank) {
|
||||||
|
return {
|
||||||
|
.program_id = program_id,
|
||||||
|
.user_id = user_id,
|
||||||
|
.system_save_data_id = system_save_data_id,
|
||||||
|
.type = type,
|
||||||
|
.rank = rank,
|
||||||
|
.index = index,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr SaveDataAttribute Make(ncm::ProgramId program_id, SaveDataType type, UserId user_id, SystemSaveDataId system_save_data_id, u16 index) {
|
||||||
|
return Make(program_id, type, user_id, system_save_data_id, index, SaveDataRank::Primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr SaveDataAttribute Make(ncm::ProgramId program_id, SaveDataType type, UserId user_id, SystemSaveDataId system_save_data_id) {
|
||||||
|
return Make(program_id, type, user_id, system_save_data_id, 0, SaveDataRank::Primary);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static_assert(sizeof(SaveDataAttribute) == 0x40);
|
||||||
|
static_assert(std::is_trivially_destructible<SaveDataAttribute>::value);
|
||||||
|
|
||||||
|
constexpr inline bool operator<(const SaveDataAttribute &lhs, const SaveDataAttribute &rhs) {
|
||||||
|
return std::tie(lhs.program_id, lhs.user_id, lhs.system_save_data_id, lhs.index, lhs.rank) <
|
||||||
|
std::tie(rhs.program_id, rhs.user_id, rhs.system_save_data_id, rhs.index, rhs.rank);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline bool operator==(const SaveDataAttribute &lhs, const SaveDataAttribute &rhs) {
|
||||||
|
return std::tie(lhs.program_id, lhs.user_id, lhs.system_save_data_id, lhs.type, lhs.rank, lhs.index) ==
|
||||||
|
std::tie(rhs.program_id, rhs.user_id, rhs.system_save_data_id, rhs.type, rhs.rank, rhs.index);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline bool operator!=(const SaveDataAttribute &lhs, const SaveDataAttribute &rhs) {
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline size_t DefaultSaveDataBlockSize = 16_KB;
|
||||||
|
|
||||||
|
struct SaveDataExtraData {
|
||||||
|
SaveDataAttribute attr;
|
||||||
|
u64 owner_id;
|
||||||
|
s64 timestamp;
|
||||||
|
u32 flags;
|
||||||
|
u8 pad[4];
|
||||||
|
s64 available_size;
|
||||||
|
s64 journal_size;
|
||||||
|
s64 commit_id;
|
||||||
|
u8 unused[0x190];
|
||||||
|
};
|
||||||
|
static_assert(sizeof(SaveDataExtraData) == 0x200);
|
||||||
|
static_assert(std::is_pod<SaveDataExtraData>::value);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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 "fs_common.hpp"
|
||||||
|
|
||||||
|
namespace ams::fs {
|
||||||
|
|
||||||
|
Result MountSdCard(const char *name);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "fs_common.hpp"
|
||||||
|
|
||||||
|
namespace ams::fs {
|
||||||
|
|
||||||
|
bool IsSignedSystemPartitionOnSdCardValid(const char *system_root_path);
|
||||||
|
bool IsSignedSystemPartitionOnSdCardValidDeprecated();
|
||||||
|
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user