Compare commits
12 Commits
thermosphe
...
experiment
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3107ec0127 | ||
|
|
60ea1dade2 | ||
|
|
a2f611edb7 | ||
|
|
8719e6da02 | ||
|
|
102e1e0e74 | ||
|
|
ceef76c428 | ||
|
|
7b01d59b3b | ||
|
|
f9c5470ac9 | ||
|
|
75f006b002 | ||
|
|
4466a74e40 | ||
|
|
dd6c9e1de1 | ||
|
|
7418c80e7f |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -95,5 +95,3 @@ sept/sept-secondary/KEYS.py
|
|||||||
**/build_nintendo_nx_arm
|
**/build_nintendo_nx_arm
|
||||||
**/build_nintendo_nx_x64
|
**/build_nintendo_nx_x64
|
||||||
**/build_nintendo_nx_x86
|
**/build_nintendo_nx_x86
|
||||||
|
|
||||||
stratosphere/test/
|
|
||||||
|
|||||||
1
Makefile
1
Makefile
@@ -135,7 +135,6 @@ dist: dist-no-debug
|
|||||||
cp exosphere/program/sc7fw/sc7fw.elf atmosphere-$(AMSVER)-debug/exosphere-sc7fw.elf
|
cp exosphere/program/sc7fw/sc7fw.elf atmosphere-$(AMSVER)-debug/exosphere-sc7fw.elf
|
||||||
cp exosphere/program/rebootstub/rebootstub.elf atmosphere-$(AMSVER)-debug/exosphere-rebootstub.elf
|
cp exosphere/program/rebootstub/rebootstub.elf atmosphere-$(AMSVER)-debug/exosphere-rebootstub.elf
|
||||||
cp mesosphere/kernel_ldr/kernel_ldr.elf atmosphere-$(AMSVER)-debug/kernel_ldr.elf
|
cp mesosphere/kernel_ldr/kernel_ldr.elf atmosphere-$(AMSVER)-debug/kernel_ldr.elf
|
||||||
cp mesosphere/kernel/kernel.elf atmosphere-$(AMSVER)-debug/kernel.elf
|
|
||||||
cp stratosphere/ams_mitm/ams_mitm.elf atmosphere-$(AMSVER)-debug/ams_mitm.elf
|
cp stratosphere/ams_mitm/ams_mitm.elf atmosphere-$(AMSVER)-debug/ams_mitm.elf
|
||||||
cp stratosphere/boot/boot.elf atmosphere-$(AMSVER)-debug/boot.elf
|
cp stratosphere/boot/boot.elf atmosphere-$(AMSVER)-debug/boot.elf
|
||||||
cp stratosphere/boot2/boot2.elf atmosphere-$(AMSVER)-debug/boot2.elf
|
cp stratosphere/boot2/boot2.elf atmosphere-$(AMSVER)-debug/boot2.elf
|
||||||
|
|||||||
@@ -1,15 +1,4 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
## 0.18.1
|
|
||||||
+ A number of minor issues were fixed, including:
|
|
||||||
+ The new `dns.mitm` module added in 0.18.0 no longer fatal errors when receiving port=nullptr.
|
|
||||||
+ This fixes youtube ad-blocking, and possibly other usecases.
|
|
||||||
+ A bug was fixed that caused ams.mitm to incorrectly cache data storages.
|
|
||||||
+ This potentially broke DLC when using romfs mods, and could have caused other issues (e.g. with custom themes, and maybe other cases).
|
|
||||||
+ A bug was fixed in power state control module registration.
|
|
||||||
+ This might fix a weird edge case with system module dependencies on sleep/wake, but probably nobody should notice any differences.
|
|
||||||
+ A bug was fixed where mesosphere sometimes treated virtual core IDs as though they were physical core IDs.
|
|
||||||
+ This had zero impact, because for Switch virtual core == physical core, but it could have affected future platforms if it had remained unresolved.
|
|
||||||
+ Several issues were fixed, and usability and stability were improved.
|
|
||||||
## 0.18.0
|
## 0.18.0
|
||||||
+ A new mitm module was added (`dns.mitm`).
|
+ A new mitm module was added (`dns.mitm`).
|
||||||
+ This provides a highly configurable mechanism for redirecting DNS resolution requests.
|
+ This provides a highly configurable mechanism for redirecting DNS resolution requests.
|
||||||
|
|||||||
@@ -25,6 +25,12 @@ set_mitm enables intercepting requests to the system settings service. It curren
|
|||||||
+ `ns` system module and games (to allow for overriding game locales)
|
+ `ns` system module and games (to allow for overriding game locales)
|
||||||
+ All firmware debug settings requests (to allow modification of system settings not directly exposed to the user)
|
+ All firmware debug settings requests (to allow modification of system settings not directly exposed to the user)
|
||||||
|
|
||||||
|
## dns_mitm
|
||||||
|
|
||||||
|
dns_mitm enables intercepting requests to dns resolution services, to enable redirecting requests for specified hostnames.
|
||||||
|
|
||||||
|
For documentation, see [here](../../features/dns_mitm.md).
|
||||||
|
|
||||||
### Firmware Version
|
### Firmware Version
|
||||||
set_mitm intercepts the `GetFirmwareVersion` command, if the requester is `qlaunch` or `maintenance`.
|
set_mitm intercepts the `GetFirmwareVersion` command, if the requester is `qlaunch` or `maintenance`.
|
||||||
It modifies the `display_version` field of the returned system version, causing the version to display
|
It modifies the `display_version` field of the returned system version, causing the version to display
|
||||||
@@ -33,8 +39,3 @@ in settings as `#.#.#|AMS #.#.#|?` with `? = S` when running under system eMMC o
|
|||||||
### System Settings
|
### System Settings
|
||||||
set_mitm intercepts the `GetSettingsItemValueSize` and `GetSettingsItemValue` commands for all requesters.
|
set_mitm intercepts the `GetSettingsItemValueSize` and `GetSettingsItemValue` commands for all requesters.
|
||||||
It does so in order to enable user configuration of system settings, which are parsed from `/atmosphere/system_settings.ini` on boot. See [here](../../features/configurations.md) for more information on the system settings format.
|
It does so in order to enable user configuration of system settings, which are parsed from `/atmosphere/system_settings.ini` on boot. See [here](../../features/configurations.md) for more information on the system settings format.
|
||||||
|
|
||||||
## dns_mitm
|
|
||||||
dns_mitm enables intercepting requests to dns resolution services, to enable redirecting requests for specified hostnames.
|
|
||||||
|
|
||||||
For documentation, see [here](../../features/dns_mitm.md).
|
|
||||||
|
|||||||
@@ -35,10 +35,9 @@
|
|||||||
|
|
||||||
static void package2_decrypt(package2_header_t *package2);
|
static void package2_decrypt(package2_header_t *package2);
|
||||||
static size_t package2_get_src_section(void **section, package2_header_t *package2, unsigned int id);
|
static size_t package2_get_src_section(void **section, package2_header_t *package2, unsigned int id);
|
||||||
static size_t package2_get_thermosphere(const void **thermosphere);
|
static size_t package2_get_thermosphere(void **thermosphere);
|
||||||
static ini1_header_t *package2_rebuild_ini1(ini1_header_t *ini1, uint32_t target_firmware, void *emummc, size_t emummc_size);
|
static ini1_header_t *package2_rebuild_ini1(ini1_header_t *ini1, uint32_t target_firmware, void *emummc, size_t emummc_size);
|
||||||
static void package2_append_section(unsigned int id, package2_header_t *package2, const void *data, size_t size);
|
static void package2_append_section(unsigned int id, package2_header_t *package2, void *data, size_t size);
|
||||||
static void package2_fixup_thermosphere_and_entrypoint(package2_header_t *package2);
|
|
||||||
static void package2_fixup_header_and_section_hashes(package2_header_t *package2, size_t size);
|
static void package2_fixup_header_and_section_hashes(package2_header_t *package2, size_t size);
|
||||||
|
|
||||||
static inline size_t align_to_4(size_t s) {
|
static inline size_t align_to_4(size_t s) {
|
||||||
@@ -51,7 +50,7 @@ void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firm
|
|||||||
void *kernel;
|
void *kernel;
|
||||||
size_t kernel_size;
|
size_t kernel_size;
|
||||||
bool is_sd_kernel = false;
|
bool is_sd_kernel = false;
|
||||||
const void *thermosphere;
|
void *thermosphere;
|
||||||
size_t thermosphere_size;
|
size_t thermosphere_size;
|
||||||
ini1_header_t *orig_ini1, *rebuilt_ini1;
|
ini1_header_t *orig_ini1, *rebuilt_ini1;
|
||||||
|
|
||||||
@@ -68,8 +67,6 @@ void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firm
|
|||||||
fatal_error(u8"Error: Package2 has no unused section for Thermosphère!\n");
|
fatal_error(u8"Error: Package2 has no unused section for Thermosphère!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
package2->metadata.section_offsets[PACKAGE2_SECTION_UNUSED] = 0; /* base of DRAM */
|
|
||||||
|
|
||||||
/* Load Kernel from SD, if possible. */
|
/* Load Kernel from SD, if possible. */
|
||||||
{
|
{
|
||||||
size_t sd_kernel_size = get_file_size("atmosphere/kernel.bin");
|
size_t sd_kernel_size = get_file_size("atmosphere/kernel.bin");
|
||||||
@@ -146,9 +143,6 @@ void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firm
|
|||||||
package2_append_section(PACKAGE2_SECTION_INI1, rebuilt_package2, rebuilt_ini1, rebuilt_ini1->size);
|
package2_append_section(PACKAGE2_SECTION_INI1, rebuilt_package2, rebuilt_ini1, rebuilt_ini1->size);
|
||||||
package2_append_section(PACKAGE2_SECTION_UNUSED, rebuilt_package2, thermosphere, thermosphere_size);
|
package2_append_section(PACKAGE2_SECTION_UNUSED, rebuilt_package2, thermosphere, thermosphere_size);
|
||||||
|
|
||||||
/* Swap entrypoint if Thermosphère is present */
|
|
||||||
package2_fixup_thermosphere_and_entrypoint(rebuilt_package2);
|
|
||||||
|
|
||||||
/* Fix all necessary data in the header to accomodate for the new patches. */
|
/* Fix all necessary data in the header to accomodate for the new patches. */
|
||||||
package2_fixup_header_and_section_hashes(rebuilt_package2, rebuilt_package2_size);
|
package2_fixup_header_and_section_hashes(rebuilt_package2, rebuilt_package2_size);
|
||||||
|
|
||||||
@@ -333,9 +327,12 @@ static size_t package2_get_src_section(void **section, package2_header_t *packag
|
|||||||
return package2->metadata.section_sizes[id];
|
return package2->metadata.section_sizes[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t package2_get_thermosphere(const void **thermosphere) {
|
static size_t package2_get_thermosphere(void **thermosphere) {
|
||||||
(*thermosphere) = thermosphere_bin;
|
/*extern const uint8_t thermosphere_bin[];
|
||||||
return thermosphere_bin_size;
|
extern const uint32_t thermosphere_bin_size;*/
|
||||||
|
/* TODO: enable when tested. */
|
||||||
|
(*thermosphere) = NULL;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ini1_header_t *package2_rebuild_ini1(ini1_header_t *ini1, uint32_t target_firmware, void *emummc, size_t emummc_size) {
|
static ini1_header_t *package2_rebuild_ini1(ini1_header_t *ini1, uint32_t target_firmware, void *emummc, size_t emummc_size) {
|
||||||
@@ -356,7 +353,7 @@ static ini1_header_t *package2_rebuild_ini1(ini1_header_t *ini1, uint32_t target
|
|||||||
return merged;
|
return merged;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void package2_append_section(unsigned int id, package2_header_t *package2, const void *data, size_t size) {
|
static void package2_append_section(unsigned int id, package2_header_t *package2, void *data, size_t size) {
|
||||||
/* This function must be called in ascending order of id. */
|
/* This function must be called in ascending order of id. */
|
||||||
/* We assume that the loading address doesn't need to be changed. */
|
/* We assume that the loading address doesn't need to be changed. */
|
||||||
uint8_t *dst = package2->data;
|
uint8_t *dst = package2->data;
|
||||||
@@ -368,22 +365,6 @@ static void package2_append_section(unsigned int id, package2_header_t *package2
|
|||||||
package2->metadata.section_sizes[id] = align_to_4(size);
|
package2->metadata.section_sizes[id] = align_to_4(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void package2_fixup_thermosphere_and_entrypoint(package2_header_t *package2) {
|
|
||||||
/* Return if Thermosphère is not present */
|
|
||||||
if (package2->metadata.section_sizes[PACKAGE2_SECTION_UNUSED] == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t *dst = package2->data;
|
|
||||||
for (unsigned int i = 0; i < PACKAGE2_SECTION_UNUSED; i++) {
|
|
||||||
dst += package2->metadata.section_sizes[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Swap kernel entrypoint with Thermosphère */
|
|
||||||
*(uint64_t *)(dst + 8) = DRAM_BASE_PHYSICAL + package2->metadata.entrypoint;
|
|
||||||
package2->metadata.entrypoint = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void package2_fixup_header_and_section_hashes(package2_header_t *package2, size_t size) {
|
static void package2_fixup_header_and_section_hashes(package2_header_t *package2, size_t size) {
|
||||||
uint8_t *data = package2->data;
|
uint8_t *data = package2->data;
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,6 @@
|
|||||||
#define PACKAGE2_MINVER_1100_CURRENT 0x10
|
#define PACKAGE2_MINVER_1100_CURRENT 0x10
|
||||||
|
|
||||||
#define NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS ((void *)(0xA9800000ull))
|
#define NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS ((void *)(0xA9800000ull))
|
||||||
#define DRAM_BASE_PHYSICAL (0x80000000)
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
union {
|
union {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
[subrepo]
|
[subrepo]
|
||||||
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
||||||
branch = master
|
branch = master
|
||||||
commit = bc08912dd31bb172467add8e24b4f0adac431939
|
commit = 17960517bad5d2d07effb28b744ac8d907d571e0
|
||||||
parent = 71add1add8521e0c2115ec612c514400ac7ba688
|
parent = ee2e9d50fd93721b365daf0eae1eef17c8ba62e8
|
||||||
method = merge
|
method = merge
|
||||||
cmdver = 0.4.1
|
cmdver = 0.4.1
|
||||||
|
|||||||
@@ -45,21 +45,7 @@
|
|||||||
|
|
||||||
namespace ams::kern {
|
namespace ams::kern {
|
||||||
|
|
||||||
namespace cpu {
|
static_assert(cpu::NumCores <= static_cast<s32>(BITSIZEOF(u64)));
|
||||||
|
static_assert(util::size(cpu::VirtualToPhysicalCoreMap) == BITSIZEOF(u64));
|
||||||
static constexpr inline size_t NumVirtualCores = BITSIZEOF(u64);
|
|
||||||
|
|
||||||
static constexpr inline u64 VirtualCoreMask = [] {
|
|
||||||
u64 mask = 0;
|
|
||||||
for (size_t i = 0; i < NumVirtualCores; ++i) {
|
|
||||||
mask |= (UINT64_C(1) << i);
|
|
||||||
}
|
|
||||||
return mask;
|
|
||||||
}();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static_assert(cpu::NumCores <= cpu::NumVirtualCores);
|
|
||||||
static_assert(util::size(cpu::VirtualToPhysicalCoreMap) == cpu::NumVirtualCores);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace ams::kern {
|
|||||||
/* Most fields have already been cleared by our constructor. */
|
/* Most fields have already been cleared by our constructor. */
|
||||||
|
|
||||||
/* Initial processes may run on all cores. */
|
/* Initial processes may run on all cores. */
|
||||||
m_core_mask = cpu::VirtualCoreMask;
|
m_core_mask = (1ul << cpu::NumCores) - 1;
|
||||||
|
|
||||||
/* Initial processes may use any user priority they like. */
|
/* Initial processes may use any user priority they like. */
|
||||||
m_priority_mask = ~0xFul;
|
m_priority_mask = ~0xFul;
|
||||||
@@ -57,15 +57,16 @@ namespace ams::kern {
|
|||||||
|
|
||||||
R_UNLESS(min_core <= max_core, svc::ResultInvalidCombination());
|
R_UNLESS(min_core <= max_core, svc::ResultInvalidCombination());
|
||||||
R_UNLESS(min_prio <= max_prio, svc::ResultInvalidCombination());
|
R_UNLESS(min_prio <= max_prio, svc::ResultInvalidCombination());
|
||||||
R_UNLESS(max_core < cpu::NumVirtualCores, svc::ResultInvalidCoreId());
|
R_UNLESS(max_core < cpu::NumCores, svc::ResultInvalidCoreId());
|
||||||
|
|
||||||
|
MESOSPHERE_ASSERT(max_core < BITSIZEOF(u64));
|
||||||
MESOSPHERE_ASSERT(max_prio < BITSIZEOF(u64));
|
MESOSPHERE_ASSERT(max_prio < BITSIZEOF(u64));
|
||||||
|
|
||||||
/* Set core mask. */
|
/* Set core mask. */
|
||||||
for (auto core_id = min_core; core_id <= max_core; core_id++) {
|
for (auto core_id = min_core; core_id <= max_core; core_id++) {
|
||||||
m_core_mask |= (1ul << core_id);
|
m_core_mask |= (1ul << core_id);
|
||||||
}
|
}
|
||||||
MESOSPHERE_ASSERT((m_core_mask & cpu::VirtualCoreMask) == m_core_mask);
|
MESOSPHERE_ASSERT((m_core_mask & ((1ul << cpu::NumCores) - 1)) == m_core_mask);
|
||||||
|
|
||||||
/* Set priority mask. */
|
/* Set priority mask. */
|
||||||
for (auto prio = min_prio; prio <= max_prio; prio++) {
|
for (auto prio = min_prio; prio <= max_prio; prio++) {
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ namespace ams::kern::svc {
|
|||||||
case ams::svc::InfoType_ThreadTickCount:
|
case ams::svc::InfoType_ThreadTickCount:
|
||||||
{
|
{
|
||||||
/* Verify the requested core is valid. */
|
/* Verify the requested core is valid. */
|
||||||
const bool core_valid = (info_subtype == static_cast<u64>(-1ul)) || (info_subtype < cpu::NumVirtualCores);
|
const bool core_valid = (info_subtype == static_cast<u64>(-1ul)) || (info_subtype < util::size(cpu::VirtualToPhysicalCoreMap));
|
||||||
R_UNLESS(core_valid, svc::ResultInvalidCombination());
|
R_UNLESS(core_valid, svc::ResultInvalidCombination());
|
||||||
|
|
||||||
/* Get the thread from its handle. */
|
/* Get the thread from its handle. */
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ namespace ams::kern::svc {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr bool IsValidVirtualCoreId(int32_t core_id) {
|
constexpr bool IsValidCoreId(int32_t core_id) {
|
||||||
return (0 <= core_id && core_id < static_cast<int32_t>(cpu::NumVirtualCores));
|
return (0 <= core_id && core_id < static_cast<int32_t>(cpu::NumCores));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExitProcess() {
|
void ExitProcess() {
|
||||||
@@ -275,7 +275,7 @@ namespace ams::kern::svc {
|
|||||||
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
|
||||||
|
|
||||||
/* Validate the core id. */
|
/* Validate the core id. */
|
||||||
R_UNLESS(IsValidVirtualCoreId(core_id), svc::ResultInvalidCoreId());
|
R_UNLESS(IsValidCoreId(core_id), svc::ResultInvalidCoreId());
|
||||||
R_UNLESS(((1ul << core_id) & process->GetCoreMask()) != 0, svc::ResultInvalidCoreId());
|
R_UNLESS(((1ul << core_id) & process->GetCoreMask()) != 0, svc::ResultInvalidCoreId());
|
||||||
|
|
||||||
/* Validate the priority. */
|
/* Validate the priority. */
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ namespace ams::kern::svc {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr bool IsValidVirtualCoreId(int32_t core_id) {
|
constexpr bool IsValidCoreId(int32_t core_id) {
|
||||||
return (0 <= core_id && core_id < static_cast<int32_t>(cpu::NumVirtualCores));
|
return (0 <= core_id && core_id < static_cast<int32_t>(cpu::NumCores));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result CreateThread(ams::svc::Handle *out, ams::svc::ThreadFunc f, uintptr_t arg, uintptr_t stack_bottom, int32_t priority, int32_t core_id) {
|
Result CreateThread(ams::svc::Handle *out, ams::svc::ThreadFunc f, uintptr_t arg, uintptr_t stack_bottom, int32_t priority, int32_t core_id) {
|
||||||
@@ -33,7 +33,7 @@ namespace ams::kern::svc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Validate arguments. */
|
/* Validate arguments. */
|
||||||
R_UNLESS(IsValidVirtualCoreId(core_id), svc::ResultInvalidCoreId());
|
R_UNLESS(IsValidCoreId(core_id), svc::ResultInvalidCoreId());
|
||||||
R_UNLESS(((1ul << core_id) & process.GetCoreMask()) != 0, svc::ResultInvalidCoreId());
|
R_UNLESS(((1ul << core_id) & process.GetCoreMask()) != 0, svc::ResultInvalidCoreId());
|
||||||
|
|
||||||
R_UNLESS(ams::svc::HighestThreadPriority <= priority && priority <= ams::svc::LowestThreadPriority, svc::ResultInvalidPriority());
|
R_UNLESS(ams::svc::HighestThreadPriority <= priority && priority <= ams::svc::LowestThreadPriority, svc::ResultInvalidPriority());
|
||||||
@@ -168,7 +168,7 @@ namespace ams::kern::svc {
|
|||||||
R_UNLESS(affinity_mask != 0, svc::ResultInvalidCombination());
|
R_UNLESS(affinity_mask != 0, svc::ResultInvalidCombination());
|
||||||
|
|
||||||
/* Validate the core id. */
|
/* Validate the core id. */
|
||||||
if (IsValidVirtualCoreId(core_id)) {
|
if (IsValidCoreId(core_id)) {
|
||||||
R_UNLESS(((1ul << core_id) & affinity_mask) != 0, svc::ResultInvalidCombination());
|
R_UNLESS(((1ul << core_id) & affinity_mask) != 0, svc::ResultInvalidCombination());
|
||||||
} else {
|
} else {
|
||||||
R_UNLESS(core_id == ams::svc::IdealCoreNoUpdate || core_id == ams::svc::IdealCoreDontCare, svc::ResultInvalidCoreId());
|
R_UNLESS(core_id == ams::svc::IdealCoreNoUpdate || core_id == ams::svc::IdealCoreDontCare, svc::ResultInvalidCoreId());
|
||||||
|
|||||||
@@ -79,7 +79,6 @@
|
|||||||
#include <stratosphere/spl.hpp>
|
#include <stratosphere/spl.hpp>
|
||||||
#include <stratosphere/time.hpp>
|
#include <stratosphere/time.hpp>
|
||||||
#include <stratosphere/updater.hpp>
|
#include <stratosphere/updater.hpp>
|
||||||
#include <stratosphere/usb.hpp>
|
|
||||||
#include <stratosphere/wec.hpp>
|
#include <stratosphere/wec.hpp>
|
||||||
|
|
||||||
/* Include FS last. */
|
/* Include FS last. */
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
namespace ams::psc {
|
namespace ams::psc {
|
||||||
|
|
||||||
enum PmModuleId : u32 {
|
enum PmModuleId : u16 {
|
||||||
PmModuleId_Usb = 4,
|
PmModuleId_Usb = 4,
|
||||||
PmModuleId_Ethernet = 5,
|
PmModuleId_Ethernet = 5,
|
||||||
PmModuleId_Fgm = 6,
|
PmModuleId_Fgm = 6,
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <vapours.hpp>
|
|
||||||
#include <stratosphere/usb/usb_limits.hpp>
|
|
||||||
#include <stratosphere/usb/usb_types.hpp>
|
|
||||||
#include <stratosphere/usb/usb_device_types.hpp>
|
|
||||||
|
|
||||||
#define AMS_USB_I_DS_ENDPOINT_INTERFACE_INFO(C, H) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 0, Result, PostBufferAsync, (sf::Out<u32> out_urb_id, u64 address, u32 size), (out_urb_id, address, size)) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 1, Result, Cancel, (), ()) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 2, Result, GetCompletionEvent, (sf::OutCopyHandle out), (out)) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 3, Result, GetUrbReport, (sf::Out<usb::UrbReport> out), (out)) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 4, Result, Stall, (), ()) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 5, Result, SetZlt, (bool zlt), (zlt))
|
|
||||||
|
|
||||||
/* TODO: Deprecated interface? */
|
|
||||||
|
|
||||||
AMS_SF_DEFINE_INTERFACE(ams::usb::ds, IDsEndpoint, AMS_USB_I_DS_ENDPOINT_INTERFACE_INFO)
|
|
||||||
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <vapours.hpp>
|
|
||||||
#include <stratosphere/usb/usb_limits.hpp>
|
|
||||||
#include <stratosphere/usb/usb_types.hpp>
|
|
||||||
#include <stratosphere/usb/usb_device_types.hpp>
|
|
||||||
#include <stratosphere/usb/ds/usb_i_ds_endpoint.hpp>
|
|
||||||
|
|
||||||
#define AMS_USB_I_DS_INTERFACE_INTERFACE_INFO(C, H) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 0, Result, RegisterEndpoint, (u8 endpoint_address, sf::Out<sf::SharedPointer<usb::ds::IDsEndpoint>> out), (endpoint_address, out)) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 1, Result, GetSetupEvent, (sf::OutCopyHandle out), (out)) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 2, Result, GetSetupPacket, (const sf::OutBuffer &out), (out)) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 3, Result, CtrlInAsync, (sf::Out<u32> out_urb_id, u64 address, u32 size), (out_urb_id, address, size)) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 4, Result, CtrlOutAsync, (sf::Out<u32> out_urb_id, u64 address, u32 size), (out_urb_id, address, size)) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 5, Result, GetCtrlInCompletionEvent, (sf::OutCopyHandle out), (out)) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 6, Result, GetCtrlInUrbReport, (sf::Out<usb::UrbReport> out), (out)) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 7, Result, GetCtrlOutCompletionEvent, (sf::OutCopyHandle out), (out)) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 8, Result, GetCtrlOutUrbReport, (sf::Out<usb::UrbReport> out), (out)) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 9, Result, CtrlStall, (), ()) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 10, Result, AppendConfigurationData, (u8 bInterfaceNumber, usb::UsbDeviceSpeed device_speed, const sf::InBuffer &data), (bInterfaceNumber, device_speed, data)) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 65000, Result, Enable, (), ()) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 65001, Result, Disable, (), ())
|
|
||||||
|
|
||||||
/* TODO: Deprecated interface? */
|
|
||||||
|
|
||||||
AMS_SF_DEFINE_INTERFACE(ams::usb::ds, IDsInterface, AMS_USB_I_DS_INTERFACE_INTERFACE_INFO)
|
|
||||||
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <vapours.hpp>
|
|
||||||
#include <stratosphere/usb/usb_limits.hpp>
|
|
||||||
#include <stratosphere/usb/usb_types.hpp>
|
|
||||||
#include <stratosphere/usb/usb_device_types.hpp>
|
|
||||||
#include <stratosphere/usb/ds/usb_i_ds_interface.hpp>
|
|
||||||
|
|
||||||
#define AMS_USB_I_DS_SERVICE_INTERFACE_INFO(C, H) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 0, Result, Bind, (usb::ComplexId complex_id, sf::CopyHandle process_h), (complex_id, process_h)) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 1, Result, RegisterInterface, (sf::Out<sf::SharedPointer<usb::ds::IDsInterface>> out, u8 bInterfaceNumber), (out, bInterfaceNumber)) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 2, Result, GetStateChangeEvent, (sf::OutCopyHandle out), (out)) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 3, Result, GetState, (sf::Out<usb::UsbState> out), (out)) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 4, Result, ClearDeviceData, (), ()) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 5, Result, AddUsbStringDescriptor, (sf::Out<u8> out, const sf::InBuffer &desc), (out, desc)) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 6, Result, DeleteUsbStringDescriptor, (u8 index), (index)) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 7, Result, SetUsbDeviceDescriptor, (const sf::InBuffer &desc, usb::UsbDeviceSpeed speed), (desc, speed)) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 8, Result, SetBinaryObjectStore, (const sf::InBuffer &bos), (bos)) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 9, Result, Enable, (), ()) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 10, Result, Disable, (), ())
|
|
||||||
|
|
||||||
/* TODO: Deprecated interface? */
|
|
||||||
|
|
||||||
AMS_SF_DEFINE_INTERFACE(ams::usb::ds, IDsService, AMS_USB_I_DS_SERVICE_INTERFACE_INFO)
|
|
||||||
|
|
||||||
#define AMS_USB_I_DS_ROOT_SERVICE_INTERFACE_INFO(C, H) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 0, Result, GetService, (sf::Out<sf::SharedPointer<usb::ds::IDsService>> out), (out))
|
|
||||||
|
|
||||||
AMS_SF_DEFINE_INTERFACE(ams::usb::ds, IDsRootService, AMS_USB_I_DS_ROOT_SERVICE_INTERFACE_INFO)
|
|
||||||
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <vapours.hpp>
|
|
||||||
#include <stratosphere/os/os_system_event.hpp>
|
|
||||||
#include <stratosphere/sf/sf_lmem_utility.hpp>
|
|
||||||
#include <stratosphere/usb/usb_limits.hpp>
|
|
||||||
#include <stratosphere/usb/usb_device_types.hpp>
|
|
||||||
#include <stratosphere/usb/ds/usb_i_ds_service.hpp>
|
|
||||||
|
|
||||||
namespace ams::usb {
|
|
||||||
|
|
||||||
class DsInterface;
|
|
||||||
class DsEndpoint;
|
|
||||||
|
|
||||||
class DsClient {
|
|
||||||
friend class DsInterface;
|
|
||||||
friend class DsEndpoint;
|
|
||||||
private:
|
|
||||||
/* NOTE: Nintendo uses a UnitHeap here on newer firmware versions. */
|
|
||||||
/* For now, we'll use an ExpHeap and do it the old way. */
|
|
||||||
sf::ExpHeapAllocator m_allocator{};
|
|
||||||
u8 m_heap_buffer[32_KB];
|
|
||||||
lmem::HeapHandle m_heap_handle{};
|
|
||||||
sf::SharedPointer<ds::IDsRootService> m_root_service{};
|
|
||||||
sf::SharedPointer<ds::IDsService> m_ds_service{};
|
|
||||||
bool m_is_initialized{false};
|
|
||||||
std::atomic<int> m_reference_count{0};
|
|
||||||
os::SystemEventType m_state_change_event{};
|
|
||||||
DsInterface *m_interfaces[DsLimitMaxInterfacesPerConfigurationCount]{};
|
|
||||||
bool m_is_enabled{false};
|
|
||||||
public:
|
|
||||||
DsClient() = default;
|
|
||||||
~DsClient() { /* ... */ }
|
|
||||||
public:
|
|
||||||
Result Initialize(ComplexId complex_id);
|
|
||||||
Result Finalize();
|
|
||||||
|
|
||||||
bool IsInitialized();
|
|
||||||
|
|
||||||
Result EnableDevice();
|
|
||||||
Result DisableDevice();
|
|
||||||
|
|
||||||
os::SystemEventType *GetStateChangeEvent();
|
|
||||||
Result GetState(UsbState *out);
|
|
||||||
|
|
||||||
Result ClearDeviceData();
|
|
||||||
|
|
||||||
Result AddUsbStringDescriptor(u8 *out_index, UsbStringDescriptor *desc);
|
|
||||||
Result DeleteUsbStringDescriptor(u8 index);
|
|
||||||
|
|
||||||
Result SetUsbDeviceDescriptor(UsbDeviceDescriptor *desc, UsbDeviceSpeed speed);
|
|
||||||
|
|
||||||
Result SetBinaryObjectStore(u8 *data, int size);
|
|
||||||
private:
|
|
||||||
Result AddInterface(DsInterface *intf, sf::SharedPointer<ds::IDsInterface> *out_srv, uint8_t bInterfaceNumber);
|
|
||||||
Result DeleteInterface(uint8_t bInterfaceNumber);
|
|
||||||
};
|
|
||||||
|
|
||||||
class DsInterface {
|
|
||||||
friend class DsEndpoint;
|
|
||||||
private:
|
|
||||||
DsClient *m_client;
|
|
||||||
sf::SharedPointer<ds::IDsInterface> m_interface;
|
|
||||||
bool m_is_initialized;
|
|
||||||
std::atomic<int> m_reference_count;
|
|
||||||
os::SystemEventType m_setup_event;
|
|
||||||
os::SystemEventType m_ctrl_in_completion_event;
|
|
||||||
os::SystemEventType m_ctrl_out_completion_event;
|
|
||||||
UrbReport m_report;
|
|
||||||
u8 m_interface_num;
|
|
||||||
DsEndpoint *m_endpoints[UsbLimitMaxEndpointsCount];
|
|
||||||
public:
|
|
||||||
DsInterface() : m_client(nullptr), m_is_initialized(false), m_reference_count(0) { /* ... */ }
|
|
||||||
~DsInterface() { /* ... */ }
|
|
||||||
public:
|
|
||||||
Result Initialize(DsClient *client, u8 bInterfaceNumber);
|
|
||||||
Result Finalize();
|
|
||||||
|
|
||||||
Result AppendConfigurationData(UsbDeviceSpeed speed, void *data, u32 size);
|
|
||||||
|
|
||||||
bool IsInitialized();
|
|
||||||
|
|
||||||
os::SystemEventType *GetSetupEvent();
|
|
||||||
Result GetSetupPacket(UsbCtrlRequest *out);
|
|
||||||
|
|
||||||
Result Enable();
|
|
||||||
Result Disable();
|
|
||||||
|
|
||||||
Result CtrlRead(u32 *out_transferred, void *dst, u32 size);
|
|
||||||
Result CtrlWrite(u32 *out_transferred, void *dst, u32 size);
|
|
||||||
Result CtrlDone();
|
|
||||||
Result CtrlStall();
|
|
||||||
private:
|
|
||||||
Result AddEndpoint(DsEndpoint *ep, u8 bEndpointAddress, sf::SharedPointer<ds::IDsEndpoint> *out);
|
|
||||||
Result DeleteEndpoint(u8 bEndpointAddress);
|
|
||||||
|
|
||||||
Result CtrlIn(u32 *out_transferred, void *dst, u32 size);
|
|
||||||
Result CtrlOut(u32 *out_transferred, void *dst, u32 size);
|
|
||||||
};
|
|
||||||
|
|
||||||
class DsEndpoint {
|
|
||||||
private:
|
|
||||||
bool m_is_initialized;
|
|
||||||
bool m_is_new_format;
|
|
||||||
std::atomic<int> m_reference_count;
|
|
||||||
DsInterface *m_interface;
|
|
||||||
sf::SharedPointer<ds::IDsEndpoint> m_endpoint;
|
|
||||||
u8 m_address;
|
|
||||||
os::SystemEventType m_completion_event;
|
|
||||||
os::SystemEventType m_unknown_event;
|
|
||||||
public:
|
|
||||||
DsEndpoint() : m_is_initialized(false), m_is_new_format(false), m_reference_count(0) { /* ... */ }
|
|
||||||
public:
|
|
||||||
Result Initialize(DsInterface *interface, u8 bEndpointAddress);
|
|
||||||
Result Finalize();
|
|
||||||
|
|
||||||
bool IsInitialized();
|
|
||||||
|
|
||||||
Result PostBuffer(u32 *out_transferred, void *buf, u32 size);
|
|
||||||
Result PostBufferAsync(u32 *out_urb_id, void *buf, u32 size);
|
|
||||||
|
|
||||||
os::SystemEventType *GetCompletionEvent();
|
|
||||||
|
|
||||||
Result GetUrbReport(UrbReport *out);
|
|
||||||
|
|
||||||
Result Cancel();
|
|
||||||
|
|
||||||
Result SetZeroLengthTransfer(bool zlt);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <vapours.hpp>
|
|
||||||
#include <stratosphere/usb/usb_limits.hpp>
|
|
||||||
#include <stratosphere/usb/usb_types.hpp>
|
|
||||||
|
|
||||||
namespace ams::usb {
|
|
||||||
|
|
||||||
constexpr inline u8 InterfaceNumberAuto = DsLimitMaxInterfacesPerConfigurationCount;
|
|
||||||
constexpr inline u8 EndpointAddressAutoIn = UsbEndpointAddressMask_DirDevicetoHost;
|
|
||||||
constexpr inline u8 EndpointAddressAutoOut = UsbEndpointAddressMask_DirHostToDevice;
|
|
||||||
|
|
||||||
enum UrbStatus {
|
|
||||||
UrbStatus_Invalid = 0,
|
|
||||||
UrbStatus_Pending = 1,
|
|
||||||
UrbStatus_Running = 2,
|
|
||||||
UrbStatus_Finished = 3,
|
|
||||||
UrbStatus_Cancelled = 4,
|
|
||||||
UrbStatus_Failed = 5,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct UrbReport {
|
|
||||||
struct Report {
|
|
||||||
u32 id;
|
|
||||||
u32 requested_size;
|
|
||||||
u32 transferred_size;
|
|
||||||
UrbStatus status;
|
|
||||||
} reports[DsLimitRingSize];
|
|
||||||
u32 count;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum DsString {
|
|
||||||
DsString_Max = 0x20,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct DsVidPidBcd {
|
|
||||||
uint16_t idVendor;
|
|
||||||
uint16_t idProduct;
|
|
||||||
uint16_t bcdDevice;
|
|
||||||
|
|
||||||
char manufacturer[DsString_Max];
|
|
||||||
char product[DsString_Max];
|
|
||||||
char serial_number[DsString_Max];
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <vapours.hpp>
|
|
||||||
#include <stratosphere/dd/dd_device_address_space_common.hpp>
|
|
||||||
|
|
||||||
namespace ams::usb {
|
|
||||||
|
|
||||||
constexpr inline int HwLimitDmaBufferAlignmentSize = dd::DeviceAddressSpaceMemoryRegionAlignment;
|
|
||||||
constexpr inline int HwLimitDataCacheLineSize = 0x40;
|
|
||||||
constexpr inline int HwLimitMaxPortCount = 0x4;
|
|
||||||
|
|
||||||
constexpr inline int UsbLimitMaxEndpointsCount = 0x20;
|
|
||||||
constexpr inline int UsbLimitMaxEndpointPairCount = 0x10;
|
|
||||||
|
|
||||||
constexpr inline int DsLimitMaxConfigurationsPerDeviceCount = 1;
|
|
||||||
constexpr inline int DsLimitMaxInterfacesPerConfigurationCount = 4;
|
|
||||||
constexpr inline int DsLimitMaxNameSize = 0x40;
|
|
||||||
constexpr inline int DsLimitRingSize = 8;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,223 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <vapours.hpp>
|
|
||||||
#include <stratosphere/usb/usb_limits.hpp>
|
|
||||||
|
|
||||||
namespace ams::usb {
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE bool IsDmaAligned(u64 address) {
|
|
||||||
return util::IsAligned(address, static_cast<u64>(HwLimitDmaBufferAlignmentSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ComplexId {
|
|
||||||
ComplexId_Tegra21x = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum UsbDescriptorType {
|
|
||||||
UsbDescriptorType_Device = 1,
|
|
||||||
UsbDescriptorType_Config = 2,
|
|
||||||
UsbDescriptorType_String = 3,
|
|
||||||
UsbDescriptorType_Interface = 4,
|
|
||||||
UsbDescriptorType_Endpoint = 5,
|
|
||||||
UsbDescriptorType_DeviceQualifier = 6,
|
|
||||||
UsbDescriptorType_OtherSpeedConfig = 7,
|
|
||||||
UsbDescriptorType_InterfacePower = 8,
|
|
||||||
UsbDescriptorType_Otg = 9,
|
|
||||||
UsbDescriptorType_Debug = 10,
|
|
||||||
UsbDescriptorType_InterfaceAssociation = 11,
|
|
||||||
UsbDescriptorType_Bos = 15,
|
|
||||||
UsbDescriptorType_DeviceCapability = 16,
|
|
||||||
|
|
||||||
UsbDescriptorType_Hid = 33,
|
|
||||||
UsbDescriptorType_Report = 34,
|
|
||||||
UsbDescriptorType_Physical = 35,
|
|
||||||
|
|
||||||
UsbDescriptorType_Hub = 41,
|
|
||||||
|
|
||||||
UsbDescriptorType_EndpointCompanion = 48,
|
|
||||||
UsbDescriptorType_IsocEndpointCompanion = 49,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct UsbDescriptorHeader {
|
|
||||||
uint8_t bLength;
|
|
||||||
uint8_t bDescriptorType;
|
|
||||||
} PACKED;
|
|
||||||
|
|
||||||
struct UsbInterfaceDescriptor {
|
|
||||||
uint8_t bLength;
|
|
||||||
uint8_t bDescriptorType;
|
|
||||||
uint8_t bInterfaceNumber;
|
|
||||||
uint8_t bAlternateSetting;
|
|
||||||
uint8_t bNumEndpoints;
|
|
||||||
uint8_t bInterfaceClass;
|
|
||||||
uint8_t bInterfaceSubClass;
|
|
||||||
uint8_t bInterfaceProtocol;
|
|
||||||
uint8_t iInterface;
|
|
||||||
} PACKED;
|
|
||||||
|
|
||||||
struct UsbEndpointDescriptor {
|
|
||||||
uint8_t bLength;
|
|
||||||
uint8_t bDescriptorType;
|
|
||||||
uint8_t bEndpointAddress;
|
|
||||||
uint8_t bmAttributes;
|
|
||||||
uint16_t wMaxPacketSize;
|
|
||||||
uint8_t bInterval;
|
|
||||||
} PACKED;
|
|
||||||
|
|
||||||
struct UsbDeviceDescriptor {
|
|
||||||
uint8_t bLength;
|
|
||||||
uint8_t bDescriptorType;
|
|
||||||
uint16_t bcdUSB;
|
|
||||||
uint8_t bDeviceClass;
|
|
||||||
uint8_t bDeviceSubClass;
|
|
||||||
uint8_t bDeviceProtocol;
|
|
||||||
uint8_t bMaxPacketSize0;
|
|
||||||
uint16_t idVendor;
|
|
||||||
uint16_t idProduct;
|
|
||||||
uint16_t bcdDevice;
|
|
||||||
uint8_t iManufacturer;
|
|
||||||
uint8_t iProduct;
|
|
||||||
uint8_t iSerialNumber;
|
|
||||||
uint8_t bNumConfigurations;
|
|
||||||
} PACKED;
|
|
||||||
|
|
||||||
struct UsbConfigDescriptor {
|
|
||||||
uint8_t bLength;
|
|
||||||
uint8_t bDescriptorType;
|
|
||||||
uint16_t wTotalLength;
|
|
||||||
uint8_t bNumInterfaces;
|
|
||||||
uint8_t bConfigurationValue;
|
|
||||||
uint8_t iConfiguration;
|
|
||||||
uint8_t bmAttributes;
|
|
||||||
uint8_t bMaxPower;
|
|
||||||
} PACKED;
|
|
||||||
|
|
||||||
struct UsbEndpointCompanionDescriptor {
|
|
||||||
uint8_t bLength;
|
|
||||||
uint8_t bDescriptorType;
|
|
||||||
uint8_t bMaxBurst;
|
|
||||||
uint8_t bmAttributes;
|
|
||||||
uint16_t wBytesPerInterval;
|
|
||||||
} PACKED;
|
|
||||||
|
|
||||||
struct UsbStringDescriptor {
|
|
||||||
uint8_t bLength;
|
|
||||||
uint8_t bDescriptorType;
|
|
||||||
uint16_t wData[DsLimitMaxNameSize];
|
|
||||||
} PACKED;
|
|
||||||
|
|
||||||
struct UsbCtrlRequest {
|
|
||||||
uint8_t bmRequestType;
|
|
||||||
uint8_t bRequest;
|
|
||||||
uint16_t wValue;
|
|
||||||
uint16_t wIndex;
|
|
||||||
uint16_t wLength;
|
|
||||||
} PACKED;
|
|
||||||
|
|
||||||
enum UsbState {
|
|
||||||
UsbState_Detached = 0,
|
|
||||||
UsbState_Attached = 1,
|
|
||||||
UsbState_Powered = 2,
|
|
||||||
UsbState_Default = 3,
|
|
||||||
UsbState_Address = 4,
|
|
||||||
UsbState_Configured = 5,
|
|
||||||
UsbState_Suspended = 6,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum UsbDescriptorSize {
|
|
||||||
UsbDescriptorSize_Interface = sizeof(UsbInterfaceDescriptor),
|
|
||||||
UsbDescriptorSize_Endpoint = sizeof(UsbEndpointDescriptor),
|
|
||||||
UsbDescriptorSize_Device = sizeof(UsbDeviceDescriptor),
|
|
||||||
UsbDescriptorSize_Config = sizeof(UsbConfigDescriptor),
|
|
||||||
UsbDescriptorSize_EndpointCompanion = sizeof(UsbEndpointCompanionDescriptor),
|
|
||||||
};
|
|
||||||
|
|
||||||
enum UsbDeviceSpeed {
|
|
||||||
UsbDeviceSpeed_Invalid = 0,
|
|
||||||
UsbDeviceSpeed_Low = 1,
|
|
||||||
UsbDeviceSpeed_Full = 2,
|
|
||||||
UsbDeviceSpeed_High = 3,
|
|
||||||
UsbDeviceSpeed_Super = 4,
|
|
||||||
UsbDeviceSpeed_SuperPlus = 5,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
enum UsbEndpointAddressMask {
|
|
||||||
UsbEndpointAddressMask_EndpointNumber = (0xF << 0),
|
|
||||||
|
|
||||||
UsbEndpointAddressMask_Dir = (0x1 << 7),
|
|
||||||
|
|
||||||
UsbEndpointAddressMask_DirHostToDevice = (0x0 << 7),
|
|
||||||
UsbEndpointAddressMask_DirDevicetoHost = (0x1 << 7),
|
|
||||||
};
|
|
||||||
|
|
||||||
enum UsbEndpointAttributeMask {
|
|
||||||
UsbEndpointAttributeMask_XferType = (0x3 << 0),
|
|
||||||
|
|
||||||
UsbEndpointAttributeMask_XferTypeControl = (0x0 << 0),
|
|
||||||
UsbEndpointAttributeMask_XferTypeIsoc = (0x1 << 0),
|
|
||||||
UsbEndpointAttributeMask_XferTypeBulk = (0x2 << 0),
|
|
||||||
UsbEndpointAttributeMask_XferTypeInt = (0x3 << 0),
|
|
||||||
};
|
|
||||||
|
|
||||||
enum UsbEndpointDirection {
|
|
||||||
UsbEndpointDirection_Invalid = 0,
|
|
||||||
UsbEndpointDirection_ToDevice = 1,
|
|
||||||
UsbEndpointDirection_ToHost = 2,
|
|
||||||
UsbEndpointDirection_Control = 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr inline u8 UsbGetEndpointNumber(const UsbEndpointDescriptor *desc) {
|
|
||||||
return desc->bEndpointAddress & UsbEndpointAddressMask_EndpointNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr inline bool UsbEndpointIsHostToDevice(const UsbEndpointDescriptor *desc) {
|
|
||||||
return (desc->bEndpointAddress & UsbEndpointAddressMask_Dir) == UsbEndpointAddressMask_DirHostToDevice;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr inline bool UsbEndpointIsDeviceToHost(const UsbEndpointDescriptor *desc) {
|
|
||||||
return (desc->bEndpointAddress & UsbEndpointAddressMask_Dir) == UsbEndpointAddressMask_DirDevicetoHost;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr inline u8 UsbGetEndpointAddress(u8 number, UsbEndpointDirection dir) {
|
|
||||||
u8 val = static_cast<u8>(number & UsbEndpointAddressMask_EndpointNumber);
|
|
||||||
if (dir == UsbEndpointDirection_ToHost) {
|
|
||||||
val |= UsbEndpointAddressMask_DirDevicetoHost;
|
|
||||||
} else {
|
|
||||||
val |= UsbEndpointAddressMask_DirHostToDevice;
|
|
||||||
}
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr inline UsbEndpointDirection GetUsbEndpointDirection(const UsbEndpointDescriptor *desc) {
|
|
||||||
if (UsbEndpointIsDeviceToHost(desc)) {
|
|
||||||
return UsbEndpointDirection_ToHost;
|
|
||||||
} else {
|
|
||||||
return UsbEndpointDirection_ToDevice;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr inline bool UsbEndpointIsValid(const UsbEndpointDescriptor *desc) {
|
|
||||||
return desc != nullptr && desc->bLength >= UsbDescriptorSize_Endpoint && desc->bEndpointAddress != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr inline void UsbMarkEndpointInvalid(UsbEndpointDescriptor *desc) {
|
|
||||||
desc->bLength = 0;
|
|
||||||
desc->bEndpointAddress = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -46,9 +46,9 @@ namespace ams::psc {
|
|||||||
Result PmModule::Initialize(const PmModuleId mid, const PmModuleId *dependencies, u32 dependency_count, os::EventClearMode clear_mode) {
|
Result PmModule::Initialize(const PmModuleId mid, const PmModuleId *dependencies, u32 dependency_count, os::EventClearMode clear_mode) {
|
||||||
R_UNLESS(!this->initialized, psc::ResultAlreadyInitialized());
|
R_UNLESS(!this->initialized, psc::ResultAlreadyInitialized());
|
||||||
|
|
||||||
static_assert(sizeof(*dependencies) == sizeof(u32));
|
static_assert(sizeof(*dependencies) == sizeof(u16));
|
||||||
::PscPmModule module;
|
::PscPmModule module;
|
||||||
R_TRY(::pscmGetPmModule(std::addressof(module), static_cast<::PscPmModuleId>(mid), reinterpret_cast<const u32 *>(dependencies), dependency_count, clear_mode == os::EventClearMode_AutoClear));
|
R_TRY(::pscmGetPmModule(std::addressof(module), static_cast<::PscPmModuleId>(mid), reinterpret_cast<const u16 *>(dependencies), dependency_count, clear_mode == os::EventClearMode_AutoClear));
|
||||||
|
|
||||||
this->intf = RemoteObjectFactory::CreateSharedEmplaced<psc::sf::IPmModule, RemotePmModule>(module);
|
this->intf = RemoteObjectFactory::CreateSharedEmplaced<psc::sf::IPmModule, RemotePmModule>(module);
|
||||||
this->system_event.AttachReadableHandle(module.event.revent, false, clear_mode);
|
this->system_event.AttachReadableHandle(module.event.revent, false, clear_mode);
|
||||||
|
|||||||
@@ -1,803 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
#include "usb_remote_ds_root_service.hpp"
|
|
||||||
#include "usb_remote_ds_service.hpp"
|
|
||||||
#include "impl/usb_util.hpp"
|
|
||||||
|
|
||||||
namespace ams::usb {
|
|
||||||
|
|
||||||
Result DsClient::Initialize(ComplexId complex_id) {
|
|
||||||
/* Clear interfaces. */
|
|
||||||
for (size_t i = 0; i < util::size(m_interfaces); ++i) {
|
|
||||||
m_interfaces[i] = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialize heap. */
|
|
||||||
m_heap_handle = lmem::CreateExpHeap(m_heap_buffer, sizeof(m_heap_buffer), lmem::CreateOption_None);
|
|
||||||
R_UNLESS(m_heap_handle != nullptr, usb::ResultMemAllocFailure());
|
|
||||||
|
|
||||||
/* Attach our allocator. */
|
|
||||||
m_allocator.Attach(m_heap_handle);
|
|
||||||
|
|
||||||
/* Connect to usb:ds. */
|
|
||||||
/* NOTE: Here, Nintendo does m_domain.InitializeByDomain<...>(...); m_domain.SetSessionCount(1); */
|
|
||||||
{
|
|
||||||
Service srv;
|
|
||||||
R_TRY(sm::GetService(std::addressof(srv), sm::ServiceName::Encode("usb:ds")));
|
|
||||||
|
|
||||||
R_ABORT_UNLESS(serviceConvertToDomain(std::addressof(srv)));
|
|
||||||
|
|
||||||
using Allocator = decltype(m_allocator);
|
|
||||||
using ObjectFactory = sf::ObjectFactory<Allocator::Policy>;
|
|
||||||
|
|
||||||
if (hos::GetVersion() >= hos::Version_11_0_0) {
|
|
||||||
m_root_service = ObjectFactory::CreateSharedEmplaced<ds::IDsRootService, RemoteDsRootService>(std::addressof(m_allocator), srv, std::addressof(m_allocator));
|
|
||||||
|
|
||||||
R_TRY(m_root_service->GetService(std::addressof(m_ds_service)));
|
|
||||||
} else {
|
|
||||||
m_ds_service = ObjectFactory::CreateSharedEmplaced<ds::IDsService, RemoteDsService>(std::addressof(m_allocator), srv, std::addressof(m_allocator));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Bind the client process. */
|
|
||||||
R_TRY(m_ds_service->Bind(complex_id, dd::GetCurrentProcessHandle()));
|
|
||||||
|
|
||||||
/* Get the state change event. */
|
|
||||||
sf::CopyHandle event_handle;
|
|
||||||
R_TRY(m_ds_service->GetStateChangeEvent(std::addressof(event_handle)));
|
|
||||||
|
|
||||||
/* Attach the state change event handle to our event. */
|
|
||||||
os::AttachReadableHandleToSystemEvent(std::addressof(m_state_change_event), event_handle.GetValue(), true, os::EventClearMode_ManualClear);
|
|
||||||
|
|
||||||
/* Mark ourselves as initialized. */
|
|
||||||
m_is_initialized = true;
|
|
||||||
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsClient::Finalize() {
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
/* Check that we're initialized. */
|
|
||||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
|
||||||
|
|
||||||
/* Disable and finalize all interfaces. */
|
|
||||||
R_TRY(this->DisableDevice());
|
|
||||||
for (size_t i = 0; i < util::size(m_interfaces); ++i) {
|
|
||||||
if (m_interfaces[i] != nullptr) {
|
|
||||||
R_TRY(m_interfaces[i]->Finalize());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check our reference count .*/
|
|
||||||
R_UNLESS(m_reference_count <= 1, usb::ResultResourceBusy());
|
|
||||||
|
|
||||||
/* Finalize members. */
|
|
||||||
m_is_initialized = false;
|
|
||||||
os::DestroySystemEvent(std::addressof(m_state_change_event));
|
|
||||||
lmem::DestroyExpHeap(m_heap_handle);
|
|
||||||
m_heap_handle = nullptr;
|
|
||||||
|
|
||||||
/* Destroy interface objects. */
|
|
||||||
m_ds_service = nullptr;
|
|
||||||
m_root_service = nullptr;
|
|
||||||
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DsClient::IsInitialized() {
|
|
||||||
return m_is_initialized;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsClient::EnableDevice() {
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
/* Check that we're initialized. */
|
|
||||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
|
||||||
|
|
||||||
/* Enable all interfaces. */
|
|
||||||
if (hos::GetVersion() < hos::Version_11_0_0) {
|
|
||||||
for (size_t i = 0; i < util::size(m_interfaces); ++i) {
|
|
||||||
if (m_interfaces[i] != nullptr) {
|
|
||||||
R_TRY(m_interfaces[i]->Enable());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Enable the device. */
|
|
||||||
R_TRY(m_ds_service->Enable());
|
|
||||||
|
|
||||||
/* Mark disabled. */
|
|
||||||
m_is_enabled = true;
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsClient::DisableDevice() {
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
/* Check that we're initialized. */
|
|
||||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
|
||||||
|
|
||||||
/* Disable the device. */
|
|
||||||
R_TRY(m_ds_service->Disable());
|
|
||||||
|
|
||||||
/* Disable all interfaces. */
|
|
||||||
if (hos::GetVersion() < hos::Version_11_0_0) {
|
|
||||||
for (size_t i = 0; i < util::size(m_interfaces); ++i) {
|
|
||||||
if (m_interfaces[i] != nullptr) {
|
|
||||||
R_TRY(m_interfaces[i]->Disable());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mark disabled. */
|
|
||||||
m_is_enabled = false;
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
os::SystemEventType *DsClient::GetStateChangeEvent() {
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
return m_is_initialized ? std::addressof(m_state_change_event) : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsClient::GetState(UsbState *out) {
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
/* Check that we're initialized. */
|
|
||||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
|
||||||
|
|
||||||
/* Check that we have a service. */
|
|
||||||
AMS_ABORT_UNLESS(m_ds_service != nullptr);
|
|
||||||
|
|
||||||
return m_ds_service->GetState(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsClient::ClearDeviceData() {
|
|
||||||
return m_ds_service->ClearDeviceData();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsClient::AddUsbStringDescriptor(u8 *out_index, UsbStringDescriptor *desc) {
|
|
||||||
return m_ds_service->AddUsbStringDescriptor(out_index, sf::InBuffer(reinterpret_cast<const u8 *>(desc), sizeof(*desc)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsClient::DeleteUsbStringDescriptor(u8 index) {
|
|
||||||
return m_ds_service->DeleteUsbStringDescriptor(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsClient::SetUsbDeviceDescriptor(UsbDeviceDescriptor *desc, UsbDeviceSpeed speed) {
|
|
||||||
return m_ds_service->SetUsbDeviceDescriptor(sf::InBuffer(reinterpret_cast<const u8 *>(desc), sizeof(*desc)), speed);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsClient::SetBinaryObjectStore(u8 *data, int size) {
|
|
||||||
return m_ds_service->SetBinaryObjectStore(sf::InBuffer(reinterpret_cast<const u8 *>(data), size));
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsClient::AddInterface(DsInterface *intf, sf::SharedPointer<ds::IDsInterface> *out_srv, uint8_t bInterfaceNumber) {
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
/* Check that we're initialized. */
|
|
||||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
|
||||||
|
|
||||||
/* Check that we have a service. */
|
|
||||||
AMS_ABORT_UNLESS(m_ds_service != nullptr);
|
|
||||||
|
|
||||||
/* Register the interface. */
|
|
||||||
R_TRY(m_ds_service->RegisterInterface(out_srv, bInterfaceNumber));
|
|
||||||
|
|
||||||
/* Set interface. */
|
|
||||||
m_interfaces[bInterfaceNumber] = intf;
|
|
||||||
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsClient::DeleteInterface(uint8_t bInterfaceNumber) {
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
/* Check that we're initialized. */
|
|
||||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
|
||||||
|
|
||||||
/* Check that we have the interface. */
|
|
||||||
R_UNLESS(m_interfaces[bInterfaceNumber] != nullptr, usb::ResultOperationDenied());
|
|
||||||
|
|
||||||
/* Clear the interface. */
|
|
||||||
m_interfaces[bInterfaceNumber] = nullptr;
|
|
||||||
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsInterface::Initialize(DsClient *client, u8 bInterfaceNumber) {
|
|
||||||
/* Check that we haven't already initialized. */
|
|
||||||
R_UNLESS(!m_is_initialized, usb::ResultAlreadyInitialized());
|
|
||||||
|
|
||||||
/* Set our client. */
|
|
||||||
m_client = client;
|
|
||||||
|
|
||||||
/* Clear all endpoints. */
|
|
||||||
for (size_t i = 0; i < util::size(m_endpoints); ++i) {
|
|
||||||
m_endpoints[i] = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set our interface number. */
|
|
||||||
R_UNLESS(bInterfaceNumber < util::size(m_client->m_interfaces), usb::ResultInvalidParameter());
|
|
||||||
m_interface_num = bInterfaceNumber;
|
|
||||||
|
|
||||||
/* Add the interface. */
|
|
||||||
R_TRY(m_client->AddInterface(this, std::addressof(m_interface), m_interface_num));
|
|
||||||
|
|
||||||
/* Ensure we cleanup if we fail after this. */
|
|
||||||
auto intf_guard = SCOPE_GUARD { m_client->DeleteInterface(m_interface_num); m_interface = nullptr; };
|
|
||||||
|
|
||||||
/* Get events. */
|
|
||||||
sf::CopyHandle setup_event_handle;
|
|
||||||
sf::CopyHandle ctrl_in_event_handle;
|
|
||||||
sf::CopyHandle ctrl_out_event_handle;
|
|
||||||
R_TRY(m_interface->GetSetupEvent(std::addressof(setup_event_handle)));
|
|
||||||
R_TRY(m_interface->GetCtrlInCompletionEvent(std::addressof(ctrl_in_event_handle)));
|
|
||||||
R_TRY(m_interface->GetCtrlOutCompletionEvent(std::addressof(ctrl_out_event_handle)));
|
|
||||||
|
|
||||||
/* Attach events. */
|
|
||||||
os::AttachReadableHandleToSystemEvent(std::addressof(m_setup_event), setup_event_handle.GetValue(), true, os::EventClearMode_ManualClear);
|
|
||||||
os::AttachReadableHandleToSystemEvent(std::addressof(m_ctrl_in_completion_event), ctrl_in_event_handle.GetValue(), true, os::EventClearMode_ManualClear);
|
|
||||||
os::AttachReadableHandleToSystemEvent(std::addressof(m_ctrl_out_completion_event), ctrl_out_event_handle.GetValue(), true, os::EventClearMode_ManualClear);
|
|
||||||
|
|
||||||
/* Increment our client's reference count. */
|
|
||||||
++m_client->m_reference_count;
|
|
||||||
|
|
||||||
/* Set ourselves as initialized. */
|
|
||||||
m_is_initialized = true;
|
|
||||||
|
|
||||||
intf_guard.Cancel();
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsInterface::Finalize() {
|
|
||||||
/* Validate that we have a service. */
|
|
||||||
R_ABORT_UNLESS(m_interface != nullptr);
|
|
||||||
|
|
||||||
/* We must be disabled. */
|
|
||||||
R_UNLESS(!m_client->m_is_enabled, usb::ResultResourceBusy());
|
|
||||||
|
|
||||||
/* Finalize all endpoints. */
|
|
||||||
for (size_t i = 0; i < util::size(m_endpoints); ++i) {
|
|
||||||
if (m_endpoints[i] != nullptr) {
|
|
||||||
R_TRY(m_endpoints[i]->Finalize());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
/* Check that we're initialized. */
|
|
||||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
|
||||||
|
|
||||||
/* Check our reference count .*/
|
|
||||||
R_UNLESS(m_reference_count <= 1, usb::ResultResourceBusy());
|
|
||||||
|
|
||||||
/* Finalize members. */
|
|
||||||
m_is_initialized = false;
|
|
||||||
os::DestroySystemEvent(std::addressof(m_setup_event));
|
|
||||||
os::DestroySystemEvent(std::addressof(m_ctrl_in_completion_event));
|
|
||||||
os::DestroySystemEvent(std::addressof(m_ctrl_out_completion_event));
|
|
||||||
|
|
||||||
/* Delete ourselves from our cleint. */
|
|
||||||
m_client->DeleteInterface(m_interface_num);
|
|
||||||
|
|
||||||
/* Destroy our service. */
|
|
||||||
m_interface = nullptr;
|
|
||||||
|
|
||||||
/* Close our reference to our client. */
|
|
||||||
--m_client->m_reference_count;
|
|
||||||
m_client = nullptr;
|
|
||||||
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsInterface::AppendConfigurationData(UsbDeviceSpeed speed, void *data, u32 size) {
|
|
||||||
return m_interface->AppendConfigurationData(m_interface_num, speed, sf::InBuffer(data, size));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DsInterface::IsInitialized() {
|
|
||||||
return m_is_initialized;
|
|
||||||
}
|
|
||||||
|
|
||||||
os::SystemEventType *DsInterface::GetSetupEvent() {
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
return m_is_initialized ? std::addressof(m_setup_event) : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsInterface::GetSetupPacket(UsbCtrlRequest *out) {
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
/* Check that we're initialized. */
|
|
||||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
|
||||||
|
|
||||||
/* Check that we have a service. */
|
|
||||||
AMS_ABORT_UNLESS(m_interface != nullptr);
|
|
||||||
|
|
||||||
return m_interface->GetSetupPacket(sf::OutBuffer(out, sizeof(*out)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsInterface::Enable() {
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
/* Check that we're initialized. */
|
|
||||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
|
||||||
|
|
||||||
/* If we're already enabled, nothing to do. */
|
|
||||||
R_SUCCEED_IF(m_client->m_is_enabled);
|
|
||||||
|
|
||||||
/* Check that we have a service. */
|
|
||||||
AMS_ABORT_UNLESS(m_interface != nullptr);
|
|
||||||
|
|
||||||
/* Perform the enable. */
|
|
||||||
R_TRY(m_interface->Enable());
|
|
||||||
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsInterface::Disable() {
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
/* Check that we're initialized. */
|
|
||||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
|
||||||
|
|
||||||
/* If we're already disabled, nothing to do. */
|
|
||||||
R_SUCCEED_IF(!m_client->m_is_enabled);
|
|
||||||
|
|
||||||
/* Check that we have a service. */
|
|
||||||
AMS_ABORT_UNLESS(m_interface != nullptr);
|
|
||||||
|
|
||||||
/* Perform the disable. */
|
|
||||||
R_TRY(m_interface->Disable());
|
|
||||||
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsInterface::AddEndpoint(DsEndpoint *ep, u8 bEndpointAddress, sf::SharedPointer<ds::IDsEndpoint> *out) {
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
/* Check that we're initialized. */
|
|
||||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
|
||||||
|
|
||||||
/* Check that we're not already enabled. */
|
|
||||||
R_UNLESS(!m_client->m_is_enabled, usb::ResultOperationDenied());
|
|
||||||
|
|
||||||
/* Check that we have a service. */
|
|
||||||
AMS_ABORT_UNLESS(m_interface != nullptr);
|
|
||||||
|
|
||||||
/* Register the endpoint. */
|
|
||||||
R_TRY(m_interface->RegisterEndpoint(bEndpointAddress, out));
|
|
||||||
|
|
||||||
/* Set the endpoint. */
|
|
||||||
m_endpoints[impl::GetEndpointIndex(bEndpointAddress)] = ep;
|
|
||||||
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsInterface::DeleteEndpoint(u8 bEndpointAddress) {
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
/* Check that we're initialized. */
|
|
||||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
|
||||||
|
|
||||||
/* Check that we're disabled and have the endpoint. */
|
|
||||||
const auto index = impl::GetEndpointIndex(bEndpointAddress);
|
|
||||||
R_UNLESS(!m_client->m_is_enabled, usb::ResultOperationDenied());
|
|
||||||
R_UNLESS(m_endpoints[index] != nullptr, usb::ResultOperationDenied());
|
|
||||||
|
|
||||||
/* Clear the endpoint. */
|
|
||||||
m_endpoints[index] = nullptr;
|
|
||||||
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsInterface::CtrlIn(u32 *out_transferred, void *dst, u32 size) {
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
/* Check that we're initialized. */
|
|
||||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
|
||||||
|
|
||||||
/* Check that we're enabled. */
|
|
||||||
R_UNLESS(m_client->m_is_enabled, usb::ResultOperationDenied());
|
|
||||||
|
|
||||||
/* Check that the data is aligned. */
|
|
||||||
R_UNLESS(usb::IsDmaAligned(reinterpret_cast<u64>(dst)), usb::ResultAlignmentError());
|
|
||||||
|
|
||||||
/* If we should, flush cache. */
|
|
||||||
if (size != 0) {
|
|
||||||
dd::FlushDataCache(dst, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check that we have a service. */
|
|
||||||
AMS_ABORT_UNLESS(m_interface != nullptr);
|
|
||||||
|
|
||||||
/* Perform the transfer. */
|
|
||||||
u32 urb_id;
|
|
||||||
R_TRY(m_interface->CtrlInAsync(std::addressof(urb_id), reinterpret_cast<u64>(dst), size));
|
|
||||||
|
|
||||||
/* Wait for control to finish. */
|
|
||||||
os::WaitSystemEvent(std::addressof(m_ctrl_in_completion_event));
|
|
||||||
os::ClearSystemEvent(std::addressof(m_ctrl_in_completion_event));
|
|
||||||
|
|
||||||
/* Get the urb report. */
|
|
||||||
R_ABORT_UNLESS(m_interface->GetCtrlInUrbReport(std::addressof(m_report)));
|
|
||||||
|
|
||||||
/* Check the report is for our urb. */
|
|
||||||
R_UNLESS(m_report.count == 1, usb::ResultInternalStateError());
|
|
||||||
R_UNLESS(m_report.reports[0].id == urb_id, usb::ResultInternalStateError());
|
|
||||||
|
|
||||||
/* Set output bytes. */
|
|
||||||
if (out_transferred != nullptr) {
|
|
||||||
*out_transferred = m_report.reports[0].transferred_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handle the report. */
|
|
||||||
switch (m_report.reports[0].status) {
|
|
||||||
case UrbStatus_Cancelled:
|
|
||||||
return usb::ResultInterrupted();
|
|
||||||
case UrbStatus_Failed:
|
|
||||||
return usb::ResultTransactionError();
|
|
||||||
case UrbStatus_Finished:
|
|
||||||
return ResultSuccess();
|
|
||||||
default:
|
|
||||||
return usb::ResultInternalStateError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsInterface::CtrlOut(u32 *out_transferred, void *dst, u32 size) {
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
/* Check that we're initialized. */
|
|
||||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
|
||||||
|
|
||||||
/* Check that we're enabled. */
|
|
||||||
R_UNLESS(m_client->m_is_enabled, usb::ResultOperationDenied());
|
|
||||||
|
|
||||||
/* Check that the data is aligned. */
|
|
||||||
R_UNLESS(usb::IsDmaAligned(reinterpret_cast<u64>(dst)), usb::ResultAlignmentError());
|
|
||||||
|
|
||||||
/* If we should, invalidate cache. */
|
|
||||||
if (size != 0) {
|
|
||||||
dd::InvalidateDataCache(dst, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check that we have a service. */
|
|
||||||
AMS_ABORT_UNLESS(m_interface != nullptr);
|
|
||||||
|
|
||||||
/* Perform the transfer. */
|
|
||||||
u32 urb_id;
|
|
||||||
R_TRY(m_interface->CtrlOutAsync(std::addressof(urb_id), reinterpret_cast<u64>(dst), size));
|
|
||||||
|
|
||||||
/* Wait for control to finish. */
|
|
||||||
os::WaitSystemEvent(std::addressof(m_ctrl_out_completion_event));
|
|
||||||
os::ClearSystemEvent(std::addressof(m_ctrl_out_completion_event));
|
|
||||||
|
|
||||||
/* Ensure that cache remains consistent. */
|
|
||||||
ON_SCOPE_EXIT {
|
|
||||||
if (size != 0) {
|
|
||||||
dd::InvalidateDataCache(dst, size);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Get the urb report. */
|
|
||||||
R_ABORT_UNLESS(m_interface->GetCtrlOutUrbReport(std::addressof(m_report)));
|
|
||||||
|
|
||||||
/* Check the report is for our urb. */
|
|
||||||
R_UNLESS(m_report.count == 1, usb::ResultInternalStateError());
|
|
||||||
R_UNLESS(m_report.reports[0].id == urb_id, usb::ResultInternalStateError());
|
|
||||||
|
|
||||||
/* Set output bytes. */
|
|
||||||
if (out_transferred != nullptr) {
|
|
||||||
*out_transferred = m_report.reports[0].transferred_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handle the report. */
|
|
||||||
switch (m_report.reports[0].status) {
|
|
||||||
case UrbStatus_Cancelled:
|
|
||||||
return usb::ResultInterrupted();
|
|
||||||
case UrbStatus_Failed:
|
|
||||||
return usb::ResultTransactionError();
|
|
||||||
case UrbStatus_Finished:
|
|
||||||
return ResultSuccess();
|
|
||||||
default:
|
|
||||||
return usb::ResultInternalStateError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsInterface::CtrlRead(u32 *out_transferred, void *dst, u32 size) {
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
/* Check that we're initialized. */
|
|
||||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
|
||||||
|
|
||||||
/* Do the data transfer. */
|
|
||||||
Result result = this->CtrlOut(out_transferred, dst, size);
|
|
||||||
|
|
||||||
/* Do the status transfer. */
|
|
||||||
if (R_SUCCEEDED(result)) {
|
|
||||||
result = this->CtrlIn(nullptr, nullptr, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we failed, stall. */
|
|
||||||
if (R_FAILED(result)) {
|
|
||||||
result = this->CtrlStall();
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsInterface::CtrlWrite(u32 *out_transferred, void *dst, u32 size) {
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
/* Check that we're initialized. */
|
|
||||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
|
||||||
|
|
||||||
/* Do the data transfer. */
|
|
||||||
Result result = this->CtrlIn(out_transferred, dst, size);
|
|
||||||
|
|
||||||
/* Do the status transfer. */
|
|
||||||
if (R_SUCCEEDED(result)) {
|
|
||||||
result = this->CtrlOut(nullptr, nullptr, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we failed, stall. */
|
|
||||||
if (R_FAILED(result)) {
|
|
||||||
result = this->CtrlStall();
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsInterface::CtrlDone() {
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
/* Check that we're initialized. */
|
|
||||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
|
||||||
|
|
||||||
/* Do the status transfer. */
|
|
||||||
Result result = this->CtrlIn(nullptr, nullptr, 0);
|
|
||||||
|
|
||||||
/* If we failed, stall. */
|
|
||||||
if (R_FAILED(result)) {
|
|
||||||
result = this->CtrlStall();
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsInterface::CtrlStall() {
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
/* Check that we're initialized. */
|
|
||||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
|
||||||
|
|
||||||
/* Check that we're enabled. */
|
|
||||||
R_UNLESS(m_client->m_is_enabled, usb::ResultOperationDenied());
|
|
||||||
|
|
||||||
/* Check that we have a service. */
|
|
||||||
AMS_ABORT_UNLESS(m_interface != nullptr);
|
|
||||||
|
|
||||||
return m_interface->CtrlStall();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsEndpoint::Initialize(DsInterface *interface, u8 bEndpointAddress) {
|
|
||||||
/* Check that the interface is valid. */
|
|
||||||
AMS_ABORT_UNLESS(interface != nullptr);
|
|
||||||
|
|
||||||
/* Check that we're not already initialized. */
|
|
||||||
R_UNLESS(!m_is_initialized, usb::ResultAlreadyInitialized());
|
|
||||||
|
|
||||||
/* Set our interface. */
|
|
||||||
m_interface = interface;
|
|
||||||
|
|
||||||
/* Add the endpoint. */
|
|
||||||
R_TRY(m_interface->AddEndpoint(this, bEndpointAddress, std::addressof(m_endpoint)));
|
|
||||||
|
|
||||||
/* Set our address. */
|
|
||||||
m_address = bEndpointAddress;
|
|
||||||
|
|
||||||
/* Ensure we clean up if we fail after this. */
|
|
||||||
auto ep_guard = SCOPE_GUARD { m_interface->DeleteEndpoint(m_address); m_endpoint = nullptr; };
|
|
||||||
|
|
||||||
/* Get completion event. */
|
|
||||||
sf::CopyHandle event_handle;
|
|
||||||
R_TRY(m_endpoint->GetCompletionEvent(std::addressof(event_handle)));
|
|
||||||
|
|
||||||
/* Increment our interface's reference count. */
|
|
||||||
++m_interface->m_reference_count;
|
|
||||||
++m_interface->m_client->m_reference_count;
|
|
||||||
|
|
||||||
/* Attach our event. */
|
|
||||||
os::AttachReadableHandleToSystemEvent(std::addressof(m_completion_event), event_handle, true, os::EventClearMode_ManualClear);
|
|
||||||
|
|
||||||
/* Mark initialized. */
|
|
||||||
m_is_initialized = true;
|
|
||||||
|
|
||||||
ep_guard.Cancel();
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsEndpoint::Finalize() {
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
/* Check that we're initialized. */
|
|
||||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
|
||||||
|
|
||||||
/* Cancel any pending transactions. */
|
|
||||||
m_endpoint->Cancel();
|
|
||||||
|
|
||||||
/* Wait for us to be at one reference count. */
|
|
||||||
while (m_reference_count > 1) {
|
|
||||||
os::SleepThread(TimeSpan::FromMilliSeconds(25));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Destroy our event. */
|
|
||||||
os::DestroySystemEvent(std::addressof(m_completion_event));
|
|
||||||
|
|
||||||
/* Decrement our interface's reference count. */
|
|
||||||
--m_interface->m_reference_count;
|
|
||||||
--m_interface->m_client->m_reference_count;
|
|
||||||
|
|
||||||
/* Delete ourselves. */
|
|
||||||
R_TRY(m_interface->DeleteEndpoint(m_address));
|
|
||||||
|
|
||||||
/* Clear ourselves. */
|
|
||||||
m_interface = nullptr;
|
|
||||||
m_endpoint = nullptr;
|
|
||||||
|
|
||||||
/* Mark uninitialized. */
|
|
||||||
m_is_initialized = false;
|
|
||||||
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DsEndpoint::IsInitialized() {
|
|
||||||
return m_is_initialized;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsEndpoint::PostBuffer(u32 *out_transferred, void *buf, u32 size) {
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
/* Check that we're initialized. */
|
|
||||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
|
||||||
|
|
||||||
/* Post buffer. */
|
|
||||||
u32 urb_id;
|
|
||||||
R_TRY(this->PostBufferAsync(std::addressof(urb_id), buf, size));
|
|
||||||
|
|
||||||
/* Wait for completion. */
|
|
||||||
os::WaitSystemEvent(std::addressof(m_completion_event));
|
|
||||||
os::ClearSystemEvent(std::addressof(m_completion_event));
|
|
||||||
|
|
||||||
/* Get URB report. */
|
|
||||||
UrbReport report;
|
|
||||||
AMS_ABORT_UNLESS(m_endpoint != nullptr);
|
|
||||||
R_ABORT_UNLESS(m_endpoint->GetUrbReport(std::addressof(report)));
|
|
||||||
|
|
||||||
/* Check the report is for our urb. */
|
|
||||||
R_UNLESS(report.count == 1, usb::ResultInternalStateError());
|
|
||||||
R_UNLESS(report.reports[0].id == urb_id, usb::ResultInternalStateError());
|
|
||||||
|
|
||||||
/* Set output bytes. */
|
|
||||||
if (out_transferred != nullptr) {
|
|
||||||
*out_transferred = report.reports[0].transferred_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handle the report. */
|
|
||||||
switch (report.reports[0].status) {
|
|
||||||
case UrbStatus_Cancelled:
|
|
||||||
return usb::ResultInterrupted();
|
|
||||||
case UrbStatus_Failed:
|
|
||||||
return usb::ResultTransactionError();
|
|
||||||
case UrbStatus_Finished:
|
|
||||||
return ResultSuccess();
|
|
||||||
default:
|
|
||||||
return usb::ResultInternalStateError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsEndpoint::PostBufferAsync(u32 *out_urb_id, void *buf, u32 size) {
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
/* Check that we're initialized. */
|
|
||||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
|
||||||
|
|
||||||
/* Check that the buffer is DMA aligned. */
|
|
||||||
R_UNLESS(usb::IsDmaAligned(reinterpret_cast<u64>(buf)), usb::ResultAlignmentError());
|
|
||||||
|
|
||||||
/* Check that we have a service. */
|
|
||||||
AMS_ABORT_UNLESS(m_endpoint != nullptr);
|
|
||||||
|
|
||||||
/* Post */
|
|
||||||
u32 urb_id = 0;
|
|
||||||
R_TRY(m_endpoint->PostBufferAsync(std::addressof(urb_id), reinterpret_cast<u64>(buf), size));
|
|
||||||
|
|
||||||
*out_urb_id = urb_id;
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
os::SystemEventType *DsEndpoint::GetCompletionEvent() {
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
return m_is_initialized ? std::addressof(m_completion_event) : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsEndpoint::GetUrbReport(UrbReport *out) {
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
/* Check that we're initialized. */
|
|
||||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
|
||||||
|
|
||||||
/* Check that we have a service. */
|
|
||||||
AMS_ABORT_UNLESS(m_endpoint != nullptr);
|
|
||||||
|
|
||||||
return m_endpoint->GetUrbReport(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsEndpoint::Cancel() {
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
/* Check that we're initialized. */
|
|
||||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
|
||||||
|
|
||||||
/* Check that we have a service. */
|
|
||||||
AMS_ABORT_UNLESS(m_endpoint != nullptr);
|
|
||||||
|
|
||||||
return m_endpoint->Cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result DsEndpoint::SetZeroLengthTransfer(bool zlt) {
|
|
||||||
/* Create a scoped reference. */
|
|
||||||
impl::ScopedRefCount scoped_ref(m_reference_count);
|
|
||||||
|
|
||||||
/* Check that we're initialized. */
|
|
||||||
R_UNLESS(m_is_initialized, usb::ResultNotInitialized());
|
|
||||||
|
|
||||||
/* Check that we have a service. */
|
|
||||||
AMS_ABORT_UNLESS(m_endpoint != nullptr);
|
|
||||||
|
|
||||||
return m_endpoint->SetZlt(zlt);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
#include "usb_remote_ds_endpoint.hpp"
|
|
||||||
|
|
||||||
namespace ams::usb {
|
|
||||||
|
|
||||||
Result RemoteDsEndpoint::PostBufferAsync(sf::Out<u32> out_urb_id, u64 address, u32 size) {
|
|
||||||
const struct {
|
|
||||||
u32 size;
|
|
||||||
u64 address;
|
|
||||||
} in = { size, address };
|
|
||||||
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatchInOut(std::addressof(m_srv), 0, in, *out_urb_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsEndpoint::Cancel() {
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatch(std::addressof(m_srv), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsEndpoint::GetCompletionEvent(sf::OutCopyHandle out) {
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatch(std::addressof(m_srv), 2,
|
|
||||||
.out_handle_attrs = { SfOutHandleAttr_HipcCopy },
|
|
||||||
.out_handles = out.GetHandlePointer(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsEndpoint::GetUrbReport(sf::Out<usb::UrbReport> out) {
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatchOut(std::addressof(m_srv), 3, *out);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsEndpoint::Stall() {
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatch(std::addressof(m_srv), 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsEndpoint::SetZlt(bool zlt) {
|
|
||||||
const u8 in = zlt ? 1 : 0;
|
|
||||||
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatchIn(std::addressof(m_srv), 5, in);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
#include "usb_remote_ds_interface.hpp"
|
|
||||||
#include "usb_remote_ds_endpoint.hpp"
|
|
||||||
|
|
||||||
namespace ams::usb {
|
|
||||||
|
|
||||||
Result RemoteDsInterface::RegisterEndpoint(u8 endpoint_address, sf::Out<sf::SharedPointer<usb::ds::IDsEndpoint>> out) {
|
|
||||||
Service srv;
|
|
||||||
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
R_TRY(serviceDispatchIn(std::addressof(m_srv), 0, endpoint_address,
|
|
||||||
.out_num_objects = 1,
|
|
||||||
.out_objects = std::addressof(srv),
|
|
||||||
));
|
|
||||||
|
|
||||||
*out = ObjectFactory::CreateSharedEmplaced<ds::IDsEndpoint, RemoteDsEndpoint>(m_allocator, srv);
|
|
||||||
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsInterface::GetSetupEvent(sf::OutCopyHandle out) {
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatch(std::addressof(m_srv), 1,
|
|
||||||
.out_handle_attrs = { SfOutHandleAttr_HipcCopy },
|
|
||||||
.out_handles = out.GetHandlePointer(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsInterface::GetSetupPacket(const sf::OutBuffer &out) {
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatch(std::addressof(m_srv), 2,
|
|
||||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
|
|
||||||
.buffers = { { out.GetPointer(), out.GetSize() } },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsInterface::CtrlInAsync(sf::Out<u32> out_urb_id, u64 address, u32 size) {
|
|
||||||
const struct {
|
|
||||||
u32 size;
|
|
||||||
u64 address;
|
|
||||||
} in = { size, address };
|
|
||||||
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatchInOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 3 : 5, in, *out_urb_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsInterface::CtrlOutAsync(sf::Out<u32> out_urb_id, u64 address, u32 size) {
|
|
||||||
const struct {
|
|
||||||
u32 size;
|
|
||||||
u64 address;
|
|
||||||
} in = { size, address };
|
|
||||||
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatchInOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 4 : 6, in, *out_urb_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsInterface::GetCtrlInCompletionEvent(sf::OutCopyHandle out) {
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 5 : 7,
|
|
||||||
.out_handle_attrs = { SfOutHandleAttr_HipcCopy },
|
|
||||||
.out_handles = out.GetHandlePointer(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsInterface::GetCtrlInUrbReport(sf::Out<usb::UrbReport> out) {
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatchOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 6 : 8, *out);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsInterface::GetCtrlOutCompletionEvent(sf::OutCopyHandle out) {
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 7 : 9,
|
|
||||||
.out_handle_attrs = { SfOutHandleAttr_HipcCopy },
|
|
||||||
.out_handles = out.GetHandlePointer(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsInterface::GetCtrlOutUrbReport(sf::Out<usb::UrbReport> out) {
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatchOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 8 : 10, *out);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsInterface::CtrlStall() {
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 9 : 11);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsInterface::AppendConfigurationData(u8 bInterfaceNumber, usb::UsbDeviceSpeed device_speed, const sf::InBuffer &data) {
|
|
||||||
if (hos::GetVersion() >= hos::Version_11_0_0) {
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatchIn(std::addressof(m_srv), 10, device_speed,
|
|
||||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
|
|
||||||
.buffers = { { data.GetPointer(), data.GetSize() } },
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const struct {
|
|
||||||
u8 bInterfaceNumber;
|
|
||||||
usb::UsbDeviceSpeed device_speed;
|
|
||||||
} in = { bInterfaceNumber, device_speed };
|
|
||||||
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatchIn(std::addressof(m_srv), 12, in,
|
|
||||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
|
|
||||||
.buffers = { { data.GetPointer(), data.GetSize() } },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsInterface::Enable() {
|
|
||||||
R_SUCCEED_IF(hos::GetVersion() >= hos::Version_11_0_0);
|
|
||||||
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatch(std::addressof(m_srv), 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsInterface::Disable() {
|
|
||||||
R_SUCCEED_IF(hos::GetVersion() >= hos::Version_11_0_0);
|
|
||||||
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatch(std::addressof(m_srv), 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
|
|
||||||
namespace ams::usb {
|
|
||||||
|
|
||||||
class RemoteDsInterface {
|
|
||||||
private:
|
|
||||||
using Allocator = sf::ExpHeapAllocator;
|
|
||||||
using ObjectFactory = sf::ObjectFactory<Allocator::Policy>;
|
|
||||||
private:
|
|
||||||
Service m_srv;
|
|
||||||
Allocator *m_allocator;
|
|
||||||
public:
|
|
||||||
RemoteDsInterface(Service &srv, sf::ExpHeapAllocator *allocator) : m_srv(srv), m_allocator(allocator) { /* ... */ }
|
|
||||||
virtual ~RemoteDsInterface() { serviceClose(std::addressof(m_srv)); }
|
|
||||||
public:
|
|
||||||
Result RegisterEndpoint(u8 endpoint_address, sf::Out<sf::SharedPointer<usb::ds::IDsEndpoint>> out);
|
|
||||||
Result GetSetupEvent(sf::OutCopyHandle out);
|
|
||||||
Result GetSetupPacket(const sf::OutBuffer & out);
|
|
||||||
Result CtrlInAsync(sf::Out<u32> out_urb_id, u64 address, u32 size);
|
|
||||||
Result CtrlOutAsync(sf::Out<u32> out_urb_id, u64 address, u32 size);
|
|
||||||
Result GetCtrlInCompletionEvent(sf::OutCopyHandle out);
|
|
||||||
Result GetCtrlInUrbReport(sf::Out<usb::UrbReport> out);
|
|
||||||
Result GetCtrlOutCompletionEvent(sf::OutCopyHandle out);
|
|
||||||
Result GetCtrlOutUrbReport(sf::Out<usb::UrbReport> out);
|
|
||||||
Result CtrlStall();
|
|
||||||
Result AppendConfigurationData(u8 bInterfaceNumber, usb::UsbDeviceSpeed device_speed, const sf::InBuffer &data);
|
|
||||||
Result Enable();
|
|
||||||
Result Disable();
|
|
||||||
};
|
|
||||||
static_assert(ds::IsIDsInterface<RemoteDsInterface>);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
|
|
||||||
namespace ams::usb {
|
|
||||||
|
|
||||||
class RemoteDsRootService {
|
|
||||||
private:
|
|
||||||
using Allocator = sf::ExpHeapAllocator;
|
|
||||||
using ObjectFactory = sf::ObjectFactory<Allocator::Policy>;
|
|
||||||
private:
|
|
||||||
Service m_srv;
|
|
||||||
Allocator *m_allocator;
|
|
||||||
public:
|
|
||||||
RemoteDsRootService(Service &srv, sf::ExpHeapAllocator *allocator) : m_srv(srv), m_allocator(allocator) { /* ... */ }
|
|
||||||
virtual ~RemoteDsRootService() { serviceClose(std::addressof(m_srv)); }
|
|
||||||
public:
|
|
||||||
Result GetService(sf::Out<sf::SharedPointer<usb::ds::IDsService>> out);
|
|
||||||
};
|
|
||||||
static_assert(ds::IsIDsRootService<RemoteDsRootService>);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
#include "usb_remote_ds_service.hpp"
|
|
||||||
#include "usb_remote_ds_interface.hpp"
|
|
||||||
|
|
||||||
namespace ams::usb {
|
|
||||||
|
|
||||||
Result RemoteDsService::Bind(usb::ComplexId complex_id, sf::CopyHandle process_h) {
|
|
||||||
if (hos::GetVersion() >= hos::Version_11_0_0) {
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
R_TRY(serviceDispatchIn(std::addressof(m_srv), 0, complex_id,
|
|
||||||
.in_num_handles = 1,
|
|
||||||
.in_handles = { process_h.GetValue() }
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
R_TRY(serviceDispatchIn(std::addressof(m_srv), 0, complex_id));
|
|
||||||
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
R_TRY(serviceDispatch(std::addressof(m_srv), 1,
|
|
||||||
.in_num_handles = 1,
|
|
||||||
.in_handles = { process_h.GetValue() })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsService::RegisterInterface(sf::Out<sf::SharedPointer<usb::ds::IDsInterface>> out, u8 bInterfaceNumber) {
|
|
||||||
Service srv;
|
|
||||||
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
R_TRY(serviceDispatchIn(std::addressof(m_srv), (hos::GetVersion() >= hos::Version_11_0_0 ? 1 : 2), bInterfaceNumber,
|
|
||||||
.out_num_objects = 1,
|
|
||||||
.out_objects = std::addressof(srv),
|
|
||||||
));
|
|
||||||
|
|
||||||
*out = ObjectFactory::CreateSharedEmplaced<ds::IDsInterface, RemoteDsInterface>(m_allocator, srv, m_allocator);
|
|
||||||
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsService::GetStateChangeEvent(sf::OutCopyHandle out) {
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 2 : 3,
|
|
||||||
.out_handle_attrs = { SfOutHandleAttr_HipcCopy },
|
|
||||||
.out_handles = out.GetHandlePointer(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsService::GetState(sf::Out<usb::UsbState> out) {
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatchOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 3 : 4, *out);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsService::ClearDeviceData() {
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 4 : 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsService::AddUsbStringDescriptor(sf::Out<u8> out, const sf::InBuffer &desc) {
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatchOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 5 : 6, *out,
|
|
||||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
|
|
||||||
.buffers = { { desc.GetPointer(), desc.GetSize() } },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsService::DeleteUsbStringDescriptor(u8 index) {
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatchIn(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 6 : 7, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsService::SetUsbDeviceDescriptor(const sf::InBuffer &desc, usb::UsbDeviceSpeed speed) {
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatchIn(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 7 : 8, speed,
|
|
||||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
|
|
||||||
.buffers = { { desc.GetPointer(), desc.GetSize() } },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsService::SetBinaryObjectStore(const sf::InBuffer &bos) {
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 8 : 9,
|
|
||||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
|
|
||||||
.buffers = { { bos.GetPointer(), bos.GetSize() } },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsService::Enable() {
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 9 : 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result RemoteDsService::Disable() {
|
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
|
||||||
return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 10 : 11);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
|
|
||||||
namespace ams::usb {
|
|
||||||
|
|
||||||
class RemoteDsService {
|
|
||||||
private:
|
|
||||||
using Allocator = sf::ExpHeapAllocator;
|
|
||||||
using ObjectFactory = sf::ObjectFactory<Allocator::Policy>;
|
|
||||||
private:
|
|
||||||
Service m_srv;
|
|
||||||
Allocator *m_allocator;
|
|
||||||
public:
|
|
||||||
RemoteDsService(Service &srv, sf::ExpHeapAllocator *allocator) : m_srv(srv), m_allocator(allocator) { /* ... */ }
|
|
||||||
virtual ~RemoteDsService() { serviceClose(std::addressof(m_srv)); }
|
|
||||||
public:
|
|
||||||
Result Bind(usb::ComplexId complex_id, sf::CopyHandle process_h);
|
|
||||||
Result RegisterInterface(sf::Out<sf::SharedPointer<usb::ds::IDsInterface>> out, u8 bInterfaceNumber);
|
|
||||||
Result GetStateChangeEvent(sf::OutCopyHandle out);
|
|
||||||
Result GetState(sf::Out<usb::UsbState> out);
|
|
||||||
Result ClearDeviceData();
|
|
||||||
Result AddUsbStringDescriptor(sf::Out<u8> out, const sf::InBuffer &desc);
|
|
||||||
Result DeleteUsbStringDescriptor(u8 index);
|
|
||||||
Result SetUsbDeviceDescriptor(const sf::InBuffer &desc, usb::UsbDeviceSpeed speed);
|
|
||||||
Result SetBinaryObjectStore(const sf::InBuffer &bos);
|
|
||||||
Result Enable();
|
|
||||||
Result Disable();
|
|
||||||
};
|
|
||||||
static_assert(ds::IsIDsService<RemoteDsService>);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -39,3 +39,4 @@
|
|||||||
|
|
||||||
#include <vapours/dd.hpp>
|
#include <vapours/dd.hpp>
|
||||||
#include <vapours/sdmmc.hpp>
|
#include <vapours/sdmmc.hpp>
|
||||||
|
#include <vapours/prfile2.hpp>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 0
|
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 0
|
||||||
#define ATMOSPHERE_RELEASE_VERSION_MINOR 18
|
#define ATMOSPHERE_RELEASE_VERSION_MINOR 18
|
||||||
#define ATMOSPHERE_RELEASE_VERSION_MICRO 1
|
#define ATMOSPHERE_RELEASE_VERSION_MICRO 0
|
||||||
|
|
||||||
#define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO
|
#define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO
|
||||||
|
|
||||||
|
|||||||
@@ -13,21 +13,19 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include <stratosphere.hpp>
|
|
||||||
#include "usb_remote_ds_root_service.hpp"
|
|
||||||
#include "usb_remote_ds_service.hpp"
|
|
||||||
|
|
||||||
namespace ams::usb {
|
#pragma once
|
||||||
|
#include <vapours/common.hpp>
|
||||||
|
#include <vapours/assert.hpp>
|
||||||
|
#include <vapours/results.hpp>
|
||||||
|
#include <vapours/util.hpp>
|
||||||
|
#include <vapours/svc.hpp>
|
||||||
|
|
||||||
Result RemoteDsRootService::GetService(sf::Out<sf::SharedPointer<usb::ds::IDsService>> out) {
|
#include <vapours/prfile2/prfile2_common.hpp>
|
||||||
Service srv;
|
#include <vapours/prfile2/prfile2_str.hpp>
|
||||||
|
#include <vapours/prfile2/prfile2_system.hpp>
|
||||||
serviceAssumeDomain(std::addressof(m_srv));
|
#include <vapours/prfile2/pdm/prfile2_pdm_api.hpp>
|
||||||
R_TRY(serviceDispatch(std::addressof(m_srv), 0, .out_num_objects = 1, .out_objects = std::addressof(srv)));
|
#include <vapours/prfile2/pdm/prfile2_pdm_disk_management.hpp>
|
||||||
|
#include <vapours/prfile2/pdm/prfile2_pdm_upper_layer_api.hpp>
|
||||||
*out = ObjectFactory::CreateSharedEmplaced<ds::IDsService, RemoteDsService>(m_allocator, srv, m_allocator);
|
#include <vapours/prfile2/prfile2_fatfs.hpp>
|
||||||
|
#include <vapours/prfile2/prfile2_volume.hpp>
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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/prfile2/pdm/prfile2_pdm_types.hpp>
|
||||||
|
#include <vapours/prfile2/pdm/prfile2_pdm_common.hpp>
|
||||||
|
#include <vapours/prfile2/pdm/prfile2_pdm_disk_management.hpp>
|
||||||
|
|
||||||
|
namespace ams::prfile2::pdm {
|
||||||
|
|
||||||
|
pdm::Error Initialize(u32 config, void *param);
|
||||||
|
|
||||||
|
pdm::Error OpenDisk(InitDisk *init_disk_table, HandleType *out);
|
||||||
|
pdm::Error CloseDisk(HandleType handle);
|
||||||
|
|
||||||
|
pdm::Error OpenPartition(HandleType disk_handle, u16 part_id, HandleType *out);
|
||||||
|
pdm::Error ClosePartition(HandleType handle);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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 <vapours/prfile2/prfile2_build_config.hpp>
|
||||||
|
#include <vapours/prfile2/prfile2_handle.hpp>
|
||||||
|
#include <vapours/prfile2/pdm/prfile2_pdm_types.hpp>
|
||||||
|
|
||||||
|
namespace ams::prfile2::pdm {
|
||||||
|
|
||||||
|
constexpr inline const auto MaxDisks = 5;
|
||||||
|
constexpr inline const auto MaxPartitions = 5;
|
||||||
|
|
||||||
|
struct Disk;
|
||||||
|
struct Partition;
|
||||||
|
|
||||||
|
using PartitionEventCallback = void (*)(u32 event, void *param);
|
||||||
|
|
||||||
|
using EraseFunction = pdm::Error (*)(u32, u32);
|
||||||
|
|
||||||
|
struct DiskInfo {
|
||||||
|
u32 total_sectors;
|
||||||
|
u16 cylinders;
|
||||||
|
u8 heads;
|
||||||
|
u8 sectors_per_track;
|
||||||
|
u16 bytes_per_sector;
|
||||||
|
u32 media_attr;
|
||||||
|
void *format_param;
|
||||||
|
/* ... */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FunctionTable {
|
||||||
|
pdm::Error (*initialize)(HandleType disk_handle);
|
||||||
|
pdm::Error (*finalize)(HandleType disk_handle);
|
||||||
|
pdm::Error (*mount)(HandleType disk_handle);
|
||||||
|
pdm::Error (*unmount)(HandleType disk_handle);
|
||||||
|
pdm::Error (*format)(HandleType disk_handle, const u8 *);
|
||||||
|
pdm::Error (*physical_read)(HandleType disk_handle, u8 *dst, u32 block, u32 count, u32 *num_read);
|
||||||
|
pdm::Error (*physical_write)(HandleType disk_handle, const u8 *src, u32 block, u32 count, u32 *num_read);
|
||||||
|
pdm::Error (*get_disk_info)(HandleType disk_handle, DiskInfo *out);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DiskTable {
|
||||||
|
FunctionTable *function_table;
|
||||||
|
u64 ui_ext;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct InitDisk {
|
||||||
|
pdm::Error (*function)(DiskTable *disk_table, u64 ui_ext);
|
||||||
|
u64 ui_ext;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ... */
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* 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/prfile2/prfile2_handle.hpp>
|
||||||
|
#include <vapours/prfile2/pdm/prfile2_pdm_common.hpp>
|
||||||
|
|
||||||
|
namespace ams::prfile2::pdm {
|
||||||
|
|
||||||
|
struct Disk {
|
||||||
|
u32 status;
|
||||||
|
DiskTable disk_table;
|
||||||
|
u32 signature;
|
||||||
|
u16 open_count;
|
||||||
|
u16 lock_count;
|
||||||
|
Disk *lock_handle;
|
||||||
|
DiskInfo disk_info;
|
||||||
|
InitDisk *init_disk_table;
|
||||||
|
HandleType current_partition_handle;
|
||||||
|
DiskCallback erase_callback;
|
||||||
|
bool is_inserted;
|
||||||
|
volatile NonBlockingProtocolType nbc;
|
||||||
|
volatile NonBlockingProtocolType nbc_detect;
|
||||||
|
volatile NonBlockingProtocolType nbc_req;
|
||||||
|
|
||||||
|
template<size_t Ix>
|
||||||
|
constexpr ALWAYS_INLINE bool GetStatusBit() const {
|
||||||
|
constexpr u32 Mask = (1u << Ix);
|
||||||
|
return (this->status & Mask) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t Ix>
|
||||||
|
constexpr ALWAYS_INLINE void SetStatusBit(bool en) {
|
||||||
|
constexpr u32 Mask = (1u << Ix);
|
||||||
|
if (en) {
|
||||||
|
this->status |= Mask;
|
||||||
|
} else {
|
||||||
|
this->status &= ~Mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool IsOpen() const { return this->GetStatusBit<0>(); }
|
||||||
|
constexpr void SetOpen(bool en) { this->SetStatusBit<0>(en); }
|
||||||
|
|
||||||
|
constexpr bool IsLocked() const { return this->GetStatusBit<1>(); }
|
||||||
|
constexpr void SetLocked(bool en) { this->SetStatusBit<1>(en); }
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace disk {
|
||||||
|
|
||||||
|
pdm::Error OpenDisk(InitDisk *init_disk_table, HandleType *out);
|
||||||
|
pdm::Error CloseDisk(HandleType handle);
|
||||||
|
|
||||||
|
/* ... */
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -14,24 +14,30 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <stratosphere.hpp>
|
#include <vapours/prfile2/prfile2_handle.hpp>
|
||||||
|
#include <vapours/prfile2/pdm/prfile2_pdm_common.hpp>
|
||||||
|
#include <vapours/prfile2/pdm/prfile2_pdm_disk.hpp>
|
||||||
|
#include <vapours/prfile2/pdm/prfile2_pdm_partition.hpp>
|
||||||
|
|
||||||
namespace ams::usb {
|
namespace ams::prfile2::pdm {
|
||||||
|
|
||||||
class RemoteDsEndpoint {
|
struct PartitionHolder {
|
||||||
private:
|
u32 signature;
|
||||||
Service m_srv;
|
Partition *partition;
|
||||||
public:
|
};
|
||||||
RemoteDsEndpoint(Service &srv) : m_srv(srv) { /* ... */ }
|
|
||||||
virtual ~RemoteDsEndpoint() { serviceClose(std::addressof(m_srv)); }
|
struct DiskHolder {
|
||||||
public:
|
u32 signature;
|
||||||
Result PostBufferAsync(sf::Out<u32> out_urb_id, u64 address, u32 size);
|
Disk *disk;
|
||||||
Result Cancel();
|
};
|
||||||
Result GetCompletionEvent(sf::OutCopyHandle out);
|
|
||||||
Result GetUrbReport(sf::Out<usb::UrbReport> out);
|
struct DiskSet {
|
||||||
Result Stall();
|
u16 num_partitions;
|
||||||
Result SetZlt(bool zlt);
|
u16 num_allocated_disks;
|
||||||
|
DiskHolder disk_holders[MaxDisks];
|
||||||
|
PartitionHolder partition_holders[MaxPartitions];
|
||||||
|
Disk disks[MaxDisks];
|
||||||
|
Partition partitions[MaxPartitions];
|
||||||
};
|
};
|
||||||
static_assert(ds::IsIDsEndpoint<RemoteDsEndpoint>);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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/prfile2/prfile2_handle.hpp>
|
||||||
|
#include <vapours/prfile2/pdm/prfile2_pdm_common.hpp>
|
||||||
|
|
||||||
|
namespace ams::prfile2::pdm {
|
||||||
|
|
||||||
|
struct Partition {
|
||||||
|
u32 status;
|
||||||
|
HandleType disk_handle;
|
||||||
|
u32 signature;
|
||||||
|
u16 partition_id;
|
||||||
|
u16 open_count;
|
||||||
|
Partition *lock_handle;
|
||||||
|
u32 start_sector;
|
||||||
|
u32 total_sector;
|
||||||
|
u32 mbr_sector;
|
||||||
|
u8 partition_type;
|
||||||
|
pdm::Error last_driver_error;
|
||||||
|
void *volume;
|
||||||
|
volatile NonBlockingProtocolType nbc_detect;
|
||||||
|
PartitionEventCallback event_callback;
|
||||||
|
void *event_callback_param;
|
||||||
|
|
||||||
|
template<size_t Ix>
|
||||||
|
constexpr ALWAYS_INLINE bool GetStatusBit() const {
|
||||||
|
constexpr u32 Mask = (1u << Ix);
|
||||||
|
return (this->status & Mask) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t Ix>
|
||||||
|
constexpr ALWAYS_INLINE void SetStatusBit(bool en) {
|
||||||
|
constexpr u32 Mask = (1u << Ix);
|
||||||
|
if (en) {
|
||||||
|
this->status |= Mask;
|
||||||
|
} else {
|
||||||
|
this->status &= ~Mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool IsOpen() const { return this->GetStatusBit<0>(); }
|
||||||
|
constexpr void SetOpen(bool en) { this->SetStatusBit<0>(en); }
|
||||||
|
|
||||||
|
constexpr bool IsLocked() const { return this->GetStatusBit<1>(); }
|
||||||
|
constexpr void SetLocked(bool en) { this->SetStatusBit<1>(en); }
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace part {
|
||||||
|
|
||||||
|
pdm::Error OpenPartition(HandleType disk_handle, u16 partition_id, HandleType *out);
|
||||||
|
pdm::Error ClosePartition(HandleType part_handle);
|
||||||
|
|
||||||
|
void SetDriverErrorCode(HandleType part_handle, pdm::Error err);
|
||||||
|
|
||||||
|
void CheckPartitionOpen(HandleType disk_handle, bool *out);
|
||||||
|
void NotifyMediaEvent(HandleType disk_handle, u32 event);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* 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/prfile2/prfile2_build_config.hpp>
|
||||||
|
#include <vapours/prfile2/prfile2_handle.hpp>
|
||||||
|
|
||||||
|
namespace ams::prfile2::pdm {
|
||||||
|
|
||||||
|
enum Error {
|
||||||
|
Error_NoError = 0,
|
||||||
|
Error_Ok = Error_NoError,
|
||||||
|
|
||||||
|
Error_InvalidParameter = 0x0001,
|
||||||
|
Error_InvalidMasterBoot = 0x0002,
|
||||||
|
Error_InvalidBootSector = 0x0003,
|
||||||
|
Error_InvalidBpb = 0x0004,
|
||||||
|
Error_NotExistMbr = 0x0005,
|
||||||
|
Error_NotExistEpbr = 0x0006,
|
||||||
|
Error_NotExistPartition = 0x0007,
|
||||||
|
Error_NotExistFreeDiskStruct = 0x0008,
|
||||||
|
Error_NotExistPartitionStruct = 0x0009,
|
||||||
|
Error_NotExistFreePartitionStruct = 0x000A,
|
||||||
|
Error_StateOpened = 0x000B,
|
||||||
|
Error_StateClosed = 0x000C,
|
||||||
|
Error_StateLocked = 0x000D,
|
||||||
|
Error_StateUnlocked = 0x000E,
|
||||||
|
Error_AccessPermission = 0x000F,
|
||||||
|
Error_WriteProtected = 0x0010,
|
||||||
|
Error_MediaEjected = 0x0011,
|
||||||
|
Error_OutOfRange = 0x0012,
|
||||||
|
Error_SystemCallError = 0x0013,
|
||||||
|
Error_LockError = 0x0014,
|
||||||
|
Error_DriverError = 0x0015,
|
||||||
|
Error_UnsupportDiskFormat = 0x0016,
|
||||||
|
};
|
||||||
|
|
||||||
|
using NonBlockingProtocolType = int;
|
||||||
|
|
||||||
|
using DiskCallback = void (*)();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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/prfile2/pdm/prfile2_pdm_types.hpp>
|
||||||
|
#include <vapours/prfile2/pdm/prfile2_pdm_common.hpp>
|
||||||
|
#include <vapours/prfile2/pdm/prfile2_pdm_disk_management.hpp>
|
||||||
|
|
||||||
|
namespace ams::prfile2::pdm {
|
||||||
|
|
||||||
|
namespace part {
|
||||||
|
|
||||||
|
pdm::Error CheckDataEraseRequest(HandleType part_handle, bool *out);
|
||||||
|
pdm::Error CheckMediaDetect(HandleType part_handle, bool *out);
|
||||||
|
pdm::Error CheckMediaInsert(HandleType part_handle, bool *out);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace disk {
|
||||||
|
|
||||||
|
pdm::Error CheckDataEraseRequest(HandleType disk_handle, bool *out);
|
||||||
|
pdm::Error CheckMediaDetect(HandleType disk_handle, bool *out);
|
||||||
|
pdm::Error CheckMediaInsert(HandleType disk_handle, bool *out);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours/prfile2/pf/prfile2_pf_config.hpp>
|
||||||
|
#include <vapours/prfile2/pf/prfile2_pf_types.hpp>
|
||||||
|
#include <vapours/prfile2/prfile2_critical_section.hpp>
|
||||||
|
|
||||||
|
namespace ams::prfile2::pf {
|
||||||
|
|
||||||
|
int Initialize(u32 config, void *param);
|
||||||
|
|
||||||
|
int Attach(DriveTable **drive_table);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2020 Atmosphère-NX
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
* under the terms and conditions of the GNU General Public License,
|
* under the terms and conditions of the GNU General Public License,
|
||||||
@@ -13,14 +13,12 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <vapours/prfile2/prfile2_build_config.hpp>
|
||||||
|
|
||||||
#include "hvisor_gdb_context.hpp"
|
namespace ams::prfile2::pf {
|
||||||
|
|
||||||
namespace ams::hvisor::gdb {
|
constexpr inline const auto MaximumFileCount = 256;
|
||||||
|
constexpr inline const auto MaximumDirectoryCount = 32;
|
||||||
int ConvertTidToCoreId(unsigned long tid);
|
|
||||||
std::optional<int> ParseConvertExactlyOneTid(std::string_view str);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* 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/prfile2/prfile2_build_config.hpp>
|
||||||
|
#include <vapours/prfile2/prfile2_handle.hpp>
|
||||||
|
|
||||||
|
namespace ams::prfile2::pf {
|
||||||
|
|
||||||
|
using DriveCharacter = char;
|
||||||
|
|
||||||
|
enum Error {
|
||||||
|
Error_NoError = 0,
|
||||||
|
Error_Ok = Error_NoError,
|
||||||
|
|
||||||
|
Error_Generic = -1,
|
||||||
|
|
||||||
|
Error_InvalidFileName = 1,
|
||||||
|
Error_InvalidPathName = 2,
|
||||||
|
Error_FileNotFound = 3,
|
||||||
|
Error_TooManyVolumesAttached = 4,
|
||||||
|
Error_DirectoryFull = 5,
|
||||||
|
Error_VolumeFull = 6,
|
||||||
|
Error_InvalidDiskFormat = 7,
|
||||||
|
Error_FileAlreadyExists = 8,
|
||||||
|
Error_VolumeNotMounted = 9,
|
||||||
|
Error_InvalidParameter = 10,
|
||||||
|
Error_WriteProtected = 11,
|
||||||
|
Error_UnsupportedFormat = 12,
|
||||||
|
Error_BrokenClusterChain = 13,
|
||||||
|
Error_InvalidClusterNum = 14,
|
||||||
|
Error_InvalidBpb = 15,
|
||||||
|
Error_AccessOutOfVolume = 16,
|
||||||
|
Error_DriverError = 17,
|
||||||
|
Error_InvalidVolumeLabel = 18,
|
||||||
|
Error_FileOpened = 19,
|
||||||
|
Error_NotADirectory = 20,
|
||||||
|
Error_TooManyFilesOpenedS = 21,
|
||||||
|
Error_TooManyFilesOpenedU = 22,
|
||||||
|
Error_NotAFile = 23,
|
||||||
|
Error_ReadOnly = 24,
|
||||||
|
Error_LockError = 25,
|
||||||
|
Error_InternalError = 26,
|
||||||
|
Error_EndOfFile = 27,
|
||||||
|
Error_AccessNotAllowed = 28,
|
||||||
|
Error_DirectoryNotEmpty = 29,
|
||||||
|
Error_NotEnoughCachePages = 30,
|
||||||
|
Error_DifferentDrive = 31,
|
||||||
|
Error_DifferentEntry = 32,
|
||||||
|
Error_InvalidEntry = 33,
|
||||||
|
Error_InvalidSector = 34,
|
||||||
|
Error_BrokenVolume = 35,
|
||||||
|
Error_NotEffective = 36,
|
||||||
|
Error_FileSizeOver = 37,
|
||||||
|
Error_InvalidFileDiscriptor = 38,
|
||||||
|
Error_InvalidLockFile = 39,
|
||||||
|
Error_ExtensionNotRegistered = 40,
|
||||||
|
Error_ExtensionError = 41,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr inline const int ReturnValueNoError = 0;
|
||||||
|
constexpr inline const int ReturnValueError = -1;
|
||||||
|
|
||||||
|
constexpr inline int ConvertReturnValue(Error err) {
|
||||||
|
if (AMS_LIKELY(err == Error_NoError)) {
|
||||||
|
return ReturnValueNoError;
|
||||||
|
} else {
|
||||||
|
return ReturnValueError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CachePage {
|
||||||
|
u16 status;
|
||||||
|
u16 option;
|
||||||
|
u8 *buffer_head;
|
||||||
|
u8 *buffer_cur;
|
||||||
|
u8 *buffer_dirty_start;
|
||||||
|
u8 *buffer_dirty_end;
|
||||||
|
u32 size;
|
||||||
|
u32 sector;
|
||||||
|
void *signature;
|
||||||
|
CachePage *next;
|
||||||
|
CachePage *prev;
|
||||||
|
};
|
||||||
|
static_assert(util::is_pod<CachePage>::value);
|
||||||
|
|
||||||
|
constexpr inline auto SectorBufferSize = 0x200;
|
||||||
|
constexpr inline auto Log2SectorBufferSize = 9;
|
||||||
|
static_assert((1u << Log2SectorBufferSize) == SectorBufferSize);
|
||||||
|
|
||||||
|
using SectorBuffer = u8[SectorBufferSize];
|
||||||
|
static_assert(sizeof(SectorBuffer) == SectorBufferSize);
|
||||||
|
|
||||||
|
struct CacheSetting {
|
||||||
|
CachePage *pages;
|
||||||
|
SectorBuffer *buffers;
|
||||||
|
u16 num_fat_pages;
|
||||||
|
u16 num_data_pages;
|
||||||
|
u32 fat_buf_size;
|
||||||
|
u32 data_buf_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DriveTable {
|
||||||
|
CacheSetting *cache;
|
||||||
|
HandleType partition_handle;
|
||||||
|
DriveCharacter drive_char;
|
||||||
|
u8 status;
|
||||||
|
|
||||||
|
template<size_t Ix>
|
||||||
|
constexpr ALWAYS_INLINE bool GetStatusBit() const {
|
||||||
|
constexpr u32 Mask = (1u << Ix);
|
||||||
|
return (this->status & Mask) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t Ix>
|
||||||
|
constexpr ALWAYS_INLINE void SetStatusBit(bool en) {
|
||||||
|
constexpr u32 Mask = (1u << Ix);
|
||||||
|
if (en) {
|
||||||
|
this->status |= Mask;
|
||||||
|
} else {
|
||||||
|
this->status &= ~Mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool IsAttached() const { return this->GetStatusBit<0>(); }
|
||||||
|
constexpr void SetAttached(bool en) { this->SetStatusBit<0>(en); }
|
||||||
|
|
||||||
|
constexpr bool IsMounted() const { return this->GetStatusBit<1>(); }
|
||||||
|
constexpr void SetMounted(bool en) { this->SetStatusBit<1>(en); }
|
||||||
|
|
||||||
|
constexpr bool IsDiskInserted() const { return this->GetStatusBit<2>(); }
|
||||||
|
constexpr void SetDiskInserted(bool en) { this->SetStatusBit<2>(en); }
|
||||||
|
|
||||||
|
constexpr bool IsFlag12() const { return this->GetStatusBit<12>(); }
|
||||||
|
constexpr void SetFlag12(bool en) { this->SetStatusBit<12>(en); }
|
||||||
|
};
|
||||||
|
|
||||||
|
using TailBuf = u32;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2019 Atmosphère-NX
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
* under the terms and conditions of the GNU General Public License,
|
* under the terms and conditions of the GNU General Public License,
|
||||||
@@ -13,27 +13,26 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <vapours/common.hpp>
|
||||||
|
#include <vapours/assert.hpp>
|
||||||
|
#include <vapours/results.hpp>
|
||||||
|
#include <vapours/util.hpp>
|
||||||
|
#include <vapours/svc.hpp>
|
||||||
|
#include <vapours/dd.hpp>
|
||||||
|
|
||||||
#define BAUD_115200 115200
|
#if defined(ATMOSPHERE_IS_EXOSPHERE) || defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||||
|
|
||||||
#if PLATFORM_TEGRA
|
//#define AMS_PRFILE2_THREAD_SAFE
|
||||||
|
|
||||||
#include "tegra/uart.h"
|
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||||
|
|
||||||
#define DEFAULT_UART UART_C
|
//#define AMS_PRFILE2_THREAD_SAFE
|
||||||
#define DEFAULT_UART_FLAGS 1
|
|
||||||
|
|
||||||
#elif defined(PLATFORM_QEMU)
|
#elif defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||||
|
|
||||||
#define DEFAULT_UART UART_A
|
#define AMS_PRFILE2_THREAD_SAFE
|
||||||
#define DEFAULT_UART_FLAGS 0
|
|
||||||
|
|
||||||
#include "qemu/uart.h"
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
#error "Unknown execution context for ams::prfile2!"
|
||||||
#error "Error: platform not defined"
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -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/prfile2/prfile2_common.hpp>
|
||||||
|
|
||||||
|
namespace ams::prfile2 {
|
||||||
|
|
||||||
|
constexpr inline const auto MinimumFatBufferSize = 1;
|
||||||
|
constexpr inline const auto MaximumFatBufferSize = (1 << 16) - 1;
|
||||||
|
constexpr inline const auto MinimumDataBufferSize = 1;
|
||||||
|
constexpr inline const auto MaximumDataBufferSize = (1 << 15) - 1;
|
||||||
|
|
||||||
|
constexpr inline const auto MinimumFatPages = 1;
|
||||||
|
constexpr inline const auto MinimumDataPages = 4;
|
||||||
|
|
||||||
|
struct SectorCache {
|
||||||
|
u32 mode;
|
||||||
|
u16 num_fat_pages;
|
||||||
|
u16 num_data_pages;
|
||||||
|
pf::CachePage *pages;
|
||||||
|
pf::CachePage *current_fat;
|
||||||
|
pf::CachePage *current_data;
|
||||||
|
pf::SectorBuffer *buffers;
|
||||||
|
u32 fat_buf_size;
|
||||||
|
u32 data_buf_size;
|
||||||
|
void *signature;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Volume;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ams::prfile2::cache {
|
||||||
|
|
||||||
|
void SetCache(Volume *vol, pf::CachePage *cache_page, pf::SectorBuffer *cache_buf, u16 num_fat_pages, u16 num_data_pages);
|
||||||
|
void SetFatBufferSize(Volume *vol, u32 size);
|
||||||
|
void SetDataBufferSize(Volume *vol, u32 size);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* 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/prfile2/prfile2_build_config.hpp>
|
||||||
|
#include <vapours/prfile2/pf/prfile2_pf_config.hpp>
|
||||||
|
#include <vapours/prfile2/pf/prfile2_pf_api_common.hpp>
|
||||||
|
#include <vapours/prfile2/prfile2_wide_string.hpp>
|
||||||
|
|
||||||
|
namespace ams::prfile2 {
|
||||||
|
|
||||||
|
constexpr inline const auto MaximumOpenFileCountSystem = pf::MaximumFileCount;
|
||||||
|
constexpr inline const auto MaximumOpenFileCountUser = pf::MaximumFileCount;
|
||||||
|
constexpr inline const auto MaximumOpenDirectoryCountSystem = pf::MaximumDirectoryCount;
|
||||||
|
constexpr inline const auto MaximumOpenDirectoryCountUser = pf::MaximumDirectoryCount;
|
||||||
|
|
||||||
|
enum Error {
|
||||||
|
Error_NoError = 0,
|
||||||
|
Error_Ok = Error_NoError,
|
||||||
|
|
||||||
|
Error_EPERM = 1,
|
||||||
|
Error_ENOENT = 2,
|
||||||
|
Error_ESRCH = 3,
|
||||||
|
Error_EIO = 5,
|
||||||
|
Error_ENOEXEC = 8,
|
||||||
|
Error_EBADF = 9,
|
||||||
|
Error_ENOMEM = 12,
|
||||||
|
Error_EACCES = 13,
|
||||||
|
Error_EBUSY = 16,
|
||||||
|
Error_EEXIST = 17,
|
||||||
|
Error_ENODEV = 19,
|
||||||
|
Error_EISDIR = 21,
|
||||||
|
Error_EINVAL = 22,
|
||||||
|
Error_ENFILE = 23,
|
||||||
|
Error_EMFILE = 24,
|
||||||
|
Error_EFBIG = 27,
|
||||||
|
Error_ENOSPC = 28,
|
||||||
|
Error_ENOLCK = 46,
|
||||||
|
Error_ENOSYS = 88,
|
||||||
|
Error_ENOTEMPTY = 90,
|
||||||
|
|
||||||
|
Error_EMOD_NOTREG = 100,
|
||||||
|
Error_EMOD_NOTSPRT = 101,
|
||||||
|
Error_EMOD_FCS = 102,
|
||||||
|
Error_EMOD_SAFE = 103,
|
||||||
|
|
||||||
|
Error_ENOMEDIUM = 123,
|
||||||
|
|
||||||
|
Error_EEXFAT_NOTSPRT = 200,
|
||||||
|
|
||||||
|
Error_DFNC = 0x1000,
|
||||||
|
|
||||||
|
Error_SYSTEM = -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr inline const u32 InvalidSector = std::numeric_limits<u32>::max();
|
||||||
|
constexpr inline const u32 InvalidCluster = std::numeric_limits<u32>::max();
|
||||||
|
|
||||||
|
enum OpenMode {
|
||||||
|
OpenMode_None = 0,
|
||||||
|
OpenMode_Write = (1u << 0),
|
||||||
|
OpenMode_Read = (1u << 1),
|
||||||
|
OpenMode_Append = (1u << 2),
|
||||||
|
OpenMode_Plus = (1u << 3),
|
||||||
|
OpenMode_NoOverwrite = (1u << 4),
|
||||||
|
OpenMode_V = (1u << 5),
|
||||||
|
OpenMode_ContCluster = (1u << 6),
|
||||||
|
};
|
||||||
|
|
||||||
|
enum FatFormat {
|
||||||
|
FatFormat_Fat12 = 0,
|
||||||
|
FatFormat_Fat16 = 1,
|
||||||
|
FatFormat_Fat32 = 2,
|
||||||
|
FatFormat_ExFat = 3,
|
||||||
|
|
||||||
|
FatFormat_Invalid = -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours/prfile2/pf/prfile2_pf_config.hpp>
|
||||||
|
|
||||||
|
namespace ams::prfile2 {
|
||||||
|
|
||||||
|
struct CriticalSection {
|
||||||
|
enum State {
|
||||||
|
State_NotInitialized = 0,
|
||||||
|
State_Initialized = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Resource;
|
||||||
|
|
||||||
|
u8 state;
|
||||||
|
int lock_count;
|
||||||
|
Resource *resource;
|
||||||
|
u64 owner;
|
||||||
|
};
|
||||||
|
|
||||||
|
void InitializeCriticalSection(CriticalSection *cs);
|
||||||
|
void FinalizeCriticalSection(CriticalSection *cs);
|
||||||
|
|
||||||
|
void EnterCriticalSection(CriticalSection *cs);
|
||||||
|
void ExitCriticalSection(CriticalSection *cs);
|
||||||
|
|
||||||
|
class ScopedCriticalSection {
|
||||||
|
private:
|
||||||
|
CriticalSection *cs;
|
||||||
|
public:
|
||||||
|
ALWAYS_INLINE ScopedCriticalSection(CriticalSection *c) : cs(c) { EnterCriticalSection(this->cs); }
|
||||||
|
ALWAYS_INLINE ScopedCriticalSection(CriticalSection &c) : ScopedCriticalSection(std::addressof(c)) { /* ... */ }
|
||||||
|
|
||||||
|
ALWAYS_INLINE ~ScopedCriticalSection() { ExitCriticalSection(this->cs); }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2019 Atmosphère-NX
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
* under the terms and conditions of the GNU General Public License,
|
* under the terms and conditions of the GNU General Public License,
|
||||||
@@ -13,17 +13,20 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <vapours/prfile2/prfile2_common.hpp>
|
||||||
|
|
||||||
#ifdef PLATFORM_TEGRA
|
namespace ams::prfile2 {
|
||||||
|
|
||||||
#include "tegra/stage2_config.h"
|
struct Volume;
|
||||||
|
|
||||||
#elif defined(PLATFORM_QEMU)
|
}
|
||||||
|
|
||||||
#include "qemu/stage2_config.h"
|
namespace ams::prfile2::drv {
|
||||||
|
|
||||||
#endif
|
pf::Error Initialize(Volume *volume);
|
||||||
|
|
||||||
void stage2ConfigureAndEnable(void);
|
bool IsDetected(Volume *volume);
|
||||||
|
bool IsInserted(Volume *volume);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* 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/prfile2/prfile2_common.hpp>
|
||||||
|
|
||||||
|
namespace ams::prfile2 {
|
||||||
|
|
||||||
|
constexpr inline const auto LongNamePathCharacters = 260;
|
||||||
|
|
||||||
|
struct Volume;
|
||||||
|
|
||||||
|
struct DirectoryEntry {
|
||||||
|
u16 long_name[LongNamePathCharacters];
|
||||||
|
u32 attr;
|
||||||
|
u64 file_size;
|
||||||
|
u8 create_time_ms;
|
||||||
|
u16 create_time;
|
||||||
|
u16 create_date;
|
||||||
|
u8 create_flags;
|
||||||
|
u8 access_time_ms;
|
||||||
|
u16 access_time;
|
||||||
|
u16 access_date;
|
||||||
|
u8 access_flags;
|
||||||
|
u8 modify_time_ms;
|
||||||
|
u16 modify_time;
|
||||||
|
u16 modify_date;
|
||||||
|
u8 modify_flags;
|
||||||
|
Volume *volume;
|
||||||
|
u32 path_len;
|
||||||
|
u32 start_cluster;
|
||||||
|
u32 entry_sector;
|
||||||
|
u16 entry_offset;
|
||||||
|
/* ... */
|
||||||
|
u8 num_entry_lfns;
|
||||||
|
u8 ordinal;
|
||||||
|
u8 check_sum;
|
||||||
|
char short_name[13];
|
||||||
|
u8 small_letter_flag;
|
||||||
|
/* ... */
|
||||||
|
u16 full_path[LongNamePathCharacters];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DirectoryTail {
|
||||||
|
u32 tracker_size;
|
||||||
|
pf::TailBuf tracker_buf[1];
|
||||||
|
u32 *tracker_bits;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
64
libraries/libvapours/include/vapours/prfile2/prfile2_fat.hpp
Normal file
64
libraries/libvapours/include/vapours/prfile2/prfile2_fat.hpp
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* 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/prfile2/prfile2_common.hpp>
|
||||||
|
|
||||||
|
namespace ams::prfile2 {
|
||||||
|
|
||||||
|
struct FatHint {
|
||||||
|
u32 chain_index;
|
||||||
|
u32 cluster;
|
||||||
|
};
|
||||||
|
|
||||||
|
using LastAccess = FatHint;
|
||||||
|
|
||||||
|
struct LastCluster {
|
||||||
|
u32 num_last_cluster;
|
||||||
|
u32 max_chain_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ClusterLinkForVolume {
|
||||||
|
u16 flag;
|
||||||
|
u16 interval;
|
||||||
|
u32 *buffer;
|
||||||
|
u32 link_max;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ClusterBuf = u32;
|
||||||
|
|
||||||
|
struct ClusterLink {
|
||||||
|
u64 position;
|
||||||
|
ClusterBuf *buffer;
|
||||||
|
u16 interval;
|
||||||
|
u16 interval_offset;
|
||||||
|
u32 save_index;
|
||||||
|
u32 max_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Volume;
|
||||||
|
|
||||||
|
struct FatFileDescriptor {
|
||||||
|
u32 start_cluster;
|
||||||
|
u32 *p_start_cluster;
|
||||||
|
LastCluster last_cluster;
|
||||||
|
LastAccess last_access;
|
||||||
|
ClusterLink cluster_link;
|
||||||
|
FatHint *hint;
|
||||||
|
Volume *volume;
|
||||||
|
/* ... */
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -14,8 +14,10 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <vapours/prfile2/prfile2_common.hpp>
|
||||||
|
|
||||||
#include <stratosphere/usb/usb_limits.hpp>
|
namespace ams::prfile2::fatfs {
|
||||||
#include <stratosphere/usb/usb_types.hpp>
|
|
||||||
#include <stratosphere/usb/usb_device_types.hpp>
|
pf::Error Initialize(u32 config, void *param);
|
||||||
#include <stratosphere/usb/usb_device.hpp>
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* 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/prfile2/prfile2_build_config.hpp>
|
||||||
|
|
||||||
|
namespace ams::prfile2 {
|
||||||
|
|
||||||
|
using HandleType = util::BitPack32;
|
||||||
|
|
||||||
|
struct HandleField {
|
||||||
|
using Id = util::BitPack32::Field< 0, 8>;
|
||||||
|
using Kind = util::BitPack32::Field< 8, 8>;
|
||||||
|
using Signature = util::BitPack32::Field<16, 16>;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum HandleKind {
|
||||||
|
HandleKind_Disk = 3,
|
||||||
|
HandleKind_Partition = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr ALWAYS_INLINE HandleType ConstructHandle(u8 id, HandleKind kind, u16 signature) {
|
||||||
|
/* Construct the handle. */
|
||||||
|
HandleType val = {};
|
||||||
|
val.Set<HandleField::Id>(id);
|
||||||
|
val.Set<HandleField::Kind>(kind);
|
||||||
|
val.Set<HandleField::Signature>(signature);
|
||||||
|
return val;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr ALWAYS_INLINE u8 GetHandleId(HandleType handle) {
|
||||||
|
return handle.Get<HandleField::Id>();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr ALWAYS_INLINE u16 GetHandleSignature(HandleType handle) {
|
||||||
|
return handle.Get<HandleField::Signature>();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline const HandleType InvalidHandle = {};
|
||||||
|
|
||||||
|
constexpr ALWAYS_INLINE bool IsInvalidHandle(HandleType handle) {
|
||||||
|
return handle.value == 0;
|
||||||
|
}
|
||||||
|
static_assert(IsInvalidHandle(InvalidHandle));
|
||||||
|
|
||||||
|
constexpr ALWAYS_INLINE HandleType ConstructDiskHandle(u8 id, u16 signature) { return ConstructHandle(id, HandleKind_Disk, signature); }
|
||||||
|
constexpr ALWAYS_INLINE HandleType ConstructPartitionHandle(u8 id, u16 signature) { return ConstructHandle(id, HandleKind_Partition, signature); }
|
||||||
|
|
||||||
|
constexpr ALWAYS_INLINE bool IsDiskHandle(HandleType handle) { return handle.Get<HandleField::Kind>() == HandleKind_Disk; }
|
||||||
|
constexpr ALWAYS_INLINE bool IsPartitionHandle(HandleType handle) { return handle.Get<HandleField::Kind>() == HandleKind_Partition; }
|
||||||
|
|
||||||
|
}
|
||||||
65
libraries/libvapours/include/vapours/prfile2/prfile2_str.hpp
Normal file
65
libraries/libvapours/include/vapours/prfile2/prfile2_str.hpp
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* 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/prfile2/prfile2_common.hpp>
|
||||||
|
|
||||||
|
namespace ams::prfile2::str {
|
||||||
|
|
||||||
|
enum CodeMode {
|
||||||
|
CodeMode_Invalid = 0,
|
||||||
|
CodeMode_Local = 1,
|
||||||
|
CodeMode_Unicode = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum TargetString {
|
||||||
|
TargetString_Head = 1,
|
||||||
|
TargetString_Tail = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct String {
|
||||||
|
const char *head;
|
||||||
|
const char *tail;
|
||||||
|
CodeMode code_mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
pf::Error Initialize(String *str, const char *s, CodeMode code_mode);
|
||||||
|
|
||||||
|
void SetCodeMode(String *str, CodeMode code_mode);
|
||||||
|
CodeMode GetCodeMode(const String *str);
|
||||||
|
|
||||||
|
char *GetPos(String *str, TargetString target);
|
||||||
|
void MovePos(String *str, s16 num_char);
|
||||||
|
|
||||||
|
u16 GetLength(String *str);
|
||||||
|
u16 GetNumChar(String *str, TargetString target);
|
||||||
|
|
||||||
|
int Compare(const String *str, const char *rhs);
|
||||||
|
int Compare(const String *str, const WideChar *rhs);
|
||||||
|
|
||||||
|
/* TODO: StrNCmp */
|
||||||
|
/* TODO: ToUpperNStr */
|
||||||
|
|
||||||
|
constexpr bool IsNull(const String *str) {
|
||||||
|
return str->head == nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ams::prfile2 {
|
||||||
|
|
||||||
|
using String = str::String;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2019 Atmosphère-NX
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
* under the terms and conditions of the GNU General Public License,
|
* under the terms and conditions of the GNU General Public License,
|
||||||
@@ -13,15 +13,13 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <vapours/prfile2/prfile2_common.hpp>
|
||||||
|
|
||||||
#ifdef PLATFORM_TEGRA
|
namespace ams::prfile2::system {
|
||||||
|
|
||||||
#include "tegra/devices.h"
|
void Initialize();
|
||||||
|
|
||||||
#elif defined(PLATFORM_QEMU)
|
int GetCurrentContextId(u64 *out);
|
||||||
|
|
||||||
#include "qemu/devices.h"
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
238
libraries/libvapours/include/vapours/prfile2/prfile2_volume.hpp
Normal file
238
libraries/libvapours/include/vapours/prfile2/prfile2_volume.hpp
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
/*
|
||||||
|
* 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/prfile2/prfile2_common.hpp>
|
||||||
|
#include <vapours/prfile2/prfile2_cache.hpp>
|
||||||
|
#include <vapours/prfile2/prfile2_critical_section.hpp>
|
||||||
|
#include <vapours/prfile2/prfile2_drv.hpp>
|
||||||
|
#include <vapours/prfile2/prfile2_entry.hpp>
|
||||||
|
#include <vapours/prfile2/prfile2_fat.hpp>
|
||||||
|
|
||||||
|
namespace ams::prfile2 {
|
||||||
|
|
||||||
|
constexpr inline const auto MaxVolumes = 5;
|
||||||
|
|
||||||
|
struct Cursor {
|
||||||
|
u64 position;
|
||||||
|
u32 sector;
|
||||||
|
u32 file_sector_index;
|
||||||
|
u16 offset_in_sector;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DirectoryCursor {
|
||||||
|
u32 physical_entry_index;
|
||||||
|
u32 logical_entry_index;
|
||||||
|
u32 logical_seek_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct File;
|
||||||
|
|
||||||
|
struct FileLock {
|
||||||
|
u16 mode;
|
||||||
|
u16 count;
|
||||||
|
u16 waiting_count;
|
||||||
|
File *owner;
|
||||||
|
u32 resource;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SystemFileDescriptor {
|
||||||
|
u32 status;
|
||||||
|
FatFileDescriptor ffd;
|
||||||
|
DirectoryEntry dir_entry;
|
||||||
|
FileLock lock;
|
||||||
|
u16 num_handlers;
|
||||||
|
SystemFileDescriptor *next_sfd;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SystemDirectoryDescriptor {
|
||||||
|
u32 status;
|
||||||
|
u16 num_handlers;
|
||||||
|
FatFileDescriptor ffd;
|
||||||
|
DirectoryEntry dir_entry;
|
||||||
|
/* ... */
|
||||||
|
u64 context_id;
|
||||||
|
/* ... */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct File {
|
||||||
|
u32 status;
|
||||||
|
/* TODO OpenMode */ u32 open_mode;
|
||||||
|
SystemFileDescriptor *sfd;
|
||||||
|
FatHint hint;
|
||||||
|
LastAccess last_access;
|
||||||
|
pf::Error last_error;
|
||||||
|
Cursor cursor;
|
||||||
|
u16 lock_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Directory {
|
||||||
|
u32 stat;
|
||||||
|
SystemDirectoryDescriptor *sdd;
|
||||||
|
FatHint hint;
|
||||||
|
DirectoryCursor cursor;
|
||||||
|
/* ... */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BiosParameterBlock {
|
||||||
|
u16 bytes_per_sector;
|
||||||
|
u32 num_reserved_sectors;
|
||||||
|
u16 num_root_dir_entries;
|
||||||
|
u32 sectors_per_cluster;
|
||||||
|
u8 num_fats;
|
||||||
|
u32 total_sectors;
|
||||||
|
u32 sectors_per_fat;
|
||||||
|
u32 root_dir_cluster;
|
||||||
|
u16 fs_info_sector;
|
||||||
|
u16 backup_boot_sector;
|
||||||
|
u16 ext_flags;
|
||||||
|
u16 ext_flags_;
|
||||||
|
u8 media;
|
||||||
|
FatFormat fat_fmt;
|
||||||
|
u8 log2_bytes_per_sector;
|
||||||
|
u8 log2_sectors_per_cluster;
|
||||||
|
u8 num_active_fats;
|
||||||
|
u8 num_active_fats_;
|
||||||
|
u16 num_root_dir_sectors;
|
||||||
|
u32 active_fat_sector;
|
||||||
|
u32 active_fat_sector_;
|
||||||
|
u32 first_root_dir_sector;
|
||||||
|
u32 first_data_sector;
|
||||||
|
u32 num_clusters;
|
||||||
|
/* ... */
|
||||||
|
};
|
||||||
|
|
||||||
|
using VolumeCallback = void (*)();
|
||||||
|
|
||||||
|
struct VolumeContext {
|
||||||
|
u64 context_id;
|
||||||
|
u32 volume_id;
|
||||||
|
DirectoryEntry dir_entries[MaxVolumes];
|
||||||
|
pf::Error last_error;
|
||||||
|
pf::Error last_driver_error[MaxVolumes];
|
||||||
|
pf::Error last_unk_error[MaxVolumes];
|
||||||
|
/* ... */
|
||||||
|
VolumeContext *next_used_context;
|
||||||
|
union {
|
||||||
|
VolumeContext *prev_used_context;
|
||||||
|
VolumeContext *next_free_context;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace pdm {
|
||||||
|
|
||||||
|
struct Partition;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Volume {
|
||||||
|
BiosParameterBlock bpb;
|
||||||
|
u32 num_free_clusters;
|
||||||
|
u32 num_free_clusters_;
|
||||||
|
u32 last_free_cluster;
|
||||||
|
u32 last_free_cluster_;
|
||||||
|
SystemFileDescriptor sfds[MaximumOpenFileCountSystem];
|
||||||
|
File ufds[MaximumOpenFileCountUser];
|
||||||
|
SystemDirectoryDescriptor sdds[MaximumOpenDirectoryCountSystem];
|
||||||
|
Directory udds[MaximumOpenDirectoryCountUser];
|
||||||
|
u32 num_open_files;
|
||||||
|
u32 num_open_directories;
|
||||||
|
/* ... */
|
||||||
|
SectorCache cache;
|
||||||
|
VolumeContext *context;
|
||||||
|
u64 context_id;
|
||||||
|
DirectoryTail tail_entry;
|
||||||
|
u32 volume_config;
|
||||||
|
u32 file_config;
|
||||||
|
u32 flags;
|
||||||
|
pf::DriveCharacter drive_char;
|
||||||
|
CriticalSection critical_section;
|
||||||
|
u16 fsi_flag;
|
||||||
|
ClusterLinkForVolume cluster_link;
|
||||||
|
pf::Error last_error;
|
||||||
|
pf::Error last_driver_error;
|
||||||
|
/* ... */
|
||||||
|
HandleType partition_handle;
|
||||||
|
VolumeCallback callback;
|
||||||
|
const u8 *format_param;
|
||||||
|
/* ... */
|
||||||
|
/* TODO: ExtensionTable extension_table; */
|
||||||
|
/* TODO: ExtensionData ext; */
|
||||||
|
/* ... */
|
||||||
|
|
||||||
|
template<size_t Ix>
|
||||||
|
constexpr ALWAYS_INLINE bool GetFlagsBit() const {
|
||||||
|
constexpr u32 Mask = (1u << Ix);
|
||||||
|
return (this->flags & Mask) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t Ix>
|
||||||
|
constexpr ALWAYS_INLINE void SetFlagsBit(bool en) {
|
||||||
|
constexpr u32 Mask = (1u << Ix);
|
||||||
|
if (en) {
|
||||||
|
this->flags |= Mask;
|
||||||
|
} else {
|
||||||
|
this->flags &= ~Mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool IsAttached() const { return this->GetFlagsBit<0>(); }
|
||||||
|
constexpr void SetAttached(bool en) { this->SetFlagsBit<0>(en); }
|
||||||
|
|
||||||
|
constexpr bool IsMounted() const { return this->GetFlagsBit<1>(); }
|
||||||
|
constexpr void SetMounted(bool en) { this->SetFlagsBit<1>(en); }
|
||||||
|
|
||||||
|
constexpr bool IsFormatAfterMountRequested() const { return this->GetFlagsBit<4>(); }
|
||||||
|
constexpr void SetFormatAfterMountRequested(bool en) { this->SetFlagsBit<4>(en); }
|
||||||
|
|
||||||
|
constexpr bool IsNoFormattingByFatFsLayer() const { return this->GetFlagsBit<5>(); }
|
||||||
|
constexpr void SetNoFormattingByFatFsLayer(bool en) { this->SetFlagsBit<5>(en); }
|
||||||
|
|
||||||
|
constexpr bool IsDataEraseRequested() const { return this->GetFlagsBit<6>(); }
|
||||||
|
constexpr void SetDataEraseRequested(bool en) { this->SetFlagsBit<6>(en); }
|
||||||
|
|
||||||
|
constexpr bool IsFlag12() const { return this->GetFlagsBit<12>(); }
|
||||||
|
constexpr void SetFlag12(bool en) { this->SetFlagsBit<12>(en); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VolumeSet {
|
||||||
|
bool initialized;
|
||||||
|
u32 num_attached_drives;
|
||||||
|
u32 num_mounted_volumes;
|
||||||
|
u32 config;
|
||||||
|
void *param;
|
||||||
|
/* TODO: CodeSet codeset; */
|
||||||
|
u32 setting;
|
||||||
|
CriticalSection critical_section;
|
||||||
|
VolumeContext default_context;
|
||||||
|
VolumeContext contexts[MaxVolumes];
|
||||||
|
VolumeContext *used_context_head;
|
||||||
|
VolumeContext *used_context_tail;
|
||||||
|
VolumeContext *free_context_head;
|
||||||
|
Volume volumes[MaxVolumes];
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ams::prfile2::vol {
|
||||||
|
|
||||||
|
pf::Error Initialize(u32 config, void *param);
|
||||||
|
|
||||||
|
pf::Error Attach(pf::DriveTable *drive_table);
|
||||||
|
|
||||||
|
VolumeContext *RegisterContext(u64 *out_context_id);
|
||||||
|
pf::Error UnregisterContext();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2020 Atmosphère-NX
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
* under the terms and conditions of the GNU General Public License,
|
* under the terms and conditions of the GNU General Public License,
|
||||||
@@ -13,23 +13,20 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <vapours/prfile2/prfile2_common.hpp>
|
||||||
|
|
||||||
#include "defines.hpp"
|
namespace ams::prfile2 {
|
||||||
|
|
||||||
namespace ams::hvisor {
|
using WideChar = u16;
|
||||||
|
|
||||||
size_t GuestReadWriteMemory(uintptr_t addr, size_t size, void *readBuf, const void *writeBuf);
|
size_t w_strlen(const WideChar *s);
|
||||||
|
size_t w_strnlen(const WideChar *s, size_t length);
|
||||||
|
|
||||||
inline size_t GuestReadMemory(uintptr_t addr, size_t size, void *buf)
|
WideChar *w_strcpy(WideChar *dst, const WideChar *src);
|
||||||
{
|
WideChar *w_strncpy(WideChar *dst, const WideChar *src, size_t length);
|
||||||
return GuestReadWriteMemory(addr, size, buf, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline size_t GuestWriteMemory(uintptr_t addr, size_t size, const void *buf)
|
int w_strcmp(const WideChar *lhs, const WideChar *rhs);
|
||||||
{
|
int w_strncmp(const WideChar *lhs, const WideChar *rhs, size_t length);
|
||||||
return GuestReadWriteMemory(addr, size, NULL, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -59,7 +59,6 @@
|
|||||||
#include <vapours/results/svc_results.hpp>
|
#include <vapours/results/svc_results.hpp>
|
||||||
#include <vapours/results/time_results.hpp>
|
#include <vapours/results/time_results.hpp>
|
||||||
#include <vapours/results/updater_results.hpp>
|
#include <vapours/results/updater_results.hpp>
|
||||||
#include <vapours/results/usb_results.hpp>
|
|
||||||
#include <vapours/results/vi_results.hpp>
|
#include <vapours/results/vi_results.hpp>
|
||||||
|
|
||||||
/* Unofficial. */
|
/* Unofficial. */
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <vapours/results/results_common.hpp>
|
|
||||||
|
|
||||||
namespace ams::usb {
|
|
||||||
|
|
||||||
R_DEFINE_NAMESPACE_RESULT_MODULE(140);
|
|
||||||
|
|
||||||
R_DEFINE_ERROR_RESULT(NotInitialized, 0);
|
|
||||||
R_DEFINE_ERROR_RESULT(AlreadyInitialized, 1);
|
|
||||||
|
|
||||||
R_DEFINE_ERROR_RANGE(InvalidParameter, 100, 199);
|
|
||||||
R_DEFINE_ERROR_RESULT(AlignmentError, 103);
|
|
||||||
|
|
||||||
R_DEFINE_ERROR_RESULT(OperationDenied, 201);
|
|
||||||
R_DEFINE_ERROR_RESULT(MemAllocFailure, 202);
|
|
||||||
R_DEFINE_ERROR_RESULT(ResourceBusy, 206);
|
|
||||||
R_DEFINE_ERROR_RESULT(InternalStateError, 207);
|
|
||||||
|
|
||||||
R_DEFINE_ERROR_RESULT(TransactionError, 401);
|
|
||||||
R_DEFINE_ERROR_RESULT(Interrupted, 409);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -46,9 +46,6 @@
|
|||||||
#include <vapours/util/util_format_string.hpp>
|
#include <vapours/util/util_format_string.hpp>
|
||||||
#include <vapours/util/util_range.hpp>
|
#include <vapours/util/util_range.hpp>
|
||||||
|
|
||||||
#include <vapours/util/util_fixed_map.hpp>
|
|
||||||
#include <vapours/util/util_fixed_set.hpp>
|
|
||||||
|
|
||||||
#ifdef ATMOSPHERE_IS_STRATOSPHERE
|
#ifdef ATMOSPHERE_IS_STRATOSPHERE
|
||||||
#include <vapours/util/util_mutex_utils.hpp>
|
#include <vapours/util/util_mutex_utils.hpp>
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,220 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <vapours/common.hpp>
|
|
||||||
#include <vapours/assert.hpp>
|
|
||||||
#include <vapours/util/util_fourcc.hpp>
|
|
||||||
|
|
||||||
namespace ams::util::impl {
|
|
||||||
|
|
||||||
class AvailableIndexFinder {
|
|
||||||
private:
|
|
||||||
static constexpr int MaxDepthOfBox = 5;
|
|
||||||
|
|
||||||
struct BitFlags64 {
|
|
||||||
private:
|
|
||||||
u64 m_data;
|
|
||||||
public:
|
|
||||||
constexpr ALWAYS_INLINE bool GetFlag(int index) const {
|
|
||||||
AMS_ASSERT(index < 64);
|
|
||||||
return ((m_data >> index) & UINT64_C(1)) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE void SetFlag(int index) {
|
|
||||||
AMS_ASSERT(index < 64);
|
|
||||||
m_data |= (UINT64_C(1) << index);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE void ClearFlag(int index) {
|
|
||||||
AMS_ASSERT(index < 64);
|
|
||||||
m_data &= ~(UINT64_C(1) << index);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE bool IsFull() const {
|
|
||||||
return m_data == ~(UINT64_C(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE int FindIndexOfBitZero() const {
|
|
||||||
AMS_ASSERT(!this->IsFull());
|
|
||||||
return __builtin_ctzll(~m_data);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
private:
|
|
||||||
int *m_p_current_index;
|
|
||||||
int *m_p_map_index;
|
|
||||||
void *m_buffer;
|
|
||||||
int m_depth;
|
|
||||||
BitFlags64 *m_flags;
|
|
||||||
private:
|
|
||||||
static constexpr int Pow64(int e) {
|
|
||||||
switch (e) {
|
|
||||||
case 0: return 0x1;
|
|
||||||
case 1: return 0x40;
|
|
||||||
case 2: return 0x1000;
|
|
||||||
case 3: return 0x40000;
|
|
||||||
case 4: return 0x1000000;
|
|
||||||
case 5: return 0x40000000;
|
|
||||||
default: return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr u64 Roundup64(u64 value) {
|
|
||||||
return (value + (64 - 1)) / 64;
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr size_t GetRequiredNumOfBox(int depth, size_t num_elements) {
|
|
||||||
if (depth == 1) {
|
|
||||||
return Roundup64(num_elements);
|
|
||||||
} else if (depth == 2) {
|
|
||||||
return Roundup64(num_elements) + Pow64(0);
|
|
||||||
} else if (depth == 3) {
|
|
||||||
return Roundup64(num_elements) + Pow64(0) + Pow64(1) + Pow64(2);
|
|
||||||
} else if (depth == 4) {
|
|
||||||
return Roundup64(num_elements) + Pow64(0) + Pow64(1) + Pow64(2) + Pow64(3);
|
|
||||||
} else if (depth == 5) {
|
|
||||||
return Roundup64(num_elements) + Pow64(0) + Pow64(1) + Pow64(2) + Pow64(3) + Pow64(4);
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr int CalcOffset(int *arr, int depth) {
|
|
||||||
int offset = 0;
|
|
||||||
for (auto i = 0; i < depth; ++i) {
|
|
||||||
offset += Pow64(i);
|
|
||||||
}
|
|
||||||
for (auto i = 0; i < depth; ++i) {
|
|
||||||
offset += Pow64(i) - arr[depth - 1 - i];
|
|
||||||
}
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
static consteval int GetSignature() {
|
|
||||||
return static_cast<int>(util::ReverseFourCC<'B', 'I', 'T', 'S'>::Code);
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr int GetNeedDepth(size_t num_elements) {
|
|
||||||
if (num_elements <= 0x40) {
|
|
||||||
return 1;
|
|
||||||
} else if (num_elements <= 0x1000) {
|
|
||||||
return 2;
|
|
||||||
} else if (num_elements <= 0x40000) {
|
|
||||||
return 3;
|
|
||||||
} else if (num_elements <= 0x1000000) {
|
|
||||||
return 4;
|
|
||||||
} else if (num_elements <= 0x40000000) {
|
|
||||||
return 5;
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr size_t GetRequiredMemorySize(size_t num_elements) {
|
|
||||||
return sizeof(BitFlags64) * GetRequiredNumOfBox(GetNeedDepth(num_elements), num_elements);
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
void Initialize(int *cur, int *map, u8 *buf) {
|
|
||||||
const size_t num_elements = static_cast<size_t>(*map);
|
|
||||||
AMS_ASSERT(num_elements <= static_cast<size_t>(Pow64(MaxDepthOfBox - 1)));
|
|
||||||
|
|
||||||
/* Set fields. */
|
|
||||||
m_p_current_index = cur;
|
|
||||||
m_p_map_index = map;
|
|
||||||
m_buffer = buf;
|
|
||||||
m_depth = GetNeedDepth(num_elements);
|
|
||||||
|
|
||||||
/* Validate fields. */
|
|
||||||
AMS_ASSERT(m_depth > 0);
|
|
||||||
|
|
||||||
/* Setup memory. */
|
|
||||||
std::memset(m_buffer, 0, GetRequiredMemorySize(num_elements));
|
|
||||||
m_flags = reinterpret_cast<BitFlags64 *>(m_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
int AcquireIndex() {
|
|
||||||
/* Validate pre-conditions. */
|
|
||||||
AMS_ASSERT(*m_p_current_index < *m_p_map_index);
|
|
||||||
|
|
||||||
/* Build up arrays. */
|
|
||||||
int table[MaxDepthOfBox];
|
|
||||||
BitFlags64 *pos[MaxDepthOfBox];
|
|
||||||
for (auto i = 0; i < m_depth; ++i) {
|
|
||||||
/* Determine the position. */
|
|
||||||
pos[i] = std::addressof(m_flags[CalcOffset(table, i)]);
|
|
||||||
|
|
||||||
/* Set table entry. */
|
|
||||||
table[i] = pos[i]->FindIndexOfBitZero();
|
|
||||||
AMS_ASSERT(table[i] != BITSIZEOF(u64));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Validate that the index is not acquired. */
|
|
||||||
AMS_ASSERT(!pos[m_depth - 1]->GetFlag(table[m_depth - 1]));
|
|
||||||
|
|
||||||
/* Acquire the index. */
|
|
||||||
pos[m_depth - 1]->SetFlag(table[m_depth - 1]);
|
|
||||||
|
|
||||||
/* Validate that the index was acquired. */
|
|
||||||
AMS_ASSERT(pos[m_depth - 1]->GetFlag(table[m_depth - 1]));
|
|
||||||
|
|
||||||
/* Update tracking flags. */
|
|
||||||
for (auto i = m_depth - 1; i > 0; --i) {
|
|
||||||
if (pos[i]->IsFull()) {
|
|
||||||
pos[i - 1]->SetFlag(table[i - 1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Calculate the index we acquired. */
|
|
||||||
int index = 0, pow = 0;
|
|
||||||
for (auto i = m_depth; i > 0; --i, ++pow) {
|
|
||||||
index += Pow64(pow) * table[i - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Increment current index. */
|
|
||||||
++(*m_p_current_index);
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReleaseIndex(int index) {
|
|
||||||
/* Convert index to table. */
|
|
||||||
int table[MaxDepthOfBox];
|
|
||||||
for (auto i = 0; i < m_depth; ++i) {
|
|
||||||
table[m_depth - 1 - i] = index % BITSIZEOF(u64);
|
|
||||||
index /= BITSIZEOF(u64);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Build up arrays. */
|
|
||||||
BitFlags64 *pos[MaxDepthOfBox];
|
|
||||||
for (auto i = 0; i < m_depth; ++i) {
|
|
||||||
/* Determine the position. */
|
|
||||||
pos[i] = std::addressof(m_flags[CalcOffset(table, i)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Validate that the flag is set. */
|
|
||||||
AMS_ASSERT(pos[m_depth - 1]->GetFlag(table[m_depth - 1]));
|
|
||||||
|
|
||||||
/* Clear the flags. */
|
|
||||||
for (auto i = m_depth - 1; i >= 0; --i) {
|
|
||||||
pos[i]->ClearFlag(table[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Decrement current index. */
|
|
||||||
--(*m_p_current_index);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -75,6 +75,14 @@ namespace ams::util {
|
|||||||
this->value &= ~FieldMask;
|
this->value &= ~FieldMask;
|
||||||
this->value |= (static_cast<IntegralStorageType>(field_value) << FieldType::Index) & FieldMask;
|
this->value |= (static_cast<IntegralStorageType>(field_value) << FieldType::Index) & FieldMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr ALWAYS_INLINE bool operator==(const BitPack &rhs) {
|
||||||
|
return this->value == rhs.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr ALWAYS_INLINE bool operator!=(const BitPack &rhs) {
|
||||||
|
return !(*this == rhs);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <vapours/common.hpp>
|
|
||||||
#include <vapours/assert.hpp>
|
|
||||||
#include <vapours/util/util_fixed_tree.hpp>
|
|
||||||
|
|
||||||
namespace ams::util {
|
|
||||||
|
|
||||||
template<typename Key, typename Value, typename Compare = std::less<Key>, size_t BufferAlignment = 8>
|
|
||||||
class FixedMap {
|
|
||||||
private:
|
|
||||||
using KeyValuePair = std::pair<Key const, Value>;
|
|
||||||
|
|
||||||
struct LessTypeForMap {
|
|
||||||
constexpr ALWAYS_INLINE bool operator()(const KeyValuePair &lhs, const KeyValuePair &rhs) const {
|
|
||||||
return Compare{}(lhs.first, rhs.first);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using TreeType = ::ams::util::FixedTree<KeyValuePair, LessTypeForMap, KeyValuePair, BufferAlignment>;
|
|
||||||
|
|
||||||
using iterator = typename TreeType::iterator;
|
|
||||||
using const_iterator = typename TreeType::const_iterator;
|
|
||||||
public:
|
|
||||||
static constexpr size_t GetRequiredMemorySize(size_t num_elements) {
|
|
||||||
return TreeType::GetRequiredMemorySize(num_elements);
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
TreeType m_tree;
|
|
||||||
public:
|
|
||||||
FixedMap() : m_tree() { /* ... */ }
|
|
||||||
|
|
||||||
void Initialize(size_t num_elements, void *buffer, size_t buffer_size) {
|
|
||||||
return m_tree.Initialize(num_elements, buffer, buffer_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
ALWAYS_INLINE iterator begin() { return m_tree.begin(); }
|
|
||||||
ALWAYS_INLINE const_iterator begin() const { return m_tree.begin(); }
|
|
||||||
|
|
||||||
ALWAYS_INLINE iterator end() { return m_tree.end(); }
|
|
||||||
ALWAYS_INLINE const_iterator end() const { return m_tree.end(); }
|
|
||||||
|
|
||||||
ALWAYS_INLINE bool erase(const Key &key) { const KeyValuePair pair(key, Value{}); return m_tree.erase(pair); }
|
|
||||||
|
|
||||||
ALWAYS_INLINE iterator find(const Key &key) { const KeyValuePair pair(key, Value{}); return m_tree.find(pair); }
|
|
||||||
ALWAYS_INLINE const_iterator find(const Key &key) const { const KeyValuePair pair(key, Value{}); return m_tree.find(pair); }
|
|
||||||
|
|
||||||
ALWAYS_INLINE std::pair<iterator, bool> insert(const KeyValuePair &pair) { return m_tree.insert(pair); }
|
|
||||||
|
|
||||||
ALWAYS_INLINE size_t size() const { return m_tree.size(); }
|
|
||||||
|
|
||||||
ALWAYS_INLINE void clear() { return m_tree.clear(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,998 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
#include <vapours/common.hpp>
|
|
||||||
#include <vapours/assert.hpp>
|
|
||||||
#include <vapours/util/impl/util_available_index_finder.hpp>
|
|
||||||
#include <vapours/util/util_alignment.hpp>
|
|
||||||
|
|
||||||
namespace ams::util {
|
|
||||||
|
|
||||||
template<typename Member, typename Compare, typename IteratorMember, size_t BufferAlignment = 8> requires std::convertible_to<Member &, IteratorMember &>
|
|
||||||
class FixedTree {
|
|
||||||
private:
|
|
||||||
class IteratorBase;
|
|
||||||
friend class IteratorBase;
|
|
||||||
private:
|
|
||||||
enum class Color : u8 {
|
|
||||||
Red = 0,
|
|
||||||
Black = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
static constexpr inline int Index_Nil = -1;
|
|
||||||
static constexpr inline int Index_Leaf = -2;
|
|
||||||
static constexpr inline int Index_BeforeBegin = -3;
|
|
||||||
static constexpr inline int Index_AfterEnd = -4;
|
|
||||||
|
|
||||||
static constexpr inline size_t max_size = 0x40000000;
|
|
||||||
|
|
||||||
struct Header {
|
|
||||||
/* "Nintendo Red-Black tree" */
|
|
||||||
static constexpr u32 Signature = util::ReverseFourCC<'N','N','R','B'>::Code;
|
|
||||||
|
|
||||||
u32 header_size;
|
|
||||||
u32 header_signature;
|
|
||||||
u32 _08;
|
|
||||||
s32 max_elements;
|
|
||||||
s32 cur_elements;
|
|
||||||
s32 root_index;
|
|
||||||
s32 left_most_index;
|
|
||||||
s32 right_most_index;
|
|
||||||
s32 index_signature;
|
|
||||||
u32 buffer_size;
|
|
||||||
u32 node_size;
|
|
||||||
u32 element_size;
|
|
||||||
u32 _30;
|
|
||||||
u32 _34;
|
|
||||||
u32 _38;
|
|
||||||
u32 _3C;
|
|
||||||
u32 _40;
|
|
||||||
u32 _44;
|
|
||||||
u32 _48;
|
|
||||||
u32 _4C;
|
|
||||||
|
|
||||||
void InitializeHeader(u32 _08, s32 max_e, s32 cur_e, u32 ind_sig, u32 buf_sz, u32 node_sz, u32 e_sz, u32 _30, u32 _34, u32 _38, u32 _3C, u32 _40, u32 _44) {
|
|
||||||
this->header_size = sizeof(Header);
|
|
||||||
this->header_signature = Signature;
|
|
||||||
this->_08 = _08;
|
|
||||||
this->max_elements = max_e;
|
|
||||||
this->cur_elements = cur_e;
|
|
||||||
this->root_index = Index_Nil;
|
|
||||||
this->left_most_index = Index_Nil;
|
|
||||||
this->right_most_index = Index_Nil;
|
|
||||||
this->index_signature = ind_sig;
|
|
||||||
this->buffer_size = buf_sz;
|
|
||||||
this->node_size = node_sz;
|
|
||||||
this->element_size = e_sz;
|
|
||||||
this->_30 = _30;
|
|
||||||
this->_34 = _34;
|
|
||||||
this->_38 = _38;
|
|
||||||
this->_3C = _3C;
|
|
||||||
this->_40 = _40;
|
|
||||||
this->_44 = _44;
|
|
||||||
this->_48 = 0;
|
|
||||||
this->_4C = 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
static_assert(sizeof(Header) == 0x50);
|
|
||||||
|
|
||||||
struct IndexPair {
|
|
||||||
int first;
|
|
||||||
int last;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Node {
|
|
||||||
Member m_data;
|
|
||||||
int m_parent;
|
|
||||||
int m_right;
|
|
||||||
int m_left;
|
|
||||||
Color m_color;
|
|
||||||
|
|
||||||
void SetLeft(int l, Node *n, int p) {
|
|
||||||
m_left = l;
|
|
||||||
n->m_parent = p;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetRight(int r, Node *n, int p) {
|
|
||||||
m_right = r;
|
|
||||||
n->m_parent = p;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Iterator;
|
|
||||||
class ConstIterator;
|
|
||||||
|
|
||||||
class IteratorBase {
|
|
||||||
private:
|
|
||||||
friend class ConstIterator;
|
|
||||||
private:
|
|
||||||
const FixedTree *m_this;
|
|
||||||
int m_index;
|
|
||||||
protected:
|
|
||||||
constexpr ALWAYS_INLINE IteratorBase(const FixedTree *tree, int index) : m_this(tree), m_index(index) { /* ... */ }
|
|
||||||
|
|
||||||
constexpr bool IsEqualImpl(const IteratorBase &rhs) const {
|
|
||||||
/* Validate pre-conditions. */
|
|
||||||
AMS_ASSERT(m_this);
|
|
||||||
|
|
||||||
/* Check for tree equality. */
|
|
||||||
if (m_this != rhs.m_this) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check for nil. */
|
|
||||||
if (m_this->IsNil(m_index) && m_this->IsNil(rhs.m_index)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check for index equality. */
|
|
||||||
return m_index == rhs.m_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr IteratorMember &DereferenceImpl() const {
|
|
||||||
/* Validate pre-conditions. */
|
|
||||||
AMS_ASSERT(m_this);
|
|
||||||
|
|
||||||
if (!m_this->IsNil(m_index)) {
|
|
||||||
return m_this->m_nodes[m_index].m_data;
|
|
||||||
} else {
|
|
||||||
AMS_ASSERT(false);
|
|
||||||
return m_this->GetNode(std::numeric_limits<int>::max())->m_data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE IteratorBase &IncrementImpl() {
|
|
||||||
/* Validate pre-conditions. */
|
|
||||||
AMS_ASSERT(m_this);
|
|
||||||
|
|
||||||
this->OperateIndex(true);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE IteratorBase &DecrementImpl() {
|
|
||||||
/* Validate pre-conditions. */
|
|
||||||
AMS_ASSERT(m_this);
|
|
||||||
|
|
||||||
this->OperateIndex(false);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr void OperateIndex(bool increment) {
|
|
||||||
if (increment) {
|
|
||||||
/* We're incrementing. */
|
|
||||||
if (m_index == Index_BeforeBegin) {
|
|
||||||
m_index = 0;
|
|
||||||
} else {
|
|
||||||
m_index = m_this->UncheckedPP(m_index);
|
|
||||||
if (m_this->IsNil(m_index)) {
|
|
||||||
m_index = Index_AfterEnd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* We're decrementing. */
|
|
||||||
if (m_index == Index_AfterEnd) {
|
|
||||||
m_index = static_cast<int>(m_this->size()) - 1;
|
|
||||||
} else {
|
|
||||||
m_index = m_this->UncheckedMM(m_index);
|
|
||||||
if (m_this->IsNil(m_index)) {
|
|
||||||
m_index = Index_BeforeBegin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Iterator : public IteratorBase {
|
|
||||||
public:
|
|
||||||
constexpr ALWAYS_INLINE Iterator(const FixedTree &tree) : IteratorBase(std::addressof(tree), tree.size() ? tree.GetLMost() : Index_Leaf) { /* ... */ }
|
|
||||||
constexpr ALWAYS_INLINE Iterator(const FixedTree &tree, int index) : IteratorBase(std::addressof(tree), index) { /* ... */ }
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE Iterator(const Iterator &rhs) = default;
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE bool operator==(const Iterator &rhs) const {
|
|
||||||
return this->IsEqualImpl(rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE bool operator!=(const Iterator &rhs) const {
|
|
||||||
return !(*this == rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE IteratorMember &operator*() const {
|
|
||||||
return static_cast<IteratorMember &>(this->DereferenceImpl());
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE IteratorMember *operator->() const {
|
|
||||||
return std::addressof(this->operator *());
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE Iterator &operator++() {
|
|
||||||
return static_cast<Iterator &>(this->IncrementImpl());
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE Iterator &operator--() {
|
|
||||||
return static_cast<Iterator &>(this->DecrementImpl());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class ConstIterator : public IteratorBase {
|
|
||||||
public:
|
|
||||||
constexpr ALWAYS_INLINE ConstIterator(const FixedTree &tree) : IteratorBase(std::addressof(tree), tree.size() ? tree.GetLMost() : Index_Leaf) { /* ... */ }
|
|
||||||
constexpr ALWAYS_INLINE ConstIterator(const FixedTree &tree, int index) : IteratorBase(std::addressof(tree), index) { /* ... */ }
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE ConstIterator(const ConstIterator &rhs) = default;
|
|
||||||
constexpr ALWAYS_INLINE ConstIterator(const Iterator &rhs) : IteratorBase(rhs.m_this, rhs.m_index) { /* ... */ }
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE bool operator==(const ConstIterator &rhs) const {
|
|
||||||
return this->IsEqualImpl(rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE bool operator!=(const ConstIterator &rhs) const {
|
|
||||||
return !(*this == rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE const IteratorMember &operator*() const {
|
|
||||||
return static_cast<const IteratorMember &>(this->DereferenceImpl());
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE const IteratorMember *operator->() const {
|
|
||||||
return std::addressof(this->operator *());
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE ConstIterator &operator++() {
|
|
||||||
return static_cast<ConstIterator &>(this->IncrementImpl());
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE ConstIterator &operator--() {
|
|
||||||
return static_cast<ConstIterator &>(this->DecrementImpl());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public:
|
|
||||||
using iterator = Iterator;
|
|
||||||
using const_iterator = ConstIterator;
|
|
||||||
private:
|
|
||||||
impl::AvailableIndexFinder m_index_finder;
|
|
||||||
Node m_dummy_leaf;
|
|
||||||
Node *m_p_dummy_leaf;
|
|
||||||
u8 *m_buffer;
|
|
||||||
Header *m_header;
|
|
||||||
Node *m_nodes;
|
|
||||||
iterator m_end_iterator;
|
|
||||||
public:
|
|
||||||
FixedTree() : m_end_iterator(*this, Index_Nil) {
|
|
||||||
this->SetDummyMemory();
|
|
||||||
}
|
|
||||||
protected:
|
|
||||||
void InitializeImpl(int num_elements, void *buffer, size_t buffer_size) {
|
|
||||||
/* Check pre-conditions. */
|
|
||||||
AMS_ASSERT(num_elements > 0);
|
|
||||||
AMS_ASSERT(static_cast<size_t>(num_elements) <= max_size);
|
|
||||||
AMS_ASSERT(util::IsAligned(reinterpret_cast<uintptr_t>(buffer), BufferAlignment));
|
|
||||||
AMS_ASSERT(buffer_size == GetRequiredMemorySize(num_elements));
|
|
||||||
|
|
||||||
/* Set buffer. */
|
|
||||||
m_buffer = static_cast<u8 *>(buffer);
|
|
||||||
m_header = reinterpret_cast<Header *>(m_buffer);
|
|
||||||
|
|
||||||
/* Setup memory. */
|
|
||||||
this->InitializeMemory(num_elements, buffer_size, impl::AvailableIndexFinder::GetSignature());
|
|
||||||
|
|
||||||
/* Check that buffer was set up correctly. */
|
|
||||||
AMS_ASSERT(static_cast<u32>(buffer_size) == m_header->buffer_size);
|
|
||||||
|
|
||||||
/* Setup dummy leaf. */
|
|
||||||
this->SetDummyMemory();
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
static constexpr size_t SizeOfNodes(size_t num_elements) {
|
|
||||||
return util::AlignUp(sizeof(Node) * num_elements, BufferAlignment);
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr size_t SizeOfIndex(size_t num_elements) {
|
|
||||||
return impl::AvailableIndexFinder::GetRequiredMemorySize(num_elements);
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr size_t GetRequiredMemorySize(size_t num_elements) {
|
|
||||||
return sizeof(Header) + SizeOfNodes(num_elements) + SizeOfIndex(num_elements);
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
void SetDummyMemory() {
|
|
||||||
m_dummy_leaf.m_color = Color::Black;
|
|
||||||
m_dummy_leaf.m_parent = Index_Nil;
|
|
||||||
m_dummy_leaf.m_left = Index_Leaf;
|
|
||||||
m_dummy_leaf.m_right = Index_Leaf;
|
|
||||||
m_p_dummy_leaf = std::addressof(m_dummy_leaf);
|
|
||||||
}
|
|
||||||
|
|
||||||
void InitializeMemory(int num_elements, u32 buffer_size, u32 signature) {
|
|
||||||
/* Initialize the header. */
|
|
||||||
m_header->InitializeHeader(1, num_elements, 0, signature, buffer_size, sizeof(Node), sizeof(Member), 4, 4, 4, 4, 4, BufferAlignment);
|
|
||||||
|
|
||||||
/* Setup index finder. */
|
|
||||||
m_index_finder.Initialize(std::addressof(m_header->cur_elements), std::addressof(m_header->max_elements), m_buffer + sizeof(*m_header) + SizeOfNodes(num_elements));
|
|
||||||
|
|
||||||
/* Set nodes array. */
|
|
||||||
m_nodes = reinterpret_cast<Node *>(m_buffer + sizeof(*m_header));
|
|
||||||
}
|
|
||||||
|
|
||||||
Node *GetNode(int index) const {
|
|
||||||
if (index >= 0) {
|
|
||||||
return m_nodes + index;
|
|
||||||
} else {
|
|
||||||
return m_p_dummy_leaf;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE bool IsNil(int index) const {
|
|
||||||
return index < 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE bool IsLeaf(int index) const {
|
|
||||||
return index == Index_Leaf;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetRoot() const { return m_header->root_index; }
|
|
||||||
void SetRoot(int index) {
|
|
||||||
if (index == Index_Leaf) {
|
|
||||||
index = Index_Nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_header->root_index = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetLMost() const { return m_header->left_most_index; }
|
|
||||||
void SetLMost(int index) { m_header->left_most_index = index; }
|
|
||||||
|
|
||||||
int GetRMost() const { return m_header->right_most_index; }
|
|
||||||
void SetRMost(int index) { m_header->right_most_index = index; }
|
|
||||||
|
|
||||||
int GetParent(int index) const {
|
|
||||||
return this->GetNode(index)->m_parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
int AcquireIndex() { return m_index_finder.AcquireIndex(); }
|
|
||||||
void ReleaseIndex(int index) { return m_index_finder.ReleaseIndex(index); }
|
|
||||||
|
|
||||||
int EraseByIndex(int target_index) {
|
|
||||||
/* Setup tracking variables. */
|
|
||||||
const auto next_index = this->UncheckedPP(target_index);
|
|
||||||
auto *target_node = this->GetNode(target_index);
|
|
||||||
|
|
||||||
auto a_index = Index_Leaf;
|
|
||||||
auto *a_node = this->GetNode(a_index);
|
|
||||||
auto b_index = Index_Leaf;
|
|
||||||
auto *b_node = this->GetNode(b_index);
|
|
||||||
auto cur_index = target_index;
|
|
||||||
auto *cur_node = this->GetNode(cur_index);
|
|
||||||
|
|
||||||
if (cur_node->m_left == Index_Leaf) {
|
|
||||||
a_index = cur_node->m_right;
|
|
||||||
a_node = this->GetNode(a_index);
|
|
||||||
|
|
||||||
m_p_dummy_leaf->m_parent = cur_index;
|
|
||||||
} else {
|
|
||||||
if (cur_node->m_right == Index_Leaf) {
|
|
||||||
a_index = cur_node->m_left;
|
|
||||||
} else {
|
|
||||||
cur_index = next_index;
|
|
||||||
cur_node = this->GetNode(cur_index);
|
|
||||||
a_index = cur_node->m_right;
|
|
||||||
}
|
|
||||||
a_node = this->GetNode(a_index);
|
|
||||||
|
|
||||||
m_p_dummy_leaf->m_parent = cur_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ensure the a node is updated (redundant) */
|
|
||||||
a_node = this->GetNode(a_index);
|
|
||||||
|
|
||||||
/* Update relevant metrics/links. */
|
|
||||||
if (cur_index == target_index) {
|
|
||||||
/* No left, but has right. */
|
|
||||||
b_index = target_node->m_parent;
|
|
||||||
b_node = this->GetNode(b_index);
|
|
||||||
|
|
||||||
if (a_index != Index_Leaf) {
|
|
||||||
a_node->m_parent = b_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->GetRoot() == target_index) {
|
|
||||||
this->SetRoot(a_index);
|
|
||||||
} else if (b_node->m_left == target_index) {
|
|
||||||
b_node->m_left = a_index;
|
|
||||||
} else {
|
|
||||||
b_node->m_right = a_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->GetLMost() == target_index) {
|
|
||||||
this->SetLMost((a_index != Index_Leaf) ? this->FindMinInSubtree(a_index) : b_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->GetRMost() == target_index) {
|
|
||||||
this->SetRMost((a_index != Index_Leaf) ? this->FindMaxInSubtree(a_index) : b_index);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Has left or doesn't have right. */
|
|
||||||
|
|
||||||
/* Fix left links. */
|
|
||||||
this->GetNode(target_node->m_left)->m_parent = cur_index;
|
|
||||||
cur_node->m_left = target_node->m_left;
|
|
||||||
|
|
||||||
if (cur_index == target_node->m_right) {
|
|
||||||
b_index = cur_index;
|
|
||||||
b_node = this->GetNode(b_index);
|
|
||||||
} else {
|
|
||||||
b_index = cur_node->m_parent;
|
|
||||||
b_node = this->GetNode(b_index);
|
|
||||||
|
|
||||||
if (!this->IsNil(a_index)) {
|
|
||||||
a_node->m_parent = b_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
b_node->m_left = a_index;
|
|
||||||
cur_node->m_right = target_node->m_right;
|
|
||||||
|
|
||||||
this->GetNode(target_node->m_right)->m_parent = cur_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->GetRoot() == target_index) {
|
|
||||||
this->SetRoot(cur_index);
|
|
||||||
} else {
|
|
||||||
if (this->GetNode(target_node->m_parent)->m_left == target_index) {
|
|
||||||
this->GetNode(target_node->m_parent)->m_left = cur_index;
|
|
||||||
} else {
|
|
||||||
this->GetNode(target_node->m_parent)->m_right = cur_index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_node->m_parent = target_node->m_parent;
|
|
||||||
std::swap(cur_node->m_color, target_node->m_color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ensure the tree remains balanced. */
|
|
||||||
if (target_node->m_color == Color::Black) {
|
|
||||||
while (true) {
|
|
||||||
if (a_index == this->GetRoot() || a_node->m_color != Color::Black) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a_index == b_node->m_left) {
|
|
||||||
cur_index = b_node->m_right;
|
|
||||||
cur_node = this->GetNode(cur_index);
|
|
||||||
|
|
||||||
if (cur_node->m_color == Color::Red) {
|
|
||||||
cur_node->m_color = Color::Black;
|
|
||||||
b_node->m_color = Color::Red;
|
|
||||||
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
|
|
||||||
|
|
||||||
this->RotateLeft(b_index);
|
|
||||||
|
|
||||||
cur_index = b_node->m_right;
|
|
||||||
cur_node = this->GetNode(cur_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->IsNil(cur_index)) {
|
|
||||||
a_index = b_index;
|
|
||||||
a_node = b_node;
|
|
||||||
} else {
|
|
||||||
if (this->GetNode(cur_node->m_left)->m_color != Color::Black || this->GetNode(cur_node->m_right)->m_color != Color::Black) {
|
|
||||||
if (this->GetNode(cur_node->m_right)->m_color == Color::Black) {
|
|
||||||
this->GetNode(cur_node->m_left)->m_color = Color::Black;
|
|
||||||
cur_node->m_color = Color::Red;
|
|
||||||
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
|
|
||||||
|
|
||||||
this->RotateRight(cur_index);
|
|
||||||
|
|
||||||
cur_index = b_node->m_right;
|
|
||||||
cur_node = this->GetNode(cur_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_node->m_color = b_node->m_color;
|
|
||||||
b_node->m_color = Color::Black;
|
|
||||||
|
|
||||||
this->GetNode(cur_node->m_right)->m_color = Color::Black;
|
|
||||||
|
|
||||||
this->RotateLeft(b_index);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_node->m_color = Color::Red;
|
|
||||||
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
|
|
||||||
|
|
||||||
a_index = b_index;
|
|
||||||
a_node = b_node;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cur_index = b_node->m_left;
|
|
||||||
cur_node = this->GetNode(cur_index);
|
|
||||||
|
|
||||||
if (cur_node->m_color == Color::Red) {
|
|
||||||
cur_node->m_color = Color::Black;
|
|
||||||
b_node->m_color = Color::Red;
|
|
||||||
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
|
|
||||||
|
|
||||||
this->RotateRight(b_index);
|
|
||||||
|
|
||||||
cur_index = b_node->m_left;
|
|
||||||
cur_node = this->GetNode(cur_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->IsNil(cur_index)) {
|
|
||||||
a_index = b_index;
|
|
||||||
a_node = b_node;
|
|
||||||
} else {
|
|
||||||
if (this->GetNode(cur_node->m_right)->m_color != Color::Black || this->GetNode(cur_node->m_left)->m_color != Color::Black) {
|
|
||||||
if (this->GetNode(cur_node->m_left)->m_color == Color::Black) {
|
|
||||||
this->GetNode(cur_node->m_right)->m_color = Color::Black;
|
|
||||||
cur_node->m_color = Color::Red;
|
|
||||||
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
|
|
||||||
|
|
||||||
this->RotateLeft(cur_index);
|
|
||||||
|
|
||||||
cur_index = b_node->m_left;
|
|
||||||
cur_node = this->GetNode(cur_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_node->m_color = b_node->m_color;
|
|
||||||
b_node->m_color = Color::Black;
|
|
||||||
|
|
||||||
this->GetNode(cur_node->m_left)->m_color = Color::Black;
|
|
||||||
|
|
||||||
this->RotateRight(b_index);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_node->m_color = Color::Red;
|
|
||||||
AMS_ASSERT(m_p_dummy_leaf->m_color == Color::Black);
|
|
||||||
|
|
||||||
a_index = b_index;
|
|
||||||
a_node = b_node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b_index = a_node->m_parent;
|
|
||||||
b_node = this->GetNode(b_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
a_node->m_color = Color::Black;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Release the index. */
|
|
||||||
this->ReleaseIndex(target_index);
|
|
||||||
return target_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
int FindIndex(const Member &elem) const {
|
|
||||||
return this->FindIndexSub(this->GetRoot(), elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
int FindIndexSub(int index, const Member &elem) const {
|
|
||||||
if (index != Index_Nil) {
|
|
||||||
auto *node = this->GetNode(index);
|
|
||||||
if (Compare{}(elem, node->m_data)) {
|
|
||||||
if (!this->IsLeaf(node->m_left)) {
|
|
||||||
return this->FindIndexSub(node->m_left, elem);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!Compare{}(node->m_data, elem)) {
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this->IsLeaf(node->m_right)) {
|
|
||||||
return this->FindIndexSub(node->m_right, elem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Index_Nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
int FindMaxInSubtree(int index) const {
|
|
||||||
int max = index;
|
|
||||||
for (auto *node = this->GetNode(index); !this->IsNil(node->m_right); node = this->GetNode(node->m_right)) {
|
|
||||||
max = node->m_right;
|
|
||||||
}
|
|
||||||
return max;
|
|
||||||
}
|
|
||||||
|
|
||||||
int FindMinInSubtree(int index) const {
|
|
||||||
int min = index;
|
|
||||||
for (auto *node = this->GetNode(index); !this->IsNil(node->m_left); node = this->GetNode(node->m_left)) {
|
|
||||||
min = node->m_left;
|
|
||||||
}
|
|
||||||
return min;
|
|
||||||
}
|
|
||||||
|
|
||||||
int InsertAt(bool before, int parent, const Member &elem) {
|
|
||||||
/* Get an index for the new element. */
|
|
||||||
const auto index = this->AcquireIndex();
|
|
||||||
|
|
||||||
/* Create the node. */
|
|
||||||
auto *node = this->GetNode(index);
|
|
||||||
node->m_color = Color::Red;
|
|
||||||
node->m_parent = parent;
|
|
||||||
node->m_right = Index_Leaf;
|
|
||||||
node->m_left = Index_Leaf;
|
|
||||||
std::memcpy(reinterpret_cast<u8 *>(std::addressof(node->m_data)), reinterpret_cast<const u8 *>(std::addressof(elem)), sizeof(node->m_data));
|
|
||||||
|
|
||||||
/* Fix up the parent node. */
|
|
||||||
auto *parent_node = this->GetNode(parent);
|
|
||||||
if (before) {
|
|
||||||
parent_node->m_left = index;
|
|
||||||
if (parent == this->GetLMost()) {
|
|
||||||
this->SetLMost(index);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
parent_node->m_right = index;
|
|
||||||
if (parent == this->GetRMost()) {
|
|
||||||
this->SetRMost(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ensure the tree is balanced. */
|
|
||||||
int cur_index = index;
|
|
||||||
while (true) {
|
|
||||||
auto *cur_node = this->GetNode(cur_index);
|
|
||||||
if (this->GetNode(cur_node->m_parent)->m_color != Color::Red) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto *p_node = this->GetNode(cur_node->m_parent);
|
|
||||||
auto *g_node = this->GetNode(p_node->m_parent);
|
|
||||||
if (cur_node->m_parent == g_node->m_left) {
|
|
||||||
if (auto *gr_node = this->GetNode(g_node->m_right); gr_node->m_color == Color::Red) {
|
|
||||||
p_node->m_color = Color::Black;
|
|
||||||
gr_node->m_color = Color::Black;
|
|
||||||
g_node->m_color = Color::Red;
|
|
||||||
AMS_ASSERT(m_p_dummy_leaf->m_color != Color::Red);
|
|
||||||
|
|
||||||
cur_index = p_node->m_parent;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cur_index == p_node->m_right) {
|
|
||||||
cur_index = cur_node->m_parent;
|
|
||||||
cur_node = this->GetNode(cur_index);
|
|
||||||
this->RotateLeft(cur_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
p_node = this->GetNode(cur_node->m_parent);
|
|
||||||
p_node->m_color = Color::Black;
|
|
||||||
|
|
||||||
g_node = this->GetNode(p_node->m_parent);
|
|
||||||
g_node->m_color = Color::Red;
|
|
||||||
|
|
||||||
AMS_ASSERT(m_p_dummy_leaf->m_color != Color::Red);
|
|
||||||
|
|
||||||
this->RotateRight(p_node->m_parent);
|
|
||||||
} else {
|
|
||||||
if (auto *gl_node = this->GetNode(g_node->m_left); gl_node->m_color == Color::Red) {
|
|
||||||
p_node->m_color = Color::Black;
|
|
||||||
gl_node->m_color = Color::Black;
|
|
||||||
g_node->m_color = Color::Red;
|
|
||||||
AMS_ASSERT(m_p_dummy_leaf->m_color != Color::Red);
|
|
||||||
|
|
||||||
cur_index = p_node->m_parent;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cur_index == p_node->m_left) {
|
|
||||||
cur_index = cur_node->m_parent;
|
|
||||||
cur_node = this->GetNode(cur_index);
|
|
||||||
this->RotateRight(cur_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
p_node = this->GetNode(cur_node->m_parent);
|
|
||||||
p_node->m_color = Color::Black;
|
|
||||||
|
|
||||||
g_node = this->GetNode(p_node->m_parent);
|
|
||||||
g_node->m_color = Color::Red;
|
|
||||||
|
|
||||||
AMS_ASSERT(m_p_dummy_leaf->m_color != Color::Red);
|
|
||||||
|
|
||||||
this->RotateLeft(p_node->m_parent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set root color. */
|
|
||||||
this->GetNode(this->GetRoot())->m_color = Color::Black;
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
int InsertNoHint(bool before, const Member &elem) {
|
|
||||||
int cur_index = this->GetRoot();
|
|
||||||
int prev_index = Index_Nil;
|
|
||||||
bool less = true;
|
|
||||||
while (cur_index != Index_Nil && cur_index != Index_Leaf) {
|
|
||||||
auto *node = this->GetNode(cur_index);
|
|
||||||
prev_index = cur_index;
|
|
||||||
|
|
||||||
if (before) {
|
|
||||||
less = Compare{}(node->m_data, elem);
|
|
||||||
} else {
|
|
||||||
less = Compare{}(elem, node->m_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (less) {
|
|
||||||
cur_index = node->m_left;
|
|
||||||
} else {
|
|
||||||
cur_index = node->m_right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cur_index == Index_Nil) {
|
|
||||||
/* Create a new node. */
|
|
||||||
const auto index = this->AcquireIndex();
|
|
||||||
auto *node = this->GetNode(index);
|
|
||||||
node->m_color = Color::Black;
|
|
||||||
node->m_parent = Index_Nil;
|
|
||||||
node->m_right = Index_Leaf;
|
|
||||||
node->m_left = Index_Leaf;
|
|
||||||
std::memcpy(reinterpret_cast<u8 *>(std::addressof(node->m_data)), reinterpret_cast<const u8 *>(std::addressof(elem)), sizeof(node->m_data));
|
|
||||||
|
|
||||||
this->SetRoot(index);
|
|
||||||
this->SetLMost(index);
|
|
||||||
this->SetRMost(index);
|
|
||||||
|
|
||||||
return index;
|
|
||||||
} else {
|
|
||||||
auto *compare_node = this->GetNode(prev_index);
|
|
||||||
if (less) {
|
|
||||||
if (prev_index == this->GetLMost()) {
|
|
||||||
return this->InsertAt(less, prev_index, elem);
|
|
||||||
} else {
|
|
||||||
compare_node = this->GetNode(this->UncheckedMM(prev_index));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Compare{}(compare_node->m_data, elem)) {
|
|
||||||
return this->InsertAt(less, prev_index, elem);
|
|
||||||
} else {
|
|
||||||
return Index_Nil;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void RotateLeft(int index) {
|
|
||||||
/* Determine indices. */
|
|
||||||
const auto p_index = this->GetParent(index);
|
|
||||||
const auto r_index = this->GetNode(index)->m_right;
|
|
||||||
const auto l_index = this->GetNode(index)->m_left;
|
|
||||||
const auto rl_index = this->GetNode(r_index)->m_left;
|
|
||||||
const auto rr_index = this->GetNode(r_index)->m_right;
|
|
||||||
|
|
||||||
/* Get nodes. */
|
|
||||||
auto *node = this->GetNode(index);
|
|
||||||
auto *p_node = this->GetNode(p_index);
|
|
||||||
auto *r_node = this->GetNode(r_index);
|
|
||||||
auto *l_node = this->GetNode(l_index);
|
|
||||||
auto *rl_node = this->GetNode(rl_index);
|
|
||||||
auto *rr_node = this->GetNode(rr_index);
|
|
||||||
|
|
||||||
/* Perform the rotation. */
|
|
||||||
if (p_index == Index_Nil) {
|
|
||||||
r_node->m_parent = Index_Nil;
|
|
||||||
m_header->root_index = r_index;
|
|
||||||
} else if (p_node->m_left == index) {
|
|
||||||
p_node->SetLeft(r_index, r_node, p_index);
|
|
||||||
} else {
|
|
||||||
p_node->SetRight(r_index, r_node, p_index);
|
|
||||||
}
|
|
||||||
r_node->SetLeft(index, node, r_index);
|
|
||||||
r_node->SetRight(rr_index, rr_node, r_index);
|
|
||||||
node->SetLeft(l_index, l_node, index);
|
|
||||||
node->SetRight(rl_index, rl_node, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RotateRight(int index) {
|
|
||||||
/* Determine indices. */
|
|
||||||
const auto p_index = this->GetParent(index);
|
|
||||||
const auto l_index = this->GetNode(index)->m_left;
|
|
||||||
const auto ll_index = this->GetNode(l_index)->m_left;
|
|
||||||
const auto lr_index = this->GetNode(l_index)->m_right;
|
|
||||||
const auto r_index = this->GetNode(index)->m_right;
|
|
||||||
|
|
||||||
/* Get nodes. */
|
|
||||||
auto *node = this->GetNode(index);
|
|
||||||
auto *p_node = this->GetNode(p_index);
|
|
||||||
auto *l_node = this->GetNode(l_index);
|
|
||||||
auto *ll_node = this->GetNode(ll_index);
|
|
||||||
auto *lr_node = this->GetNode(lr_index);
|
|
||||||
auto *r_node = this->GetNode(r_index);
|
|
||||||
|
|
||||||
/* Perform the rotation. */
|
|
||||||
if (p_index == Index_Nil) {
|
|
||||||
l_node->m_parent = Index_Nil;
|
|
||||||
m_header->root_index = l_index;
|
|
||||||
} else if (p_node->m_left == index) {
|
|
||||||
p_node->SetLeft(l_index, l_node, p_index);
|
|
||||||
} else {
|
|
||||||
p_node->SetRight(l_index, l_node, p_index);
|
|
||||||
}
|
|
||||||
l_node->SetLeft(ll_index, ll_node, l_index);
|
|
||||||
l_node->SetRight(index, node, l_index);
|
|
||||||
node->SetLeft(lr_index, lr_node, index);
|
|
||||||
node->SetRight(r_index, r_node, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
int UncheckedMM(int index) const {
|
|
||||||
auto *node = this->GetNode(index);
|
|
||||||
if (this->IsNil(index)) {
|
|
||||||
index = this->GetRMost();
|
|
||||||
node = this->GetNode(index);
|
|
||||||
} else if (this->IsNil(node->m_left)) {
|
|
||||||
int parent = node->m_parent;
|
|
||||||
Node *p;
|
|
||||||
|
|
||||||
for (p = this->GetNode(parent); !this->IsNil(parent) && index == p->m_left; p = this->GetNode(parent)) {
|
|
||||||
index = parent;
|
|
||||||
node = p;
|
|
||||||
parent = p->m_parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this->IsNil(index)) {
|
|
||||||
index = parent;
|
|
||||||
node = p;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
index = this->FindMaxInSubtree(node->m_left);
|
|
||||||
node = this->GetNode(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->IsNil(index)) {
|
|
||||||
return Index_Leaf;
|
|
||||||
} else {
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int UncheckedPP(int index) const {
|
|
||||||
auto *node = this->GetNode(index);
|
|
||||||
|
|
||||||
if (!this->IsNil(index)) {
|
|
||||||
if (this->IsNil(node->m_right)) {
|
|
||||||
int parent = node->m_parent;
|
|
||||||
Node *p;
|
|
||||||
|
|
||||||
for (p = this->GetNode(parent); !this->IsNil(parent) && index == p->m_right; p = this->GetNode(parent)) {
|
|
||||||
index = parent;
|
|
||||||
node = p;
|
|
||||||
parent = p->m_parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
index = parent;
|
|
||||||
node = p;
|
|
||||||
} else {
|
|
||||||
index = this->FindMinInSubtree(node->m_right);
|
|
||||||
node = this->GetNode(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->IsNil(index)) {
|
|
||||||
return Index_Leaf;
|
|
||||||
} else {
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
void Initialize(size_t num_elements, void *buffer, size_t buffer_size) {
|
|
||||||
AMS_ASSERT(num_elements <= max_size);
|
|
||||||
|
|
||||||
return this->InitializeImpl(static_cast<int>(num_elements), buffer, buffer_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator begin() { return iterator(*this); }
|
|
||||||
const_iterator begin() const { return const_iterator(*this); }
|
|
||||||
|
|
||||||
iterator end() { return m_end_iterator; }
|
|
||||||
const_iterator end() const { return m_end_iterator; }
|
|
||||||
|
|
||||||
size_t size() const { return m_header->cur_elements; }
|
|
||||||
|
|
||||||
void clear() {
|
|
||||||
const auto num_elements = m_header->max_elements;
|
|
||||||
const auto buffer_size = m_header->buffer_size;
|
|
||||||
AMS_ASSERT(buffer_size == static_cast<u32>(GetRequiredMemorySize(num_elements)));
|
|
||||||
|
|
||||||
return this->InitializeMemory(num_elements, buffer_size, impl::AvailableIndexFinder::GetSignature());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool erase(const Member &elem) {
|
|
||||||
const auto range = this->equal_range(elem);
|
|
||||||
if (range.first != range.last) {
|
|
||||||
this->EraseByIndex(range.first);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator find(const Member &elem) {
|
|
||||||
if (const auto index = this->FindIndex(elem); index >= 0) {
|
|
||||||
return iterator(*this, index);
|
|
||||||
} else {
|
|
||||||
return this->end();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const_iterator find(const Member &elem) const {
|
|
||||||
if (const auto index = this->FindIndex(elem); index >= 0) {
|
|
||||||
return const_iterator(*this, index);
|
|
||||||
} else {
|
|
||||||
return this->end();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<iterator, bool> insert(const Member &elem) {
|
|
||||||
const auto index = this->InsertNoHint(false, elem);
|
|
||||||
const auto it = iterator(*this, index);
|
|
||||||
return std::make_pair(it, !this->IsNil(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
IndexPair equal_range(const Member &elem) {
|
|
||||||
/* Get node to start iteration. */
|
|
||||||
auto cur_index = this->GetRoot();
|
|
||||||
auto cur_node = this->GetNode(cur_index);
|
|
||||||
|
|
||||||
auto min_index = Index_Leaf;
|
|
||||||
auto min_node = this->GetNode(min_index);
|
|
||||||
|
|
||||||
auto max_index = Index_Leaf;
|
|
||||||
auto max_node = this->GetNode(max_index);
|
|
||||||
|
|
||||||
/* Iterate until current is leaf, to find min/max. */
|
|
||||||
while (cur_index != Index_Leaf) {
|
|
||||||
if (Compare{}(cur_node->m_data, elem)) {
|
|
||||||
cur_index = cur_node->m_right;
|
|
||||||
cur_node = this->GetNode(cur_index);
|
|
||||||
} else {
|
|
||||||
if (max_index == Index_Leaf && Compare{}(elem, cur_node->m_data)) {
|
|
||||||
max_index = cur_index;
|
|
||||||
max_node = this->GetNode(max_index);
|
|
||||||
}
|
|
||||||
min_index = cur_index;
|
|
||||||
min_node = this->GetNode(min_index);
|
|
||||||
|
|
||||||
cur_index = cur_node->m_left;
|
|
||||||
cur_node = this->GetNode(cur_index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Iterate again, to find correct range extent for max. */
|
|
||||||
cur_index = (max_index == Index_Leaf) ? this->GetRoot() : max_node->m_left;
|
|
||||||
cur_node = this->GetNode(cur_index);
|
|
||||||
while (cur_index != Index_Leaf) {
|
|
||||||
if (Compare{}(elem, cur_node->m_data)) {
|
|
||||||
max_index = cur_index;
|
|
||||||
max_node = cur_node;
|
|
||||||
cur_index = cur_node->m_left;
|
|
||||||
} else {
|
|
||||||
cur_index = cur_node->m_right;
|
|
||||||
}
|
|
||||||
cur_node = this->GetNode(cur_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
AMS_UNUSED(min_node);
|
|
||||||
return IndexPair{min_index, max_index};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Scope guard logic lovingly taken from Andrei Alexandrescu's "Systemic Error Handling in C++" */
|
/* Scope guard logic lovingly taken from Andrei Alexandrescu's "Systemic Error Handling in C++" */
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <vapours/common.hpp>
|
#include <vapours/common.hpp>
|
||||||
|
|||||||
90
libraries/libvapours/source/prfile2/pdm/prfile2_pdm_api.cpp
Normal file
90
libraries/libvapours/source/prfile2/pdm/prfile2_pdm_api.cpp
Normal 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/>.
|
||||||
|
*/
|
||||||
|
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||||
|
#include <mesosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||||
|
#include <exosphere.hpp>
|
||||||
|
#else
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#endif
|
||||||
|
#include "prfile2_pdm_disk_set.hpp"
|
||||||
|
|
||||||
|
namespace ams::prfile2::pdm {
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
|
||||||
|
constinit DiskSet g_disk_set;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pdm::Error Initialize(u32 config, void *param) {
|
||||||
|
AMS_UNUSED(config, param);
|
||||||
|
|
||||||
|
/* Clear the disk set. */
|
||||||
|
std::memset(std::addressof(impl::g_disk_set), 0, sizeof(impl::g_disk_set));
|
||||||
|
|
||||||
|
return pdm::Error_Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdm::Error OpenDisk(InitDisk *init_disk_table, HandleType *out) {
|
||||||
|
/* Check the arguments. */
|
||||||
|
if (out == nullptr) {
|
||||||
|
return pdm::Error_InvalidParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the output as invalid. */
|
||||||
|
*out = InvalidHandle;
|
||||||
|
|
||||||
|
/* Open the disk. */
|
||||||
|
return disk::OpenDisk(init_disk_table, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
pdm::Error CloseDisk(HandleType handle) {
|
||||||
|
/* Check the input. */
|
||||||
|
if (IsInvalidHandle(handle)) {
|
||||||
|
return pdm::Error_InvalidParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close the disk. */
|
||||||
|
return disk::CloseDisk(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
pdm::Error OpenPartition(HandleType disk_handle, u16 part_id, HandleType *out) {
|
||||||
|
/* Check the arguments. */
|
||||||
|
if (out == nullptr || IsInvalidHandle(disk_handle)) {
|
||||||
|
return pdm::Error_InvalidParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the output as invalid. */
|
||||||
|
*out = InvalidHandle;
|
||||||
|
|
||||||
|
/* Open the partition. */
|
||||||
|
return part::OpenPartition(disk_handle, part_id, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
pdm::Error ClosePartition(HandleType handle) {
|
||||||
|
/* Check the input. */
|
||||||
|
if (IsInvalidHandle(handle)) {
|
||||||
|
return pdm::Error_InvalidParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close the partition. */
|
||||||
|
return part::ClosePartition(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
148
libraries/libvapours/source/prfile2/pdm/prfile2_pdm_disk.cpp
Normal file
148
libraries/libvapours/source/prfile2/pdm/prfile2_pdm_disk.cpp
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||||
|
#include <mesosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||||
|
#include <exosphere.hpp>
|
||||||
|
#else
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#endif
|
||||||
|
#include "prfile2_pdm_disk_set.hpp"
|
||||||
|
|
||||||
|
namespace ams::prfile2::pdm::disk {
|
||||||
|
|
||||||
|
pdm::Error OpenDisk(InitDisk *init_disk_table, HandleType *out) {
|
||||||
|
/* Check the arguments. */
|
||||||
|
if (out == nullptr || init_disk_table == nullptr || init_disk_table->function == nullptr) {
|
||||||
|
return pdm::Error_InvalidParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find a free disk holder. */
|
||||||
|
DiskHolder *holder = nullptr;
|
||||||
|
for (auto &h : impl::g_disk_set.disk_holders) {
|
||||||
|
if (h.disk == nullptr) {
|
||||||
|
holder = std::addressof(h);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (holder == nullptr) {
|
||||||
|
return pdm::Error_NotExistFreeDiskStruct;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find a free disk. */
|
||||||
|
Disk *disk = nullptr;
|
||||||
|
for (auto &d : impl::g_disk_set.disks) {
|
||||||
|
if (!d.IsOpen()) {
|
||||||
|
disk = std::addressof(d);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (disk == nullptr) {
|
||||||
|
return pdm::Error_NotExistFreeDiskStruct;
|
||||||
|
}
|
||||||
|
const auto disk_id = disk - impl::g_disk_set.disks;
|
||||||
|
|
||||||
|
/* Call the disk initialze function. */
|
||||||
|
init_disk_table->function(std::addressof(disk->disk_table), init_disk_table->ui_ext);
|
||||||
|
|
||||||
|
/* Set the disk as open .*/
|
||||||
|
disk->SetOpen(true);
|
||||||
|
|
||||||
|
/* Set the init disk table. */
|
||||||
|
disk->init_disk_table = init_disk_table;
|
||||||
|
|
||||||
|
/* Note the opened disk. */
|
||||||
|
++impl::g_disk_set.num_allocated_disks;
|
||||||
|
|
||||||
|
/* Increment the disk's signature. */
|
||||||
|
disk->signature = static_cast<u16>(disk->signature + 1);
|
||||||
|
|
||||||
|
/* Increment the disk's open count. */
|
||||||
|
++disk->open_count;
|
||||||
|
|
||||||
|
/* Set the disk in the holder. */
|
||||||
|
holder->signature = disk->signature;
|
||||||
|
holder->disk = disk;
|
||||||
|
|
||||||
|
/* Set the output handle. */
|
||||||
|
*out = ConstructDiskHandle(disk_id, disk->signature);
|
||||||
|
|
||||||
|
/* If this was the first opening for the disk, initialize it. */
|
||||||
|
if (disk->open_count == 1) {
|
||||||
|
if (auto err = disk->disk_table.function_table->initialize(*out); err != pdm::Error_Ok) {
|
||||||
|
/* Close the disk. */
|
||||||
|
--disk->open_count;
|
||||||
|
disk->SetOpen(false);
|
||||||
|
--impl::g_disk_set.num_allocated_disks;
|
||||||
|
|
||||||
|
/* Reset the holder. */
|
||||||
|
holder->disk = nullptr;
|
||||||
|
|
||||||
|
return pdm::Error_DriverError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pdm::Error_Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdm::Error CloseDisk(HandleType handle) {
|
||||||
|
/* Get the disk. */
|
||||||
|
Disk *disk = GetDisk(handle);
|
||||||
|
if (disk == nullptr) {
|
||||||
|
return pdm::Error_InvalidParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that the disk is open and unlocked. */
|
||||||
|
if (!disk->IsOpen()) {
|
||||||
|
return pdm::Error_StateClosed;
|
||||||
|
}
|
||||||
|
if (disk->IsLocked()) {
|
||||||
|
return pdm::Error_StateLocked;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the disk holder. */
|
||||||
|
DiskHolder *holder = GetDiskHolder(handle);
|
||||||
|
if (holder == nullptr) {
|
||||||
|
return pdm::Error_InvalidParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close the disk. */
|
||||||
|
if (disk->open_count == 1) {
|
||||||
|
/* Finalize the disk. */
|
||||||
|
if (auto err = disk->disk_table.function_table->finalize(handle); err != pdm::Error_Ok) {
|
||||||
|
if (auto part = disk->current_partition_handle; part != InvalidHandle) {
|
||||||
|
part::SetDriverErrorCode(part, err);
|
||||||
|
}
|
||||||
|
return pdm::Error_DriverError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the disk as closed. */
|
||||||
|
disk->SetOpen(false);
|
||||||
|
--impl::g_disk_set.num_allocated_disks;
|
||||||
|
|
||||||
|
/* Clear the disk holder. */
|
||||||
|
holder->disk = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decrement the disk's open count. */
|
||||||
|
--disk->open_count;
|
||||||
|
|
||||||
|
return pdm::Error_Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
103
libraries/libvapours/source/prfile2/pdm/prfile2_pdm_disk_set.hpp
Normal file
103
libraries/libvapours/source/prfile2/pdm/prfile2_pdm_disk_set.hpp
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
|
||||||
|
namespace ams::prfile2::pdm {
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
|
||||||
|
extern DiskSet g_disk_set;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE Disk *GetDisk(HandleType handle) {
|
||||||
|
if (AMS_LIKELY(IsDiskHandle(handle))) {
|
||||||
|
if (const auto id = GetHandleId(handle); AMS_LIKELY(id < MaxDisks)) {
|
||||||
|
const auto signature = GetHandleSignature(handle);
|
||||||
|
Disk *disk = std::addressof(impl::g_disk_set.disks[id]);
|
||||||
|
|
||||||
|
for (const auto &holder : impl::g_disk_set.disk_holders) {
|
||||||
|
if (holder.disk == disk && holder.signature == signature) {
|
||||||
|
return disk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE Disk *GetDiskUnsafe(HandleType handle) {
|
||||||
|
return std::addressof(impl::g_disk_set.disks[GetHandleId(handle)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE DiskHolder *GetDiskHolder(HandleType handle) {
|
||||||
|
if (AMS_LIKELY(IsDiskHandle(handle))) {
|
||||||
|
if (const auto id = GetHandleId(handle); AMS_LIKELY(id < MaxDisks)) {
|
||||||
|
const auto signature = GetHandleSignature(handle);
|
||||||
|
Disk *disk = std::addressof(impl::g_disk_set.disks[id]);
|
||||||
|
|
||||||
|
for (auto &holder : impl::g_disk_set.disk_holders) {
|
||||||
|
if (holder.disk == disk && holder.signature == signature) {
|
||||||
|
return std::addressof(holder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE Partition *GetPartition(HandleType handle) {
|
||||||
|
if (AMS_LIKELY(IsPartitionHandle(handle))) {
|
||||||
|
if (const auto id = GetHandleId(handle); AMS_LIKELY(id < MaxPartitions)) {
|
||||||
|
const auto signature = GetHandleSignature(handle);
|
||||||
|
Partition *part = std::addressof(impl::g_disk_set.partitions[id]);
|
||||||
|
|
||||||
|
for (const auto &holder : impl::g_disk_set.partition_holders) {
|
||||||
|
if (holder.partition == part && holder.signature == signature) {
|
||||||
|
return part;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE Partition *GetPartitionUnsafe(HandleType handle) {
|
||||||
|
return std::addressof(impl::g_disk_set.partitions[GetHandleId(handle)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE PartitionHolder *GetPartitionHolder(HandleType handle) {
|
||||||
|
if (AMS_LIKELY(IsPartitionHandle(handle))) {
|
||||||
|
if (const auto id = GetHandleId(handle); AMS_LIKELY(id < MaxPartitions)) {
|
||||||
|
const auto signature = GetHandleSignature(handle);
|
||||||
|
Partition *part = std::addressof(impl::g_disk_set.partitions[id]);
|
||||||
|
|
||||||
|
for (auto &holder : impl::g_disk_set.partition_holders) {
|
||||||
|
if (holder.partition == part && holder.signature == signature) {
|
||||||
|
return std::addressof(holder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||||
|
#include <mesosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||||
|
#include <exosphere.hpp>
|
||||||
|
#else
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#endif
|
||||||
|
#include "prfile2_pdm_disk_set.hpp"
|
||||||
|
|
||||||
|
namespace ams::prfile2::pdm::part {
|
||||||
|
|
||||||
|
pdm::Error OpenPartition(HandleType disk_handle, u16 partition_id, HandleType *out) {
|
||||||
|
/* Check the arguments. */
|
||||||
|
if (out == nullptr || disk_handle == InvalidHandle) {
|
||||||
|
return pdm::Error_InvalidParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find a free partition holder. */
|
||||||
|
PartitionHolder *holder = nullptr;
|
||||||
|
for (auto &h : impl::g_disk_set.partition_holders) {
|
||||||
|
if (h.partition == nullptr) {
|
||||||
|
holder = std::addressof(h);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (holder == nullptr) {
|
||||||
|
return pdm::Error_NotExistFreePartitionStruct;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Locate a free (or matching) partition. */
|
||||||
|
Partition *part = nullptr;
|
||||||
|
bool found_matching = false;
|
||||||
|
for (auto &p : impl::g_disk_set.partitions) {
|
||||||
|
if (p.IsOpen()) {
|
||||||
|
if (p.disk_handle == disk_handle && p.partition_id == partition_id) {
|
||||||
|
part = std::addressof(p);
|
||||||
|
found_matching = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (part == nullptr) {
|
||||||
|
part = std::addressof(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (part == nullptr) {
|
||||||
|
return pdm::Error_NotExistFreePartitionStruct;
|
||||||
|
}
|
||||||
|
const auto part_id = part - impl::g_disk_set.partitions;
|
||||||
|
|
||||||
|
/* If we're not working with a match, open the new partition. */
|
||||||
|
if (!found_matching) {
|
||||||
|
/* Set the partition as open. */
|
||||||
|
part->SetOpen(true);
|
||||||
|
|
||||||
|
/* Increment the number of open partitions. */
|
||||||
|
++impl::g_disk_set.num_partitions;
|
||||||
|
|
||||||
|
/* Set the partition's disk/id. */
|
||||||
|
part->disk_handle = disk_handle;
|
||||||
|
part->partition_id = partition_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Increment the partition's signature. */
|
||||||
|
part->signature = static_cast<u16>(part->signature + 1);
|
||||||
|
|
||||||
|
/* Increment the partition's open count. */
|
||||||
|
++part->open_count;
|
||||||
|
|
||||||
|
/* Set the holder. */
|
||||||
|
holder->signature = part->signature;
|
||||||
|
holder->partition = part;
|
||||||
|
|
||||||
|
/* Set the output handle. */
|
||||||
|
*out = ConstructPartitionHandle(part_id, part->signature);
|
||||||
|
|
||||||
|
return pdm::Error_Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdm::Error ClosePartition(HandleType part_handle) {
|
||||||
|
/* Get the partition. */
|
||||||
|
Partition *part = GetPartition(part_handle);
|
||||||
|
if (part == nullptr) {
|
||||||
|
return pdm::Error_InvalidParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that the partition is open and unlocked. */
|
||||||
|
if (!part->IsOpen()) {
|
||||||
|
return pdm::Error_StateClosed;
|
||||||
|
}
|
||||||
|
if (part->IsLocked()) {
|
||||||
|
return pdm::Error_StateLocked;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the partition holder. */
|
||||||
|
PartitionHolder *holder = GetPartitionHolder(part_handle);
|
||||||
|
if (holder == nullptr) {
|
||||||
|
return pdm::Error_InvalidParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close the partition. */
|
||||||
|
if (part->open_count == 1) {
|
||||||
|
/* Set the partition as closed. */
|
||||||
|
part->SetOpen(false);
|
||||||
|
--impl::g_disk_set.num_partitions;
|
||||||
|
|
||||||
|
/* Clear the partition holder. */
|
||||||
|
holder->partition = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decrement the partition's open count. */
|
||||||
|
--part->open_count;
|
||||||
|
|
||||||
|
return pdm::Error_Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetDriverErrorCode(HandleType part_handle, pdm::Error err) {
|
||||||
|
GetPartitionUnsafe(part_handle)->last_driver_error = err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckPartitionOpen(HandleType disk_handle, bool *out) {
|
||||||
|
/* TODO */
|
||||||
|
AMS_UNUSED(disk_handle, out);
|
||||||
|
AMS_ABORT("CheckPartitionOpen");
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotifyMediaEvent(HandleType disk_handle, u32 event) {
|
||||||
|
/* TODO */
|
||||||
|
AMS_UNUSED(disk_handle, event);
|
||||||
|
AMS_ABORT("NotifyMediaEvent");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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/>.
|
||||||
|
*/
|
||||||
|
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||||
|
#include <mesosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||||
|
#include <exosphere.hpp>
|
||||||
|
#else
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#endif
|
||||||
|
#include "prfile2_pdm_disk_set.hpp"
|
||||||
|
|
||||||
|
namespace ams::prfile2::pdm::disk {
|
||||||
|
|
||||||
|
pdm::Error CheckDataEraseRequest(HandleType disk_handle, bool *out) {
|
||||||
|
/* Check parameters. */
|
||||||
|
Disk *disk = GetDisk(disk_handle);
|
||||||
|
if (out == nullptr || disk == nullptr) {
|
||||||
|
return pdm::Error_InvalidParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for data erase function. */
|
||||||
|
*out = disk->erase_callback != nullptr;
|
||||||
|
return pdm::Error_Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdm::Error CheckMediaDetect(HandleType disk_handle, bool *out) {
|
||||||
|
/* Check parameters. */
|
||||||
|
Disk *disk = GetDisk(disk_handle);
|
||||||
|
if (out == nullptr || disk == nullptr) {
|
||||||
|
return pdm::Error_InvalidParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Default to no status change detected. */
|
||||||
|
*out = false;
|
||||||
|
|
||||||
|
/* Detect status change via disk nbc detect. */
|
||||||
|
volatile NonBlockingProtocolType nbc;
|
||||||
|
do {
|
||||||
|
do {
|
||||||
|
nbc = disk->nbc;
|
||||||
|
} while ((nbc & 1) != 0);
|
||||||
|
if (nbc != disk->nbc_detect) {
|
||||||
|
*out = true;
|
||||||
|
}
|
||||||
|
} while (nbc != disk->nbc);
|
||||||
|
|
||||||
|
return pdm::Error_Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pdm::Error CheckMediaInsert(HandleType disk_handle, bool *out) {
|
||||||
|
/* Check parameters. */
|
||||||
|
Disk *disk = GetDisk(disk_handle);
|
||||||
|
if (out == nullptr || disk == nullptr) {
|
||||||
|
return pdm::Error_InvalidParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get whether the disk is inserted. */
|
||||||
|
*out = disk->is_inserted;
|
||||||
|
return pdm::Error_Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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/>.
|
||||||
|
*/
|
||||||
|
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||||
|
#include <mesosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||||
|
#include <exosphere.hpp>
|
||||||
|
#else
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#endif
|
||||||
|
#include "prfile2_pdm_disk_set.hpp"
|
||||||
|
|
||||||
|
namespace ams::prfile2::pdm::part {
|
||||||
|
|
||||||
|
pdm::Error CheckDataEraseRequest(HandleType part_handle, bool *out) {
|
||||||
|
/* Check parameters. */
|
||||||
|
Partition *part = GetPartition(part_handle);
|
||||||
|
if (out == nullptr || part == nullptr) {
|
||||||
|
return pdm::Error_InvalidParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check the disk. */
|
||||||
|
return pdm::disk::CheckDataEraseRequest(part->disk_handle, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
pdm::Error CheckMediaDetect(HandleType part_handle, bool *out) {
|
||||||
|
/* Check parameters. */
|
||||||
|
Partition *part = GetPartition(part_handle);
|
||||||
|
if (out == nullptr || part == nullptr) {
|
||||||
|
return pdm::Error_InvalidParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the disk (unsafe/not checked). */
|
||||||
|
Disk *disk = GetDiskUnsafe(part->disk_handle);
|
||||||
|
|
||||||
|
/* Default to no status change detected. */
|
||||||
|
*out = false;
|
||||||
|
|
||||||
|
/* Detect status change via partition nbc detect. */
|
||||||
|
volatile NonBlockingProtocolType nbc;
|
||||||
|
do {
|
||||||
|
do {
|
||||||
|
nbc = disk->nbc;
|
||||||
|
} while ((nbc & 1) != 0);
|
||||||
|
if (nbc != part->nbc_detect) {
|
||||||
|
*out = true;
|
||||||
|
}
|
||||||
|
} while (nbc != disk->nbc);
|
||||||
|
|
||||||
|
return pdm::Error_Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdm::Error CheckMediaInsert(HandleType part_handle, bool *out) {
|
||||||
|
/* Check parameters. */
|
||||||
|
Partition *part = GetPartition(part_handle);
|
||||||
|
if (out == nullptr || part == nullptr) {
|
||||||
|
return pdm::Error_InvalidParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if the disk is inserted. */
|
||||||
|
return pdm::disk::CheckMediaInsert(part->disk_handle, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
62
libraries/libvapours/source/prfile2/pf/prfile2_pf_api.cpp
Normal file
62
libraries/libvapours/source/prfile2/pf/prfile2_pf_api.cpp
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||||
|
#include <mesosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||||
|
#include <exosphere.hpp>
|
||||||
|
#else
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#endif
|
||||||
|
#include "prfile2_pf_errnum.hpp"
|
||||||
|
|
||||||
|
namespace ams::prfile2::pf {
|
||||||
|
|
||||||
|
|
||||||
|
int Initialize(u32 config, void *param) {
|
||||||
|
/* Initialize the fatfs api. */
|
||||||
|
if (auto err = fatfs::Initialize(config, param)) {
|
||||||
|
return ConvertReturnValue(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize the system api. */
|
||||||
|
system::Initialize();
|
||||||
|
return ConvertReturnValue(pf::Error_Ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Attach(DriveTable **drive_tables) {
|
||||||
|
/* Check parameters. */
|
||||||
|
if (drive_tables == nullptr || *drive_tables == nullptr) {
|
||||||
|
return ConvertReturnValue(SetInternalErrorAndReturn(pf::Error_InvalidParameter));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Attach each volume in the list. */
|
||||||
|
for (auto *table = *drive_tables; table != nullptr; table = *(++drive_tables)) {
|
||||||
|
if (auto err = vol::Attach(table); err != pf::Error_Ok) {
|
||||||
|
/* Clear each unattached drive character. */
|
||||||
|
for (table = *drive_tables; table != nullptr; table = *(++drive_tables)) {
|
||||||
|
table->drive_char = 0;
|
||||||
|
}
|
||||||
|
return ConvertReturnValue(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ConvertReturnValue(pf::Error_Ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2020 Atmosphère-NX
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
* under the terms and conditions of the GNU General Public License,
|
* under the terms and conditions of the GNU General Public License,
|
||||||
@@ -13,14 +13,16 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
|
||||||
#include "defines.hpp"
|
namespace ams::prfile2::pf {
|
||||||
|
|
||||||
namespace ams::hvisor {
|
void SetInternalError(pf::Error err);
|
||||||
|
|
||||||
// Caller needs to disable interrupts
|
ALWAYS_INLINE pf::Error SetInternalErrorAndReturn(pf::Error err) {
|
||||||
size_t SafeIoCopy(void *dst, const void *src, size_t size);
|
SetInternalError(err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
49
libraries/libvapours/source/prfile2/prfile2_cache.cpp
Normal file
49
libraries/libvapours/source/prfile2/prfile2_cache.cpp
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||||
|
#include <mesosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||||
|
#include <exosphere.hpp>
|
||||||
|
#else
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ams::prfile2::cache {
|
||||||
|
|
||||||
|
void SetCache(Volume *vol, pf::CachePage *cache_page, pf::SectorBuffer *cache_buf, u16 num_fat_pages, u16 num_data_pages) {
|
||||||
|
/* Set the cache fields. */
|
||||||
|
vol->cache.pages = cache_page;
|
||||||
|
vol->cache.buffers = cache_buf;
|
||||||
|
vol->cache.num_fat_pages = num_fat_pages;
|
||||||
|
vol->cache.num_data_pages = num_data_pages;
|
||||||
|
std::memset(vol->cache.pages, 0, sizeof(*vol->cache.pages) * (num_fat_pages + num_data_pages));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetFatBufferSize(Volume *vol, u32 size) {
|
||||||
|
if (size > 0) {
|
||||||
|
vol->cache.fat_buf_size = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetDataBufferSize(Volume *vol, u32 size) {
|
||||||
|
if (size > 0) {
|
||||||
|
vol->cache.data_buf_size = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
141
libraries/libvapours/source/prfile2/prfile2_critical_section.cpp
Normal file
141
libraries/libvapours/source/prfile2/prfile2_critical_section.cpp
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||||
|
#include <mesosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||||
|
#include <exosphere.hpp>
|
||||||
|
#else
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ams::prfile2 {
|
||||||
|
|
||||||
|
#if defined(AMS_PRFILE2_THREAD_SAFE)
|
||||||
|
|
||||||
|
struct CriticalSection::Resource {
|
||||||
|
os::MutexType mutex;
|
||||||
|
bool in_use;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr inline const auto NumCriticalSectionResources = MaxVolumes + 1;
|
||||||
|
|
||||||
|
constinit os::SdkMutex g_crit_resource_mutex;
|
||||||
|
constinit CriticalSection::Resource g_crit_resources[NumCriticalSectionResources];
|
||||||
|
|
||||||
|
CriticalSection::Resource *AllocateCriticalSectionResource() {
|
||||||
|
std::scoped_lock lk(g_crit_resource_mutex);
|
||||||
|
|
||||||
|
for (auto &resource : g_crit_resources) {
|
||||||
|
if (!resource.in_use) {
|
||||||
|
resource.in_use = true;
|
||||||
|
return std::addressof(resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AMS_ABORT("Failed to allocate critical section resource");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AcquireCriticalSectionResource(CriticalSection::Resource *resource) {
|
||||||
|
return os::LockMutex(std::addressof(resource->mutex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReleaseCriticalSectionResource(CriticalSection::Resource *resource) {
|
||||||
|
return os::UnlockMutex(std::addressof(resource->mutex));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeCriticalSection(CriticalSection *cs) {
|
||||||
|
/* Check pre-condition. */
|
||||||
|
AMS_ASSERT(cs->state == CriticalSection::State_NotInitialized);
|
||||||
|
|
||||||
|
/* Perform initialization. */
|
||||||
|
if (cs->state == CriticalSection::State_NotInitialized) {
|
||||||
|
cs->resource = AllocateCriticalSectionResource();
|
||||||
|
cs->owner = 0;
|
||||||
|
cs->state = CriticalSection::State_Initialized;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FinalizeCriticalSection(CriticalSection *cs) {
|
||||||
|
/* Check pre-condition. */
|
||||||
|
AMS_ASSERT(cs->state == CriticalSection::State_Initialized);
|
||||||
|
|
||||||
|
/* TODO */
|
||||||
|
AMS_UNUSED(cs);
|
||||||
|
AMS_ABORT("prfile2::FinalizeCriticalSection");
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnterCriticalSection(CriticalSection *cs) {
|
||||||
|
/* Check pre-condition. */
|
||||||
|
AMS_ASSERT(cs->state == CriticalSection::State_Initialized);
|
||||||
|
|
||||||
|
if (AMS_LIKELY(cs->lock_count == 0)) {
|
||||||
|
/* Acquire the lock .*/
|
||||||
|
AcquireCriticalSectionResource(cs->resource);
|
||||||
|
system::GetCurrentContextId(std::addressof(cs->owner));
|
||||||
|
} else {
|
||||||
|
/* Get the current context id. */
|
||||||
|
u64 current_context_id;
|
||||||
|
system::GetCurrentContextId(std::addressof(current_context_id));
|
||||||
|
|
||||||
|
/* If the lock isn't already held, acquire it. */
|
||||||
|
if (cs->owner != current_context_id) {
|
||||||
|
AcquireCriticalSectionResource(cs->resource);
|
||||||
|
cs->owner = current_context_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Increment the lock count. */
|
||||||
|
++cs->lock_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExitCriticalSection(CriticalSection *cs) {
|
||||||
|
/* Check pre-condition. */
|
||||||
|
AMS_ASSERT(cs->state == CriticalSection::State_Initialized);
|
||||||
|
|
||||||
|
/* Unlock. */
|
||||||
|
if ((--cs->lock_count) == 0) {
|
||||||
|
cs->owner = 0;
|
||||||
|
ReleaseCriticalSectionResource(cs->resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
void InitializeCriticalSection(CriticalSection *cs) {
|
||||||
|
AMS_UNUSED(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FinalizeCriticalSection(CriticalSection *cs) {
|
||||||
|
AMS_UNUSED(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnterCriticalSection(CriticalSection *cs) {
|
||||||
|
AMS_UNUSED(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExitCriticalSection(CriticalSection *cs) {
|
||||||
|
AMS_UNUSED(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
61
libraries/libvapours/source/prfile2/prfile2_drv.cpp
Normal file
61
libraries/libvapours/source/prfile2/prfile2_drv.cpp
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||||
|
#include <mesosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||||
|
#include <exosphere.hpp>
|
||||||
|
#else
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ams::prfile2::drv {
|
||||||
|
|
||||||
|
pf::Error Initialize(Volume *volume) {
|
||||||
|
/* Check the volume. */
|
||||||
|
if (volume == nullptr) {
|
||||||
|
return pf::Error_InvalidParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check the data erase request. */
|
||||||
|
bool data_erase;
|
||||||
|
if (auto pdm_err = pdm::part::CheckDataEraseRequest(volume->partition_handle, std::addressof(data_erase)); pdm_err != pdm::Error_Ok) {
|
||||||
|
return pf::Error_Generic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the data erase request flag. */
|
||||||
|
volume->SetDataEraseRequested(data_erase);
|
||||||
|
return pf::Error_Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsDetected(Volume *volume) {
|
||||||
|
/* Check detect status changed. */
|
||||||
|
/* NOTE: Error is not checked here. */
|
||||||
|
bool detected = false;
|
||||||
|
pdm::part::CheckMediaDetect(volume->partition_handle, std::addressof(detected));
|
||||||
|
return detected;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsInserted(Volume *volume) {
|
||||||
|
/* Check inserted. */
|
||||||
|
/* NOTE: Error is not checked here. */
|
||||||
|
bool inserted = false;
|
||||||
|
pdm::part::CheckMediaInsert(volume->partition_handle, std::addressof(inserted));
|
||||||
|
return inserted;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -13,16 +13,20 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||||
#include <vapours/common.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include <vapours/assert.hpp>
|
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||||
#include <vapours/util/util_fixed_tree.hpp>
|
#include <mesosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||||
|
#include <exosphere.hpp>
|
||||||
|
#else
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace ams::util {
|
namespace ams::prfile2::fatfs {
|
||||||
|
|
||||||
template<typename Element, typename Compare = std::less<Element>, size_t BufferAlignment = 8>
|
pf::Error Initialize(u32 config, void *param) {
|
||||||
class FixedSet : public ::ams::util::FixedTree<Element, Compare, const Element, BufferAlignment> {
|
return vol::Initialize(config, param);
|
||||||
/* ... */
|
}
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
110
libraries/libvapours/source/prfile2/prfile2_str.cpp
Normal file
110
libraries/libvapours/source/prfile2/prfile2_str.cpp
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||||
|
#include <mesosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||||
|
#include <exosphere.hpp>
|
||||||
|
#else
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ams::prfile2::str {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/* TODO: Where does this come from? */
|
||||||
|
/* It's maximum path length * 2, but where should the definition live? */
|
||||||
|
constexpr inline size_t StringLengthMax = 520;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pf::Error Initialize(String *str, const char *s, CodeMode code_mode) {
|
||||||
|
/* Check parameters. */
|
||||||
|
if (str == nullptr || s == nullptr) {
|
||||||
|
return pf::Error_InvalidParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize the string. */
|
||||||
|
switch (code_mode) {
|
||||||
|
case CodeMode_Local:
|
||||||
|
{
|
||||||
|
str->head = s;
|
||||||
|
str->tail = s + sizeof(char) * strnlen(s, StringLengthMax);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CodeMode_Unicode:
|
||||||
|
{
|
||||||
|
str->head = s;
|
||||||
|
str->tail = s + sizeof(WideChar) * w_strnlen(reinterpret_cast<const WideChar *>(s), StringLengthMax);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return pf::Error_InvalidParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the code mode. */
|
||||||
|
str->code_mode = code_mode;
|
||||||
|
|
||||||
|
return pf::Error_Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetCodeMode(String *str, CodeMode code_mode) {
|
||||||
|
str->code_mode = code_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeMode GetCodeMode(const String *str) {
|
||||||
|
return str->code_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *GetPos(String *str, TargetString target) {
|
||||||
|
if (target == TargetString_Head) {
|
||||||
|
return const_cast<char *>(str->head);
|
||||||
|
} else {
|
||||||
|
return const_cast<char *>(str->tail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MovePos(String *str, s16 num_char) {
|
||||||
|
AMS_UNUSED(str, num_char);
|
||||||
|
AMS_ABORT("TODO: oem charset");
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 GetLength(String *str) {
|
||||||
|
if (str->code_mode == CodeMode_Unicode) {
|
||||||
|
return (str->tail - str->head) / sizeof(WideChar);
|
||||||
|
} else {
|
||||||
|
return (str->tail - str->head) / sizeof(char);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 GetNumChar(String *str, TargetString target) {
|
||||||
|
AMS_UNUSED(str, target);
|
||||||
|
AMS_ABORT("TODO: oem charset");
|
||||||
|
}
|
||||||
|
|
||||||
|
int Compare(const String *str, const char *rhs) {
|
||||||
|
AMS_UNUSED(str, rhs);
|
||||||
|
AMS_ABORT("TODO: oem charset");
|
||||||
|
}
|
||||||
|
|
||||||
|
int Compare(const String *str, const WideChar *rhs) {
|
||||||
|
AMS_UNUSED(str, rhs);
|
||||||
|
AMS_ABORT("TODO: oem charset");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -13,33 +13,36 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||||
|
#include <mesosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||||
|
#include <exosphere.hpp>
|
||||||
|
#else
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace ams::usb::impl {
|
namespace ams::prfile2::system {
|
||||||
|
|
||||||
constexpr int GetEndpointIndex(u8 address) {
|
void Initialize() {
|
||||||
int idx = address & UsbEndpointAddressMask_EndpointNumber;
|
/* ... */
|
||||||
if ((address & UsbEndpointAddressMask_Dir) == UsbEndpointAddressMask_DirDevicetoHost) {
|
|
||||||
idx += 0x10;
|
|
||||||
}
|
|
||||||
return idx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
int GetCurrentContextId(u64 *out) {
|
||||||
class ScopedRefCount {
|
/* Check that out isn't null. */
|
||||||
NON_COPYABLE(ScopedRefCount);
|
if (out == nullptr) {
|
||||||
NON_MOVEABLE(ScopedRefCount);
|
return -2;
|
||||||
private:
|
|
||||||
T &m_obj;
|
|
||||||
public:
|
|
||||||
ALWAYS_INLINE ScopedRefCount(T &o) : m_obj(o) {
|
|
||||||
++m_obj;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE ~ScopedRefCount() {
|
/* Set the output. */
|
||||||
--m_obj;
|
#if defined(AMS_PRFILE2_THREAD_SAFE)
|
||||||
|
*out = reinterpret_cast<u64>(os::GetCurrentThread());
|
||||||
|
#else
|
||||||
|
*out = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
433
libraries/libvapours/source/prfile2/prfile2_volume.cpp
Normal file
433
libraries/libvapours/source/prfile2/prfile2_volume.cpp
Normal file
@@ -0,0 +1,433 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||||
|
#include <mesosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||||
|
#include <exosphere.hpp>
|
||||||
|
#else
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#endif
|
||||||
|
#include "prfile2_volume_set.hpp"
|
||||||
|
|
||||||
|
namespace ams::prfile2::vol {
|
||||||
|
|
||||||
|
/* Global volume context object. */
|
||||||
|
constinit VolumeContext g_vol_set;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr inline u32 CharacterCheckDisable = 0x10000;
|
||||||
|
constexpr inline u32 CharacterCheckEnable = 0x20000;
|
||||||
|
|
||||||
|
constexpr inline u32 CharacterCheckMask = CharacterCheckDisable | CharacterCheckEnable;
|
||||||
|
|
||||||
|
constexpr inline u32 VolumeSetConfigMask = 0x5FFFFFFF;
|
||||||
|
|
||||||
|
VolumeContext *GetVolumeContextById(u64 context_id) {
|
||||||
|
/* Get the volume set. */
|
||||||
|
auto &vol_set = GetVolumeSet();
|
||||||
|
|
||||||
|
/* Acquire exclusive access to the volume set. */
|
||||||
|
ScopedCriticalSection lk(vol_set.critical_section);
|
||||||
|
|
||||||
|
/* Find a matching context. */
|
||||||
|
for (auto *ctx = vol_set.used_context_head; ctx != nullptr; ctx = ctx->next_used_context) {
|
||||||
|
if (ctx->context_id == context_id) {
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
VolumeContext *GetCurrentVolumeContext(u64 *out_context_id) {
|
||||||
|
/* Get the volume set. */
|
||||||
|
auto &vol_set = GetVolumeSet();
|
||||||
|
|
||||||
|
/* Acquire exclusive access to the volume set. */
|
||||||
|
ScopedCriticalSection lk(vol_set.critical_section);
|
||||||
|
|
||||||
|
/* Get the current context id. */
|
||||||
|
u64 context_id = 0;
|
||||||
|
system::GetCurrentContextId(std::addressof(context_id));
|
||||||
|
if (out_context_id != nullptr) {
|
||||||
|
*out_context_id = context_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto *ctx = GetVolumeContextById(context_id); ctx != nullptr) {
|
||||||
|
return ctx;
|
||||||
|
} else {
|
||||||
|
return std::addressof(vol_set.default_context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsValidDriveCharacter(pf::DriveCharacter drive_char) {
|
||||||
|
return static_cast<u8>((drive_char & 0xDF) - 'A') < 26;
|
||||||
|
}
|
||||||
|
|
||||||
|
Volume *GetVolumeByDriveCharacter(pf::DriveCharacter drive_char) {
|
||||||
|
/* Get the volume set. */
|
||||||
|
auto &vol_set = GetVolumeSet();
|
||||||
|
|
||||||
|
/* Calculate the volume index. */
|
||||||
|
const auto index = std::toupper(static_cast<unsigned char>(drive_char)) - 'A';
|
||||||
|
|
||||||
|
/* Acquire exclusive access to the volume set. */
|
||||||
|
ScopedCriticalSection lk(vol_set.critical_section);
|
||||||
|
|
||||||
|
if (index < MaxVolumes) {
|
||||||
|
return std::addressof(vol_set.volumes[index]);
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pf::Error Initialize(u32 config, void *param) {
|
||||||
|
/* Check the input config. */
|
||||||
|
if ((config & ~CharacterCheckMask) != 0) {
|
||||||
|
return pf::Error_InvalidParameter;
|
||||||
|
}
|
||||||
|
if ((config & CharacterCheckMask) == CharacterCheckMask) {
|
||||||
|
return pf::Error_InvalidParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the volume set. */
|
||||||
|
auto &vol_set = GetVolumeSet();
|
||||||
|
|
||||||
|
/* Clear the default volume context. */
|
||||||
|
std::memset(std::addressof(vol_set.default_context), 0, sizeof(VolumeContext));
|
||||||
|
vol_set.default_context.volume_id = 0;
|
||||||
|
|
||||||
|
/* Setup the context lists. */
|
||||||
|
vol_set.used_context_head = nullptr;
|
||||||
|
vol_set.used_context_tail = nullptr;
|
||||||
|
vol_set.free_context_head = vol_set.contexts;
|
||||||
|
for (auto i = 0; i < MaxVolumes - 1; ++i) {
|
||||||
|
vol_set.contexts[i].next_free_context = std::addressof(vol_set.contexts[i + 1]);
|
||||||
|
}
|
||||||
|
vol_set.contexts[MaxVolumes - 1].next_free_context = nullptr;
|
||||||
|
|
||||||
|
/* Set the setting. */
|
||||||
|
vol_set.setting = 1;
|
||||||
|
|
||||||
|
/* Set the config. */
|
||||||
|
if ((config & CharacterCheckEnable) != 0) {
|
||||||
|
vol_set.config |= CharacterCheckDisable;
|
||||||
|
} else {
|
||||||
|
vol_set.config &= ~CharacterCheckDisable;
|
||||||
|
}
|
||||||
|
vol_set.config &= VolumeSetConfigMask;
|
||||||
|
|
||||||
|
/* Clear number of attached drives/volumes. */
|
||||||
|
vol_set.num_attached_drives = 0;
|
||||||
|
vol_set.num_mounted_volumes = 0;
|
||||||
|
|
||||||
|
/* Set the parameter. */
|
||||||
|
vol_set.param = param;
|
||||||
|
|
||||||
|
/* Set the codeset. */
|
||||||
|
/* TODO */
|
||||||
|
|
||||||
|
/* Clear the volumes. */
|
||||||
|
for (auto &volume : vol_set.volumes) {
|
||||||
|
std::memset(std::addressof(volume), 0, sizeof(volume));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize the volume set critical section. */
|
||||||
|
InitializeCriticalSection(std::addressof(vol_set.critical_section));
|
||||||
|
|
||||||
|
/* NOTE: Here "InitLockFile()" is called, but this doesn't seem used so far. TODO: Add if used? */
|
||||||
|
|
||||||
|
/* Mark initialized. */
|
||||||
|
vol_set.initialized = true;
|
||||||
|
|
||||||
|
return pf::Error_Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
pf::Error Attach(pf::DriveTable *drive_table) {
|
||||||
|
/* Get the volume set. */
|
||||||
|
auto &vol_set = GetVolumeSet();
|
||||||
|
|
||||||
|
/* Get the volume context for error tracking. */
|
||||||
|
u64 context_id = 0;
|
||||||
|
auto *vol_ctx = GetCurrentVolumeContext(std::addressof(context_id));
|
||||||
|
|
||||||
|
auto SetLastErrorAndReturn = [&] ALWAYS_INLINE_LAMBDA (pf::Error err) -> pf::Error { vol_ctx->last_error = err; return err; };
|
||||||
|
|
||||||
|
/* Check the drive table. */
|
||||||
|
if (drive_table == nullptr) {
|
||||||
|
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear the drive table's character/status. */
|
||||||
|
const auto drive_char = drive_table->drive_char;
|
||||||
|
drive_table->drive_char = 0;
|
||||||
|
drive_table->status = 0;
|
||||||
|
|
||||||
|
/* Check that we can attach. */
|
||||||
|
if (vol_set.num_attached_drives >= MaxVolumes) {
|
||||||
|
return SetLastErrorAndReturn(pf::Error_TooManyVolumesAttached);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check the cache setting. */
|
||||||
|
auto *cache_setting = drive_table->cache;
|
||||||
|
if (cache_setting == nullptr) {
|
||||||
|
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
|
||||||
|
}
|
||||||
|
if (cache_setting->fat_buf_size > MaximumFatBufferSize) {
|
||||||
|
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
|
||||||
|
}
|
||||||
|
if (cache_setting->data_buf_size > MaximumDataBufferSize) {
|
||||||
|
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
|
||||||
|
}
|
||||||
|
if (cache_setting->num_fat_pages < MinimumFatPages) {
|
||||||
|
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
|
||||||
|
}
|
||||||
|
if (cache_setting->num_data_pages < MinimumDataPages) {
|
||||||
|
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
|
||||||
|
}
|
||||||
|
if (cache_setting->pages == nullptr) {
|
||||||
|
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
|
||||||
|
}
|
||||||
|
if (cache_setting->buffers == nullptr) {
|
||||||
|
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
|
||||||
|
}
|
||||||
|
if (!util::IsAligned(reinterpret_cast<uintptr_t>(cache_setting->pages), alignof(u32))) {
|
||||||
|
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
|
||||||
|
}
|
||||||
|
if (!util::IsAligned(reinterpret_cast<uintptr_t>(cache_setting->buffers), alignof(u32))) {
|
||||||
|
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adjust the cache setting. */
|
||||||
|
cache_setting->fat_buf_size = std::max<u32>(cache_setting->fat_buf_size, MinimumFatBufferSize);
|
||||||
|
cache_setting->data_buf_size = std::max<u32>(cache_setting->data_buf_size, MinimumDataBufferSize);
|
||||||
|
|
||||||
|
/* Check the adjusted setting. */
|
||||||
|
if (cache_setting->fat_buf_size > cache_setting->num_fat_pages) {
|
||||||
|
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
|
||||||
|
}
|
||||||
|
if ((cache_setting->num_data_pages / cache_setting->data_buf_size) < MinimumDataPages) {
|
||||||
|
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate the drive character. */
|
||||||
|
if (drive_char != 0) {
|
||||||
|
if (!IsValidDriveCharacter(drive_char)) {
|
||||||
|
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto *vol = GetVolumeByDriveCharacter(drive_char); vol == nullptr || vol->IsAttached()) {
|
||||||
|
return SetLastErrorAndReturn(pf::Error_InvalidVolumeLabel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Perform the bulk of the attach. */
|
||||||
|
Volume *vol = nullptr;
|
||||||
|
{
|
||||||
|
/* Lock the volume set. */
|
||||||
|
ScopedCriticalSection lk(vol_set.critical_section);
|
||||||
|
|
||||||
|
/* Find a free volume. */
|
||||||
|
for (auto &v : vol_set.volumes) {
|
||||||
|
if (!v.IsAttached()) {
|
||||||
|
vol = std::addressof(v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (vol == nullptr) {
|
||||||
|
return SetLastErrorAndReturn(pf::Error_TooManyVolumesAttached);
|
||||||
|
}
|
||||||
|
const auto vol_id = vol - vol_set.volumes;
|
||||||
|
|
||||||
|
/* Clear the volume. */
|
||||||
|
std::memset(vol, 0, sizeof(*vol));
|
||||||
|
|
||||||
|
/* Initialize the volume. */
|
||||||
|
vol->num_free_clusters = InvalidCluster;
|
||||||
|
vol->num_free_clusters_ = InvalidCluster;
|
||||||
|
vol->last_free_cluster = InvalidCluster;
|
||||||
|
vol->partition_handle = drive_table->partition_handle;
|
||||||
|
InitializeCriticalSection(std::addressof(vol->critical_section));
|
||||||
|
vol->drive_char = 'A' + vol_id;
|
||||||
|
|
||||||
|
/* Setup directory tail. */
|
||||||
|
vol->tail_entry.tracker_size = util::size(vol->tail_entry.tracker_buf);
|
||||||
|
vol->tail_entry.tracker_bits = vol->tail_entry.tracker_buf;
|
||||||
|
|
||||||
|
/* NOTE: Cluster link is cleared here, but we already memset vol to zero, so it's unnecessary. */
|
||||||
|
|
||||||
|
/* Initialize driver for volume. */
|
||||||
|
if (auto err = drv::Initialize(vol); err != pf::Error_Ok) {
|
||||||
|
return SetLastErrorAndReturn(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup the cache. */
|
||||||
|
cache::SetCache(vol, drive_table->cache->pages, drive_table->cache->buffers, drive_table->cache->num_fat_pages, drive_table->cache->num_data_pages);
|
||||||
|
cache::SetFatBufferSize(vol, drive_table->cache->fat_buf_size);
|
||||||
|
cache::SetDataBufferSize(vol, drive_table->cache->data_buf_size);
|
||||||
|
|
||||||
|
/* Set flags. */
|
||||||
|
vol->SetAttached(true);
|
||||||
|
vol->SetFlag12(true);
|
||||||
|
|
||||||
|
/* Update the drive table. */
|
||||||
|
drive_table->SetAttached(true);
|
||||||
|
drive_table->drive_char = vol->drive_char;
|
||||||
|
|
||||||
|
/* Update the number of attached drives. */
|
||||||
|
if ((vol_set.num_attached_drives++) == 0) {
|
||||||
|
vol_set.default_context.volume_id = vol_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Acquire exclusive access to the volume set. */
|
||||||
|
ScopedCriticalSection lk(vol_set.critical_section);
|
||||||
|
|
||||||
|
/* Associate the volume to our context while we operate on it. */
|
||||||
|
vol->context = vol_ctx;
|
||||||
|
vol->context_id = context_id;
|
||||||
|
ON_SCOPE_EXIT { vol->context_id = 0; };
|
||||||
|
|
||||||
|
/* TODO: Copy volume root dir entry to all contexts. */
|
||||||
|
|
||||||
|
/* TODO: Clear tracking fields at the end of the volume. */
|
||||||
|
|
||||||
|
/* Perform mount as appropriate. */
|
||||||
|
const auto check_mount_err = /* TODO vol::CheckMediaInsertForAttachMount(vol) */ pf::Error_Ok;
|
||||||
|
const bool inserted = drv::IsInserted(vol);
|
||||||
|
if (check_mount_err != pf::Error_Ok) {
|
||||||
|
if (inserted) {
|
||||||
|
drive_table->SetDiskInserted(true);
|
||||||
|
}
|
||||||
|
vol_ctx->last_error = check_mount_err;
|
||||||
|
} else if (inserted) {
|
||||||
|
drive_table->SetDiskInserted(true);
|
||||||
|
if (auto mount_err = /* TODO vol::MountImpl(vol, 0x1B, false) */pf::Error_InternalError; mount_err != pf::Error_Ok) {
|
||||||
|
vol_ctx->last_error = mount_err;
|
||||||
|
} else {
|
||||||
|
drive_table->SetMounted(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pf::Error_Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
VolumeContext *RegisterContext(u64 *out_context_id) {
|
||||||
|
/* Get the current context id. */
|
||||||
|
u64 context_id = 0;
|
||||||
|
system::GetCurrentContextId(std::addressof(context_id));
|
||||||
|
if (out_context_id != nullptr) {
|
||||||
|
*out_context_id = context_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the volume set. */
|
||||||
|
auto &vol_set = GetVolumeSet();
|
||||||
|
|
||||||
|
/* Acquire exclusive access to the volume set. */
|
||||||
|
ScopedCriticalSection lk(vol_set.critical_section);
|
||||||
|
|
||||||
|
/* Get the volume context by ID. If we already have a context, return it. */
|
||||||
|
if (VolumeContext *match = GetVolumeContextById(context_id); match != nullptr) {
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to find a free context in the list. */
|
||||||
|
VolumeContext *ctx = vol_set.free_context_head;
|
||||||
|
if (ctx == nullptr) {
|
||||||
|
vol_set.default_context.last_error = pf::Error_InternalError;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update the free lists. */
|
||||||
|
vol_set.free_context_head = ctx->next_free_context;
|
||||||
|
if (VolumeContext *next = vol_set.used_context_head; next != nullptr) {
|
||||||
|
next->prev_used_context = ctx;
|
||||||
|
ctx->next_used_context = next;
|
||||||
|
ctx->prev_used_context = nullptr;
|
||||||
|
vol_set.used_context_head = ctx;
|
||||||
|
} else {
|
||||||
|
ctx->next_used_context = nullptr;
|
||||||
|
ctx->prev_used_context = nullptr;
|
||||||
|
vol_set.used_context_head = ctx;
|
||||||
|
vol_set.used_context_tail = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the context's fields. */
|
||||||
|
ctx->context_id = context_id;
|
||||||
|
ctx->last_error = pf::Error_Ok;
|
||||||
|
for (auto i = 0; i < MaxVolumes; ++i) {
|
||||||
|
ctx->last_driver_error[i] = pf::Error_Ok;
|
||||||
|
ctx->last_unk_error[i] = pf::Error_Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy from the default context. */
|
||||||
|
const auto volume_id = vol_set.default_context.volume_id;
|
||||||
|
ctx->volume_id = volume_id;
|
||||||
|
ctx->dir_entries[volume_id] = vol_set.default_context.dir_entries[volume_id];
|
||||||
|
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
pf::Error UnregisterContext() {
|
||||||
|
/* Get the current context id. */
|
||||||
|
u64 context_id = 0;
|
||||||
|
system::GetCurrentContextId(std::addressof(context_id));
|
||||||
|
|
||||||
|
/* Get the volume set. */
|
||||||
|
auto &vol_set = GetVolumeSet();
|
||||||
|
|
||||||
|
/* Acquire exclusive access to the volume set. */
|
||||||
|
ScopedCriticalSection lk(vol_set.critical_section);
|
||||||
|
|
||||||
|
/* Get the volume context by ID. */
|
||||||
|
VolumeContext *ctx = GetVolumeContextById(context_id);
|
||||||
|
if (ctx == nullptr) {
|
||||||
|
vol_set.default_context.last_error = pf::Error_InternalError;
|
||||||
|
return pf::Error_InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update the lists. */
|
||||||
|
auto *prev_used = ctx->prev_used_context;
|
||||||
|
auto *next_used = ctx->next_used_context;
|
||||||
|
if (prev_used != nullptr) {
|
||||||
|
if (next_used != nullptr) {
|
||||||
|
prev_used->next_used_context = next_used;
|
||||||
|
next_used->prev_used_context = prev_used;
|
||||||
|
} else {
|
||||||
|
prev_used->next_used_context = nullptr;
|
||||||
|
vol_set.used_context_tail = prev_used;
|
||||||
|
}
|
||||||
|
} else if (next_used != nullptr) {
|
||||||
|
next_used->prev_used_context = nullptr;
|
||||||
|
vol_set.used_context_head = next_used;
|
||||||
|
} else {
|
||||||
|
vol_set.used_context_head = nullptr;
|
||||||
|
vol_set.used_context_tail = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->next_used_context = nullptr;
|
||||||
|
ctx->next_free_context = vol_set.free_context_head;
|
||||||
|
vol_set.free_context_head = ctx;
|
||||||
|
|
||||||
|
return pf::Error_Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2020 Atmosphère-NX
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
* under the terms and conditions of the GNU General Public License,
|
* under the terms and conditions of the GNU General Public License,
|
||||||
@@ -13,13 +13,19 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
|
||||||
#include "../hvisor_exception_stack_frame.hpp"
|
namespace ams::prfile2 {
|
||||||
|
|
||||||
namespace ams::hvisor::traps {
|
namespace impl {
|
||||||
|
|
||||||
void HandleHvc(ExceptionStackFrame *frame, cpu::ExceptionSyndromeRegister esr);
|
extern VolumeSet g_vol_set;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE VolumeSet &GetVolumeSet() {
|
||||||
|
return impl::g_vol_set;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
109
libraries/libvapours/source/prfile2/prfile2_wide_string.cpp
Normal file
109
libraries/libvapours/source/prfile2/prfile2_wide_string.cpp
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||||
|
#include <mesosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||||
|
#include <exosphere.hpp>
|
||||||
|
#else
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ams::prfile2 {
|
||||||
|
|
||||||
|
size_t w_strlen(const WideChar *s) {
|
||||||
|
const WideChar *cur;
|
||||||
|
for (cur = s; *cur != 0; ++cur) {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
return cur - s;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t w_strnlen(const WideChar *s, size_t length) {
|
||||||
|
const WideChar *cur;
|
||||||
|
for (cur = s; *cur != 0 && length != 0; ++cur, --length) {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
return cur - s;
|
||||||
|
}
|
||||||
|
|
||||||
|
WideChar *w_strcpy(WideChar *dst, const WideChar *src) {
|
||||||
|
WideChar * const ret = dst;
|
||||||
|
while (true) {
|
||||||
|
const auto c = *(src++);
|
||||||
|
*(dst++) = c;
|
||||||
|
|
||||||
|
if (c == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
WideChar *w_strncpy(WideChar *dst, const WideChar *src, size_t length) {
|
||||||
|
WideChar * const ret = dst;
|
||||||
|
while (length > 1) {
|
||||||
|
const auto c = *(src++);
|
||||||
|
*(dst++) = c;
|
||||||
|
|
||||||
|
if (c == 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length == 1) {
|
||||||
|
*(dst++) = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int w_strcmp(const WideChar *lhs, const WideChar *rhs) {
|
||||||
|
WideChar l, r;
|
||||||
|
while (true) {
|
||||||
|
l = *(lhs++);
|
||||||
|
r = *(rhs++);
|
||||||
|
if (l == 0 || r == 0 || l != r) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return l - r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int w_strncmp(const WideChar *lhs, const WideChar *rhs, size_t length) {
|
||||||
|
if (length == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
WideChar l, r;
|
||||||
|
while (true) {
|
||||||
|
l = *(lhs++);
|
||||||
|
r = *(rhs++);
|
||||||
|
if (l == 0 || r == 0 || l != r) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((--length) == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return l - r;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -380,7 +380,7 @@ namespace ams::mitm::fs {
|
|||||||
|
|
||||||
/* Try to get a storage from the cache. */
|
/* Try to get a storage from the cache. */
|
||||||
{
|
{
|
||||||
std::shared_ptr<fs::IStorage> cached_storage = GetStorageCacheEntry(data_id);
|
std::shared_ptr<fs::IStorage> cached_storage = GetStorageCacheEntry(this->client_info.program_id);
|
||||||
if (cached_storage != nullptr) {
|
if (cached_storage != nullptr) {
|
||||||
out.SetValue(MakeSharedStorage(cached_storage), target_object_id);
|
out.SetValue(MakeSharedStorage(cached_storage), target_object_id);
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
@@ -403,7 +403,7 @@ namespace ams::mitm::fs {
|
|||||||
new_storage = std::move(layered_storage);
|
new_storage = std::move(layered_storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetStorageCacheEntry(data_id, &new_storage);
|
SetStorageCacheEntry(this->client_info.program_id, &new_storage);
|
||||||
out.SetValue(MakeSharedStorage(new_storage), target_object_id);
|
out.SetValue(MakeSharedStorage(new_storage), target_object_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1
thermosphere/.gitignore
vendored
Normal file
1
thermosphere/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
out
|
||||||
@@ -9,39 +9,13 @@ endif
|
|||||||
TOPDIR ?= $(CURDIR)
|
TOPDIR ?= $(CURDIR)
|
||||||
include $(DEVKITPRO)/devkitA64/base_rules
|
include $(DEVKITPRO)/devkitA64/base_rules
|
||||||
|
|
||||||
export AMSLIBSDIR := $(TOPDIR)/../libraries
|
|
||||||
|
|
||||||
AMSBRANCH := $(shell git symbolic-ref --short HEAD)
|
AMSBRANCH := $(shell git symbolic-ref --short HEAD)
|
||||||
AMSHASH = $(shell git rev-parse --short=16 HEAD)
|
|
||||||
AMSREV := $(AMSBRANCH)-$(shell git rev-parse --short HEAD)
|
AMSREV := $(AMSBRANCH)-$(shell git rev-parse --short HEAD)
|
||||||
|
|
||||||
ifneq (, $(strip $(shell git status --porcelain 2>/dev/null)))
|
ifneq (, $(strip $(shell git status --porcelain 2>/dev/null)))
|
||||||
AMSREV := $(AMSREV)-dirty
|
AMSREV := $(AMSREV)-dirty
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(PLATFORM), qemu)
|
|
||||||
|
|
||||||
export PLATFORM := qemu
|
|
||||||
|
|
||||||
PLATFORM_SOURCES := src/platform/qemu
|
|
||||||
PLATFORM_DEFINES := -DPLATFORM_QEMU -DMAX_CORE=4 -DMAX_BCR=6 -DMAX_WCR=4
|
|
||||||
|
|
||||||
else ifeq ($(PLATFORM), tegra-t210-arm-tf)
|
|
||||||
|
|
||||||
export PLATFORM := tegra-t210-arm-tf
|
|
||||||
|
|
||||||
PLATFORM_SOURCES := src/platform/tegra
|
|
||||||
PLATFORM_DEFINES := -DPLATFORM_TEGRA -DPLATFORM_TEGRA_T210_ARM_TF -DMAX_CORE=4 -DMAX_BCR=6 -DMAX_WCR=4
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
export PLATFORM := tegra-t210-nintendo
|
|
||||||
|
|
||||||
PLATFORM_SOURCES := src/platform/tegra
|
|
||||||
PLATFORM_DEFINES := -DPLATFORM_TEGRA -D DPLATFORM_TEGRA_T210_NINTENDO -DMAX_CORE=4 -DMAX_BCR=6 -DMAX_WCR=4
|
|
||||||
|
|
||||||
endif
|
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# TARGET is the name of the output
|
# TARGET is the name of the output
|
||||||
# BUILD is the directory where object files & intermediate files will be placed
|
# BUILD is the directory where object files & intermediate files will be placed
|
||||||
@@ -51,62 +25,43 @@ endif
|
|||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
TARGET := $(notdir $(CURDIR))
|
TARGET := $(notdir $(CURDIR))
|
||||||
BUILD := build
|
BUILD := build
|
||||||
SOURCES := src src/libc src/platform src/gdb $(PLATFORM_SOURCES)
|
SOURCES := src src/lib
|
||||||
DATA := data
|
DATA := data
|
||||||
INCLUDES :=
|
INCLUDES := include ../common/include
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# options for code generation
|
# options for code generation
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# Note: -ffixed-x18 and -mgeneral-regs-only are very important and must be enabled
|
ARCH := -march=armv8-a -mtune=cortex-a57
|
||||||
ARCH := -march=armv8-a -mtune=cortex-a57 -mgeneral-regs-only -ffixed-x18 -Wno-psabi
|
DEFINES := -DATMOSPHERE_GIT_BRANCH=\"$(AMSBRANCH)\" -DATMOSPHERE_GIT_REV=\"$(AMSREV)\"
|
||||||
DEFINES := $(PLATFORM_DEFINES)
|
|
||||||
CFLAGS := \
|
CFLAGS := \
|
||||||
-g \
|
-g \
|
||||||
-fmacro-prefix-map=$(TOPDIR)/src/= \
|
-O2 \
|
||||||
-Os \
|
|
||||||
-ffunction-sections \
|
-ffunction-sections \
|
||||||
-fdata-sections \
|
-fdata-sections \
|
||||||
|
-mgeneral-regs-only \
|
||||||
-fomit-frame-pointer \
|
-fomit-frame-pointer \
|
||||||
-fno-asynchronous-unwind-tables \
|
-std=gnu11 \
|
||||||
-fno-unwind-tables \
|
|
||||||
-fno-stack-protector \
|
|
||||||
-fstrict-volatile-bitfields \
|
|
||||||
-Wall \
|
|
||||||
-Werror \
|
-Werror \
|
||||||
|
-Wall \
|
||||||
-Wno-main \
|
-Wno-main \
|
||||||
$(ARCH) $(DEFINES)
|
$(ARCH) $(DEFINES)
|
||||||
|
|
||||||
export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \
|
CFLAGS += $(INCLUDE) -D__CCPLEX__
|
||||||
-Wl,--wrap,__cxa_throw \
|
|
||||||
-Wl,--wrap,__cxa_rethrow \
|
|
||||||
-Wl,--wrap,__cxa_allocate_exception \
|
|
||||||
-Wl,--wrap,__cxa_free_exception \
|
|
||||||
-Wl,--wrap,__cxa_begin_catch \
|
|
||||||
-Wl,--wrap,__cxa_end_catch \
|
|
||||||
-Wl,--wrap,__cxa_call_unexpected \
|
|
||||||
-Wl,--wrap,__cxa_call_terminate \
|
|
||||||
-Wl,--wrap,__gxx_personality_v0 \
|
|
||||||
-Wl,--wrap,_Unwind_Resume \
|
|
||||||
-Wl,--wrap,_Unwind_Resume \
|
|
||||||
-Wl,--wrap,_ZSt19__throw_logic_errorPKc \
|
|
||||||
-Wl,--wrap,_ZSt20__throw_length_errorPKc \
|
|
||||||
-Wl,--wrap,_ZNSt11logic_errorC2EPKc
|
|
||||||
|
|
||||||
CFLAGS += $(INCLUDE)
|
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
|
||||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++2a
|
|
||||||
CFLAGS += -std=gnu11
|
|
||||||
|
|
||||||
ASFLAGS := -g $(ARCH) $(DEFINES)
|
ASFLAGS := -g $(ARCH)
|
||||||
LDFLAGS = -specs=$(TOPDIR)/linker.specs -nostartfiles -nostdlib -g $(ARCH) $(CXXWRAPS) -Wl,-Map,$(notdir $*.map)
|
LDFLAGS = -specs=$(TOPDIR)/linker.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||||
|
|
||||||
LIBS := -lgcc
|
LIBS :=
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# list of directories containing libraries, this must be the top level containing
|
# list of directories containing libraries, this must be the top level containing
|
||||||
# include and lib
|
# include and lib
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
LIBDIRS := $(AMSLIBSDIR)/libvapours
|
LIBDIRS :=
|
||||||
|
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
@@ -154,29 +109,11 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
|||||||
|
|
||||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||||
|
|
||||||
.PHONY: $(BUILD) clean all qemu qemudbg
|
.PHONY: $(BUILD) clean all
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
all: $(BUILD)
|
all: $(BUILD)
|
||||||
|
|
||||||
ifeq ($(PLATFORM), qemu)
|
|
||||||
|
|
||||||
export QEMU := qemu-system-aarch64
|
|
||||||
#export QEMU := ~/qemu/aarch64-softmmu/qemu-system-aarch64
|
|
||||||
|
|
||||||
QEMUFLAGS := -nographic -machine virt,virtualization=on,accel=tcg,gic-version=2 -cpu cortex-a57 -smp 4 -m 1024\
|
|
||||||
-kernel thermosphere.elf -d unimp,guest_errors -semihosting-config enable,target=native\
|
|
||||||
-chardev socket,id=uart,port=2222,host=0.0.0.0,server,nowait -chardev stdio,id=test -serial chardev:uart\
|
|
||||||
-monitor tcp:localhost:3333,server,nowait
|
|
||||||
|
|
||||||
qemu: all
|
|
||||||
@$(QEMU) $(QEMUFLAGS)
|
|
||||||
|
|
||||||
qemudbg: all
|
|
||||||
@$(QEMU) $(QEMUFLAGS) -s -S
|
|
||||||
|
|
||||||
endif
|
|
||||||
|
|
||||||
$(BUILD):
|
$(BUILD):
|
||||||
@[ -d $@ ] || mkdir -p $@
|
@[ -d $@ ] || mkdir -p $@
|
||||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||||
@@ -214,7 +151,7 @@ $(OFILES_SRC) : $(HFILES_BIN)
|
|||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# you need a rule like this for each extension you use as binary data
|
# you need a rule like this for each extension you use as binary data
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
%.bin.o %_bin.h: %.bin
|
%.bin.o : %.bin
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
@echo $(notdir $<)
|
@echo $(notdir $<)
|
||||||
@$(bin2o)
|
@$(bin2o)
|
||||||
|
|||||||
6
thermosphere/README.md
Normal file
6
thermosphere/README.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Thermosphère
|
||||||
|
=====
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Thermosphère is a hypervisor for the Nintendo Switch.
|
||||||
@@ -1,209 +1,55 @@
|
|||||||
|
OUTPUT_FORMAT("elf64-littleaarch64")
|
||||||
OUTPUT_ARCH(aarch64)
|
OUTPUT_ARCH(aarch64)
|
||||||
ENTRY(_start)
|
ENTRY(_start)
|
||||||
|
|
||||||
PHDRS
|
|
||||||
{
|
|
||||||
main PT_LOAD;
|
|
||||||
}
|
|
||||||
|
|
||||||
MEMORY
|
|
||||||
{
|
|
||||||
mainVa : ORIGIN = 0x7FFFE10000, LENGTH = 2M - 64K
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
{
|
{
|
||||||
__start_pa__ = ABSOLUTE(ORIGIN(main));
|
. = 0x800D0000;
|
||||||
__temp_pa__ = ABSOLUTE(ORIGIN(temp));
|
|
||||||
__max_image_size__ = ABSOLUTE(LENGTH(main));
|
|
||||||
__max_temp_size__ = ABSOLUTE(LENGTH(temp) - 0x1000);
|
|
||||||
|
|
||||||
.text :
|
. = ALIGN(4);
|
||||||
{
|
.text : {
|
||||||
. = ALIGN(8);
|
PROVIDE(lds_thermo_start = .);
|
||||||
__start__ = ABSOLUTE(.);
|
start.o (.text*)
|
||||||
KEEP(*(.crt0*));
|
*(.text*)
|
||||||
*(.text.unlikely .text.*_unlikely .text.unlikely.*)
|
}
|
||||||
*(.text.exit .text.exit.*)
|
|
||||||
*(.text.startup .text.startup.*)
|
. = ALIGN(8);
|
||||||
*(.text.hot .text.hot.*)
|
.rodata : {
|
||||||
*(.text .stub .text.* .gnu.linkonce.t.*)
|
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*)))
|
||||||
. = ALIGN(0x800);
|
}
|
||||||
__vectors_start__ = ABSOLUTE(.);
|
|
||||||
KEEP(*(.vectors*));
|
. = ALIGN(8);
|
||||||
__vectors_end__ = ABSOLUTE(.);
|
.data : {
|
||||||
ASSERT(__vectors_end__ - __vectors_start__ <= 0x800, "Exception vectors section should be max 0x800 in size!");
|
*(.data*)
|
||||||
. = ALIGN(8);
|
}
|
||||||
} >mainVa AT>main :main
|
|
||||||
|
/* Uninitialised data */
|
||||||
.init :
|
. = ALIGN(8);
|
||||||
{
|
PROVIDE(lds_bss_start = .);
|
||||||
KEEP( *(.init) )
|
.bss (NOLOAD) : {
|
||||||
. = ALIGN(8);
|
*(.bss*) . = ALIGN(8);
|
||||||
} >mainVa AT>main :main
|
}
|
||||||
|
PROVIDE(lds_bss_end = .);
|
||||||
.plt :
|
|
||||||
{
|
/* EL2 stack */
|
||||||
*(.plt)
|
. = ALIGN(16);
|
||||||
*(.iplt)
|
. += 0x10000; /* 64 KiB stack */
|
||||||
. = ALIGN(8);
|
el2_stack_end = .;
|
||||||
} >mainVa AT>main :main
|
|
||||||
|
/* Page align the end of binary */
|
||||||
|
. = ALIGN(512);
|
||||||
.fini :
|
PROVIDE(lds_el2_thermo_end = .);
|
||||||
{
|
|
||||||
KEEP( *(.fini) )
|
/* EL1 stack */
|
||||||
. = ALIGN(8);
|
. = ALIGN(16);
|
||||||
} >mainVa AT>main :main
|
. += 0x10000; /* 64 KiB stack */
|
||||||
|
el1_stack_end = .;
|
||||||
.rodata :
|
|
||||||
{
|
lds_thermo_end = .;
|
||||||
*(.rodata .rodata.* .gnu.linkonce.r.*)
|
|
||||||
SORT(CONSTRUCTORS)
|
/DISCARD/ : { *(.dynstr*) }
|
||||||
. = ALIGN(8);
|
/DISCARD/ : { *(.dynamic*) }
|
||||||
} >mainVa AT>main :main
|
/DISCARD/ : { *(.plt*) }
|
||||||
|
/DISCARD/ : { *(.interp*) }
|
||||||
.got : { __got_start__ = ABSOLUTE(.); *(.got) *(.igot) } >mainVa AT>main :main
|
/DISCARD/ : { *(.gnu*) }
|
||||||
.got.plt : { *(.got.plt) *(.igot.plt) __got_end__ = ABSOLUTE(.);} >mainVa AT>main :main
|
|
||||||
|
|
||||||
.preinit_array :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
PROVIDE (__preinit_array_start = ABSOLUTE(.));
|
|
||||||
KEEP (*(.preinit_array))
|
|
||||||
PROVIDE (__preinit_array_end = ABSOLUTE(.));
|
|
||||||
ASSERT(__preinit_array_end == __preinit_array_start, ".preinit_array not empty!");
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >mainVa AT>main :main
|
|
||||||
|
|
||||||
.init_array :
|
|
||||||
{
|
|
||||||
PROVIDE (__init_array_start = ABSOLUTE(.));
|
|
||||||
KEEP (*(SORT(.init_array.*)))
|
|
||||||
KEEP (*(.init_array))
|
|
||||||
PROVIDE (__init_array_end = ABSOLUTE(.));
|
|
||||||
ASSERT(__init_array_end == __init_array_start, ".init_array not empty!");
|
|
||||||
} >mainVa AT>main :main
|
|
||||||
|
|
||||||
.fini_array :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
PROVIDE (__fini_array_start = ABSOLUTE(.));
|
|
||||||
KEEP (*(.fini_array))
|
|
||||||
KEEP (*(SORT(.fini_array.*)))
|
|
||||||
PROVIDE (__fini_array_end = ABSOLUTE(.));
|
|
||||||
. = ALIGN(8);
|
|
||||||
ASSERT(__fini_array_end == __fini_array_start, ".fini_array not empty!");
|
|
||||||
} >mainVa AT>main :main
|
|
||||||
|
|
||||||
.ctors :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */
|
|
||||||
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
|
|
||||||
KEEP (*(SORT(.ctors.*)))
|
|
||||||
KEEP (*(.ctors))
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >mainVa AT>main :main
|
|
||||||
|
|
||||||
.dtors ALIGN(8) :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
KEEP (*crtbegin.o(.dtors))
|
|
||||||
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
|
|
||||||
KEEP (*(SORT(.dtors.*)))
|
|
||||||
KEEP (*(.dtors))
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >mainVa AT>main :main
|
|
||||||
|
|
||||||
.data ALIGN(8) :
|
|
||||||
{
|
|
||||||
*(.data .data.* .gnu.linkonce.d.*)
|
|
||||||
CONSTRUCTORS
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >mainVa AT>main :main
|
|
||||||
|
|
||||||
.dynamic : { *(.dynamic) } >mainVa AT>main :main
|
|
||||||
.interp : { *(.interp) } >mainVa AT>main :main
|
|
||||||
.note.gnu.build-id : { *(.note.gnu.build-id) } >mainVa AT>main :main
|
|
||||||
.hash : { *(.hash) } >mainVa AT>main :main
|
|
||||||
.gnu.hash : { *(.gnu.hash) } >mainVa AT>main :main
|
|
||||||
.gnu.version : { *(.gnu.version) } >mainVa AT>main :main
|
|
||||||
.gnu.version_d : { *(.gnu.version_d) } >mainVa AT>main :main
|
|
||||||
.gnu.version_r : { *(.gnu.version_r) } >mainVa AT>main :main
|
|
||||||
.dynsym : { *(.dynsym) } >mainVa AT>main :main
|
|
||||||
.dynstr : { *(.dynstr) } >mainVa AT>main :main
|
|
||||||
.rela.dyn : { *(.rela.*); __main_end__ = ABSOLUTE(.);} >mainVa AT>main :main
|
|
||||||
|
|
||||||
.bss (NOLOAD) :
|
|
||||||
{
|
|
||||||
__bss_start__ = ABSOLUTE(.);
|
|
||||||
*(.dynbss)
|
|
||||||
*(.bss .bss.* .gnu.linkonce.b.*)
|
|
||||||
*(COMMON)
|
|
||||||
} >mainVa :NONE
|
|
||||||
|
|
||||||
.tempbss (NOLOAD) :
|
|
||||||
{
|
|
||||||
. = ALIGN(0x1000);
|
|
||||||
__real_bss_end__ = ABSOLUTE(.);
|
|
||||||
__image_size__ = ABSOLUTE(__real_bss_end__ - __start__);
|
|
||||||
/*ASSERT(__image_size__ <= __max_image_size__, "Image too big!");*/
|
|
||||||
*(.tempbss .tempbss.*)
|
|
||||||
. = ALIGN(0x1000);
|
|
||||||
__bss_end__ = ABSOLUTE(.);
|
|
||||||
__temp_size__ = ABSOLUTE(__bss_end__ - __real_bss_end__);
|
|
||||||
ASSERT(__temp_size__ <= __max_temp_size__, "tempbss too big!");
|
|
||||||
} >mainVa :NONE
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
. = ALIGN(8);
|
|
||||||
|
|
||||||
/* Shit we keep in the elf but otherwise discard */
|
|
||||||
.eh_frame_hdr (NOLOAD) : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } >mainVa :NONE
|
|
||||||
.eh_frame (NOLOAD) : { KEEP (*(.eh_frame)) *(.eh_frame.*) } >mainVa :NONE
|
|
||||||
.gcc_except_table (NOLOAD) : { *(.gcc_except_table .gcc_except_table.*) } >mainVa :NONE
|
|
||||||
.gnu_extab (NOLOAD) : { *(.gnu_extab*) } >mainVa :NONE
|
|
||||||
.exception_ranges (NOLOAD) : { *(.exception_ranges .exception_ranges*) } >mainVa :NONE
|
|
||||||
|
|
||||||
/* ==================
|
|
||||||
==== Metadata ====
|
|
||||||
================== */
|
|
||||||
|
|
||||||
/* Discard sections that difficult post-processing */
|
|
||||||
/DISCARD/ : { *(.group .comment .note) }
|
|
||||||
|
|
||||||
/* Stabs debugging sections. */
|
|
||||||
.stab 0 : { *(.stab) }
|
|
||||||
.stabstr 0 : { *(.stabstr) }
|
|
||||||
.stab.excl 0 : { *(.stab.excl) }
|
|
||||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
|
||||||
.stab.index 0 : { *(.stab.index) }
|
|
||||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
|
||||||
|
|
||||||
/* DWARF debug sections.
|
|
||||||
Symbols in the DWARF debugging sections are relative to the beginning
|
|
||||||
of the section so we begin them at 0. */
|
|
||||||
|
|
||||||
/* DWARF 1 */
|
|
||||||
.debug 0 : { *(.debug) }
|
|
||||||
.line 0 : { *(.line) }
|
|
||||||
|
|
||||||
/* GNU DWARF 1 extensions */
|
|
||||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
|
||||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
|
||||||
|
|
||||||
/* DWARF 1.1 and DWARF 2 */
|
|
||||||
.debug_aranges 0 : { *(.debug_aranges) }
|
|
||||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
|
||||||
|
|
||||||
/* DWARF 2 */
|
|
||||||
.debug_info 0 : { *(.debug_info) }
|
|
||||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
|
||||||
.debug_line 0 : { *(.debug_line) }
|
|
||||||
.debug_frame 0 : { *(.debug_frame) }
|
|
||||||
.debug_str 0 : { *(.debug_str) }
|
|
||||||
.debug_loc 0 : { *(.debug_loc) }
|
|
||||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
%rename link old_link
|
%rename link old_link
|
||||||
|
|
||||||
*link:
|
*link:
|
||||||
%(old_link) -T %:getenv(TOPDIR /%:getenv(PLATFORM .mem)) -T %:getenv(TOPDIR /linker.ld) -no-pie --nmagic --gc-sections
|
%(old_link) -T %:getenv(TOPDIR /linker.ld) --nmagic --gc-sections
|
||||||
|
|
||||||
|
*startfile:
|
||||||
|
crti%O%s crtbegin%O%s
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
MEMORY
|
|
||||||
{
|
|
||||||
NULL : ORIGIN = 0, LENGTH = 0x1000
|
|
||||||
main : ORIGIN = 0x60000000, LENGTH = 64M /* QEMU's memory map changes dynamically? */
|
|
||||||
temp : ORIGIN = 0x64000000, LENGTH = 64M
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "defines.hpp"
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
/* Redefine abort to trigger these handlers. */
|
|
||||||
void abort();
|
|
||||||
|
|
||||||
/* Redefine C++ exception handlers. Requires wrap linker flag. */
|
|
||||||
#define WRAP_ABORT_FUNC(func) void NORETURN __wrap_##func(void) { abort(); __builtin_unreachable(); }
|
|
||||||
WRAP_ABORT_FUNC(__cxa_pure_virtual)
|
|
||||||
WRAP_ABORT_FUNC(__cxa_throw)
|
|
||||||
WRAP_ABORT_FUNC(__cxa_rethrow)
|
|
||||||
WRAP_ABORT_FUNC(__cxa_allocate_exception)
|
|
||||||
WRAP_ABORT_FUNC(__cxa_free_exception)
|
|
||||||
WRAP_ABORT_FUNC(__cxa_begin_catch)
|
|
||||||
WRAP_ABORT_FUNC(__cxa_end_catch)
|
|
||||||
WRAP_ABORT_FUNC(__cxa_call_unexpected)
|
|
||||||
WRAP_ABORT_FUNC(__cxa_call_terminate)
|
|
||||||
WRAP_ABORT_FUNC(__gxx_personality_v0)
|
|
||||||
WRAP_ABORT_FUNC(_ZSt19__throw_logic_errorPKc)
|
|
||||||
WRAP_ABORT_FUNC(_ZSt20__throw_length_errorPKc)
|
|
||||||
WRAP_ABORT_FUNC(_ZNSt11logic_errorC2EPKc)
|
|
||||||
|
|
||||||
/* TODO: We may wish to consider intentionally not defining an _Unwind_Resume wrapper. */
|
|
||||||
/* This would mean that a failure to wrap all exception functions is a linker error. */
|
|
||||||
WRAP_ABORT_FUNC(_Unwind_Resume)
|
|
||||||
#undef WRAP_ABORT_FUNC
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Custom abort handler, so that std::abort will trigger these. */
|
|
||||||
void abort()
|
|
||||||
{
|
|
||||||
#ifndef PLATFORM_QEMU
|
|
||||||
__builtin_trap();
|
|
||||||
#endif
|
|
||||||
for (;;);
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2018-2019 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define EXCEP_STACK_FRAME_SIZE 0x140
|
|
||||||
|
|
||||||
.macro FUNCTION name
|
|
||||||
.section .text.\name, "ax", %progbits
|
|
||||||
.global \name
|
|
||||||
.type \name, %function
|
|
||||||
.func \name
|
|
||||||
.cfi_sections .debug_frame
|
|
||||||
.cfi_startproc
|
|
||||||
\name:
|
|
||||||
.endm
|
|
||||||
|
|
||||||
.macro END_FUNCTION
|
|
||||||
.cfi_endproc
|
|
||||||
.endfunc
|
|
||||||
.endm
|
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "hvisor_cpu_caches.hpp"
|
|
||||||
|
|
||||||
#define DEFINE_CACHE_RANGE_FUNC(isn, name, cache, post)\
|
|
||||||
void name(const void *addr, size_t size)\
|
|
||||||
{\
|
|
||||||
u32 lineCacheSize = GetSmallest##cache##CacheLineSize();\
|
|
||||||
uintptr_t begin = reinterpret_cast<uintptr_t>(addr) & ~(lineCacheSize - 1);\
|
|
||||||
uintptr_t end = (reinterpret_cast<uintptr_t>(addr) + size + lineCacheSize - 1) & ~(lineCacheSize - 1);\
|
|
||||||
for (uintptr_t pos = begin; pos < end; pos += lineCacheSize) {\
|
|
||||||
__asm__ __volatile__ (isn ", %0" :: "r"(pos) : "memory");\
|
|
||||||
}\
|
|
||||||
post;\
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
ALWAYS_INLINE void SelectCacheLevel(bool instructionCache, u32 level)
|
|
||||||
{
|
|
||||||
u32 ibit = instructionCache ? 1 : 0;
|
|
||||||
u32 lbits = (level & 7) << 1;
|
|
||||||
THERMOSPHERE_SET_SYSREG(csselr_el1, lbits | ibit);
|
|
||||||
ams::hvisor::cpu::isb();
|
|
||||||
}
|
|
||||||
|
|
||||||
[[gnu::optimize("O2")]] ALWAYS_INLINE void InvalidateDataCacheLevel(u32 level)
|
|
||||||
{
|
|
||||||
SelectCacheLevel(false, level);
|
|
||||||
u32 ccsidr = static_cast<u32>(THERMOSPHERE_GET_SYSREG(ccsidr_el1));
|
|
||||||
u32 numWays = 1 + ((ccsidr >> 3) & 0x3FF);
|
|
||||||
u32 numSets = 1 + ((ccsidr >> 13) & 0x7FFF);
|
|
||||||
u32 wayShift = __builtin_clz(numWays);
|
|
||||||
u32 setShift = (ccsidr & 7) + 4;
|
|
||||||
u32 lbits = (level & 7) << 1;
|
|
||||||
|
|
||||||
for (u32 way = 0; way < numWays; way++) {
|
|
||||||
for (u32 set = 0; set < numSets; set++) {
|
|
||||||
u64 val = ((u64)way << wayShift) | ((u64)set << setShift) | lbits;
|
|
||||||
__asm__ __volatile__ ("dc isw, %0" :: "r"(val) : "memory");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ALWAYS_INLINE void CleanInvalidateDataCacheLevel(u32 level)
|
|
||||||
{
|
|
||||||
SelectCacheLevel(false, level);
|
|
||||||
u32 ccsidr = static_cast<u32>(THERMOSPHERE_GET_SYSREG(ccsidr_el1));
|
|
||||||
u32 numWays = 1 + ((ccsidr >> 3) & 0x3FF);
|
|
||||||
u32 numSets = 1 + ((ccsidr >> 13) & 0x7FFF);
|
|
||||||
u32 wayShift = __builtin_clz(numWays);
|
|
||||||
u32 setShift = (ccsidr & 7) + 4;
|
|
||||||
u32 lbits = (level & 7) << 1;
|
|
||||||
|
|
||||||
for (u32 way = 0; way < numWays; way++) {
|
|
||||||
for (u32 set = 0; set < numSets; set++) {
|
|
||||||
u64 val = ((u64)way << wayShift) | ((u64)set << setShift) | lbits;
|
|
||||||
__asm__ __volatile__ ("dc cisw, %0" :: "r"(val) : "memory");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[[gnu::optimize("O2")]] ALWAYS_INLINE void InvalidateDataCacheLevels(u32 from, u32 to)
|
|
||||||
{
|
|
||||||
// Let's hope it doesn't generate a stack frame...
|
|
||||||
for (u32 level = from; level < to; level++) {
|
|
||||||
InvalidateDataCacheLevel(level);
|
|
||||||
}
|
|
||||||
|
|
||||||
ams::hvisor::cpu::dsbSy();
|
|
||||||
ams::hvisor::cpu::isb();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
namespace ams::hvisor::cpu {
|
|
||||||
|
|
||||||
DEFINE_CACHE_RANGE_FUNC("dc civac", CleanInvalidateDataCacheRange, Data, dsbSy())
|
|
||||||
DEFINE_CACHE_RANGE_FUNC("dc cvau", CleanDataCacheRangePoU, Data, dsb())
|
|
||||||
DEFINE_CACHE_RANGE_FUNC("ic ivau", InvalidateInstructionCacheRangePoU, Instruction, dsb(); isb())
|
|
||||||
|
|
||||||
void HandleSelfModifyingCodePoU(const void *addr, size_t size)
|
|
||||||
{
|
|
||||||
// See docs for ctr_el0.{dic, idc}. It's unclear when these bits have been added, but they're
|
|
||||||
// RES0 if not implemented, so that's fine
|
|
||||||
u32 ctr = static_cast<u32>(THERMOSPHERE_GET_SYSREG(ctr_el0));
|
|
||||||
if (!(ctr & BIT(28))) {
|
|
||||||
CleanDataCacheRangePoU(addr, size);
|
|
||||||
}
|
|
||||||
if (!(ctr & BIT(29))) {
|
|
||||||
InvalidateInstructionCacheRangePoU(addr, size);
|
|
||||||
} else {
|
|
||||||
// Make sure we have at least a dsb/isb
|
|
||||||
dsb();
|
|
||||||
isb();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[[gnu::optimize("O2")]] void ClearSharedDataCachesOnBoot(void)
|
|
||||||
{
|
|
||||||
u32 clidr = static_cast<u32>(THERMOSPHERE_GET_SYSREG(clidr_el1));
|
|
||||||
u32 louis = (clidr >> 21) & 7;
|
|
||||||
u32 loc = (clidr >> 24) & 7;
|
|
||||||
InvalidateDataCacheLevels(louis, loc);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[gnu::optimize("O2")]] void ClearLocalDataCacheOnBoot(void)
|
|
||||||
{
|
|
||||||
u32 clidr = static_cast<u32>(THERMOSPHERE_GET_SYSREG(clidr_el1));
|
|
||||||
u32 louis = (clidr >> 21) & 7;
|
|
||||||
InvalidateDataCacheLevels(0, louis);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ok so:
|
|
||||||
- cache set/way ops can't really be virtualized
|
|
||||||
- since we have only one guest OS & don't care about security (for space limitations),
|
|
||||||
we do the following:
|
|
||||||
- ignore all cache s/w ops applying before the Level Of Unification Inner Shareable (L1, typically).
|
|
||||||
These clearly break coherency and should only be done once, on power on/off/suspend/resume only. And we already
|
|
||||||
do it ourselves...
|
|
||||||
- allow ops after the LoUIS, but do it ourselves and ignore the next (numSets*numWay - 1) requests. This is because
|
|
||||||
we have to handle Nintendo's dodgy code (check if SetWay == 0)
|
|
||||||
- transform all s/w cache ops into clean and invalidate
|
|
||||||
*/
|
|
||||||
void HandleTrappedSetWayOperation(u32 val)
|
|
||||||
{
|
|
||||||
u32 clidr = static_cast<u32>(THERMOSPHERE_GET_SYSREG(clidr_el1));
|
|
||||||
u32 louis = (clidr >> 21) & 7;
|
|
||||||
|
|
||||||
u32 level = val >> 1 & 7;
|
|
||||||
u32 setway = val >> 3;
|
|
||||||
if (level < louis) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (setway == 0) {
|
|
||||||
CleanInvalidateDataCacheLevel(level);
|
|
||||||
dsbSy();
|
|
||||||
isb();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 "hvisor_cpu_instructions.hpp"
|
|
||||||
#include "hvisor_cpu_sysreg_general.hpp"
|
|
||||||
|
|
||||||
namespace ams::hvisor::cpu {
|
|
||||||
|
|
||||||
inline u32 GetInstructionCachePolicy(void)
|
|
||||||
{
|
|
||||||
u32 ctr = static_cast<u32>(THERMOSPHERE_GET_SYSREG(ctr_el0));
|
|
||||||
return (ctr >> 14) & 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline u32 GetSmallestInstructionCacheLineSize(void)
|
|
||||||
{
|
|
||||||
u32 ctr = static_cast<u32>(THERMOSPHERE_GET_SYSREG(ctr_el0));
|
|
||||||
u32 shift = ctr & 0xF;
|
|
||||||
// "log2 of the number of words"...
|
|
||||||
return 4 << shift;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline u32 GetSmallestDataCacheLineSize(void)
|
|
||||||
{
|
|
||||||
u32 ctr = static_cast<u32>(THERMOSPHERE_GET_SYSREG(ctr_el0));
|
|
||||||
u32 shift = (ctr >> 16) & 0xF;
|
|
||||||
// "log2 of the number of words"...
|
|
||||||
return 4 << shift;
|
|
||||||
}
|
|
||||||
|
|
||||||
ALWAYS_INLINE void InvalidateInstructionCache(void)
|
|
||||||
{
|
|
||||||
__asm__ __volatile__ ("ic ialluis" ::: "memory");
|
|
||||||
cpu::isb();
|
|
||||||
}
|
|
||||||
|
|
||||||
ALWAYS_INLINE void InvalidateInstructionCacheLocal(void)
|
|
||||||
{
|
|
||||||
__asm__ __volatile__ ("ic iallu" ::: "memory");
|
|
||||||
cpu::isb();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CleanInvalidateDataCacheRange(const void *addr, size_t size);
|
|
||||||
void CleanDataCacheRangePoU(const void *addr, size_t size);
|
|
||||||
|
|
||||||
void InvalidateInstructionCacheRangePoU(const void *addr, size_t size);
|
|
||||||
|
|
||||||
void HandleSelfModifyingCodePoU(const void *addr, size_t size);
|
|
||||||
|
|
||||||
void ClearSharedDataCachesOnBoot(void);
|
|
||||||
void ClearLocalDataCacheOnBoot(void);
|
|
||||||
|
|
||||||
// Dunno where else to put that
|
|
||||||
void HandleTrappedSetWayOperation(u32 val);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 "../defines.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
namespace ams::hvisor::cpu {
|
|
||||||
|
|
||||||
// TODO GCC 10, use enum class.
|
|
||||||
// Would be nice if gcc didn't take 9+ years to fix a trivial bug ("too small to fit")
|
|
||||||
struct DebugRegisterPair {
|
|
||||||
// For breakpoints only
|
|
||||||
/// BT[3:1] or res0. BT[0]/WT[0] is "is linked"
|
|
||||||
enum BreakpointType : u32 {
|
|
||||||
AddressMatch = 0,
|
|
||||||
VheContextIdMatch = 1,
|
|
||||||
ContextIdMatch = 3,
|
|
||||||
VmidMatch = 4,
|
|
||||||
VmidContextIdMatch = 5,
|
|
||||||
VmidVheContextIdMatch = 6,
|
|
||||||
FullVheContextIdMatch = 7,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Note: some SSC HMC PMC combinations are invalid
|
|
||||||
// Refer to "Table D2-9 Summary of breakpoint HMC, SSC, and PMC encodings"
|
|
||||||
|
|
||||||
/// Security State Control
|
|
||||||
enum SecurityStateControl : u32 {
|
|
||||||
Both = 0,
|
|
||||||
NonSecure = 1,
|
|
||||||
Secure = 2,
|
|
||||||
SecureIfLowerOrBoth = 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Higher Mode Control
|
|
||||||
enum HigherModeControl : u32 {
|
|
||||||
LowerEl = 0,
|
|
||||||
HigherEl = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Privilege Mode Control (called PAC for watchpoints)
|
|
||||||
enum PrivilegeModeControl : u32 {
|
|
||||||
NeitherEl1Nor0 = 0,
|
|
||||||
El1 = 1,
|
|
||||||
El0 = 2,
|
|
||||||
El1And0 = 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Watchpoints only
|
|
||||||
enum LoadStoreControl : u32 {
|
|
||||||
NotAWatchpoint = 0,
|
|
||||||
Load = 1,
|
|
||||||
Store = 2,
|
|
||||||
LoadStore = 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
// bas only 4 bits for breakpoints, other bits res0.
|
|
||||||
// lsc, mask only for watchpoints, res0 for breakpoints
|
|
||||||
// bt only from breakpoints, res0 for watchpoints
|
|
||||||
struct ControlRegister {
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
bool enabled : 1;
|
|
||||||
PrivilegeModeControl pmc : 2;
|
|
||||||
LoadStoreControl lsc : 2;
|
|
||||||
u32 bas : 8;
|
|
||||||
HigherModeControl hmc : 1;
|
|
||||||
SecurityStateControl ssc : 2;
|
|
||||||
u32 lbn : 4;
|
|
||||||
bool linked : 1;
|
|
||||||
BreakpointType bt : 3;
|
|
||||||
u32 mask : 5;
|
|
||||||
u64 res0 : 35;
|
|
||||||
};
|
|
||||||
u64 raw;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
ControlRegister cr;
|
|
||||||
u64 vr;
|
|
||||||
|
|
||||||
constexpr void SetDefaults()
|
|
||||||
{
|
|
||||||
cr.linked = false;
|
|
||||||
|
|
||||||
// NS EL1&0 only
|
|
||||||
cr.hmc = LowerEl;
|
|
||||||
cr.ssc = NonSecure;
|
|
||||||
cr.pmc = El1And0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static_assert(std::is_standard_layout_v<DebugRegisterPair>);
|
|
||||||
static_assert(std::is_trivial_v<DebugRegisterPair>);
|
|
||||||
}
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 "../defines.hpp"
|
|
||||||
#include "hvisor_cpu_sysreg_general.hpp"
|
|
||||||
|
|
||||||
namespace ams::hvisor::cpu {
|
|
||||||
|
|
||||||
// FIXME GCC 10
|
|
||||||
|
|
||||||
struct ExceptionSyndromeRegister {
|
|
||||||
enum ExceptionClass : u32 {
|
|
||||||
Uncategorized = 0x0,
|
|
||||||
WFxTrap = 0x1,
|
|
||||||
CP15RTTrap = 0x3,
|
|
||||||
CP15RRTTrap = 0x4,
|
|
||||||
CP14RTTrap = 0x5,
|
|
||||||
CP14DTTrap = 0x6,
|
|
||||||
AdvSIMDFPAccessTrap = 0x7,
|
|
||||||
FPIDTrap = 0x8,
|
|
||||||
PACTrap = 0x9,
|
|
||||||
CP14RRTTrap = 0xC,
|
|
||||||
BranchTargetException = 0xD, // No official enum field name from Arm yet
|
|
||||||
IllegalState = 0xE,
|
|
||||||
SupervisorCallA32 = 0x11,
|
|
||||||
HypervisorCallA32 = 0x12,
|
|
||||||
MonitorCallA32 = 0x13,
|
|
||||||
SupervisorCallA64 = 0x15,
|
|
||||||
HypervisorCallA64 = 0x16,
|
|
||||||
MonitorCallA64 = 0x17,
|
|
||||||
SystemRegisterTrap = 0x18,
|
|
||||||
SVEAccessTrap = 0x19,
|
|
||||||
ERetTrap = 0x1A,
|
|
||||||
El3_ImplementationDefined = 0x1F,
|
|
||||||
InstructionAbortLowerEl = 0x20,
|
|
||||||
InstructionAbortSameEl = 0x21,
|
|
||||||
PCAlignment = 0x22,
|
|
||||||
DataAbortLowerEl = 0x24,
|
|
||||||
DataAbortSameEl = 0x25,
|
|
||||||
SPAlignment = 0x26,
|
|
||||||
FPTrappedExceptionA32 = 0x28,
|
|
||||||
FPTrappedExceptionA64 = 0x2C,
|
|
||||||
SError = 0x2F,
|
|
||||||
BreakpointLowerEl = 0x30,
|
|
||||||
BreakpointSameEl = 0x31,
|
|
||||||
SoftwareStepLowerEl = 0x32,
|
|
||||||
SoftwareStepSameEl = 0x33,
|
|
||||||
WatchpointLowerEl = 0x34,
|
|
||||||
WatchpointSameEl = 0x35,
|
|
||||||
SoftwareBreakpointA32 = 0x38,
|
|
||||||
VectorCatchA32 = 0x3A,
|
|
||||||
SoftwareBreakpointA64 = 0x3C,
|
|
||||||
};
|
|
||||||
|
|
||||||
u32 iss : 25; // Instruction Specific Syndrome
|
|
||||||
u32 il : 1; // Instruction Length (16 or 32-bit)
|
|
||||||
ExceptionClass ec : 6; // Exception Class
|
|
||||||
u32 res0 : 32;
|
|
||||||
|
|
||||||
constexpr size_t GetInstructionLength()
|
|
||||||
{
|
|
||||||
return il == 0 ? 2 : 4;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct DataAbortIss {
|
|
||||||
u32 dfsc : 6; // Fault status code
|
|
||||||
|
|
||||||
u32 wnr : 1; // Write, not Read
|
|
||||||
u32 s1ptw : 1; // Stage1 page table walk fault
|
|
||||||
u32 cm : 1; // Cache maintenance
|
|
||||||
u32 ea : 1; // External abort
|
|
||||||
u32 fnv : 1; // FAR not Valid
|
|
||||||
u32 set : 2; // Synchronous error type
|
|
||||||
u32 vncr : 1; // vncr_el2 trap
|
|
||||||
|
|
||||||
u32 ar : 1; // Acquire/release. Bit 14
|
|
||||||
u32 sf : 1; // 64-bit register used
|
|
||||||
u32 srt : 5; // Syndrome register transfer (register used)
|
|
||||||
u32 sse : 1; // Syndrome sign extend
|
|
||||||
u32 sas : 2; // Syndrome access size. Bit 23
|
|
||||||
|
|
||||||
u32 isv : 1; // Instruction syndrome valid (ISS[23:14] valid)
|
|
||||||
|
|
||||||
constexpr bool HasValidFar()
|
|
||||||
{
|
|
||||||
return isv && !fnv;
|
|
||||||
}
|
|
||||||
constexpr size_t GetAccessSize()
|
|
||||||
{
|
|
||||||
return BITL(sas);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static_assert(std::is_standard_layout_v<ExceptionSyndromeRegister>);
|
|
||||||
static_assert(std::is_standard_layout_v<DataAbortIss>);
|
|
||||||
static_assert(std::is_trivial_v<ExceptionSyndromeRegister>);
|
|
||||||
static_assert(std::is_trivial_v<DataAbortIss>);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 "../defines.hpp"
|
|
||||||
|
|
||||||
#define _ASM_ARITHMETIC_UNARY_HELPER(sz, regalloc, op) ({\
|
|
||||||
u##sz res;\
|
|
||||||
__asm__ __volatile__ (STRINGIZE(op) " %" STRINGIZE(regalloc) "[res], %" STRINGIZE(regalloc) "[val]" : [res] "=r" (res) : [val] "r" (val));\
|
|
||||||
res;\
|
|
||||||
})
|
|
||||||
|
|
||||||
#define DECLARE_SINGLE_ASM_INSN2(name, what) ALWAYS_INLINE void name() { __asm__ __volatile__ (what ::: "memory"); }
|
|
||||||
#define DECLARE_SINGLE_ASM_INSN(name) ALWAYS_INLINE void name() { __asm__ __volatile__ (STRINGIZE(name) ::: "memory"); }
|
|
||||||
|
|
||||||
namespace ams::hvisor::cpu {
|
|
||||||
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
ALWAYS_INLINE static T rbit(T val)
|
|
||||||
{
|
|
||||||
static_assert(std::is_integral_v<T> && (sizeof(T) == 8 || sizeof(T) == 4));
|
|
||||||
if constexpr (sizeof(T) == 8) {
|
|
||||||
return _ASM_ARITHMETIC_UNARY_HELPER(64, x, rbit);
|
|
||||||
} else {
|
|
||||||
return _ASM_ARITHMETIC_UNARY_HELPER(32, w, rbit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DECLARE_SINGLE_ASM_INSN(wfi)
|
|
||||||
DECLARE_SINGLE_ASM_INSN(wfe)
|
|
||||||
DECLARE_SINGLE_ASM_INSN(sevl)
|
|
||||||
DECLARE_SINGLE_ASM_INSN(sev)
|
|
||||||
DECLARE_SINGLE_ASM_INSN2(dmb, "dmb ish")
|
|
||||||
DECLARE_SINGLE_ASM_INSN2(dmbSy, "dmb sy")
|
|
||||||
DECLARE_SINGLE_ASM_INSN2(dsb, "dsb ish")
|
|
||||||
DECLARE_SINGLE_ASM_INSN2(dsbSy, "dsb sy")
|
|
||||||
DECLARE_SINGLE_ASM_INSN(isb)
|
|
||||||
|
|
||||||
DECLARE_SINGLE_ASM_INSN2(TlbInvalidateEl2Local, "tlbi alle2")
|
|
||||||
DECLARE_SINGLE_ASM_INSN2(TlbInvalidateEl2, "tlbi alle2is")
|
|
||||||
DECLARE_SINGLE_ASM_INSN2(TlbInvalidateEl1, "tlbi vmalle1is")
|
|
||||||
DECLARE_SINGLE_ASM_INSN2(TlbInvalidateEl1Stage12, "tlbi alle1is")
|
|
||||||
DECLARE_SINGLE_ASM_INSN2(TlbInvalidateEl1Stage12Local, "tlbi alle1")
|
|
||||||
|
|
||||||
ALWAYS_INLINE void TlbInvalidateEl2Page(uintptr_t addr)
|
|
||||||
{
|
|
||||||
__asm__ __volatile__ ("tlbi vae2is, %0" :: "r"(addr) : "memory");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef DECLARE_SINGLE_ASM_INSN
|
|
||||||
#undef DECLARE_SINGLE_ASM_INSN2
|
|
||||||
#undef _ASM_ARITHMETIC_UNARY_HELPER
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 "hvisor_cpu_sysreg_general.hpp"
|
|
||||||
|
|
||||||
namespace ams::hvisor::cpu {
|
|
||||||
|
|
||||||
ALWAYS_INLINE u64 MaskIrq()
|
|
||||||
{
|
|
||||||
u64 daif = THERMOSPHERE_GET_SYSREG(daif);
|
|
||||||
THERMOSPHERE_SET_SYSREG_IMM(daifset, BIT(1));
|
|
||||||
return daif;
|
|
||||||
}
|
|
||||||
|
|
||||||
ALWAYS_INLINE u64 UnmaskIrq()
|
|
||||||
{
|
|
||||||
u64 daif = THERMOSPHERE_GET_SYSREG(daif);
|
|
||||||
THERMOSPHERE_SET_SYSREG_IMM(daifclr, BIT(1));
|
|
||||||
return daif;
|
|
||||||
}
|
|
||||||
|
|
||||||
ALWAYS_INLINE void RestoreInterruptFlags(u64 flags)
|
|
||||||
{
|
|
||||||
THERMOSPHERE_SET_SYSREG(daif, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
class InterruptMaskGuard final {
|
|
||||||
NON_COPYABLE(InterruptMaskGuard);
|
|
||||||
NON_MOVEABLE(InterruptMaskGuard);
|
|
||||||
private:
|
|
||||||
u64 m_flags;
|
|
||||||
public:
|
|
||||||
ALWAYS_INLINE InterruptMaskGuard() : m_flags(MaskIrq()) {}
|
|
||||||
ALWAYS_INLINE ~InterruptMaskGuard()
|
|
||||||
{
|
|
||||||
RestoreInterruptFlags(m_flags);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,198 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 "hvisor_cpu_sysreg_general.hpp"
|
|
||||||
|
|
||||||
namespace ams::hvisor::cpu {
|
|
||||||
|
|
||||||
// Assumes addr is valid, must be called with interrupts masked
|
|
||||||
inline uintptr_t Va2Pa(const void *vaddrEl2) {
|
|
||||||
uintptr_t va = reinterpret_cast<uintptr_t>(vaddrEl2);
|
|
||||||
__asm__ __volatile__("at s1e2r, %0" :: "r"(va) : "memory");
|
|
||||||
return (THERMOSPHERE_GET_SYSREG(par_el1) & MASK2L(47, 12)) | (va & MASKL(12));
|
|
||||||
}
|
|
||||||
|
|
||||||
enum MmuPteType : u64 {
|
|
||||||
MMU_ENTRY_FAULT = 0,
|
|
||||||
MMU_ENTRY_BLOCK = 1,
|
|
||||||
MMU_ENTRY_TABLE = 3,
|
|
||||||
|
|
||||||
// L3 (this definition allows for recursive page tables)
|
|
||||||
MMU_ENTRY_PAGE = 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Multi-byte attributes...
|
|
||||||
constexpr u64 MMU_ATTRINDX(u64 idx) { return (idx & 8) << 2; }
|
|
||||||
constexpr u64 MMU_MEMATTR(u64 attr) { return (attr & 0xF) << 2; }
|
|
||||||
constexpr u64 MMU_SH(u64 sh) { return (sh & 3) << 8; }
|
|
||||||
|
|
||||||
// Attributes. They are defined in a way that allows recursive page tables (assuming PBHA isn't used)
|
|
||||||
enum MmuPteAttributes : u64 {
|
|
||||||
// Stage 1 Table only, the rest is block/page only
|
|
||||||
MMU_NS_TABLE = BITL(62),
|
|
||||||
MMU_AP_TABLE = BITL(61),
|
|
||||||
MMU_XN_TABLE = BITL(60),
|
|
||||||
MMU_PXN_TABLE = BITL(59),
|
|
||||||
|
|
||||||
MMU_UXN = BITL(54), // EL1&0 only
|
|
||||||
MMU_PXN = BITL(53), // EL1&0 only
|
|
||||||
MMU_XN = MMU_UXN,
|
|
||||||
MMU_XN0 = MMU_PXN, // Armv8.2, stage 2 only
|
|
||||||
MMU_CONTIGUOUS = BITL(52),
|
|
||||||
MMU_DBM = BITL(51), // stage 1 only
|
|
||||||
MMU_GP = BITL(50), // undocumented
|
|
||||||
|
|
||||||
// ARMv8.4-TTRem only
|
|
||||||
MMU_NT = BITL(16),
|
|
||||||
|
|
||||||
// EL1&0 only
|
|
||||||
MMU_NG = BITL(11),
|
|
||||||
|
|
||||||
MMU_AF = BITL(10),
|
|
||||||
|
|
||||||
// SH[1:0]
|
|
||||||
MMU_NON_SHAREABLE = MMU_SH(0),
|
|
||||||
MMU_OUTER_SHAREABLE = MMU_SH(2),
|
|
||||||
MMU_INNER_SHAREABLE = MMU_SH(3),
|
|
||||||
|
|
||||||
// AP[2:1], stage 1 only. AP[0] does not exist.
|
|
||||||
MMU_AP_PRIV_RW = 0 << 6,
|
|
||||||
MMU_AP_RW = 1 << 6,
|
|
||||||
MMU_AP_PRIV_RO = 2 << 6,
|
|
||||||
MMU_AP_RO = 3 << 6,
|
|
||||||
|
|
||||||
// S2AP[1:0], stage 2 only
|
|
||||||
MMU_S2AP_NONE = 0 << 6,
|
|
||||||
MMU_S2AP_RO = 1 << 6,
|
|
||||||
MMU_S2AP_WO = 2 << 6,
|
|
||||||
MMU_S2AP_RW = 3 << 6,
|
|
||||||
|
|
||||||
// NS, stage 1 only
|
|
||||||
MMU_NS = BITL(5),
|
|
||||||
|
|
||||||
// See above...
|
|
||||||
|
|
||||||
// MemAttr[3:0], stage 2 only (convenience defs). When combining, strongest memory type applies
|
|
||||||
MMU_MEMATTR_DEVICE_NGNRE = MMU_MEMATTR(2),
|
|
||||||
MMU_MEMATTR_UNCHANGED = MMU_MEMATTR(0xF),
|
|
||||||
|
|
||||||
// Other useful defines for stage 2:
|
|
||||||
MMU_SAME_SHAREABILITY = MMU_NON_SHAREABLE,
|
|
||||||
};
|
|
||||||
|
|
||||||
template<u32 Level, u32 AddressSpaceSize, bool IsMmuEnabled = false, TranslationGranuleSize GranuleSize = TranslationGranule_4K>
|
|
||||||
class MmuTableBuilder final {
|
|
||||||
private:
|
|
||||||
static constexpr u32 tgBitSize = GetTranslationGranuleBitSize(GranuleSize);
|
|
||||||
|
|
||||||
// tgBitSize - 3 = log2(tg / sizeof(u64))
|
|
||||||
static constexpr u32 levelShift = tgBitSize + (tgBitSize - 3) * (3 - Level);
|
|
||||||
static constexpr u32 levelBitSize = std::min(AddressSpaceSize - levelShift, tgBitSize - 3);
|
|
||||||
static constexpr u64 levelMask = MASKL(levelBitSize);
|
|
||||||
static constexpr size_t ComputeIndex(uintptr_t va)
|
|
||||||
{
|
|
||||||
return (va >> levelShift) & levelMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
u64 *m_pageTable = nullptr;
|
|
||||||
|
|
||||||
public:
|
|
||||||
using NextLevelBuilder = MmuTableBuilder<Level + 1, AddressSpaceSize, IsMmuEnabled, GranuleSize>;
|
|
||||||
static_assert(Level <= 3, "Invalid translation table level");
|
|
||||||
static_assert(AddressSpaceSize <= 48);
|
|
||||||
static_assert(AddressSpaceSize > levelShift, "Address space size mismatch with translation level");
|
|
||||||
static constexpr size_t blockSize = BITL(levelShift);
|
|
||||||
static constexpr size_t tableSize = BITL(levelBitSize);
|
|
||||||
|
|
||||||
public:
|
|
||||||
constexpr MmuTableBuilder(u64 *pageTable = nullptr) : m_pageTable{pageTable} {}
|
|
||||||
|
|
||||||
constexpr MmuTableBuilder &InitializeTable()
|
|
||||||
{
|
|
||||||
std::memset(m_pageTable, 0, 8 * tableSize);
|
|
||||||
// Fails to optimize before GCC 10: std::fill_n(m_pageTable, tableSize, MMU_ENTRY_FAULT);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Precondition: va and pa bits in range
|
|
||||||
constexpr NextLevelBuilder MapTable(uintptr_t va, uintptr_t pa, u64 *table, u64 attribs = 0) const
|
|
||||||
{
|
|
||||||
static_assert(Level < 3, "Level 3 is the last level of translation");
|
|
||||||
|
|
||||||
m_pageTable[ComputeIndex(va)] = pa | attribs | MMU_ENTRY_TABLE;
|
|
||||||
return NextLevelBuilder{table};
|
|
||||||
}
|
|
||||||
|
|
||||||
NextLevelBuilder MapTable(uintptr_t va, u64 *table, u64 attribs = 0) const
|
|
||||||
{
|
|
||||||
if constexpr (IsMmuEnabled) {
|
|
||||||
return MapTable(va, Va2Pa(table), table, attribs);
|
|
||||||
} else {
|
|
||||||
return MapTable(va, reinterpret_cast<uintptr_t>(table), table, attribs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr MmuTableBuilder &Unmap(uintptr_t va)
|
|
||||||
{
|
|
||||||
m_pageTable[ComputeIndex(va)] = MMU_ENTRY_FAULT;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Precondition: guardSize == 0 if Level == 0
|
|
||||||
constexpr MmuTableBuilder &UnmapRange(uintptr_t va, size_t size, size_t guardSize = 0)
|
|
||||||
{
|
|
||||||
for (size_t off = 0, offVa = 0; off < size; off += blockSize, offVa += blockSize + guardSize) {
|
|
||||||
Unmap(va + offVa);
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Precondition: va and pa bits in range
|
|
||||||
constexpr MmuTableBuilder &MapBlock(uintptr_t va, uintptr_t pa, u64 attribs)
|
|
||||||
{
|
|
||||||
static_assert(Level > 0, "Can only map L1 tables at L0");
|
|
||||||
|
|
||||||
constexpr u64 entryType = Level == 3 ? MMU_ENTRY_PAGE : MMU_ENTRY_BLOCK;
|
|
||||||
m_pageTable[ComputeIndex(va)] = pa | attribs | MMU_AF | entryType;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr MmuTableBuilder &MapBlock(uintptr_t pa, u64 attribs)
|
|
||||||
{
|
|
||||||
return MapBlock(pa, pa, attribs);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Precondition: size and guardSize are multiples of blockSize
|
|
||||||
constexpr MmuTableBuilder &MapBlockRange(uintptr_t va, uintptr_t pa, size_t size, u64 attribs, size_t guardSize = 0)
|
|
||||||
{
|
|
||||||
for (size_t off = 0, offVa = 0; off < size; off += blockSize, offVa += blockSize + guardSize) {
|
|
||||||
MapBlock(va + offVa, pa + off, attribs);
|
|
||||||
UnmapRange(va + offVa + blockSize, guardSize, 0);
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr MmuTableBuilder &MapBlockRange(uintptr_t pa, size_t size, u64 attribs)
|
|
||||||
{
|
|
||||||
return MapBlockRange(pa, pa, attribs, size, 0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,495 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 "../preprocessor.h"
|
|
||||||
#include "../defines.hpp"
|
|
||||||
|
|
||||||
#define THERMOSPHERE_GET_SYSREG(r) ({\
|
|
||||||
u64 __val; \
|
|
||||||
__asm__ __volatile__("mrs %0, " STRINGIZE(r) : "=r" (__val) :: "memory"); \
|
|
||||||
__val; \
|
|
||||||
})
|
|
||||||
|
|
||||||
#define THERMOSPHERE_SET_SYSREG(reg, val)\
|
|
||||||
do {\
|
|
||||||
u64 temp_reg = (val);\
|
|
||||||
__asm__ __volatile__ ("msr " STRINGIZE(reg) ", %0" :: "r"(temp_reg) : "memory");\
|
|
||||||
} while(false)
|
|
||||||
|
|
||||||
#define THERMOSPHERE_SET_SYSREG_IMM(reg, imm)\
|
|
||||||
do {\
|
|
||||||
__asm__ __volatile__ ("msr " STRINGIZE(reg) ", %0" :: "I"(imm) : "memory", "cc");\
|
|
||||||
} while(false)
|
|
||||||
|
|
||||||
|
|
||||||
namespace ams::hvisor::cpu {
|
|
||||||
|
|
||||||
using SysregEncoding = std::array<u8, 5>;
|
|
||||||
|
|
||||||
constexpr u32 EncodeSysregIss(SysregEncoding reg)
|
|
||||||
{
|
|
||||||
auto [op0, op1, crn, crm, op2] = reg;
|
|
||||||
return op0 << 20 | op2 << 17 | op1 << 14 | crn << 10 | crm << 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr u32 MakeMsrFromEncoding(SysregEncoding reg, u32 Rt)
|
|
||||||
{
|
|
||||||
auto [op0, op1, crn, crm, op2] = reg;
|
|
||||||
u32 enc = op0 << 19 | op1 << 16 | crn << 12 | crm << 8 | op2 << 5;
|
|
||||||
return 0xD5000000u | enc | (Rt & 0x1Fu);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr u32 MakeMrsFromEncoding(SysregEncoding reg, u32 Rt)
|
|
||||||
{
|
|
||||||
auto [op0, op1, crn, crm, op2] = reg;
|
|
||||||
u32 enc = op0 << 19 | op1 << 16 | crn << 12 | crm << 8 | op2 << 5;
|
|
||||||
return 0xD5200000u | enc | (Rt & 0x1Fu);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The list mostly includes EL1 registers as these are the one we're trapping
|
|
||||||
|
|
||||||
constexpr SysregEncoding dbgbvrN_el1(u8 n) { return {2, 0, 0, n, 4}; }
|
|
||||||
constexpr SysregEncoding dbgbcrN_el1(u8 n) { return {2, 0, 0, n, 5}; }
|
|
||||||
constexpr SysregEncoding dbgwvrN_el1(u8 n) { return {2, 0, 0, n, 6}; }
|
|
||||||
constexpr SysregEncoding dbgwcrN_el1(u8 n) { return {2, 0, 0, n, 7}; }
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding dc_isw = {1, 0, 7, 6, 2};
|
|
||||||
constexpr inline SysregEncoding dc_csw = {1, 0, 7, 10, 2};
|
|
||||||
constexpr inline SysregEncoding dc_cisw = {1, 0, 7, 14, 2};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding osdtrrx_el1 = {2, 0, 0, 0, 2};
|
|
||||||
constexpr inline SysregEncoding mdccint_el1 = {2, 0, 0, 2, 0};
|
|
||||||
constexpr inline SysregEncoding mdscr_el1 = {2, 0, 0, 2, 2};
|
|
||||||
constexpr inline SysregEncoding osdtrtx_el1 = {2, 0, 0, 3, 2};
|
|
||||||
constexpr inline SysregEncoding oseccr_el1 = {2, 0, 0, 6, 2};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding mdrar_el1 = {2, 0, 1, 0, 0};
|
|
||||||
constexpr inline SysregEncoding oslar_el1 = {2, 0, 1, 0, 4};
|
|
||||||
constexpr inline SysregEncoding oslsr_el1 = {2, 0, 1, 1, 4};
|
|
||||||
constexpr inline SysregEncoding osdlr_el1 = {2, 0, 1, 3, 4};
|
|
||||||
constexpr inline SysregEncoding dbgprcr_el1 = {2, 0, 1, 4, 4};
|
|
||||||
constexpr inline SysregEncoding dbgclaimset_el1 = {2, 0, 7, 8, 6};
|
|
||||||
constexpr inline SysregEncoding dbgclaimclr_el1 = {2, 0, 7, 9, 6};
|
|
||||||
constexpr inline SysregEncoding dbgauthstatus_el1 = {2, 0, 7, 14, 6};
|
|
||||||
constexpr inline SysregEncoding mdccsr_el0 = {2, 3, 0, 1, 0};
|
|
||||||
constexpr inline SysregEncoding dbgdtr_el0 = {2, 3, 0, 4, 0};
|
|
||||||
constexpr inline SysregEncoding dbgdtrrx_el0 = {2, 3, 0, 5, 0};
|
|
||||||
constexpr inline SysregEncoding dbgdtrtx_el0 = {2, 3, 0, 5, 0};
|
|
||||||
constexpr inline SysregEncoding dbgvcr32_el2 = {2, 4, 0, 7, 0};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding midr_el1 = {3, 0, 0, 0, 0};
|
|
||||||
constexpr inline SysregEncoding mpidr_el1 = {3, 0, 0, 0, 5};
|
|
||||||
constexpr inline SysregEncoding revidr_el1 = {3, 0, 0, 0, 6};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding id_pfr0_el1 = {3, 0, 0, 1, 0};
|
|
||||||
constexpr inline SysregEncoding id_pfr1_el1 = {3, 0, 0, 1, 1};
|
|
||||||
constexpr inline SysregEncoding id_dfr0_el1 = {3, 0, 0, 1, 2};
|
|
||||||
constexpr inline SysregEncoding id_afr0_el1 = {3, 0, 0, 1, 3};
|
|
||||||
constexpr inline SysregEncoding id_mmfr0_el1 = {3, 0, 0, 1, 4};
|
|
||||||
constexpr inline SysregEncoding id_mmfr1_el1 = {3, 0, 0, 1, 5};
|
|
||||||
constexpr inline SysregEncoding id_mmfr2_el1 = {3, 0, 0, 1, 6};
|
|
||||||
constexpr inline SysregEncoding id_mmfr3_el1 = {3, 0, 0, 1, 7};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding id_isar0_el1 = {3, 0, 0, 2, 0};
|
|
||||||
constexpr inline SysregEncoding id_isar1_el1 = {3, 0, 0, 2, 1};
|
|
||||||
constexpr inline SysregEncoding id_isar2_el1 = {3, 0, 0, 2, 2};
|
|
||||||
constexpr inline SysregEncoding id_isar3_el1 = {3, 0, 0, 2, 3};
|
|
||||||
constexpr inline SysregEncoding id_isar4_el1 = {3, 0, 0, 2, 4};
|
|
||||||
constexpr inline SysregEncoding id_isar5_el1 = {3, 0, 0, 2, 5};
|
|
||||||
constexpr inline SysregEncoding id_mmfr4_el1 = {3, 0, 0, 2, 6};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding mvfr0_el1 = {3, 0, 0, 3, 0};
|
|
||||||
constexpr inline SysregEncoding mvfr1_el1 = {3, 0, 0, 3, 1};
|
|
||||||
constexpr inline SysregEncoding mvfr2_el1 = {3, 0, 0, 3, 2};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding id_aa64pfr0_el1 = {3, 0, 0, 4, 0};
|
|
||||||
constexpr inline SysregEncoding id_aa64pfr1_el1 = {3, 0, 0, 4, 1};
|
|
||||||
constexpr inline SysregEncoding id_aa64zfr0_el1 = {3, 0, 0, 4, 4};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding id_aa64dfr0_el1 = {3, 0, 0, 5, 0};
|
|
||||||
constexpr inline SysregEncoding id_aa64dfr1_el1 = {3, 0, 0, 5, 1};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding id_aa64afr0_el1 = {3, 0, 0, 5, 4};
|
|
||||||
constexpr inline SysregEncoding id_aa64afr1_el1 = {3, 0, 0, 5, 5};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding id_aa64isar0_el1 = {3, 0, 0, 6, 0};
|
|
||||||
constexpr inline SysregEncoding id_aa64isar1_el1 = {3, 0, 0, 6, 1};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding id_aa64mmfr0_el1 = {3, 0, 0, 7, 0};
|
|
||||||
constexpr inline SysregEncoding id_aa64mmfr1_el1 = {3, 0, 0, 7, 1};
|
|
||||||
constexpr inline SysregEncoding id_aa64mmfr2_el1 = {3, 0, 0, 7, 2};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding sctlr_el1 = {3, 0, 1, 0, 0};
|
|
||||||
constexpr inline SysregEncoding actlr_el1 = {3, 0, 1, 0, 1};
|
|
||||||
constexpr inline SysregEncoding cpacr_el1 = {3, 0, 1, 0, 2};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding zcr_el1 = {3, 0, 1, 2, 0};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding ttbr0_el1 = {3, 0, 2, 0, 0};
|
|
||||||
constexpr inline SysregEncoding ttbr1_el1 = {3, 0, 2, 0, 1};
|
|
||||||
constexpr inline SysregEncoding tcr_el1 = {3, 0, 2, 0, 2};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding apiakeylo_el1 = {3, 0, 2, 1, 0};
|
|
||||||
constexpr inline SysregEncoding apiakeyhi_el1 = {3, 0, 2, 1, 1};
|
|
||||||
constexpr inline SysregEncoding apibkeylo_el1 = {3, 0, 2, 1, 2};
|
|
||||||
constexpr inline SysregEncoding apibkeyhi_el1 = {3, 0, 2, 1, 3};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding apdakeylo_el1 = {3, 0, 2, 2, 0};
|
|
||||||
constexpr inline SysregEncoding apdakeyhi_el1 = {3, 0, 2, 2, 1};
|
|
||||||
constexpr inline SysregEncoding apdbkeylo_el1 = {3, 0, 2, 2, 2};
|
|
||||||
constexpr inline SysregEncoding apdbkeyhi_el1 = {3, 0, 2, 2, 3};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding apgakeylo_el1 = {3, 0, 2, 3, 0};
|
|
||||||
constexpr inline SysregEncoding apgakeyhi_el1 = {3, 0, 2, 3, 1};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding afsr0_el1 = {3, 0, 5, 1, 0};
|
|
||||||
constexpr inline SysregEncoding afsr1_el1 = {3, 0, 5, 1, 1};
|
|
||||||
constexpr inline SysregEncoding esr_el1 = {3, 0, 5, 2, 0};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding erridr_el1 = {3, 0, 5, 3, 0};
|
|
||||||
constexpr inline SysregEncoding errselr_el1 = {3, 0, 5, 3, 1};
|
|
||||||
constexpr inline SysregEncoding erxfr_el1 = {3, 0, 5, 4, 0};
|
|
||||||
constexpr inline SysregEncoding erxctlr_el1 = {3, 0, 5, 4, 1};
|
|
||||||
constexpr inline SysregEncoding erxstatus_el1 = {3, 0, 5, 4, 2};
|
|
||||||
constexpr inline SysregEncoding erxaddr_el1 = {3, 0, 5, 4, 3};
|
|
||||||
constexpr inline SysregEncoding erxmisc0_el1 = {3, 0, 5, 5, 0};
|
|
||||||
constexpr inline SysregEncoding erxmisc1_el1 = {3, 0, 5, 5, 1};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding far_el1 = {3, 0, 6, 0, 0};
|
|
||||||
constexpr inline SysregEncoding par_el1 = {3, 0, 7, 4, 0};
|
|
||||||
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding pmsidr_el1 = {3, 0, 9, 9, 7};
|
|
||||||
constexpr inline SysregEncoding pmbidr_el1 = {3, 0, 9, 10, 7};
|
|
||||||
constexpr inline SysregEncoding pmscr_el1 = {3, 0, 9, 9, 0};
|
|
||||||
constexpr inline SysregEncoding pmscr_el2 = {3, 4, 9, 9, 0};
|
|
||||||
constexpr inline SysregEncoding pmsicr_el1 = {3, 0, 9, 9, 2};
|
|
||||||
constexpr inline SysregEncoding pmsirr_el1 = {3, 0, 9, 9, 3};
|
|
||||||
constexpr inline SysregEncoding pmsfcr_el1 = {3, 0, 9, 9, 4};
|
|
||||||
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding pmsevfr_el1 = {3, 0, 9, 9, 5};
|
|
||||||
constexpr inline SysregEncoding pmslatfr_el1 = {3, 0, 9, 9, 6};
|
|
||||||
constexpr inline SysregEncoding pmblimitr_el1 = {3, 0, 9, 10, 0};
|
|
||||||
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding pmbptr_el1 = {3, 0, 9, 10, 1};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding pmbsr_el1 = {3, 0, 9, 10, 3};
|
|
||||||
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding pmintenset_el1 = {3, 0, 9, 14, 1};
|
|
||||||
constexpr inline SysregEncoding pmintenclr_el1 = {3, 0, 9, 14, 2};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding mair_el1 = {3, 0, 10, 2, 0};
|
|
||||||
constexpr inline SysregEncoding amair_el1 = {3, 0, 10, 3, 0};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding lorsa_el1 = {3, 0, 10, 4, 0};
|
|
||||||
constexpr inline SysregEncoding lorea_el1 = {3, 0, 10, 4, 1};
|
|
||||||
constexpr inline SysregEncoding lorn_el1 = {3, 0, 10, 4, 2};
|
|
||||||
constexpr inline SysregEncoding lorc_el1 = {3, 0, 10, 4, 3};
|
|
||||||
constexpr inline SysregEncoding lorid_el1 = {3, 0, 10, 4, 7};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding vbar_el1 = {3, 0, 12, 0, 0};
|
|
||||||
constexpr inline SysregEncoding disr_el1 = {3, 0, 12, 1, 1};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding contextidr_el1 = {3, 0, 13, 0, 1};
|
|
||||||
constexpr inline SysregEncoding tpidr_el1 = {3, 0, 13, 0, 4};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding cntkctl_el1 = {3, 0, 14, 1, 0};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding ccsidr_el1 = {3, 1, 0, 0, 0};
|
|
||||||
constexpr inline SysregEncoding clidr_el1 = {3, 1, 0, 0, 1};
|
|
||||||
constexpr inline SysregEncoding aidr_el1 = {3, 1, 0, 0, 7};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding csselr_el1 = {3, 2, 0, 0, 0};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding ctr_el0 = {3, 3, 0, 0, 1};
|
|
||||||
constexpr inline SysregEncoding dczid_el0 = {3, 3, 0, 0, 7};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding pmcr_el0 = {3, 3, 9, 12, 0};
|
|
||||||
constexpr inline SysregEncoding pmcntenset_el0 = {3, 3, 9, 12, 1};
|
|
||||||
constexpr inline SysregEncoding pmcntenclr_el0 = {3, 3, 9, 12, 2};
|
|
||||||
constexpr inline SysregEncoding pmovsclr_el0 = {3, 3, 9, 12, 3};
|
|
||||||
constexpr inline SysregEncoding pmswinc_el0 = {3, 3, 9, 12, 4};
|
|
||||||
constexpr inline SysregEncoding pmselr_el0 = {3, 3, 9, 12, 5};
|
|
||||||
constexpr inline SysregEncoding pmceid0_el0 = {3, 3, 9, 12, 6};
|
|
||||||
constexpr inline SysregEncoding pmceid1_el0 = {3, 3, 9, 12, 7};
|
|
||||||
constexpr inline SysregEncoding pmccntr_el0 = {3, 3, 9, 13, 0};
|
|
||||||
constexpr inline SysregEncoding pmxevtyper_el0 = {3, 3, 9, 13, 1};
|
|
||||||
constexpr inline SysregEncoding pmxevcntr_el0 = {3, 3, 9, 13, 2};
|
|
||||||
constexpr inline SysregEncoding pmuserenr_el0 = {3, 3, 9, 14, 0};
|
|
||||||
constexpr inline SysregEncoding pmovsset_el0 = {3, 3, 9, 14, 3};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding tpidr_el0 = {3, 3, 13, 0, 2};
|
|
||||||
constexpr inline SysregEncoding tpidrro_el0 = {3, 3, 13, 0, 3};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding cntfrq_el0 = {3, 3, 14, 0, 0};
|
|
||||||
constexpr inline SysregEncoding cntpct_el0 = {3, 3, 14, 0, 1};
|
|
||||||
constexpr inline SysregEncoding cntvct_el0 = {3, 3, 14, 0, 2};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding cntp_tval_el0 = {3, 3, 14, 2, 0};
|
|
||||||
constexpr inline SysregEncoding cntp_ctl_el0 = {3, 3, 14, 2, 1};
|
|
||||||
constexpr inline SysregEncoding cntp_cval_el0 = {3, 3, 14, 2, 2};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding cntv_tval_el0 = {3, 3, 14, 3, 0};
|
|
||||||
constexpr inline SysregEncoding cntv_ctl_el0 = {3, 3, 14, 3, 1};
|
|
||||||
constexpr inline SysregEncoding cntv_cval_el0 = {3, 3, 14, 3, 2};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding cntvoff_el2 = {3, 4, 14, 0, 3};
|
|
||||||
constexpr inline SysregEncoding cnthctl_el2 = {3, 4, 14, 1, 0};
|
|
||||||
constexpr inline SysregEncoding cnthp_cval_el2 = {3, 4, 14, 2, 2};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding pmccfiltr_el0 = {3, 3, 14, 15, 7};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding zcr_el2 = {3, 4, 1, 2, 0};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding dacr32_el2 = {3, 4, 3, 0, 0};
|
|
||||||
constexpr inline SysregEncoding ifsr32_el2 = {3, 4, 5, 0, 1};
|
|
||||||
constexpr inline SysregEncoding vsesr_el2 = {3, 4, 5, 2, 3};
|
|
||||||
constexpr inline SysregEncoding fpexc32_el2 = {3, 4, 5, 3, 0};
|
|
||||||
|
|
||||||
constexpr inline SysregEncoding zcr_el12 = {3, 5, 1, 2, 0};
|
|
||||||
|
|
||||||
enum SctlrFlags {
|
|
||||||
SCTLR_ELx_DSSBS = BITL(44),
|
|
||||||
SCTLR_ELx_ENIA = BITL(31),
|
|
||||||
SCTLR_ELx_ENIB = BITL(30),
|
|
||||||
SCTLR_ELx_ENDA = BITL(27),
|
|
||||||
SCTLR_ELx_EE = BITL(25),
|
|
||||||
SCTLR_ELx_IESB = BITL(21),
|
|
||||||
SCTLR_ELx_WXN = BITL(19),
|
|
||||||
SCTLR_ELx_ENDB = BITL(13),
|
|
||||||
SCTLR_ELx_I = BITL(12),
|
|
||||||
SCTLR_ELx_SA = BITL(3),
|
|
||||||
SCTLR_ELx_C = BITL(2),
|
|
||||||
SCTLR_ELx_A = BITL(1),
|
|
||||||
SCTLR_ELx_M = BITL(0),
|
|
||||||
|
|
||||||
SCTLR_EL1_UCI = BITL(26),
|
|
||||||
SCTLR_EL1_E0E = BITL(24),
|
|
||||||
SCTLR_EL1_SPAN = BITL(23),
|
|
||||||
SCTLR_EL1_NTWE = BITL(18),
|
|
||||||
SCTLR_EL1_NTWI = BITL(16),
|
|
||||||
SCTLR_EL1_UCT = BITL(15),
|
|
||||||
SCTLR_EL1_DZE = BITL(14),
|
|
||||||
SCTLR_EL1_UMA = BITL(9),
|
|
||||||
SCTLR_EL1_SED = BITL(8),
|
|
||||||
SCTLR_EL1_ITD = BITL(7),
|
|
||||||
SCTLR_EL1_CP15BEN = BITL(5),
|
|
||||||
SCTLR_EL1_SA0 = BITL(4),
|
|
||||||
|
|
||||||
SCTLR_EL2_RES1 = util::CombineBits<u64>(29, 28, 23, 22, 18, 16, 11, 5, 4),
|
|
||||||
SCTLR_EL2_RES0 = (0xFFFFEFFFull << 32) | util::CombineBits<u64>(
|
|
||||||
31, 30, 27, 26, 24, 20, 17, 15, 14, 13, 10, 9, 8, 7, 6
|
|
||||||
),
|
|
||||||
|
|
||||||
SCTLR_EL1_RES1 = util::CombineBits<u64>(29, 28, 22, 20, 11),
|
|
||||||
SCTLR_EL1_RES0 = (0xFFFFEFFFull << 32) | util::CombineBits<u64>(31, 30, 27, 17, 13, 10, 6),
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// HCR Flags
|
|
||||||
enum HcrFlags {
|
|
||||||
HCR_FWB = BITL(46),
|
|
||||||
HCR_API = BITL(41),
|
|
||||||
HCR_APK = BITL(40),
|
|
||||||
HCR_TEA = BITL(37),
|
|
||||||
HCR_TERR = BITL(36),
|
|
||||||
HCR_TLOR = BITL(35),
|
|
||||||
HCR_E2H = BITL(34),
|
|
||||||
HCR_ID = BITL(33),
|
|
||||||
HCR_CD = BITL(32),
|
|
||||||
HCR_RW = BITL(31),
|
|
||||||
HCR_TRVM = BITL(30),
|
|
||||||
HCR_HCD = BITL(29),
|
|
||||||
HCR_TDZ = BITL(28),
|
|
||||||
HCR_TGE = BITL(27),
|
|
||||||
HCR_TVM = BITL(26),
|
|
||||||
HCR_TTLB = BITL(25),
|
|
||||||
HCR_TPU = BITL(24),
|
|
||||||
HCR_TPC = BITL(23),
|
|
||||||
HCR_TSW = BITL(22),
|
|
||||||
HCR_TAC = BITL(21),
|
|
||||||
HCR_TIDCP = BITL(20),
|
|
||||||
HCR_TSC = BITL(19),
|
|
||||||
HCR_TID3 = BITL(18),
|
|
||||||
HCR_TID2 = BITL(17),
|
|
||||||
HCR_TID1 = BITL(16),
|
|
||||||
HCR_TID0 = BITL(15),
|
|
||||||
HCR_TWE = BITL(14),
|
|
||||||
HCR_TWI = BITL(13),
|
|
||||||
HCR_DC = BITL(12),
|
|
||||||
HCR_BSU = (3ul << 10),
|
|
||||||
HCR_BSU_IS = BITL(10),
|
|
||||||
HCR_FB = BITL(9),
|
|
||||||
HCR_VSE = BITL(8),
|
|
||||||
HCR_VI = BITL(7),
|
|
||||||
HCR_VF = BITL(6),
|
|
||||||
HCR_AMO = BITL(5),
|
|
||||||
HCR_IMO = BITL(4),
|
|
||||||
HCR_FMO = BITL(3),
|
|
||||||
HCR_PTW = BITL(2),
|
|
||||||
HCR_SWI = BITL(1),
|
|
||||||
HCR_VM = BITL(0),
|
|
||||||
};
|
|
||||||
|
|
||||||
// CPTR flags
|
|
||||||
enum CptrFlags {
|
|
||||||
CPTR_TCPAC = BITL(31),
|
|
||||||
CPTR_TAM = BITL(30),
|
|
||||||
CPTR_TTA = BITL(20),
|
|
||||||
CPTR_TFP = BITL(10),
|
|
||||||
CPTR_TZ = BITL(8), // (EL2)
|
|
||||||
CPTR_EZ = BITL(8), // (EL3)
|
|
||||||
CPTR_RES1 = 0x000032FFul,
|
|
||||||
};
|
|
||||||
|
|
||||||
// MDCR flags (EL2)
|
|
||||||
enum MdcrEl2Flags {
|
|
||||||
MDCR_EL2_TPMS = BITL(14),
|
|
||||||
MDCR_EL2_E2PB_MASK = 3ul,
|
|
||||||
MDCR_EL2_E2PB_SHIFT = 12,
|
|
||||||
MDCR_EL2_TDRA = BITL(11),
|
|
||||||
MDCR_EL2_TDOSA = BITL(10),
|
|
||||||
MDCR_EL2_TDA = BITL(9),
|
|
||||||
MDCR_EL2_TDE = BITL(8),
|
|
||||||
MDCR_EL2_HPME = BITL(7),
|
|
||||||
MDCR_EL2_TPM = BITL(6),
|
|
||||||
MDCR_EL2_TPMCR = BITL(5),
|
|
||||||
MDCR_EL2_HPMN_MASK = 0x1Ful,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Some MDSCR flags
|
|
||||||
enum MdscrFlags {
|
|
||||||
MDSCR_MDE = BITL(15),
|
|
||||||
MDSCR_KDE = BITL(13),
|
|
||||||
MDSCR_SS = BITL(0),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Some CNTHCTL flags + shifts
|
|
||||||
enum CnthctlFlags {
|
|
||||||
CNTHCTL_EVNTI_MASK = 0xFul,
|
|
||||||
CNTHCTL_EVNTI_SHIFT = 4,
|
|
||||||
|
|
||||||
CNTHCTL_EVNTDIR = BITL(3),
|
|
||||||
CNTHCTL_EVNTEN = BITL(2),
|
|
||||||
CNTHCTL_EL1PCEN = BITL(1),
|
|
||||||
CNTHCTL_EL1PCTEN = BITL(0),
|
|
||||||
};
|
|
||||||
|
|
||||||
// PAR_EL1 flags, shifts, masks
|
|
||||||
enum ParFlags {
|
|
||||||
PAR_F = BITL(0),
|
|
||||||
|
|
||||||
// Successful translation:
|
|
||||||
PAR_ATTR_SHIFT = 56,
|
|
||||||
PAR_ATTR_MASK = 0xFFul,
|
|
||||||
PAR_PA_MASK = MASK2L(51, 12),// bits 51-48 RES0 if not implemented
|
|
||||||
PAR_NS = BITL(9),
|
|
||||||
PAR_SH_SHIFT = 7,
|
|
||||||
PAR_SH_MASK = 3ul,
|
|
||||||
|
|
||||||
// Faulting translation:
|
|
||||||
PAR_S = BITL(9),
|
|
||||||
PAR_PTW = BITL(8),
|
|
||||||
PAR_FST_SHIFT = 1,
|
|
||||||
PAR_FST_MASK = 0x3Ful,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Some (S)PSR flags, masks, shifts
|
|
||||||
enum PsrFlags {
|
|
||||||
PSR_AA32_IT10_SHIFT = 25,
|
|
||||||
PSR_AA32_IT10_MASK = 3ul,
|
|
||||||
|
|
||||||
PSR_SS = BITL(21),
|
|
||||||
|
|
||||||
PSR_AA32_IT72_SHIFT = 10,
|
|
||||||
PSR_AA32_IT72_MASK = 0x3Ful,
|
|
||||||
|
|
||||||
PSR_DAIF_SHIFT = 6,
|
|
||||||
PSR_D = BITL(9),
|
|
||||||
PSR_A = BITL(8),
|
|
||||||
PSR_I = BITL(7),
|
|
||||||
PSR_F = BITL(6),
|
|
||||||
|
|
||||||
PSR_AA32_THUMB = BITL(5),
|
|
||||||
PSR_MODE32 = BITL(4),
|
|
||||||
|
|
||||||
PSR_EL_SHIFT = 2,
|
|
||||||
PSR_EL_MASK = 3ul,
|
|
||||||
|
|
||||||
PSR_SP_ELX = BITL(0),
|
|
||||||
};
|
|
||||||
|
|
||||||
// cnt*_ctl flags
|
|
||||||
enum CntCtlFlags {
|
|
||||||
CNTCTL_ISTATUS = BITL(2),
|
|
||||||
CNTCTL_IMASK = BITL(1),
|
|
||||||
CNTCTL_ENABLE = BITL(0),
|
|
||||||
};
|
|
||||||
|
|
||||||
// TCR_ELx flags
|
|
||||||
enum TcrFlags {
|
|
||||||
TCR_IRGN_NC = (0 << 8),
|
|
||||||
TCR_IRGN_WBWA = (1 << 8),
|
|
||||||
TCR_IRGN_WT = (2 << 8),
|
|
||||||
TCR_IRGN_WBNWA = (3 << 8),
|
|
||||||
TCR_IRGN_MASK = (3 << 8),
|
|
||||||
TCR_ORGN_NC = (0 << 10),
|
|
||||||
TCR_ORGN_WBWA = (1 << 10),
|
|
||||||
TCR_ORGN_WT = (2 << 10),
|
|
||||||
TCR_ORGN_WBNWA = (3 << 10),
|
|
||||||
TCR_ORGN_MASK = (3 << 10),
|
|
||||||
TCR_NOT_SHARED = (0 << 12),
|
|
||||||
TCR_SHARED_OUTER = (2 << 12),
|
|
||||||
TCR_SHARED_INNER = (3 << 12),
|
|
||||||
TCR_EPD1_DISABLE = BITL(23),
|
|
||||||
|
|
||||||
TCR_EL1_RSVD = BITL(31),
|
|
||||||
TCR_EL2_RSVD = (BITL(31) | BITL(23)),
|
|
||||||
VTCR_EL2_RSVD = BITL(31),
|
|
||||||
TCR_EL3_RSVD = (BITL(31) | BITL(23)),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Could have used enum class here, but can't start identifiers with a digit...
|
|
||||||
enum TranslationGranuleSize : u64 {
|
|
||||||
TranslationGranule_4K = 0,
|
|
||||||
TranslationGranule_64K = 1,
|
|
||||||
TranslationGranule_16K = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr size_t GetTranslationGranuleBitSize(TranslationGranuleSize granuleSize)
|
|
||||||
{
|
|
||||||
switch (granuleSize) {
|
|
||||||
case TranslationGranule_4K: return 12;
|
|
||||||
case TranslationGranule_64K: return 16;
|
|
||||||
case TranslationGranule_16K: return 14;
|
|
||||||
default: return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr u64 TCR_TG0(TranslationGranuleSize granuleSize)
|
|
||||||
{
|
|
||||||
return (granuleSize & 3) << 14;
|
|
||||||
}
|
|
||||||
constexpr u64 TCR_T0SZ(size_t addressSpaceSize) { return (64ul - (addressSpaceSize & 0x3F)) << 0; }
|
|
||||||
constexpr u64 TCR_PS(u64 n) { return (n & 7) << 16; }
|
|
||||||
constexpr u64 VTCR_SL0(u64 n) { return (n & 3) << 6; }
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2019 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 <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "debug_log.h"
|
|
||||||
#include "platform/uart.h"
|
|
||||||
#include "semihosting.h"
|
|
||||||
#include "utils.h"
|
|
||||||
#include "transport_interface.h"
|
|
||||||
#include "platform/uart.h"
|
|
||||||
|
|
||||||
#ifndef DLOG_USE_SEMIHOSTING_WRITE0
|
|
||||||
#define DLOG_USE_SEMIHOSTING_WRITE0 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static TransportInterface *g_debugLogTransportInterface;
|
|
||||||
|
|
||||||
void debugLogInit(void)
|
|
||||||
{
|
|
||||||
if (!DLOG_USE_SEMIHOSTING_WRITE0) {
|
|
||||||
transportInterfaceCreate(TRANSPORT_INTERFACE_TYPE_UART, DEFAULT_UART, DEFAULT_UART_FLAGS, NULL, NULL, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void debugLogRaw(const char *str)
|
|
||||||
{
|
|
||||||
// Use semihosting if available (we assume qemu was launched with -semihosting), otherwise UART
|
|
||||||
if (DLOG_USE_SEMIHOSTING_WRITE0 && semihosting_connection_supported()) {
|
|
||||||
semihosting_write_string(str);
|
|
||||||
} else {
|
|
||||||
transportInterfaceWriteData(g_debugLogTransportInterface, str, strlen(str));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: UNSAFE!
|
|
||||||
int debugLog(const char *fmt, ...)
|
|
||||||
{
|
|
||||||
char buf[128];
|
|
||||||
va_list args;
|
|
||||||
va_start(args, fmt);
|
|
||||||
int res = vsprintf(buf, fmt, args);
|
|
||||||
va_end(args);
|
|
||||||
|
|
||||||
debugLogRaw(buf);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2019 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 <stdarg.h>
|
|
||||||
|
|
||||||
#ifndef NDEBUG
|
|
||||||
#define DEBUG(...) debugLog(__VA_ARGS__)
|
|
||||||
#define DEBUGRAW(str) debugLogRaw(str)
|
|
||||||
#else
|
|
||||||
#define DEBUG(...)
|
|
||||||
#define DEBUGRAW(str)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void debugLogInit(void);
|
|
||||||
void debugLogRaw(const char *str);
|
|
||||||
int debugLog(const char *fmt, ...);
|
|
||||||
@@ -1,263 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2019 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 <stdatomic.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "debug_manager.h"
|
|
||||||
#include "core_ctx.h"
|
|
||||||
#include "irq.h"
|
|
||||||
#include "spinlock.h"
|
|
||||||
#include "single_step.h"
|
|
||||||
|
|
||||||
#include "gdb/debug.h"
|
|
||||||
|
|
||||||
GDBContext g_gdbContext = { 0 };
|
|
||||||
|
|
||||||
typedef struct DebugManager {
|
|
||||||
DebugEventInfo debugEventInfos[MAX_CORE];
|
|
||||||
uintptr_t steppingRangeStartAddrs[MAX_CORE];
|
|
||||||
uintptr_t steppingRangeEndAddrs[MAX_CORE];
|
|
||||||
|
|
||||||
ALIGN(64) atomic_uint pausedCoreList;
|
|
||||||
atomic_uint singleStepCoreList;
|
|
||||||
atomic_uint eventsSentList;
|
|
||||||
Barrier pauseBarrier;
|
|
||||||
atomic_bool reportingEnabled;
|
|
||||||
} DebugManager;
|
|
||||||
|
|
||||||
static DebugManager g_debugManager = { 0 };
|
|
||||||
|
|
||||||
static void debugManagerDoPauseCores(u32 coreList)
|
|
||||||
{
|
|
||||||
__builtin_prefetch(&g_debugManager.pausedCoreList, 1, 0);
|
|
||||||
|
|
||||||
u32 desiredList = coreList;
|
|
||||||
u32 remainingList = coreList;
|
|
||||||
u32 readList = atomic_load(&g_debugManager.pausedCoreList);
|
|
||||||
do {
|
|
||||||
desiredList |= readList;
|
|
||||||
remainingList &= ~readList;
|
|
||||||
} while (!atomic_compare_exchange_weak(&g_debugManager.pausedCoreList, &readList, desiredList));
|
|
||||||
|
|
||||||
if (remainingList & ~BIT(currentCoreCtx->coreId)) {
|
|
||||||
// We need to notify other cores...
|
|
||||||
u32 otherCores = remainingList & ~BIT(currentCoreCtx->coreId);
|
|
||||||
barrierInit(&g_debugManager.pauseBarrier, otherCores | BIT(currentCoreCtx->coreId));
|
|
||||||
generateSgiForList(ThermosphereSgi_DebugPause, otherCores);
|
|
||||||
barrierWait(&g_debugManager.pauseBarrier);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remainingList & BIT(currentCoreCtx->coreId)) {
|
|
||||||
currentCoreCtx->wasPaused = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
__sev();
|
|
||||||
}
|
|
||||||
|
|
||||||
void debugManagerPauseSgiHandler(void)
|
|
||||||
{
|
|
||||||
currentCoreCtx->wasPaused = true;
|
|
||||||
barrierWait(&g_debugManager.pauseBarrier);
|
|
||||||
}
|
|
||||||
|
|
||||||
void debugManagerInit(TransportInterfaceType gdbIfaceType, u32 gdbIfaceId, u32 gdbIfaceFlags)
|
|
||||||
{
|
|
||||||
memset(&g_debugManager, 0, sizeof(DebugManager));
|
|
||||||
GDB_InitializeContext(&g_gdbContext, gdbIfaceType, gdbIfaceId, gdbIfaceFlags);
|
|
||||||
GDB_MigrateRxIrq(&g_gdbContext, currentCoreCtx->coreId);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool debugManagerHandlePause(void)
|
|
||||||
{
|
|
||||||
u32 coreId = currentCoreCtx->coreId;
|
|
||||||
__builtin_prefetch(&g_debugManager.pausedCoreList, 0, 3);
|
|
||||||
|
|
||||||
if (atomic_load(&g_debugManager.pausedCoreList) & BIT(coreId)) {
|
|
||||||
unmaskIrq();
|
|
||||||
do {
|
|
||||||
__wfe();
|
|
||||||
} while (atomic_load(&g_debugManager.pausedCoreList) & BIT(coreId));
|
|
||||||
maskIrq();
|
|
||||||
|
|
||||||
if (!g_debugManager.debugEventInfos[coreId].handled) {
|
|
||||||
// Do we still have an unhandled debug event?
|
|
||||||
GDB_TrySignalDebugEvent(&g_gdbContext, &g_debugManager.debugEventInfos[coreId]);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
currentCoreCtx->wasPaused = false;
|
|
||||||
|
|
||||||
// Single-step: if inactive and requested, start single step; cancel if active and not requested
|
|
||||||
u32 ssReqd = (atomic_load(&g_debugManager.singleStepCoreList) & BIT(currentCoreCtx->coreId)) != 0;
|
|
||||||
SingleStepState singleStepState = singleStepGetNextState(currentCoreCtx->guestFrame);
|
|
||||||
if (ssReqd) {
|
|
||||||
currentCoreCtx->steppingRangeStartAddr = g_debugManager.steppingRangeStartAddrs[coreId];
|
|
||||||
currentCoreCtx->steppingRangeEndAddr = g_debugManager.steppingRangeEndAddrs[coreId];
|
|
||||||
if(singleStepState == SingleStepState_Inactive) {
|
|
||||||
singleStepSetNextState(currentCoreCtx->guestFrame, SingleStepState_ActiveNotPending);
|
|
||||||
}
|
|
||||||
} else if (!ssReqd && singleStepState != SingleStepState_Inactive) {
|
|
||||||
singleStepSetNextState(currentCoreCtx->guestFrame, SingleStepState_Inactive);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void debugManagerSetReportingEnabled(bool enabled)
|
|
||||||
{
|
|
||||||
atomic_store(&g_debugManager.reportingEnabled, enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool debugManagerHasDebugEvent(u32 coreId)
|
|
||||||
{
|
|
||||||
bool isPaused = debugManagerIsCorePaused(coreId);
|
|
||||||
return isPaused && g_debugManager.debugEventInfos[coreId].type != DBGEVENT_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void debugManagerPauseCores(u32 coreList)
|
|
||||||
{
|
|
||||||
u64 flags = maskIrq();
|
|
||||||
debugManagerDoPauseCores(coreList);
|
|
||||||
restoreInterruptFlags(flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
void debugManagerSetSingleStepCoreList(u32 coreList)
|
|
||||||
{
|
|
||||||
atomic_store(&g_debugManager.singleStepCoreList, coreList);
|
|
||||||
}
|
|
||||||
|
|
||||||
void debugManagerUnpauseCores(u32 coreList)
|
|
||||||
{
|
|
||||||
FOREACH_BIT (tmp, coreId, coreList) {
|
|
||||||
if (&g_debugManager.debugEventInfos[coreId].handled) {
|
|
||||||
// Discard already handled debug events
|
|
||||||
g_debugManager.debugEventInfos[coreId].type = DBGEVENT_NONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
atomic_fetch_and(&g_debugManager.pausedCoreList, ~coreList);
|
|
||||||
|
|
||||||
__sev();
|
|
||||||
}
|
|
||||||
|
|
||||||
void debugManagerSetSteppingRange(u32 coreId, uintptr_t startAddr, uintptr_t endAddr)
|
|
||||||
{
|
|
||||||
g_debugManager.steppingRangeStartAddrs[coreId] = startAddr;
|
|
||||||
g_debugManager.steppingRangeEndAddrs[coreId] = endAddr;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 debugManagerGetPausedCoreList(void)
|
|
||||||
{
|
|
||||||
return atomic_load(&g_debugManager.pausedCoreList);
|
|
||||||
}
|
|
||||||
|
|
||||||
DebugEventInfo *debugManagerGetDebugEvent(u32 coreId)
|
|
||||||
{
|
|
||||||
return &g_debugManager.debugEventInfos[coreId];
|
|
||||||
}
|
|
||||||
|
|
||||||
void debugManagerReportEvent(DebugEventType type, ...)
|
|
||||||
{
|
|
||||||
u64 flags = maskIrq();
|
|
||||||
bool reportingEnabled = atomic_load(&g_debugManager.reportingEnabled);
|
|
||||||
if (!reportingEnabled && type != DBGEVENT_DEBUGGER_BREAK) {
|
|
||||||
restoreInterruptFlags(flags);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 coreId = currentCoreCtx->coreId;
|
|
||||||
|
|
||||||
DebugEventInfo *info = &g_debugManager.debugEventInfos[coreId];
|
|
||||||
memset(info, 0 , sizeof(DebugEventInfo));
|
|
||||||
|
|
||||||
info->type = type;
|
|
||||||
info->coreId = coreId;
|
|
||||||
info->frame = currentCoreCtx->guestFrame;
|
|
||||||
|
|
||||||
va_list args;
|
|
||||||
va_start(args, type);
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case DBGEVENT_OUTPUT_STRING:
|
|
||||||
info->outputString.address = va_arg(args, uintptr_t);
|
|
||||||
info->outputString.size = va_arg(args, size_t);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
va_end(args);
|
|
||||||
|
|
||||||
// Now, pause ourselves and try to signal we have a debug event
|
|
||||||
debugManagerDoPauseCores(BIT(coreId));
|
|
||||||
|
|
||||||
if (reportingEnabled) {
|
|
||||||
exceptionEnterInterruptibleHypervisorCode();
|
|
||||||
unmaskIrq();
|
|
||||||
|
|
||||||
GDB_TrySignalDebugEvent(&g_gdbContext, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
restoreInterruptFlags(flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
void debugManagerBreakCores(u32 coreList)
|
|
||||||
{
|
|
||||||
u32 coreId = currentCoreCtx->coreId;
|
|
||||||
if (coreList & ~BIT(coreId)) {
|
|
||||||
generateSgiForList(ThermosphereSgi_ReportDebuggerBreak, coreList & ~BIT(coreId));
|
|
||||||
}
|
|
||||||
if ((coreList & BIT(coreId)) && !debugManagerHasDebugEvent(coreId)) {
|
|
||||||
debugManagerReportEvent(DBGEVENT_DEBUGGER_BREAK);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for all cores
|
|
||||||
__sevl();
|
|
||||||
do {
|
|
||||||
__wfe();
|
|
||||||
} while ((atomic_load(&g_debugManager.pausedCoreList) & coreList) != coreList);
|
|
||||||
}
|
|
||||||
|
|
||||||
void debugManagerContinueCores(u32 coreList)
|
|
||||||
{
|
|
||||||
u32 coreId = currentCoreCtx->coreId;
|
|
||||||
if (coreList & ~BIT(coreId)) {
|
|
||||||
generateSgiForList(ThermosphereSgi_DebuggerContinue, coreList & ~BIT(coreId));
|
|
||||||
}
|
|
||||||
if (coreList & BIT(coreId) && debugManagerIsCorePaused(coreId)) {
|
|
||||||
debugManagerUnpauseCores(BIT(coreId));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for all cores
|
|
||||||
__sevl();
|
|
||||||
do {
|
|
||||||
__wfe();
|
|
||||||
} while ((atomic_load(&g_debugManager.pausedCoreList) & coreList) != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* u64 mdcr = GET_SYSREG(mdcr_el2);
|
|
||||||
|
|
||||||
// Trap Debug Exceptions, and accesses to debug registers.
|
|
||||||
mdcr |= MDCR_EL2_TDE;
|
|
||||||
|
|
||||||
// Implied from TDE
|
|
||||||
mdcr |= MDCR_EL2_TDRA | MDCR_EL2_TDOSA | MDCR_EL2_TDA;
|
|
||||||
|
|
||||||
SET_SYSREG(mdcr_el2, mdcr);
|
|
||||||
*/
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user