Compare commits

...

52 Commits

Author SHA1 Message Date
Michael Scire
3cb54e2b4b git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "f4a966eb4"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "f4a966eb4"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2023-05-14 03:17:20 -07:00
Michael Scire
e9de11a746 ams: bump version to 1.5.4 2023-05-14 03:16:27 -07:00
Michael Scire
b979b5aa36 docs: 1.5.4 changelog, zelda why the fuck 2023-05-14 03:16:15 -07:00
SciresM
f2ee44da74 romfs/ams.mitm/pm: refactor to dynamically steal heap for certain games. (#2122)
* fs.mitm: skeleton the use of special allocation in romfs build

* pm: add api for ams.mitm to steal application memory

* pm/mitm: okay, that api won't work, try a different one

* romfs: revert memory usage increases; we'll handle torture games case-by-case.

* pm/romfs: first (broken?) pass at dynamic heap.

I cannot wait to figure out all the ways this is wrong.

* Release the dynamic heap a little more eagerly

* romfs: animal crossing is also not a nice game

* romfs: fix issues in close-during-build

* romfs: zelda is a blight upon this earth
2023-05-14 03:06:52 -07:00
Michael Scire
85c23b5781 fusee: actually identify new FS 2023-05-08 18:38:13 -07:00
Michael Scire
8e042f2262 git subrepo push emummc
subrepo:
  subdir:   "emummc"
  merged:   "30205111e"
upstream:
  origin:   "https://github.com/m4xw/emummc"
  branch:   "develop"
  commit:   "30205111e"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2023-05-08 18:25:25 -07:00
Michael Scire
81e9154a52 emummc: add enums for 16.0.3 2023-05-08 18:24:49 -07:00
Michael Scire
e9b9dbc2aa docs: add changelog for 1.5.3 2023-05-08 18:19:31 -07:00
Michael Scire
7e6c849ca4 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "cd0fc2c1d"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "cd0fc2c1d"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2023-05-08 18:06:50 -07:00
Michael Scire
8ec7c096d0 git subrepo push emummc
subrepo:
  subdir:   "emummc"
  merged:   "d2fcc73eb"
upstream:
  origin:   "https://github.com/m4xw/emummc"
  branch:   "develop"
  commit:   "d2fcc73eb"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2023-05-08 18:05:36 -07:00
Michael Scire
6e5b901a9b emummc: support 16.0.3 2023-05-08 18:03:35 -07:00
Michael Scire
b800953d66 ams: recognize 16.0.3('s FS) 2023-05-08 17:51:13 -07:00
Michael Scire
f0240db75a fs.mitm: mitm glue for font replacement, before I forget 2023-05-08 17:40:10 -07:00
Michael Scire
1f5ec68a5c ams: fix compilation with gcc 13 2023-05-07 03:36:46 -07:00
Michael Scire
ed9e60acb9 kern: track heap in KPageTableBase::MemoryRange 2023-04-30 16:50:53 -07:00
Liam
a7300b0fa4 haze: fix file size transmission issue 2023-04-18 22:15:48 -07:00
Liam
8e2eca2004 haze: abstract firmware version and serial number fetch 2023-04-18 22:15:48 -07:00
Michael Scire
9f83b3c838 ams: I really need to automate keeping this in sync 2023-04-17 20:57:57 -07:00
Michael Scire
434c8cefc4 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "ecc8b1811"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "ecc8b1811"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2023-04-17 20:40:41 -07:00
Michael Scire
d8aed7de6d ams: add 16.0.2 enum 2023-04-17 20:40:02 -07:00
Liam
a346014dc7 haze: get serial number and firmware version from set:sys 2023-04-17 22:22:28 -04:00
Michael Scire
4b3c801e9f dist: add haze to output zip 2023-04-17 14:35:25 -07:00
Michael Scire
90db1223f6 docs: avoid unintended insinuations 2023-04-17 14:24:27 -07:00
Michael Scire
fa64a6ff4d docs: pretend to be literate 2023-04-17 14:20:16 -07:00
Michael Scire
0c6a06a0cf git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "7340e2eab"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "7340e2eab"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2023-04-17 14:19:59 -07:00
Michael Scire
5efb4a2a98 docs: bump version, 16.0.1 exists too 2023-04-17 14:19:18 -07:00
liamwhite
3b662122f9 troposphere: add haze MTP server (#2095)
* troposphere: add haze MTP server

* haze: adjust banner, new lines between class sections, single-statement if

* haze: remove additional newlines between sections

* haze: avoid use of reference out parameter

* haze: console_main_loop: style

* haze: event_reactor, file_system_proxy, ptp: style

* haze: ptp_data_builder, ptp_object_database, ptp_object_heap, results, usb_session: style

* haze: event_reactor, ptp_data_parser, async_usb_server, ptp_object_database, ptp_object_heap: style

* haze: ptp_responder: style

* haze: usb_session: style

* haze: use svc defs from vapours

* haze: misc comments

* haze: fix pointer overflow check

* haze: fix copy paste error

* haze: use alignment, invalidate cached use values in console

* haze: fix stray hex constant

* haze: misc style

* haze: fixes for windows

* haze: add GetObjectPropsSupported, GetObjectPropDesc, GetObjectPropValue

* haze: add SetObjectPropValue

* haze: improve object database API

* haze: improve WriteVariableLengthData readability

* haze: fix directory renames on windows

* haze: ptp_object_database: fix collation

* haze: event_reactor: fix size validation

* haze: ptp_responder: improve documentation

* haze: ptp_responder: avoid unnecessary fs interaction in GetObjectPropValue

* haze: ptp_responder: fix object deletion on windows

* haze: tear down sessions on suspension

* haze: prefer more specific data types

* haze: misc

* haze: fix usb 3.0 support

* haze: improve host-to-device file transfer performance

* haze: report device serial

* haze: move static_assert to requires, fix alignment
2023-04-17 14:08:05 -07:00
Michael Scire
e9b28ab4b1 kern: adjust wording to be more technically correct 2023-03-27 10:54:58 -07:00
Michael Scire
1afb184c14 docs: pretend I know how to type common words 2023-03-14 01:52:06 -07:00
Michael Scire
5e070600a9 docs: add faq.md
"Alexa, what does June 15th mean?"
2023-03-14 01:48:19 -07:00
Michael Scire
8274081e39 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "b1607dc8a"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "b1607dc8a"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2023-03-13 17:17:50 -07:00
Michael Scire
f1ad26ce84 ams: bump version 2023-03-13 17:16:51 -07:00
Michael Scire
e4c314146e docs: update changelog 2023-03-13 17:16:33 -07:00
Michael Scire
52f00731d9 settings: add os guard 2023-03-12 22:34:23 -07:00
Michael Scire
476d658a79 ams: allow convertible-to-result in abort print macros 2023-03-11 15:14:33 -07:00
Michael Scire
7263022bac docs: mention bluetooth db feature 2023-03-10 10:24:19 -07:00
Michael Scire
e0e7aa1e2f set.mitm: misc style fixes for bluetooth db 2023-03-10 10:19:58 -07:00
ndeadly
bd9d8fff46 Add system setting to mirror bluetooth pairing database to sd card (#1787)
* ams_mitm: add ability to mirror bluetooth device pairing database to sd card via a system setting

* ams_mitm: address requested stylistic changes

* ams_mitm: make use of R_SUCCEED macro

* ams_mitm: use settings::BluetoothDevicesSettings instead of libnx type

* ams_mitm: fix logic error when truncating pairing database on read

* Update .ini comment

* ams_mitm: missing R_TRY around call to fs::FlushFile

* stratosphere: remove union from BluetoothDevicesSettings type

---------

Co-authored-by: ndeadly <24677491+ndeadly@users.noreply.github.com>
2023-03-10 10:06:38 -07:00
Michael Scire
61e3f0b391 windows: disable -fdata-sections
This causes all data to be emitted as .data$*. This breaks fzero-initialized-in-bss,
because linker puts stuff in .data even when it's all-zero and should end up in .bss.
2023-03-09 23:46:54 -07:00
Michael Scire
cd9b173318 windows: re-enable lto for os/fs libraries 2023-03-09 23:46:31 -07:00
Michael Scire
a8df400825 kern: fix stray addressof operator 2023-03-08 09:46:38 -07:00
Michael Scire
68040e2922 windows: add brave new mingw compat hook for gcc 12.2.0+ 2023-03-07 22:07:03 -07:00
Michael Scire
8da4d14e15 kern: minor cleanup (thanks @liamwhite) 2023-03-06 21:18:00 -07:00
Michael Scire
e2ebf9c0ff fatal: correct comment about pools 2023-03-01 03:21:51 -07:00
Michael Scire
5fb6f52b9e fatal: dynamically allocate memory as required (preferring system > nonsecure > unsafe) 2023-03-01 03:20:49 -07:00
Michael Scire
982389dceb os: add UnsafeMemory apis 2023-03-01 03:20:49 -07:00
Adubbz
f636596ee2 daybreak: scale title font size
(cherry picked from commit 035b37c615183bd387210a54d269346e3a9b379a)
2023-02-25 10:53:16 +11:00
Michael Scire
0a2440522f fatal: use a hack to be kinder wrt pl:u session limit 2023-02-23 22:06:49 -07:00
Michael Scire
3292ea5970 kern: fix stray comment copy/paste error 2023-02-23 22:00:00 -07:00
Michael Scire
33d42f4831 erpt: update server code for 16.0.0 logic changes 2023-02-23 21:51:51 -07:00
Michael Scire
46094cfb3e kern: increase non-dynamic heap sizes to 8 GB to match 2023-02-23 19:05:52 -07:00
Michael Scire
618691a500 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "af0d00890"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "af0d00890"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2023-02-23 09:01:41 -07:00
116 changed files with 5424 additions and 254 deletions

View File

@@ -51,6 +51,8 @@ dist: dist-no-debug
cp $(CURRENT_DIRECTORY)/stratosphere/spl/$(ATMOSPHERE_OUT_DIR)/spl.elf $(DIST_DIR)/spl.elf
cp $(CURRENT_DIRECTORY)/stratosphere/TioServer/$(ATMOSPHERE_OUT_DIR)/TioServer.elf $(DIST_DIR)/TioServer.elf
cp $(CURRENT_DIRECTORY)/troposphere/daybreak/daybreak.elf $(DIST_DIR)/daybreak.elf
cp $(CURRENT_DIRECTORY)/troposphere/haze/haze.elf $(DIST_DIR)/haze.elf
cp $(CURRENT_DIRECTORY)/troposphere/reboot_to_payload/reboot_to_payload.elf $(DIST_DIR)/reboot_to_payload.elf
cd $(DIST_DIR); zip -r ../atmosphere-$(ATMOSPHERE_VERSION)-debug.zip ./*; cd ../;
rm -rf $(DIST_DIR)
@@ -106,6 +108,7 @@ dist-no-debug: package3 $(CURRENT_DIRECTORY)/$(ATMOSPHERE_OUT_DIR)
rm -r $(DIST_DIR)/stratosphere_romfs
cp troposphere/reboot_to_payload/reboot_to_payload.nro $(DIST_DIR)/switch/reboot_to_payload.nro
cp troposphere/daybreak/daybreak.nro $(DIST_DIR)/switch/daybreak.nro
cp troposphere/haze/haze.nro $(DIST_DIR)/switch/haze.nro
cd $(DIST_DIR); zip -r ../atmosphere-$(ATMOSPHERE_VERSION).zip ./*; cd ../;
rm -rf $(DIST_DIR)
cp fusee/$(ATMOSPHERE_BOOT_OUT_DIR)/fusee.bin $(CURRENT_DIRECTORY)/$(ATMOSPHERE_OUT_DIR)/fusee.bin

View File

@@ -67,6 +67,10 @@
; Note that this setting is ignored (and treated as 1) when htc is enabled.
; 0 = Disabled, 1 = Enabled
; enable_log_manager = u8!0x0
; Controls whether the bluetooth pairing database is redirected to the SD card (shared across sysmmc/all emummcs)
; NOTE: On <13.0.0, the database size was 10 instead of 20; booting pre-13.0.0 will truncate the database.
; 0 = Disabled, 1 = Enabled
; enable_external_bluetooth_db = u8!0x0
[hbloader]
; Controls the size of the homebrew heap when running as applet.
; If set to zero, all available applet memory is used as heap.

View File

@@ -1,4 +1,46 @@
# Changelog
## 1.5.4
+ Experimental new functionality was implemented to prevent crashing when building romfs for certain games with obscene file counts.
+ This includes both Fire Emblem: Engage (~190000 files), and The Legend of Zelda: Tears of the Kingdom (~300000) files.
+ The solution involved adding functionality to ams.mitm/pm to dynamically steal memory from the application (and system) pool as needed when the games have romfs mods.
+ No memory is taken, and there is no cost to this functionality when playing without mods (or with overrides disabled).
+ The Legend of Zelda: Tears of the Kingdom is currently the absolute worst case game, requiring ~48 MB of memory to build a romfs image to play with mods.
+ Right now, the memory is sourced as follows: 32 MB (base ams.mitm heap), 10 MB (stolen from application pool), 8 MB (dynamically stolen from system pool).
+ This is 50 MB, which allows a little overhead in the worst case (prevents crashing due to exhausting the heap for other allocations in ams.mitm).
+ Zelda is remarkably sensitive to memory being stolen from the application pool, tolerating no more than 16 MB on 1.0.0 and 12 MB on 1.1.0. I have chosen to steal 10 MB, to be safe, for now.
+ This may break on a future game update, but I will fix it if and when that happens. There is no perfect solution; the game simply requires too much memory to support mods flawlessly, and I am forced to compromise.
+ As usual, if you encounter a game that exhausts ams.mitm's memory (crashing it) when loading layeredfs mods, please contact `SciresM#0524`.
"I am jinxing myself by saying this, but it's really hard to imagine any game being worse than The Legend of Zelda: Tears of the Kingdom, but if it happens again I will drop everything to fix it as usual".
+ General system stability improvements to enhance the user's experience.
## 1.5.3
+ Support was added for 16.0.3.
+ Atmosphère was updated to use GCC 13/newlib (latest devkitA64/devkitARM releases).
+ **Please note**: This introduces a known issue, which is currently being worked on.
+ As you may recall from the 1.4.1 changelog, Fire Emblem: Engage requires enormous amounts of memory to support using layeredfs mods with the game.
+ Latest GCC/newlib slightly increases malloc overhead size, which makes the previous memory increase insufficient.
+ A general-case solution to this is in the works, which should hopefully fix the problem in a way that doesn't jinx me for the future.
+ A number of minor issues were fixed and improvements were made, including:
+ An issue was fixed that caused system font replacement to not work on 16.0.0+.
+ An minor accuracy issue was addressed in mesosphere's management of certain memory ranges; this issue would have had zero visible impact to the end-user.
+ General system stability improvements to enhance the user's experience.
## 1.5.2
+ A homebrew application (`haze`) was added for performing USB file transfer (with thanks to @liamwhite for both design and implementation).
+ `haze` is included with atmosphère, and provides access to the SD card via the PTP/MTP protocol.
+ **Please note**: haze will show inside the homebrew menu under the name "USB File Transfer".
+ **Please note**: Atmosphère cannot be updated at runtime, and trying to install an atmosphère update via haze will fail as usual.
+ General system stability improvements to enhance the user's experience.
## 1.5.1
+ `fatal` was updated to reduce memory footprint.
+ Starting in 16.0.0, official `fatal` has no framebuffer or rendering logic, and instead calls other system service commands to draw the screen.
+ However, these commands aren't usable by atmosphère (too small rendering window, bad color support).
+ To reduce the relative memory footprint differential between atmosphère and official code, the framebuffer (2 MB) is now dynamically allocated when needed.
+ This will try to allocate from multiple pools (preferring System > System_NonSecure > Application).
+ This technically requires that 2 MB be available in at least one of these pools for the fatal screen to render (otherwise, a reboot-to-black-and-white-fatal will occur), but this should be a non-issue in almost all cases.
+ A feature was added to optionally mirror the bluetooth pairing database to the SD card (thanks @ndeadly).
+ This allows device pairings to be automatically kept in-sync across sysmmc/all emummcs.
+ This is opt-in, and can be controlled by setting `atmosphere!enable_external_bluetooth_db = u8!0x1`.
+ When enabled, the pairing database will be synchronized to `/atmosphere/bluetooth_devices.db`.
+ General system stability improvements to enhance the user's experience.
## 1.5.0
+ Support was added for 16.0.0
+ `mesosphère` was updated to reflect the latest official kernel behavior.
@@ -22,6 +64,7 @@
+ The additional memory here is taken from the applet pool; no issues are expected to arise from this, but please report anything you may run into.
+ As usual, if you encounter a game that exhausts ams.mitm's memory (crashing it) when loading layeredfs mods, please contact `SciresM#0524`.
+ I am jinxing myself by saying this, but it's really hard to imagine any game being worse than Fire Emblem: Engage, but if it happens again I will drop everything to fix it as usual.
+ General system stability improvements to enhance the user's experience.
## 1.4.0
+ Support was added for 15.0.0.
+ `mesosphère` was updated to reflect the latest official kernel behavior.

18
docs/faq.md Normal file
View File

@@ -0,0 +1,18 @@
# Frequently Asked Questions
This document serves as a place to store answers for common questions received about Atmosphère.
## What does "June 15th" mean?
When Atmosphère began development in February 2018, "June 15" was given as the estimate/target date for a first release, to coincide with the planned disclosure of a vulnerability.
This deadline was missed, hard.
People made rather a lot of fun of me (SciresM) for this.
Several months later, when the first Atmosphère release occurred, I captioned it "Happy June 15th!" and pretended like I hadn't missed the first deadline.
This amused me a lot, and so the practice has been kept up for every single release since.
Depending on who you ask, you may be told that this is a dumb joke and it is not funny.
This is incorrect. It is definitely a dumb joke, but it is also hilarious.

View File

@@ -27,3 +27,6 @@ A list of planned features for Atmosphère can be found [here](roadmap.md).
## Release History
A changelog of previous versions of Atmosphère can be found [here](changelog.md).
## Frequently Asked Questions
Answers to one or more frequently asked questions may be found [here](faq.md).

4
emummc/.gitrepo vendored
View File

@@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/m4xw/emummc
branch = develop
commit = bba1f1fb65f75721caf6d023a35d75d3c9aafd0f
parent = 1ab8b234447864503e21c600681219a96962e6c1
commit = 30205111ee375bef96f0f76cb6a3130a2f0fc85c
parent = 81e9154a52a976f85317bddd0131426599d26a62
method = merge
cmdver = 0.4.1

2
emummc/README.md vendored
View File

@@ -2,7 +2,7 @@
*A SDMMC driver replacement for Nintendo's Filesystem Services, by **m4xw***
### Supported Horizon Versions
**1.0.0 - 16.0.0**
**1.0.0 - 16.0.3**
## Features
* Arbitrary SDMMC backend selection

View File

@@ -65,6 +65,8 @@
#include "offsets/1500_exfat.h"
#include "offsets/1600.h"
#include "offsets/1600_exfat.h"
#include "offsets/1603.h"
#include "offsets/1603_exfat.h"
#include "../utils/fatal.h"
#define GET_OFFSET_STRUCT_NAME(vers) g_offsets##vers
@@ -141,6 +143,8 @@ DEFINE_OFFSET_STRUCT(_1500);
DEFINE_OFFSET_STRUCT(_1500_EXFAT);
DEFINE_OFFSET_STRUCT(_1600);
DEFINE_OFFSET_STRUCT(_1600_EXFAT);
DEFINE_OFFSET_STRUCT(_1603);
DEFINE_OFFSET_STRUCT(_1603_EXFAT);
const fs_offsets_t *get_fs_offsets(enum FS_VER version) {
switch (version) {
@@ -242,6 +246,10 @@ const fs_offsets_t *get_fs_offsets(enum FS_VER version) {
return &(GET_OFFSET_STRUCT_NAME(_1600));
case FS_VER_16_0_0_EXFAT:
return &(GET_OFFSET_STRUCT_NAME(_1600_EXFAT));
case FS_VER_16_0_3:
return &(GET_OFFSET_STRUCT_NAME(_1603));
case FS_VER_16_0_3_EXFAT:
return &(GET_OFFSET_STRUCT_NAME(_1603_EXFAT));
default:
fatal_abort(Fatal_UnknownVersion);
}

View File

@@ -95,6 +95,9 @@ enum FS_VER
FS_VER_16_0_0,
FS_VER_16_0_0_EXFAT,
FS_VER_16_0_3,
FS_VER_16_0_3_EXFAT,
FS_VER_MAX,
};

59
emummc/source/FS/offsets/1603.h vendored Normal file
View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) 2019 m4xw <m4x@m4xw.net>
* Copyright (c) 2019 Atmosphere-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __FS_1603_H__
#define __FS_1603_H__
// Accessor vtable getters
#define FS_OFFSET_1603_SDMMC_ACCESSOR_GC 0x1862F0
#define FS_OFFSET_1603_SDMMC_ACCESSOR_SD 0x187F70
#define FS_OFFSET_1603_SDMMC_ACCESSOR_NAND 0x1867B0
// Hooks
#define FS_OFFSET_1603_SDMMC_WRAPPER_READ 0x182240
#define FS_OFFSET_1603_SDMMC_WRAPPER_WRITE 0x1822A0
#define FS_OFFSET_1603_RTLD 0x269B0
#define FS_OFFSET_1603_RTLD_DESTINATION ((uintptr_t)(INT64_C(-0x3C)))
#define FS_OFFSET_1603_CLKRST_SET_MIN_V_CLK_RATE 0x1A2D80
// Misc funcs
#define FS_OFFSET_1603_LOCK_MUTEX 0x17B780
#define FS_OFFSET_1603_UNLOCK_MUTEX 0x17B7D0
#define FS_OFFSET_1603_SDMMC_WRAPPER_CONTROLLER_OPEN 0x182200
#define FS_OFFSET_1603_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x182220
// Misc Data
#define FS_OFFSET_1603_SD_MUTEX 0xFFB3F0
#define FS_OFFSET_1603_NAND_MUTEX 0xFF6B58
#define FS_OFFSET_1603_ACTIVE_PARTITION 0xFF6B98
#define FS_OFFSET_1603_SDMMC_DAS_HANDLE 0xFDC8B0
// NOPs
#define FS_OFFSET_1603_SD_DAS_INIT 0x258D4
// Nintendo Paths
#define FS_OFFSET_1603_NINTENDO_PATHS \
{ \
{.opcode_reg = 3, .adrp_offset = 0x00063B98, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 3, .adrp_offset = 0x00070DBC, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 4, .adrp_offset = 0x0007795C, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 4, .adrp_offset = 0x0008A7A4, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
}
#endif // __FS_1603_H__

59
emummc/source/FS/offsets/1603_exfat.h vendored Normal file
View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) 2019 m4xw <m4x@m4xw.net>
* Copyright (c) 2019 Atmosphere-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __FS_1603_EXFAT_H__
#define __FS_1603_EXFAT_H__
// Accessor vtable getters
#define FS_OFFSET_1603_EXFAT_SDMMC_ACCESSOR_GC 0x190FD0
#define FS_OFFSET_1603_EXFAT_SDMMC_ACCESSOR_SD 0x192C50
#define FS_OFFSET_1603_EXFAT_SDMMC_ACCESSOR_NAND 0x191490
// Hooks
#define FS_OFFSET_1603_EXFAT_SDMMC_WRAPPER_READ 0x18CF20
#define FS_OFFSET_1603_EXFAT_SDMMC_WRAPPER_WRITE 0x18CF80
#define FS_OFFSET_1603_EXFAT_RTLD 0x269B0
#define FS_OFFSET_1603_EXFAT_RTLD_DESTINATION ((uintptr_t)(INT64_C(-0x3C)))
#define FS_OFFSET_1603_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x1ADA60
// Misc funcs
#define FS_OFFSET_1603_EXFAT_LOCK_MUTEX 0x186460
#define FS_OFFSET_1603_EXFAT_UNLOCK_MUTEX 0x1864B0
#define FS_OFFSET_1603_EXFAT_SDMMC_WRAPPER_CONTROLLER_OPEN 0x18CEE0
#define FS_OFFSET_1603_EXFAT_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x18CF00
// Misc Data
#define FS_OFFSET_1603_EXFAT_SD_MUTEX 0x100D3F0
#define FS_OFFSET_1603_EXFAT_NAND_MUTEX 0x1008B58
#define FS_OFFSET_1603_EXFAT_ACTIVE_PARTITION 0x1008B98
#define FS_OFFSET_1603_EXFAT_SDMMC_DAS_HANDLE 0xFE98B0
// NOPs
#define FS_OFFSET_1603_EXFAT_SD_DAS_INIT 0x258D4
// Nintendo Paths
#define FS_OFFSET_1603_EXFAT_NINTENDO_PATHS \
{ \
{.opcode_reg = 3, .adrp_offset = 0x00063B98, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 3, .adrp_offset = 0x00070DBC, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 4, .adrp_offset = 0x0007795C, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 4, .adrp_offset = 0x0008A7A4, .add_rel_offset = 0x00000004}, \
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
}
#endif // __FS_1603_EXFAT_H__

View File

@@ -162,6 +162,9 @@ namespace ams::nxboot {
FsVersion_16_0_0,
FsVersion_16_0_0_Exfat,
FsVersion_16_0_3,
FsVersion_16_0_3_Exfat,
FsVersion_Count,
};
@@ -239,6 +242,9 @@ namespace ams::nxboot {
{ 0x56, 0xE8, 0x56, 0x56, 0x6C, 0x38, 0xD8, 0xBE }, /* FsVersion_16_0_0 */
{ 0xCF, 0xAB, 0x45, 0x0C, 0x2C, 0x53, 0x9D, 0xA9 }, /* FsVersion_16_0_0_Exfat */
{ 0x39, 0xEE, 0x1F, 0x1E, 0x0E, 0xA7, 0x32, 0x5D }, /* FsVersion_16_0_3 */
{ 0x62, 0xC6, 0x5E, 0xFD, 0x9A, 0xBF, 0x7C, 0x43 }, /* FsVersion_16_0_3_Exfat */
};
const InitialProcessBinaryHeader *FindInitialProcessBinary(const pkg2::Package2Header *header, const u8 *data, ams::TargetFirmware target_firmware) {
@@ -656,6 +662,14 @@ namespace ams::nxboot {
AddPatch(fs_meta, 0x1913B9, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x16B950, NogcPatch1, sizeof(NogcPatch1));
break;
case FsVersion_16_0_3:
AddPatch(fs_meta, 0x186729, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x160CC0, NogcPatch1, sizeof(NogcPatch1));
break;
case FsVersion_16_0_3_Exfat:
AddPatch(fs_meta, 0x191409, NogcPatch0, sizeof(NogcPatch0));
AddPatch(fs_meta, 0x16B9A0, NogcPatch1, sizeof(NogcPatch1));
break;
default:
break;
}

View File

@@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
branch = master
commit = b7711b8fbcec5013e1738218267d69b2cb44f85e
parent = 590f22933db38f089ab2224f7fd86658a9533e8d
commit = f4a966eb4cd102c4648d5c0d0e6c2527c02ff8d2
parent = e9de11a746ce94e556bf6a7b4a73edc5502d8806
method = merge
cmdver = 0.4.1

View File

@@ -1,5 +1,5 @@
export ATMOSPHERE_DEFINES += -DATMOSPHERE_OS_WINDOWS
export ATMOSPHERE_SETTINGS += -fno-omit-frame-pointer
export ATMOSPHERE_SETTINGS += -fno-omit-frame-pointer -fno-data-sections
export ATMOSPHERE_CFLAGS +=
export ATMOSPHERE_CXXFLAGS +=
export ATMOSPHERE_ASFLAGS +=

View File

@@ -58,7 +58,7 @@ export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \
-Wl,--wrap,exit
else ifeq ($(ATMOSPHERE_BOARD),generic_windows)
export CXXREQUIRED :=
export CXXWRAPS := -Wl,--wrap,__p__acmdln
export CXXWRAPS := -Wl,--wrap,__p__acmdln -Wl,--wrap,_set_invalid_parameter_handler
else
export CXXREQUIRED :=
export CXXWRAPS :=

View File

@@ -79,7 +79,7 @@ namespace ams::kern {
}
ALWAYS_INLINE bool Close() {
/* Atomically decrement the reference count, not allowing it to become negative. */
/* Atomically decrement the reference count, not allowing it to decrement past zero. */
u32 cur = m_value.Load<std::memory_order_relaxed>();
do {
MESOSPHERE_ABORT_UNLESS(cur > 0);
@@ -257,7 +257,7 @@ namespace ams::kern {
class KScopedAutoObject {
NON_COPYABLE(KScopedAutoObject);
private:
template<typename U>
template<typename U> requires std::derived_from<U, KAutoObject>
friend class KScopedAutoObject;
private:
T *m_obj;

View File

@@ -48,7 +48,7 @@ namespace ams::kern {
/* Check that the object is closed. */
R_UNLESS(derived->IsServerClosed(), svc::ResultInvalidState());
return Delete(obj.GetPointerUnsafe(), name);
R_RETURN(Delete(obj.GetPointerUnsafe(), name));
}
template<typename Derived> requires std::derived_from<Derived, KAutoObject>

View File

@@ -57,11 +57,26 @@ namespace ams::kern {
using TraversalEntry = KPageTableImpl::TraversalEntry;
using TraversalContext = KPageTableImpl::TraversalContext;
struct MemoryRange {
KPhysicalAddress address;
size_t size;
class MemoryRange {
private:
KPhysicalAddress m_address;
size_t m_size;
bool m_heap;
public:
constexpr MemoryRange() : m_address(Null<KPhysicalAddress>), m_size(0), m_heap(false) { /* ... */ }
void Close();
void Set(KPhysicalAddress address, size_t size, bool heap) {
m_address = address;
m_size = size;
m_heap = heap;
}
constexpr KPhysicalAddress GetAddress() const { return m_address; }
constexpr size_t GetSize() const { return m_size; }
constexpr bool IsHeap() const { return m_heap; }
void Open();
void Close();
};
protected:
enum MemoryFillValue {

View File

@@ -234,11 +234,11 @@ namespace ams::kern {
KPriorityQueueImpl m_scheduled_queue;
KPriorityQueueImpl m_suggested_queue;
private:
constexpr ALWAYS_INLINE void ClearAffinityBit(u64 &affinity, s32 core) {
static constexpr ALWAYS_INLINE void ClearAffinityBit(u64 &affinity, s32 core) {
affinity &= ~(UINT64_C(1) << core);
}
constexpr ALWAYS_INLINE s32 GetNextCore(u64 &affinity) {
static constexpr ALWAYS_INLINE s32 GetNextCore(u64 &affinity) {
const s32 core = __builtin_ctzll(static_cast<unsigned long long>(affinity));
ClearAffinityBit(affinity, core);
return core;

View File

@@ -214,7 +214,7 @@ namespace ams::kern {
template<typename T> requires (std::same_as<T, KThread> || std::same_as<T, RedBlackKeyType>)
static constexpr ALWAYS_INLINE int Compare(const T &lhs, const KThread &rhs) {
if (lhs.GetPriority() < rhs.GetPriority()) {
/* And then by priority. */
/* Sort by priority. */
return -1;
} else {
return 1;

View File

@@ -120,10 +120,6 @@ namespace ams::kern {
return m_address == rhs;
}
constexpr ALWAYS_INLINE bool operator!=(uintptr_t rhs) const {
return m_address != rhs;
}
/* Allow getting the address explicitly, for use in accessors. */
constexpr ALWAYS_INLINE uintptr_t GetValue() const {
return m_address;

View File

@@ -164,8 +164,9 @@ namespace ams::kern {
};
template<typename Derived, typename Base, bool SupportDynamicExpansion = false> requires std::derived_from<Base, KAutoObjectWithList>
template<typename Derived, typename Base, bool SupportDynamicExpansion = false>
class KAutoObjectWithSlabHeapAndContainer : public KAutoObjectWithSlabHeapBase<Derived, Base, SupportDynamicExpansion> {
static_assert(std::derived_from<Base, KAutoObjectWithList>);
private:
static constinit inline KAutoObjectWithListContainer<Derived> s_container;
public:

View File

@@ -1133,17 +1133,17 @@ namespace ams::kern::board::nintendo::nx {
size_t cur_size;
{
/* Get the current contiguous range. */
KPageTableBase::MemoryRange contig_range = { .address = Null<KPhysicalAddress>, .size = 0 };
KPageTableBase::MemoryRange contig_range;
R_TRY(page_table->OpenMemoryRangeForMapDeviceAddressSpace(std::addressof(contig_range), process_address + mapped_size, size - mapped_size, ConvertToKMemoryPermission(device_perm), is_aligned));
/* Ensure we close the range when we're done. */
ON_SCOPE_EXIT { contig_range.Close(); };
/* Get the current size. */
cur_size = contig_range.size;
cur_size = contig_range.GetSize();
/* Map the device page. */
R_TRY(this->MapDevicePage(contig_range.address, contig_range.size, cur_addr, device_perm));
R_TRY(this->MapDevicePage(contig_range.GetAddress(), cur_size, cur_addr, device_perm));
}
/* Advance. */
@@ -1288,7 +1288,7 @@ namespace ams::kern::board::nintendo::nx {
MESOSPHERE_ASSERT(((device_address + size - 1) & ~DeviceVirtualAddressMask) == 0);
/* We need to traverse the ranges that make up our mapping, to make sure they're all good. Start by getting a contiguous range. */
KPageTableBase::MemoryRange contig_range = { .address = Null<KPhysicalAddress>, .size = 0 };
KPageTableBase::MemoryRange contig_range;
if (R_FAILED(page_table->OpenMemoryRangeForUnmapDeviceAddressSpace(std::addressof(contig_range), process_address, size))) {
return false;
}
@@ -1300,8 +1300,8 @@ namespace ams::kern::board::nintendo::nx {
/* Walk the directory. */
KProcessAddress cur_process_address = process_address;
size_t remaining_size = size;
KPhysicalAddress cur_phys_address = contig_range.address;
size_t remaining_in_range = contig_range.size;
KPhysicalAddress cur_phys_address = contig_range.GetAddress();
size_t remaining_in_range = contig_range.GetSize();
bool first = true;
u32 first_attr = 0;
while (remaining_size > 0) {
@@ -1347,8 +1347,8 @@ namespace ams::kern::board::nintendo::nx {
}
range_open = true;
cur_phys_address = contig_range.address;
remaining_in_range = contig_range.size;
cur_phys_address = contig_range.GetAddress();
remaining_in_range = contig_range.GetSize();
}
/* Check that the physical address is expected. */
@@ -1390,8 +1390,8 @@ namespace ams::kern::board::nintendo::nx {
}
range_open = true;
cur_phys_address = contig_range.address;
remaining_in_range = contig_range.size;
cur_phys_address = contig_range.GetAddress();
remaining_in_range = contig_range.GetSize();
}
/* Check that the physical address is expected, and there's enough in the range. */

View File

@@ -51,7 +51,7 @@ namespace ams::kern {
/* Sleep the thread. */
{
KScopedSchedulerLockAndSleep lk(&timer, owner, timeout);
KScopedSchedulerLockAndSleep lk(std::addressof(timer), owner, timeout);
if (!allow_terminating_thread && owner->IsTerminationRequested()) {
lk.CancelSleep();

View File

@@ -74,8 +74,18 @@ namespace ams::kern {
}
void KPageTableBase::MemoryRange::Open() {
/* If the range contains heap pages, open them. */
if (this->IsHeap()) {
Kernel::GetMemoryManager().Open(this->GetAddress(), this->GetSize() / PageSize);
}
}
void KPageTableBase::MemoryRange::Close() {
Kernel::GetMemoryManager().Close(address, size / PageSize);
/* If the range contains heap pages, close them. */
if (this->IsHeap()) {
Kernel::GetMemoryManager().Close(this->GetAddress(), this->GetSize() / PageSize);
}
}
Result KPageTableBase::InitializeForKernel(bool is_64_bit, void *table, KVirtualAddress start, KVirtualAddress end) {
@@ -1504,16 +1514,13 @@ namespace ams::kern {
/* Check that the memory is contiguous (modulo the reference count bit). */
const u32 test_state_mask = state_mask | KMemoryState_FlagReferenceCounted;
if (R_FAILED(this->CheckMemoryStateContiguous(address, size, test_state_mask, state | KMemoryState_FlagReferenceCounted, perm_mask, perm, attr_mask, attr))) {
const bool is_heap = R_SUCCEEDED(this->CheckMemoryStateContiguous(address, size, test_state_mask, state | KMemoryState_FlagReferenceCounted, perm_mask, perm, attr_mask, attr));
if (!is_heap) {
R_TRY(this->CheckMemoryStateContiguous(address, size, test_state_mask, state, perm_mask, perm, attr_mask, attr));
}
/* The memory is contiguous, so set the output range. */
*out = {
.address = phys_address,
.size = size,
};
out->Set(phys_address, size, is_heap);
R_SUCCEED();
}
@@ -2932,7 +2939,7 @@ namespace ams::kern {
KMemoryAttribute_IpcLocked | KMemoryAttribute_Locked, KMemoryAttribute_None));
/* We got the range, so open it. */
Kernel::GetMemoryManager().Open(out->address, out->size / PageSize);
out->Open();
R_SUCCEED();
}
@@ -2949,7 +2956,7 @@ namespace ams::kern {
KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked, KMemoryAttribute_DeviceShared));
/* We got the range, so open it. */
Kernel::GetMemoryManager().Open(out->address, out->size / PageSize);
out->Open();
R_SUCCEED();
}
@@ -3020,7 +3027,7 @@ namespace ams::kern {
KMemoryAttribute_Uncached, KMemoryAttribute_None));
/* We got the range, so open it. */
Kernel::GetMemoryManager().Open(out->address, out->size / PageSize);
out->Open();
R_SUCCEED();
}

View File

@@ -36,15 +36,15 @@ namespace ams::kern::svc {
size_t remaining = size;
while (remaining > 0) {
/* Get a contiguous range to operate on. */
KPageTableBase::MemoryRange contig_range = { .address = Null<KPhysicalAddress>, .size = 0 };
KPageTableBase::MemoryRange contig_range;
R_TRY(page_table.OpenMemoryRangeForProcessCacheOperation(std::addressof(contig_range), cur_address, aligned_end - cur_address));
/* Close the range when we're done operating on it. */
ON_SCOPE_EXIT { contig_range.Close(); };
/* Adjust to remain within range. */
KVirtualAddress operate_address = KMemoryLayout::GetLinearVirtualAddress(contig_range.address);
size_t operate_size = contig_range.size;
KVirtualAddress operate_address = KMemoryLayout::GetLinearVirtualAddress(contig_range.GetAddress());
size_t operate_size = contig_range.GetSize();
if (cur_address < address) {
operate_address += (address - cur_address);
operate_size -= (address - cur_address);
@@ -57,7 +57,7 @@ namespace ams::kern::svc {
operation.Operate(GetVoidPointer(operate_address), operate_size);
/* Advance. */
cur_address += contig_range.size;
cur_address += contig_range.GetSize();
remaining -= operate_size;
}
MESOSPHERE_ASSERT(remaining == 0);

View File

@@ -27,7 +27,7 @@ namespace ams::kern::svc {
R_UNLESS(size < ams::kern::MainMemorySizeMax, svc::ResultInvalidSize());
/* Set the heap size. */
KProcessAddress address;
KProcessAddress address = Null<KProcessAddress>;
R_TRY(GetCurrentProcess().GetPageTable().SetHeapSize(std::addressof(address), size));
/* Set the output. */

View File

@@ -73,6 +73,7 @@
#include <stratosphere/ldr.hpp>
#include <stratosphere/lr.hpp>
#include <stratosphere/lm.hpp>
#include <stratosphere/mitm.hpp>
#include <stratosphere/ncm.hpp>
#include <stratosphere/nim.hpp>
#include <stratosphere/ns.hpp>

View File

@@ -76,6 +76,9 @@ namespace ams::hos {
Version_15_0_0 = ::ams::TargetFirmware_15_0_0,
Version_15_0_1 = ::ams::TargetFirmware_15_0_1,
Version_16_0_0 = ::ams::TargetFirmware_16_0_0,
Version_16_0_1 = ::ams::TargetFirmware_16_0_1,
Version_16_0_2 = ::ams::TargetFirmware_16_0_2,
Version_16_0_3 = ::ams::TargetFirmware_16_0_3,
Version_Current = ::ams::TargetFirmware_Current,

View File

@@ -0,0 +1,20 @@
/*
* Copyright (c) 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/mitm/impl/mitm_pm_interface.hpp>
#include <stratosphere/mitm/mitm_pm_api.hpp>

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/ncm/ncm_program_id.hpp>
#include <stratosphere/cfg/cfg_types.hpp>
#include <stratosphere/sf.hpp>
#define AMS_MITM_PM_IMPL_I_PM_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 65000, Result, PrepareLaunchProgram, (sf::Out<u64> out_boost_size, ncm::ProgramId program_id, const cfg::OverrideStatus &override_status, bool is_application), (out_boost_size, program_id, override_status, is_application))
AMS_SF_DEFINE_INTERFACE(ams::mitm::pm::impl, IPmInterface, AMS_MITM_PM_IMPL_I_PM_INTERFACE_INFO, 0xEA88789C)

View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/ncm/ncm_program_id.hpp>
namespace ams::mitm::pm {
/* PM API. */
void Initialize();
void Finalize();
Result PrepareLaunchProgram(u64 *out, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application);
}

View File

@@ -29,6 +29,7 @@
#include <stratosphere/os/os_process_memory_api.hpp>
#include <stratosphere/os/os_process_code_memory_api.hpp>
#include <stratosphere/os/os_insecure_memory_api.hpp>
#include <stratosphere/os/os_unsafe_memory_api.hpp>
#include <stratosphere/os/os_random.hpp>
#include <stratosphere/os/os_mutex.hpp>
#include <stratosphere/os/os_condition_variable.hpp>

View File

@@ -0,0 +1,25 @@
/*
* Copyright (c) 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/os/os_memory_common.hpp>
namespace ams::os {
Result AllocateUnsafeMemory(uintptr_t *out_address, size_t size);
Result FreeUnsafeMemory(uintptr_t address, size_t size);
}

View File

@@ -19,31 +19,32 @@
#include <stratosphere/pm/pm_types.hpp>
#include <stratosphere/sf.hpp>
#define AMS_PM_I_SHELL_INTERFACE_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 0, Result, LaunchProgram, (sf::Out<os::ProcessId> out_process_id, const ncm::ProgramLocation &loc, u32 flags), (out_process_id, loc, flags)) \
AMS_SF_METHOD_INFO(C, H, 1, Result, TerminateProcess, (os::ProcessId process_id), (process_id)) \
AMS_SF_METHOD_INFO(C, H, 2, Result, TerminateProgram, (ncm::ProgramId program_id), (program_id)) \
AMS_SF_METHOD_INFO(C, H, 3, void, GetProcessEventHandle, (sf::OutCopyHandle out), (out)) \
AMS_SF_METHOD_INFO(C, H, 4, void, GetProcessEventInfo, (sf::Out<pm::ProcessEventInfo> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 5, void, NotifyBootFinished, (), ()) \
AMS_SF_METHOD_INFO(C, H, 6, Result, GetApplicationProcessIdForShell, (sf::Out<os::ProcessId> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 7, Result, BoostSystemMemoryResourceLimit, (u64 boost_size), (boost_size)) \
AMS_SF_METHOD_INFO(C, H, 8, Result, BoostApplicationThreadResourceLimit, (), ()) \
AMS_SF_METHOD_INFO(C, H, 9, void, GetBootFinishedEventHandle, (sf::OutCopyHandle out), (out), hos::Version_8_0_0) \
AMS_SF_METHOD_INFO(C, H, 10, Result, BoostSystemThreadResourceLimit, (), ())
#define AMS_PM_I_SHELL_INTERFACE_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 0, Result, LaunchProgram, (sf::Out<os::ProcessId> out_process_id, const ncm::ProgramLocation &loc, u32 flags), (out_process_id, loc, flags)) \
AMS_SF_METHOD_INFO(C, H, 1, Result, TerminateProcess, (os::ProcessId process_id), (process_id)) \
AMS_SF_METHOD_INFO(C, H, 2, Result, TerminateProgram, (ncm::ProgramId program_id), (program_id)) \
AMS_SF_METHOD_INFO(C, H, 3, void, GetProcessEventHandle, (sf::OutCopyHandle out), (out)) \
AMS_SF_METHOD_INFO(C, H, 4, void, GetProcessEventInfo, (sf::Out<pm::ProcessEventInfo> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 5, void, NotifyBootFinished, (), ()) \
AMS_SF_METHOD_INFO(C, H, 6, Result, GetApplicationProcessIdForShell, (sf::Out<os::ProcessId> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 7, Result, BoostSystemMemoryResourceLimit, (u64 boost_size), (boost_size)) \
AMS_SF_METHOD_INFO(C, H, 8, Result, BoostApplicationThreadResourceLimit, (), ()) \
AMS_SF_METHOD_INFO(C, H, 9, void, GetBootFinishedEventHandle, (sf::OutCopyHandle out), (out), hos::Version_8_0_0) \
AMS_SF_METHOD_INFO(C, H, 10, Result, BoostSystemThreadResourceLimit, (), ())
AMS_SF_DEFINE_INTERFACE(ams::pm::impl, IShellInterface, AMS_PM_I_SHELL_INTERFACE_INTERFACE_INFO, 0x387D60C0)
#define AMS_PM_I_DEPRECATED_SHELL_INTERFACE_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 0, Result, LaunchProgram, (sf::Out<os::ProcessId> out_process_id, const ncm::ProgramLocation &loc, u32 flags), (out_process_id, loc, flags)) \
AMS_SF_METHOD_INFO(C, H, 1, Result, TerminateProcess, (os::ProcessId process_id), (process_id)) \
AMS_SF_METHOD_INFO(C, H, 2, Result, TerminateProgram, (ncm::ProgramId program_id), (program_id)) \
AMS_SF_METHOD_INFO(C, H, 3, void, GetProcessEventHandle, (sf::OutCopyHandle out), (out)) \
AMS_SF_METHOD_INFO(C, H, 4, void, GetProcessEventInfo, (sf::Out<pm::ProcessEventInfo> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 5, Result, CleanupProcess, (os::ProcessId process_id), (process_id)) \
AMS_SF_METHOD_INFO(C, H, 6, Result, ClearExceptionOccurred, (os::ProcessId process_id), (process_id)) \
AMS_SF_METHOD_INFO(C, H, 7, void, NotifyBootFinished, (), ()) \
AMS_SF_METHOD_INFO(C, H, 8, Result, GetApplicationProcessIdForShell, (sf::Out<os::ProcessId> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 9, Result, BoostSystemMemoryResourceLimit, (u64 boost_size), (boost_size), hos::Version_4_0_0)
#define AMS_PM_I_DEPRECATED_SHELL_INTERFACE_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 0, Result, LaunchProgram, (sf::Out<os::ProcessId> out_process_id, const ncm::ProgramLocation &loc, u32 flags), (out_process_id, loc, flags)) \
AMS_SF_METHOD_INFO(C, H, 1, Result, TerminateProcess, (os::ProcessId process_id), (process_id)) \
AMS_SF_METHOD_INFO(C, H, 2, Result, TerminateProgram, (ncm::ProgramId program_id), (program_id)) \
AMS_SF_METHOD_INFO(C, H, 3, void, GetProcessEventHandle, (sf::OutCopyHandle out), (out)) \
AMS_SF_METHOD_INFO(C, H, 4, void, GetProcessEventInfo, (sf::Out<pm::ProcessEventInfo> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 5, Result, CleanupProcess, (os::ProcessId process_id), (process_id)) \
AMS_SF_METHOD_INFO(C, H, 6, Result, ClearExceptionOccurred, (os::ProcessId process_id), (process_id)) \
AMS_SF_METHOD_INFO(C, H, 7, void, NotifyBootFinished, (), ()) \
AMS_SF_METHOD_INFO(C, H, 8, Result, GetApplicationProcessIdForShell, (sf::Out<os::ProcessId> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 9, Result, BoostSystemMemoryResourceLimit, (u64 boost_size), (boost_size), hos::Version_4_0_0) \
AMS_SF_METHOD_INFO(C, H, 10, Result, BoostSystemThreadResourceLimit, (), ())
AMS_SF_DEFINE_INTERFACE(ams::pm::impl, IDeprecatedShellInterface, AMS_PM_I_DEPRECATED_SHELL_INTERFACE_INTERFACE_INFO, 0x387D60C0)

View File

@@ -235,4 +235,31 @@ namespace ams::settings {
return !(lhs <= rhs);
}
struct BluetoothDevicesSettings : public sf::LargeData {
u8 address[0x6];
char name[0x20];
u8 class_of_device[0x3];
u8 link_key[0x10];
u8 link_key_present;
u16 version;
u32 trusted_services;
u16 vid;
u16 pid;
u8 sub_class;
u8 attribute_mask;
u16 descriptor_length;
u8 descriptor[0x80];
u8 key_type;
u8 device_type;
u16 brr_size;
u8 brr[0x9];
u8 reserved0;
char name2[0xF9];
u8 reserved1[0x31];
};
#if defined(ATMOSPHERE_OS_HORIZON)
static_assert(sizeof(BluetoothDevicesSettings) == sizeof(::SetSysBluetoothDevicesSettings));
#endif
}

View File

@@ -154,10 +154,12 @@ spl_secure_monitor_api.os.generic.o: CXXFLAGS += -I$(ATMOSPHERE_LIBRARIES_DIR)/l
fs_id_string_impl.os.generic.o: CXXFLAGS += -I$(ATMOSPHERE_LIBRARIES_DIR)/libexosphere/include
ifeq ($(ATMOSPHERE_OS_NAME),windows)
os_%.o: CXXFLAGS += -fno-lto
fssystem_%.o: CXXFLAGS += -fno-lto
fssrv_%.o: CXXFLAGS += -fno-lto
fs_%.o: CXXFLAGS += -fno-lto
# I do not remember why these had fno-lto, but it appears to
# work without no-lto (2023/03/09), so I am disabling these. I may regret this later.
#os_%.o: CXXFLAGS += -fno-lto
#fssystem_%.o: CXXFLAGS += -fno-lto
#fssrv_%.o: CXXFLAGS += -fno-lto
#fs_%.o: CXXFLAGS += -fno-lto
endif
#---------------------------------------------------------------------------------

View File

@@ -16,6 +16,7 @@
#include <stratosphere.hpp>
extern "C" char **__real___p__acmdln(void);
extern "C" _invalid_parameter_handler __real__set_invalid_parameter_handler(_invalid_parameter_handler);
namespace ams {
@@ -65,4 +66,11 @@ extern "C" {
return __real___p__acmdln();
}
/* On some mingw gcc versions, acmdln isn't used, so we need to hook a different part of crt init. */
_invalid_parameter_handler __wrap__set_invalid_parameter_handler(_invalid_parameter_handler handler) {
::ams::os::Initialize();
return __real__set_invalid_parameter_handler(handler);
}
}

View File

@@ -29,7 +29,7 @@ namespace ams::erpt::srv {
}
Context::Context(CategoryId cat, u32 max_records) : m_category(cat), m_max_record_count(max_records), m_record_count(0) {
Context::Context(CategoryId cat) : m_category(cat) {
g_category_list.push_front(*this);
}
@@ -38,12 +38,11 @@ namespace ams::erpt::srv {
}
Result Context::AddCategoryToReport(Report *report) {
R_SUCCEED_IF(m_record_list.empty());
for (auto it = m_record_list.begin(); it != m_record_list.end(); it++) {
for (u32 i = 0; i < it->m_ctx.field_count; i++) {
auto *field = std::addressof(it->m_ctx.fields[i]);
u8 *arr_buf = it->m_ctx.array_buffer;
if (m_record != nullptr) {
const auto *entry = m_record->GetContextEntryPtr();
for (u32 i = 0; i < entry->field_count; i++) {
auto *field = std::addressof(entry->fields[i]);
u8 *arr_buf = entry->array_buffer;
switch (field->type) {
case FieldType_Bool: R_TRY(Cipher::AddField(report, field->id, field->value_bool)); break;
@@ -70,46 +69,23 @@ namespace ams::erpt::srv {
R_SUCCEED();
}
Result Context::AddContextToCategory(const ContextEntry *entry, const u8 *data, u32 data_size) {
Result Context::SubmitContext(const ContextEntry *entry, const u8 *data, u32 data_size) {
auto record = std::make_unique<ContextRecord>();
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
R_TRY(record->Initialize(entry, data, data_size));
this->AddContextRecordToCategory(std::move(record));
R_SUCCEED();
}
Result Context::AddContextRecordToCategory(std::unique_ptr<ContextRecord> record) {
if (m_record_count < m_max_record_count) {
m_record_list.push_front(*record.release());
m_record_count++;
} else {
ContextRecord *back = std::addressof(m_record_list.back());
m_record_list.pop_back();
m_record_list.push_front(*record.release());
delete back;
}
R_SUCCEED();
}
Result Context::SubmitContext(const ContextEntry *entry, const u8 *data, u32 data_size) {
auto it = util::range::find_if(g_category_list, [&](const Context &cur) {
return cur.m_category == entry->category;
});
R_UNLESS(it != g_category_list.end(), erpt::ResultCategoryNotFound());
R_RETURN(it->AddContextToCategory(entry, data, data_size));
R_RETURN(SubmitContextRecord(std::move(record)));
}
Result Context::SubmitContextRecord(std::unique_ptr<ContextRecord> record) {
auto it = util::range::find_if(g_category_list, [&](const Context &cur) {
return cur.m_category == record->m_ctx.category;
return cur.m_category == record->GetContextEntryPtr()->category;
});
R_UNLESS(it != g_category_list.end(), erpt::ResultCategoryNotFound());
R_RETURN(it->AddContextRecordToCategory(std::move(record)));
it->m_record = std::move(record);
R_SUCCEED();
}
Result Context::WriteContextsToReport(Report *report) {

View File

@@ -26,16 +26,12 @@ namespace ams::erpt::srv {
class Context : public Allocator, public util::IntrusiveListBaseNode<Context> {
private:
const CategoryId m_category;
const u32 m_max_record_count;
u32 m_record_count;
util::IntrusiveListBaseTraits<ContextRecord>::ListType m_record_list;
std::unique_ptr<ContextRecord> m_record;
public:
Context(CategoryId cat, u32 max_records);
Context(CategoryId cat);
~Context();
Result AddCategoryToReport(Report *report);
Result AddContextToCategory(const ContextEntry *entry, const u8 *data, u32 data_size);
Result AddContextRecordToCategory(std::unique_ptr<ContextRecord> record);
public:
static Result SubmitContext(const ContextEntry *entry, const u8 *data, u32 data_size);
static Result SubmitContextRecord(std::unique_ptr<ContextRecord> record);

View File

@@ -21,8 +21,7 @@ namespace ams::erpt::srv {
class Context;
class ContextRecord : public Allocator, public util::IntrusiveListBaseNode<ContextRecord> {
friend class Context;
class ContextRecord : public Allocator {
private:
static u32 s_record_count;
public:

View File

@@ -106,7 +106,7 @@ namespace ams::erpt::srv {
g_sf_allocator.Attach(g_heap_handle);
for (auto i = 0; i < CategoryId_Count; i++) {
Context *ctx = new Context(static_cast<CategoryId>(i), 1);
Context *ctx = new Context(static_cast<CategoryId>(i));
AMS_ABORT_UNLESS(ctx != nullptr);
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define NX_SERVICE_ASSUME_NON_DOMAIN
#include "../service_guard.h"
#include "mitm_pm.os.horizon.h"
static Service g_amsMitmPmSrv;
NX_GENERATE_SERVICE_GUARD(amsMitmPm);
Result _amsMitmPmInitialize(void) {
return smGetService(&g_amsMitmPmSrv, "mitm:pm");
}
void _amsMitmPmCleanup(void) {
serviceClose(&g_amsMitmPmSrv);
}
Service *amsMitmPmGetServiceSession(void) {
return &g_amsMitmPmSrv;
}
Result amsMitmPmPrepareLaunchProgram(u64 *out, u64 program_id, const CfgOverrideStatus *status, bool is_application) {
const struct {
u8 is_application;
u64 program_id;
CfgOverrideStatus status;
} in = { is_application ? 1 : 0, program_id, *status };
return serviceDispatchInOut(&g_amsMitmPmSrv, 65000, in, *out);
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 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 <switch.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
u64 keys_down;
u64 flags;
} CfgOverrideStatus;
Result amsMitmPmInitialize(void);
void amsMitmPmExit(void);
Service *amsMitmPmGetServiceSession(void);
Result amsMitmPmPrepareLaunchProgram(u64 *out, u64 program_id, const CfgOverrideStatus *status, bool is_application);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "mitm_pm.os.horizon.h"
namespace ams::mitm::pm {
/* PM API. */
#if defined(ATMOSPHERE_OS_HORIZON)
void Initialize() {
R_ABORT_UNLESS(amsMitmPmInitialize());
}
void Finalize() {
amsMitmPmExit();
}
Result PrepareLaunchProgram(u64 *out, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application) {
static_assert(sizeof(status) == sizeof(CfgOverrideStatus), "CfgOverrideStatus definition!");
R_RETURN(amsMitmPmPrepareLaunchProgram(out, program_id.value, reinterpret_cast<const CfgOverrideStatus *>(std::addressof(status)), is_application));
}
#endif
}

View File

@@ -24,6 +24,7 @@ namespace ams::os::impl {
R_RETURN(impl::GetAslrSpaceManager().MapAtRandomAddress(out_address,
[](uintptr_t map_address, size_t map_size) -> Result {
R_TRY_CATCH(svc::MapInsecureMemory(map_address, map_size)) {
R_CONVERT(svc::ResultOutOfMemory, os::ResultOutOfMemory())
R_CONVERT(svc::ResultInvalidCurrentMemory, os::ResultInvalidCurrentMemoryState())
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::os::impl {
class UnsafeMemoryImpl {
public:
static Result AllocateUnsafeMemoryImpl(uintptr_t *out_address, size_t size);
static Result FreeUnsafeMemoryImpl(uintptr_t address, size_t size);
};
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "os_unsafe_memory_impl.hpp"
#include "os_aslr_space_manager.hpp"
namespace ams::os::impl {
Result UnsafeMemoryImpl::AllocateUnsafeMemoryImpl(uintptr_t *out_address, size_t size) {
/* Map at a random address. */
R_RETURN(impl::GetAslrSpaceManager().MapAtRandomAddress(out_address,
[](uintptr_t map_address, size_t map_size) -> Result {
R_TRY_CATCH(svc::MapPhysicalMemoryUnsafe(map_address, map_size)) {
R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfResource())
R_CONVERT(svc::ResultOutOfMemory, os::ResultOutOfMemory())
R_CONVERT(svc::ResultInvalidCurrentMemory, os::ResultInvalidCurrentMemoryState())
R_CONVERT(svc::ResultLimitReached, os::ResultOutOfMemory())
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
R_SUCCEED();
},
[](uintptr_t map_address, size_t map_size) -> void {
R_ABORT_UNLESS(UnsafeMemoryImpl::FreeUnsafeMemoryImpl(map_address, map_size));
},
size,
0
));
}
Result UnsafeMemoryImpl::FreeUnsafeMemoryImpl(uintptr_t address, size_t size) {
R_TRY_CATCH(svc::UnmapPhysicalMemoryUnsafe(address, size)) {
R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfResource())
R_CONVERT(svc::ResultOutOfMemory, os::ResultOutOfResource())
R_CONVERT(svc::ResultInvalidCurrentMemory, os::ResultBusy())
R_CONVERT(svc::ResultInvalidMemoryRegion, os::ResultInvalidParameter())
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
R_SUCCEED();
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "impl/os_unsafe_memory_impl.hpp"
namespace ams::os {
Result AllocateUnsafeMemory(uintptr_t *out_address, size_t size) {
/* Check arguments. */
AMS_ASSERT(size > 0);
AMS_ASSERT(util::IsAligned(size, os::MemoryPageSize));
/* Allocate memory. */
R_RETURN(impl::UnsafeMemoryImpl::AllocateUnsafeMemoryImpl(out_address, size));
}
Result FreeUnsafeMemory(uintptr_t address, size_t size) {
/* Check arguments. */
AMS_ASSERT(util::IsAligned(address, os::MemoryPageSize));
AMS_ASSERT(util::IsAligned(size, os::MemoryPageSize));
/* Free memory. */
R_RETURN(impl::UnsafeMemoryImpl::FreeUnsafeMemoryImpl(address, size));
}
}

View File

@@ -14,6 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "pm_ams.os.horizon.h"
namespace ams::pm::shell {

View File

@@ -48,7 +48,7 @@ namespace ams::sf::hipc {
AMS_ABORT_UNLESS(ServerManagerBase::CanAnyManageMitmServers());
/* Clone the forward service. */
std::shared_ptr<::Service> new_forward_service = std::move(ServerSession::CreateForwardService());
std::shared_ptr<::Service> new_forward_service = ServerSession::CreateForwardService();
R_ABORT_UNLESS(serviceClone(util::GetReference(m_session->m_forward_service).get(), new_forward_service.get()));
R_ABORT_UNLESS(tagged_manager->RegisterMitmSession(server_handle, std::move(clone), std::move(new_forward_service)));
}
@@ -168,7 +168,7 @@ namespace ams::sf::hipc {
R_ABORT_UNLESS(hipc::CreateSession(std::addressof(server_handle), std::addressof(client_handle)));
/* Register. */
std::shared_ptr<::Service> new_forward_service = std::move(ServerSession::CreateForwardService());
std::shared_ptr<::Service> new_forward_service = ServerSession::CreateForwardService();
serviceCreate(new_forward_service.get(), new_forward_target);
R_ABORT_UNLESS(m_manager->RegisterMitmSession(server_handle, std::move(object), std::move(new_forward_service)));

View File

@@ -17,10 +17,10 @@
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 1
#define ATMOSPHERE_RELEASE_VERSION_MINOR 5
#define ATMOSPHERE_RELEASE_VERSION_MICRO 0
#define ATMOSPHERE_RELEASE_VERSION_MICRO 4
#define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 16
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 0
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 0
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 3

View File

@@ -74,8 +74,11 @@
#define ATMOSPHERE_TARGET_FIRMWARE_15_0_0 ATMOSPHERE_TARGET_FIRMWARE(15, 0, 0)
#define ATMOSPHERE_TARGET_FIRMWARE_15_0_1 ATMOSPHERE_TARGET_FIRMWARE(15, 0, 1)
#define ATMOSPHERE_TARGET_FIRMWARE_16_0_0 ATMOSPHERE_TARGET_FIRMWARE(16, 0, 0)
#define ATMOSPHERE_TARGET_FIRMWARE_16_0_1 ATMOSPHERE_TARGET_FIRMWARE(16, 0, 1)
#define ATMOSPHERE_TARGET_FIRMWARE_16_0_2 ATMOSPHERE_TARGET_FIRMWARE(16, 0, 2)
#define ATMOSPHERE_TARGET_FIRMWARE_16_0_3 ATMOSPHERE_TARGET_FIRMWARE(16, 0, 3)
#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_16_0_0
#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_16_0_3
#define ATMOSPHERE_TARGET_FIRMWARE_MIN ATMOSPHERE_TARGET_FIRMWARE(0, 0, 0)
#define ATMOSPHERE_TARGET_FIRMWARE_MAX ATMOSPHERE_TARGET_FIRMWARE_CURRENT
@@ -142,6 +145,9 @@ namespace ams {
TargetFirmware_15_0_0 = ATMOSPHERE_TARGET_FIRMWARE_15_0_0,
TargetFirmware_15_0_1 = ATMOSPHERE_TARGET_FIRMWARE_15_0_1,
TargetFirmware_16_0_0 = ATMOSPHERE_TARGET_FIRMWARE_16_0_0,
TargetFirmware_16_0_1 = ATMOSPHERE_TARGET_FIRMWARE_16_0_1,
TargetFirmware_16_0_2 = ATMOSPHERE_TARGET_FIRMWARE_16_0_2,
TargetFirmware_16_0_3 = ATMOSPHERE_TARGET_FIRMWARE_16_0_3,
TargetFirmware_Current = ATMOSPHERE_TARGET_FIRMWARE_CURRENT,

View File

@@ -41,8 +41,9 @@ namespace ams::fs {
R_DEFINE_ERROR_RESULT(MountNameAlreadyExists, 60);
R_DEFINE_ERROR_RANGE(HandledBySystemProcess, 1000, 2999);
R_DEFINE_ERROR_RESULT(PartitionNotFound, 1001);
R_DEFINE_ERROR_RESULT(TargetNotFound, 1002);
R_DEFINE_ERROR_RESULT(PartitionNotFound, 1001);
R_DEFINE_ERROR_RESULT(TargetNotFound, 1002);
R_DEFINE_ERROR_RESULT(NcaExternalKeyNotFound, 1004);
R_DEFINE_ERROR_RANGE(SdCardAccessFailed, 2000, 2499);
R_DEFINE_ERROR_RESULT(SdCardNotPresent, 2001);

View File

@@ -457,10 +457,10 @@ namespace ams::result::impl {
#define AMS_CALL_ON_RESULT_ABORT_IMPL(cond, val) do { ::ams::diag::impl::FatalErrorByResultForNx(val); AMS_INFINITE_LOOP(); AMS_ASSUME(false); } while (false)
#elif defined(ATMOSPHERE_OS_HORIZON)
#define AMS_CALL_ON_RESULT_ASSERTION_IMPL(cond, val) AMS_CALL_ASSERT_FAIL_IMPL(::ams::diag::AssertionType_Assert, "ams::Result::IsSuccess()", "Failed: %s\n Module: %d\n Description: %d\n InnerValue: 0x%08" PRIX32, cond, val.GetModule(), val.GetDescription(), static_cast<::ams::Result>(val).GetInnerValue())
#define AMS_CALL_ON_RESULT_ABORT_IMPL(cond, val) AMS_CALL_ABORT_IMPL("ams::Result::IsSuccess()", "Failed: %s\n Module: %d\n Description: %d\n InnerValue: 0x%08" PRIX32, cond, val.GetModule(), val.GetDescription(), static_cast<::ams::Result>(val).GetInnerValue())
#define AMS_CALL_ON_RESULT_ABORT_IMPL(cond, val) AMS_CALL_ABORT_IMPL("ams::Result::IsSuccess()", "Failed: %s\n Module: %d\n Description: %d\n InnerValue: 0x%08" PRIX32, cond, static_cast<::ams::Result>(val).GetModule(), static_cast<::ams::Result>(val).GetDescription(), static_cast<::ams::Result>(val).GetInnerValue())
#else
#define AMS_CALL_ON_RESULT_ASSERTION_IMPL(cond, val) AMS_CALL_ASSERT_FAIL_IMPL(::ams::diag::AssertionType_Assert, "ams::Result::IsSuccess()", "Failed: %s\n Module: %d\n Description: %d\n InnerValue: 0x%08" PRIX32 "\n Name: %s", cond, val.GetModule(), val.GetDescription(), static_cast<::ams::Result>(val).GetInnerValue(), ::ams::GetResultName(val))
#define AMS_CALL_ON_RESULT_ABORT_IMPL(cond, val) AMS_CALL_ABORT_IMPL("ams::Result::IsSuccess()", "Failed: %s\n Module: %d\n Description: %d\n InnerValue: 0x%08" PRIX32 "\n Name: %s", cond, val.GetModule(), val.GetDescription(), static_cast<::ams::Result>(val).GetInnerValue(), ::ams::GetResultName(val))
#define AMS_CALL_ON_RESULT_ASSERTION_IMPL(cond, val) AMS_CALL_ASSERT_FAIL_IMPL(::ams::diag::AssertionType_Assert, "ams::Result::IsSuccess()", "Failed: %s\n Module: %d\n Description: %d\n InnerValue: 0x%08" PRIX32 "\n Name: %s", cond, val.GetModule(), val.GetDescription(), static_cast<::ams::Result>(val).GetInnerValue(), ::ams::GetResultName(static_cast<::ams::Result>(val)))
#define AMS_CALL_ON_RESULT_ABORT_IMPL(cond, val) AMS_CALL_ABORT_IMPL("ams::Result::IsSuccess()", "Failed: %s\n Module: %d\n Description: %d\n InnerValue: 0x%08" PRIX32 "\n Name: %s", cond, static_cast<::ams::Result>(val).GetModule(), static_cast<::ams::Result>(val).GetDescription(), static_cast<::ams::Result>(val).GetInnerValue(), ::ams::GetResultName(static_cast<::ams::Result>(val)))
#endif
/// Evaluates an expression that returns a result, and asserts the result if it would fail.

View File

@@ -27,11 +27,11 @@ namespace ams::svc {
constexpr inline size_t AddressMemoryRegionSmall36Size = 2_GB;
constexpr inline size_t AddressMemoryRegionLarge36Size = 64_GB - AddressMemoryRegionSmall36Size;
constexpr inline size_t AddressMemoryRegionHeap36Size = 6_GB;
constexpr inline size_t AddressMemoryRegionHeap36Size = 8_GB;
constexpr inline size_t AddressMemoryRegionAlias36Size = 6_GB;
constexpr inline size_t AddressMemoryRegionSmall39Size = 64_GB;
constexpr inline size_t AddressMemoryRegionHeap39Size = 6_GB;
constexpr inline size_t AddressMemoryRegionHeap39Size = 8_GB;
constexpr inline size_t AddressMemoryRegionAlias39Size = 64_GB;
constexpr inline size_t AddressMemoryRegionStack39Size = 2_GB;

View File

@@ -144,10 +144,6 @@ namespace ams::util {
return m_node == rhs.m_node;
}
constexpr ALWAYS_INLINE bool operator!=(const Iterator &rhs) const {
return !(*this == rhs);
}
constexpr ALWAYS_INLINE pointer operator->() const {
return m_node;
}
@@ -355,10 +351,6 @@ namespace ams::util {
return m_iterator == rhs.m_iterator;
}
constexpr ALWAYS_INLINE bool operator!=(const Iterator &rhs) const {
return !(*this == rhs);
}
constexpr ALWAYS_INLINE pointer operator->() const {
return std::addressof(Traits::GetParent(*m_iterator));
}

View File

@@ -94,10 +94,6 @@ namespace ams::util {
return m_node == rhs.m_node;
}
constexpr ALWAYS_INLINE bool operator!=(const Iterator &rhs) const {
return !(*this == rhs);
}
constexpr ALWAYS_INLINE pointer operator->() const {
return m_node;
}
@@ -304,10 +300,6 @@ namespace ams::util {
return m_impl == rhs.m_impl;
}
constexpr ALWAYS_INLINE bool operator!=(const Iterator &rhs) const {
return !(*this == rhs);
}
constexpr ALWAYS_INLINE pointer operator->() const {
return Traits::GetParent(std::addressof(*m_impl));
}

View File

@@ -29,7 +29,6 @@ _ZN3ams4kern10KScheduler12ScheduleImplEv:
mov x1, x0
/* First, clear the need's scheduling bool (and dmb ish after, as it's an atomic). */
/* TODO: Should this be a stlrb? Nintendo does not do one. */
strb wzr, [x1]
dmb ish

View File

@@ -65,6 +65,8 @@
"svcReplyAndReceive": "0x43",
"svcReplyAndReceiveWithUserBuffer": "0x44",
"svcCreateEvent": "0x45",
"svcMapPhysicalMemoryUnsafe": "0x48",
"svcUnmapPhysicalMemoryUnsafe": "0x49",
"svcMapTransferMemory": "0x51",
"svcUnmapTransferMemory": "0x52",
"svcCreateInterruptEvent": "0x53",

View File

@@ -24,6 +24,7 @@
#include "ns_mitm/nsmitm_module.hpp"
#include "dns_mitm/dnsmitm_module.hpp"
#include "sysupdater/sysupdater_module.hpp"
#include "mitm_pm/mitm_pm_module.hpp"
namespace ams::mitm {
@@ -37,6 +38,7 @@ namespace ams::mitm {
ModuleId_NsMitm,
ModuleId_DnsMitm,
ModuleId_Sysupdater,
ModuleId_PmService,
ModuleId_Count,
};
@@ -70,6 +72,7 @@ namespace ams::mitm {
GetModuleDefinition<ns::MitmModule>(),
GetModuleDefinition<socket::resolver::MitmModule>(),
GetModuleDefinition<sysupdater::MitmModule>(),
GetModuleDefinition<pm::MitmModule>(),
};
}

View File

@@ -71,7 +71,7 @@ namespace ams::mitm::fs {
Result OpenHblWebContentFileSystem(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> &out, ncm::ProgramId program_id) {
/* Verify eligibility. */
bool is_hbl;
R_UNLESS(R_SUCCEEDED(pm::info::IsHblProgramId(std::addressof(is_hbl), program_id)), sm::mitm::ResultShouldForwardToSession());
R_UNLESS(R_SUCCEEDED(ams::pm::info::IsHblProgramId(std::addressof(is_hbl), program_id)), sm::mitm::ResultShouldForwardToSession());
R_UNLESS(is_hbl, sm::mitm::ResultShouldForwardToSession());
/* Hbl html directory must exist. */

View File

@@ -54,7 +54,8 @@ namespace ams::mitm::fs {
}
/* We want to mitm sdb, to support sd-romfs redirection of common system archives (like system font, etc). */
if (program_id == ncm::SystemProgramId::Sdb) {
/* NOTE: In 16.0.0+, this was moved to glue. */
if (program_id == ncm::SystemProgramId::Sdb || program_id == ncm::SystemProgramId::Glue) {
return true;
}

View File

@@ -78,6 +78,7 @@ namespace ams::mitm::fs {
constinit os::SdkRecursiveMutex g_storage_set_mutex;
constinit LayeredRomfsStorageSet g_storage_set;
constinit os::SdkMutex g_initialization_mutex;
void OpenReference(LayeredRomfsStorageImpl *impl) {
std::scoped_lock lk(g_storage_set_mutex);
@@ -106,6 +107,8 @@ namespace ams::mitm::fs {
auto *impl = reinterpret_cast<LayeredRomfsStorageImpl *>(storage_uptr);
g_ack_mq.Send(storage_uptr);
std::scoped_lock lk(g_initialization_mutex);
impl->InitializeImpl();
/* Close the initial reference. */
@@ -255,6 +258,21 @@ namespace ams::mitm::fs {
return std::make_shared<LayeredRomfsStorage>(impl);
}
void FinalizeLayeredRomfsStorage(ncm::ProgramId program_id) {
std::scoped_lock lk(g_initialization_mutex);
std::scoped_lock lk2(g_storage_set_mutex);
/* Find an existing storage. */
if (auto it = g_storage_set.find_key(program_id.value); it != g_storage_set.end()) {
/* We need to delete the process romfs. Require invariant that it is unreferenced, by this point. */
AMS_ABORT_UNLESS(it->GetReferenceCount() == 0);
auto *holder = std::addressof(*it);
it = g_storage_set.erase(it);
delete holder;
}
}
LayeredRomfsStorageImpl::LayeredRomfsStorageImpl(std::unique_ptr<IStorage> s_r, std::unique_ptr<IStorage> f_r, ncm::ProgramId pr_id) : m_storage_romfs(std::move(s_r)), m_file_romfs(std::move(f_r)), m_initialize_event(os::EventClearMode_ManualClear), m_program_id(std::move(pr_id)), m_is_initialized(false), m_started_initialize(false) {
/* ... */
}

View File

@@ -22,7 +22,7 @@ namespace ams::mitm::fs {
class LayeredRomfsStorageImpl {
private:
std::vector<romfs::SourceInfo> m_source_infos;
romfs::Builder::SourceInfoVector m_source_infos;
std::unique_ptr<ams::fs::IStorage> m_storage_romfs;
std::unique_ptr<ams::fs::IStorage> m_file_romfs;
os::Event m_initialize_event;
@@ -51,4 +51,6 @@ namespace ams::mitm::fs {
std::shared_ptr<ams::fs::IStorage> GetLayeredRomfsStorage(ncm::ProgramId program_id, ::FsStorage &data_storage, bool is_process_romfs);
void FinalizeLayeredRomfsStorage(ncm::ProgramId program_id);
}

View File

@@ -16,6 +16,7 @@
#include <stratosphere.hpp>
#include "../amsmitm_fs_utils.hpp"
#include "fsmitm_romfs.hpp"
#include "fsmitm_layered_romfs_storage.hpp"
namespace ams::mitm::fs {
@@ -23,6 +24,206 @@ namespace ams::mitm::fs {
namespace romfs {
namespace {
struct ApplicationWithDynamicHeapInfo {
ncm::ProgramId program_id;
size_t dynamic_app_heap_size;
size_t dynamic_system_heap_size;
};
constexpr const ApplicationWithDynamicHeapInfo ApplicationsWithDynamicHeap[] = {
/* Animal Crossing: New Horizons. */
/* Requirement ~24 MB. */
/* No particular heap sensitivity. */
{ 0x01006F8002326000, 16_MB, 0_MB },
/* Fire Emblem: Engage. */
/* Requirement ~32+ MB. */
/* No particular heap sensitivity. */
{ 0x0100A6301214E000, 16_MB, 0_MB },
/* The Legend of Zelda: Tears of the Kingdom. */
/* Requirement ~48 MB. */
/* Game is highly sensitive to memory stolen from application heap. */
/* 1.0.0 tolerates no more than 16 MB stolen. 1.1.0 no more than 12 MB. */
{ 0x0100F2C0115B6000, 10_MB, 8_MB },
};
constexpr size_t GetDynamicAppHeapSize(ncm::ProgramId program_id) {
for (const auto &info : ApplicationsWithDynamicHeap) {
if (info.program_id == program_id) {
return info.dynamic_app_heap_size;
}
}
return 0;
}
constexpr size_t GetDynamicSysHeapSize(ncm::ProgramId program_id) {
for (const auto &info : ApplicationsWithDynamicHeap) {
if (info.program_id == program_id) {
return info.dynamic_system_heap_size;
}
}
return 0;
}
template<auto MapImpl, auto UnmapImpl>
struct DynamicHeap {
uintptr_t heap_address{};
size_t heap_size{};
size_t outstanding_allocations{};
util::TypedStorage<mem::StandardAllocator> heap{};
os::SdkMutex release_heap_lock{};
constexpr DynamicHeap() = default;
void Map() {
if (this->heap_address == 0) {
/* NOTE: Lock not necessary, because this is the only location which do 0 -> non-zero. */
R_ABORT_UNLESS(MapImpl(std::addressof(this->heap_address), this->heap_size));
AMS_ABORT_UNLESS(this->heap_address != 0);
/* Create heap. */
util::ConstructAt(this->heap, reinterpret_cast<void *>(this->heap_address), this->heap_size);
}
}
void TryRelease() {
if (this->outstanding_allocations == 0) {
std::scoped_lock lk(this->release_heap_lock);
if (this->heap_address != 0) {
util::DestroyAt(this->heap);
this->heap = {};
R_ABORT_UNLESS(UnmapImpl(this->heap_address, this->heap_size));
this->heap_address = 0;
}
}
}
void *Allocate(size_t size) {
void * const ret = util::GetReference(this->heap).Allocate(size);
if (AMS_LIKELY(ret != nullptr)) {
++this->outstanding_allocations;
}
return ret;
}
bool TryFree(void *p) {
if (this->IsAllocated(p)) {
--this->outstanding_allocations;
util::GetReference(this->heap).Free(p);
return true;
} else {
return false;
}
}
bool IsAllocated(void *p) const {
const uintptr_t address = reinterpret_cast<uintptr_t>(p);
return this->heap_address != 0 && (this->heap_address <= address && address < this->heap_address + this->heap_size);
}
void Reset() {
/* This should require no remaining allocations. */
AMS_ABORT_UNLESS(this->outstanding_allocations == 0);
/* Free the heap. */
this->TryRelease();
AMS_ABORT_UNLESS(this->heap_address == 0);
/* Clear the heap size. */
this->heap_size = 0;
}
};
Result MapByHeap(uintptr_t *out, size_t size) {
R_TRY(os::SetMemoryHeapSize(size));
R_RETURN(os::AllocateMemoryBlock(out, size));
}
Result UnmapByHeap(uintptr_t address, size_t size) {
os::FreeMemoryBlock(address, size);
R_RETURN(os::SetMemoryHeapSize(0));
}
/* Dynamic allocation globals. */
constinit os::SdkMutex g_romfs_build_lock;
constinit ncm::ProgramId g_dynamic_heap_program_id{};
constinit bool g_building_from_dynamic_heap = false;
constinit DynamicHeap<os::AllocateUnsafeMemory, os::FreeUnsafeMemory> g_dynamic_app_heap;
constinit DynamicHeap<MapByHeap, UnmapByHeap> g_dynamic_sys_heap;
void InitializeDynamicHeapForBuildRomfs(ncm::ProgramId program_id) {
if (program_id == g_dynamic_heap_program_id && g_dynamic_app_heap.heap_size > 0) {
/* This romfs will build out of dynamic heap. */
g_building_from_dynamic_heap = true;
g_dynamic_app_heap.Map();
if (g_dynamic_sys_heap.heap_size > 0) {
g_dynamic_sys_heap.Map();
}
}
}
void FinalizeDynamicHeapForBuildRomfs() {
/* We are definitely no longer building out of dynamic heap. */
g_building_from_dynamic_heap = false;
g_dynamic_app_heap.TryRelease();
}
}
void *AllocateTracked(AllocationType type, size_t size) {
AMS_UNUSED(type);
if (g_building_from_dynamic_heap) {
void *ret = g_dynamic_app_heap.Allocate(size);
if (ret == nullptr && g_dynamic_sys_heap.heap_address != 0) {
ret = g_dynamic_sys_heap.Allocate(size);
}
if (ret == nullptr) {
ret = std::malloc(size);
}
return ret;
} else {
return std::malloc(size);
}
}
void FreeTracked(AllocationType type, void *p, size_t size) {
AMS_UNUSED(type);
AMS_UNUSED(size);
if (g_dynamic_app_heap.TryFree(p)) {
if (!g_building_from_dynamic_heap) {
g_dynamic_app_heap.TryRelease();
}
} else if (g_dynamic_sys_heap.TryFree(p)) {
if (!g_building_from_dynamic_heap) {
g_dynamic_sys_heap.TryRelease();
}
} else {
std::free(p);
}
}
namespace {
constexpr u32 EmptyEntry = 0xFFFFFFFF;
@@ -71,22 +272,23 @@ namespace ams::mitm::fs {
static constexpr size_t MaxCachedSize = (1_MB / 4);
private:
size_t m_cache_bitsize;
size_t m_cache_size;
protected:
void *m_cache;
protected:
DynamicTableCache(size_t sz) {
size_t cache_size = util::CeilingPowerOfTwo(std::min(sz, MaxCachedSize));
m_cache = std::malloc(cache_size);
m_cache_size = util::CeilingPowerOfTwo(std::min(sz, MaxCachedSize));
m_cache = AllocateTracked(AllocationType_TableCache, m_cache_size);
while (m_cache == nullptr) {
cache_size >>= 1;
AMS_ABORT_UNLESS(cache_size >= 16_KB);
m_cache = std::malloc(cache_size);
m_cache_size >>= 1;
AMS_ABORT_UNLESS(m_cache_size >= 16_KB);
m_cache = AllocateTracked(AllocationType_TableCache, m_cache_size);
}
m_cache_bitsize = util::CountTrailingZeros(cache_size);
m_cache_bitsize = util::CountTrailingZeros(m_cache_size);
}
~DynamicTableCache() {
std::free(m_cache);
FreeTracked(AllocationType_TableCache, m_cache, m_cache_size);
}
ALWAYS_INLINE size_t GetCacheSize() const { return static_cast<size_t>(1) << m_cache_bitsize; }
@@ -113,21 +315,33 @@ namespace ams::mitm::fs {
size_t m_cache_idx;
u8 m_fallback_cache[FallbackCacheSize];
private:
ALWAYS_INLINE void Read(size_t ofs, void *dst, size_t size) {
R_ABORT_UNLESS(m_storage->Read(m_offset + ofs, dst, size));
ALWAYS_INLINE bool Read(size_t ofs, void *dst, size_t size) {
R_TRY_CATCH(m_storage->Read(m_offset + ofs, dst, size)) {
R_CATCH(fs::ResultNcaExternalKeyNotFound) { return false; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
return true;
}
ALWAYS_INLINE void ReloadCacheImpl(size_t idx) {
ALWAYS_INLINE bool ReloadCacheImpl(size_t idx) {
const size_t rel_ofs = idx * this->GetCacheSize();
AMS_ABORT_UNLESS(rel_ofs < m_size);
const size_t new_cache_size = std::min(m_size - rel_ofs, this->GetCacheSize());
this->Read(rel_ofs, m_cache, new_cache_size);
if (!this->Read(rel_ofs, m_cache, new_cache_size)) {
return false;
}
m_cache_idx = idx;
return true;
}
ALWAYS_INLINE void ReloadCache(size_t idx) {
ALWAYS_INLINE bool ReloadCache(size_t idx) {
if (m_cache_idx != idx) {
this->ReloadCacheImpl(idx);
if (!this->ReloadCacheImpl(idx)) {
return false;
}
}
return true;
}
ALWAYS_INLINE size_t GetCacheIndex(u32 ofs) {
@@ -140,13 +354,18 @@ namespace ams::mitm::fs {
}
const Entry *GetEntry(u32 entry_offset) {
this->ReloadCache(this->GetCacheIndex(entry_offset));
if (!this->ReloadCache(this->GetCacheIndex(entry_offset))) {
return nullptr;
}
const size_t ofs = entry_offset % this->GetCacheSize();
const Entry *entry = reinterpret_cast<const Entry *>(reinterpret_cast<uintptr_t>(m_cache) + ofs);
if (AMS_UNLIKELY(this->GetCacheIndex(entry_offset) != this->GetCacheIndex(entry_offset + sizeof(Entry) + entry->name_size + sizeof(u32)))) {
this->Read(entry_offset, m_fallback_cache, std::min(m_size - entry_offset, FallbackCacheSize));
if (!this->Read(entry_offset, m_fallback_cache, std::min(m_size - entry_offset, FallbackCacheSize))) {
return nullptr;
}
entry = reinterpret_cast<const Entry *>(m_fallback_cache);
}
return entry;
@@ -251,12 +470,11 @@ namespace ams::mitm::fs {
using DirectoryTableWriter = TableWriter<DirectoryEntry>;
using FileTableWriter = TableWriter<FileEntry>;
constexpr inline u32 CalculatePathHash(u32 parent, const char *_path, u32 start, size_t path_len) {
const unsigned char *path = reinterpret_cast<const unsigned char *>(_path);
constexpr inline u32 CalculatePathHash(u32 parent, const char *path, u32 start, size_t path_len) {
u32 hash = parent ^ 123456789;
for (size_t i = 0; i < path_len; i++) {
hash = (hash >> 5) | (hash << 27);
hash ^= path[start + i];
hash ^= static_cast<unsigned char>(path[start + i]);
}
return hash;
}
@@ -294,13 +512,28 @@ namespace ams::mitm::fs {
}
Builder::Builder(ncm::ProgramId pr_id) : m_program_id(pr_id), m_num_dirs(0), m_num_files(0), m_dir_table_size(0), m_file_table_size(0), m_dir_hash_table_size(0), m_file_hash_table_size(0), m_file_partition_size(0) {
auto res = m_directories.emplace(std::make_unique<BuildDirectoryContext>(BuildDirectoryContext::RootTag{}));
/* Ensure only one romfs is built at any time. */
g_romfs_build_lock.Lock();
/* If we should be using dynamic heap, turn it on. */
InitializeDynamicHeapForBuildRomfs(m_program_id);
auto res = m_directories.emplace(std::unique_ptr<BuildDirectoryContext>(AllocateTyped<BuildDirectoryContext>(AllocationType_BuildDirContext, BuildDirectoryContext::RootTag{})));
AMS_ABORT_UNLESS(res.second);
m_root = res.first->get();
m_num_dirs = 1;
m_dir_table_size = 0x18;
}
Builder::~Builder() {
/* If we have nothing remaining in dynamic heap, release it. */
FinalizeDynamicHeapForBuildRomfs();
/* Release the romfs build lock. */
g_romfs_build_lock.Unlock();
}
void Builder::AddDirectory(BuildDirectoryContext **out, BuildDirectoryContext *parent_ctx, std::unique_ptr<BuildDirectoryContext> child_ctx) {
/* Set parent context member. */
child_ctx->parent = parent_ctx;
@@ -348,9 +581,9 @@ namespace ams::mitm::fs {
AMS_ABORT_UNLESS(num_child_dirs >= 0);
{
BuildDirectoryContext **child_dirs = num_child_dirs != 0 ? reinterpret_cast<BuildDirectoryContext **>(std::malloc(sizeof(BuildDirectoryContext *) * num_child_dirs)) : nullptr;
BuildDirectoryContext **child_dirs = num_child_dirs != 0 ? reinterpret_cast<BuildDirectoryContext **>(AllocateTracked(AllocationType_DirPointerArray, sizeof(BuildDirectoryContext *) * num_child_dirs)) : nullptr;
AMS_ABORT_UNLESS(num_child_dirs == 0 || child_dirs != nullptr);
ON_SCOPE_EXIT { std::free(child_dirs); };
ON_SCOPE_EXIT { if (child_dirs != nullptr) { FreeTracked(AllocationType_DirPointerArray, child_dirs, sizeof(BuildDirectoryContext *) * num_child_dirs); } };
s64 cur_child_dir_ind = 0;
{
@@ -369,12 +602,12 @@ namespace ams::mitm::fs {
AMS_ABORT_UNLESS(child_dirs != nullptr);
BuildDirectoryContext *real_child = nullptr;
this->AddDirectory(std::addressof(real_child), parent, std::make_unique<BuildDirectoryContext>(m_dir_entry.name, strlen(m_dir_entry.name)));
this->AddDirectory(std::addressof(real_child), parent, std::unique_ptr<BuildDirectoryContext>(AllocateTyped<BuildDirectoryContext>(AllocationType_BuildDirContext, m_dir_entry.name, strlen(m_dir_entry.name))));
AMS_ABORT_UNLESS(real_child != nullptr);
child_dirs[cur_child_dir_ind++] = real_child;
AMS_ABORT_UNLESS(cur_child_dir_ind <= num_child_dirs);
} else /* if (m_dir_entry.type == FsDirEntryType_File) */ {
this->AddFile(parent, std::make_unique<BuildFileContext>(m_dir_entry.name, strlen(m_dir_entry.name), m_dir_entry.file_size, 0, m_cur_source_type));
this->AddFile(parent, std::unique_ptr<BuildFileContext>(AllocateTyped<BuildFileContext>(AllocationType_BuildFileContext, m_dir_entry.name, strlen(m_dir_entry.name), m_dir_entry.file_size, 0, m_cur_source_type)));
}
}
}
@@ -399,12 +632,18 @@ namespace ams::mitm::fs {
void Builder::VisitDirectory(BuildDirectoryContext *parent, u32 parent_offset, DirectoryTableReader &dir_table, FileTableReader &file_table) {
const DirectoryEntry *parent_entry = dir_table.GetEntry(parent_offset);
if (AMS_UNLIKELY(parent_entry == nullptr)) {
return;
}
u32 cur_file_offset = parent_entry->file;
while (cur_file_offset != EmptyEntry) {
const FileEntry *cur_file = file_table.GetEntry(cur_file_offset);
if (AMS_UNLIKELY(cur_file == nullptr)) {
return;
}
this->AddFile(parent, std::make_unique<BuildFileContext>(cur_file->name, cur_file->name_size, cur_file->size, cur_file->offset, m_cur_source_type));
this->AddFile(parent, std::unique_ptr<BuildFileContext>(AllocateTyped<BuildFileContext>(AllocationType_BuildFileContext, cur_file->name, cur_file->name_size, cur_file->size, cur_file->offset, m_cur_source_type)));
cur_file_offset = cur_file->sibling;
}
@@ -415,8 +654,11 @@ namespace ams::mitm::fs {
u32 next_child_offset = 0;
{
const DirectoryEntry *cur_child = dir_table.GetEntry(cur_child_offset);
if (AMS_UNLIKELY(cur_child == nullptr)) {
return;
}
this->AddDirectory(std::addressof(real_child), parent, std::make_unique<BuildDirectoryContext>(cur_child->name, cur_child->name_size));
this->AddDirectory(std::addressof(real_child), parent, std::unique_ptr<BuildDirectoryContext>(AllocateTyped<BuildDirectoryContext>(AllocationType_BuildDirContext, cur_child->name, cur_child->name_size)));
AMS_ABORT_UNLESS(real_child != nullptr);
next_child_offset = cur_child->sibling;
@@ -439,7 +681,7 @@ namespace ams::mitm::fs {
/* If there is no romfs folder on the SD, don't bother continuing. */
{
FsDir dir;
if (R_FAILED(mitm::fs::OpenAtmosphereRomfsDirectory(std::addressof(dir), m_program_id, m_root->path.get(), OpenDirectoryMode_Directory, std::addressof(sd_filesystem)))) {
if (R_FAILED(mitm::fs::OpenAtmosphereRomfsDirectory(std::addressof(dir), m_program_id, m_root->path, OpenDirectoryMode_Directory, std::addressof(sd_filesystem)))) {
return;
}
fsDirClose(std::addressof(dir));
@@ -462,7 +704,7 @@ namespace ams::mitm::fs {
this->VisitDirectory(m_root, 0x0, dir_table, file_table);
}
void Builder::Build(std::vector<SourceInfo> *out_infos) {
void Builder::Build(SourceInfoVector *out_infos) {
/* Clear output. */
out_infos->clear();
@@ -478,7 +720,7 @@ namespace ams::mitm::fs {
m_file_hash_table_size = sizeof(u32) * num_file_hash_table_entries;
/* Allocate metadata, make pointers. */
Header *header = reinterpret_cast<Header *>(std::malloc(sizeof(Header)));
Header *header = reinterpret_cast<Header *>(AllocateTracked(AllocationType_Memory, sizeof(Header)));
std::memset(header, 0x00, sizeof(*header));
/* Open metadata file. */
@@ -553,13 +795,13 @@ namespace ams::mitm::fs {
/* Set all files' hash value = hash index. */
for (const auto &it : m_files) {
BuildFileContext *cur_file = it.get();
cur_file->hash_value = CalculatePathHash(cur_file->parent->entry_offset, cur_file->path.get(), 0, cur_file->path_len) % num_file_hash_table_entries;
cur_file->hash_value = CalculatePathHash(cur_file->parent->entry_offset, cur_file->path, 0, cur_file->path_len) % num_file_hash_table_entries;
}
/* Set all directories' hash value = hash index. */
for (const auto &it : m_directories) {
BuildDirectoryContext *cur_dir = it.get();
cur_dir->hash_value = CalculatePathHash(cur_dir == m_root ? 0 : cur_dir->parent->entry_offset, cur_dir->path.get(), 0, cur_dir->path_len) % num_dir_hash_table_entries;
cur_dir->hash_value = CalculatePathHash(cur_dir == m_root ? 0 : cur_dir->parent->entry_offset, cur_dir->path, 0, cur_dir->path_len) % num_dir_hash_table_entries;
}
/* Write hash tables. */
@@ -662,7 +904,7 @@ namespace ams::mitm::fs {
const u32 name_size = cur_file->path_len;
cur_entry->name_size = name_size;
if (name_size) {
std::memcpy(cur_entry->name, cur_file->path.get(), name_size);
std::memcpy(cur_entry->name, cur_file->path, name_size);
for (size_t i = name_size; i < util::AlignUp(name_size, 4); i++) {
cur_entry->name[i] = 0;
}
@@ -689,9 +931,10 @@ namespace ams::mitm::fs {
AMS_ABORT_UNLESS(path_needed_size <= sizeof(full_path));
cur_file->GetPath(full_path);
cur_file->path.reset();
FreeTracked(AllocationType_FileName, cur_file->path, cur_file->path_len + 1);
cur_file->path = nullptr;
char *new_path = new char[path_needed_size];
char *new_path = static_cast<char *>(AllocateTracked(AllocationType_FullPath, path_needed_size));
std::memcpy(new_path, full_path, path_needed_size);
out_infos->emplace_back(cur_file->offset + FilePartitionOffset, cur_file->size, cur_file->source_type, new_path);
}
@@ -720,7 +963,7 @@ namespace ams::mitm::fs {
const u32 name_size = cur_dir->path_len;
cur_entry->name_size = name_size;
if (name_size) {
std::memcpy(cur_entry->name, cur_dir->path.get(), name_size);
std::memcpy(cur_entry->name, cur_dir->path, name_size);
for (size_t i = name_size; i < util::AlignUp(name_size, 4); i++) {
cur_entry->name[i] = 0;
}
@@ -752,6 +995,39 @@ namespace ams::mitm::fs {
}
}
Result ConfigureDynamicHeap(u64 *out_size, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application) {
/* Baseline: use no dynamic heap. */
*out_size = 0;
/* If the process is not an application, we do not care about dynamic heap. */
R_SUCCEED_IF(!is_application);
/* First, we need to ensure that, if the game used dynamic heap, we clear it. */
if (g_dynamic_app_heap.heap_size > 0) {
mitm::fs::FinalizeLayeredRomfsStorage(g_dynamic_heap_program_id);
/* Free the heap. */
g_dynamic_app_heap.Reset();
g_dynamic_sys_heap.Reset();
}
/* Next, if we aren't going to end up building a romfs, we can ignore dynamic heap. */
R_SUCCEED_IF(!status.IsProgramSpecific());
/* Only mitm if there is actually an override romfs. */
R_SUCCEED_IF(!mitm::fs::HasSdRomfsContent(program_id));
/* Next, set the new program id for dynamic heap. */
g_dynamic_heap_program_id = program_id;
g_dynamic_app_heap.heap_size = GetDynamicAppHeapSize(g_dynamic_heap_program_id);
g_dynamic_sys_heap.heap_size = GetDynamicSysHeapSize(g_dynamic_heap_program_id);
/* Set output. */
*out_size = g_dynamic_app_heap.heap_size;
R_SUCCEED();
}
}
}

View File

@@ -27,6 +27,52 @@ namespace ams::mitm::fs::romfs {
Memory,
};
enum AllocationType {
AllocationType_FileName,
AllocationType_DirName,
AllocationType_FullPath,
AllocationType_SourceInfo,
AllocationType_BuildFileContext,
AllocationType_BuildDirContext,
AllocationType_TableCache,
AllocationType_DirPointerArray,
AllocationType_DirContextSet,
AllocationType_FileContextSet,
AllocationType_Memory,
AllocationType_Count,
};
void *AllocateTracked(AllocationType type, size_t size);
void FreeTracked(AllocationType type, void *p, size_t size);
template<typename T, typename... Args>
T *AllocateTyped(AllocationType type, Args &&... args) {
void *mem = AllocateTracked(type, sizeof(T));
return std::construct_at(static_cast<T *>(mem), std::forward<Args>(args)...);
}
template<AllocationType AllocType, typename T>
class TrackedAllocator {
public:
using value_type = T;
template<typename U>
struct rebind {
using other = TrackedAllocator<AllocType, U>;
};
public:
TrackedAllocator() = default;
T *allocate(size_t n) {
return static_cast<T *>(AllocateTracked(AllocType, sizeof(T) * n));
}
void deallocate(T *p, size_t n) {
FreeTracked(AllocType, p, sizeof(T) * n);
}
};
struct SourceInfo {
s64 virtual_offset;
s64 size;
@@ -89,10 +135,10 @@ namespace ams::mitm::fs::romfs {
delete this->metadata_source_info.file;
break;
case DataSourceType::LooseSdFile:
delete[] this->loose_source_info.path;
FreeTracked(AllocationType_FullPath, this->loose_source_info.path, std::strlen(this->loose_source_info.path) + 1);
break;
case DataSourceType::Memory:
std::free(static_cast<void *>(this->memory_source_info.data));
FreeTracked(AllocationType_Memory, this->memory_source_info.data, this->size);
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
@@ -113,7 +159,7 @@ namespace ams::mitm::fs::romfs {
NON_COPYABLE(BuildDirectoryContext);
NON_MOVEABLE(BuildDirectoryContext);
std::unique_ptr<char[]> path;
char *path;
union {
BuildDirectoryContext *parent;
};
@@ -139,16 +185,28 @@ namespace ams::mitm::fs::romfs {
struct RootTag{};
BuildDirectoryContext(RootTag) : parent(nullptr), child(nullptr), sibling(nullptr), file(nullptr), path_len(0), entry_offset(0), hash_value(0xFFFFFFFF) {
this->path = std::make_unique<char[]>(1);
this->path = static_cast<char *>(AllocateTracked(AllocationType_DirName, 1));
this->path[0] = '\x00';
}
BuildDirectoryContext(const char *entry_name, size_t entry_name_len) : parent(nullptr), child(nullptr), sibling(nullptr), file(nullptr), entry_offset(0) {
this->path_len = entry_name_len;
this->path = std::unique_ptr<char[]>(new char[this->path_len + 1]);
std::memcpy(this->path.get(), entry_name, entry_name_len);
this->path = static_cast<char *>(AllocateTracked(AllocationType_DirName, this->path_len + 1));
std::memcpy(this->path, entry_name, entry_name_len);
this->path[this->path_len] = '\x00';
}
~BuildDirectoryContext() {
if (this->path != nullptr) {
FreeTracked(AllocationType_DirName, this->path, this->path_len + 1);
this->path = nullptr;
}
}
void operator delete(void *p) {
FreeTracked(AllocationType_BuildDirContext, p, sizeof(BuildDirectoryContext));
}
size_t GetPathLength() const {
if (this->parent == nullptr) {
return 0;
@@ -165,7 +223,7 @@ namespace ams::mitm::fs::romfs {
const size_t parent_len = this->parent->GetPath(dst);
dst[parent_len] = '/';
std::memcpy(dst + parent_len + 1, this->path.get(), this->path_len);
std::memcpy(dst + parent_len + 1, this->path, this->path_len);
dst[parent_len + 1 + this->path_len] = '\x00';
return parent_len + 1 + this->path_len;
}
@@ -187,7 +245,7 @@ namespace ams::mitm::fs::romfs {
NON_COPYABLE(BuildFileContext);
NON_MOVEABLE(BuildFileContext);
std::unique_ptr<char[]> path;
char *path;
BuildDirectoryContext *parent;
union {
BuildFileContext *sibling;
@@ -203,11 +261,22 @@ namespace ams::mitm::fs::romfs {
BuildFileContext(const char *entry_name, size_t entry_name_len, s64 sz, s64 o_o, DataSourceType type) : parent(nullptr), sibling(nullptr), offset(0), size(sz), orig_offset(o_o), entry_offset(0), hash_value(0xFFFFFFFF), source_type(type) {
this->path_len = entry_name_len;
this->path = std::unique_ptr<char[]>(new char[this->path_len + 1]);
std::memcpy(this->path.get(), entry_name, entry_name_len);
this->path = static_cast<char *>(AllocateTracked(AllocationType_FileName, this->path_len + 1));
std::memcpy(this->path, entry_name, entry_name_len);
this->path[this->path_len] = 0;
}
~BuildFileContext() {
if (this->path != nullptr) {
FreeTracked(AllocationType_FileName, this->path, this->path_len + 1);
this->path = nullptr;
}
}
void operator delete(void *p) {
FreeTracked(AllocationType_BuildFileContext, p, sizeof(BuildFileContext));
}
size_t GetPathLength() const {
if (this->parent == nullptr) {
return 0;
@@ -224,7 +293,7 @@ namespace ams::mitm::fs::romfs {
const size_t parent_len = this->parent->GetPath(dst);
dst[parent_len] = '/';
std::memcpy(dst + parent_len + 1, this->path.get(), this->path_len);
std::memcpy(dst + parent_len + 1, this->path, this->path_len);
dst[parent_len + 1 + this->path_len] = '\x00';
return parent_len + 1 + this->path_len;
}
@@ -248,6 +317,8 @@ namespace ams::mitm::fs::romfs {
class Builder {
NON_COPYABLE(Builder);
NON_MOVEABLE(Builder);
public:
using SourceInfoVector = std::vector<SourceInfo, TrackedAllocator<AllocationType_SourceInfo, SourceInfo>>;
private:
template<typename T>
struct Comparator {
@@ -270,13 +341,13 @@ namespace ams::mitm::fs::romfs {
}
};
template<typename T>
using ContextSet = std::set<std::unique_ptr<T>, Comparator<T>>;
template<AllocationType AllocType, typename T>
using ContextSet = std::set<std::unique_ptr<T>, Comparator<T>, TrackedAllocator<AllocType, std::unique_ptr<T>>>;
private:
ncm::ProgramId m_program_id;
BuildDirectoryContext *m_root;
ContextSet<BuildDirectoryContext> m_directories;
ContextSet<BuildFileContext> m_files;
ContextSet<AllocationType_DirContextSet, BuildDirectoryContext> m_directories;
ContextSet<AllocationType_FileContextSet, BuildFileContext> m_files;
size_t m_num_dirs;
size_t m_num_files;
size_t m_dir_table_size;
@@ -295,11 +366,14 @@ namespace ams::mitm::fs::romfs {
void AddFile(BuildDirectoryContext *parent_ctx, std::unique_ptr<BuildFileContext> file_ctx);
public:
Builder(ncm::ProgramId pr_id);
~Builder();
void AddSdFiles();
void AddStorageFiles(ams::fs::IStorage *storage, DataSourceType source_type);
void Build(std::vector<SourceInfo> *out_infos);
void Build(SourceInfoVector *out_infos);
};
Result ConfigureDynamicHeap(u64 *out_size, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application);
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "../amsmitm_initialization.hpp"
#include "mitm_pm_module.hpp"
#include "mitm_pm_service.hpp"
namespace ams::mitm::pm {
namespace {
constexpr sm::ServiceName PmServiceName = sm::ServiceName::Encode("mitm:pm");
constexpr size_t PmMaxSessions = 1;
constexpr size_t MaxServers = 1;
constexpr size_t MaxSessions = PmMaxSessions;
using ServerOptions = sf::hipc::DefaultServerManagerOptions;
sf::hipc::ServerManager<MaxServers, ServerOptions, MaxSessions> g_server_manager;
constinit sf::UnmanagedServiceObject<mitm::pm::impl::IPmInterface, mitm::pm::PmService> g_pm_service_object;
}
void MitmModule::ThreadFunction(void *) {
/* Create bpc:ams. */
R_ABORT_UNLESS(g_server_manager.RegisterObjectForServer(g_pm_service_object.GetShared(), PmServiceName, PmMaxSessions));
/* Loop forever, servicing our services. */
g_server_manager.LoopProcess();
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "../amsmitm_module.hpp"
namespace ams::mitm::pm {
DEFINE_MITM_MODULE_CLASS(0x1000, AMS_GET_SYSTEM_THREAD_PRIORITY(fs, WorkerThreadPool) - 2);
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "../amsmitm_initialization.hpp"
#include "mitm_pm_service.hpp"
#include "mitm_pm_service.hpp"
#include "../fs_mitm/fsmitm_romfs.hpp"
namespace ams::mitm::pm {
Result PmService::PrepareLaunchProgram(sf::Out<u64> out, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application) {
/* Default to zero heap. */
*out = 0;
/* Actually configure the required boost size for romfs. */
R_TRY(mitm::fs::romfs::ConfigureDynamicHeap(out.GetPointer(), program_id, status, is_application));
/* TODO: Is there anything else we should do, while we have the opportunity? */
R_SUCCEED();
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::mitm::pm {
class PmService {
public:
Result PrepareLaunchProgram(sf::Out<u64> out, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application);
};
static_assert(impl::IsIPmInterface<PmService>);
}

View File

@@ -27,7 +27,7 @@ namespace ams::mitm::ns {
Result NsAmMitmService::ResolveApplicationContentPath(ncm::ProgramId application_id, u8 content_type) {
/* Always succeed for web applets asking about HBL to enable hbl_html, and applications with manual_html to allow custom manual data. */
bool is_hbl = false;
if ((R_SUCCEEDED(pm::info::IsHblProgramId(std::addressof(is_hbl), application_id)) && is_hbl) || (static_cast<ncm::ContentType>(content_type) == ncm::ContentType::HtmlDocument && mitm::fs::HasSdManualHtmlContent(application_id))) {
if ((R_SUCCEEDED(ams::pm::info::IsHblProgramId(std::addressof(is_hbl), application_id)) && is_hbl) || (static_cast<ncm::ContentType>(content_type) == ncm::ContentType::HtmlDocument && mitm::fs::HasSdManualHtmlContent(application_id))) {
nsamResolveApplicationContentPathFwd(m_forward_service.get(), static_cast<u64>(application_id), static_cast<NcmContentType>(content_type));
R_SUCCEED();
}

View File

@@ -27,7 +27,7 @@ namespace ams::mitm::ns {
Result NsDocumentService::ResolveApplicationContentPath(ncm::ProgramId application_id, u8 content_type) {
/* Always succeed for web applets asking about HBL to enable hbl_html, and applications with manual_html to allow custom manual data. */
bool is_hbl = false;
if ((R_SUCCEEDED(pm::info::IsHblProgramId(std::addressof(is_hbl), application_id)) && is_hbl) || (static_cast<ncm::ContentType>(content_type) == ncm::ContentType::HtmlDocument && mitm::fs::HasSdManualHtmlContent(application_id))) {
if ((R_SUCCEEDED(ams::pm::info::IsHblProgramId(std::addressof(is_hbl), application_id)) && is_hbl) || (static_cast<ncm::ContentType>(content_type) == ncm::ContentType::HtmlDocument && mitm::fs::HasSdManualHtmlContent(application_id))) {
nswebResolveApplicationContentPath(m_srv.get(), static_cast<u64>(application_id), static_cast<NcmContentType>(content_type));
R_SUCCEED();
}

View File

@@ -32,7 +32,7 @@ namespace ams::mitm::settings {
SetMitmService::SetMitmService(std::shared_ptr<::Service> &&s, const sm::MitmProcessInfo &c) : sf::MitmServiceImplBase(std::forward<std::shared_ptr<::Service>>(s), c) {
if (m_client_info.program_id == ncm::SystemProgramId::Ns) {
os::ProcessId application_process_id;
if (R_SUCCEEDED(pm::dmnt::GetApplicationProcessId(std::addressof(application_process_id))) && g_application_process_id == application_process_id) {
if (R_SUCCEEDED(ams::pm::dmnt::GetApplicationProcessId(std::addressof(application_process_id))) && g_application_process_id == application_process_id) {
m_locale = g_application_locale;
m_is_valid_language = g_valid_language;
m_is_valid_region = g_valid_region;
@@ -61,8 +61,8 @@ namespace ams::mitm::settings {
if (is_ns) {
/* When NS asks for a locale, refresh to get the current application locale. */
R_TRY(pm::dmnt::GetApplicationProcessId(std::addressof(application_process_id)));
R_TRY(pm::info::GetProgramId(std::addressof(program_id), application_process_id));
R_TRY(ams::pm::dmnt::GetApplicationProcessId(std::addressof(application_process_id)));
R_TRY(ams::pm::info::GetProgramId(std::addressof(program_id), application_process_id));
}
m_locale = cfg::GetOverrideLocale(program_id);
m_is_valid_language = settings::IsValidLanguageCode(m_locale.language_code);

View File

@@ -29,7 +29,7 @@ namespace ams::mitm::settings {
PortIndex_Count,
};
constexpr sm::ServiceName SetMitmServiceName = sm::ServiceName::Encode("set");
constexpr sm::ServiceName SetMitmServiceName = sm::ServiceName::Encode("set");
constexpr sm::ServiceName SetSysMitmServiceName = sm::ServiceName::Encode("set:sys");
struct ServerOptions {

View File

@@ -16,6 +16,7 @@
#include <stratosphere.hpp>
#include "setsys_mitm_service.hpp"
#include "settings_sd_kvs.hpp"
#include "setsys_shim.h"
namespace ams::mitm::settings {
@@ -23,6 +24,8 @@ namespace ams::mitm::settings {
namespace {
constexpr const char ExternalBluetoothDatabasePath[] = "@Sdcard:/atmosphere/bluetooth_devices.db";
constinit os::SdkMutex g_firmware_version_lock;
constinit bool g_cached_firmware_version;
constinit settings::FirmwareVersion g_firmware_version;
@@ -77,7 +80,7 @@ namespace ams::mitm::settings {
CacheFirmwareVersion();
/* We want to give a special firmware version to the home menu title, and nothing else. */
/* This means Qlaunch + Maintenance Menu, and nothing else. */
/* This means qlaunch + maintenance menu, and nothing else. */
if (client_info.program_id == ncm::SystemAppletId::Qlaunch || client_info.program_id == ncm::SystemAppletId::MaintenanceMenu) {
*out = g_ams_firmware_version;
} else {
@@ -87,6 +90,99 @@ namespace ams::mitm::settings {
R_SUCCEED();
}
bool IsExternalBluetoothDatabaseEnabled() {
u8 en = 0;
settings::fwdbg::GetSettingsItemValue(std::addressof(en), sizeof(en), "atmosphere", "enable_external_bluetooth_db");
return en;
}
bool HasExternalBluetoothDatabase() {
bool file_exists;
R_ABORT_UNLESS(fs::HasFile(std::addressof(file_exists), ExternalBluetoothDatabasePath));
return file_exists;
}
Result ReadExternalBluetoothDatabase(s32 *entries_read, settings::BluetoothDevicesSettings *db, size_t db_max_size) {
/* Open the external database file. */
fs::FileHandle file;
R_TRY(fs::OpenFile(std::addressof(file), ExternalBluetoothDatabasePath, fs::OpenMode_Read));
ON_SCOPE_EXIT { fs::CloseFile(file); };
/* Read the number of database entries stored in external database. */
u64 total_entries;
R_TRY(fs::ReadFile(file, 0, std::addressof(total_entries), sizeof(total_entries)));
u64 db_offset = sizeof(total_entries);
if (total_entries > db_max_size) {
/* Pairings are stored from least to most recent. Add offset to skip the older entries that won't fit. */
db_offset += (total_entries - db_max_size) * sizeof(settings::BluetoothDevicesSettings);
/* Cap number of database entries read to size of database on this firmware. */
total_entries = db_max_size;
}
/* Read database entries. */
R_TRY(fs::ReadFile(file, db_offset, db, total_entries * sizeof(settings::BluetoothDevicesSettings)));
/* Convert entries to the old format if running on a firmware below 13.0.0. */
if (hos::GetVersion() < hos::Version_13_0_0) {
for (size_t i = 0; i < total_entries; ++i) {
/* Copy the newer name field to the older one. */
util::TSNPrintf(db[i].name, sizeof(db[i].name), "%s", db[i].name2);
/* Clear the newer name field. */
std::memset(db[i].name2, 0, sizeof(db[i].name2));
}
}
/* Set the output. */
*entries_read = static_cast<s32>(total_entries);
R_SUCCEED();
}
Result StoreExternalBluetoothDatabase(const settings::BluetoothDevicesSettings *db, u64 total_entries) {
/* Open the external database file. */
fs::FileHandle file;
R_TRY(fs::OpenFile(std::addressof(file), ExternalBluetoothDatabasePath, fs::OpenMode_Write));
ON_SCOPE_EXIT { fs::CloseFile(file); };
/* Ensure the file is the appropriate size for the number of entries. */
R_TRY(fs::SetFileSize(file, sizeof(total_entries) + total_entries * sizeof(settings::BluetoothDevicesSettings)));
/* Write the number of database entries. */
R_TRY(fs::WriteFile(file, 0, std::addressof(total_entries), sizeof(total_entries), fs::WriteOption::None));
/* Write the database entries. */
u64 db_offset = sizeof(total_entries);
if (hos::GetVersion() < hos::Version_13_0_0) {
/* Convert entries to the new format if running on a firmware below 13.0.0. */
for (size_t i = 0; i < total_entries; ++i) {
/* Make a copy of the current database entry. */
settings::BluetoothDevicesSettings tmp = db[i];
/* Copy the older name field to the newer one. */
util::TSNPrintf(tmp.name2, sizeof(tmp.name2), "%s", tmp.name);
/* Clear the original name field. */
std::memset(tmp.name, 0, sizeof(tmp.name));
/* Write the converted database entry. */
R_TRY(fs::WriteFile(file, db_offset, std::addressof(tmp), sizeof(settings::BluetoothDevicesSettings), fs::WriteOption::None));
/* Advance to the next database entry. */
db_offset += sizeof(settings::BluetoothDevicesSettings);
}
/* Flush the data we've written. */
R_TRY(fs::FlushFile(file));
} else {
/* We can just write the database to the file. */
R_TRY(fs::WriteFile(file, db_offset, db, total_entries * sizeof(settings::BluetoothDevicesSettings), fs::WriteOption::Flush));
}
R_SUCCEED();
}
}
Result SetSysMitmService::GetFirmwareVersion(sf::Out<settings::FirmwareVersion> out) {
@@ -102,6 +198,47 @@ namespace ams::mitm::settings {
R_RETURN(GetFirmwareVersionImpl(out.GetPointer(), m_client_info));
}
Result SetSysMitmService::SetBluetoothDevicesSettings(const sf::InMapAliasArray<settings::BluetoothDevicesSettings> &settings) {
/* We only want to perform additional logic when the external database setting is enabled. */
R_UNLESS(IsExternalBluetoothDatabaseEnabled(), sm::mitm::ResultShouldForwardToSession());
/* Create the external database if it doesn't exist. */
if (!HasExternalBluetoothDatabase()) {
R_TRY(fs::CreateFile(ExternalBluetoothDatabasePath, 0));
}
/* Backup the local database to the sd card. */
R_TRY(StoreExternalBluetoothDatabase(settings.GetPointer(), settings.GetSize()));
/* Ensure that the updated database is stored to the system save as usual. */
static_assert(sizeof(settings::BluetoothDevicesSettings) == sizeof(::SetSysBluetoothDevicesSettings));
R_TRY(setsysSetBluetoothDevicesSettingsFwd(m_forward_service.get(), reinterpret_cast<const ::SetSysBluetoothDevicesSettings *>(settings.GetPointer()), settings.GetSize()));
R_SUCCEED();
}
Result SetSysMitmService::GetBluetoothDevicesSettings(sf::Out<s32> out_count, const sf::OutMapAliasArray<settings::BluetoothDevicesSettings> &out) {
/* We only want to perform additional logic when the external database setting is enabled. */
R_UNLESS(IsExternalBluetoothDatabaseEnabled(), sm::mitm::ResultShouldForwardToSession());
if (!HasExternalBluetoothDatabase()) {
/* Fetch the local database from the system save. */
static_assert(sizeof(settings::BluetoothDevicesSettings) == sizeof(::SetSysBluetoothDevicesSettings));
R_TRY(setsysGetBluetoothDevicesSettingsFwd(m_forward_service.get(), out_count.GetPointer(), reinterpret_cast<::SetSysBluetoothDevicesSettings *>(out.GetPointer()), out.GetSize()));
/* Create the external database file. */
R_TRY(fs::CreateFile(ExternalBluetoothDatabasePath, 0));
/* Backup the local database to the sd card. */
R_TRY(StoreExternalBluetoothDatabase(out.GetPointer(), out_count.GetValue()));
} else {
/* Read the external database from the sd card. */
R_TRY(ReadExternalBluetoothDatabase(out_count.GetPointer(), out.GetPointer(), out.GetSize()));
}
R_SUCCEED();
}
Result SetSysMitmService::GetSettingsItemValueSize(sf::Out<u64> out_size, const settings::SettingsName &name, const settings::SettingsItemKey &key) {
R_TRY_CATCH(settings::fwdbg::GetSdCardKeyValueStoreSettingsItemValueSize(out_size.GetPointer(), name.value, key.value)) {
R_CATCH_RETHROW(sf::impl::ResultRequestContextChanged)

View File

@@ -16,12 +16,14 @@
#pragma once
#include <stratosphere.hpp>
#define AMS_SETTINGS_SYSTEM_MITM_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 3, Result, GetFirmwareVersion, (sf::Out<ams::settings::FirmwareVersion> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 4, Result, GetFirmwareVersion2, (sf::Out<ams::settings::FirmwareVersion> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 37, Result, GetSettingsItemValueSize, (sf::Out<u64> out_size, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key), (out_size, name, key)) \
AMS_SF_METHOD_INFO(C, H, 38, Result, GetSettingsItemValue, (sf::Out<u64> out_size, const sf::OutBuffer &out, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key), (out_size, out, name, key)) \
AMS_SF_METHOD_INFO(C, H, 62, Result, GetDebugModeFlag, (sf::Out<bool> out), (out))
#define AMS_SETTINGS_SYSTEM_MITM_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 3, Result, GetFirmwareVersion, (sf::Out<ams::settings::FirmwareVersion> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 4, Result, GetFirmwareVersion2, (sf::Out<ams::settings::FirmwareVersion> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 11, Result, SetBluetoothDevicesSettings, (const sf::InMapAliasArray<ams::settings::BluetoothDevicesSettings> &settings), (settings)) \
AMS_SF_METHOD_INFO(C, H, 12, Result, GetBluetoothDevicesSettings, (sf::Out<s32> out_count, const sf::OutMapAliasArray<ams::settings::BluetoothDevicesSettings> &out), (out_count, out)) \
AMS_SF_METHOD_INFO(C, H, 37, Result, GetSettingsItemValueSize, (sf::Out<u64> out_size, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key), (out_size, name, key)) \
AMS_SF_METHOD_INFO(C, H, 38, Result, GetSettingsItemValue, (sf::Out<u64> out_size, const sf::OutBuffer &out, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key), (out_size, out, name, key)) \
AMS_SF_METHOD_INFO(C, H, 62, Result, GetDebugModeFlag, (sf::Out<bool> out), (out))
AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::settings, ISetSysMitmInterface, AMS_SETTINGS_SYSTEM_MITM_INTERFACE_INFO, 0x0E82ED13)
@@ -41,6 +43,8 @@ namespace ams::mitm::settings {
public:
Result GetFirmwareVersion(sf::Out<ams::settings::FirmwareVersion> out);
Result GetFirmwareVersion2(sf::Out<ams::settings::FirmwareVersion> out);
Result SetBluetoothDevicesSettings(const sf::InMapAliasArray<ams::settings::BluetoothDevicesSettings> &settings);
Result GetBluetoothDevicesSettings(sf::Out<s32> out_count, const sf::OutMapAliasArray<ams::settings::BluetoothDevicesSettings> &out);
Result GetSettingsItemValueSize(sf::Out<u64> out_size, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key);
Result GetSettingsItemValue(sf::Out<u64> out_size, const sf::OutBuffer &out, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key);
Result GetDebugModeFlag(sf::Out<bool> out);

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 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 "setsys_shim.h"
Result setsysSetBluetoothDevicesSettingsFwd(Service *s, const SetSysBluetoothDevicesSettings *settings, s32 count) {
return serviceDispatch(s, 11,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
.buffers = { { settings, count * sizeof(SetSysBluetoothDevicesSettings) } },
);
}
Result setsysGetBluetoothDevicesSettingsFwd(Service *s, s32 *total_out, SetSysBluetoothDevicesSettings *settings, s32 count) {
return serviceDispatchOut(s, 12, *total_out,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
.buffers = { { settings, count * sizeof(SetSysBluetoothDevicesSettings) } },
);
}

View File

@@ -0,0 +1,20 @@
/**
* @file setsys_shim.h
* @brief Settings Services (fs) IPC wrapper for setsys.mitm.
* @author ndeadly
* @copyright libnx Authors
*/
#pragma once
#include <switch.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Forwarding shims. */
Result setsysSetBluetoothDevicesSettingsFwd(Service *s, const SetSysBluetoothDevicesSettings *settings, s32 count);
Result setsysGetBluetoothDevicesSettingsFwd(Service *s, s32 *total_out, SetSysBluetoothDevicesSettings *settings, s32 count);
#ifdef __cplusplus
}
#endif

View File

@@ -393,6 +393,11 @@ namespace ams::settings::fwdbg {
/* 0 = Disabled, 1 = Enabled */
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_log_manager", "u8!0x0"));
/* Controls whether the bluetooth pairing database is redirected to the SD card (shared across sysmmc/all emummcs) */
/* NOTE: On <13.0.0, the database size was 10 instead of 20; booting pre-13.0.0 will truncate the database. */
/* 0 = Disabled, 1 = Enabled */
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_external_bluetooth_db", "u8!0x0"));
/* Hbloader custom settings. */
/* Controls the size of the homebrew heap when running as applet. */

View File

@@ -78,6 +78,9 @@
"svcReplyAndReceive": "0x43",
"svcReplyAndReceiveWithUserBuffer": "0x44",
"svcCreateEvent": "0x45",
"svcMapPhysicalMemoryUnsafe": "0x48",
"svcUnmapPhysicalMemoryUnsafe": "0x49",
"svcSetUnsafeLimit": "0x4A",
"svcReadWriteRegister": "0x4E",
"svcDebugActiveProcess": "0x60",
"svcGetDebugEvent": "0x63",
@@ -86,7 +89,9 @@
"svcQueryDebugProcessMemory": "0x69",
"svcReadDebugProcessMemory": "0x6a",
"svcGetDebugThreadParam": "0x6d",
"svcCallSecureMonitor": "0x7f"
"svcCallSecureMonitor": "0x7f",
"svcMapInsecureMemory": "0x90",
"svcUnmapInsecureMemory": "0x91"
}
}, {
"type": "min_kernel_version",

View File

@@ -99,6 +99,41 @@ namespace ams {
namespace init {
namespace {
Result InitializePlatformServiceWithoutSessionCountForHomebrewCompatibility() {
/* NOTE: This implements a hack, to keep a session to pl:u without counting against the global limit. */
/* This is done because as of 16.0.0, pl:u is the only way to get shared font access, and there are */
/* not enough sessions for all reasonable the clients when homebrew gets involved. */
/* Please do not do similar things for other services; this is a hack which may not always work, */
/* and which could cause problems in other contexts where the ServerManager doesn't have enough */
/* slots in truth. */
/* Initialize pl. */
R_ABORT_UNLESS(plInitialize(::PlServiceType_User));
/* Get the service session for pl. */
Service *srv = plGetServiceSession();
/* Next, create a clone. */
/* Because this doesn't go through sm, this does not count against the session limit. */
Service clone;
R_TRY(serviceClone(srv, std::addressof(clone)));
/* Next, close the pl service session from sm. */
/* This decrements the used session count by one, since the session is from sm. */
serviceClose(srv);
/* HACK: replace the session with the clone we made, to restore functionality. */
*srv = clone;
return 0;
}
}
void InitializeSystemModule() {
/* Initialize heap. */
fatal::srv::InitializeFsHeap();
@@ -126,7 +161,7 @@ namespace ams {
R_ABORT_UNLESS(psmInitialize());
R_ABORT_UNLESS(spsmInitialize());
R_ABORT_UNLESS(plInitialize(::PlServiceType_User));
R_ABORT_UNLESS(InitializePlatformServiceWithoutSessionCountForHomebrewCompatibility());
gpio::Initialize();
/* Mount the SD card. */

View File

@@ -42,7 +42,46 @@ namespace ams::fatal::srv {
alignas(os::MemoryPageSize) constinit u8 g_nv_transfer_memory[0x40000];
/* There should only be a single (1280*768) framebuffer. */
alignas(os::MemoryPageSize) constinit u8 g_framebuffer_memory[FatalScreenWidthAlignedBytes * util::AlignUp(FatalScreenHeight, 128)];
constexpr size_t FrameBufferRequiredSizeBytes = FatalScreenWidthAlignedBytes * util::AlignUp(FatalScreenHeight, 128);
constexpr size_t FrameBufferRequiredSizePageAligned = util::AlignUp(FrameBufferRequiredSizeBytes, os::MemoryPageSize);
constexpr size_t FrameBufferRequiredSizeHeapAligned = util::AlignUp(FrameBufferRequiredSizeBytes, os::MemoryHeapUnitSize);
constinit u8 *g_framebuffer_pointer = nullptr;
void InitializeFrameBufferPointer() {
/* Try to get a framebuffer from heap. */
{
if (R_SUCCEEDED(os::SetMemoryHeapSize(FrameBufferRequiredSizeHeapAligned))) {
g_framebuffer_pointer = reinterpret_cast<u8 *>(os::GetMemoryHeapAddress());
return;
}
}
/* We couldn't use heap, so try insecure memory, from the system nonsecure pool. */
{
uintptr_t address = 0;
if (R_SUCCEEDED(os::AllocateInsecureMemory(std::addressof(address), FrameBufferRequiredSizePageAligned))) {
g_framebuffer_pointer = reinterpret_cast<u8 *>(address);
return;
}
}
/* Neither heap nor insecure is available, so we're going to have to try to raid the unsafe pool. */
{
/* First, increase the limit to an extremely high value. */
size_t large_size = std::max(128_MB, FrameBufferRequiredSizeHeapAligned);
while (svc::ResultLimitReached::Includes(svc::SetUnsafeLimit(large_size))) {
large_size *= 2;
}
/* Next, map some unsafe memory. */
uintptr_t address = 0;
if (R_SUCCEEDED(os::AllocateUnsafeMemory(std::addressof(address), FrameBufferRequiredSizePageAligned))) {
g_framebuffer_pointer = reinterpret_cast<u8 *>(address);
return;
}
}
}
}
@@ -202,8 +241,12 @@ namespace ams::fatal::srv {
void ShowFatalTask::PreRenderFrameBuffer() {
const FatalConfig &config = GetFatalConfig();
/* Allocate a frame buffer. */
InitializeFrameBufferPointer();
AMS_ABORT_UNLESS(g_framebuffer_pointer != nullptr);
/* Pre-render the image into the static framebuffer. */
u16 *tiled_buf = reinterpret_cast<u16 *>(g_framebuffer_memory);
u16 *tiled_buf = reinterpret_cast<u16 *>(g_framebuffer_pointer);
/* Temporarily use the NV transfer memory as font backing heap. */
font::SetHeapMemory(g_nv_transfer_memory, sizeof(g_nv_transfer_memory));
@@ -214,7 +257,7 @@ namespace ams::fatal::srv {
font::SetFontColor(0xFFFF);
/* Draw a background. */
for (size_t i = 0; i < sizeof(g_framebuffer_memory) / sizeof(*tiled_buf); i++) {
for (size_t i = 0; i < FrameBufferRequiredSizeBytes / sizeof(*tiled_buf); i++) {
tiled_buf[i] = 0x39C9;
}
@@ -439,7 +482,7 @@ namespace ams::fatal::srv {
R_TRY(nvFenceInit());
/* Create nvmap. */
R_TRY(nvMapCreate(std::addressof(m_map), g_framebuffer_memory, sizeof(g_framebuffer_memory), 0x20000, NvKind_Pitch, true));
R_TRY(nvMapCreate(std::addressof(m_map), g_framebuffer_pointer, FrameBufferRequiredSizeBytes, 0x20000, NvKind_Pitch, true));
/* Setup graphics buffer. */
{
@@ -460,9 +503,9 @@ namespace ams::fatal::srv {
grbuf.planes[0].block_height_log2 = 4;
grbuf.nvmap_id = nvMapGetId(std::addressof(m_map));
grbuf.stride = FatalScreenWidthAligned;
grbuf.total_size = sizeof(g_framebuffer_memory);
grbuf.total_size = FrameBufferRequiredSizeBytes;
grbuf.planes[0].pitch = FatalScreenWidthAlignedBytes;
grbuf.planes[0].size = sizeof(g_framebuffer_memory);
grbuf.planes[0].size = FrameBufferRequiredSizeBytes;
grbuf.planes[0].offset = 0;
R_TRY(nwindowConfigureBuffer(std::addressof(m_win), 0, std::addressof(grbuf)));
@@ -474,7 +517,7 @@ namespace ams::fatal::srv {
void ShowFatalTask::DisplayPreRenderedFrame() {
s32 slot;
R_ABORT_UNLESS(nwindowDequeueBuffer(std::addressof(m_win), std::addressof(slot), nullptr));
dd::FlushDataCache(g_framebuffer_memory, sizeof(g_framebuffer_memory));
dd::FlushDataCache(g_framebuffer_pointer, FrameBufferRequiredSizeBytes);
R_ABORT_UNLESS(nwindowQueueBuffer(std::addressof(m_win), m_win.cur_slot, NULL));
}

View File

@@ -244,6 +244,24 @@ namespace ams::pm::impl {
/* If we fail after now, unpin. */
ON_RESULT_FAILURE { ldr::pm::UnpinProgram(pin_id); };
/* Ensure we can talk to mitm services. */
{
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(bool, s_initialized_mitm, false);
if (!s_initialized_mitm) {
mitm::pm::Initialize();
s_initialized_mitm = true;
}
}
/* Determine boost size for mitm. */
u64 mitm_boost_size = 0;
R_TRY(mitm::pm::PrepareLaunchProgram(std::addressof(mitm_boost_size), program_info.program_id, override_status, is_application));
if (mitm_boost_size > 0 || is_application) {
R_ABORT_UNLESS(BoostSystemMemoryResourceLimitForMitm(mitm_boost_size));
}
ON_RESULT_FAILURE_2 { if (mitm_boost_size > 0 || is_application) { R_ABORT_UNLESS(BoostSystemMemoryResourceLimitForMitm(0)); } };
/* Ensure resources are available. */
resource::WaitResourceAvailable(std::addressof(program_info));
@@ -713,4 +731,8 @@ namespace ams::pm::impl {
R_RETURN(resource::GetResourceLimitValues(out_cur_val, out_lim_val, static_cast<ResourceLimitGroup>(group), static_cast<svc::LimitableResource>(resource)));
}
Result BoostSystemMemoryResourceLimitForMitm(u64 boost_size) {
R_RETURN(resource::BoostSystemMemoryResourceLimitForMitm(boost_size));
}
}

View File

@@ -55,5 +55,6 @@ namespace ams::pm::impl {
Result GetAppletCurrentResourceLimitValues(pm::ResourceLimitValues *out);
Result GetAppletPeakResourceLimitValues(pm::ResourceLimitValues *out);
Result AtmosphereGetCurrentLimitInfo(s64 *out_cur_val, s64 *out_lim_val, u32 group, u32 resource);
Result BoostSystemMemoryResourceLimitForMitm(u64 boost_size);
}

View File

@@ -52,9 +52,16 @@ namespace ams::pm::resource {
constinit os::SdkMutex g_resource_limit_lock;
constinit os::NativeHandle g_resource_limit_handles[ResourceLimitGroup_Count];
constinit spl::MemoryArrangement g_memory_arrangement = spl::MemoryArrangement_Standard;
constinit u64 g_system_memory_boost_size = 0;
constinit u64 g_extra_threads_available[ResourceLimitGroup_Count];
constinit os::SdkMutex g_system_memory_boost_lock;
constinit u64 g_system_memory_boost_size = 0;
constinit u64 g_system_memory_boost_size_for_mitm = 0;
ALWAYS_INLINE u64 GetCurrentSystemMemoryBoostSize() {
return g_system_memory_boost_size + g_system_memory_boost_size_for_mitm;
}
constinit u64 g_resource_limits[ResourceLimitGroup_Count][svc::LimitableResource_Count] = {
[ResourceLimitGroup_System] = {
[svc::LimitableResource_PhysicalMemoryMax] = 0, /* Initialized dynamically later. */
@@ -220,6 +227,47 @@ namespace ams::pm::resource {
R_SUCCEED();
}
Result BoostSystemMemoryResourceLimitLocked(u64 normal_boost, u64 mitm_boost) {
/* Check pre-conditions. */
AMS_ASSERT(g_system_memory_boost_lock.IsLockedByCurrentThread());
/* Determine total boost. */
const u64 boost_size = normal_boost + mitm_boost;
/* Don't allow all application memory to be taken away. */
R_UNLESS(boost_size <= g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_Application], pm::ResultInvalidSize());
const u64 new_app_size = g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_Application] - boost_size;
{
std::scoped_lock lk(g_resource_limit_lock);
if (hos::GetVersion() >= hos::Version_5_0_0) {
/* Starting in 5.0.0, PM does not allow for only one of the sets to fail. */
if (boost_size < GetCurrentSystemMemoryBoostSize()) {
R_TRY(svc::SetUnsafeLimit(boost_size));
R_ABORT_UNLESS(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
} else {
R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
R_ABORT_UNLESS(svc::SetUnsafeLimit(boost_size));
}
} else {
const u64 new_sys_size = g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_System] + boost_size;
if (boost_size < GetCurrentSystemMemoryBoostSize()) {
R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_System, new_sys_size));
R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
} else {
R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_System, new_sys_size));
}
}
g_system_memory_boost_size = normal_boost;
g_system_memory_boost_size_for_mitm = mitm_boost;
}
R_SUCCEED();
}
}
/* Resource API. */
@@ -352,37 +400,19 @@ namespace ams::pm::resource {
}
Result BoostSystemMemoryResourceLimit(u64 boost_size) {
/* Don't allow all application memory to be taken away. */
R_UNLESS(boost_size <= g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_Application], pm::ResultInvalidSize());
/* Ensure only one boost change happens at a time. */
std::scoped_lock lk(g_system_memory_boost_lock);
const u64 new_app_size = g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_Application] - boost_size;
{
std::scoped_lock lk(g_resource_limit_lock);
/* Boost to the appropriate total amount. */
R_RETURN(BoostSystemMemoryResourceLimitLocked(boost_size, g_system_memory_boost_size_for_mitm));
}
if (hos::GetVersion() >= hos::Version_5_0_0) {
/* Starting in 5.0.0, PM does not allow for only one of the sets to fail. */
if (boost_size < g_system_memory_boost_size) {
R_TRY(svc::SetUnsafeLimit(boost_size));
R_ABORT_UNLESS(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
} else {
R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
R_ABORT_UNLESS(svc::SetUnsafeLimit(boost_size));
}
} else {
const u64 new_sys_size = g_memory_resource_limits[g_memory_arrangement][ResourceLimitGroup_System] + boost_size;
if (boost_size < g_system_memory_boost_size) {
R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_System, new_sys_size));
R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
} else {
R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_Application, new_app_size));
R_TRY(SetMemoryResourceLimitLimitValue(ResourceLimitGroup_System, new_sys_size));
}
}
Result BoostSystemMemoryResourceLimitForMitm(u64 boost_size) {
/* Ensure only one boost change happens at a time. */
std::scoped_lock lk(g_system_memory_boost_lock);
g_system_memory_boost_size = boost_size;
}
R_SUCCEED();
/* Boost to the appropriate total amount. */
R_RETURN(BoostSystemMemoryResourceLimitLocked(g_system_memory_boost_size, boost_size));
}
Result BoostApplicationThreadResourceLimit() {

View File

@@ -24,6 +24,8 @@ namespace ams::pm::resource {
Result BoostApplicationThreadResourceLimit();
Result BoostSystemThreadResourceLimit();
Result BoostSystemMemoryResourceLimitForMitm(u64 boost_size);
os::NativeHandle GetResourceLimitHandle(ResourceLimitGroup group);
os::NativeHandle GetResourceLimitHandle(const ldr::ProgramInfo *info);

View File

@@ -1,4 +1,4 @@
APPLICATIONS := daybreak reboot_to_payload
APPLICATIONS := daybreak haze reboot_to_payload
SUBFOLDERS := $(APPLICATIONS)

View File

@@ -16,6 +16,7 @@
#include "ui_util.hpp"
#include <cstdio>
#include <math.h>
#include <cstring>
namespace dbk {
@@ -73,7 +74,7 @@ namespace dbk {
nvgFill(vg);
/* Setup the font. */
nvgFontSize(vg, 32.0f);
nvgFontSize(vg, std::min(32.0f, -(strlen(title)*0.5f) + 47.0f));
nvgFontFace(vg, SwitchStandardFont);
nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
nvgFillColor(vg, nvgRGB(0, 0, 0));

226
troposphere/haze/Makefile Normal file
View File

@@ -0,0 +1,226 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
TOPDIR ?= $(CURDIR)
include $(DEVKITPRO)/libnx/switch_rules
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
#
# NO_ICON: if set to anything, do not use icon.
# NO_NACP: if set to anything, no .nacp file is generated.
# APP_TITLE is the name of the app stored in the .nacp file (Optional)
# APP_AUTHOR is the author of the app stored in the .nacp file (Optional)
# APP_VERSION is the version of the app stored in the .nacp file (Optional)
# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional)
# ICON is the filename of the icon (.jpg), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.jpg
# - icon.jpg
# - <libnx folder>/default_icon.jpg
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include ../../libraries/libvapours/include
#ROMFS := romfs
APP_TITLE := USB File Transfer
APP_AUTHOR := Atmosphere-NX
APP_VERSION := 1.0.0
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++20
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lnx
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(LIBNX)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export OUTPUT := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
ifeq ($(strip $(ICON)),)
icons := $(wildcard *.jpg)
ifneq (,$(findstring $(TARGET).jpg,$(icons)))
export APP_ICON := $(TOPDIR)/$(TARGET).jpg
else
ifneq (,$(findstring icon.jpg,$(icons)))
export APP_ICON := $(TOPDIR)/icon.jpg
endif
endif
else
export APP_ICON := $(TOPDIR)/$(ICON)
endif
ifeq ($(strip $(NO_ICON)),)
export NROFLAGS += --icon=$(APP_ICON)
endif
ifeq ($(strip $(NO_NACP)),)
export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp
endif
ifneq ($(APP_TITLEID),)
export NACPFLAGS += --titleid=$(APP_TITLEID)
endif
ifneq ($(ROMFS),)
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
endif
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
ifeq ($(strip $(APP_JSON)),)
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

BIN
troposphere/haze/icon.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="svg1500" width="182" height="182" version="1.1" viewBox="0 0 182 182" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs id="defs1484"><linearGradient id="a" x1="36.532" x2="36.532" y1="39.898" y2="80.671" gradientTransform="scale(1.0484 .95387)" gradientUnits="userSpaceOnUse"><stop id="stop1474" stop-color="#fff" offset="0"/><stop id="stop1476" stop-color="#9FC1DB" offset="1"/></linearGradient><linearGradient id="linearGradient2139" x1="451.42" x2="426.88" y1="93.955" y2="145.12" gradientTransform="matrix(1.4611 0 0 1.4611 -557.72 -38.455)" gradientUnits="userSpaceOnUse"><stop id="stop1479" stop-color="#90B9D9" offset="0"/><stop id="stop1481" stop-color="#554BBA" offset="1"/></linearGradient><linearGradient id="linearGradient2147" x1="436.58" x2="451.74" y1="96.001" y2="108.43" gradientTransform="matrix(1.4611 0 0 1.4611 -557.72 -34.137)" gradientUnits="userSpaceOnUse" xlink:href="#a"/><linearGradient id="linearGradient5197" x1="438.54" x2="455.23" y1="117.18" y2="124.38" gradientTransform="matrix(1.5947 0 0 1.5947 -617.05 -51.062)" gradientUnits="userSpaceOnUse" xlink:href="#a"/></defs><rect id="rect6884" width="182" height="182" ry="0" fill="#37394c" stop-color="#000000"/><g id="g1716" transform="matrix(1.8352 0 0 1.8352 -75.998 -148.92)" stroke-width=".54491"><rect id="rect1998" x="80.151" y="91.556" width="21.697" height="21.879" ry="1.4596" fill="url(#linearGradient2147)" stop-color="#000000"/><path id="rect1828" d="m107.41 151.77v-41.952c0-1.4858-1.1967-2.6824-2.6825-2.6824h-27.447c-1.4858 0-2.6825 1.1967-2.6825 2.6824v41.952c0 13.67 13.523 9.3797 13.523 23.423v10.594h5.7652v-10.594c0-14.043 13.523-9.7531 13.523-23.423" fill="url(#linearGradient2139)" stop-color="#000000"/><path id="path1334" d="m91 116.2-2.5153 4.3556h1.7943v22.263l-4.5798-4.3348c-0.2957-0.3689-0.50311-0.85159-0.51463-1.3481 0-2.0086-5.2e-4 -3.2015-8.82e-4 -3.6405 0.84792-0.29761 1.46-1.0971 1.46-2.0475 0-1.2023-0.97564-2.178-2.1784-2.178-1.2033 0-2.1786 0.97563-2.1786 2.178 0 0.9504 0.61169 1.7499 1.4589 2.0475l-6.01e-4 3.5979c0 0.97511 0.53498 1.9969 1.1622 2.6472-0.01854-0.0177-0.0384-0.0363 3.53e-4 8.9e-4 0.01589 0.0143 4.8585 4.5991 4.8585 4.5991 0.29527 0.36811 0.50139 0.85054 0.51324 1.3467v2.5185c-1.6637 0.33382-2.9172 1.803-2.9172 3.5654 0 2.0093 1.6288 3.6381 3.6375 3.6381 2.0093 0 3.6382-1.6288 3.6382-3.6381 0-1.7627-1.2545-3.2319-2.9197-3.5657v-2.4743c0-6e-3 3.53e-4 -0.0121 0-0.0191v-5.4728c0.01209-0.49523 0.21903-0.97704 0.51464-1.3448 0 0 4.8427-4.5843 4.8583-4.5985 0.03901-0.0367 0.01854-0.0184 3.54e-4 -3.5e-4 0.62708-0.65035 1.1617-1.6726 1.1617-2.6478l-7.86e-4 -3.4673h1.4604v-4.3568h-4.3563v4.3568h1.4585s-8.83e-4 0.91324-8.83e-4 3.5098c-0.01095 0.49662-0.21857 0.9798-0.51427 1.3486l-4.5807 4.3358v-16.818h1.7972z" fill="url(#linearGradient5197)" stroke-width=".54491"/></g></svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 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 <haze/async_usb_server.hpp>
#include <haze/common.hpp>
#include <haze/device_properties.hpp>
#include <haze/event_reactor.hpp>
#include <haze/file_system_proxy.hpp>
#include <haze/ptp.hpp>
#include <haze/ptp_object_database.hpp>
#include <haze/ptp_object_heap.hpp>
#include <haze/ptp_responder.hpp>
#include <haze/usb_session.hpp>

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 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
#define HAZE_ASSERT(expr) \
{ \
const bool __tmp_haze_assert_val = static_cast<bool>(expr); \
if (AMS_UNLIKELY(!__tmp_haze_assert_val)) { \
svcBreak(BreakReason_Assert, 0, 0); \
} \
}
#define HAZE_R_ABORT_UNLESS(res_expr) \
{ \
const auto _tmp_r_abort_rc = (res_expr); \
HAZE_ASSERT(R_SUCCEEDED(_tmp_r_abort_rc)); \
}
#define HAZE_UNREACHABLE_DEFAULT_CASE() default: HAZE_ASSERT(false)

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 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 <haze/common.hpp>
#include <haze/event_reactor.hpp>
namespace haze {
class AsyncUsbServer final {
private:
EventReactor *m_reactor;
public:
constexpr explicit AsyncUsbServer() : m_reactor() { /* ... */ }
Result Initialize(const UsbCommsInterfaceInfo *interface_info, u16 id_vendor, u16 id_product, EventReactor *reactor);
void Finalize();
private:
Result TransferPacketImpl(bool read, void *page, u32 size, u32 *out_size_transferred) const;
public:
Result ReadPacket(void *page, u32 size, u32 *out_size_transferred) const {
R_RETURN(this->TransferPacketImpl(true, page, size, out_size_transferred));
}
Result WritePacket(void *page, u32 size) const {
u32 size_transferred;
R_RETURN(this->TransferPacketImpl(false, page, size, std::addressof(size_transferred)));
}
};
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 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
#define ATMOSPHERE_OS_HORIZON
#define ATMOSPHERE_ARCH_ARM64
#define ATMOSPHERE_ARCH_ARM_V8A
#include <algorithm>
#include <cstring>
#include <bit>
#include <memory>
#include <stdint.h>
#include <stdio.h>
#include <stddef.h>
#include <switch.h>
#include <haze/results.hpp>
#include <haze/assert.hpp>
#include <vapours/literals.hpp>
#include <vapours/svc/svc_common.hpp>
#include <vapours/svc/svc_types_common.hpp>
namespace haze {
using namespace ::ams::literals;
using namespace ::ams;
using Result = ::ams::Result;
static constexpr u32 UsbBulkPacketBufferSize = 1_MB;
}

View File

@@ -0,0 +1,249 @@
/*
* Copyright (c) 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 <haze/event_reactor.hpp>
#include <haze/ptp_object_heap.hpp>
namespace haze {
class ConsoleMainLoop : public EventConsumer {
private:
static constexpr size_t FrameDelayNs = 33'333'333;
private:
EventReactor *m_reactor;
PtpObjectHeap *m_object_heap;
PadState m_pad;
Thread m_thread;
UEvent m_event;
UEvent m_cancel_event;
u32 m_last_heap_used;
u32 m_last_heap_total;
bool m_is_applet_mode;
private:
static void Run(void *arg) {
static_cast<ConsoleMainLoop *>(arg)->Run();
}
void Run() {
int idx;
while (true) {
/* Wait for up to 1 frame delay time to be cancelled. */
Waiter cancel_waiter = waiterForUEvent(std::addressof(m_cancel_event));
Result rc = waitObjects(std::addressof(idx), std::addressof(cancel_waiter), 1, FrameDelayNs);
/* Finish if we were cancelled. */
if (R_SUCCEEDED(rc)) {
break;
}
/* Otherwise, signal the console update event. */
if (svc::ResultTimedOut::Includes(rc)) {
ueventSignal(std::addressof(m_event));
}
}
}
public:
explicit ConsoleMainLoop() : m_reactor(), m_pad(), m_thread(), m_event(), m_cancel_event(), m_last_heap_used(), m_last_heap_total(), m_is_applet_mode() { /* ... */ }
Result Initialize(EventReactor *reactor, PtpObjectHeap *object_heap) {
/* Register event reactor and heap. */
m_reactor = reactor;
m_object_heap = object_heap;
/* Set cached use amounts to invalid values. */
m_last_heap_used = 0xffffffffu;
m_last_heap_total = 0xffffffffu;
/* Get whether we are launched in applet mode. */
AppletType applet_type = appletGetAppletType();
m_is_applet_mode = applet_type != AppletType_Application && applet_type != AppletType_SystemApplication;
/* Initialize events. */
ueventCreate(std::addressof(m_event), true);
ueventCreate(std::addressof(m_cancel_event), true);
/* Set up pad inputs to allow exiting the program. */
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
padInitializeAny(std::addressof(m_pad));
/* Create the delay thread with higher priority than the main thread (which runs at priority 0x2c). */
R_TRY(threadCreate(std::addressof(m_thread), ConsoleMainLoop::Run, this, nullptr, 4_KB, 0x2b, svc::IdealCoreUseProcessValue));
/* Ensure we close the thread on failure. */
ON_RESULT_FAILURE { threadClose(std::addressof(m_thread)); };
/* Connect ourselves to the event loop. */
R_UNLESS(m_reactor->AddConsumer(this, waiterForUEvent(std::addressof(m_event))), haze::ResultRegistrationFailed());
/* Start the delay thread. */
R_RETURN(threadStart(std::addressof(m_thread)));
}
void Finalize() {
/* Signal the delay thread to shut down. */
ueventSignal(std::addressof(m_cancel_event));
/* Wait for the delay thread to exit and close it. */
HAZE_R_ABORT_UNLESS(threadWaitForExit(std::addressof(m_thread)));
HAZE_R_ABORT_UNLESS(threadClose(std::addressof(m_thread)));
/* Disconnect from the event loop.*/
m_reactor->RemoveConsumer(this);
}
private:
void RedrawConsole() {
/* Get use amounts from the heap. */
u32 heap_used = m_object_heap->GetUsedSize();
u32 heap_total = m_object_heap->GetTotalSize();
u32 heap_pct = heap_total > 0 ? static_cast<u32>((heap_used * 100ul) / heap_total) : 0;
if (heap_used == m_last_heap_used && heap_total == m_last_heap_total) {
/* If usage didn't change, skip redrawing the console. */
/* This provides a substantial performance improvement in file transfer speed. */
return;
}
/* Update cached use amounts. */
m_last_heap_used = heap_used;
m_last_heap_total = heap_total;
/* Determine units to use for printing to the console. */
const char *used_unit = "B";
if (heap_used >= 1_KB) { heap_used >>= 10; used_unit = "KiB"; }
if (heap_used >= (1_MB / 1_KB)) { heap_used >>= 10; used_unit = "MiB"; }
const char *total_unit = "B";
if (heap_total >= 1_KB) { heap_total >>= 10; total_unit = "KiB"; }
if (heap_total >= (1_MB / 1_KB)) { heap_total >>= 10; total_unit = "MiB"; }
/* Draw the console UI. */
consoleClear();
printf("USB File Transfer\n\n");
printf("Connect console to computer. Press [+] to exit.\n");
printf("Heap used: %u %s / %u %s (%u%%)\n", heap_used, used_unit, heap_total, total_unit, heap_pct);
if (m_is_applet_mode) {
/* Print "Applet Mode" in red text. */
printf("\n" CONSOLE_ESC(38;5;196m) "Applet Mode" CONSOLE_ESC(0m) "\n");
}
consoleUpdate(nullptr);
}
protected:
void ProcessEvent() override {
/* Update the console. */
this->RedrawConsole();
/* Check buttons. */
padUpdate(std::addressof(m_pad));
/* If the plus button is held, request immediate exit. */
if (padGetButtonsDown(std::addressof(m_pad)) & HidNpadButton_Plus) {
m_reactor->SetResult(haze::ResultStopRequested());
}
/* Pump applet events, and check if exit was requested. */
if (!appletMainLoop()) {
m_reactor->SetResult(haze::ResultStopRequested());
}
/* Check if focus was lost. */
if (appletGetFocusState() == AppletFocusState_Background) {
m_reactor->SetResult(haze::ResultFocusLost());
}
}
private:
static bool SuspendAndWaitForFocus() {
/* Enable suspension with resume notification. */
appletSetFocusHandlingMode(AppletFocusHandlingMode_SuspendHomeSleepNotify);
/* Pump applet events. */
while (appletMainLoop()) {
/* Check if focus was regained. */
if (appletGetFocusState() != AppletFocusState_Background) {
return true;
}
}
/* Exit was requested. */
return false;
}
public:
static void RunApplication() {
/* Declare the object heap, to hold the database for an active session. */
PtpObjectHeap ptp_object_heap;
/* Declare the event reactor, and components which use it. */
EventReactor event_reactor;
PtpResponder ptp_responder;
ConsoleMainLoop console_main_loop;
/* Initialize the console.*/
consoleInit(nullptr);
while (true) {
/* Disable suspension. */
appletSetFocusHandlingMode(AppletFocusHandlingMode_NoSuspend);
/* Declare result from serving to use. */
Result rc;
{
/* Ensure we don't go to sleep while transferring files. */
appletSetAutoSleepDisabled(true);
/* Clear the event reactor. */
event_reactor.SetResult(ResultSuccess());
/* Configure the PTP responder and console main loop. */
ptp_responder.Initialize(std::addressof(event_reactor), std::addressof(ptp_object_heap));
console_main_loop.Initialize(std::addressof(event_reactor), std::addressof(ptp_object_heap));
/* Ensure we maintain a clean state on exit. */
ON_SCOPE_EXIT {
/* Finalize the console main loop and PTP responder. */
console_main_loop.Finalize();
ptp_responder.Finalize();
/* Restore auto sleep setting. */
appletSetAutoSleepDisabled(false);
};
/* Begin processing requests. */
rc = ptp_responder.LoopProcess();
}
/* If focus was lost, try to pump the applet main loop until we receive focus again. */
if (haze::ResultFocusLost::Includes(rc) && SuspendAndWaitForFocus()) {
continue;
}
/* Otherwise, enable suspension and finish. */
appletSetFocusHandlingMode(AppletFocusHandlingMode_SuspendHomeSleep);
break;
}
/* Finalize the console. */
consoleExit(nullptr);
}
};
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
namespace haze {
Result LoadDeviceProperties();
const char *GetSerialNumber();
const char *GetFirmwareVersion();
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 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 <haze/common.hpp>
namespace haze {
class EventConsumer {
public:
virtual ~EventConsumer() = default;
virtual void ProcessEvent() = 0;
};
class EventReactor {
private:
EventConsumer *m_consumers[svc::ArgumentHandleCountMax];
Waiter m_waiters[svc::ArgumentHandleCountMax];
s32 m_num_wait_objects;
Result m_result;
public:
constexpr explicit EventReactor() : m_consumers(), m_waiters(), m_num_wait_objects(), m_result(ResultSuccess()) { /* ... */ }
bool AddConsumer(EventConsumer *consumer, Waiter waiter);
void RemoveConsumer(EventConsumer *consumer);
public:
void SetResult(Result r) { m_result = r; }
Result GetResult() const { return m_result; }
public:
template <typename... Args> requires (sizeof...(Args) > 0)
Result WaitFor(s32 *out_arg_waiter, Args &&... arg_waiters) {
const Waiter arg_waiter_array[] = { arg_waiters... };
return this->WaitForImpl(out_arg_waiter, arg_waiter_array, sizeof...(Args));
}
private:
Result WaitForImpl(s32 *out_arg_waiter, const Waiter *arg_waiters, s32 num_arg_waiters);
};
}

View File

@@ -0,0 +1,131 @@
/*
* Copyright (c) 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 <haze/common.hpp>
#include <haze/event_reactor.hpp>
namespace haze {
class FileSystemProxy final {
private:
EventReactor *m_reactor;
FsFileSystem *m_filesystem;
public:
constexpr explicit FileSystemProxy() : m_reactor(), m_filesystem() { /* ... */ }
void Initialize(EventReactor *reactor, FsFileSystem *fs) {
HAZE_ASSERT(fs != nullptr);
m_reactor = reactor;
m_filesystem = fs;
}
void Finalize() {
m_reactor = nullptr;
m_filesystem = nullptr;
}
private:
template <typename F, typename... Args>
Result ForwardResult(F func, Args &&... args) {
/* Perform the method call, collecting its result. */
const Result rc = func(std::forward<Args>(args)...);
/* If the event loop was stopped, return that here. */
R_TRY(m_reactor->GetResult());
/* Otherwise, return the call result. */
R_RETURN(rc);
}
public:
Result GetTotalSpace(const char *path, s64 *out) {
R_RETURN(this->ForwardResult(fsFsGetTotalSpace, m_filesystem, path, out));
}
Result GetFreeSpace(const char *path, s64 *out) {
R_RETURN(this->ForwardResult(fsFsGetFreeSpace, m_filesystem, path, out));
}
Result GetEntryType(const char *path, FsDirEntryType *out_entry_type) {
R_RETURN(this->ForwardResult(fsFsGetEntryType, m_filesystem, path, out_entry_type));
}
Result CreateFile(const char* path, s64 size, u32 option) {
R_RETURN(this->ForwardResult(fsFsCreateFile, m_filesystem, path, size, option));
}
Result DeleteFile(const char* path) {
R_RETURN(this->ForwardResult(fsFsDeleteFile, m_filesystem, path));
}
Result RenameFile(const char *old_path, const char *new_path) {
R_RETURN(this->ForwardResult(fsFsRenameFile, m_filesystem, old_path, new_path));
}
Result OpenFile(const char *path, u32 mode, FsFile *out_file) {
R_RETURN(this->ForwardResult(fsFsOpenFile, m_filesystem, path, mode, out_file));
}
Result GetFileSize(FsFile *file, s64 *out_size) {
R_RETURN(this->ForwardResult(fsFileGetSize, file, out_size));
}
Result SetFileSize(FsFile *file, s64 size) {
R_RETURN(this->ForwardResult(fsFileSetSize, file, size));
}
Result ReadFile(FsFile *file, s64 off, void *buf, u64 read_size, u32 option, u64 *out_bytes_read) {
R_RETURN(this->ForwardResult(fsFileRead, file, off, buf, read_size, option, out_bytes_read));
}
Result WriteFile(FsFile *file, s64 off, const void *buf, u64 write_size, u32 option) {
R_RETURN(this->ForwardResult(fsFileWrite, file, off, buf, write_size, option));
}
void CloseFile(FsFile *file) {
fsFileClose(file);
}
Result CreateDirectory(const char* path) {
R_RETURN(this->ForwardResult(fsFsCreateDirectory, m_filesystem, path));
}
Result DeleteDirectoryRecursively(const char* path) {
R_RETURN(this->ForwardResult(fsFsDeleteDirectoryRecursively, m_filesystem, path));
}
Result RenameDirectory(const char *old_path, const char *new_path) {
R_RETURN(this->ForwardResult(fsFsRenameDirectory, m_filesystem, old_path, new_path));
}
Result OpenDirectory(const char *path, u32 mode, FsDir *out_dir) {
R_RETURN(this->ForwardResult(fsFsOpenDirectory, m_filesystem, path, mode, out_dir));
}
Result ReadDirectory(FsDir *d, s64 *out_total_entries, size_t max_entries, FsDirectoryEntry *buf) {
R_RETURN(this->ForwardResult(fsDirRead, d, out_total_entries, max_entries, buf));
}
Result GetDirectoryEntryCount(FsDir *d, s64 *out_count) {
R_RETURN(this->ForwardResult(fsDirGetEntryCount, d, out_count));
}
void CloseDirectory(FsDir *d) {
fsDirClose(d);
}
};
}

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