Compare commits

...

14 Commits

Author SHA1 Message Date
Michael Scire
79d7e9197c dmnt: revise per WerWolv's feedback. 2020-04-16 23:16:33 -07:00
Michael Scire
11b8a92458 dmnt: implement break/continue, static reg commands 2020-04-16 21:16:55 -07:00
Michael Scire
332dbdd497 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "c1fe12fc"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "c1fe12fc"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2020-04-16 20:06:20 -07:00
SciresM
98cc051387 pgl: Reimplement the pgl sysmodule (#896)
* pgl: add skeleton folder to stratosphere

* pgl: Add service interface for IShellInterface

* pgl: begin skeletoning shell service, implement two commands.

* pgl: Implement three more commands.

* pgl: implement bool tracking commands

* pgl: Implement TriggerApplicationSnapShotDumper

* pgl: implement InitializeProcessControlTask

* pgl: Implement pgl::srv::Initialize

* pgl: Implement main()

* pgl: Implement (Get)ShellEventObserver

* pgl: implement LaunchProgramFromHost, GetHostContentMetaInfo

* pgl: Implement ProcessControlTask

* settings: fix duplicate object name

* pgl: fix minor bugs in impl
2020-04-16 19:55:47 -07:00
Michael Scire
f2944d36ba kern: amend syntax 2020-04-16 18:00:42 -07:00
Michael Scire
46d79387e8 mesosphere: implement KMemoryBlockManager::UpdateLock 2020-04-16 17:58:51 -07:00
Michael Scire
0bb2c0a04f licensing: update exemptions (approved by contributors). 2020-04-16 17:33:04 -07:00
Michael Scire
eca2b453ae pgl: update with client C++ bindings 2020-04-15 20:07:20 -07:00
Michael Scire
e14dc18bd3 pgl: skeleton api 2020-04-15 17:37:11 -07:00
Michael Scire
c7743c6098 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "96825c75"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "96825c75"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2020-04-15 11:55:50 -07:00
Michael Scire
d81a3bdc36 bump version to 0.11.1 2020-04-15 01:34:35 -07:00
Michael Scire
32e5283ac2 bump version to 0.11.1 2020-04-15 01:30:12 -07:00
Michael Scire
1d9a4f47fd exosphere: set cpuactlr to guarantee it holds non reset value 2020-04-15 01:26:28 -07:00
Michael Scire
3f5f9b60ea exosphere: ... 2020-04-15 00:14:36 -07:00
68 changed files with 3148 additions and 159 deletions

View File

@@ -27,7 +27,7 @@ This software is licensed under the terms of the GPLv2, with exemptions for spec
You can find a copy of the license in the [LICENSE file](LICENSE).
Exemptions:
* The [yuzu emulator project](https://github.com/yuzu-emu/yuzu) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the Atmosphère project as GPLv2 or later.
* The [yuzu Nintendo Switch emulator](https://github.com/yuzu-emu/yuzu) and the [Ryujinx Team and Contributors](https://github.com/orgs/Ryujinx) are exempt from GPLv2 licensing. They are permitted, each at their individual discretion, to instead license any source code authored for the Atmosphère project as either GPLv2 or later or the [MIT license](https://github.com/Atmosphere-NX/Atmosphere/blob/master/docs/licensing_exemptions/MIT_LICENSE). In doing so, they may alter, supplement, or entirely remove the copyright notice for each file they choose to relicense. Neither the Atmosphère project nor its individual contributors shall assert their moral rights against any of the aforementioned projects.
* [Nintendo](https://github.com/Nintendo) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the Atmosphère project under the Zero-Clause BSD license.
Credits

View File

@@ -1,4 +1,11 @@
# Changelog
## 0.11.1
+ A bug was fixed that could cause owls to flicker under certain circumstances.
+ For those interested in technical details, in 10.0.0 kernelldr/kernel no longer set cpuactlr_el1, assuming that it was set correctly by the secure monitor.
+ However, exosphere did not set cpuactlr_el1. This meant that the register held the reset value going into boot.
+ This caused a variety of highly erratic symptoms, including causing basically any game to crash seemingly randomly.
+ A number of other major inaccuracies in exosphere were corrected.
+ General system stability improvements to enhance the user's experience.
## 0.11.0
+ Support was added for 10.0.0.
+ Exosphere has been updated to reflect the new key import semantics in 10.0.0.

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018-2020 Atmosphère-NX
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

32
exosphere/src/mc0.h Normal file
View File

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

32
exosphere/src/mc1.h Normal file
View File

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

View File

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

View File

@@ -25,6 +25,8 @@
#include "synchronization.h"
#include "masterkey.h"
#include "mc.h"
#include "mc0.h"
#include "mc1.h"
#include "memory_map.h"
#include "pmc.h"
#include "randomcache.h"
@@ -617,7 +619,7 @@ uint32_t smc_read_write_register(smc_args_t *args) {
}
/* Check for PMC registers. */
if (0x7000E400 <= address && address <= 0x7000EFFF) {
const uint8_t pmc_whitelist[0x28] = {
static const uint8_t pmc_whitelist[0x28] = {
0xB9, 0xF9, 0x07, 0x00, 0x00, 0x00, 0x80, 0x03,
0x00, 0x00, 0x00, 0x17, 0x00, 0xC4, 0x07, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x00,
@@ -633,39 +635,83 @@ uint32_t smc_read_write_register(smc_args_t *args) {
} else {
return 2;
}
} else if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400 && MMIO_GET_DEVICE_PA(MMIO_DEVID_MC) <= address &&
address < MMIO_GET_DEVICE_PA(MMIO_DEVID_MC) + MMIO_GET_DEVICE_SIZE(MMIO_DEVID_MC)) {
/* Memory Controller RW supported only on 4.0.0+ */
const uint8_t mc_whitelist[0x68] = {
0x9F, 0x31, 0x30, 0x00, 0xF0, 0xFF, 0xF7, 0x01,
0xCD, 0xFE, 0xC0, 0xFE, 0x00, 0x00, 0x00, 0x00,
0x03, 0x40, 0x73, 0x3E, 0x2F, 0x00, 0x00, 0x6E,
0x30, 0x05, 0x06, 0xB0, 0x71, 0xC8, 0x43, 0x04,
0x80, 0x1F, 0x08, 0x80, 0x03, 0x00, 0x0E, 0x00,
0x08, 0x00, 0xE0, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x30, 0xF0, 0x03, 0x03, 0x30,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x31, 0x00, 0x40, 0x00, 0x00,
0x00, 0x03, 0x00, 0x00, 0xE4, 0xFF, 0xFF, 0x01,
0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xFE, 0x0F,
0x01, 0x00, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00
};
uint32_t offset = (uint32_t)(address - 0x70019000);
uint32_t wl_ind = (offset >> 5);
/* If address is whitelisted, allow write. */
if (wl_ind < sizeof(mc_whitelist) && (mc_whitelist[wl_ind] & (1 << ((offset >> 2) & 0x7)))) {
p_mmio = (volatile uint32_t *)(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_MC) + offset);
} else {
/* These addresses are not allowed by the whitelist. */
/* They correspond to SMMU DISABLE for the BPMP, and for APB-DMA. */
/* However, smcReadWriteRegister returns 0 for these addresses despite not actually performing the write. */
/* This is "probably" to fuck with hackers who got access to smcReadWriteRegister and are trying to get */
/* control of the BPMP for jamais vu etc., since there's no other reason to return 0 despite failure. */
if (address == 0x7001923C || address == 0x70019298) {
return 0;
} else {
if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_500) {
static const uint8_t mc_whitelist_5x[0xD00/(sizeof(uint32_t) * 8)] = {
0x9F, 0x31, 0x30, 0x00, 0xF0, 0xFF, 0xF7, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x40, 0x73, 0x3E, 0x2F, 0x00, 0x00, 0x6E,
0x30, 0x05, 0x06, 0xB0, 0x71, 0xC8, 0x43, 0x04,
0x80, 0xFF, 0x08, 0x80, 0x03, 0x38, 0x8E, 0x1F,
0xC8, 0xFF, 0xFF, 0x00, 0x0E, 0x00, 0x00, 0x00,
0xF0, 0x1F, 0x00, 0x30, 0xF0, 0x03, 0x03, 0x30,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x31, 0x00, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xE4, 0xFF, 0xFF, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x0F,
0x01, 0x00, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00
};
static const uint8_t mc01_whitelist_5x[0xC00/(sizeof(uint32_t) * 8)] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xCD, 0xFE, 0xC0, 0xFE, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00,
};
static const struct {
uint32_t phys_addr;
uint32_t size;
uint64_t virt_addr;
const uint8_t *whitelist;
} register_whitelists[3] = {
{ MMIO_GET_DEVICE_PA(MMIO_DEVID_MC), sizeof(mc_whitelist_5x) * (sizeof(uint32_t) * 8), MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_MC), mc_whitelist_5x },
{ MMIO_GET_DEVICE_PA(MMIO_DEVID_MC0), sizeof(mc01_whitelist_5x) * (sizeof(uint32_t) * 8), MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_MC0), mc01_whitelist_5x },
{ MMIO_GET_DEVICE_PA(MMIO_DEVID_MC1), sizeof(mc01_whitelist_5x) * (sizeof(uint32_t) * 8), MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_MC1), mc01_whitelist_5x },
};
for (unsigned int which = 0; which < 3; which++) {
if (register_whitelists[which].phys_addr <= address && address < register_whitelists[which].phys_addr + register_whitelists[which].size) {
uint32_t offset = (uint32_t)(address - register_whitelists[which].phys_addr);
uint32_t wl_ind = (offset >> 5);
/* If address is whitelisted, allow write. */
if (register_whitelists[which].whitelist[wl_ind] & (1 << ((offset >> 2) & 0x7))) {
p_mmio = (volatile uint32_t *)(register_whitelists[which].virt_addr + offset);
}
break;
}
}
} else if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400) {
if (MMIO_GET_DEVICE_PA(MMIO_DEVID_MC) <= address && address < MMIO_GET_DEVICE_PA(MMIO_DEVID_MC) + 0xD00) {
/* Memory Controller RW supported only on 4.0.0+ */
static const uint8_t mc_whitelist[0x68] = {
0x9F, 0x31, 0x30, 0x00, 0xF0, 0xFF, 0xF7, 0x01,
0xCD, 0xFE, 0xC0, 0xFE, 0x00, 0x00, 0x00, 0x00,
0x03, 0x40, 0x73, 0x3E, 0x2F, 0x00, 0x00, 0x6E,
0x30, 0x05, 0x06, 0xB0, 0x71, 0xC8, 0x43, 0x04,
0x80, 0x1F, 0x08, 0x80, 0x03, 0x00, 0x0E, 0x00,
0x08, 0x00, 0xE0, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x30, 0xF0, 0x03, 0x03, 0x30,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x31, 0x00, 0x40, 0x00, 0x00,
0x00, 0x03, 0x00, 0x00, 0xE4, 0xFF, 0xFF, 0x01,
0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xFE, 0x0F,
0x01, 0x00, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00
};
uint32_t offset = (uint32_t)(address - MMIO_GET_DEVICE_PA(MMIO_DEVID_MC));
uint32_t wl_ind = (offset >> 5);
/* If address is whitelisted, allow write. */
if (mc_whitelist[wl_ind] & (1 << ((offset >> 2) & 0x7))) {
p_mmio = (volatile uint32_t *)(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_MC) + offset);
}
}
return 2;
}
}
@@ -684,9 +730,16 @@ uint32_t smc_read_write_register(smc_args_t *args) {
/* Return old value. */
args->X[1] = old_value;
return 0;
} else if (exosphere_get_target_firmware() >= ATMOSPHERE_TARGET_FIRMWARE_400 && (address == 0x7001923C || address == 0x70019298)) {
/* These addresses are not allowed by the whitelist. */
/* They correspond to SMMU DISABLE for the BPMP, and for APB-DMA. */
/* However, smcReadWriteRegister returns 0 for these addresses despite not actually performing the write. */
/* This is "probably" to fuck with hackers who got access to smcReadWriteRegister and are trying to get */
/* control of the BPMP for jamais vu etc., since there's no other reason to return 0 despite failure. */
return 0;
} else {
return 2;
}
return 2;
}

View File

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

View File

@@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
branch = master
commit = da6eac986d7f67ca3dcc3a8df0c191ffbea4686f
parent = eb48e7cc5943bd594b3166cb888d3dadb0474107
commit = c1fe12fcbd661ae47673af5f224804c92eebdbb5
parent = 98cc051387515977dcdede8ab0f1b09077f2f24b
method = merge
cmdver = 0.4.1

View File

@@ -10,7 +10,7 @@ This software is licensed under the terms of the GPLv2, with exemptions for spec
You can find a copy of the license in the [LICENSE file](LICENSE).
Exemptions:
* The [yuzu emulator project](https://github.com/yuzu-emu/yuzu) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the atmosphere-libs project as GPLv2 or later.
* The [yuzu Nintendo Switch emulator](https://github.com/yuzu-emu/yuzu) and the [Ryujinx Team and Contributors](https://github.com/orgs/Ryujinx) are exempt from GPLv2 licensing. They are permitted, each at their individual discretion, to instead license any source code authored for the atmosphere-libs project as either GPLv2 or later or the [MIT license](https://github.com/Atmosphere-NX/Atmosphere/blob/master/docs/licensing_exemptions/MIT_LICENSE). In doing so, they may alter, supplement, or entirely remove the copyright notice for each file they choose to relicense. Neither the Atmosphère project nor its individual contributors shall assert their moral rights against any of the aforementioned projects.
* [Nintendo](https://github.com/Nintendo) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the atmosphere-libs project under the Zero-Clause BSD license.
Credits

View File

@@ -10,7 +10,7 @@ This software is licensed under the terms of the GPLv2, with exemptions for spec
You can find a copy of the license in the [LICENSE file](LICENSE).
Exemptions:
* The [yuzu emulator project](https://github.com/yuzu-emu/yuzu) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the libmesosphere project as GPLv2 or later.
* The [yuzu Nintendo Switch emulator](https://github.com/yuzu-emu/yuzu) and the [Ryujinx Team and Contributors](https://github.com/orgs/Ryujinx) are exempt from GPLv2 licensing. They are permitted, each at their individual discretion, to instead license any source code authored for the libmesosphere project as either GPLv2 or later or the [MIT license](https://github.com/Atmosphere-NX/Atmosphere/blob/master/docs/licensing_exemptions/MIT_LICENSE). In doing so, they may alter, supplement, or entirely remove the copyright notice for each file they choose to relicense. Neither the Atmosphère project nor its individual contributors shall assert their moral rights against any of the aforementioned projects.
* [Nintendo](https://github.com/Nintendo) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the libmesosphere project under the Zero-Clause BSD license.
Credits

View File

@@ -94,6 +94,7 @@ namespace ams::kern {
KProcessAddress FindFreeArea(KProcessAddress region_start, size_t region_num_pages, size_t num_pages, size_t alignment, size_t offset, size_t guard_pages) const;
void Update(KMemoryBlockManagerUpdateAllocator *allocator, KProcessAddress address, size_t num_pages, KMemoryState state, KMemoryPermission perm, KMemoryAttribute attr);
void UpdateLock(KMemoryBlockManagerUpdateAllocator *allocator, KProcessAddress address, size_t num_pages, void (KMemoryBlock::*lock_func)(KMemoryPermission new_perm), KMemoryPermission perm);
iterator FindIterator(KProcessAddress address) const {
return this->memory_block_tree.find(KMemoryBlock(address, 1, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None));

View File

@@ -197,6 +197,7 @@ namespace ams::kern {
cur_address += cur_info.GetSize();
remaining_pages -= cur_info.GetNumPages();
}
it++;
}
/* Find the iterator now that we've updated. */
@@ -227,6 +228,86 @@ namespace ams::kern {
}
}
void KMemoryBlockManager::UpdateLock(KMemoryBlockManagerUpdateAllocator *allocator, KProcessAddress address, size_t num_pages, void (KMemoryBlock::*lock_func)(KMemoryPermission new_perm), KMemoryPermission perm) {
/* Ensure for auditing that we never end up with an invalid tree. */
KScopedMemoryBlockManagerAuditor auditor(this);
MESOSPHERE_ASSERT(util::IsAligned(GetInteger(address), PageSize));
KProcessAddress cur_address = address;
size_t remaining_pages = num_pages;
iterator it = this->FindIterator(address);
iterator prev = it, next = it;
bool check_coalesce_prev = false, check_coalesce_next = false;
while (remaining_pages > 0) {
const size_t remaining_size = remaining_pages * PageSize;
KMemoryInfo cur_info = it->GetMemoryInfo();
/* If we need to, create a new block before and insert it. */
if (cur_info.address != GetInteger(cur_address)) {
KMemoryBlock *new_block = allocator->Allocate();
it->Split(new_block, cur_address);
it = this->memory_block_tree.insert(*new_block);
it++;
cur_info = it->GetMemoryInfo();
cur_address = cur_info.GetAddress();
} else if (cur_address == address && cur_address != this->start_address) {
/* If there's a previous, we should check for coalescing. */
check_coalesce_prev = true;
prev--;
} else if (cur_info.GetSize() > remaining_size) {
/* If we need to, create a new block after and insert it. */
KMemoryBlock *new_block = allocator->Allocate();
it->Split(new_block, cur_address + remaining_size);
it = this->memory_block_tree.insert(*new_block);
cur_info = it->GetMemoryInfo();
} else if (cur_info.GetSize() == remaining_size) {
/* Otherwise if we can map precisely, we may need to check for coalescing against next block. */
next = it;
++next;
if (next != this->memory_block_tree.end()) {
check_coalesce_next = true;
}
}
/* Call the locked update function. */
(std::addressof(*it)->*lock_func)(perm);
cur_address += cur_info.GetSize();
remaining_pages -= cur_info.GetNumPages();
it++;
}
/* If we should try to coalesce prev, do so. */
if (check_coalesce_prev) {
it = prev;
it++;
if (prev->HasSameProperties(*it)) {
KMemoryBlock *block = std::addressof(*it);
const size_t pages = it->GetNumPages();
this->memory_block_tree.erase(it);
allocator->Free(block);
prev->Add(pages);
}
}
/* If we should try to coalesce next, do so. */
if (check_coalesce_next) {
it = next;
it--;
if (it->HasSameProperties(*next)) {
KMemoryBlock *block = std::addressof(*next);
const size_t pages = next->GetNumPages();
this->memory_block_tree.erase(next);
allocator->Free(block);
it->Add(pages);
}
}
}
/* Debug. */
bool KMemoryBlockManager::CheckState() const {
/* If we fail, we should dump blocks. */

View File

@@ -14,7 +14,7 @@ This software is licensed under the terms of the GPLv2, with exemptions for spec
You can find a copy of the license in the [LICENSE file](LICENSE).
Exemptions:
* The [yuzu emulator project](https://github.com/yuzu-emu/yuzu) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the libstratosphere project as GPLv2 or later.
* The [yuzu Nintendo Switch emulator](https://github.com/yuzu-emu/yuzu) and the [Ryujinx Team and Contributors](https://github.com/orgs/Ryujinx) are exempt from GPLv2 licensing. They are permitted, each at their individual discretion, to instead license any source code authored for the libstratosphere project as either GPLv2 or later or the [MIT license](https://github.com/Atmosphere-NX/Atmosphere/blob/master/docs/licensing_exemptions/MIT_LICENSE). In doing so, they may alter, supplement, or entirely remove the copyright notice for each file they choose to relicense. Neither the Atmosphère project nor its individual contributors shall assert their moral rights against any of the aforementioned projects.
* [Nintendo](https://github.com/Nintendo) is exempt from GPLv2 licensing and may (at its option) instead license any source code authored for the libstratosphere project under the Zero-Clause BSD license.
Credits

View File

@@ -51,6 +51,7 @@
#include <stratosphere/ncm.hpp>
#include <stratosphere/nim.hpp>
#include <stratosphere/patcher.hpp>
#include <stratosphere/pgl.hpp>
#include <stratosphere/psc.hpp>
#include <stratosphere/pm.hpp>
#include <stratosphere/reg.hpp>

View File

@@ -16,5 +16,6 @@
#pragma once
#include "ldr/ldr_types.hpp"
#include "ldr/ldr_pm_api.hpp"
#include <stratosphere/ldr/ldr_types.hpp>
#include <stratosphere/ldr/ldr_shell_api.hpp>
#include <stratosphere/ldr/ldr_pm_api.hpp>

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/ncm/ncm_ids.hpp>
#include <stratosphere/ldr/ldr_types.hpp>
namespace ams::ldr {
/* Shell API. */
Result InitializeForShell();
Result FinalizeForShell();
Result SetProgramArgument(ncm::ProgramId program_id, const void *arg, size_t size);
Result FlushArguments();
}

View File

@@ -402,6 +402,34 @@ namespace ams::ncm {
return true;
}
struct SystemDebugAppletId {
u64 value;
constexpr operator ProgramId() const {
return { this->value };
}
static const SystemDebugAppletId Start;
static const SystemDebugAppletId SnapShotDumper;
static const SystemDebugAppletId End;
};
inline constexpr const SystemDebugAppletId SystemDebugAppletId::Start = { 0x0100000000002000ul };
inline constexpr const SystemDebugAppletId SystemDebugAppletId::SnapShotDumper = { 0x0100000000002071ul };
inline constexpr const SystemDebugAppletId SystemDebugAppletId::End = { 0x0100000000002FFFul };
inline constexpr bool IsSystemDebugAppletId(const ProgramId &program_id) {
return SystemDebugAppletId::Start <= program_id && program_id <= SystemDebugAppletId::End;
}
inline constexpr bool IsSystemDebugAppletId(const SystemDebugAppletId &program_id) {
return true;
}
struct LibraryAppletId {
u64 value;

View File

@@ -27,6 +27,7 @@
#include <stratosphere/os/os_random.hpp>
#include <stratosphere/os/os_mutex.hpp>
#include <stratosphere/os/os_condition_variable.hpp>
#include <stratosphere/os/os_sdk_mutex.hpp>
#include <stratosphere/os/os_rw_lock.hpp>
#include <stratosphere/os/os_semaphore.hpp>
#include <stratosphere/os/os_event.hpp>

View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/impl/os_internal_critical_section.hpp>
namespace ams::os {
class SdkConditionVariable;
struct SdkMutexType {
union {
s32 _arr[sizeof(impl::InternalCriticalSectionStorage) / sizeof(s32)];
impl::InternalCriticalSectionStorage _storage;
};
};
static_assert(std::is_trivial<SdkMutexType>::value);
void InitializeSdkMutex(SdkMutexType *mutex);
void LockSdkMutex(SdkMutexType *mutex);
bool TryLockSdkMutex(SdkMutexType *mutex);
void UnlockSdkMutex(SdkMutexType *mutex);
bool IsSdkMutexLockedByCurrentThread(const SdkMutexType *mutex);
class SdkMutex {
private:
friend class SdkConditionVariable;
private:
SdkMutexType mutex;
public:
constexpr SdkMutex() : mutex{{0}} { /* ... */ }
ALWAYS_INLINE void Lock() { return os::LockSdkMutex(std::addressof(this->mutex)); }
ALWAYS_INLINE bool TryLock() { return os::TryLockSdkMutex(std::addressof(this->mutex)); }
ALWAYS_INLINE void Unlock() { return os::UnlockSdkMutex(std::addressof(this->mutex)); }
ALWAYS_INLINE bool IsLockedByCurrentThread() const { return os::IsSdkMutexLockedByCurrentThread(std::addressof(this->mutex)); }
ALWAYS_INLINE void lock() { return this->Lock(); }
ALWAYS_INLINE bool try_lock() { return this->TryLock(); }
ALWAYS_INLINE void unlock() { return this->Unlock(); }
};
}

View File

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

View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 2019-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/os.hpp>
#include <stratosphere/pm.hpp>
#include <stratosphere/pgl/pgl_types.hpp>
#include <stratosphere/pgl/sf/pgl_sf_i_event_observer.hpp>
namespace ams::pgl {
class EventObserver {
NON_COPYABLE(EventObserver);
private:
std::shared_ptr<pgl::sf::IEventObserver> interface;
public:
EventObserver() { /* ... */ }
explicit EventObserver(std::shared_ptr<pgl::sf::IEventObserver> intf) : interface(std::move(intf)) { /* ... */ }
EventObserver(EventObserver &&rhs) {
this->interface = std::move(rhs.interface);
}
EventObserver &operator=(EventObserver &&rhs) {
EventObserver(std::move(rhs)).Swap(*this);
return *this;
}
void Swap(EventObserver &rhs) {
std::swap(this->interface, rhs.interface);
}
public:
Result GetSystemEvent(os::SystemEventType *out) {
ams::sf::CopyHandle handle;
R_TRY(this->interface->GetProcessEventHandle(std::addressof(handle)));
os::AttachSystemEvent(out, handle.GetValue(), true, svc::InvalidHandle, false, os::EventClearMode_AutoClear);
return ResultSuccess();
}
Result GetProcessEventInfo(pm::ProcessEventInfo *out) {
return this->interface->GetProcessEventInfo(out);
}
};
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/pgl/pgl_types.hpp>
#include <stratosphere/pgl/pgl_event_observer.hpp>
namespace ams::pgl {
Result Initialize();
void Finalize();
Result LaunchProgram(os::ProcessId *out, const ncm::ProgramLocation &loc, u32 process_flags, u8 pgl_flags);
Result TerminateProcess(os::ProcessId process_id);
Result LaunchProgramFromHost(os::ProcessId *out, const char *content_path, u32 process_flags);
Result GetHostContentMetaInfo(pgl::ContentMetaInfo *out, const char *content_path);
Result GetApplicationProcessId(os::ProcessId *out);
Result BoostSystemMemoryResourceLimit(u64 size);
Result IsProcessTracked(bool *out, os::ProcessId process_id);
Result EnableApplicationCrashReport(bool enabled);
Result IsApplicationCrashReportEnabled(bool *out);
Result EnableApplicationAllThreadDumpOnCrash(bool enabled);
Result TriggerApplicationSnapShotDumper(const char *arg, SnapShotDumpType dump_type);
Result GetEventObserver(pgl::EventObserver *out);
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os.hpp>
#include <stratosphere/pm.hpp>
#include <stratosphere/ncm.hpp>
namespace ams::pgl {
enum LaunchFlags : u8 {
LaunchFlags_None = 0,
LaunchFlags_EnableDetailedCrashReport = (1 << 0),
LaunchFlags_EnableCrashReportScreenShotForProduction = (1 << 1),
LaunchFlags_EnableCrashReportScreenShotForDevelop = (1 << 2),
};
enum class SnapShotDumpType : u32 {
None = 0,
Auto = 1,
Full = 2,
};
/* TODO: Is this really nn::ncm::Content<Something>Info? */
struct ContentMetaInfo {
u64 id;
u32 version;
ncm::ContentType content_type;
u8 id_offset;
u8 reserved_0E[2];
static constexpr ContentMetaInfo Make(u64 id, u32 version, ncm::ContentType content_type, u8 id_offset) {
return {
.id = id,
.version = version,
.content_type = content_type,
.id_offset = id_offset,
};
}
};
static_assert(sizeof(ContentMetaInfo) == 0x10 && std::is_pod<ContentMetaInfo>::value);
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os.hpp>
#include <stratosphere/pm.hpp>
#include <stratosphere/pgl/pgl_types.hpp>
namespace ams::pgl::sf {
class IEventObserver : public ams::sf::IServiceObject {
protected:
enum class CommandId {
GetProcessEventHandle = 0,
GetProcessEventInfo = 1,
};
public:
/* Actual commands. */
virtual Result GetProcessEventHandle(ams::sf::OutCopyHandle out) = 0;
virtual Result GetProcessEventInfo(ams::sf::Out<pm::ProcessEventInfo> out) = 0;
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(GetProcessEventHandle),
MAKE_SERVICE_COMMAND_META(GetProcessEventInfo),
};
};
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os.hpp>
#include <stratosphere/pm.hpp>
#include <stratosphere/pgl/pgl_types.hpp>
#include <stratosphere/pgl/sf/pgl_sf_i_event_observer.hpp>
namespace ams::pgl::sf {
class IShellInterface : public ams::sf::IServiceObject {
protected:
enum class CommandId {
LaunchProgram = 0,
TerminateProcess = 1,
LaunchProgramFromHost = 2,
GetHostContentMetaInfo = 4,
GetApplicationProcessId = 5,
BoostSystemMemoryResourceLimit = 6,
IsProcessTracked = 7,
EnableApplicationCrashReport = 8,
IsApplicationCrashReportEnabled = 9,
EnableApplicationAllThreadDumpOnCrash = 10,
TriggerApplicationSnapShotDumper = 12,
GetShellEventObserver = 20,
};
public:
/* Actual commands. */
virtual Result LaunchProgram(ams::sf::Out<os::ProcessId> out, const ncm::ProgramLocation &loc, u32 pm_flags, u8 pgl_flags) = 0;
virtual Result TerminateProcess(os::ProcessId process_id) = 0;
virtual Result LaunchProgramFromHost(ams::sf::Out<os::ProcessId> out, const ams::sf::InBuffer &content_path, u32 pm_flags) = 0;
virtual Result GetHostContentMetaInfo(ams::sf::Out<pgl::ContentMetaInfo> out, const ams::sf::InBuffer &content_path) = 0;
virtual Result GetApplicationProcessId(ams::sf::Out<os::ProcessId> out) = 0;
virtual Result BoostSystemMemoryResourceLimit(u64 size) = 0;
virtual Result IsProcessTracked(ams::sf::Out<bool> out, os::ProcessId process_id) = 0;
virtual Result EnableApplicationCrashReport(bool enabled) = 0;
virtual Result IsApplicationCrashReportEnabled(ams::sf::Out<bool> out) = 0;
virtual Result EnableApplicationAllThreadDumpOnCrash(bool enabled) = 0;
virtual Result TriggerApplicationSnapShotDumper(SnapShotDumpType dump_type, const ams::sf::InBuffer &arg) = 0;
virtual Result GetShellEventObserver(ams::sf::Out<std::shared_ptr<pgl::sf::IEventObserver>> out) = 0;
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(LaunchProgram),
MAKE_SERVICE_COMMAND_META(TerminateProcess),
MAKE_SERVICE_COMMAND_META(LaunchProgramFromHost),
MAKE_SERVICE_COMMAND_META(GetHostContentMetaInfo),
MAKE_SERVICE_COMMAND_META(GetApplicationProcessId),
MAKE_SERVICE_COMMAND_META(BoostSystemMemoryResourceLimit),
MAKE_SERVICE_COMMAND_META(IsProcessTracked),
MAKE_SERVICE_COMMAND_META(EnableApplicationCrashReport),
MAKE_SERVICE_COMMAND_META(IsApplicationCrashReportEnabled),
MAKE_SERVICE_COMMAND_META(EnableApplicationAllThreadDumpOnCrash),
MAKE_SERVICE_COMMAND_META(TriggerApplicationSnapShotDumper),
MAKE_SERVICE_COMMAND_META(GetShellEventObserver),
};
};
}

View File

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

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/pgl/pgl_types.hpp>
#include <stratosphere/pgl/sf/pgl_sf_i_shell_interface.hpp>
namespace ams::pgl::srv {
class ShellInterface final : public pgl::sf::IShellInterface {
NON_COPYABLE(ShellInterface);
NON_MOVEABLE(ShellInterface);
private:
MemoryResource *memory_resource;
public:
constexpr ShellInterface() : memory_resource(nullptr) { /* ... */ }
void Initialize(MemoryResource *mr) {
AMS_ASSERT(this->memory_resource == nullptr);
this->memory_resource = mr;
}
public:
/* Interface commands. */
virtual Result LaunchProgram(ams::sf::Out<os::ProcessId> out, const ncm::ProgramLocation &loc, u32 pm_flags, u8 pgl_flags) override final;
virtual Result TerminateProcess(os::ProcessId process_id) override final;
virtual Result LaunchProgramFromHost(ams::sf::Out<os::ProcessId> out, const ams::sf::InBuffer &content_path, u32 pm_flags) override final;
virtual Result GetHostContentMetaInfo(ams::sf::Out<pgl::ContentMetaInfo> out, const ams::sf::InBuffer &content_path) override final;
virtual Result GetApplicationProcessId(ams::sf::Out<os::ProcessId> out) override final;
virtual Result BoostSystemMemoryResourceLimit(u64 size) override final;
virtual Result IsProcessTracked(ams::sf::Out<bool> out, os::ProcessId process_id) override final;
virtual Result EnableApplicationCrashReport(bool enabled) override final;
virtual Result IsApplicationCrashReportEnabled(ams::sf::Out<bool> out) override final;
virtual Result EnableApplicationAllThreadDumpOnCrash(bool enabled) override final;
virtual Result TriggerApplicationSnapShotDumper(SnapShotDumpType dump_type, const ams::sf::InBuffer &arg) override final;
virtual Result GetShellEventObserver(ams::sf::Out<std::shared_ptr<pgl::sf::IEventObserver>> out) override final;
};
}

View File

@@ -23,6 +23,12 @@
namespace ams::pm::shell {
/* Shell API. */
Result LaunchProgram(os::ProcessId *out_process_id, const ncm::ProgramLocation &loc, u32 launch_flags);
Result LaunchProgram(os::ProcessId *out, const ncm::ProgramLocation &loc, u32 launch_flags);
Result TerminateProcess(os::ProcessId process_id);
Result GetProcessEventEvent(os::SystemEvent *out);
Result GetProcessEventInfo(ProcessEventInfo *out);
Result GetApplicationProcessIdForShell(os::ProcessId *out);
Result BoostSystemMemoryResourceLimit(u64 size);
Result EnableApplicationExtraThread();
}

View File

@@ -32,7 +32,66 @@ namespace ams::pm {
ResourceLimitGroup_Count,
};
using LimitableResource = ::LimitableResource;
enum LaunchFlags : u32 {
LaunchFlags_None = 0,
LaunchFlags_SignalOnExit = (1 << 0),
LaunchFlags_SignalOnStart = (1 << 1),
LaunchFlags_SignalOnException = (1 << 2),
LaunchFlags_SignalOnDebugEvent = (1 << 3),
LaunchFlags_StartSuspended = (1 << 4),
LaunchFlags_DisableAslr = (1 << 5),
};
enum LaunchFlagsDeprecated : u32 {
LaunchFlagsDeprecated_None = 0,
LaunchFlagsDeprecated_SignalOnExit = (1 << 0),
LaunchFlagsDeprecated_StartSuspended = (1 << 1),
LaunchFlagsDeprecated_SignalOnException = (1 << 2),
LaunchFlagsDeprecated_DisableAslr = (1 << 3),
LaunchFlagsDeprecated_SignalOnDebugEvent = (1 << 4),
LaunchFlagsDeprecated_SignalOnStart = (1 << 5),
};
constexpr inline u32 LaunchFlagsMask = (1 << 6) - 1;
enum class ProcessEvent : u32 {
None = 0,
Exited = 1,
Started = 2,
Exception = 3,
DebugRunning = 4,
DebugBreak = 5,
};
enum class ProcessEventDeprecated : u32 {
None = 0,
Exception = 1,
Exited = 2,
DebugRunning = 3,
DebugBreak = 4,
Started = 5,
};
inline u32 GetProcessEventValue(ProcessEvent event) {
if (hos::GetVersion() >= hos::Version_5_0_0) {
return static_cast<u32>(event);
}
switch (event) {
case ProcessEvent::None:
return static_cast<u32>(ProcessEventDeprecated::None);
case ProcessEvent::Exited:
return static_cast<u32>(ProcessEventDeprecated::Exited);
case ProcessEvent::Started:
return static_cast<u32>(ProcessEventDeprecated::Started);
case ProcessEvent::Exception:
return static_cast<u32>(ProcessEventDeprecated::Exception);
case ProcessEvent::DebugRunning:
return static_cast<u32>(ProcessEventDeprecated::DebugRunning);
case ProcessEvent::DebugBreak:
return static_cast<u32>(ProcessEventDeprecated::DebugBreak);
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
struct ProcessEventInfo {
u32 event;

View File

@@ -19,6 +19,7 @@
#include "settings/settings_types.hpp"
#include "settings/settings_fwdbg_types.hpp"
#include "settings/settings_fwdbg_api.hpp"
#include "settings/system/settings_error_report.hpp"
#include "settings/system/settings_firmware_version.hpp"
#include "settings/system/settings_product_model.hpp"
#include "settings/system/settings_region.hpp"

View File

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

View File

@@ -16,15 +16,16 @@
#pragma once
#include "sf/sf_common.hpp"
#include "sf/sf_mem_utility.hpp"
#include "sf/sf_service_object.hpp"
#include "sf/hipc/sf_hipc_server_session_manager.hpp"
#include <stratosphere/sf/sf_common.hpp>
#include <stratosphere/sf/sf_lmem_utility.hpp>
#include <stratosphere/sf/sf_mem_utility.hpp>
#include <stratosphere/sf/sf_service_object.hpp>
#include <stratosphere/sf/hipc/sf_hipc_server_session_manager.hpp>
#include "sf/sf_out.hpp"
#include "sf/sf_buffers.hpp"
#include "sf/impl/sf_impl_command_serialization.hpp"
#include <stratosphere/sf/sf_out.hpp>
#include <stratosphere/sf/sf_buffers.hpp>
#include <stratosphere/sf/impl/sf_impl_command_serialization.hpp>
#include "sf/hipc/sf_hipc_server_manager.hpp"
#include <stratosphere/sf/hipc/sf_hipc_server_manager.hpp>
#include "sf/sf_mitm_dispatch.h"
#include <stratosphere/sf/sf_mitm_dispatch.h>

View File

@@ -0,0 +1,67 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/sf/sf_common.hpp>
#include <stratosphere/lmem.hpp>
namespace ams::sf {
class ExpHeapMemoryResource : public MemoryResource {
private:
lmem::HeapHandle handle;
public:
explicit ExpHeapMemoryResource(lmem::HeapHandle h) : handle(h) { /* ... */ }
lmem::HeapHandle GetHandle() const { return this->handle; }
private:
virtual void *AllocateImpl(size_t size, size_t alignment) override {
return lmem::AllocateFromExpHeap(this->handle, size, static_cast<int>(alignment));
}
virtual void DeallocateImpl(void *buffer, size_t size, size_t alignment) override {
return lmem::FreeToExpHeap(this->handle, buffer);
}
virtual bool IsEqualImpl(const MemoryResource &resource) const {
return this == std::addressof(resource);
}
};
class UnitHeapMemoryResource : public MemoryResource {
private:
lmem::HeapHandle handle;
public:
explicit UnitHeapMemoryResource(lmem::HeapHandle h) : handle(h) { /* ... */ }
lmem::HeapHandle GetHandle() const { return this->handle; }
private:
virtual void *AllocateImpl(size_t size, size_t alignment) override {
AMS_ASSERT(size <= lmem::GetUnitHeapUnitSize(this->handle));
AMS_ASSERT(alignment <= static_cast<size_t>(lmem::GetUnitHeapAlignment(this->handle)));
return lmem::AllocateFromUnitHeap(this->handle);
}
virtual void DeallocateImpl(void *buffer, size_t size, size_t alignment) override {
return lmem::FreeToUnitHeap(this->handle, buffer);
}
virtual bool IsEqualImpl(const MemoryResource &resource) const {
return this == std::addressof(resource);
}
};
}

View File

@@ -44,7 +44,7 @@ namespace ams::diag {
inline void DebugLog(const char *format, ...) __attribute__((format(printf, 1, 2)));
#ifdef AMS_ENABLE_DEBUG_PRINT
os::Mutex g_debug_log_lock;
os::Mutex g_debug_log_lock(true);
char g_debug_buffer[0x400];
void DebugLogImpl(const char *format, ::std::va_list vl) {

View File

@@ -169,7 +169,7 @@ namespace ams::fssystem {
if (this->size > ideal_size) {
/* If we do, we need to have a buffer allocated from the heap. */
AMS_ASSERT(this->buffer != nullptr);
AMS_ASSERT(g_heap.GetBlockSize(), HeapBlockSize);
AMS_ASSERT(g_heap.GetBlockSize() == HeapBlockSize);
const size_t new_size = util::AlignUp(ideal_size, HeapBlockSize);

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::ldr {
Result InitializeForShell() {
return ::ldrShellInitialize();
}
Result FinalizeForShell() {
::ldrShellExit();
return ResultSuccess();
}
Result SetProgramArgument(ncm::ProgramId program_id, const void *arg, size_t size) {
return ::ldrShellSetProgramArguments(static_cast<u64>(program_id), arg, size);
}
Result FlushArguments() {
return ::ldrShellFlushArguments();
}
}

View File

@@ -28,19 +28,19 @@ namespace ams::lr {
public:
/* Actual commands. */
virtual Result ResolveProgramPath(sf::Out<Path> out, ncm::ProgramId id) override {
return lrLrResolveProgramPath(std::addressof(this->srv), static_cast<u64>(id), out->str);
return lrLrResolveProgramPath(std::addressof(this->srv), id.value, out->str);
}
virtual Result RedirectProgramPath(const Path &path, ncm::ProgramId id) override {
return lrLrRedirectProgramPath(std::addressof(this->srv), static_cast<u64>(id), path.str);
return lrLrRedirectProgramPath(std::addressof(this->srv), id.value, path.str);
}
virtual Result ResolveApplicationControlPath(sf::Out<Path> out, ncm::ProgramId id) override {
return lrLrResolveApplicationControlPath(std::addressof(this->srv), static_cast<u64>(id), out->str);
return lrLrResolveApplicationControlPath(std::addressof(this->srv), id.value, out->str);
}
virtual Result ResolveApplicationHtmlDocumentPath(sf::Out<Path> out, ncm::ProgramId id) override {
return lrLrResolveApplicationHtmlDocumentPath(std::addressof(this->srv), static_cast<u64>(id), out->str);
return lrLrResolveApplicationHtmlDocumentPath(std::addressof(this->srv), id.value, out->str);
}
virtual Result ResolveDataPath(sf::Out<Path> out, ncm::DataId id) override {
@@ -48,31 +48,31 @@ namespace ams::lr {
}
virtual Result RedirectApplicationControlPathDeprecated(const Path &path, ncm::ProgramId id) override {
return lrLrRedirectApplicationControlPath(std::addressof(this->srv), static_cast<u64>(id), 0, path.str);
return lrLrRedirectApplicationControlPath(std::addressof(this->srv), id.value, 0, path.str);
}
virtual Result RedirectApplicationControlPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override {
return lrLrRedirectApplicationControlPath(std::addressof(this->srv), static_cast<u64>(id), static_cast<u64>(owner_id), path.str);
return lrLrRedirectApplicationControlPath(std::addressof(this->srv), id.value, owner_id.value, path.str);
}
virtual Result RedirectApplicationHtmlDocumentPathDeprecated(const Path &path, ncm::ProgramId id) override {
return lrLrRedirectApplicationHtmlDocumentPath(std::addressof(this->srv), static_cast<u64>(id), 0, path.str);
return lrLrRedirectApplicationHtmlDocumentPath(std::addressof(this->srv), id.value, 0, path.str);
}
virtual Result RedirectApplicationHtmlDocumentPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override {
return lrLrRedirectApplicationHtmlDocumentPath(std::addressof(this->srv), static_cast<u64>(id), static_cast<u64>(owner_id), path.str);
return lrLrRedirectApplicationHtmlDocumentPath(std::addressof(this->srv), id.value, owner_id.value, path.str);
}
virtual Result ResolveApplicationLegalInformationPath(sf::Out<Path> out, ncm::ProgramId id) override {
return lrLrResolveApplicationLegalInformationPath(std::addressof(this->srv), static_cast<u64>(id), out->str);
return lrLrResolveApplicationLegalInformationPath(std::addressof(this->srv), id.value, out->str);
}
virtual Result RedirectApplicationLegalInformationPathDeprecated(const Path &path, ncm::ProgramId id) override {
return lrLrRedirectApplicationLegalInformationPath(std::addressof(this->srv), static_cast<u64>(id), 0, path.str);
return lrLrRedirectApplicationLegalInformationPath(std::addressof(this->srv), id.value, 0, path.str);
}
virtual Result RedirectApplicationLegalInformationPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override {
return lrLrRedirectApplicationLegalInformationPath(std::addressof(this->srv), static_cast<u64>(id), static_cast<u64>(owner_id), path.str);
return lrLrRedirectApplicationLegalInformationPath(std::addressof(this->srv), id.value, owner_id.value, path.str);
}
virtual Result Refresh() override {
@@ -100,8 +100,7 @@ namespace ams::lr {
}
virtual Result EraseProgramRedirection(ncm::ProgramId id) override {
/* TODO: libnx bindings */
AMS_ABORT();
return lrLrEraseProgramRedirection(std::addressof(this->srv), id.value);
}
virtual Result EraseApplicationControlRedirection(ncm::ProgramId id) override {

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::os {
void InitializeSdkMutex(SdkMutexType *mutex) {
GetReference(mutex->_storage).Initialize();
}
bool IsSdkMutexLockedByCurrentThread(const SdkMutexType *mutex) {
return GetReference(mutex->_storage).IsLockedByCurrentThread();
}
void LockSdkMutex(SdkMutexType *mutex) {
AMS_ABORT_UNLESS(!IsSdkMutexLockedByCurrentThread(mutex));
return GetReference(mutex->_storage).Enter();
}
bool TryLockSdkMutex(SdkMutexType *mutex) {
AMS_ABORT_UNLESS(!IsSdkMutexLockedByCurrentThread(mutex));
return GetReference(mutex->_storage).TryEnter();
}
void UnlockSdkMutex(SdkMutexType *mutex) {
AMS_ABORT_UNLESS(IsSdkMutexLockedByCurrentThread(mutex));
return GetReference(mutex->_storage).Leave();
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::pgl {
class RemoteEventObserver final : public pgl::sf::IEventObserver {
NON_COPYABLE(RemoteEventObserver);
NON_MOVEABLE(RemoteEventObserver);
private:
::PglEventObserver observer;
public:
constexpr RemoteEventObserver(const ::PglEventObserver &o) : observer(o) { /* ... */ }
virtual ~RemoteEventObserver() override {
::pglEventObserverClose(std::addressof(this->observer));
}
virtual Result GetProcessEventHandle(ams::sf::OutCopyHandle out) override {
::Event ev;
R_TRY(::pglEventObserverGetProcessEvent(std::addressof(this->observer), std::addressof(ev)));
out.SetValue(ev.revent);
return ResultSuccess();
}
virtual Result GetProcessEventInfo(ams::sf::Out<pm::ProcessEventInfo> out) override {
static_assert(sizeof(*out.GetPointer()) == sizeof(::PmProcessEventInfo));
return ::pglEventObserverGetProcessEventInfo(std::addressof(this->observer), reinterpret_cast<::PmProcessEventInfo *>(out.GetPointer()));
}
};
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "pgl_remote_event_observer.hpp"
namespace ams::pgl {
Result Initialize() {
return ::pglInitialize();
}
void Finalize() {
return pglExit();
}
Result LaunchProgram(os::ProcessId *out, const ncm::ProgramLocation &loc, u32 process_flags, u8 pgl_flags) {
static_assert(sizeof(*out) == sizeof(u64));
static_assert(sizeof(loc) == sizeof(::NcmProgramLocation));
return ::pglLaunchProgram(reinterpret_cast<u64 *>(out), reinterpret_cast<const ::NcmProgramLocation *>(std::addressof(loc)), process_flags, pgl_flags);
}
Result TerminateProcess(os::ProcessId process_id) {
return ::pglTerminateProcess(static_cast<u64>(process_id));
}
Result LaunchProgramFromHost(os::ProcessId *out, const char *content_path, u32 process_flags) {
static_assert(sizeof(*out) == sizeof(u64));
return ::pglLaunchProgramFromHost(reinterpret_cast<u64 *>(out), content_path, process_flags);
}
Result GetHostContentMetaInfo(pgl::ContentMetaInfo *out, const char *content_path) {
static_assert(sizeof(*out) == sizeof(::PglContentMetaInfo));
return ::pglGetHostContentMetaInfo(reinterpret_cast<::PglContentMetaInfo *>(out), content_path);
}
Result GetApplicationProcessId(os::ProcessId *out) {
static_assert(sizeof(*out) == sizeof(u64));
return ::pglGetApplicationProcessId(reinterpret_cast<u64 *>(out));
}
Result BoostSystemMemoryResourceLimit(u64 size) {
return ::pglBoostSystemMemoryResourceLimit(size);
}
Result IsProcessTracked(bool *out, os::ProcessId process_id) {
return ::pglIsProcessTracked(out, static_cast<u64>(process_id));
}
Result EnableApplicationCrashReport(bool enabled) {
return ::pglEnableApplicationCrashReport(enabled);
}
Result IsApplicationCrashReportEnabled(bool *out) {
return ::pglIsApplicationCrashReportEnabled(out);
}
Result EnableApplicationAllThreadDumpOnCrash(bool enabled) {
return ::pglEnableApplicationAllThreadDumpOnCrash(enabled);
}
Result TriggerApplicationSnapShotDumper(const char *arg, SnapShotDumpType dump_type) {
return ::pglTriggerApplicationSnapShotDumper(static_cast<::PglSnapShotDumpType>(dump_type), arg);
}
Result GetEventObserver(pgl::EventObserver *out) {
::PglEventObserver obs;
R_TRY(::pglGetEventObserver(std::addressof(obs)));
auto remote_observer = std::make_shared<RemoteEventObserver>(obs);
AMS_ABORT_UNLESS(remote_observer != nullptr);
*out = pgl::EventObserver(remote_observer);
return ResultSuccess();
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "pgl_srv_shell.hpp"
namespace ams::pgl::srv {
void Initialize(ShellInterface *interface, MemoryResource *mr) {
/* Set the memory resource for the interface. */
interface->Initialize(mr);
/* Enable extra application threads, if we should. */
u8 enable_application_extra_thread;
const size_t sz = settings::fwdbg::GetSettingsItemValue(std::addressof(enable_application_extra_thread), sizeof(enable_application_extra_thread), "application_extra_thread", "enable_application_extra_thread");
if (sz == sizeof(enable_application_extra_thread) && enable_application_extra_thread != 0) {
/* NOTE: Nintendo does not check that this succeeds. */
pm::shell::EnableApplicationExtraThread();
}
/* Start the Process Tracking thread. */
pgl::srv::InitializeProcessControlTask();
}
}

View File

@@ -0,0 +1,485 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "pgl_srv_shell.hpp"
#include "pgl_srv_shell_event_observer.hpp"
namespace ams::pgl::srv {
namespace {
constexpr inline size_t ProcessDataCount = 0x20;
struct ProcessData {
os::ProcessId process_id;
u32 flags;
};
static_assert(std::is_pod<ProcessData>::value);
enum ProcessDataFlag : u32 {
ProcessDataFlag_None = 0,
ProcessDataFlag_DetailedCrashReportAllowed = (1 << 0),
ProcessDataFlag_DetailedCrashReportEnabled = (1 << 1),
ProcessDataFlag_HasLogOption = (1 << 2),
ProcessDataFlag_OutputAllLog = (1 << 3),
ProcessDataFlag_EnableCrashReportScreenShot = (1 << 4),
};
bool g_is_production = true;
bool g_enable_crash_report_screenshot = true;
bool g_enable_jit_debug = false;
constexpr inline size_t ProcessControlTaskStackSize = 8_KB;
constexpr inline s32 ProcessControlTaskPriority = 21;
os::ThreadType g_process_control_task_thread;
alignas(os::ThreadStackAlignment) u8 g_process_control_task_stack[ProcessControlTaskStackSize];
os::SdkMutex g_observer_list_mutex;
util::IntrusiveListBaseTraits<ShellEventObserverHolder>::ListType g_observer_list;
os::SdkMutex g_process_data_mutex;
ProcessData g_process_data[ProcessDataCount];
ProcessData *FindProcessData(os::ProcessId process_id) {
for (auto &data : g_process_data) {
if (data.process_id == process_id) {
return std::addressof(data);
}
}
return nullptr;
}
u32 ConvertToProcessDataFlags(u8 pgl_flags) {
if ((pgl_flags & pgl::LaunchFlags_EnableDetailedCrashReport) == 0) {
/* If we shouldn't generate detailed crash reports, set no flags. */
return ProcessDataFlag_None;
} else {
/* We can and should generate detailed crash reports. */
u32 data_flags = ProcessDataFlag_DetailedCrashReportAllowed | ProcessDataFlag_DetailedCrashReportEnabled;
/* If we should enable crash report screenshots, check the correct flag. */
if (g_enable_crash_report_screenshot) {
const u32 test_flag = g_is_production ? pgl::LaunchFlags_EnableCrashReportScreenShotForProduction : pgl::LaunchFlags_EnableCrashReportScreenShotForDevelop;
if ((pgl_flags & test_flag) != 0) {
data_flags |= ProcessDataFlag_EnableCrashReportScreenShot;
}
}
return data_flags;
}
}
std::optional<os::ProcessId> GetRunningApplicationProcessId() {
os::ProcessId process_id;
if (R_SUCCEEDED(pm::shell::GetApplicationProcessIdForShell(std::addressof(process_id)))) {
return process_id;
} else {
return std::nullopt;
}
}
s32 ConvertDumpTypeToArgument(SnapShotDumpType dump_type) {
switch (dump_type) {
case SnapShotDumpType::None: return -1;
case SnapShotDumpType::Auto: return 0;
case SnapShotDumpType::Full: return 1;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
bool GetSnapShotDumpOutputAllLog(os::ProcessId process_id) {
/* Check if we have an option set for the process. */
{
std::scoped_lock lk(g_process_data_mutex);
if (ProcessData *data = FindProcessData(process_id); data != nullptr) {
if ((data->flags & ProcessDataFlag_HasLogOption) != 0) {
return ((data->flags & ProcessDataFlag_OutputAllLog) != 0);
}
}
}
/* If we don't have an option for the process, fall back to settings. */
u8 log_option;
const size_t option_size = settings::fwdbg::GetSettingsItemValue(std::addressof(log_option), sizeof(log_option), "snap_shot_dump", "output_all_log");
return (option_size == sizeof(log_option) && log_option != 0);
}
size_t CreateSnapShotDumpArguments(char *dst, size_t dst_size, os::ProcessId process_id, SnapShotDumpType dump_type, const char *str_arg) {
const s32 dump_arg = ConvertDumpTypeToArgument(dump_type);
const s32 log_arg = GetSnapShotDumpOutputAllLog(process_id) ? 1 : 0;
if (str_arg != nullptr) {
return std::snprintf(dst, dst_size, "D %010llu \"%s\" -log %d -dump %d", static_cast<unsigned long long>(static_cast<u64>(process_id)), str_arg, log_arg, dump_arg);
} else {
return std::snprintf(dst, dst_size, "D %010llu -log %d -dump %d", static_cast<unsigned long long>(static_cast<u64>(process_id)), log_arg, dump_arg);
}
}
Result TriggerSnapShotDumper(os::ProcessId process_id, SnapShotDumpType dump_type, const char *arg) {
/* Create the arguments. */
char process_arguments[800];
const size_t arg_len = CreateSnapShotDumpArguments(process_arguments, sizeof(process_arguments), process_id, dump_type, arg);
/* Set the arguments. */
R_TRY(ldr::SetProgramArgument(ncm::SystemDebugAppletId::SnapShotDumper, process_arguments, arg_len + 1));
/* Launch the process. */
os::ProcessId dummy_process_id;
return pm::shell::LaunchProgram(std::addressof(dummy_process_id), ncm::ProgramLocation::Make(ncm::SystemDebugAppletId::SnapShotDumper, ncm::StorageId::BuiltInSystem), pm::LaunchFlags_None);
}
bool ShouldSnapShotAutoDump() {
bool dump;
const size_t sz = settings::fwdbg::GetSettingsItemValue(std::addressof(dump), sizeof(dump), "snap_shot_dump", "auto_dump");
return sz == sizeof(dump) && dump;
}
bool ShouldSnapShotFullDump() {
bool dump;
const size_t sz = settings::fwdbg::GetSettingsItemValue(std::addressof(dump), sizeof(dump), "snap_shot_dump", "full_dump");
return sz == sizeof(dump) && dump;
}
SnapShotDumpType GetSnapShotDumpType() {
if (ShouldSnapShotAutoDump()) {
if (ShouldSnapShotFullDump()) {
return SnapShotDumpType::Full;
} else {
return SnapShotDumpType::Auto;
}
} else {
return SnapShotDumpType::None;
}
}
void TriggerSnapShotDumper(os::ProcessId process_id) {
TriggerSnapShotDumper(process_id, GetSnapShotDumpType(), nullptr);
}
s32 GetCrashReportDetailedArgument(u32 data_flags) {
if (((data_flags & ProcessDataFlag_DetailedCrashReportAllowed) != 0) && ((data_flags & ProcessDataFlag_DetailedCrashReportEnabled) != 0)) {
return 1;
} else {
return 0;
}
}
s32 GetCrashReportScreenShotArgument(u32 data_flags) {
if (settings::system::GetErrorReportSharePermission() == settings::system::ErrorReportSharePermission_Granted) {
return ((data_flags & ProcessDataFlag_EnableCrashReportScreenShot) != 0) ? 1 : 0;
} else {
return 0;
}
}
void TriggerCrashReport(os::ProcessId process_id) {
static os::ProcessId s_crashed_process_id = os::InvalidProcessId;
static os::ProcessId s_creport_process_id = os::InvalidProcessId;
/* If the program that crashed is creport, we should just terminate both processes and return. */
if (process_id == s_creport_process_id) {
TerminateProcess(s_crashed_process_id);
TerminateProcess(s_creport_process_id);
s_crashed_process_id = os::InvalidProcessId;
s_creport_process_id = os::InvalidProcessId;
return;
}
/* Get the data flags for the process. */
u32 data_flags;
{
std::scoped_lock lk(g_process_data_mutex);
if (auto *data = FindProcessData(process_id); data != nullptr) {
data_flags = data->flags;
} else {
data_flags = ProcessDataFlag_None;
}
}
/* Generate arguments. */
char arguments[0x40];
const size_t len = std::snprintf(arguments, sizeof(arguments), "%ld %d %d", static_cast<s64>(static_cast<u64>(process_id)), GetCrashReportDetailedArgument(data_flags), GetCrashReportScreenShotArgument(data_flags));
if (R_FAILED(ldr::SetProgramArgument(ncm::SystemProgramId::Creport, arguments, len + 1))) {
return;
}
/* Launch creport. */
os::ProcessId creport_process_id;
if (R_FAILED(pm::shell::LaunchProgram(std::addressof(creport_process_id), ncm::ProgramLocation::Make(ncm::SystemProgramId::Creport, ncm::StorageId::BuiltInSystem), pm::LaunchFlags_None))) {
return;
}
/* Set the globals. */
s_crashed_process_id = process_id;
s_creport_process_id = creport_process_id;
}
void HandleException(os::ProcessId process_id) {
if (g_enable_jit_debug) {
/* If jit debug is enabled, we want to try to launch snap shot dumper. */
ProcessData *data = nullptr;
{
std::scoped_lock lk(g_process_data_mutex);
data = FindProcessData(process_id);
}
/* If we're tracking the process, we can launch dumper. Otherwise we should just terminate. */
if (data != nullptr) {
TriggerSnapShotDumper(process_id);
} else {
TerminateProcess(process_id);
}
} else {
/* Otherwise, we want to launch creport. */
TriggerCrashReport(process_id);
}
}
void HandleExit(os::ProcessId process_id) {
std::scoped_lock lk(g_process_data_mutex);
if (auto *data = FindProcessData(process_id); data != nullptr) {
data->process_id = os::InvalidProcessId;
}
}
void OnProcessEvent(const pm::ProcessEventInfo &event_info) {
/* Determine if we're tracking the process. */
ProcessData *data = nullptr;
{
std::scoped_lock lk(g_process_data_mutex);
data = FindProcessData(event_info.process_id);
}
/* If we are, we're going to want to notify our listeners. */
if (data != nullptr) {
/* If we closed the process, note that. */
if (static_cast<pm::ProcessEvent>(event_info.event) == pm::ProcessEvent::Exited) {
HandleExit(event_info.process_id);
}
/* Notify all observers. */
std::scoped_lock lk(g_observer_list_mutex);
for (auto &observer : g_observer_list) {
observer.Notify(event_info);
}
}
/* If the process crashed, handle that. */
if (static_cast<pm::ProcessEvent>(event_info.event) == pm::ProcessEvent::Exception) {
HandleException(event_info.process_id);
}
}
void ProcessControlTask(void *) {
/* Get the process event event from pm. */
os::SystemEvent process_event;
R_ABORT_UNLESS(pm::shell::GetProcessEventEvent(std::addressof(process_event)));
while (true) {
/* Wait for an event to come in, and clear our signal. */
process_event.Wait();
process_event.Clear();
bool continue_getting_event = true;
while (continue_getting_event) {
/* Try to get an event info. */
pm::ProcessEventInfo event_info;
if (R_FAILED(pm::shell::GetProcessEventInfo(std::addressof(event_info)))) {
break;
}
/* Process the event. */
switch (static_cast<pm::ProcessEvent>(event_info.event)) {
case pm::ProcessEvent::None:
continue_getting_event = false;
break;
case pm::ProcessEvent::Exited:
case pm::ProcessEvent::Started:
case pm::ProcessEvent::Exception:
case pm::ProcessEvent::DebugRunning:
case pm::ProcessEvent::DebugBreak:
OnProcessEvent(event_info);
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
}
}
}
void InitializeProcessControlTask() {
/* Create the task thread. */
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_process_control_task_thread), ProcessControlTask, nullptr, g_process_control_task_stack, sizeof(g_process_control_task_stack), ProcessControlTaskPriority));
/* Retrieve settings. */
settings::fwdbg::GetSettingsItemValue(std::addressof(g_enable_jit_debug), sizeof(g_enable_jit_debug), "jit_debug", "enable_jit_debug");
settings::fwdbg::GetSettingsItemValue(std::addressof(g_enable_crash_report_screenshot), sizeof(g_enable_crash_report_screenshot), "creport", "crash_screen_shot");
g_is_production = !settings::fwdbg::IsDebugModeEnabled();
/* Clear all process data. */
{
for (size_t i = 0; i < util::size(g_process_data); i++) {
g_process_data[i].process_id = os::InvalidProcessId;
}
}
/* Start the thread. */
os::StartThread(std::addressof(g_process_control_task_thread));
}
void RegisterShellEventObserver(ShellEventObserverHolder *holder) {
std::scoped_lock lk(g_observer_list_mutex);
g_observer_list.push_back(*holder);
}
void UnregisterShellEventObserver(ShellEventObserverHolder *holder) {
std::scoped_lock lk(g_observer_list_mutex);
for (auto &observer : g_observer_list) {
if (std::addressof(observer) == holder) {
g_observer_list.erase(g_observer_list.iterator_to(observer));
break;
}
}
}
Result LaunchProgram(os::ProcessId *out, const ncm::ProgramLocation &loc, u32 pm_flags, u8 pgl_flags) {
/* Convert the input flags to the internal format. */
const u32 data_flags = ConvertToProcessDataFlags(pgl_flags);
/* If jit debug is enabled, we want to be signaled on crash. */
if (g_enable_jit_debug) {
pm_flags |= pm::LaunchFlags_SignalOnException;
}
/* Launch the process. */
os::ProcessId process_id;
R_TRY(pm::shell::LaunchProgram(std::addressof(process_id), loc, pm_flags & pm::LaunchFlagsMask));
/* Create a ProcessData for the process. */
{
std::scoped_lock lk(g_process_data_mutex);
ProcessData *new_data = FindProcessData(os::InvalidProcessId);
AMS_ABORT_UNLESS(new_data != nullptr);
new_data->process_id = process_id;
new_data->flags = data_flags;
}
/* We succeeded. */
*out = process_id;
return ResultSuccess();
}
Result TerminateProcess(os::ProcessId process_id) {
/* Ask PM to terminate the process. */
return pm::shell::TerminateProcess(process_id);
}
Result GetApplicationProcessId(os::ProcessId *out) {
/* Get the application process id. */
auto application_process_id = GetRunningApplicationProcessId();
R_UNLESS(application_process_id, pgl::ResultApplicationNotRunning());
/* Return the id. */
*out = *application_process_id;
return ResultSuccess();
}
Result BoostSystemMemoryResourceLimit(u64 size) {
/* Ask PM to boost the limit. */
return pm::shell::BoostSystemMemoryResourceLimit(size);
}
bool IsProcessTracked(os::ProcessId process_id) {
/* Check whether a ProcessData exists for the process. */
std::scoped_lock lk(g_process_data_mutex);
return FindProcessData(process_id) != nullptr;
}
void EnableApplicationCrashReport(bool enabled) {
/* Get the application process id. */
auto application_process_id = GetRunningApplicationProcessId();
if (application_process_id) {
/* Find the data for the application process. */
std::scoped_lock lk(g_process_data_mutex);
ProcessData *data = FindProcessData(*application_process_id);
/* It's okay if we aren't tracking the process. */
if (data != nullptr) {
/* Set or clear the flag. */
if (enabled) {
data->flags |= ProcessDataFlag_DetailedCrashReportEnabled;
} else {
data->flags &= ~ProcessDataFlag_DetailedCrashReportEnabled;
}
}
}
}
bool IsApplicationCrashReportEnabled() {
/* Get the application process id. */
auto application_process_id = GetRunningApplicationProcessId();
if (!application_process_id) {
return false;
}
/* Find the data for the process. */
std::scoped_lock lk(g_process_data_mutex);
if (ProcessData *data = FindProcessData(*application_process_id); data != nullptr) {
return (data->flags & ProcessDataFlag_DetailedCrashReportEnabled) != 0;
} else {
return false;
}
}
void EnableApplicationAllThreadDumpOnCrash(bool enabled) {
/* Get the application process id. */
auto application_process_id = GetRunningApplicationProcessId();
if (application_process_id) {
/* Find the data for the application process. */
std::scoped_lock lk(g_process_data_mutex);
ProcessData *data = FindProcessData(*application_process_id);
/* It's okay if we aren't tracking the process. */
if (data != nullptr) {
/* Set or clear the flag. */
if (enabled) {
data->flags |= ProcessDataFlag_OutputAllLog;
} else {
data->flags &= ~ProcessDataFlag_OutputAllLog;
}
/* NOTE: Here Nintendo releases the lock, re-takes the lock, and re-finds the process data. */
/* This is unnecessary and less efficient, so we will not bother. */
/* Note that the flag bit has a meaningful value. */
data->flags |= ProcessDataFlag_HasLogOption;
}
}
}
Result TriggerApplicationSnapShotDumper(SnapShotDumpType dump_type, const char *arg) {
/* Try to get the application process id. */
os::ProcessId process_id;
R_TRY(pm::shell::GetApplicationProcessIdForShell(std::addressof(process_id)));
/* Launch the snapshot dumper. */
return TriggerSnapShotDumper(process_id, dump_type, arg);
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::pgl::srv {
void InitializeProcessControlTask();
class ShellEventObserverHolder;
void RegisterShellEventObserver(ShellEventObserverHolder *holder);
void UnregisterShellEventObserver(ShellEventObserverHolder *holder);
Result LaunchProgram(os::ProcessId *out, const ncm::ProgramLocation &loc, u32 pm_flags, u8 pgl_flags);
Result TerminateProcess(os::ProcessId process_id);
Result GetApplicationProcessId(os::ProcessId *out);
Result BoostSystemMemoryResourceLimit(u64 size);
bool IsProcessTracked(os::ProcessId process_id);
void EnableApplicationCrashReport(bool enabled);
bool IsApplicationCrashReportEnabled();
void EnableApplicationAllThreadDumpOnCrash(bool enabled);
Result TriggerApplicationSnapShotDumper(SnapShotDumpType dump_type, const char *arg);
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "pgl_srv_shell_event_observer.hpp"
#include "pgl_srv_shell.hpp"
namespace ams::pgl::srv {
ShellEventObserver::ShellEventObserver() : message_queue(queue_buffer, QueueCapacity), event(os::EventClearMode_AutoClear, true) {
this->heap_handle = lmem::CreateUnitHeap(this->event_info_data, sizeof(this->event_info_data), sizeof(this->event_info_data[0]), lmem::CreateOption_ThreadSafe, 8, GetPointer(this->heap_head));
new (GetPointer(this->holder)) ShellEventObserverHolder(this);
RegisterShellEventObserver(GetPointer(this->holder));
}
ShellEventObserver::~ShellEventObserver() {
UnregisterShellEventObserver(GetPointer(this->holder));
GetReference(this->holder).~ShellEventObserverHolder();
}
Result ShellEventObserver::PopEventInfo(pm::ProcessEventInfo *out) {
/* Receive an info from the queue. */
uintptr_t info_address;
R_UNLESS(this->message_queue.TryReceive(std::addressof(info_address)), pgl::ResultNotAvailable());
pm::ProcessEventInfo *info = reinterpret_cast<pm::ProcessEventInfo *>(info_address);
/* Set the output. */
*out = *info;
/* Free the received info. */
lmem::FreeToUnitHeap(this->heap_handle, info);
return ResultSuccess();
}
void ShellEventObserver::Notify(const pm::ProcessEventInfo &info) {
/* Allocate a new info. */
auto allocated = reinterpret_cast<pm::ProcessEventInfo *>(lmem::AllocateFromUnitHeap(this->heap_handle));
if (!allocated) {
return;
}
/* Set it to the notification. */
*allocated = info;
/* Try to send it. */
if (!this->message_queue.TrySend(reinterpret_cast<uintptr_t>(allocated))) {
lmem::FreeToUnitHeap(this->heap_handle, allocated);
return;
}
/* Notify that we have a new info available. */
this->event.Signal();
}
Result EventObserverInterface::GetProcessEventHandle(ams::sf::OutCopyHandle out) {
out.SetValue(GetReference(this->observer).GetEvent().GetReadableHandle());
return ResultSuccess();
}
Result EventObserverInterface::GetProcessEventInfo(ams::sf::Out<pm::ProcessEventInfo> out) {
return GetReference(this->observer).PopEventInfo(out.GetPointer());
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::pgl::srv {
class IShellEventObserver {
public:
virtual void Notify(const pm::ProcessEventInfo &info) = 0;
};
class ShellEventObserverHolder : public util::IntrusiveListBaseNode<ShellEventObserverHolder> {
private:
IShellEventObserver *observer;
public:
explicit ShellEventObserverHolder(IShellEventObserver *observer) : observer(observer) { /* ... */ }
void Notify(const pm::ProcessEventInfo &info) {
this->observer->Notify(info);
}
};
class ShellEventObserver final : public IShellEventObserver {
private:
static constexpr size_t QueueCapacity = 0x20;
private:
os::MessageQueue message_queue;
uintptr_t queue_buffer[QueueCapacity];
os::SystemEvent event;
TYPED_STORAGE(lmem::HeapCommonHead) heap_head;
lmem::HeapHandle heap_handle;
pm::ProcessEventInfo event_info_data[QueueCapacity];
TYPED_STORAGE(ShellEventObserverHolder) holder;
public:
ShellEventObserver();
~ShellEventObserver();
os::SystemEvent &GetEvent() {
return this->event;
}
Result PopEventInfo(pm::ProcessEventInfo *out);
virtual void Notify(const pm::ProcessEventInfo &info) override final;
};
class EventObserverInterface final : public pgl::sf::IEventObserver {
private:
TYPED_STORAGE(ShellEventObserver) observer;
public:
EventObserverInterface() {
std::memset(std::addressof(this->observer), 0, sizeof(this->observer));
new (GetPointer(this->observer)) ShellEventObserver;
}
~EventObserverInterface() {
GetReference(this->observer).~ShellEventObserver();
}
public:
virtual Result GetProcessEventHandle(ams::sf::OutCopyHandle out) override final;
virtual Result GetProcessEventInfo(ams::sf::Out<pm::ProcessEventInfo> out) override final;
};
}

View File

@@ -0,0 +1,355 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "pgl_srv_shell_host_utils.hpp"
#include "pgl_srv_shell.hpp"
namespace ams::pgl::srv {
namespace {
constexpr inline char HostPackageMountName[] = "HostPackageRead";
static_assert(sizeof(HostPackageMountName) - 1 <= fs::MountNameLengthMax);
struct CaseInsensitiveCharTraits : public std::char_traits<char> {
static char to_upper(char c) {
return std::toupper(static_cast<unsigned char>(c));
}
static bool eq(char c1, char c2) {
return to_upper(c1) == to_upper(c2);
}
static bool lt(char c1, char c2) {
return to_upper(c1) < to_upper(c2);
}
static int compare(const char *s1, const char *s2, size_t n) {
while ( n-- != 0 ) {
if ( to_upper(*s1) < to_upper(*s2) ) return -1;
if ( to_upper(*s1) > to_upper(*s2) ) return 1;
++s1; ++s2;
}
return 0;
}
static const char *find(const char *s, int n, char a) {
auto const ua (to_upper(a));
while ( n-- != 0 )
{
if (to_upper(*s) == ua)
return s;
s++;
}
return nullptr;
}
};
using PathView = std::basic_string_view<char, CaseInsensitiveCharTraits>;
enum class ExtensionType {
None = 0,
Nsp = 1,
Nspd = 2,
};
bool HasSuffix(const char *str, const char *suffix) {
const size_t suffix_len = std::strlen(suffix);
const size_t str_len = std::strlen(str);
if (suffix_len > str_len) {
return false;
}
return (PathView(str).substr(str_len - suffix_len) == PathView(suffix));
}
class HostPackageReader {
NON_COPYABLE(HostPackageReader);
NON_MOVEABLE(HostPackageReader);
private:
char content_path[fs::EntryNameLengthMax] = {};
ExtensionType extension_type = ExtensionType::None;
char mount_name[fs::MountNameLengthMax] = {};
bool is_mounted = false;
ncm::AutoBuffer content_meta_buffer;
ncm::ProgramId program_id = ncm::InvalidProgramId;
u32 program_version = 0;
ncm::ContentMetaType content_meta_type = static_cast<ncm::ContentMetaType>(0);
u8 program_index = 0;
public:
HostPackageReader() : content_meta_buffer() { /* ... */ }
~HostPackageReader() {
if (this->is_mounted) {
fs::Unmount(this->mount_name);
}
}
Result Initialize(const char *package, const char *mount) {
/* Copy in the content path. */
R_UNLESS(strlen(package) <= sizeof(this->content_path) - 1, pgl::ResultBufferNotEnough());
std::strcpy(this->content_path, package);
/* Set the extension type. */
R_TRY(this->SetExtensionType());
/* Copy in mount name. */
R_UNLESS(strlen(mount) <= sizeof(this->mount_name) - 1, pgl::ResultBufferNotEnough());
std::strcpy(this->mount_name, mount);
/* Mount the package. */
R_TRY(fs::MountApplicationPackage(this->mount_name, this->content_path));
this->is_mounted = true;
/* Set the content meta buffer. */
R_TRY(this->SetContentMetaBuffer());
/* Ensure we have a content meta buffer. */
R_UNLESS(this->content_meta_buffer.Get() != nullptr, pgl::ResultContentMetaNotFound());
return ResultSuccess();
}
Result ReadProgramInfo() {
/* First, read the program index. */
R_TRY(this->GetProgramIndex(std::addressof(this->program_index)));
/* Next, create a key for the rest of the fields. */
const auto key = ncm::PackagedContentMetaReader(this->content_meta_buffer.Get(), this->content_meta_buffer.GetSize()).GetKey();
/* Set fields. */
this->program_id = {key.id};
this->program_version = key.version;
this->content_meta_type = key.type;
return ResultSuccess();
}
Result GetContentPath(lr::Path *out, ncm::ContentType type, std::optional<u8> index) const {
switch (this->extension_type) {
case ExtensionType::Nsp: return this->GetContentPathInNsp(out, type, index);
case ExtensionType::Nspd: return this->GetContentPathInNspd(out, type, index);
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
ncm::ProgramId GetProgramId() const {
return this->program_id;
}
u32 GetProgramVersion() const {
return this->program_version;
}
ncm::ContentMetaType GetContentMetaType() const {
return this->content_meta_type;
}
u8 GetProgramIndex() const {
return this->program_index;
}
private:
Result GetContentPathInNsp(lr::Path *out, ncm::ContentType type, std::optional<u8> index) const {
/* Create a reader. */
auto reader = ncm::PackagedContentMetaReader(this->content_meta_buffer.Get(), this->content_meta_buffer.GetSize());
/* Get the content info. */
const ncm::PackagedContentInfo *content_info = nullptr;
if (index) {
content_info = reader.GetContentInfo(type, *index);
} else {
content_info = reader.GetContentInfo(type);
}
R_UNLESS(content_info != nullptr, pgl::ResultApplicationContentNotFound());
/* Get the content id string. */
ncm::ContentIdString id_str;
ncm::GetStringFromContentId(id_str.data, sizeof(id_str.data), content_info->GetId());
/* Get the file name. */
char file_name[ncm::ContentIdStringLength + 5];
const size_t len = std::snprintf(file_name, sizeof(file_name), "%s.nca", id_str.data);
R_UNLESS(len + 1 == sizeof(file_name), pgl::ResultBufferNotEnough());
/* Ensure we have the content. */
bool has_content;
R_TRY(this->SearchContent(std::addressof(has_content), out, file_name, fs::OpenDirectoryMode_File));
R_UNLESS(has_content, pgl::ResultApplicationContentNotFound());
return ResultSuccess();
}
Result GetContentPathInNspd(lr::Path *out, ncm::ContentType type, std::optional<u8> index) const {
/* Get the content name. */
const char *content_name = nullptr;
switch (type) {
case ncm::ContentType::Program: content_name = "program"; break;
case ncm::ContentType::Control: content_name = "control"; break;
case ncm::ContentType::HtmlDocument: content_name = "htmlDocument"; break;
case ncm::ContentType::LegalInformation: content_name = "legalInformation"; break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
/* Get the file name. */
/* NSPD does not support indexed content, so we always use 0 as the index. */
char file_name[0x20];
const size_t len = std::snprintf(file_name, sizeof(file_name), "%s%d.ncd", content_name, 0);
R_UNLESS(len + 1 <= sizeof(file_name), pgl::ResultBufferNotEnough());
/* Ensure we have the content. */
bool has_content;
R_TRY(this->SearchContent(std::addressof(has_content), out, file_name, fs::OpenDirectoryMode_Directory));
R_UNLESS(has_content, pgl::ResultApplicationContentNotFound());
return ResultSuccess();
}
Result GetProgramIndex(u8 *out) {
/* Nspd programs do not have indices. */
if (this->extension_type == ExtensionType::Nspd) {
*out = 0;
return ResultSuccess();
}
/* Create a reader. */
auto reader = ncm::PackagedContentMetaReader(this->content_meta_buffer.Get(), this->content_meta_buffer.GetSize());
/* Get the program content info. */
auto program_content_info = reader.GetContentInfo(ncm::ContentType::Program);
R_UNLESS(program_content_info, pgl::ResultApplicationContentNotFound());
/* Return the index. */
*out = program_content_info->GetIdOffset();
return ResultSuccess();
}
Result SetExtensionType() {
/* First, clear the suffix if the path is a program ncd. */
if (HasSuffix(this->content_path, "program0.ncd/")) {
this->content_path[strnlen(this->content_path, sizeof(this->content_path)) - std::strlen("program0.ncd/")] = 0;
}
if (HasSuffix(this->content_path, ".nsp")) {
this->extension_type = ExtensionType::Nsp;
return ResultSuccess();
} else if (HasSuffix(this->content_path, ".nspd") || HasSuffix(this->content_path, ".nspd/")) {
this->extension_type = ExtensionType::Nspd;
return ResultSuccess();
} else {
return fs::ResultPathNotFound();
}
}
Result SetContentMetaBuffer() {
constexpr const char ContentMetaFileExtension[] = ".cnmt.nca";
constexpr const char ContentMetaDirectoryExtension[] = "meta0.ncd";
/* Find the Content meta path. */
bool has_content = false;
lr::Path meta_path;
switch (this->extension_type) {
case ExtensionType::Nsp: R_TRY(this->SearchContent(std::addressof(has_content), std::addressof(meta_path), ContentMetaFileExtension, fs::OpenDirectoryMode_File)); break;
case ExtensionType::Nspd: R_TRY(this->SearchContent(std::addressof(has_content), std::addressof(meta_path), ContentMetaDirectoryExtension, fs::OpenDirectoryMode_Directory)); break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
R_UNLESS(has_content, pgl::ResultContentMetaNotFound());
/* Read the content meta buffer. */
return ncm::ReadContentMetaPath(std::addressof(this->content_meta_buffer), meta_path.str);
}
Result SearchContent(bool *out, lr::Path *out_path, const char *extension, fs::OpenDirectoryMode mode) const {
/* Generate the root directory path. */
char root_dir[sizeof(this->mount_name) + 2];
std::snprintf(root_dir, sizeof(root_dir), "%s:/", this->mount_name);
/* Open the root directory. */
fs::DirectoryHandle dir;
R_TRY(fs::OpenDirectory(std::addressof(dir), root_dir, mode));
ON_SCOPE_EXIT { fs::CloseDirectory(dir); };
/* Iterate over directory entries. */
while (true) {
fs::DirectoryEntry entry;
s64 count;
R_TRY(fs::ReadDirectory(std::addressof(count), std::addressof(entry), dir, 1));
if (count == 0) {
break;
}
/* Check if we match the suffix. */
if (HasSuffix(entry.name, extension)) {
*out = true;
if (out_path) {
const size_t len = std::snprintf(out_path->str, sizeof(out_path->str), "%s/%s", this->content_path, entry.name);
R_UNLESS(len + 1 < sizeof(out_path->str), pgl::ResultBufferNotEnough());
if (entry.type == fs::DirectoryEntryType_Directory) {
out_path->str[len] = '/';
out_path->str[len + 1] = 0;
}
}
return ResultSuccess();
}
}
/* We didn't find a match. */
*out = false;
return ResultSuccess();
}
};
}
Result LaunchProgramFromHost(os::ProcessId *out, const char *package_path, u32 pm_flags) {
/* Read the package. */
HostPackageReader reader;
R_TRY(reader.Initialize(package_path, HostPackageMountName));
/* Read the program info. */
R_TRY(reader.ReadProgramInfo());
/* Open a host location resolver. */
lr::LocationResolver host_resolver;
R_TRY(lr::OpenLocationResolver(std::addressof(host_resolver), ncm::StorageId::Host));
/* Get the content path. */
lr::Path content_path;
R_TRY(reader.GetContentPath(std::addressof(content_path), ncm::ContentType::Program, reader.GetProgramIndex()));
/* Erase the program redirection. */
R_TRY(host_resolver.EraseProgramRedirection(reader.GetProgramId()));
/* Redirect the program path to point to the new path. */
host_resolver.RedirectProgramPath(content_path, reader.GetProgramId());
/* Launch the program. */
return pgl::srv::LaunchProgram(out, ncm::ProgramLocation::Make(reader.GetProgramId(), ncm::StorageId::Host), pm_flags, pgl::LaunchFlags_None);
}
Result GetHostContentMetaInfo(pgl::ContentMetaInfo *out, const char *package_path) {
/* Read the package. */
HostPackageReader reader;
R_TRY(reader.Initialize(package_path, HostPackageMountName));
/* Read the program info. */
R_TRY(reader.ReadProgramInfo());
/* Get the content meta info. */
*out = {
.id = reader.GetProgramId().value,
.version = reader.GetProgramVersion(),
.content_type = ncm::ContentType::Program,
.id_offset = reader.GetProgramIndex(),
};
return ResultSuccess();
}
}

View File

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

View File

@@ -0,0 +1,90 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "pgl_srv_shell.hpp"
#include "pgl_srv_shell_event_observer.hpp"
#include "pgl_srv_shell_host_utils.hpp"
namespace ams::pgl::srv {
Result ShellInterface::LaunchProgram(ams::sf::Out<os::ProcessId> out, const ncm::ProgramLocation &loc, u32 pm_flags, u8 pgl_flags) {
return pgl::srv::LaunchProgram(out.GetPointer(), loc, pm_flags, pgl_flags);
}
Result ShellInterface::TerminateProcess(os::ProcessId process_id) {
return pgl::srv::TerminateProcess(process_id);
}
Result ShellInterface::LaunchProgramFromHost(ams::sf::Out<os::ProcessId> out, const ams::sf::InBuffer &content_path, u32 pm_flags) {
return pgl::srv::LaunchProgramFromHost(out.GetPointer(), reinterpret_cast<const char *>(content_path.GetPointer()), pm_flags);
}
Result ShellInterface::GetHostContentMetaInfo(ams::sf::Out<pgl::ContentMetaInfo> out, const ams::sf::InBuffer &content_path) {
return pgl::srv::GetHostContentMetaInfo(out.GetPointer(), reinterpret_cast<const char *>(content_path.GetPointer()));
}
Result ShellInterface::GetApplicationProcessId(ams::sf::Out<os::ProcessId> out) {
return pgl::srv::GetApplicationProcessId(out.GetPointer());
}
Result ShellInterface::BoostSystemMemoryResourceLimit(u64 size) {
return pgl::srv::BoostSystemMemoryResourceLimit(size);
}
Result ShellInterface::IsProcessTracked(ams::sf::Out<bool> out, os::ProcessId process_id) {
out.SetValue(pgl::srv::IsProcessTracked(process_id));
return ResultSuccess();
}
Result ShellInterface::EnableApplicationCrashReport(bool enabled) {
pgl::srv::EnableApplicationCrashReport(enabled);
return ResultSuccess();
}
Result ShellInterface::IsApplicationCrashReportEnabled(ams::sf::Out<bool> out) {
out.SetValue(pgl::srv::IsApplicationCrashReportEnabled());
return ResultSuccess();
}
Result ShellInterface::EnableApplicationAllThreadDumpOnCrash(bool enabled) {
pgl::srv::EnableApplicationAllThreadDumpOnCrash(enabled);
return ResultSuccess();
}
Result ShellInterface::TriggerApplicationSnapShotDumper(SnapShotDumpType dump_type, const ams::sf::InBuffer &arg) {
return pgl::srv::TriggerApplicationSnapShotDumper(dump_type, reinterpret_cast<const char *>(arg.GetPointer()));
}
Result ShellInterface::GetShellEventObserver(ams::sf::Out<std::shared_ptr<pgl::sf::IEventObserver>> out) {
/* Allocate a new interface. */
auto *observer_memory = this->memory_resource->Allocate(sizeof(EventObserverInterface), alignof(EventObserverInterface));
AMS_ABORT_UNLESS(observer_memory != nullptr);
/* Create the interface object. */
new (observer_memory) EventObserverInterface;
/* Set the output. */
out.SetValue(std::shared_ptr<EventObserverInterface>(reinterpret_cast<EventObserverInterface *>(observer_memory), [&](EventObserverInterface *obj) {
/* Destroy the object. */
obj->~EventObserverInterface();
/* Custom deleter: use the memory resource to free. */
this->memory_resource->Deallocate(obj, sizeof(EventObserverInterface), alignof(EventObserverInterface));
}));
return ResultSuccess();
}
}

View File

@@ -18,10 +18,39 @@
namespace ams::pm::shell {
/* Shell API. */
Result WEAK_SYMBOL LaunchProgram(os::ProcessId *out_process_id, const ncm::ProgramLocation &loc, u32 launch_flags) {
Result WEAK_SYMBOL LaunchProgram(os::ProcessId *out, const ncm::ProgramLocation &loc, u32 launch_flags) {
static_assert(sizeof(ncm::ProgramLocation) == sizeof(NcmProgramLocation));
static_assert(alignof(ncm::ProgramLocation) == alignof(NcmProgramLocation));
return pmshellLaunchProgram(launch_flags, reinterpret_cast<const NcmProgramLocation *>(&loc), reinterpret_cast<u64 *>(out_process_id));
return pmshellLaunchProgram(launch_flags, reinterpret_cast<const NcmProgramLocation *>(&loc), reinterpret_cast<u64 *>(out));
}
Result TerminateProcess(os::ProcessId process_id) {
return ::pmshellTerminateProcess(static_cast<u64>(process_id));
}
Result GetProcessEventEvent(os::SystemEvent *out) {
::Event evt;
R_TRY(::pmshellGetProcessEventHandle(std::addressof(evt)));
out->Attach(evt.revent, true, svc::InvalidHandle, false, os::EventClearMode_ManualClear);
return ResultSuccess();
}
Result GetProcessEventInfo(ProcessEventInfo *out) {
static_assert(sizeof(*out) == sizeof(::PmProcessEventInfo));
return ::pmshellGetProcessEventInfo(reinterpret_cast<::PmProcessEventInfo *>(out));
}
Result GetApplicationProcessIdForShell(os::ProcessId *out) {
static_assert(sizeof(*out) == sizeof(u64));
return ::pmshellGetApplicationProcessIdForShell(reinterpret_cast<u64 *>(out));
}
Result BoostSystemMemoryResourceLimit(u64 size) {
return ::pmshellBoostSystemMemoryResourceLimit(size);
}
Result EnableApplicationExtraThread() {
return ::pmshellEnableApplicationExtraThread();
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "settings_error_report_impl.hpp"
namespace ams::settings::impl {
Result GetErrorReportSharePermission(s32 *out) {
static_assert(sizeof(*out) == sizeof(::SetSysErrorReportSharePermission));
return ::setsysGetErrorReportSharePermission(reinterpret_cast<::SetSysErrorReportSharePermission *>(out));
}
}

View File

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

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "impl/settings_error_report_impl.hpp"
namespace ams::settings::system {
ErrorReportSharePermission GetErrorReportSharePermission() {
s32 perm = 0;
R_ABORT_UNLESS(settings::impl::GetErrorReportSharePermission(std::addressof(perm)));
return static_cast<ErrorReportSharePermission>(perm);
}
}

View File

@@ -17,8 +17,11 @@
namespace ams::settings::fwdbg {
/* TODO: Implement when libnx wrapper is added. */
bool IsDebugModeEnabled();
bool IsDebugModeEnabled() {
bool value = false;
R_ABORT_UNLESS(::setsysGetDebugModeFlag(std::addressof(value)));
return value;
}
size_t WEAK_SYMBOL GetSettingsItemValueSize(const char *name, const char *key) {
u64 size = 0;

View File

@@ -17,7 +17,7 @@
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 0
#define ATMOSPHERE_RELEASE_VERSION_MINOR 11
#define ATMOSPHERE_RELEASE_VERSION_MICRO 0
#define ATMOSPHERE_RELEASE_VERSION_MICRO 1
#define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO

View File

@@ -38,6 +38,7 @@
#include <vapours/results/lr_results.hpp>
#include <vapours/results/os_results.hpp>
#include <vapours/results/ncm_results.hpp>
#include <vapours/results/pgl_results.hpp>
#include <vapours/results/pm_results.hpp>
#include <vapours/results/psc_results.hpp>
#include <vapours/results/ro_results.hpp>

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/results/results_common.hpp>
namespace ams::pgl {
R_DEFINE_NAMESPACE_RESULT_MODULE(228);
R_DEFINE_ERROR_RESULT(NotAvailable, 2);
R_DEFINE_ERROR_RESULT(ApplicationNotRunning, 3);
R_DEFINE_ERROR_RESULT(BufferNotEnough, 4);
R_DEFINE_ERROR_RESULT(ApplicationContentNotFound, 5);
R_DEFINE_ERROR_RESULT(ContentMetaNotFound, 6);
}

View File

@@ -110,9 +110,9 @@ int main(int argc, char **argv) {
/* Try to terminate the process. */
if (hos::GetVersion() >= hos::Version_10_0_0) {
/* On 10.0.0+, use pgl to terminate. */
sm::ScopedServiceHolder<pglInitialize, pglExit> pgl_holder;
sm::ScopedServiceHolder<pgl::Initialize, pgl::Finalize> pgl_holder;
if (pgl_holder) {
pglTerminateProcess(static_cast<u64>(crashed_pid));
pgl::TerminateProcess(crashed_pid);
}
} else {
/* On < 10.0.0, use ns:dev to terminate. */

View File

@@ -66,6 +66,14 @@ namespace ams::dmnt::cheat {
return dmnt::cheat::impl::QueryCheatProcessMemory(mapping.GetPointer(), address);
}
Result CheatService::BreakCheatProcess() {
return dmnt::cheat::impl::BreakCheatProcess();
}
Result CheatService::ContinueCheatProcess() {
return dmnt::cheat::impl::ContinueCheatProcess();
}
/* ========================================================================================= */
/* =================================== Cheat Commands ==================================== */
/* ========================================================================================= */
@@ -95,6 +103,18 @@ namespace ams::dmnt::cheat {
return dmnt::cheat::impl::RemoveCheat(cheat_id);
}
Result CheatService::ReadStaticRegister(sf::Out<u64> out, u8 which) {
return dmnt::cheat::impl::ReadStaticRegister(out.GetPointer(), which);
}
Result CheatService::WriteStaticRegister(u8 which, u64 value) {
return dmnt::cheat::impl::WriteStaticRegister(which, value);
}
Result CheatService::ResetStaticRegisters() {
return dmnt::cheat::impl::ResetStaticRegisters();
}
/* ========================================================================================= */
/* =================================== Address Commands ================================== */
/* ========================================================================================= */

View File

@@ -33,14 +33,19 @@ namespace ams::dmnt::cheat {
ReadCheatProcessMemory = 65102,
WriteCheatProcessMemory = 65103,
QueryCheatProcessMemory = 65104,
BreakCheatProcess = 65105,
ContinueCheatProcess = 65106,
/* Interact with Cheats */
GetCheatCount = 65200,
GetCheats = 65201,
GetCheatById = 65202,
ToggleCheat = 65203,
AddCheat = 65204,
RemoveCheat = 65205,
GetCheatCount = 65200,
GetCheats = 65201,
GetCheatById = 65202,
ToggleCheat = 65203,
AddCheat = 65204,
RemoveCheat = 65205,
ReadStaticRegister = 65206,
WriteStaticRegister = 65207,
ResetStaticRegisters = 65208,
/* Interact with Frozen Addresses */
GetFrozenAddressCount = 65300,
@@ -60,6 +65,8 @@ namespace ams::dmnt::cheat {
Result ReadCheatProcessMemory(const sf::OutBuffer &buffer, u64 address, u64 out_size);
Result WriteCheatProcessMemory(const sf::InBuffer &buffer, u64 address, u64 in_size);
Result QueryCheatProcessMemory(sf::Out<MemoryInfo> mapping, u64 address);
Result BreakCheatProcess();
Result ContinueCheatProcess();
Result GetCheatCount(sf::Out<u64> out_count);
Result GetCheats(const sf::OutArray<CheatEntry> &cheats, sf::Out<u64> out_count, u64 offset);
@@ -67,6 +74,9 @@ namespace ams::dmnt::cheat {
Result ToggleCheat(u32 cheat_id);
Result AddCheat(const CheatDefinition &cheat, sf::Out<u32> out_cheat_id, bool enabled);
Result RemoveCheat(u32 cheat_id);
Result ReadStaticRegister(sf::Out<u64> out, u8 which);
Result WriteStaticRegister(u8 which, u64 value);
Result ResetStaticRegisters();
Result GetFrozenAddressCount(sf::Out<u64> out_count);
Result GetFrozenAddresses(const sf::OutArray<FrozenAddressEntry> &addresses, sf::Out<u64> out_count, u64 offset);
@@ -86,6 +96,8 @@ namespace ams::dmnt::cheat {
MAKE_SERVICE_COMMAND_META(ReadCheatProcessMemory),
MAKE_SERVICE_COMMAND_META(WriteCheatProcessMemory),
MAKE_SERVICE_COMMAND_META(QueryCheatProcessMemory),
MAKE_SERVICE_COMMAND_META(BreakCheatProcess),
MAKE_SERVICE_COMMAND_META(ContinueCheatProcess),
MAKE_SERVICE_COMMAND_META(GetCheatCount),
MAKE_SERVICE_COMMAND_META(GetCheats),
@@ -93,6 +105,9 @@ namespace ams::dmnt::cheat {
MAKE_SERVICE_COMMAND_META(ToggleCheat),
MAKE_SERVICE_COMMAND_META(AddCheat),
MAKE_SERVICE_COMMAND_META(RemoveCheat),
MAKE_SERVICE_COMMAND_META(ReadStaticRegister),
MAKE_SERVICE_COMMAND_META(WriteStaticRegister),
MAKE_SERVICE_COMMAND_META(ResetStaticRegisters),
MAKE_SERVICE_COMMAND_META(GetFrozenAddressCount),
MAKE_SERVICE_COMMAND_META(GetFrozenAddresses),

View File

@@ -34,6 +34,7 @@ namespace ams::dmnt::cheat::impl {
static constexpr s32 DebugEventsThreadPriority = -1;
private:
os::Mutex cheat_lock;
os::Event unsafe_break_event;
os::Event debug_events_event; /* Autoclear. */
os::ThreadType detect_thread, debug_events_thread;
os::SystemEvent cheat_process_event;
@@ -41,6 +42,7 @@ namespace ams::dmnt::cheat::impl {
CheatProcessMetadata cheat_process_metadata = {};
os::ThreadType vm_thread;
bool broken_unsafe = false;
bool needs_reload_vm = false;
CheatVirtualMachine cheat_vm;
@@ -88,6 +90,8 @@ namespace ams::dmnt::cheat::impl {
for (size_t i = 0; i < MaxCheatCount; i++) {
this->ResetCheatEntry(i);
}
this->cheat_vm.ResetStaticRegisters();
}
CheatEntry *GetCheatEntryById(size_t i) {
@@ -122,6 +126,10 @@ namespace ams::dmnt::cheat::impl {
void CloseActiveCheatProcess() {
if (this->cheat_process_debug_handle != svc::InvalidHandle) {
/* We don't need to do any unsafe brekaing. */
this->broken_unsafe = false;
this->unsafe_break_event.Signal();
/* Knock out the debug events thread. */
os::CancelThreadSynchronization(std::addressof(this->debug_events_thread));
@@ -185,7 +193,7 @@ namespace ams::dmnt::cheat::impl {
}
public:
CheatProcessManager() : cheat_lock(false), debug_events_event(os::EventClearMode_AutoClear), cheat_process_event(os::EventClearMode_AutoClear, true) {
CheatProcessManager() : cheat_lock(false), unsafe_break_event(os::EventClearMode_ManualClear), debug_events_event(os::EventClearMode_AutoClear), cheat_process_event(os::EventClearMode_AutoClear, true) {
/* Learn whether we should enable cheats by default. */
{
u8 en = 0;
@@ -257,6 +265,19 @@ namespace ams::dmnt::cheat::impl {
return ResultSuccess();
}
Result BreakCheatProcessUnsafe() {
this->broken_unsafe = true;
this->unsafe_break_event.Clear();
return svcBreakDebugProcess(this->GetCheatProcessHandle());
}
Result ContinueCheatProcessUnsafe() {
this->broken_unsafe = false;
this->unsafe_break_event.Signal();
dmnt::cheat::impl::ContinueCheatProcess(this->GetCheatProcessHandle());
return ResultSuccess();
}
Result GetCheatProcessMappingCount(u64 *out_count) {
std::scoped_lock lk(this->cheat_lock);
@@ -335,6 +356,22 @@ namespace ams::dmnt::cheat::impl {
return svcQueryDebugProcessMemory(mapping, &tmp, this->GetCheatProcessHandle(), address);
}
Result BreakCheatProcess() {
std::scoped_lock lk(this->cheat_lock);
R_TRY(this->EnsureCheatProcess());
return this->BreakCheatProcessUnsafe();
}
Result ContinueCheatProcess() {
std::scoped_lock lk(this->cheat_lock);
R_TRY(this->EnsureCheatProcess());
return this->ContinueCheatProcessUnsafe();
}
Result GetCheatCount(u64 *out_count) {
std::scoped_lock lk(this->cheat_lock);
@@ -436,6 +473,35 @@ namespace ams::dmnt::cheat::impl {
return ResultSuccess();
}
Result ReadStaticRegister(u64 *out, size_t which) {
std::scoped_lock lk(this->cheat_lock);
R_TRY(this->EnsureCheatProcess());
R_UNLESS(which < CheatVirtualMachine::NumStaticRegisters, ResultCheatInvalid());
*out = this->cheat_vm.GetStaticRegister(which);
return ResultSuccess();
}
Result WriteStaticRegister(size_t which, u64 value) {
std::scoped_lock lk(this->cheat_lock);
R_TRY(this->EnsureCheatProcess());
R_UNLESS(which < CheatVirtualMachine::NumStaticRegisters, ResultCheatInvalid());
this->cheat_vm.SetStaticRegister(which, value);
return ResultSuccess();
}
Result ResetStaticRegisters() {
std::scoped_lock lk(this->cheat_lock);
R_TRY(this->EnsureCheatProcess());
this->cheat_vm.ResetStaticRegisters();
return ResultSuccess();
}
Result GetFrozenAddressCount(u64 *out_count) {
std::scoped_lock lk(this->cheat_lock);
@@ -533,7 +599,20 @@ namespace ams::dmnt::cheat::impl {
this_ptr->debug_events_event.Wait();
while (true) {
while (R_SUCCEEDED(svcWaitSynchronizationSingle(this_ptr->GetCheatProcessHandle(), std::numeric_limits<u64>::max()))) {
std::scoped_lock lk(this_ptr->cheat_lock);
this_ptr->cheat_lock.Lock();
ON_SCOPE_EXIT { this_ptr->cheat_lock.Unlock(); };
/* If we did an unsafe break, wait until we're not broken. */
if (this_ptr->broken_unsafe) {
this_ptr->cheat_lock.Unlock();
this_ptr->unsafe_break_event.Wait();
this_ptr->cheat_lock.Lock();
if (this_ptr->GetCheatProcessHandle() != svc::InvalidHandle) {
continue;
} else {
break;
}
}
/* Handle any pending debug events. */
if (this_ptr->HasActiveCheatProcess()) {
@@ -680,6 +759,10 @@ namespace ams::dmnt::cheat::impl {
/* Cancel process guard. */
proc_guard.Cancel();
/* Reset broken state. */
this->broken_unsafe = false;
this->unsafe_break_event.Signal();
/* If new process, start the process. */
if (on_process_launch) {
this->StartProcess(this->cheat_process_metadata.process_id);
@@ -1021,6 +1104,14 @@ namespace ams::dmnt::cheat::impl {
return GetReference(g_cheat_process_manager).WriteCheatProcessMemoryUnsafe(process_addr, data, size);
}
Result BreakCheatProcessUnsafe() {
return GetReference(g_cheat_process_manager).BreakCheatProcessUnsafe();
}
Result ContinueCheatProcessUnsafe() {
return GetReference(g_cheat_process_manager).ContinueCheatProcessUnsafe();
}
Result GetCheatProcessMappingCount(u64 *out_count) {
return GetReference(g_cheat_process_manager).GetCheatProcessMappingCount(out_count);
}
@@ -1041,6 +1132,14 @@ namespace ams::dmnt::cheat::impl {
return GetReference(g_cheat_process_manager).QueryCheatProcessMemory(mapping, address);
}
Result BreakCheatProcess() {
return GetReference(g_cheat_process_manager).BreakCheatProcess();
}
Result ContinueCheatProcess() {
return GetReference(g_cheat_process_manager).ContinueCheatProcess();
}
Result GetCheatCount(u64 *out_count) {
return GetReference(g_cheat_process_manager).GetCheatCount(out_count);
}
@@ -1065,6 +1164,18 @@ namespace ams::dmnt::cheat::impl {
return GetReference(g_cheat_process_manager).RemoveCheat(cheat_id);
}
Result ReadStaticRegister(u64 *out, size_t which) {
return GetReference(g_cheat_process_manager).ReadStaticRegister(out, which);
}
Result WriteStaticRegister(size_t which, u64 value) {
return GetReference(g_cheat_process_manager).WriteStaticRegister(which, value);
}
Result ResetStaticRegisters() {
return GetReference(g_cheat_process_manager).ResetStaticRegisters();
}
Result GetFrozenAddressCount(u64 *out_count) {
return GetReference(g_cheat_process_manager).GetFrozenAddressCount(out_count);
}

View File

@@ -28,11 +28,16 @@ namespace ams::dmnt::cheat::impl {
Result ReadCheatProcessMemoryUnsafe(u64 process_addr, void *out_data, size_t size);
Result WriteCheatProcessMemoryUnsafe(u64 process_addr, void *data, size_t size);
Result BreakCheatProcessUnsafe();
Result ContinueCheatProcessUnsafe();
Result GetCheatProcessMappingCount(u64 *out_count);
Result GetCheatProcessMappings(MemoryInfo *mappings, size_t max_count, u64 *out_count, u64 offset);
Result ReadCheatProcessMemory(u64 proc_addr, void *out_data, size_t size);
Result WriteCheatProcessMemory(u64 proc_addr, const void *data, size_t size);
Result QueryCheatProcessMemory(MemoryInfo *mapping, u64 address);
Result BreakCheatProcess();
Result ContinueCheatProcess();
Result GetCheatCount(u64 *out_count);
Result GetCheats(CheatEntry *cheats, size_t max_count, u64 *out_count, u64 offset);
@@ -40,6 +45,9 @@ namespace ams::dmnt::cheat::impl {
Result ToggleCheat(u32 cheat_id);
Result AddCheat(u32 *out_id, const CheatDefinition &def, bool enabled);
Result RemoveCheat(u32 cheat_id);
Result ReadStaticRegister(u64 *out, size_t which);
Result WriteStaticRegister(size_t which, u64 value);
Result ResetStaticRegisters();
Result GetFrozenAddressCount(u64 *out_count);
Result GetFrozenAddresses(FrozenAddressEntry *frz_addrs, size_t max_count, u64 *out_count, u64 offset);

View File

@@ -237,6 +237,22 @@ namespace ams::dmnt::cheat::impl {
this->LogToDebugFile("Act[%02x]: %d\n", i, opcode->save_restore_regmask.should_operate[i]);
}
break;
case CheatVmOpcodeType_ReadWriteStaticRegister:
this->LogToDebugFile("Opcode: Read/Write Static Register\n");
if (opcode->rw_static_reg.static_idx < NumReadableStaticRegisters) {
this->LogToDebugFile("Op Type: ReadStaticRegister\n");
} else {
this->LogToDebugFile("Op Type: WriteStaticRegister\n");
}
this->LogToDebugFile("Reg Idx: %x\n", opcode->rw_static_reg.idx);
this->LogToDebugFile("Stc Idx: %x\n", opcode->rw_static_reg.static_idx);
break;
case CheatVmOpcodeType_BreakProcess:
this->LogToDebugFile("Opcode: Break Cheat Process\n");
break;
case CheatVmOpcodeType_ContinueProcess:
this->LogToDebugFile("Opcode: Continue Cheat Process\n");
break;
case CheatVmOpcodeType_DebugLog:
this->LogToDebugFile("Opcode: Debug Log\n");
this->LogToDebugFile("Bit Width: %x\n", opcode->debug_log.bit_width);
@@ -570,6 +586,30 @@ namespace ams::dmnt::cheat::impl {
}
}
break;
case CheatVmOpcodeType_ReadWriteStaticRegister:
{
/* C3000XXx */
/* C3 = opcode 0xC3. */
/* XX = static register index. */
/* x = register index. */
opcode.rw_static_reg.static_idx = ((first_dword >> 4) & 0xFF);
opcode.rw_static_reg.idx = (first_dword & 0xF);
}
break;
case CheatVmOpcodeType_BreakProcess:
{
/* FF0????? */
/* FF0 = opcode 0xFF0 */
/* Breaks the current process. */
}
break;
case CheatVmOpcodeType_ContinueProcess:
{
/* FF1????? */
/* FF1 = opcode 0xFF1 */
/* Continues the current process. */
}
break;
case CheatVmOpcodeType_DebugLog:
{
/* FFFTIX## */
@@ -1174,6 +1214,21 @@ namespace ams::dmnt::cheat::impl {
}
}
break;
case CheatVmOpcodeType_ReadWriteStaticRegister:
if (cur_opcode.rw_static_reg.static_idx < NumReadableStaticRegisters) {
/* Load a register with a static register. */
this->registers[cur_opcode.rw_static_reg.idx] = this->static_registers[cur_opcode.rw_static_reg.static_idx];
} else {
/* Store a register to a static register. */
this->static_registers[cur_opcode.rw_static_reg.static_idx] = this->registers[cur_opcode.rw_static_reg.idx];
}
break;
case CheatVmOpcodeType_BreakProcess:
dmnt::cheat::impl::BreakCheatProcessUnsafe();
break;
case CheatVmOpcodeType_ContinueProcess:
dmnt::cheat::impl::ContinueCheatProcessUnsafe();
break;
case CheatVmOpcodeType_DebugLog:
{
/* Read value from memory. */

View File

@@ -42,12 +42,15 @@ namespace ams::dmnt::cheat::impl {
CheatVmOpcodeType_BeginRegisterConditionalBlock = 0xC0,
CheatVmOpcodeType_SaveRestoreRegister = 0xC1,
CheatVmOpcodeType_SaveRestoreRegisterMask = 0xC2,
CheatVmOpcodeType_ReadWriteStaticRegister = 0xC3,
/* This is a meta entry, and not a real opcode. */
/* This is to facilitate multi-nybble instruction decoding. */
CheatVmOpcodeType_DoubleExtendedWidth = 0xF0,
/* Double-extended width opcodes. */
CheatVmOpcodeType_BreakProcess = 0xFF0,
CheatVmOpcodeType_ContinueProcess = 0xFF1,
CheatVmOpcodeType_DebugLog = 0xFFF,
};
@@ -223,6 +226,11 @@ namespace ams::dmnt::cheat::impl {
bool should_operate[0x10];
};
struct ReadWriteStaticRegisterOpcode {
u32 static_idx;
u32 idx;
};
struct DebugLogOpcode {
u32 bit_width;
u32 log_id;
@@ -252,6 +260,7 @@ namespace ams::dmnt::cheat::impl {
BeginRegisterConditionalOpcode begin_reg_cond;
SaveRestoreRegisterOpcode save_restore_reg;
SaveRestoreRegisterMaskOpcode save_restore_regmask;
ReadWriteStaticRegisterOpcode rw_static_reg;
DebugLogOpcode debug_log;
};
};
@@ -260,6 +269,9 @@ namespace ams::dmnt::cheat::impl {
public:
constexpr static size_t MaximumProgramOpcodeCount = 0x400;
constexpr static size_t NumRegisters = 0x10;
constexpr static size_t NumReadableStaticRegisters = 0x80;
constexpr static size_t NumWritableStaticRegisters = 0x80;
constexpr static size_t NumStaticRegisters = NumReadableStaticRegisters + NumWritableStaticRegisters;
private:
size_t num_opcodes = 0;
size_t instruction_ptr = 0;
@@ -268,6 +280,7 @@ namespace ams::dmnt::cheat::impl {
u32 program[MaximumProgramOpcodeCount] = {0};
u64 registers[NumRegisters] = {0};
u64 saved_values[NumRegisters] = {0};
u64 static_registers[NumStaticRegisters] = {0};
size_t loop_tops[NumRegisters] = {0};
private:
bool DecodeNextOpcode(CheatVmOpcode *out);
@@ -294,6 +307,18 @@ namespace ams::dmnt::cheat::impl {
bool LoadProgram(const CheatEntry *cheats, size_t num_cheats);
void Execute(const CheatProcessMetadata *metadata);
u64 GetStaticRegister(size_t which) const {
return this->static_registers[which];
}
void SetStaticRegister(size_t which, u64 value) {
this->static_registers[which] = value;
}
void ResetStaticRegisters() {
std::memset(this->static_registers, 0, sizeof(this->static_registers));
}
#ifdef DMNT_CHEAT_VM_DEBUG_LOG
private:
fs::FileHandle debug_log_file;

128
stratosphere/pgl/Makefile Normal file
View File

@@ -0,0 +1,128 @@
#---------------------------------------------------------------------------------
# pull in common stratosphere sysmodule configuration
#---------------------------------------------------------------------------------
include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/stratosphere.mk
#---------------------------------------------------------------------------------
# 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),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \
$(notdir $(wildcard $(dir)/*.c))))
CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c)))
CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c)))
CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c)))
CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \
$(notdir $(wildcard $(dir)/*.cpp))))
CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp)))
CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp)))
CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp)))
SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \
$(notdir $(wildcard $(dir)/*.s))))
SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s)))
SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s)))
SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).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 := $(addsuffix .o,$(BINFILES)) \
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
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)
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
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
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).npdm $(TARGET).nso $(TARGET).elf
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all : $(OUTPUT).nsp
ifeq ($(strip $(APP_JSON)),)
$(OUTPUT).nsp : $(OUTPUT).nso
else
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
endif
$(OUTPUT).nso : $(OUTPUT).elf
$(OUTPUT).elf : $(OFILES)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

88
stratosphere/pgl/pgl.json Normal file
View File

@@ -0,0 +1,88 @@
{
"name": "pgl",
"title_id": "0x0100000000000042",
"title_id_range_min": "0x0100000000000042",
"title_id_range_max": "0x0100000000000042",
"main_thread_stack_size": "0x00004000",
"main_thread_priority": 49,
"default_cpu_id": 3,
"process_category": 0,
"is_retail": true,
"pool_partition": 2,
"is_64_bit": true,
"address_space_type": 3,
"filesystem_access": {
"permissions": "0xFFFFFFFFFFFFFFFF"
},
"service_access": ["erpt:c", "fatal:u", "fsp-srv", "ldr:shel", "lm", "lr", "pm:shell", "set", "set:sys"],
"service_host": ["pgl"],
"kernel_capabilities": [{
"type": "kernel_flags",
"value": {
"highest_thread_priority": 63,
"lowest_thread_priority": 24,
"lowest_cpu_id": 3,
"highest_cpu_id": 3
}
}, {
"type": "syscalls",
"value": {
"svcSetHeapSize": "0x01",
"svcSetMemoryPermission": "0x02",
"svcSetMemoryAttribute": "0x03",
"svcMapMemory": "0x04",
"svcUnmapMemory": "0x05",
"svcQueryMemory": "0x06",
"svcExitProcess": "0x07",
"svcCreateThread": "0x08",
"svcStartThread": "0x09",
"svcExitThread": "0x0a",
"svcSleepThread": "0x0b",
"svcGetThreadPriority": "0x0c",
"svcSetThreadPriority": "0x0d",
"svcGetThreadCoreMask": "0x0e",
"svcSetThreadCoreMask": "0x0f",
"svcGetCurrentProcessorNumber": "0x10",
"svcSignalEvent": "0x11",
"svcClearEvent": "0x12",
"svcMapSharedMemory": "0x13",
"svcUnmapSharedMemory": "0x14",
"svcCreateTransferMemory": "0x15",
"svcCloseHandle": "0x16",
"svcResetSignal": "0x17",
"svcWaitSynchronization": "0x18",
"svcCancelSynchronization": "0x19",
"svcArbitrateLock": "0x1a",
"svcArbitrateUnlock": "0x1b",
"svcWaitProcessWideKeyAtomic": "0x1c",
"svcSignalProcessWideKey": "0x1d",
"svcGetSystemTick": "0x1e",
"svcConnectToNamedPort": "0x1f",
"svcSendSyncRequestLight": "0x20",
"svcSendSyncRequest": "0x21",
"svcSendSyncRequestWithUserBuffer": "0x22",
"svcSendAsyncRequestWithUserBuffer": "0x23",
"svcGetProcessId": "0x24",
"svcGetThreadId": "0x25",
"svcBreak": "0x26",
"svcOutputDebugString": "0x27",
"svcReturnFromException": "0x28",
"svcGetInfo": "0x29",
"svcWaitForAddress": "0x34",
"svcSignalToAddress": "0x35",
"svcCreateSession": "0x40",
"svcAcceptSession": "0x41",
"svcReplyAndReceiveLight": "0x42",
"svcReplyAndReceive": "0x43",
"svcReplyAndReceiveWithUserBuffer": "0x44",
"svcCreateEvent": "0x45",
"svcCallSecureMonitor": "0x7F"
}
}, {
"type": "min_kernel_version",
"value": "0x0091"
}, {
"type": "handle_table_size",
"value": 256
}]
}

View File

@@ -0,0 +1,160 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
extern "C" {
extern u32 __start__;
u32 __nx_applet_type = AppletType_None;
u32 __nx_fs_num_sessions = 1;
#define INNER_HEAP_SIZE 0x4000
size_t nx_inner_heap_size = INNER_HEAP_SIZE;
char nx_inner_heap[INNER_HEAP_SIZE];
void __libnx_initheap(void);
void __appInit(void);
void __appExit(void);
/* Exception handling. */
alignas(16) u8 __nx_exception_stack[ams::os::MemoryPageSize];
u64 __nx_exception_stack_size = sizeof(__nx_exception_stack);
void __libnx_exception_handler(ThreadExceptionDump *ctx);
}
namespace ams {
ncm::ProgramId CurrentProgramId = ncm::SystemProgramId::Pgl;
namespace result {
bool CallFatalOnResultAssertion = false;
}
}
using namespace ams;
void __libnx_exception_handler(ThreadExceptionDump *ctx) {
ams::CrashHandler(ctx);
}
namespace ams::pgl {
namespace {
/* pgl. */
constexpr size_t NumServers = 1;
ams::sf::hipc::ServerManager<NumServers> g_server_manager;
constexpr sm::ServiceName ShellServiceName = sm::ServiceName::Encode("pgl");
constexpr size_t ShellMaxSessions = 8; /* Official maximum is 8. */
/* TODO: C++20 constinit */ pgl::srv::ShellInterface g_shell_interface;
ALWAYS_INLINE std::shared_ptr<pgl::srv::ShellInterface> GetSharedPointerToShellInterface() {
return ams::sf::ServiceObjectTraits<pgl::srv::ShellInterface>::SharedPointerHelper::GetEmptyDeleteSharedPointer(std::addressof(g_shell_interface));
}
void RegisterServiceSession() {
R_ABORT_UNLESS(g_server_manager.RegisterServer<pgl::srv::ShellInterface>(ShellServiceName, ShellMaxSessions, GetSharedPointerToShellInterface()));
}
void LoopProcess() {
g_server_manager.LoopProcess();
}
/* NOTE: Nintendo reserves only 0x2000 bytes for this heap, which is used "mostly" to allocate shell event observers. */
/* However, we would like very much for homebrew sysmodules to be able to subscribe to events if they so choose */
/* And so we will use a larger heap (32 KB). */
/* We should have a smaller memory footprint than N in the end, regardless. */
u8 g_heap_memory[32_KB];
TYPED_STORAGE(ams::sf::ExpHeapMemoryResource) g_heap_memory_resource;
void *Allocate(size_t size) {
return lmem::AllocateFromExpHeap(GetReference(g_heap_memory_resource).GetHandle(), size);
}
void Deallocate(void *p, size_t size) {
return lmem::FreeToExpHeap(GetReference(g_heap_memory_resource).GetHandle(), p);
}
void InitializeHeap() {
auto heap_handle = lmem::CreateExpHeap(g_heap_memory, sizeof(g_heap_memory), lmem::CreateOption_ThreadSafe);
new (GetPointer(g_heap_memory_resource)) ams::sf::ExpHeapMemoryResource(heap_handle);
}
}
}
void __libnx_initheap(void) {
void* addr = nx_inner_heap;
size_t size = nx_inner_heap_size;
/* Newlib */
extern char* fake_heap_start;
extern char* fake_heap_end;
fake_heap_start = (char*)addr;
fake_heap_end = (char*)addr + size;
ams::pgl::InitializeHeap();
}
void __appInit(void) {
hos::SetVersionForLibnx();
fs::SetAllocator(pgl::Allocate, pgl::Deallocate);
sm::DoWithSession([&]() {
R_ABORT_UNLESS(setInitialize());
R_ABORT_UNLESS(setsysInitialize());
R_ABORT_UNLESS(pmshellInitialize());
R_ABORT_UNLESS(ldrShellInitialize());
R_ABORT_UNLESS(lrInitialize());
R_ABORT_UNLESS(fsInitialize());
});
ams::CheckApiVersion();
}
void __appExit(void) {
fsExit();
lrExit();
ldrShellExit();
pmshellExit();
setsysExit();
setExit();
}
int main(int argc, char **argv)
{
/* Register the pgl service. */
pgl::RegisterServiceSession();
/* Initialize the server library. */
pgl::srv::Initialize(std::addressof(pgl::g_shell_interface), GetPointer(pgl::g_heap_memory_resource));
/* Loop forever, servicing our services. */
pgl::LoopProcess();
/* Cleanup */
return 0;
}

View File

@@ -34,26 +34,6 @@ namespace ams::pm::impl {
u32 flags;
};
enum LaunchFlags {
LaunchFlags_None = 0,
LaunchFlags_SignalOnExit = (1 << 0),
LaunchFlags_SignalOnStart = (1 << 1),
LaunchFlags_SignalOnException = (1 << 2),
LaunchFlags_SignalOnDebugEvent = (1 << 3),
LaunchFlags_StartSuspended = (1 << 4),
LaunchFlags_DisableAslr = (1 << 5),
};
enum LaunchFlagsDeprecated {
LaunchFlagsDeprecated_None = 0,
LaunchFlagsDeprecated_SignalOnExit = (1 << 0),
LaunchFlagsDeprecated_StartSuspended = (1 << 1),
LaunchFlagsDeprecated_SignalOnException = (1 << 2),
LaunchFlagsDeprecated_DisableAslr = (1 << 3),
LaunchFlagsDeprecated_SignalOnDebugEvent = (1 << 4),
LaunchFlagsDeprecated_SignalOnStart = (1 << 5),
};
#define GET_FLAG_MASK(flag) (hos_version >= hos::Version_5_0_0 ? static_cast<u32>(LaunchFlags_##flag) : static_cast<u32>(LaunchFlagsDeprecated_##flag))
inline bool ShouldSignalOnExit(u32 launch_flags) {
@@ -91,45 +71,6 @@ namespace ams::pm::impl {
#undef GET_FLAG_MASK
enum class ProcessEvent {
None = 0,
Exited = 1,
Started = 2,
Exception = 3,
DebugRunning = 4,
DebugBreak = 5,
};
enum class ProcessEventDeprecated {
None = 0,
Exception = 1,
Exited = 2,
DebugRunning = 3,
DebugBreak = 4,
Started = 5,
};
inline u32 GetProcessEventValue(ProcessEvent event) {
if (hos::GetVersion() >= hos::Version_5_0_0) {
return static_cast<u32>(event);
}
switch (event) {
case ProcessEvent::None:
return static_cast<u32>(ProcessEventDeprecated::None);
case ProcessEvent::Exited:
return static_cast<u32>(ProcessEventDeprecated::Exited);
case ProcessEvent::Started:
return static_cast<u32>(ProcessEventDeprecated::Started);
case ProcessEvent::Exception:
return static_cast<u32>(ProcessEventDeprecated::Exception);
case ProcessEvent::DebugRunning:
return static_cast<u32>(ProcessEventDeprecated::DebugRunning);
case ProcessEvent::DebugBreak:
return static_cast<u32>(ProcessEventDeprecated::DebugBreak);
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
template<size_t MaxProcessInfos>
class ProcessInfoAllocator {
NON_COPYABLE(ProcessInfoAllocator);