mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-12-28 21:26:14 +00:00
os: implement waitable management.
This implements waitable management for Events (and implements Events). It also refactors PM to use new Event/Waitable semantics, and also adds STS_ASSERT as a macro for asserting a boolean expression. The rest of stratosphere has been refactored to use STS_ASSERT whenever possible.
This commit is contained in:
parent
e07011be32
commit
609a302e16
108 changed files with 2752 additions and 1223 deletions
|
@ -31,10 +31,8 @@ Result FsDirUtils::CopyFile(IFileSystem *dst_fs, IFileSystem *src_fs, const FsPa
|
||||||
/* Create and open destination file. */
|
/* Create and open destination file. */
|
||||||
{
|
{
|
||||||
FsPath dst_path;
|
FsPath dst_path;
|
||||||
if (static_cast<size_t>(snprintf(dst_path.str, sizeof(dst_path.str), "%s%s", dst_parent_path.str, dir_ent->name)) >= sizeof(dst_path)) {
|
/* TODO: Error code? N aborts here. */
|
||||||
/* TODO: Error code? N aborts here. */
|
STS_ASSERT(static_cast<size_t>(snprintf(dst_path.str, sizeof(dst_path.str), "%s%s", dst_parent_path.str, dir_ent->name)) < sizeof(dst_path));
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
R_TRY(dst_fs->CreateFile(dst_path, file_size));
|
R_TRY(dst_fs->CreateFile(dst_path, file_size));
|
||||||
R_TRY(dst_fs->OpenFile(dst_file, dst_path, OpenMode_Write));
|
R_TRY(dst_fs->OpenFile(dst_file, dst_path, OpenMode_Write));
|
||||||
|
|
|
@ -30,20 +30,14 @@ static char *GetNormalizedDirectory(const char *dir_prefix) {
|
||||||
|
|
||||||
/* Ensure terminating '/' */
|
/* Ensure terminating '/' */
|
||||||
if (normal_path[normal_path_len-1] != '/') {
|
if (normal_path[normal_path_len-1] != '/') {
|
||||||
if (normal_path_len + 2 > sizeof(normal_path)) {
|
STS_ASSERT(normal_path_len + 2 <= sizeof(normal_path));
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
strncat(normal_path, "/", 2);
|
strncat(normal_path, "/", 2);
|
||||||
normal_path[sizeof(normal_path)-1] = 0;
|
normal_path[sizeof(normal_path)-1] = 0;
|
||||||
normal_path_len++;
|
normal_path_len++;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *output = static_cast<char *>(malloc(normal_path_len + 1));
|
char *output = static_cast<char *>(std::malloc(normal_path_len + 1));
|
||||||
if (output == nullptr) {
|
STS_ASSERT(output != nullptr);
|
||||||
std::abort();
|
|
||||||
/* TODO: Result error code? */
|
|
||||||
}
|
|
||||||
|
|
||||||
std::strncpy(output, normal_path, normal_path_len + 1);
|
std::strncpy(output, normal_path, normal_path_len + 1);
|
||||||
output[normal_path_len] = 0;
|
output[normal_path_len] = 0;
|
||||||
|
|
|
@ -261,9 +261,7 @@ Result FsPathUtils::Normalize(char *out, size_t max_out_size, const char *src, s
|
||||||
/* Assert normalized. */
|
/* Assert normalized. */
|
||||||
bool normalized = false;
|
bool normalized = false;
|
||||||
R_ASSERT(FsPathUtils::IsNormalized(&normalized, out));
|
R_ASSERT(FsPathUtils::IsNormalized(&normalized, out));
|
||||||
if (!normalized) {
|
STS_ASSERT(normalized);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,9 +34,7 @@ Result SubDirectoryFileSystem::Initialize(const char *bp) {
|
||||||
|
|
||||||
/* Ensure terminating '/' */
|
/* Ensure terminating '/' */
|
||||||
if (normal_path[normal_path_len-1] != '/') {
|
if (normal_path[normal_path_len-1] != '/') {
|
||||||
if (normal_path_len + 2 > sizeof(normal_path)) {
|
STS_ASSERT(normal_path_len + 2 <= sizeof(normal_path));
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
strncat(normal_path, "/", 2);
|
strncat(normal_path, "/", 2);
|
||||||
normal_path[sizeof(normal_path)-1] = 0;
|
normal_path[sizeof(normal_path)-1] = 0;
|
||||||
|
|
|
@ -93,9 +93,7 @@ Result LayeredRomFS::Read(void *buffer, size_t size, u64 offset) {
|
||||||
R_ASSERT(Utils::OpenSdFileForAtmosphere(this->title_id, ROMFS_METADATA_FILE_PATH, FS_OPEN_READ, &file));
|
R_ASSERT(Utils::OpenSdFileForAtmosphere(this->title_id, ROMFS_METADATA_FILE_PATH, FS_OPEN_READ, &file));
|
||||||
size_t out_read;
|
size_t out_read;
|
||||||
R_ASSERT(fsFileRead(&file, (offset - cur_source->virtual_offset), (void *)((uintptr_t)buffer + read_so_far), cur_read_size, FS_READOPTION_NONE, &out_read));
|
R_ASSERT(fsFileRead(&file, (offset - cur_source->virtual_offset), (void *)((uintptr_t)buffer + read_so_far), cur_read_size, FS_READOPTION_NONE, &out_read));
|
||||||
if (out_read != cur_read_size) {
|
STS_ASSERT(out_read == cur_read_size);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
fsFileClose(&file);
|
fsFileClose(&file);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -105,9 +103,7 @@ Result LayeredRomFS::Read(void *buffer, size_t size, u64 offset) {
|
||||||
R_ASSERT(Utils::OpenRomFSSdFile(this->title_id, cur_source->loose_source_info.path, FS_OPEN_READ, &file));
|
R_ASSERT(Utils::OpenRomFSSdFile(this->title_id, cur_source->loose_source_info.path, FS_OPEN_READ, &file));
|
||||||
size_t out_read;
|
size_t out_read;
|
||||||
R_ASSERT(fsFileRead(&file, (offset - cur_source->virtual_offset), (void *)((uintptr_t)buffer + read_so_far), cur_read_size, FS_READOPTION_NONE, &out_read));
|
R_ASSERT(fsFileRead(&file, (offset - cur_source->virtual_offset), (void *)((uintptr_t)buffer + read_so_far), cur_read_size, FS_READOPTION_NONE, &out_read));
|
||||||
if (out_read != cur_read_size) {
|
STS_ASSERT(out_read == cur_read_size);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
fsFileClose(&file);
|
fsFileClose(&file);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -39,6 +39,7 @@ void RomFSBuildContext::VisitDirectory(FsFileSystem *filesys, RomFSBuildDirector
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
STS_ASSERT(this->dir_entry.type == ENTRYTYPE_DIR || this->dir_entry.type == ENTRYTYPE_FILE);
|
||||||
if (this->dir_entry.type == ENTRYTYPE_DIR) {
|
if (this->dir_entry.type == ENTRYTYPE_DIR) {
|
||||||
RomFSBuildDirectoryContext *child = new RomFSBuildDirectoryContext({0});
|
RomFSBuildDirectoryContext *child = new RomFSBuildDirectoryContext({0});
|
||||||
/* Set child's path. */
|
/* Set child's path. */
|
||||||
|
@ -58,7 +59,7 @@ void RomFSBuildContext::VisitDirectory(FsFileSystem *filesys, RomFSBuildDirector
|
||||||
} else {
|
} else {
|
||||||
child_dirs.push_back(child);
|
child_dirs.push_back(child);
|
||||||
}
|
}
|
||||||
} else if (this->dir_entry.type == ENTRYTYPE_FILE) {
|
} else /* if (this->dir_entry.type == ENTRYTYPE_FILE) */ {
|
||||||
RomFSBuildFileContext *child = new RomFSBuildFileContext({0});
|
RomFSBuildFileContext *child = new RomFSBuildFileContext({0});
|
||||||
/* Set child's path. */
|
/* Set child's path. */
|
||||||
child->cur_path_ofs = parent->path_len + 1;
|
child->cur_path_ofs = parent->path_len + 1;
|
||||||
|
@ -79,8 +80,6 @@ void RomFSBuildContext::VisitDirectory(FsFileSystem *filesys, RomFSBuildDirector
|
||||||
delete[] child->path;
|
delete[] child->path;
|
||||||
delete child;
|
delete child;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
std::abort();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,10 +179,7 @@ void RomFSBuildContext::VisitDirectory(RomFSBuildDirectoryContext *parent, u32 p
|
||||||
void RomFSBuildContext::MergeRomStorage(IROStorage *storage, RomFSDataSource source) {
|
void RomFSBuildContext::MergeRomStorage(IROStorage *storage, RomFSDataSource source) {
|
||||||
RomFSHeader header;
|
RomFSHeader header;
|
||||||
R_ASSERT(storage->Read(&header, sizeof(header), 0));
|
R_ASSERT(storage->Read(&header, sizeof(header), 0));
|
||||||
if (header.header_size != sizeof(header)) {
|
STS_ASSERT(header.header_size == sizeof(header));
|
||||||
/* what */
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read tables. */
|
/* Read tables. */
|
||||||
auto dir_table = std::make_unique<u8[]>(header.dir_table_size);
|
auto dir_table = std::make_unique<u8[]>(header.dir_table_size);
|
||||||
|
|
|
@ -40,16 +40,11 @@ void VersionManager::Initialize() {
|
||||||
|
|
||||||
/* Firmware version file must exist. */
|
/* Firmware version file must exist. */
|
||||||
FILE *f = fopen("sysver:/file", "rb");
|
FILE *f = fopen("sysver:/file", "rb");
|
||||||
if (f == NULL) {
|
STS_ASSERT(f != NULL);
|
||||||
std::abort();
|
ON_SCOPE_EXIT { fclose(f); };
|
||||||
}
|
|
||||||
|
|
||||||
/* Must be possible to read firmware version from file. */
|
/* Must be possible to read firmware version from file. */
|
||||||
if (fread(&fw_ver, sizeof(fw_ver), 1, f) != 1) {
|
STS_ASSERT(fread(&fw_ver, sizeof(fw_ver), 1, f) == 1);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
g_fw_version = fw_ver;
|
g_fw_version = fw_ver;
|
||||||
g_ams_fw_version = fw_ver;
|
g_ams_fw_version = fw_ver;
|
||||||
|
|
|
@ -75,26 +75,16 @@ namespace sts::boot {
|
||||||
/* Get values from PMIC. */
|
/* Get values from PMIC. */
|
||||||
{
|
{
|
||||||
PmicDriver pmic_driver;
|
PmicDriver pmic_driver;
|
||||||
if (R_FAILED(pmic_driver.GetPowerIntr(&power_intr))) {
|
R_ASSERT(pmic_driver.GetPowerIntr(&power_intr));
|
||||||
std::abort();
|
R_ASSERT(pmic_driver.GetNvErc(&nv_erc));
|
||||||
}
|
R_ASSERT(pmic_driver.GetAcOk(&ac_ok));
|
||||||
if (R_FAILED(pmic_driver.GetNvErc(&nv_erc))) {
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
if (R_FAILED(pmic_driver.GetAcOk(&ac_ok))) {
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get values from RTC. */
|
/* Get values from RTC. */
|
||||||
{
|
{
|
||||||
RtcDriver rtc_driver;
|
RtcDriver rtc_driver;
|
||||||
if (R_FAILED(rtc_driver.GetRtcIntr(&rtc_intr))) {
|
R_ASSERT(rtc_driver.GetRtcIntr(&rtc_intr));
|
||||||
std::abort();
|
R_ASSERT(rtc_driver.GetRtcIntrM(&rtc_intr_m));
|
||||||
}
|
|
||||||
if (R_FAILED(rtc_driver.GetRtcIntrM(&rtc_intr_m))) {
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set global derived boot reason. */
|
/* Set global derived boot reason. */
|
||||||
|
@ -107,19 +97,14 @@ namespace sts::boot {
|
||||||
boot_reason_value.rtc_intr = rtc_intr & ~rtc_intr_m;
|
boot_reason_value.rtc_intr = rtc_intr & ~rtc_intr_m;
|
||||||
boot_reason_value.nv_erc = nv_erc;
|
boot_reason_value.nv_erc = nv_erc;
|
||||||
boot_reason_value.boot_reason = g_boot_reason;
|
boot_reason_value.boot_reason = g_boot_reason;
|
||||||
if (R_FAILED(splSetBootReason(boot_reason_value.value))) {
|
R_ASSERT(splSetBootReason(boot_reason_value.value));
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g_detected_boot_reason = true;
|
g_detected_boot_reason = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetBootReason() {
|
u32 GetBootReason() {
|
||||||
if (!g_detected_boot_reason) {
|
STS_ASSERT(g_detected_boot_reason);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
return g_boot_reason;
|
return g_boot_reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,9 +38,9 @@ namespace sts::boot::bq24193 {
|
||||||
constexpr u32 ChargeVoltageLimitMax = 4208;
|
constexpr u32 ChargeVoltageLimitMax = 4208;
|
||||||
|
|
||||||
inline u8 EncodeChargeVoltageLimit(u32 voltage) {
|
inline u8 EncodeChargeVoltageLimit(u32 voltage) {
|
||||||
if (voltage < ChargeVoltageLimitMin || voltage > ChargeVoltageLimitMax) {
|
STS_ASSERT(voltage >= ChargeVoltageLimitMin);
|
||||||
std::abort();
|
STS_ASSERT(voltage <= ChargeVoltageLimitMax);
|
||||||
}
|
|
||||||
voltage -= ChargeVoltageLimitMin;
|
voltage -= ChargeVoltageLimitMin;
|
||||||
voltage >>= 4;
|
voltage >>= 4;
|
||||||
return static_cast<u8>(voltage << 2);
|
return static_cast<u8>(voltage << 2);
|
||||||
|
@ -54,9 +54,9 @@ namespace sts::boot::bq24193 {
|
||||||
constexpr u32 FastChargeCurrentLimitMax = 4544;
|
constexpr u32 FastChargeCurrentLimitMax = 4544;
|
||||||
|
|
||||||
inline u8 EncodeFastChargeCurrentLimit(u32 current) {
|
inline u8 EncodeFastChargeCurrentLimit(u32 current) {
|
||||||
if (current < FastChargeCurrentLimitMin || current > FastChargeCurrentLimitMax) {
|
STS_ASSERT(current >= FastChargeCurrentLimitMin);
|
||||||
std::abort();
|
STS_ASSERT(current <= FastChargeCurrentLimitMax);
|
||||||
}
|
|
||||||
current -= FastChargeCurrentLimitMin;
|
current -= FastChargeCurrentLimitMin;
|
||||||
current >>= 6;
|
current >>= 6;
|
||||||
return static_cast<u8>(current << 2);
|
return static_cast<u8>(current << 2);
|
||||||
|
@ -81,9 +81,9 @@ namespace sts::boot::bq24193 {
|
||||||
constexpr u32 PreChargeCurrentLimitMax = 2048;
|
constexpr u32 PreChargeCurrentLimitMax = 2048;
|
||||||
|
|
||||||
inline u8 EncodePreChargeCurrentLimit(u32 current) {
|
inline u8 EncodePreChargeCurrentLimit(u32 current) {
|
||||||
if (current < PreChargeCurrentLimitMin || current > PreChargeCurrentLimitMax) {
|
STS_ASSERT(current >= PreChargeCurrentLimitMin);
|
||||||
std::abort();
|
STS_ASSERT(current <= PreChargeCurrentLimitMax);
|
||||||
}
|
|
||||||
current -= PreChargeCurrentLimitMin;
|
current -= PreChargeCurrentLimitMin;
|
||||||
current >>= 7;
|
current >>= 7;
|
||||||
return static_cast<u8>(current << 4);
|
return static_cast<u8>(current << 4);
|
||||||
|
@ -97,9 +97,9 @@ namespace sts::boot::bq24193 {
|
||||||
constexpr u32 TerminationCurrentLimitMax = 2048;
|
constexpr u32 TerminationCurrentLimitMax = 2048;
|
||||||
|
|
||||||
inline u8 EncodeTerminationCurrentLimit(u32 current) {
|
inline u8 EncodeTerminationCurrentLimit(u32 current) {
|
||||||
if (current < TerminationCurrentLimitMin || current > TerminationCurrentLimitMax) {
|
STS_ASSERT(current >= TerminationCurrentLimitMin);
|
||||||
std::abort();
|
STS_ASSERT(current <= TerminationCurrentLimitMax);
|
||||||
}
|
|
||||||
current -= TerminationCurrentLimitMin;
|
current -= TerminationCurrentLimitMin;
|
||||||
current >>= 7;
|
current >>= 7;
|
||||||
return static_cast<u8>(current);
|
return static_cast<u8>(current);
|
||||||
|
@ -113,9 +113,9 @@ namespace sts::boot::bq24193 {
|
||||||
constexpr u32 MinimumSystemVoltageLimitMax = 3700;
|
constexpr u32 MinimumSystemVoltageLimitMax = 3700;
|
||||||
|
|
||||||
inline u8 EncodeMinimumSystemVoltageLimit(u32 voltage) {
|
inline u8 EncodeMinimumSystemVoltageLimit(u32 voltage) {
|
||||||
if (voltage < MinimumSystemVoltageLimitMin || voltage > MinimumSystemVoltageLimitMax) {
|
STS_ASSERT(voltage >= MinimumSystemVoltageLimitMin);
|
||||||
std::abort();
|
STS_ASSERT(voltage <= MinimumSystemVoltageLimitMax);
|
||||||
}
|
|
||||||
voltage -= MinimumSystemVoltageLimitMin;
|
voltage -= MinimumSystemVoltageLimitMin;
|
||||||
voltage /= 100;
|
voltage /= 100;
|
||||||
return static_cast<u8>(voltage << 1);
|
return static_cast<u8>(voltage << 1);
|
||||||
|
|
|
@ -38,9 +38,7 @@ namespace sts::boot {
|
||||||
0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400
|
0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400
|
||||||
};
|
};
|
||||||
|
|
||||||
if (data == nullptr) {
|
STS_ASSERT(data != nullptr);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
u16 crc16 = 0x55AA;
|
u16 crc16 = 0x55AA;
|
||||||
const u8 *data_u8 = reinterpret_cast<const u8 *>(data);
|
const u8 *data_u8 = reinterpret_cast<const u8 *>(data);
|
||||||
|
|
|
@ -103,12 +103,15 @@ namespace sts::boot {
|
||||||
|
|
||||||
inline void DoDsiSleepOrRegisterWrites(const DsiSleepOrRegisterWrite *reg_writes, size_t num_writes) {
|
inline void DoDsiSleepOrRegisterWrites(const DsiSleepOrRegisterWrite *reg_writes, size_t num_writes) {
|
||||||
for (size_t i = 0; i < num_writes; i++) {
|
for (size_t i = 0; i < num_writes; i++) {
|
||||||
if (reg_writes[i].kind == DsiSleepOrRegisterWriteKind_Write) {
|
switch (reg_writes[i].kind) {
|
||||||
reg::Write(g_dsi_regs + sizeof(u32) * reg_writes[i].offset, reg_writes[i].value);
|
case DsiSleepOrRegisterWriteKind_Write:
|
||||||
} else if (reg_writes[i].kind == DsiSleepOrRegisterWriteKind_Sleep) {
|
reg::Write(g_dsi_regs + sizeof(u32) * reg_writes[i].offset, reg_writes[i].value);
|
||||||
svcSleepThread(1'000'000ul * u64(reg_writes[i].offset));
|
break;
|
||||||
} else {
|
case DsiSleepOrRegisterWriteKind_Sleep:
|
||||||
std::abort();
|
svcSleepThread(1'000'000ul * u64(reg_writes[i].offset));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
std::abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,18 +133,12 @@ namespace sts::boot {
|
||||||
constexpr u64 DeviceName_DC = 2;
|
constexpr u64 DeviceName_DC = 2;
|
||||||
|
|
||||||
/* Create Address Space. */
|
/* Create Address Space. */
|
||||||
if (R_FAILED(svcCreateDeviceAddressSpace(&g_dc_das_hnd, 0, (1ul << 32)))) {
|
R_ASSERT(svcCreateDeviceAddressSpace(&g_dc_das_hnd, 0, (1ul << 32)));
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
/* Attach it to the DC. */
|
/* Attach it to the DC. */
|
||||||
if (R_FAILED(svcAttachDeviceAddressSpace(DeviceName_DC, g_dc_das_hnd))) {
|
R_ASSERT(svcAttachDeviceAddressSpace(DeviceName_DC, g_dc_das_hnd));
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Map the framebuffer for the DC as read-only. */
|
/* Map the framebuffer for the DC as read-only. */
|
||||||
if (R_FAILED(svcMapDeviceAddressSpaceAligned(g_dc_das_hnd, CUR_PROCESS_HANDLE, frame_buffer_aligned, FrameBufferSize, FrameBufferPaddr, 1))) {
|
R_ASSERT(svcMapDeviceAddressSpaceAligned(g_dc_das_hnd, CUR_PROCESS_HANDLE, frame_buffer_aligned, FrameBufferSize, FrameBufferPaddr, 1));
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,17 +148,11 @@ namespace sts::boot {
|
||||||
constexpr u64 DeviceName_DC = 2;
|
constexpr u64 DeviceName_DC = 2;
|
||||||
|
|
||||||
/* Unmap the framebuffer from the DC. */
|
/* Unmap the framebuffer from the DC. */
|
||||||
if (R_FAILED(svcUnmapDeviceAddressSpace(g_dc_das_hnd, CUR_PROCESS_HANDLE, frame_buffer_aligned, FrameBufferSize, FrameBufferPaddr))) {
|
R_ASSERT(svcUnmapDeviceAddressSpace(g_dc_das_hnd, CUR_PROCESS_HANDLE, frame_buffer_aligned, FrameBufferSize, FrameBufferPaddr));
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
/* Detach address space from the DC. */
|
/* Detach address space from the DC. */
|
||||||
if (R_FAILED(svcDetachDeviceAddressSpace(DeviceName_DC, g_dc_das_hnd))) {
|
R_ASSERT(svcDetachDeviceAddressSpace(DeviceName_DC, g_dc_das_hnd));
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
/* Close the address space. */
|
/* Close the address space. */
|
||||||
if (R_FAILED(svcCloseHandle(g_dc_das_hnd))) {
|
R_ASSERT(svcCloseHandle(g_dc_das_hnd));
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
g_dc_das_hnd = INVALID_HANDLE;
|
g_dc_das_hnd = INVALID_HANDLE;
|
||||||
g_frame_buffer = nullptr;
|
g_frame_buffer = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -300,8 +291,8 @@ namespace sts::boot {
|
||||||
for (size_t i = 0; i < util::size(host_response); i++) {
|
for (size_t i = 0; i < util::size(host_response); i++) {
|
||||||
host_response[i] = reg::Read(g_dsi_regs + sizeof(u32) * DSI_RD_DATA);
|
host_response[i] = reg::Read(g_dsi_regs + sizeof(u32) * DSI_RD_DATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The last word from host response is:
|
/* The last word from host response is:
|
||||||
Bits 0-7: FAB
|
Bits 0-7: FAB
|
||||||
Bits 8-15: REV
|
Bits 8-15: REV
|
||||||
Bits 16-23: Minor REV
|
Bits 16-23: Minor REV
|
||||||
|
|
|
@ -41,9 +41,8 @@ namespace sts::boot {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ReadI2cRegister(i2c::driver::Session &session, u8 *dst, size_t dst_size, const u8 *cmd, size_t cmd_size) {
|
Result ReadI2cRegister(i2c::driver::Session &session, u8 *dst, size_t dst_size, const u8 *cmd, size_t cmd_size) {
|
||||||
if (dst == nullptr || dst_size == 0 || cmd == nullptr || cmd_size == 0) {
|
STS_ASSERT(dst != nullptr && dst_size > 0);
|
||||||
std::abort();
|
STS_ASSERT(cmd != nullptr && cmd_size > 0);
|
||||||
}
|
|
||||||
|
|
||||||
u8 cmd_list[i2c::CommandListFormatter::MaxCommandListSize];
|
u8 cmd_list[i2c::CommandListFormatter::MaxCommandListSize];
|
||||||
|
|
||||||
|
@ -55,9 +54,8 @@ namespace sts::boot {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result WriteI2cRegister(i2c::driver::Session &session, const u8 *src, size_t src_size, const u8 *cmd, size_t cmd_size) {
|
Result WriteI2cRegister(i2c::driver::Session &session, const u8 *src, size_t src_size, const u8 *cmd, size_t cmd_size) {
|
||||||
if (src == nullptr || src_size == 0 || cmd == nullptr || cmd_size == 0) {
|
STS_ASSERT(src != nullptr && src_size > 0);
|
||||||
std::abort();
|
STS_ASSERT(cmd != nullptr && cmd_size > 0);
|
||||||
}
|
|
||||||
|
|
||||||
u8 cmd_list[0x20];
|
u8 cmd_list[0x20];
|
||||||
|
|
||||||
|
|
|
@ -39,9 +39,7 @@ namespace sts::boot {
|
||||||
args.X[2] = mask;
|
args.X[2] = mask;
|
||||||
args.X[3] = value;
|
args.X[3] = value;
|
||||||
R_ASSERT(svcCallSecureMonitor(&args));
|
R_ASSERT(svcCallSecureMonitor(&args));
|
||||||
if (args.X[0] != 0) {
|
STS_ASSERT(args.X[0] == 0);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
return static_cast<u32>(args.X[1]);
|
return static_cast<u32>(args.X[1]);
|
||||||
}
|
}
|
||||||
|
@ -49,18 +47,12 @@ namespace sts::boot {
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 ReadPmcRegister(u32 phys_addr) {
|
u32 ReadPmcRegister(u32 phys_addr) {
|
||||||
if (!IsValidPmcAddress(phys_addr)) {
|
STS_ASSERT(IsValidPmcAddress(phys_addr));
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
return SmcAtmosphereReadWriteRegister(phys_addr, 0, 0);
|
return SmcAtmosphereReadWriteRegister(phys_addr, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WritePmcRegister(u32 phys_addr, u32 value, u32 mask) {
|
void WritePmcRegister(u32 phys_addr, u32 value, u32 mask) {
|
||||||
if (!IsValidPmcAddress(phys_addr)) {
|
STS_ASSERT(IsValidPmcAddress(phys_addr));
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
SmcAtmosphereReadWriteRegister(phys_addr, value, mask);
|
SmcAtmosphereReadWriteRegister(phys_addr, value, mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -80,9 +80,7 @@ namespace sts::gpio {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ensure we found an appropriate config. */
|
/* Ensure we found an appropriate config. */
|
||||||
if (configs == nullptr) {
|
STS_ASSERT(configs != nullptr);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < num_configs; i++) {
|
for (size_t i = 0; i < num_configs; i++) {
|
||||||
/* Configure the GPIO. */
|
/* Configure the GPIO. */
|
||||||
|
|
|
@ -33,10 +33,7 @@ namespace sts::gpio {
|
||||||
|
|
||||||
/* Helpers. */
|
/* Helpers. */
|
||||||
inline u32 GetPadDescriptor(u32 gpio_pad_name) {
|
inline u32 GetPadDescriptor(u32 gpio_pad_name) {
|
||||||
if (gpio_pad_name >= PadNameMax) {
|
STS_ASSERT(gpio_pad_name < PadNameMax);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Map[gpio_pad_name];
|
return Map[gpio_pad_name];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,9 +88,7 @@ namespace sts::i2c::driver {
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void CheckInitialized() {
|
inline void CheckInitialized() {
|
||||||
if (!GetResourceManager().IsInitialized()) {
|
STS_ASSERT(GetResourceManager().IsInitialized());
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -107,9 +105,7 @@ namespace sts::i2c::driver {
|
||||||
/* Session management. */
|
/* Session management. */
|
||||||
void OpenSession(Session *out_session, I2cDevice device) {
|
void OpenSession(Session *out_session, I2cDevice device) {
|
||||||
CheckInitialized();
|
CheckInitialized();
|
||||||
if (!impl::IsDeviceSupported(device)) {
|
STS_ASSERT(impl::IsDeviceSupported(device));
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto bus = impl::GetDeviceBus(device);
|
const auto bus = impl::GetDeviceBus(device);
|
||||||
const auto slave_address = impl::GetDeviceSlaveAddress(device);
|
const auto slave_address = impl::GetDeviceSlaveAddress(device);
|
||||||
|
@ -128,9 +124,8 @@ namespace sts::i2c::driver {
|
||||||
/* Communication. */
|
/* Communication. */
|
||||||
Result Send(Session &session, const void *src, size_t size, I2cTransactionOption option) {
|
Result Send(Session &session, const void *src, size_t size, I2cTransactionOption option) {
|
||||||
CheckInitialized();
|
CheckInitialized();
|
||||||
if (src == nullptr || size == 0) {
|
STS_ASSERT(src != nullptr);
|
||||||
std::abort();
|
STS_ASSERT(size > 0);
|
||||||
}
|
|
||||||
|
|
||||||
std::scoped_lock<os::Mutex &> lk(GetResourceManager().GetTransactionMutex(impl::ConvertFromIndex(session.bus_idx)));
|
std::scoped_lock<os::Mutex &> lk(GetResourceManager().GetTransactionMutex(impl::ConvertFromIndex(session.bus_idx)));
|
||||||
return GetResourceManager().GetSession(session.session_id).DoTransactionWithRetry(nullptr, src, size, option, impl::Command::Send);
|
return GetResourceManager().GetSession(session.session_id).DoTransactionWithRetry(nullptr, src, size, option, impl::Command::Send);
|
||||||
|
@ -138,9 +133,8 @@ namespace sts::i2c::driver {
|
||||||
|
|
||||||
Result Receive(Session &session, void *dst, size_t size, I2cTransactionOption option) {
|
Result Receive(Session &session, void *dst, size_t size, I2cTransactionOption option) {
|
||||||
CheckInitialized();
|
CheckInitialized();
|
||||||
if (dst == nullptr || size == 0) {
|
STS_ASSERT(dst != nullptr);
|
||||||
std::abort();
|
STS_ASSERT(size > 0);
|
||||||
}
|
|
||||||
|
|
||||||
std::scoped_lock<os::Mutex &> lk(GetResourceManager().GetTransactionMutex(impl::ConvertFromIndex(session.bus_idx)));
|
std::scoped_lock<os::Mutex &> lk(GetResourceManager().GetTransactionMutex(impl::ConvertFromIndex(session.bus_idx)));
|
||||||
return GetResourceManager().GetSession(session.session_id).DoTransactionWithRetry(dst, nullptr, size, option, impl::Command::Receive);
|
return GetResourceManager().GetSession(session.session_id).DoTransactionWithRetry(dst, nullptr, size, option, impl::Command::Receive);
|
||||||
|
@ -148,9 +142,8 @@ namespace sts::i2c::driver {
|
||||||
|
|
||||||
Result ExecuteCommandList(Session &session, void *dst, size_t size, const void *cmd_list, size_t cmd_list_size) {
|
Result ExecuteCommandList(Session &session, void *dst, size_t size, const void *cmd_list, size_t cmd_list_size) {
|
||||||
CheckInitialized();
|
CheckInitialized();
|
||||||
if (dst == nullptr || size == 0 || cmd_list == nullptr || cmd_list_size == 0) {
|
STS_ASSERT(dst != nullptr && size > 0);
|
||||||
std::abort();
|
STS_ASSERT(cmd_list != nullptr && cmd_list_size > 0);
|
||||||
}
|
|
||||||
|
|
||||||
u8 *cur_dst = static_cast<u8 *>(dst);
|
u8 *cur_dst = static_cast<u8 *>(dst);
|
||||||
const u8 *cur_cmd = static_cast<const u8 *>(cmd_list);
|
const u8 *cur_cmd = static_cast<const u8 *>(cmd_list);
|
||||||
|
@ -158,9 +151,7 @@ namespace sts::i2c::driver {
|
||||||
|
|
||||||
while (cur_cmd < cmd_list_end) {
|
while (cur_cmd < cmd_list_end) {
|
||||||
Command cmd = static_cast<Command>((*cur_cmd) & 3);
|
Command cmd = static_cast<Command>((*cur_cmd) & 3);
|
||||||
if (cmd >= Command::Count) {
|
STS_ASSERT(cmd < Command::Count);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
R_TRY(g_cmd_handlers[static_cast<size_t>(cmd)](&cur_cmd, &cur_dst, session));
|
R_TRY(g_cmd_handlers[static_cast<size_t>(cmd)](&cur_cmd, &cur_dst, session));
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,9 +29,7 @@ namespace sts::i2c::driver::impl {
|
||||||
|
|
||||||
/* Ensure we're good if this isn't our first session. */
|
/* Ensure we're good if this isn't our first session. */
|
||||||
if (this->open_sessions > 1) {
|
if (this->open_sessions > 1) {
|
||||||
if (this->speed_mode != speed_mode) {
|
STS_ASSERT(this->speed_mode == speed_mode);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +56,7 @@ namespace sts::i2c::driver::impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Close interrupt event. */
|
/* Close interrupt event. */
|
||||||
eventClose(&this->interrupt_event);
|
this->interrupt_event.Finalize();
|
||||||
|
|
||||||
/* Close PCV. */
|
/* Close PCV. */
|
||||||
pcv::Finalize();
|
pcv::Finalize();
|
||||||
|
@ -158,10 +156,10 @@ namespace sts::i2c::driver::impl {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
eventClear(&this->interrupt_event);
|
this->interrupt_event.Reset();
|
||||||
if (R_FAILED(eventWait(&this->interrupt_event, InterruptTimeout))) {
|
if (!this->interrupt_event.TimedWait(InterruptTimeout)) {
|
||||||
this->HandleTransactionResult(ResultI2cBusBusy);
|
this->HandleTransactionResult(ResultI2cBusBusy);
|
||||||
eventClear(&this->interrupt_event);
|
this->interrupt_event.Reset();
|
||||||
return ResultI2cTimedOut;
|
return ResultI2cTimedOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,10 +179,10 @@ namespace sts::i2c::driver::impl {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
eventClear(&this->interrupt_event);
|
this->interrupt_event.Reset();
|
||||||
if (R_FAILED(eventWait(&this->interrupt_event, InterruptTimeout))) {
|
if (!this->interrupt_event.TimedWait(InterruptTimeout)) {
|
||||||
this->HandleTransactionResult(ResultI2cBusBusy);
|
this->HandleTransactionResult(ResultI2cBusBusy);
|
||||||
eventClear(&this->interrupt_event);
|
this->interrupt_event.Reset();
|
||||||
return ResultI2cTimedOut;
|
return ResultI2cTimedOut;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -206,11 +204,11 @@ namespace sts::i2c::driver::impl {
|
||||||
|
|
||||||
/* Receive bytes. */
|
/* Receive bytes. */
|
||||||
while (remaining > 0) {
|
while (remaining > 0) {
|
||||||
eventClear(&this->interrupt_event);
|
this->interrupt_event.Reset();
|
||||||
if (R_FAILED(eventWait(&this->interrupt_event, InterruptTimeout))) {
|
if (!this->interrupt_event.TimedWait(InterruptTimeout)) {
|
||||||
this->HandleTransactionResult(ResultI2cBusBusy);
|
this->HandleTransactionResult(ResultI2cBusBusy);
|
||||||
this->ClearInterruptMask();
|
this->ClearInterruptMask();
|
||||||
eventClear(&this->interrupt_event);
|
this->interrupt_event.Reset();
|
||||||
return ResultI2cTimedOut;
|
return ResultI2cTimedOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,16 +243,9 @@ namespace sts::i2c::driver::impl {
|
||||||
static constexpr u64 s_interrupts[] = {
|
static constexpr u64 s_interrupts[] = {
|
||||||
0x46, 0x74, 0x7C, 0x98, 0x55, 0x5F
|
0x46, 0x74, 0x7C, 0x98, 0x55, 0x5F
|
||||||
};
|
};
|
||||||
if (ConvertToIndex(bus) >= util::size(s_interrupts)) {
|
const auto index = ConvertToIndex(bus);
|
||||||
std::abort();
|
STS_ASSERT(index < util::size(s_interrupts));
|
||||||
}
|
R_ASSERT(this->interrupt_event.Initialize(s_interrupts[index], false));
|
||||||
|
|
||||||
Handle evt_h;
|
|
||||||
if (R_FAILED(svcCreateInterruptEvent(&evt_h, s_interrupts[ConvertToIndex(bus)], 1))) {
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
eventLoadRemote(&this->interrupt_event, evt_h, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BusAccessor::SetClock(SpeedMode speed_mode) {
|
void BusAccessor::SetClock(SpeedMode speed_mode) {
|
||||||
|
@ -307,29 +298,17 @@ namespace sts::i2c::driver::impl {
|
||||||
reg::Read(&this->i2c_registers->I2C_I2C_CNFG_0);
|
reg::Read(&this->i2c_registers->I2C_I2C_CNFG_0);
|
||||||
|
|
||||||
if (this->pcv_module != PcvModule_I2C5) {
|
if (this->pcv_module != PcvModule_I2C5) {
|
||||||
if (R_FAILED(pcv::SetReset(this->pcv_module, true))) {
|
R_ASSERT(pcv::SetReset(this->pcv_module, true));
|
||||||
std::abort();
|
R_ASSERT(pcv::SetClockRate(this->pcv_module, (408'000'000) / (src_div + 1)));
|
||||||
}
|
R_ASSERT(pcv::SetReset(this->pcv_module, false));
|
||||||
if (R_FAILED(pcv::SetClockRate(this->pcv_module, (408'000'000) / (src_div + 1)))) {
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
if (R_FAILED(pcv::SetReset(this->pcv_module, false))) {
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BusAccessor::ResetController() const {
|
void BusAccessor::ResetController() const {
|
||||||
if (this->pcv_module != PcvModule_I2C5) {
|
if (this->pcv_module != PcvModule_I2C5) {
|
||||||
if (R_FAILED(pcv::SetReset(this->pcv_module, true))) {
|
R_ASSERT(pcv::SetReset(this->pcv_module, true));
|
||||||
std::abort();
|
R_ASSERT(pcv::SetClockRate(this->pcv_module, 81'600'000));
|
||||||
}
|
R_ASSERT(pcv::SetReset(this->pcv_module, false));
|
||||||
if (R_FAILED(pcv::SetClockRate(this->pcv_module, 81'600'000))) {
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
if (R_FAILED(pcv::SetReset(this->pcv_module, false))) {
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,9 +367,7 @@ namespace sts::i2c::driver::impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
void BusAccessor::DisableClock() {
|
void BusAccessor::DisableClock() {
|
||||||
if (R_FAILED(pcv::SetClockEnabled(this->pcv_module, false))) {
|
R_ASSERT(pcv::SetClockEnabled(this->pcv_module, false));
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BusAccessor::SetPacketMode() {
|
void BusAccessor::SetPacketMode() {
|
||||||
|
@ -435,24 +412,21 @@ namespace sts::i2c::driver::impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
void BusAccessor::HandleTransactionResult(Result result) {
|
void BusAccessor::HandleTransactionResult(Result result) {
|
||||||
if (R_FAILED(result)) {
|
R_TRY_CATCH(result) {
|
||||||
if (result == ResultI2cNoAck || result == ResultI2cBusBusy) {
|
R_CATCH_MANY(ResultI2cNoAck, ResultI2cBusBusy) {
|
||||||
this->ResetController();
|
this->ResetController();
|
||||||
this->SetClock(this->speed_mode);
|
this->SetClock(this->speed_mode);
|
||||||
this->SetPacketMode();
|
this->SetPacketMode();
|
||||||
this->FlushFifos();
|
this->FlushFifos();
|
||||||
} else {
|
|
||||||
std::abort();
|
|
||||||
}
|
}
|
||||||
}
|
} R_END_TRY_CATCH_WITH_ASSERT;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result BusAccessor::GetAndHandleTransactionResult() {
|
Result BusAccessor::GetAndHandleTransactionResult() {
|
||||||
const Result transaction_res = this->GetTransactionResult();
|
R_TRY_CLEANUP(this->GetTransactionResult(), {
|
||||||
R_TRY_CLEANUP(transaction_res, {
|
this->HandleTransactionResult(R_CLEANUP_RESULT);
|
||||||
this->HandleTransactionResult(transaction_res);
|
|
||||||
this->ClearInterruptMask();
|
this->ClearInterruptMask();
|
||||||
eventClear(&this->interrupt_event);
|
this->interrupt_event.Reset();
|
||||||
});
|
});
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ namespace sts::i2c::driver::impl {
|
||||||
};
|
};
|
||||||
static constexpr u64 InterruptTimeout = 100'000'000ul;
|
static constexpr u64 InterruptTimeout = 100'000'000ul;
|
||||||
private:
|
private:
|
||||||
Event interrupt_event;
|
os::InterruptEvent interrupt_event;
|
||||||
os::Mutex open_mutex;
|
os::Mutex open_mutex;
|
||||||
os::Mutex register_mutex;
|
os::Mutex register_mutex;
|
||||||
Registers *i2c_registers = nullptr;
|
Registers *i2c_registers = nullptr;
|
||||||
|
|
|
@ -88,37 +88,37 @@ namespace sts::i2c::driver::impl {
|
||||||
|
|
||||||
Bus GetDeviceBus(I2cDevice dev) {
|
Bus GetDeviceBus(I2cDevice dev) {
|
||||||
const size_t dev_idx = GetDeviceIndex(dev);
|
const size_t dev_idx = GetDeviceIndex(dev);
|
||||||
if (dev_idx == DeviceInvalidIndex) { std::abort(); }
|
STS_ASSERT(dev_idx != DeviceInvalidIndex);
|
||||||
return g_device_configs[dev_idx].bus;
|
return g_device_configs[dev_idx].bus;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetDeviceSlaveAddress(I2cDevice dev) {
|
u32 GetDeviceSlaveAddress(I2cDevice dev) {
|
||||||
const size_t dev_idx = GetDeviceIndex(dev);
|
const size_t dev_idx = GetDeviceIndex(dev);
|
||||||
if (dev_idx == DeviceInvalidIndex) { std::abort(); }
|
STS_ASSERT(dev_idx != DeviceInvalidIndex);
|
||||||
return g_device_configs[dev_idx].slave_address;
|
return g_device_configs[dev_idx].slave_address;
|
||||||
}
|
}
|
||||||
|
|
||||||
AddressingMode GetDeviceAddressingMode(I2cDevice dev) {
|
AddressingMode GetDeviceAddressingMode(I2cDevice dev) {
|
||||||
const size_t dev_idx = GetDeviceIndex(dev);
|
const size_t dev_idx = GetDeviceIndex(dev);
|
||||||
if (dev_idx == DeviceInvalidIndex) { std::abort(); }
|
STS_ASSERT(dev_idx != DeviceInvalidIndex);
|
||||||
return g_device_configs[dev_idx].addressing_mode;
|
return g_device_configs[dev_idx].addressing_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
SpeedMode GetDeviceSpeedMode(I2cDevice dev) {
|
SpeedMode GetDeviceSpeedMode(I2cDevice dev) {
|
||||||
const size_t dev_idx = GetDeviceIndex(dev);
|
const size_t dev_idx = GetDeviceIndex(dev);
|
||||||
if (dev_idx == DeviceInvalidIndex) { std::abort(); }
|
STS_ASSERT(dev_idx != DeviceInvalidIndex);
|
||||||
return g_device_configs[dev_idx].speed_mode;
|
return g_device_configs[dev_idx].speed_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetDeviceMaxRetries(I2cDevice dev) {
|
u32 GetDeviceMaxRetries(I2cDevice dev) {
|
||||||
const size_t dev_idx = GetDeviceIndex(dev);
|
const size_t dev_idx = GetDeviceIndex(dev);
|
||||||
if (dev_idx == DeviceInvalidIndex) { std::abort(); }
|
STS_ASSERT(dev_idx != DeviceInvalidIndex);
|
||||||
return g_device_configs[dev_idx].max_retries;
|
return g_device_configs[dev_idx].max_retries;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 GetDeviceRetryWaitTime(I2cDevice dev) {
|
u64 GetDeviceRetryWaitTime(I2cDevice dev) {
|
||||||
const size_t dev_idx = GetDeviceIndex(dev);
|
const size_t dev_idx = GetDeviceIndex(dev);
|
||||||
if (dev_idx == DeviceInvalidIndex) { std::abort(); }
|
STS_ASSERT(dev_idx != DeviceInvalidIndex);
|
||||||
return g_device_configs[dev_idx].retry_wait_time;
|
return g_device_configs[dev_idx].retry_wait_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,9 +43,7 @@ namespace sts::i2c::driver::impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr inline Bus ConvertFromIndex(size_t idx) {
|
constexpr inline Bus ConvertFromIndex(size_t idx) {
|
||||||
if (idx >= static_cast<size_t>(Bus::Count)) {
|
STS_ASSERT(idx < static_cast<size_t>(Bus::Count));
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
return static_cast<Bus>(idx);
|
return static_cast<Bus>(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,9 +29,7 @@ namespace sts::i2c::driver::impl {
|
||||||
|
|
||||||
void ResourceManager::Finalize() {
|
void ResourceManager::Finalize() {
|
||||||
std::scoped_lock lk(this->initialize_mutex);
|
std::scoped_lock lk(this->initialize_mutex);
|
||||||
if (this->ref_cnt == 0) {
|
STS_ASSERT(this->ref_cnt > 0);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
this->ref_cnt--;
|
this->ref_cnt--;
|
||||||
if (this->ref_cnt > 0) {
|
if (this->ref_cnt > 0) {
|
||||||
return;
|
return;
|
||||||
|
@ -61,14 +59,11 @@ namespace sts::i2c::driver::impl {
|
||||||
/* Get, open session. */
|
/* Get, open session. */
|
||||||
{
|
{
|
||||||
std::scoped_lock lk(this->session_open_mutex);
|
std::scoped_lock lk(this->session_open_mutex);
|
||||||
if (out_session == nullptr || bus >= Bus::Count) {
|
STS_ASSERT(out_session != nullptr);
|
||||||
std::abort();
|
STS_ASSERT(bus < Bus::Count);
|
||||||
}
|
|
||||||
|
|
||||||
session_id = GetFreeSessionId();
|
session_id = GetFreeSessionId();
|
||||||
if (session_id == InvalidSessionId) {
|
STS_ASSERT(session_id != InvalidSessionId);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if ((bus == Bus::I2C2 || bus == Bus::I2C3) && (this->bus_accessors[ConvertToIndex(Bus::I2C2)].GetOpenSessions() == 0 && this->bus_accessors[ConvertToIndex(Bus::I2C3)].GetOpenSessions() == 0)) {
|
if ((bus == Bus::I2C2 || bus == Bus::I2C3) && (this->bus_accessors[ConvertToIndex(Bus::I2C2)].GetOpenSessions() == 0 && this->bus_accessors[ConvertToIndex(Bus::I2C3)].GetOpenSessions() == 0)) {
|
||||||
|
@ -83,12 +78,8 @@ namespace sts::i2c::driver::impl {
|
||||||
this->sessions[session_id].Start();
|
this->sessions[session_id].Start();
|
||||||
if (need_enable_ldo6) {
|
if (need_enable_ldo6) {
|
||||||
pcv::Initialize();
|
pcv::Initialize();
|
||||||
if (R_FAILED(pcv::SetVoltageValue(10, 2'900'000))) {
|
R_ASSERT(pcv::SetVoltageValue(10, 2'900'000));
|
||||||
std::abort();
|
R_ASSERT(pcv::SetVoltageEnabled(10, true));
|
||||||
}
|
|
||||||
if (R_FAILED(pcv::SetVoltageEnabled(10, true))) {
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
pcv::Finalize();
|
pcv::Finalize();
|
||||||
svcSleepThread(560'000ul);
|
svcSleepThread(560'000ul);
|
||||||
}
|
}
|
||||||
|
@ -99,9 +90,7 @@ namespace sts::i2c::driver::impl {
|
||||||
/* Get, open session. */
|
/* Get, open session. */
|
||||||
{
|
{
|
||||||
std::scoped_lock lk(this->session_open_mutex);
|
std::scoped_lock lk(this->session_open_mutex);
|
||||||
if (!this->sessions[session.session_id].IsOpen()) {
|
STS_ASSERT(this->sessions[session.session_id].IsOpen());
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
this->sessions[session.session_id].Close();
|
this->sessions[session.session_id].Close();
|
||||||
|
|
||||||
|
@ -113,18 +102,14 @@ namespace sts::i2c::driver::impl {
|
||||||
|
|
||||||
if (need_disable_ldo6) {
|
if (need_disable_ldo6) {
|
||||||
pcv::Initialize();
|
pcv::Initialize();
|
||||||
if (R_FAILED(pcv::SetVoltageEnabled(10, false))) {
|
R_ASSERT(pcv::SetVoltageEnabled(10, false));
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
pcv::Finalize();
|
pcv::Finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResourceManager::SuspendBuses() {
|
void ResourceManager::SuspendBuses() {
|
||||||
if (this->ref_cnt == 0) {
|
STS_ASSERT(this->ref_cnt > 0);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this->suspended) {
|
if (!this->suspended) {
|
||||||
{
|
{
|
||||||
|
@ -137,27 +122,19 @@ namespace sts::i2c::driver::impl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pcv::Initialize();
|
pcv::Initialize();
|
||||||
if (R_FAILED(pcv::SetVoltageEnabled(10, false))) {
|
R_ASSERT(pcv::SetVoltageEnabled(10, false));
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
pcv::Finalize();
|
pcv::Finalize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResourceManager::ResumeBuses() {
|
void ResourceManager::ResumeBuses() {
|
||||||
if (this->ref_cnt == 0) {
|
STS_ASSERT(this->ref_cnt > 0);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->suspended) {
|
if (this->suspended) {
|
||||||
if (this->bus_accessors[ConvertToIndex(Bus::I2C2)].GetOpenSessions() > 0 || this->bus_accessors[ConvertToIndex(Bus::I2C3)].GetOpenSessions() > 0) {
|
if (this->bus_accessors[ConvertToIndex(Bus::I2C2)].GetOpenSessions() > 0 || this->bus_accessors[ConvertToIndex(Bus::I2C3)].GetOpenSessions() > 0) {
|
||||||
pcv::Initialize();
|
pcv::Initialize();
|
||||||
if (R_FAILED(pcv::SetVoltageValue(10, 2'900'000))) {
|
R_ASSERT(pcv::SetVoltageValue(10, 2'900'000));
|
||||||
std::abort();
|
R_ASSERT(pcv::SetVoltageEnabled(10, true));
|
||||||
}
|
|
||||||
if (R_FAILED(pcv::SetVoltageEnabled(10, true))) {
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
pcv::Finalize();
|
pcv::Finalize();
|
||||||
svcSleepThread(1'560'000ul);
|
svcSleepThread(1'560'000ul);
|
||||||
}
|
}
|
||||||
|
@ -174,9 +151,7 @@ namespace sts::i2c::driver::impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResourceManager::SuspendPowerBus() {
|
void ResourceManager::SuspendPowerBus() {
|
||||||
if (this->ref_cnt == 0) {
|
STS_ASSERT(this->ref_cnt > 0);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
std::scoped_lock lk(this->session_open_mutex);
|
std::scoped_lock lk(this->session_open_mutex);
|
||||||
|
|
||||||
if (!this->power_bus_suspended) {
|
if (!this->power_bus_suspended) {
|
||||||
|
@ -188,9 +163,7 @@ namespace sts::i2c::driver::impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResourceManager::ResumePowerBus() {
|
void ResourceManager::ResumePowerBus() {
|
||||||
if (this->ref_cnt == 0) {
|
STS_ASSERT(this->ref_cnt > 0);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
std::scoped_lock lk(this->session_open_mutex);
|
std::scoped_lock lk(this->session_open_mutex);
|
||||||
|
|
||||||
if (this->power_bus_suspended) {
|
if (this->power_bus_suspended) {
|
||||||
|
|
|
@ -43,9 +43,7 @@ namespace sts::i2c {
|
||||||
size_t cur_index = 0;
|
size_t cur_index = 0;
|
||||||
public:
|
public:
|
||||||
CommandListFormatter(void *cmd_list, size_t cmd_list_size) : cmd_list(static_cast<u8 *>(cmd_list)), cmd_list_size(cmd_list_size) {
|
CommandListFormatter(void *cmd_list, size_t cmd_list_size) : cmd_list(static_cast<u8 *>(cmd_list)), cmd_list_size(cmd_list_size) {
|
||||||
if (cmd_list_size > MaxCommandListSize) {
|
STS_ASSERT(cmd_list_size <= MaxCommandListSize);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
~CommandListFormatter() {
|
~CommandListFormatter() {
|
||||||
this->cmd_list = nullptr;
|
this->cmd_list = nullptr;
|
||||||
|
|
|
@ -66,9 +66,7 @@ namespace sts::pinmux {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ensure we found an appropriate config. */
|
/* Ensure we found an appropriate config. */
|
||||||
if (configs == nullptr) {
|
STS_ASSERT(configs != nullptr);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < num_configs; i++) {
|
for (size_t i = 0; i < num_configs; i++) {
|
||||||
UpdatePad(configs[i].name, configs[i].val, configs[i].mask);
|
UpdatePad(configs[i].name, configs[i].val, configs[i].mask);
|
||||||
|
|
|
@ -33,19 +33,13 @@ namespace sts::pinmux {
|
||||||
|
|
||||||
/* Helpers. */
|
/* Helpers. */
|
||||||
inline const Definition *GetDefinition(u32 pinmux_name) {
|
inline const Definition *GetDefinition(u32 pinmux_name) {
|
||||||
if (pinmux_name >= PadNameMax) {
|
STS_ASSERT(pinmux_name < PadNameMax);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Map[pinmux_name];
|
return &Map[pinmux_name];
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const DrivePadDefinition *GetDrivePadDefinition(u32 pinmux_name) {
|
inline const DrivePadDefinition *GetDrivePadDefinition(u32 drivepad_name) {
|
||||||
if (pinmux_name >= DrivePadNameMax) {
|
STS_ASSERT(drivepad_name < DrivePadNameMax);
|
||||||
std::abort();
|
return &DrivePadMap[drivepad_name];
|
||||||
}
|
|
||||||
|
|
||||||
return &DrivePadMap[pinmux_name];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uintptr_t GetBaseAddress() {
|
uintptr_t GetBaseAddress() {
|
||||||
|
@ -110,9 +104,7 @@ namespace sts::pinmux {
|
||||||
u32 pinmux_val = reg::Read(pinmux_reg);
|
u32 pinmux_val = reg::Read(pinmux_reg);
|
||||||
|
|
||||||
/* This PINMUX register is locked */
|
/* This PINMUX register is locked */
|
||||||
if (pinmux_val & 0x80) {
|
STS_ASSERT((pinmux_val & 0x80) == 0);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 pm_val = (pinmux_config_val & 0x07);
|
u32 pm_val = (pinmux_config_val & 0x07);
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ namespace sts::dmnt::cheat::impl {
|
||||||
os::Mutex cheat_lock;
|
os::Mutex cheat_lock;
|
||||||
os::Event debug_events_event; /* Autoclear. */
|
os::Event debug_events_event; /* Autoclear. */
|
||||||
os::Thread detect_thread, debug_events_thread;
|
os::Thread detect_thread, debug_events_thread;
|
||||||
IEvent *cheat_process_event;
|
os::SystemEvent cheat_process_event;
|
||||||
Handle cheat_process_debug_handle = INVALID_HANDLE;
|
Handle cheat_process_debug_handle = INVALID_HANDLE;
|
||||||
CheatProcessMetadata cheat_process_metadata = {};
|
CheatProcessMetadata cheat_process_metadata = {};
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ namespace sts::dmnt::cheat::impl {
|
||||||
this->frozen_addresses_map.clear();
|
this->frozen_addresses_map.clear();
|
||||||
|
|
||||||
/* Signal to our fans. */
|
/* Signal to our fans. */
|
||||||
this->cheat_process_event->Signal();
|
this->cheat_process_event.Signal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,7 +183,7 @@ namespace sts::dmnt::cheat::impl {
|
||||||
public:
|
public:
|
||||||
CheatProcessManager() {
|
CheatProcessManager() {
|
||||||
/* Create cheat process detection event. */
|
/* Create cheat process detection event. */
|
||||||
this->cheat_process_event = CreateWriteOnlySystemEvent();
|
R_ASSERT(this->cheat_process_event.InitializeAsInterProcessEvent());
|
||||||
|
|
||||||
/* Learn whether we should enable cheats by default. */
|
/* Learn whether we should enable cheats by default. */
|
||||||
{
|
{
|
||||||
|
@ -214,8 +214,8 @@ namespace sts::dmnt::cheat::impl {
|
||||||
return this->HasActiveCheatProcess();
|
return this->HasActiveCheatProcess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle GetCheatProcessEventHandle() {
|
Handle GetCheatProcessEventHandle() const {
|
||||||
return this->cheat_process_event->GetHandle();
|
return this->cheat_process_event.GetReadableHandle();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetCheatProcessMetadata(CheatProcessMetadata *out) {
|
Result GetCheatProcessMetadata(CheatProcessMetadata *out) {
|
||||||
|
@ -710,7 +710,7 @@ namespace sts::dmnt::cheat::impl {
|
||||||
this->debug_events_event.Signal();
|
this->debug_events_event.Signal();
|
||||||
|
|
||||||
/* Signal to our fans. */
|
/* Signal to our fans. */
|
||||||
this->cheat_process_event->Signal();
|
this->cheat_process_event.Signal();
|
||||||
|
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,20 +24,29 @@ namespace sts::fatal::srv {
|
||||||
FatalConfig g_config;
|
FatalConfig g_config;
|
||||||
|
|
||||||
/* Event creator. */
|
/* Event creator. */
|
||||||
IEvent *CreateFatalDirtyEvent() {
|
Handle GetFatalDirtyEventReadableHandle() {
|
||||||
Event evt;
|
Event evt;
|
||||||
R_ASSERT(setsysBindFatalDirtyFlagEvent(&evt));
|
R_ASSERT(setsysBindFatalDirtyFlagEvent(&evt));
|
||||||
return LoadReadOnlySystemEvent(evt.revent, [](u64 timeout) {
|
return evt.revent;
|
||||||
u64 flags_0, flags_1;
|
|
||||||
if (R_SUCCEEDED(setsysGetFatalDirtyFlags(&flags_0, &flags_1)) && (flags_0 & 1)) {
|
|
||||||
g_config.UpdateLanguageCode();
|
|
||||||
}
|
|
||||||
return ResultSuccess;
|
|
||||||
}, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Global event. */
|
/* Global event. */
|
||||||
IEvent *g_fatal_dirty_event = CreateFatalDirtyEvent();
|
os::SystemEvent g_fatal_dirty_event(GetFatalDirtyEventReadableHandle(), true, false);
|
||||||
|
os::WaitableHolder g_fatal_dirty_waitable_holder(&g_fatal_dirty_event);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
os::WaitableHolder *GetFatalDirtyWaitableHolder() {
|
||||||
|
return &g_fatal_dirty_waitable_holder;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnFatalDirtyEvent() {
|
||||||
|
g_fatal_dirty_event.Reset();
|
||||||
|
|
||||||
|
u64 flags_0, flags_1;
|
||||||
|
if (R_SUCCEEDED(setsysGetFatalDirtyFlags(&flags_0, &flags_1)) && (flags_0 & 1)) {
|
||||||
|
g_config.UpdateLanguageCode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FatalConfig::FatalConfig() {
|
FatalConfig::FatalConfig() {
|
||||||
|
@ -84,10 +93,6 @@ namespace sts::fatal::srv {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IEvent *GetFatalDirtyEvent() {
|
|
||||||
return g_fatal_dirty_event;
|
|
||||||
}
|
|
||||||
|
|
||||||
const FatalConfig &GetFatalConfig() {
|
const FatalConfig &GetFatalConfig() {
|
||||||
return g_config;
|
return g_config;
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,7 +90,8 @@ namespace sts::fatal::srv {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
IEvent *GetFatalDirtyEvent();
|
os::WaitableHolder *GetFatalDirtyWaitableHolder();
|
||||||
|
void OnFatalDirtyEvent();
|
||||||
const FatalConfig &GetFatalConfig();
|
const FatalConfig &GetFatalConfig();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,7 +156,7 @@ namespace sts::fatal::srv {
|
||||||
|
|
||||||
void TryCollectDebugInformation(ThrowContext *ctx, u64 process_id) {
|
void TryCollectDebugInformation(ThrowContext *ctx, u64 process_id) {
|
||||||
/* Try to debug the process. This may fail, if we called into ourself. */
|
/* Try to debug the process. This may fail, if we called into ourself. */
|
||||||
AutoHandle debug_handle;
|
os::ManagedHandle debug_handle;
|
||||||
if (R_FAILED(svcDebugActiveProcess(debug_handle.GetPointer(), process_id))) {
|
if (R_FAILED(svcDebugActiveProcess(debug_handle.GetPointer(), process_id))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,7 +133,9 @@ int main(int argc, char **argv)
|
||||||
/* Create services. */
|
/* Create services. */
|
||||||
s_server_manager.AddWaitable(new ServiceServer<sts::fatal::srv::PrivateService>("fatal:p", 4));
|
s_server_manager.AddWaitable(new ServiceServer<sts::fatal::srv::PrivateService>("fatal:p", 4));
|
||||||
s_server_manager.AddWaitable(new ServiceServer<sts::fatal::srv::UserService>("fatal:u", 4));
|
s_server_manager.AddWaitable(new ServiceServer<sts::fatal::srv::UserService>("fatal:u", 4));
|
||||||
s_server_manager.AddWaitable(sts::fatal::srv::GetFatalDirtyEvent());
|
/* TODO: s_server_manager.AddWaitable(sts::fatal::srv::GetFatalDirtyEvent()); */
|
||||||
|
auto dirty_event_holder = sts::fatal::srv::GetFatalDirtyWaitableHolder();
|
||||||
|
(void)dirty_event_holder;
|
||||||
|
|
||||||
/* Loop forever, servicing our services. */
|
/* Loop forever, servicing our services. */
|
||||||
s_server_manager.Process();
|
s_server_manager.Process();
|
||||||
|
|
|
@ -60,10 +60,7 @@ namespace sts::fatal::srv {
|
||||||
public:
|
public:
|
||||||
TaskManager() { /* ... */ }
|
TaskManager() { /* ... */ }
|
||||||
void StartTask(ITask *task) {
|
void StartTask(ITask *task) {
|
||||||
if (this->task_count >= MaxTasks) {
|
STS_ASSERT(this->task_count < MaxTasks);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
this->task_threads[this->task_count++].StartTask(task);
|
this->task_threads[this->task_count++].StartTask(task);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,7 +16,7 @@ include $(DEVKITPRO)/libnx/switch_rules
|
||||||
# INCLUDES is a list of directories containing header files
|
# INCLUDES is a list of directories containing header files
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
TARGET := $(notdir $(CURDIR))
|
TARGET := $(notdir $(CURDIR))
|
||||||
SOURCES := source source/os source/spl source/spl/smc source/updater source/patcher source/map source/rnd source/util source/sm source/cfg source/pm source/hid source/ldr source/kvdb
|
SOURCES := source source/os source/os/impl source/spl source/spl/smc source/updater source/patcher source/map source/rnd source/util source/sm source/cfg source/pm source/hid source/ldr source/kvdb
|
||||||
DATA := data
|
DATA := data
|
||||||
INCLUDES := include
|
INCLUDES := include
|
||||||
|
|
||||||
|
|
|
@ -21,13 +21,9 @@
|
||||||
#include "stratosphere/utilities.hpp"
|
#include "stratosphere/utilities.hpp"
|
||||||
#include "stratosphere/emummc_utilities.hpp"
|
#include "stratosphere/emummc_utilities.hpp"
|
||||||
|
|
||||||
#include "stratosphere/scope_guard.hpp"
|
|
||||||
|
|
||||||
#include "stratosphere/version_check.hpp"
|
#include "stratosphere/version_check.hpp"
|
||||||
|
|
||||||
#include "stratosphere/auto_handle.hpp"
|
|
||||||
#include "stratosphere/iwaitable.hpp"
|
#include "stratosphere/iwaitable.hpp"
|
||||||
#include "stratosphere/event.hpp"
|
|
||||||
|
|
||||||
#include "stratosphere/waitable_manager.hpp"
|
#include "stratosphere/waitable_manager.hpp"
|
||||||
|
|
||||||
|
|
|
@ -1,80 +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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <switch.h>
|
|
||||||
#include "defines.hpp"
|
|
||||||
|
|
||||||
class AutoHandle {
|
|
||||||
NON_COPYABLE(AutoHandle);
|
|
||||||
private:
|
|
||||||
Handle hnd;
|
|
||||||
public:
|
|
||||||
AutoHandle() : hnd(INVALID_HANDLE) { /* ... */ }
|
|
||||||
AutoHandle(Handle h) : hnd(h) { /* ... */ }
|
|
||||||
~AutoHandle() {
|
|
||||||
if (this->hnd != INVALID_HANDLE) {
|
|
||||||
svcCloseHandle(this->hnd);
|
|
||||||
this->hnd = INVALID_HANDLE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoHandle(AutoHandle&& rhs) {
|
|
||||||
this->hnd = rhs.hnd;
|
|
||||||
rhs.hnd = INVALID_HANDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoHandle& operator=(AutoHandle&& rhs) {
|
|
||||||
rhs.Swap(*this);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit operator bool() const {
|
|
||||||
return this->hnd != INVALID_HANDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Swap(AutoHandle& rhs) {
|
|
||||||
std::swap(this->hnd, rhs.hnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
Handle Get() const {
|
|
||||||
return this->hnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
Handle *GetPointer() {
|
|
||||||
return &this->hnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
Handle *GetPointerAndClear() {
|
|
||||||
this->Clear();
|
|
||||||
return this->GetPointer();
|
|
||||||
}
|
|
||||||
|
|
||||||
Handle Move() {
|
|
||||||
const Handle h = this->hnd;
|
|
||||||
this->hnd = INVALID_HANDLE;
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Reset(Handle h) {
|
|
||||||
AutoHandle(h).Swap(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Clear() {
|
|
||||||
this->Reset(INVALID_HANDLE);
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -19,6 +19,8 @@
|
||||||
|
|
||||||
/* Any broadly useful language defines should go here. */
|
/* Any broadly useful language defines should go here. */
|
||||||
|
|
||||||
|
#define STS_ASSERT(expr) do { if (!(expr)) { std::abort(); } } while (0)
|
||||||
|
|
||||||
#define NON_COPYABLE(cls) \
|
#define NON_COPYABLE(cls) \
|
||||||
cls(const cls&) = delete; \
|
cls(const cls&) = delete; \
|
||||||
cls& operator=(const cls&) = delete
|
cls& operator=(const cls&) = delete
|
||||||
|
@ -30,21 +32,12 @@
|
||||||
#define ALIGNED(algn) __attribute__((aligned(algn)))
|
#define ALIGNED(algn) __attribute__((aligned(algn)))
|
||||||
#define WEAK __attribute__((weak))
|
#define WEAK __attribute__((weak))
|
||||||
|
|
||||||
namespace sts::util {
|
|
||||||
|
|
||||||
/* std::size() does not support zero-size C arrays. We're fixing that. */
|
#define CONCATENATE_IMPL(S1, s2) s1##s2
|
||||||
template<class C>
|
#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)
|
||||||
constexpr auto size(const C& c) -> decltype(c.size()) {
|
|
||||||
return std::size(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class C>
|
#ifdef __COUNTER__
|
||||||
constexpr std::size_t size(const C& c) {
|
#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __COUNTER__)
|
||||||
if constexpr (sizeof(C) == 0) {
|
#else
|
||||||
return 0;
|
#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __LINE__)
|
||||||
} else {
|
#endif
|
||||||
return std::size(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,139 +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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "iwaitable.hpp"
|
|
||||||
#include "results.hpp"
|
|
||||||
|
|
||||||
class IEvent : public IWaitable {
|
|
||||||
public:
|
|
||||||
/* Information members. */
|
|
||||||
Handle r_h;
|
|
||||||
Handle w_h;
|
|
||||||
bool autoclear;
|
|
||||||
public:
|
|
||||||
IEvent(bool a = false) : r_h(INVALID_HANDLE), w_h(INVALID_HANDLE), autoclear(a) { }
|
|
||||||
IEvent(Handle r, bool a = false) : r_h(r), w_h(INVALID_HANDLE), autoclear(a) { }
|
|
||||||
IEvent(Handle r, Handle w, bool a = false) : r_h(r), w_h(w), autoclear(a) { }
|
|
||||||
|
|
||||||
~IEvent() {
|
|
||||||
if (r_h != INVALID_HANDLE) {
|
|
||||||
svcCloseHandle(r_h);
|
|
||||||
}
|
|
||||||
if (w_h != INVALID_HANDLE) {
|
|
||||||
svcCloseHandle(w_h);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make it non-copyable */
|
|
||||||
IEvent() = delete;
|
|
||||||
IEvent(const IEvent &) = delete;
|
|
||||||
IEvent& operator=(const IEvent&) = delete;
|
|
||||||
|
|
||||||
|
|
||||||
bool IsAutoClear() {
|
|
||||||
return this->autoclear;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Clear() {
|
|
||||||
std::scoped_lock lock(this->sig_lock);
|
|
||||||
this->is_signaled = false;
|
|
||||||
if (this->w_h != INVALID_HANDLE) {
|
|
||||||
svcClearEvent(this->w_h);
|
|
||||||
} else if (this->r_h != INVALID_HANDLE) {
|
|
||||||
svcResetSignal(this->r_h);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Signal() {
|
|
||||||
std::scoped_lock lock(this->sig_lock);
|
|
||||||
|
|
||||||
if (this->w_h == INVALID_HANDLE && this->r_h != INVALID_HANDLE) {
|
|
||||||
/* We can't signal an event if we only have a read handle. */
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->w_h == INVALID_HANDLE && this->is_signaled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->is_signaled = true;
|
|
||||||
|
|
||||||
if (this->w_h != INVALID_HANDLE) {
|
|
||||||
svcSignalEvent(this->w_h);
|
|
||||||
} else {
|
|
||||||
this->NotifyManagerSignaled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result HandleSignaled(u64 timeout) = 0;
|
|
||||||
|
|
||||||
/* IWaitable */
|
|
||||||
virtual Handle GetHandle() override {
|
|
||||||
return this->r_h;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class F>
|
|
||||||
class HosEvent : public IEvent {
|
|
||||||
private:
|
|
||||||
F callback;
|
|
||||||
public:
|
|
||||||
HosEvent(F f, bool a = false) : IEvent(a), callback(std::move(f)) { }
|
|
||||||
HosEvent(Handle r, F f, bool a = false) : IEvent(r, a), callback(std::move(f)) { }
|
|
||||||
HosEvent(Handle r, Handle w, F f, bool a = false) : IEvent(r, w, a), callback(std::move(f)) { }
|
|
||||||
|
|
||||||
virtual Result HandleSignaled(u64 timeout) override {
|
|
||||||
if (this->IsAutoClear()) {
|
|
||||||
this->Clear();
|
|
||||||
}
|
|
||||||
return this->callback(timeout);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class F>
|
|
||||||
static IEvent *CreateHosEvent(F f, bool autoclear = false) {
|
|
||||||
return new HosEvent<F>(INVALID_HANDLE, INVALID_HANDLE, std::move(f), autoclear);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class F>
|
|
||||||
static IEvent *CreateSystemEvent(F f, bool autoclear = false) {
|
|
||||||
Handle w_h, r_h;
|
|
||||||
R_ASSERT(svcCreateEvent(&w_h, &r_h));
|
|
||||||
return new HosEvent<F>(r_h, w_h, std::move(f), autoclear);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class F>
|
|
||||||
static IEvent *CreateInterruptEvent(F f, u64 irq, bool autoclear = false) {
|
|
||||||
Handle r_h;
|
|
||||||
/* flag is "rising edge vs level". */
|
|
||||||
R_ASSERT(svcCreateInterruptEvent(&r_h, irq, autoclear ? 0 : 1));
|
|
||||||
return new HosEvent<F>(r_h, INVALID_HANDLE, std::move(f), autoclear);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <bool a = false>
|
|
||||||
static IEvent *CreateWriteOnlySystemEvent() {
|
|
||||||
return CreateSystemEvent([](u64 timeout) -> Result { std::abort(); }, a);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class F>
|
|
||||||
static IEvent *LoadReadOnlySystemEvent(Handle r_h, F f, bool autoclear = false) {
|
|
||||||
return new HosEvent<F>(r_h, f, autoclear);
|
|
||||||
}
|
|
|
@ -68,9 +68,7 @@ namespace sts::kvdb {
|
||||||
|
|
||||||
Result Initialize(size_t size) {
|
Result Initialize(size_t size) {
|
||||||
/* Check that we're not already initialized. */
|
/* Check that we're not already initialized. */
|
||||||
if (this->buffer != nullptr) {
|
STS_ASSERT(this->buffer == nullptr);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate a buffer. */
|
/* Allocate a buffer. */
|
||||||
this->buffer = static_cast<u8 *>(std::malloc(size));
|
this->buffer = static_cast<u8 *>(std::malloc(size));
|
||||||
|
|
|
@ -32,9 +32,7 @@ namespace sts::kvdb {
|
||||||
private:
|
private:
|
||||||
/* Utility. */
|
/* Utility. */
|
||||||
static inline void CheckLength(size_t len) {
|
static inline void CheckLength(size_t len) {
|
||||||
if (len >= N) {
|
STS_ASSERT(len < N);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
/* Constructors. */
|
/* Constructors. */
|
||||||
|
@ -115,9 +113,8 @@ namespace sts::kvdb {
|
||||||
/* Substring utilities. */
|
/* Substring utilities. */
|
||||||
void GetSubstring(char *dst, size_t dst_size, size_t offset, size_t length) const {
|
void GetSubstring(char *dst, size_t dst_size, size_t offset, size_t length) const {
|
||||||
/* Make sure output buffer can hold the substring. */
|
/* Make sure output buffer can hold the substring. */
|
||||||
if (offset + length > GetLength() || dst_size <= length) {
|
STS_ASSERT(offset + length <= GetLength());
|
||||||
std::abort();
|
STS_ASSERT(dst_size > length);
|
||||||
}
|
|
||||||
/* Copy substring to dst. */
|
/* Copy substring to dst. */
|
||||||
std::strncpy(dst, this->buffer + offset, length);
|
std::strncpy(dst, this->buffer + offset, length);
|
||||||
dst[length] = 0;
|
dst[length] = 0;
|
||||||
|
|
|
@ -60,9 +60,7 @@ namespace sts::kvdb {
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
void RemoveIndex(size_t i) {
|
void RemoveIndex(size_t i) {
|
||||||
if (i >= this->GetCount()) {
|
STS_ASSERT(i < this->GetCount());
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
std::memmove(this->keys + i, this->keys + i + 1, sizeof(*this->keys) * (this->GetCount() - (i + 1)));
|
std::memmove(this->keys + i, this->keys + i + 1, sizeof(*this->keys) * (this->GetCount() - (i + 1)));
|
||||||
this->DecrementCount();
|
this->DecrementCount();
|
||||||
}
|
}
|
||||||
|
@ -79,9 +77,8 @@ namespace sts::kvdb {
|
||||||
|
|
||||||
Result Initialize(const char *path, void *buf, size_t size) {
|
Result Initialize(const char *path, void *buf, size_t size) {
|
||||||
/* Only initialize once, and ensure we have sufficient memory. */
|
/* Only initialize once, and ensure we have sufficient memory. */
|
||||||
if (this->keys != nullptr || size < BufferSize) {
|
STS_ASSERT(this->keys == nullptr);
|
||||||
std::abort();
|
SSS_ASSERT(size >= BufferSize);
|
||||||
}
|
|
||||||
|
|
||||||
/* Setup member variables. */
|
/* Setup member variables. */
|
||||||
this->keys = static_cast<Key *>(buf);
|
this->keys = static_cast<Key *>(buf);
|
||||||
|
@ -148,31 +145,23 @@ namespace sts::kvdb {
|
||||||
}
|
}
|
||||||
|
|
||||||
Key Get(size_t i) const {
|
Key Get(size_t i) const {
|
||||||
if (i >= this->GetCount()) {
|
STS_ASSERT(i < this->GetCount());
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
return this->keys[i];
|
return this->keys[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
Key Peek() const {
|
Key Peek() const {
|
||||||
if (this->IsEmpty()) {
|
STS_ASSERT(!this->IsEmpty());
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
return this->Get(0);
|
return this->Get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Push(const Key &key) {
|
void Push(const Key &key) {
|
||||||
if (this->IsFull()) {
|
STS_ASSERT(!this->IsFull());
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
this->keys[this->GetCount()] = key;
|
this->keys[this->GetCount()] = key;
|
||||||
this->IncrementCount();
|
this->IncrementCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
Key Pop() {
|
Key Pop() {
|
||||||
if (this->IsEmpty()) {
|
STS_ASSERT(!this->IsEmpty());
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
this->RemoveIndex(0);
|
this->RemoveIndex(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,9 +96,7 @@ namespace sts::kvdb {
|
||||||
static_assert(std::is_pod<Value>::value && !std::is_pointer<Value>::value, "Invalid FileKeyValueStore Value!");
|
static_assert(std::is_pod<Value>::value && !std::is_pointer<Value>::value, "Invalid FileKeyValueStore Value!");
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
R_TRY(this->Get(&size, out_value, sizeof(Value), key));
|
R_TRY(this->Get(&size, out_value, sizeof(Value), key));
|
||||||
if (size < sizeof(Value)) {
|
STS_ASSERT(size >= sizeof(Value));
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,9 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include "../defines.hpp"
|
||||||
|
#include "../results.hpp"
|
||||||
|
#include "../util.hpp"
|
||||||
#include "kvdb_auto_buffer.hpp"
|
#include "kvdb_auto_buffer.hpp"
|
||||||
#include "kvdb_archive.hpp"
|
#include "kvdb_archive.hpp"
|
||||||
#include "kvdb_bounded_string.hpp"
|
#include "kvdb_bounded_string.hpp"
|
||||||
|
@ -47,9 +50,7 @@ namespace sts::kvdb {
|
||||||
Value *GetValuePointer() {
|
Value *GetValuePointer() {
|
||||||
/* Size check. Note: Nintendo does not size check. */
|
/* Size check. Note: Nintendo does not size check. */
|
||||||
if constexpr (!std::is_same<Value, void>::value) {
|
if constexpr (!std::is_same<Value, void>::value) {
|
||||||
if (sizeof(Value) > this->value_size) {
|
STS_ASSERT(sizeof(Value) <= this->value_size);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
/* Ensure we only get pod. */
|
/* Ensure we only get pod. */
|
||||||
static_assert(std::is_pod<Value>::value, "KeyValueStore Values must be pod");
|
static_assert(std::is_pod<Value>::value, "KeyValueStore Values must be pod");
|
||||||
}
|
}
|
||||||
|
@ -60,9 +61,7 @@ namespace sts::kvdb {
|
||||||
const Value *GetValuePointer() const {
|
const Value *GetValuePointer() const {
|
||||||
/* Size check. Note: Nintendo does not size check. */
|
/* Size check. Note: Nintendo does not size check. */
|
||||||
if constexpr (!std::is_same<Value, void>::value) {
|
if constexpr (!std::is_same<Value, void>::value) {
|
||||||
if (sizeof(Value) > this->value_size) {
|
STS_ASSERT(sizeof(Value) <= this->value_size);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
/* Ensure we only get pod. */
|
/* Ensure we only get pod. */
|
||||||
static_assert(std::is_pod<Value>::value, "KeyValueStore Values must be pod");
|
static_assert(std::is_pod<Value>::value, "KeyValueStore Values must be pod");
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,10 +17,16 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
|
|
||||||
|
#include "os/os_common_types.hpp"
|
||||||
|
#include "os/os_managed_handle.hpp"
|
||||||
#include "os/os_mutex.hpp"
|
#include "os/os_mutex.hpp"
|
||||||
#include "os/os_condvar.hpp"
|
#include "os/os_condvar.hpp"
|
||||||
#include "os/os_semaphore.hpp"
|
#include "os/os_semaphore.hpp"
|
||||||
#include "os/os_timeout_helper.hpp"
|
#include "os/os_timeout_helper.hpp"
|
||||||
#include "os/os_event.hpp"
|
#include "os/os_event.hpp"
|
||||||
|
#include "os/os_system_event.hpp"
|
||||||
|
#include "os/os_interrupt_event.hpp"
|
||||||
#include "os/os_thread.hpp"
|
#include "os/os_thread.hpp"
|
||||||
#include "os/os_message_queue.hpp"
|
#include "os/os_message_queue.hpp"
|
||||||
|
#include "os/os_waitable_holder.hpp"
|
||||||
|
#include "os/os_waitable_manager.hpp"
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <switch.h>
|
||||||
|
|
||||||
|
namespace sts::os {
|
||||||
|
|
||||||
|
enum class TriBool {
|
||||||
|
False = 0,
|
||||||
|
True = 1,
|
||||||
|
Undefined = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class MessageQueueWaitKind {
|
||||||
|
ForNotEmpty,
|
||||||
|
ForNotFull,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -21,93 +21,33 @@
|
||||||
|
|
||||||
namespace sts::os {
|
namespace sts::os {
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
|
||||||
|
class WaitableObjectList;
|
||||||
|
class WaitableHolderOfEvent;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
class Event {
|
class Event {
|
||||||
|
friend class impl::WaitableHolderOfEvent;
|
||||||
NON_COPYABLE(Event);
|
NON_COPYABLE(Event);
|
||||||
NON_MOVEABLE(Event);
|
NON_MOVEABLE(Event);
|
||||||
private:
|
private:
|
||||||
Mutex m;
|
util::TypedStorage<impl::WaitableObjectList, sizeof(util::IntrusiveListNode), alignof(util::IntrusiveListNode)> waitable_object_list_storage;
|
||||||
|
Mutex lock;
|
||||||
ConditionVariable cv;
|
ConditionVariable cv;
|
||||||
|
u64 counter = 0;
|
||||||
bool auto_clear;
|
bool auto_clear;
|
||||||
bool signaled;
|
bool signaled;
|
||||||
u64 counter = 0;
|
|
||||||
public:
|
public:
|
||||||
Event(bool a = true, bool s = false) : auto_clear(a), signaled(s) {}
|
Event(bool a = true, bool s = false);
|
||||||
|
~Event();
|
||||||
|
|
||||||
void Signal() {
|
void Signal();
|
||||||
std::scoped_lock lk(this->m);
|
void Reset();
|
||||||
|
void Wait();
|
||||||
/* If we're already signaled, nothing more to do. */
|
bool TryWait();
|
||||||
if (this->signaled) {
|
bool TimedWait(u64 ns);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->signaled = true;
|
|
||||||
|
|
||||||
/* Signal! */
|
|
||||||
if (this->auto_clear) {
|
|
||||||
/* If we're auto clear, signal one thread, which will clear. */
|
|
||||||
this->cv.Signal();
|
|
||||||
} else {
|
|
||||||
/* If we're manual clear, increment counter and wake all. */
|
|
||||||
this->counter++;
|
|
||||||
this->cv.Broadcast();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: Waitable auto-wakeup. */
|
|
||||||
}
|
|
||||||
|
|
||||||
void Reset() {
|
|
||||||
std::scoped_lock lk(this->m);
|
|
||||||
this->signaled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Wait() {
|
|
||||||
std::scoped_lock lk(this->m);
|
|
||||||
|
|
||||||
u64 cur_counter = this->counter;
|
|
||||||
while (!this->signaled) {
|
|
||||||
if (this->counter != cur_counter) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this->cv.Wait(&this->m);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->auto_clear) {
|
|
||||||
this->signaled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TryWait() {
|
|
||||||
std::scoped_lock lk(this->m);
|
|
||||||
|
|
||||||
const bool success = this->signaled;
|
|
||||||
if (this->auto_clear) {
|
|
||||||
this->signaled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TimedWait(u64 ns) {
|
|
||||||
TimeoutHelper timeout_helper(ns);
|
|
||||||
std::scoped_lock lk(this->m);
|
|
||||||
|
|
||||||
u64 cur_counter = this->counter;
|
|
||||||
while (!this->signaled) {
|
|
||||||
if (this->counter != cur_counter) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (R_FAILED(this->cv.TimedWait(&this->m, timeout_helper.NsUntilTimeout()))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->auto_clear) {
|
|
||||||
this->signaled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "os_common_types.hpp"
|
||||||
|
#include "os_managed_handle.hpp"
|
||||||
|
|
||||||
|
namespace sts::os {
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
|
||||||
|
class WaitableHolderOfInterruptEvent;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class InterruptEvent {
|
||||||
|
friend class impl::WaitableHolderOfInterruptEvent;
|
||||||
|
NON_COPYABLE(InterruptEvent);
|
||||||
|
NON_MOVEABLE(InterruptEvent);
|
||||||
|
private:
|
||||||
|
ManagedHandle handle;
|
||||||
|
bool auto_clear;
|
||||||
|
bool is_initialized;
|
||||||
|
public:
|
||||||
|
InterruptEvent() : auto_clear(true), is_initialized(false) { }
|
||||||
|
InterruptEvent(u32 interrupt_id, bool autoclear = true);
|
||||||
|
|
||||||
|
Result Initialize(u32 interrupt_id, bool autoclear = true);
|
||||||
|
void Finalize();
|
||||||
|
|
||||||
|
void Reset();
|
||||||
|
void Wait();
|
||||||
|
bool TryWait();
|
||||||
|
bool TimedWait(u64 ns);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <switch.h>
|
||||||
|
#include "../defines.hpp"
|
||||||
|
#include "../results.hpp"
|
||||||
|
|
||||||
|
namespace sts::os {
|
||||||
|
|
||||||
|
class ManagedHandle {
|
||||||
|
NON_COPYABLE(ManagedHandle);
|
||||||
|
private:
|
||||||
|
Handle hnd;
|
||||||
|
public:
|
||||||
|
ManagedHandle() : hnd(INVALID_HANDLE) { /* ... */ }
|
||||||
|
ManagedHandle(Handle h) : hnd(h) { /* ... */ }
|
||||||
|
~ManagedHandle() {
|
||||||
|
if (this->hnd != INVALID_HANDLE) {
|
||||||
|
R_ASSERT(svcCloseHandle(this->hnd));
|
||||||
|
this->hnd = INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ManagedHandle(ManagedHandle&& rhs) {
|
||||||
|
this->hnd = rhs.hnd;
|
||||||
|
rhs.hnd = INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ManagedHandle& operator=(ManagedHandle&& rhs) {
|
||||||
|
rhs.Swap(*this);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit operator bool() const {
|
||||||
|
return this->hnd != INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Swap(ManagedHandle& rhs) {
|
||||||
|
std::swap(this->hnd, rhs.hnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle Get() const {
|
||||||
|
return this->hnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle *GetPointer() {
|
||||||
|
return &this->hnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle *GetPointerAndClear() {
|
||||||
|
this->Clear();
|
||||||
|
return this->GetPointer();
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle Move() {
|
||||||
|
const Handle h = this->hnd;
|
||||||
|
this->hnd = INVALID_HANDLE;
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reset(Handle h) {
|
||||||
|
ManagedHandle(h).Swap(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clear() {
|
||||||
|
this->Reset(INVALID_HANDLE);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -16,15 +16,28 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include "os_common_types.hpp"
|
||||||
#include "os_mutex.hpp"
|
#include "os_mutex.hpp"
|
||||||
#include "os_condvar.hpp"
|
#include "os_condvar.hpp"
|
||||||
|
|
||||||
namespace sts::os {
|
namespace sts::os {
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
|
||||||
|
class WaitableObjectList;
|
||||||
|
|
||||||
|
template<MessageQueueWaitKind WaitKind>
|
||||||
|
class WaitableHolderOfMessageQueue;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
class MessageQueue {
|
class MessageQueue {
|
||||||
|
template<MessageQueueWaitKind WaitKind>
|
||||||
|
friend class impl::WaitableHolderOfMessageQueue;
|
||||||
NON_COPYABLE(MessageQueue);
|
NON_COPYABLE(MessageQueue);
|
||||||
NON_MOVEABLE(MessageQueue);
|
NON_MOVEABLE(MessageQueue);
|
||||||
private:
|
private:
|
||||||
|
util::TypedStorage<impl::WaitableObjectList, sizeof(util::IntrusiveListNode), alignof(util::IntrusiveListNode)> waitable_object_list_storage;
|
||||||
Mutex queue_lock;
|
Mutex queue_lock;
|
||||||
ConditionVariable cv_not_full;
|
ConditionVariable cv_not_full;
|
||||||
ConditionVariable cv_not_empty;
|
ConditionVariable cv_not_empty;
|
||||||
|
@ -47,11 +60,11 @@ namespace sts::os {
|
||||||
uintptr_t ReceiveInternal();
|
uintptr_t ReceiveInternal();
|
||||||
uintptr_t PeekInternal();
|
uintptr_t PeekInternal();
|
||||||
public:
|
public:
|
||||||
MessageQueue(size_t c) : capacity(c) {
|
MessageQueue(std::unique_ptr<uintptr_t[]> buf, size_t c);
|
||||||
this->buffer = std::make_unique<uintptr_t[]>(this->capacity);
|
~MessageQueue();
|
||||||
}
|
|
||||||
|
|
||||||
MessageQueue(std::unique_ptr<uintptr_t[]> buf, size_t c) : buffer(std::move(buf)), capacity(c) { }
|
/* For convenience. */
|
||||||
|
MessageQueue(size_t c) : MessageQueue(std::make_unique<uintptr_t[]>(c), c) { /* ... */ }
|
||||||
|
|
||||||
/* Sending (FIFO functionality) */
|
/* Sending (FIFO functionality) */
|
||||||
void Send(uintptr_t data);
|
void Send(uintptr_t data);
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "os_common_types.hpp"
|
||||||
|
#include "os_event.hpp"
|
||||||
|
|
||||||
|
namespace sts::os {
|
||||||
|
|
||||||
|
class WaitableHolder;
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
|
||||||
|
class InterProcessEvent;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class SystemEventState {
|
||||||
|
Uninitialized,
|
||||||
|
Event,
|
||||||
|
InterProcessEvent,
|
||||||
|
};
|
||||||
|
|
||||||
|
class SystemEvent {
|
||||||
|
friend class WaitableHolder;
|
||||||
|
NON_COPYABLE(SystemEvent);
|
||||||
|
NON_MOVEABLE(SystemEvent);
|
||||||
|
private:
|
||||||
|
union {
|
||||||
|
util::TypedStorage<Event, sizeof(Event), alignof(Event)> storage_for_event;
|
||||||
|
util::TypedStorage<impl::InterProcessEvent, 3 * sizeof(Handle), alignof(Handle)> storage_for_inter_process_event;
|
||||||
|
};
|
||||||
|
SystemEventState state;
|
||||||
|
private:
|
||||||
|
Event &GetEvent();
|
||||||
|
const Event &GetEvent() const;
|
||||||
|
impl::InterProcessEvent &GetInterProcessEvent();
|
||||||
|
const impl::InterProcessEvent &GetInterProcessEvent() const;
|
||||||
|
public:
|
||||||
|
SystemEvent() : state(SystemEventState::Uninitialized) { /* ... */ }
|
||||||
|
SystemEvent(bool inter_process, bool autoclear = true);
|
||||||
|
SystemEvent(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear = true);
|
||||||
|
SystemEvent(Handle read_handle, bool manage_read_handle, bool autoclear = true) : SystemEvent(read_handle, manage_read_handle, INVALID_HANDLE, false, autoclear) { /* ... */ }
|
||||||
|
~SystemEvent();
|
||||||
|
|
||||||
|
Result InitializeAsEvent(bool autoclear = true);
|
||||||
|
Result InitializeAsInterProcessEvent(bool autoclear = true);
|
||||||
|
void AttachHandles(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear = true);
|
||||||
|
void AttachReadableHandle(Handle read_handle, bool manage_read_handle, bool autoclear = true);
|
||||||
|
void AttachWritableHandle(Handle write_handle, bool manage_write_handle, bool autoclear = true);
|
||||||
|
Handle DetachReadableHandle();
|
||||||
|
Handle DetachWritableHandle();
|
||||||
|
Handle GetReadableHandle() const;
|
||||||
|
Handle GetWritableHandle() const;
|
||||||
|
void Finalize();
|
||||||
|
|
||||||
|
SystemEventState GetState() const {
|
||||||
|
return this->state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Signal();
|
||||||
|
void Reset();
|
||||||
|
void Wait();
|
||||||
|
bool TryWait();
|
||||||
|
bool TimedWait(u64 ns);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -37,6 +37,10 @@ namespace sts::os {
|
||||||
return threadStart(&this->thr);
|
return threadStart(&this->thr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result Wait() {
|
||||||
|
return threadWaitForExit(&this->thr);
|
||||||
|
}
|
||||||
|
|
||||||
Result Join() {
|
Result Join() {
|
||||||
R_TRY(threadWaitForExit(&this->thr));
|
R_TRY(threadWaitForExit(&this->thr));
|
||||||
R_TRY(threadClose(&this->thr));
|
R_TRY(threadClose(&this->thr));
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "../util.hpp"
|
||||||
|
#include "os_common_types.hpp"
|
||||||
|
|
||||||
|
namespace sts::os {
|
||||||
|
|
||||||
|
class WaitableManager;
|
||||||
|
|
||||||
|
class Event;
|
||||||
|
class SystemEvent;
|
||||||
|
class InterruptEvent;
|
||||||
|
class Thread;
|
||||||
|
class MessageQueue;
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
|
||||||
|
class WaitableHolderImpl;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class WaitableHolder {
|
||||||
|
friend class WaitableManager;
|
||||||
|
NON_COPYABLE(WaitableHolder);
|
||||||
|
NON_MOVEABLE(WaitableHolder);
|
||||||
|
private:
|
||||||
|
util::TypedStorage<impl::WaitableHolderImpl, 2 * sizeof(util::IntrusiveListNode) + 3 * sizeof(void *), alignof(void *)> impl_storage;
|
||||||
|
uintptr_t user_data;
|
||||||
|
public:
|
||||||
|
static constexpr size_t ImplStorageSize = sizeof(impl_storage);
|
||||||
|
public:
|
||||||
|
WaitableHolder(Handle handle);
|
||||||
|
WaitableHolder(Event *event);
|
||||||
|
WaitableHolder(SystemEvent *event);
|
||||||
|
WaitableHolder(InterruptEvent *event);
|
||||||
|
WaitableHolder(Thread *thread);
|
||||||
|
WaitableHolder(MessageQueue *message_queue, MessageQueueWaitKind wait_kind);
|
||||||
|
|
||||||
|
~WaitableHolder();
|
||||||
|
|
||||||
|
void SetUserData(uintptr_t data) {
|
||||||
|
this->user_data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t GetUserData() const {
|
||||||
|
return this->user_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnlinkFromWaitableManager();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "../util.hpp"
|
||||||
|
#include "os_common_types.hpp"
|
||||||
|
#include "os_mutex.hpp"
|
||||||
|
|
||||||
|
namespace sts::os {
|
||||||
|
|
||||||
|
class WaitableHolder;
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
|
||||||
|
class WaitableManagerImpl;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class WaitableManager {
|
||||||
|
NON_COPYABLE(WaitableManager);
|
||||||
|
NON_MOVEABLE(WaitableManager);
|
||||||
|
private:
|
||||||
|
util::TypedStorage<impl::WaitableManagerImpl, sizeof(util::IntrusiveListNode) + sizeof(Mutex) + 2 * sizeof(void *) + sizeof(Handle), alignof(void *)> impl_storage;
|
||||||
|
public:
|
||||||
|
static constexpr size_t ImplStorageSize = sizeof(impl_storage);
|
||||||
|
public:
|
||||||
|
WaitableManager();
|
||||||
|
~WaitableManager();
|
||||||
|
|
||||||
|
/* Wait. */
|
||||||
|
WaitableHolder *WaitAny();
|
||||||
|
WaitableHolder *TryWaitAny();
|
||||||
|
WaitableHolder *TimedWaitAny(u64 timeout);
|
||||||
|
|
||||||
|
/* Link. */
|
||||||
|
void LinkWaitableHolder(WaitableHolder *holder);
|
||||||
|
void UnlinkAll();
|
||||||
|
void MoveAllFrom(WaitableManager *other);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -32,6 +32,7 @@
|
||||||
#include "results/kvdb_results.hpp"
|
#include "results/kvdb_results.hpp"
|
||||||
#include "results/loader_results.hpp"
|
#include "results/loader_results.hpp"
|
||||||
#include "results/lr_results.hpp"
|
#include "results/lr_results.hpp"
|
||||||
|
#include "results/os_results.hpp"
|
||||||
#include "results/ncm_results.hpp"
|
#include "results/ncm_results.hpp"
|
||||||
#include "results/pm_results.hpp"
|
#include "results/pm_results.hpp"
|
||||||
#include "results/ro_results.hpp"
|
#include "results/ro_results.hpp"
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <switch.h>
|
||||||
|
|
||||||
|
static constexpr u32 Module_Os = 3;
|
||||||
|
|
||||||
|
static constexpr Result ResultOsOutOfMemory = MAKERESULT(Module_Os, 8);
|
||||||
|
static constexpr Result ResultOsResourceExhausted = MAKERESULT(Module_Os, 9);
|
|
@ -28,6 +28,14 @@ extern "C" {
|
||||||
#define R_ASSERT_IMPL(res) fatalSimple(res)
|
#define R_ASSERT_IMPL(res) fatalSimple(res)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/// Evaluates a boolean expression, and returns a result unless that expression is true.
|
||||||
|
#define R_UNLESS(expr, res) \
|
||||||
|
({ \
|
||||||
|
if (!(expr)) { \
|
||||||
|
return static_cast<Result>(res); \
|
||||||
|
} \
|
||||||
|
})
|
||||||
|
|
||||||
#define R_ASSERT(res_expr) \
|
#define R_ASSERT(res_expr) \
|
||||||
({ \
|
({ \
|
||||||
const Result _tmp_r_assert_rc = res_expr; \
|
const Result _tmp_r_assert_rc = res_expr; \
|
||||||
|
@ -83,6 +91,14 @@ extern "C" {
|
||||||
} \
|
} \
|
||||||
})
|
})
|
||||||
|
|
||||||
|
#define R_END_TRY_CATCH_WITH_ASSERT \
|
||||||
|
else { \
|
||||||
|
R_ASSERT(R_TRY_CATCH_RESULT); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
/// Evaluates an expression that returns a result, and returns the result (after evaluating a cleanup expression) if it would fail.
|
/// Evaluates an expression that returns a result, and returns the result (after evaluating a cleanup expression) if it would fail.
|
||||||
#define R_CLEANUP_RESULT _tmp_r_try_cleanup_rc
|
#define R_CLEANUP_RESULT _tmp_r_try_cleanup_rc
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include "../defines.hpp"
|
||||||
|
|
||||||
namespace sts::ro {
|
namespace sts::ro {
|
||||||
|
|
||||||
|
@ -61,9 +62,7 @@ namespace sts::ro {
|
||||||
|
|
||||||
ModuleType GetType() const {
|
ModuleType GetType() const {
|
||||||
const ModuleType type = static_cast<ModuleType>(this->type);
|
const ModuleType type = static_cast<ModuleType>(this->type);
|
||||||
if (type >= ModuleType::Count) {
|
STS_ASSERT(type < ModuleType::Count);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,62 +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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Scope guard logic lovingly taken from Andrei Alexandrescu's "Systemic Error Handling in C++" */
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
template<class F>
|
|
||||||
class ScopeGuard {
|
|
||||||
private:
|
|
||||||
F f;
|
|
||||||
bool active;
|
|
||||||
public:
|
|
||||||
ScopeGuard(F f) : f(std::move(f)), active(true) { }
|
|
||||||
~ScopeGuard() { if (active) { f(); } }
|
|
||||||
void Cancel() { active = false; }
|
|
||||||
|
|
||||||
ScopeGuard() = delete;
|
|
||||||
ScopeGuard(const ScopeGuard &) = delete;
|
|
||||||
ScopeGuard& operator=(const ScopeGuard&) = delete;
|
|
||||||
ScopeGuard(ScopeGuard&& rhs) : f(std::move(rhs.f)), active(rhs.active) {
|
|
||||||
rhs.Cancel();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class F>
|
|
||||||
ScopeGuard<F> MakeScopeGuard(F f) {
|
|
||||||
return ScopeGuard<F>(std::move(f));
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class ScopeGuardOnExit {};
|
|
||||||
|
|
||||||
template <typename F>
|
|
||||||
ScopeGuard<F> operator+(ScopeGuardOnExit, F&& f) {
|
|
||||||
return ScopeGuard<F>(std::forward<F>(f));
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CONCATENATE_IMPL(S1, s2) s1##s2
|
|
||||||
#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)
|
|
||||||
|
|
||||||
#ifdef __COUNTER__
|
|
||||||
#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __COUNTER__)
|
|
||||||
#else
|
|
||||||
#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __LINE__)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define SCOPE_GUARD ScopeGuardOnExit() + [&]()
|
|
||||||
#define ON_SCOPE_EXIT auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_) = SCOPE_GUARD
|
|
|
@ -114,9 +114,7 @@ namespace sts::sm {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Initialize() {
|
Result Initialize() {
|
||||||
if (this->has_initialized) {
|
STS_ASSERT(!this->has_initialized);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
DoWithSmSession([&]() {
|
DoWithSmSession([&]() {
|
||||||
this->result = Initializer();
|
this->result = Initializer();
|
||||||
|
@ -127,9 +125,7 @@ namespace sts::sm {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Finalize() {
|
void Finalize() {
|
||||||
if (!this->has_initialized) {
|
STS_ASSERT(this->has_initialized);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
Finalizer();
|
Finalizer();
|
||||||
this->has_initialized = false;
|
this->has_initialized = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -202,4 +202,10 @@ namespace sts::svc {
|
||||||
CreateProcessFlag_OptimizeMemoryAllocation = (1 << 11),
|
CreateProcessFlag_OptimizeMemoryAllocation = (1 << 11),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Type for svcCreateInterruptEvent. */
|
||||||
|
enum InterruptType : u32 {
|
||||||
|
InterruptType_Edge = 0,
|
||||||
|
InterruptType_Level = 1,
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
|
|
||||||
#include "util/util_compression.hpp"
|
#include "util/util_alignment.hpp"
|
||||||
#include "util/util_ini.hpp"
|
#include "util/util_size.hpp"
|
||||||
|
#include "util/util_scope_guard.hpp"
|
||||||
|
#include "util/util_typed_storage.hpp"
|
||||||
#include "util/util_intrusive_list.hpp"
|
#include "util/util_intrusive_list.hpp"
|
||||||
#include "util/util_intrusive_red_black_tree.hpp"
|
#include "util/util_intrusive_red_black_tree.hpp"
|
||||||
|
#include "util/util_compression.hpp"
|
||||||
|
#include "util/util_ini.hpp"
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <switch.h>
|
||||||
|
#include "../defines.hpp"
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace sts::util {
|
||||||
|
|
||||||
|
/* Utilities for alignment to power of two. */
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static constexpr inline T AlignUp(T value, size_t alignment) {
|
||||||
|
using U = typename std::make_unsigned<T>::type;
|
||||||
|
const U invmask = static_cast<U>(alignment - 1);
|
||||||
|
return static_cast<T>((value + invmask) & ~invmask);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static constexpr inline T AlignDown(T value, size_t alignment) {
|
||||||
|
using U = typename std::make_unsigned<T>::type;
|
||||||
|
const U invmask = static_cast<U>(alignment - 1);
|
||||||
|
return static_cast<T>(value & ~invmask);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static constexpr inline bool IsAligned(T value, size_t alignment) {
|
||||||
|
using U = typename std::make_unsigned<T>::type;
|
||||||
|
const U invmask = static_cast<U>(alignment - 1);
|
||||||
|
return (value & invmask) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -49,10 +49,8 @@ namespace sts::util {
|
||||||
private:
|
private:
|
||||||
void LinkPrev(IntrusiveListNode *node) {
|
void LinkPrev(IntrusiveListNode *node) {
|
||||||
/* We can't link an already linked node. */
|
/* We can't link an already linked node. */
|
||||||
if (node->IsLinked()) {
|
STS_ASSERT(!node->IsLinked());
|
||||||
std::abort();
|
this->SplicePrev(node, node);
|
||||||
}
|
|
||||||
return this->SplicePrev(node, node);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SplicePrev(IntrusiveListNode *first, IntrusiveListNode *last) {
|
void SplicePrev(IntrusiveListNode *first, IntrusiveListNode *last) {
|
||||||
|
@ -66,9 +64,7 @@ namespace sts::util {
|
||||||
|
|
||||||
void LinkNext(IntrusiveListNode *node) {
|
void LinkNext(IntrusiveListNode *node) {
|
||||||
/* We can't link an already linked node. */
|
/* We can't link an already linked node. */
|
||||||
if (node->IsLinked()) {
|
STS_ASSERT(!node->IsLinked());
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
return this->SpliceNext(node, node);
|
return this->SpliceNext(node, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,17 +210,13 @@ namespace sts::util {
|
||||||
|
|
||||||
iterator iterator_to(reference v) {
|
iterator iterator_to(reference v) {
|
||||||
/* Only allow iterator_to for values in lists. */
|
/* Only allow iterator_to for values in lists. */
|
||||||
if (!v.IsLinked()) {
|
STS_ASSERT(v.IsLinked());
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
return iterator(&v);
|
return iterator(&v);
|
||||||
}
|
}
|
||||||
|
|
||||||
const_iterator iterator_to(const_reference v) const {
|
const_iterator iterator_to(const_reference v) const {
|
||||||
/* Only allow iterator_to for values in lists. */
|
/* Only allow iterator_to for values in lists. */
|
||||||
if (!v.IsLinked()) {
|
STS_ASSERT(v.IsLinked());
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
return const_iterator(&v);
|
return const_iterator(&v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -488,23 +480,23 @@ namespace sts::util {
|
||||||
}
|
}
|
||||||
|
|
||||||
reference back() {
|
reference back() {
|
||||||
if (this->impl.empty()) { std::abort(); }
|
STS_ASSERT(!this->impl.empty());
|
||||||
return this->impl.back();
|
return GetParent(this->impl.back());
|
||||||
}
|
}
|
||||||
|
|
||||||
const_reference back() const {
|
const_reference back() const {
|
||||||
if (this->impl.empty()) { std::abort(); }
|
STS_ASSERT(!this->impl.empty());
|
||||||
return this->impl.back();
|
return GetParent(this->impl.back());
|
||||||
}
|
}
|
||||||
|
|
||||||
reference front() {
|
reference front() {
|
||||||
if (this->impl.empty()) { std::abort(); }
|
STS_ASSERT(!this->impl.empty());
|
||||||
return this->impl.front();
|
return GetParent(this->impl.front());
|
||||||
}
|
}
|
||||||
|
|
||||||
const_reference front() const {
|
const_reference front() const {
|
||||||
if (this->impl.empty()) { std::abort(); }
|
STS_ASSERT(!this->impl.empty());
|
||||||
return this->impl.front();
|
return GetParent(this->impl.front());
|
||||||
}
|
}
|
||||||
|
|
||||||
void push_back(reference ref) {
|
void push_back(reference ref) {
|
||||||
|
@ -516,13 +508,13 @@ namespace sts::util {
|
||||||
}
|
}
|
||||||
|
|
||||||
void pop_back() {
|
void pop_back() {
|
||||||
if (this->impl.empty()) { std::abort(); }
|
STS_ASSERT(!this->impl.empty());
|
||||||
return this->impl.pop_back();
|
this->impl.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
void pop_front() {
|
void pop_front() {
|
||||||
if (this->impl.empty()) { std::abort(); }
|
STS_ASSERT(!this->impl.empty());
|
||||||
return this->impl.pop_front();
|
this->impl.pop_front();
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator insert(const_iterator pos, reference ref) {
|
iterator insert(const_iterator pos, reference ref) {
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Scope guard logic lovingly taken from Andrei Alexandrescu's "Systemic Error Handling in C++" */
|
||||||
|
#pragma once
|
||||||
|
#include <utility>
|
||||||
|
#include "../defines.hpp"
|
||||||
|
|
||||||
|
namespace sts::util {
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
|
||||||
|
template<class F>
|
||||||
|
class ScopeGuard {
|
||||||
|
NON_COPYABLE(ScopeGuard);
|
||||||
|
private:
|
||||||
|
F f;
|
||||||
|
bool active;
|
||||||
|
public:
|
||||||
|
constexpr ScopeGuard(F f) : f(std::move(f)), active(true) { }
|
||||||
|
~ScopeGuard() { if (active) { f(); } }
|
||||||
|
void Cancel() { active = false; }
|
||||||
|
|
||||||
|
ScopeGuard(ScopeGuard&& rhs) : f(std::move(rhs.f)), active(rhs.active) {
|
||||||
|
rhs.Cancel();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class F>
|
||||||
|
constexpr ScopeGuard<F> MakeScopeGuard(F f) {
|
||||||
|
return ScopeGuard<F>(std::move(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class ScopeGuardOnExit {};
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
constexpr ScopeGuard<F> operator+(ScopeGuardOnExit, F&& f) {
|
||||||
|
return ScopeGuard<F>(std::forward<F>(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SCOPE_GUARD ::sts::util::impl::ScopeGuardOnExit() + [&]()
|
||||||
|
#define ON_SCOPE_EXIT auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_) = SCOPE_GUARD
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <switch.h>
|
||||||
|
|
||||||
|
namespace sts::util {
|
||||||
|
|
||||||
|
/* std::size() does not support zero-size C arrays. We're fixing that. */
|
||||||
|
template<class C>
|
||||||
|
constexpr auto size(const C& c) -> decltype(c.size()) {
|
||||||
|
return std::size(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
constexpr std::size_t size(const C& c) {
|
||||||
|
if constexpr (sizeof(C) == 0) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return std::size(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <switch.h>
|
||||||
|
#include "../defines.hpp"
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace sts::util {
|
||||||
|
|
||||||
|
template<typename T, size_t Size, size_t Align>
|
||||||
|
struct TypedStorage {
|
||||||
|
typename std::aligned_storage<Size, Align>::type _storage;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define TYPED_STORAGE(T) ::sts::util::TypedStorage<T, sizeof(T), alignof(T)>
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static constexpr inline __attribute__((always_inline)) T *GetPointer(TYPED_STORAGE(T) &ts) {
|
||||||
|
return reinterpret_cast<T *>(&ts._storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static constexpr inline __attribute__((always_inline)) const T *GetPointer(const TYPED_STORAGE(T) &ts) {
|
||||||
|
return reinterpret_cast<const T *>(&ts._storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static constexpr inline __attribute__((always_inline)) T &GetReference(TYPED_STORAGE(T) &ts) {
|
||||||
|
return *GetPointer(ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static constexpr inline __attribute__((always_inline)) const T &GetReference(const TYPED_STORAGE(T) &ts) {
|
||||||
|
return *GetPointer(ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -23,11 +23,10 @@
|
||||||
#include "results.hpp"
|
#include "results.hpp"
|
||||||
#include "os.hpp"
|
#include "os.hpp"
|
||||||
#include "waitable_manager_base.hpp"
|
#include "waitable_manager_base.hpp"
|
||||||
#include "event.hpp"
|
|
||||||
#include "ipc.hpp"
|
#include "ipc.hpp"
|
||||||
#include "servers.hpp"
|
#include "servers.hpp"
|
||||||
|
|
||||||
#include "scope_guard.hpp"
|
#include "util.hpp"
|
||||||
|
|
||||||
static inline Handle GetCurrentThreadHandle() {
|
static inline Handle GetCurrentThreadHandle() {
|
||||||
return threadGetCurHandle();
|
return threadGetCurHandle();
|
||||||
|
|
|
@ -81,9 +81,7 @@ static void _CacheValues(void)
|
||||||
args.X[1] = 0; /* NAND */
|
args.X[1] = 0; /* NAND */
|
||||||
args.X[2] = reinterpret_cast<u64>(&paths); /* path output */
|
args.X[2] = reinterpret_cast<u64>(&paths); /* path output */
|
||||||
R_ASSERT(svcCallSecureMonitor(&args));
|
R_ASSERT(svcCallSecureMonitor(&args));
|
||||||
if (args.X[0] != 0) {
|
STS_ASSERT(args.X[0] == 0);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
std::memcpy(&g_exo_emummc_config, &args.X[1], sizeof(args) - sizeof(args.X[0]));
|
std::memcpy(&g_exo_emummc_config, &args.X[1], sizeof(args) - sizeof(args.X[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,9 +40,7 @@ static void _CacheValues(void)
|
||||||
args.X[0] = 0xC3000002; /* smcGetConfig */
|
args.X[0] = 0xC3000002; /* smcGetConfig */
|
||||||
args.X[1] = 65000; /* ConfigItem_ExosphereVersion */
|
args.X[1] = 65000; /* ConfigItem_ExosphereVersion */
|
||||||
R_ASSERT(svcCallSecureMonitor(&args));
|
R_ASSERT(svcCallSecureMonitor(&args));
|
||||||
if (args.X[0] != 0) {
|
STS_ASSERT(args.X[0] == 0);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
target_fw = (args.X[1] >> 0x08) & 0xFF;
|
target_fw = (args.X[1] >> 0x08) & 0xFF;
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,9 +90,7 @@ namespace sts::kvdb {
|
||||||
|
|
||||||
Result ArchiveReader::ReadEntryCount(size_t *out) {
|
Result ArchiveReader::ReadEntryCount(size_t *out) {
|
||||||
/* This should only be called at the start of reading stream. */
|
/* This should only be called at the start of reading stream. */
|
||||||
if (this->offset != 0) {
|
STS_ASSERT(this->offset == 0);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read and validate header. */
|
/* Read and validate header. */
|
||||||
ArchiveHeader header;
|
ArchiveHeader header;
|
||||||
|
@ -105,9 +103,7 @@ namespace sts::kvdb {
|
||||||
|
|
||||||
Result ArchiveReader::GetEntrySize(size_t *out_key_size, size_t *out_value_size) {
|
Result ArchiveReader::GetEntrySize(size_t *out_key_size, size_t *out_value_size) {
|
||||||
/* This should only be called after ReadEntryCount. */
|
/* This should only be called after ReadEntryCount. */
|
||||||
if (this->offset == 0) {
|
STS_ASSERT(this->offset != 0);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Peek the next entry header. */
|
/* Peek the next entry header. */
|
||||||
ArchiveEntryHeader header;
|
ArchiveEntryHeader header;
|
||||||
|
@ -121,9 +117,7 @@ namespace sts::kvdb {
|
||||||
|
|
||||||
Result ArchiveReader::ReadEntry(void *out_key, size_t key_size, void *out_value, size_t value_size) {
|
Result ArchiveReader::ReadEntry(void *out_key, size_t key_size, void *out_value, size_t value_size) {
|
||||||
/* This should only be called after ReadEntryCount. */
|
/* This should only be called after ReadEntryCount. */
|
||||||
if (this->offset == 0) {
|
STS_ASSERT(this->offset != 0);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read the next entry header. */
|
/* Read the next entry header. */
|
||||||
ArchiveEntryHeader header;
|
ArchiveEntryHeader header;
|
||||||
|
@ -131,9 +125,8 @@ namespace sts::kvdb {
|
||||||
R_TRY(header.Validate());
|
R_TRY(header.Validate());
|
||||||
|
|
||||||
/* Key size and Value size must be correct. */
|
/* Key size and Value size must be correct. */
|
||||||
if (key_size != header.key_size || value_size != header.value_size) {
|
STS_ASSERT(key_size == header.key_size);
|
||||||
std::abort();
|
STS_ASSERT(value_size == header.value_size);
|
||||||
}
|
|
||||||
|
|
||||||
R_ASSERT(this->Read(out_key, key_size));
|
R_ASSERT(this->Read(out_key, key_size));
|
||||||
R_ASSERT(this->Read(out_value, value_size));
|
R_ASSERT(this->Read(out_value, value_size));
|
||||||
|
@ -154,9 +147,7 @@ namespace sts::kvdb {
|
||||||
|
|
||||||
void ArchiveWriter::WriteHeader(size_t entry_count) {
|
void ArchiveWriter::WriteHeader(size_t entry_count) {
|
||||||
/* This should only be called at start of write. */
|
/* This should only be called at start of write. */
|
||||||
if (this->offset != 0) {
|
STS_ASSERT(this->offset == 0);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
ArchiveHeader header = ArchiveHeader::Make(entry_count);
|
ArchiveHeader header = ArchiveHeader::Make(entry_count);
|
||||||
R_ASSERT(this->Write(&header, sizeof(header)));
|
R_ASSERT(this->Write(&header, sizeof(header)));
|
||||||
|
@ -164,9 +155,7 @@ namespace sts::kvdb {
|
||||||
|
|
||||||
void ArchiveWriter::WriteEntry(const void *key, size_t key_size, const void *value, size_t value_size) {
|
void ArchiveWriter::WriteEntry(const void *key, size_t key_size, const void *value, size_t value_size) {
|
||||||
/* This should only be called after writing header. */
|
/* This should only be called after writing header. */
|
||||||
if (this->offset == 0) {
|
STS_ASSERT(this->offset != 0);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
ArchiveEntryHeader header = ArchiveEntryHeader::Make(key_size, value_size);
|
ArchiveEntryHeader header = ArchiveEntryHeader::Make(key_size, value_size);
|
||||||
R_ASSERT(this->Write(&header, sizeof(header)));
|
R_ASSERT(this->Write(&header, sizeof(header)));
|
||||||
|
|
|
@ -57,9 +57,7 @@ namespace sts::kvdb {
|
||||||
this->backing_buffer_free_offset = 0;
|
this->backing_buffer_free_offset = 0;
|
||||||
this->count = 0;
|
this->count = 0;
|
||||||
this->entries = static_cast<decltype(this->entries)>(this->Allocate(sizeof(*this->entries) * this->capacity));
|
this->entries = static_cast<decltype(this->entries)>(this->Allocate(sizeof(*this->entries) * this->capacity));
|
||||||
if (this->entries == nullptr) {
|
STS_ASSERT(this->entries != nullptr);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<size_t> FileKeyValueStore::Cache::TryGet(void *out_value, size_t max_out_size, const void *key, size_t key_size) {
|
std::optional<size_t> FileKeyValueStore::Cache::TryGet(void *out_value, size_t max_out_size, const void *key, size_t key_size) {
|
||||||
|
@ -106,9 +104,7 @@ namespace sts::kvdb {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ensure key size is small enough. */
|
/* Ensure key size is small enough. */
|
||||||
if (key_size > MaxKeySize) {
|
STS_ASSERT(key_size <= MaxKeySize);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we're at capacity, invalidate the cache. */
|
/* If we're at capacity, invalidate the cache. */
|
||||||
if (this->count == this->capacity) {
|
if (this->count == this->capacity) {
|
||||||
|
|
|
@ -0,0 +1,182 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "os_inter_process_event.hpp"
|
||||||
|
|
||||||
|
namespace sts::os::impl {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
Result CreateEventHandles(Handle *out_readable, Handle *out_writable) {
|
||||||
|
/* Create the event handles. */
|
||||||
|
R_TRY_CATCH(svcCreateEvent(out_writable, out_readable)) {
|
||||||
|
R_CATCH(ResultKernelResourceExhausted) {
|
||||||
|
return ResultOsResourceExhausted;
|
||||||
|
}
|
||||||
|
} R_END_TRY_CATCH_WITH_ASSERT;
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
InterProcessEvent::InterProcessEvent(bool autoclear) : is_initialized(false) {
|
||||||
|
R_ASSERT(this->Initialize(autoclear));
|
||||||
|
}
|
||||||
|
|
||||||
|
InterProcessEvent::~InterProcessEvent() {
|
||||||
|
this->Finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InterProcessEvent::Initialize(bool autoclear) {
|
||||||
|
STS_ASSERT(!this->is_initialized);
|
||||||
|
Handle rh, wh;
|
||||||
|
R_TRY(CreateEventHandles(&rh, &wh));
|
||||||
|
this->Initialize(rh, true, wh, true, autoclear);
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterProcessEvent::Initialize(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear) {
|
||||||
|
STS_ASSERT(!this->is_initialized);
|
||||||
|
STS_ASSERT(read_handle != INVALID_HANDLE || write_handle != INVALID_HANDLE);
|
||||||
|
this->read_handle = read_handle;
|
||||||
|
this->manage_read_handle = manage_read_handle;
|
||||||
|
this->write_handle = write_handle;
|
||||||
|
this->manage_write_handle = manage_write_handle;
|
||||||
|
this->auto_clear = autoclear;
|
||||||
|
this->is_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle InterProcessEvent::DetachReadableHandle() {
|
||||||
|
STS_ASSERT(this->is_initialized);
|
||||||
|
const Handle handle = this->read_handle;
|
||||||
|
STS_ASSERT(handle != INVALID_HANDLE);
|
||||||
|
this->read_handle = INVALID_HANDLE;
|
||||||
|
this->manage_read_handle = false;
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle InterProcessEvent::DetachWritableHandle() {
|
||||||
|
STS_ASSERT(this->is_initialized);
|
||||||
|
const Handle handle = this->write_handle;
|
||||||
|
STS_ASSERT(handle != INVALID_HANDLE);
|
||||||
|
this->write_handle = INVALID_HANDLE;
|
||||||
|
this->manage_write_handle = false;
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle InterProcessEvent::GetReadableHandle() const {
|
||||||
|
STS_ASSERT(this->is_initialized);
|
||||||
|
return this->read_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle InterProcessEvent::GetWritableHandle() const {
|
||||||
|
STS_ASSERT(this->is_initialized);
|
||||||
|
return this->write_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterProcessEvent::Finalize() {
|
||||||
|
if (this->is_initialized) {
|
||||||
|
if (this->manage_read_handle && this->read_handle != INVALID_HANDLE) {
|
||||||
|
R_ASSERT(svcCloseHandle(this->read_handle));
|
||||||
|
}
|
||||||
|
if (this->manage_write_handle && this->write_handle != INVALID_HANDLE) {
|
||||||
|
R_ASSERT(svcCloseHandle(this->write_handle));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->read_handle = INVALID_HANDLE;
|
||||||
|
this->manage_read_handle = false;
|
||||||
|
this->write_handle = INVALID_HANDLE;
|
||||||
|
this->manage_write_handle = false;
|
||||||
|
this->is_initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterProcessEvent::Signal() {
|
||||||
|
R_ASSERT(svcSignalEvent(this->GetWritableHandle()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterProcessEvent::Reset() {
|
||||||
|
Handle handle = this->GetReadableHandle();
|
||||||
|
if (handle == INVALID_HANDLE) {
|
||||||
|
handle = this->GetWritableHandle();
|
||||||
|
}
|
||||||
|
R_ASSERT(svcClearEvent(handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterProcessEvent::Wait() {
|
||||||
|
const Handle handle = this->GetReadableHandle();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
/* Continuously wait, until success. */
|
||||||
|
R_TRY_CATCH(svcWaitSynchronizationSingle(handle, U64_MAX)) {
|
||||||
|
R_CATCH(ResultKernelCancelled) { continue; }
|
||||||
|
} R_END_TRY_CATCH_WITH_ASSERT;
|
||||||
|
|
||||||
|
/* Clear, if we must. */
|
||||||
|
if (this->auto_clear) {
|
||||||
|
R_TRY_CATCH(svcResetSignal(handle)) {
|
||||||
|
/* Some other thread might have caught this before we did. */
|
||||||
|
R_CATCH(ResultKernelInvalidState) { continue; }
|
||||||
|
} R_END_TRY_CATCH_WITH_ASSERT;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterProcessEvent::TryWait() {
|
||||||
|
const Handle handle = this->GetReadableHandle();
|
||||||
|
|
||||||
|
if (this->auto_clear) {
|
||||||
|
/* Auto-clear. Just try to reset. */
|
||||||
|
return R_SUCCEEDED(svcResetSignal(handle));
|
||||||
|
} else {
|
||||||
|
/* Not auto-clear. */
|
||||||
|
while (true) {
|
||||||
|
/* Continuously wait, until success or timeout. */
|
||||||
|
R_TRY_CATCH(svcWaitSynchronizationSingle(handle, 0)) {
|
||||||
|
R_CATCH(ResultKernelTimedOut) { return false; }
|
||||||
|
R_CATCH(ResultKernelCancelled) { continue; }
|
||||||
|
} R_END_TRY_CATCH_WITH_ASSERT;
|
||||||
|
|
||||||
|
/* We succeeded, so we're signaled. */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterProcessEvent::TimedWait(u64 ns) {
|
||||||
|
const Handle handle = this->GetReadableHandle();
|
||||||
|
|
||||||
|
TimeoutHelper timeout_helper(ns);
|
||||||
|
while (true) {
|
||||||
|
/* Continuously wait, until success or timeout. */
|
||||||
|
R_TRY_CATCH(svcWaitSynchronizationSingle(handle, timeout_helper.NsUntilTimeout())) {
|
||||||
|
R_CATCH(ResultKernelTimedOut) { return false; }
|
||||||
|
R_CATCH(ResultKernelCancelled) { continue; }
|
||||||
|
} R_END_TRY_CATCH_WITH_ASSERT;
|
||||||
|
|
||||||
|
/* Clear, if we must. */
|
||||||
|
if (this->auto_clear) {
|
||||||
|
R_TRY_CATCH(svcResetSignal(handle)) {
|
||||||
|
/* Some other thread might have caught this before we did. */
|
||||||
|
R_CATCH(ResultKernelInvalidState) { continue; }
|
||||||
|
} R_END_TRY_CATCH_WITH_ASSERT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace sts::os::impl {
|
||||||
|
|
||||||
|
class WaitableHolderOfInterProcessEvent;
|
||||||
|
|
||||||
|
class InterProcessEvent {
|
||||||
|
friend class WaitableHolderOfInterProcessEvent;
|
||||||
|
NON_COPYABLE(InterProcessEvent);
|
||||||
|
NON_MOVEABLE(InterProcessEvent);
|
||||||
|
private:
|
||||||
|
Handle read_handle;
|
||||||
|
Handle write_handle;
|
||||||
|
bool manage_read_handle;
|
||||||
|
bool manage_write_handle;
|
||||||
|
bool auto_clear;
|
||||||
|
bool is_initialized;
|
||||||
|
public:
|
||||||
|
InterProcessEvent() : is_initialized(false) { /* ... */ }
|
||||||
|
InterProcessEvent(bool autoclear);
|
||||||
|
~InterProcessEvent();
|
||||||
|
|
||||||
|
Result Initialize(bool autoclear = true);
|
||||||
|
void Initialize(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear = true);
|
||||||
|
Handle DetachReadableHandle();
|
||||||
|
Handle DetachWritableHandle();
|
||||||
|
Handle GetReadableHandle() const;
|
||||||
|
Handle GetWritableHandle() const;
|
||||||
|
void Finalize();
|
||||||
|
|
||||||
|
void Signal();
|
||||||
|
void Reset();
|
||||||
|
void Wait();
|
||||||
|
bool TryWait();
|
||||||
|
bool TimedWait(u64 ns);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace sts::os::impl {
|
||||||
|
|
||||||
|
class WaitableObjectList;
|
||||||
|
class WaitableManagerImpl;
|
||||||
|
|
||||||
|
class WaitableHolderBase {
|
||||||
|
public:
|
||||||
|
util::IntrusiveListNode manager_node;
|
||||||
|
util::IntrusiveListNode object_list_node;
|
||||||
|
private:
|
||||||
|
WaitableManagerImpl *manager = nullptr;
|
||||||
|
public:
|
||||||
|
/* Gets whether the held waitable is currently signaled. */
|
||||||
|
virtual TriBool IsSignaled() const = 0;
|
||||||
|
/* Adds to manager's object list, returns is signaled. */
|
||||||
|
virtual TriBool LinkToObjectList() = 0;
|
||||||
|
/* Removes from the manager's object list. */
|
||||||
|
virtual void UnlinkFromObjectList() = 0;
|
||||||
|
/* Gets handle to output, returns INVALID_HANDLE on failure. */
|
||||||
|
virtual Handle GetHandle() const = 0;
|
||||||
|
/* Gets the amount of time remaining until this wakes up. */
|
||||||
|
virtual u64 GetWakeupTime() const {
|
||||||
|
return U64_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interface with manager. */
|
||||||
|
void SetManager(WaitableManagerImpl *m) {
|
||||||
|
this->manager = m;
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitableManagerImpl *GetManager() const {
|
||||||
|
return this->manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsLinkedToManager() const {
|
||||||
|
return this->manager != nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class WaitableHolderOfUserObject : public WaitableHolderBase {
|
||||||
|
public:
|
||||||
|
/* All user objects have no handle to wait on. */
|
||||||
|
virtual Handle GetHandle() const override {
|
||||||
|
return INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class WaitableHolderOfKernelObject : public WaitableHolderBase {
|
||||||
|
public:
|
||||||
|
/* All kernel objects have native handles, and thus don't have object list semantics. */
|
||||||
|
virtual TriBool LinkToObjectList() override {
|
||||||
|
return TriBool::Undefined;
|
||||||
|
}
|
||||||
|
virtual void UnlinkFromObjectList() override {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "os_waitable_holder_of_handle.hpp"
|
||||||
|
#include "os_waitable_holder_of_event.hpp"
|
||||||
|
#include "os_waitable_holder_of_inter_process_event.hpp"
|
||||||
|
#include "os_waitable_holder_of_interrupt_event.hpp"
|
||||||
|
#include "os_waitable_holder_of_thread.hpp"
|
||||||
|
#include "os_waitable_holder_of_message_queue.hpp"
|
||||||
|
|
||||||
|
namespace sts::os::impl {
|
||||||
|
|
||||||
|
struct WaitableHolderImpl {
|
||||||
|
union {
|
||||||
|
TYPED_STORAGE(WaitableHolderOfHandle) holder_of_handle_storage;
|
||||||
|
TYPED_STORAGE(WaitableHolderOfEvent) holder_of_event_storage;
|
||||||
|
TYPED_STORAGE(WaitableHolderOfInterProcessEvent) holder_of_inter_process_event_storage;
|
||||||
|
TYPED_STORAGE(WaitableHolderOfInterruptEvent) holder_of_interrupt_event_storage;
|
||||||
|
TYPED_STORAGE(WaitableHolderOfThread) holder_of_thread_storage;
|
||||||
|
TYPED_STORAGE(WaitableHolderOfMessageQueueForNotFull) holder_of_mq_for_not_full_storage;
|
||||||
|
TYPED_STORAGE(WaitableHolderOfMessageQueueForNotEmpty) holder_of_mq_for_not_empty_storage;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CHECK_HOLDER(T) \
|
||||||
|
static_assert(std::is_base_of<::sts::os::impl::WaitableHolderBase, T>::value && std::is_trivially_destructible<T>::value, #T)
|
||||||
|
|
||||||
|
CHECK_HOLDER(WaitableHolderOfHandle);
|
||||||
|
CHECK_HOLDER(WaitableHolderOfEvent);
|
||||||
|
CHECK_HOLDER(WaitableHolderOfInterProcessEvent);
|
||||||
|
CHECK_HOLDER(WaitableHolderOfInterruptEvent);
|
||||||
|
CHECK_HOLDER(WaitableHolderOfThread);
|
||||||
|
CHECK_HOLDER(WaitableHolderOfMessageQueueForNotFull);
|
||||||
|
CHECK_HOLDER(WaitableHolderOfMessageQueueForNotEmpty);
|
||||||
|
|
||||||
|
#undef CHECK_HOLDER
|
||||||
|
|
||||||
|
static_assert(std::is_trivial<WaitableHolderImpl>::value && std::is_trivially_destructible<WaitableHolderImpl>::value, "WaitableHolderImpl");
|
||||||
|
static_assert(sizeof(WaitableHolderImpl) == WaitableHolder::ImplStorageSize, "WaitableHolderImpl size");
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "os_waitable_holder_base.hpp"
|
||||||
|
#include "os_waitable_object_list.hpp"
|
||||||
|
|
||||||
|
namespace sts::os::impl {
|
||||||
|
|
||||||
|
class WaitableHolderOfEvent : public WaitableHolderOfUserObject {
|
||||||
|
private:
|
||||||
|
Event *event;
|
||||||
|
private:
|
||||||
|
TriBool IsSignaledImpl() const {
|
||||||
|
return this->event->signaled ? TriBool::True : TriBool::False;
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
explicit WaitableHolderOfEvent(Event *e) : event(e) { /* ... */ }
|
||||||
|
|
||||||
|
/* IsSignaled, Link, Unlink implemented. */
|
||||||
|
virtual TriBool IsSignaled() const override {
|
||||||
|
std::scoped_lock lk(this->event->lock);
|
||||||
|
return this->IsSignaledImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual TriBool LinkToObjectList() override {
|
||||||
|
std::scoped_lock lk(this->event->lock);
|
||||||
|
|
||||||
|
GetReference(this->event->waitable_object_list_storage).LinkWaitableHolder(*this);
|
||||||
|
return this->IsSignaledImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void UnlinkFromObjectList() override {
|
||||||
|
std::scoped_lock lk(this->event->lock);
|
||||||
|
|
||||||
|
GetReference(this->event->waitable_object_list_storage).UnlinkWaitableHolder(*this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "os_waitable_holder_base.hpp"
|
||||||
|
|
||||||
|
namespace sts::os::impl {
|
||||||
|
|
||||||
|
class WaitableHolderOfHandle : public WaitableHolderOfKernelObject {
|
||||||
|
private:
|
||||||
|
Handle handle;
|
||||||
|
public:
|
||||||
|
explicit WaitableHolderOfHandle(Handle h) : handle(h) { /* ... */ }
|
||||||
|
|
||||||
|
/* IsSignaled, GetHandle both implemented. */
|
||||||
|
virtual TriBool IsSignaled() const override {
|
||||||
|
return TriBool::Undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Handle GetHandle() const override {
|
||||||
|
return this->handle;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "os_waitable_holder_base.hpp"
|
||||||
|
#include "os_inter_process_event.hpp"
|
||||||
|
|
||||||
|
namespace sts::os::impl {
|
||||||
|
|
||||||
|
class WaitableHolderOfInterProcessEvent : public WaitableHolderOfKernelObject {
|
||||||
|
private:
|
||||||
|
InterProcessEvent *event;
|
||||||
|
public:
|
||||||
|
explicit WaitableHolderOfInterProcessEvent(InterProcessEvent *e) : event(e) { /* ... */ }
|
||||||
|
|
||||||
|
/* IsSignaled, GetHandle both implemented. */
|
||||||
|
virtual TriBool IsSignaled() const override {
|
||||||
|
return TriBool::Undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Handle GetHandle() const override {
|
||||||
|
STS_ASSERT(this->event->is_initialized);
|
||||||
|
return this->event->GetReadableHandle();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "os_waitable_holder_base.hpp"
|
||||||
|
|
||||||
|
namespace sts::os::impl {
|
||||||
|
|
||||||
|
class WaitableHolderOfInterruptEvent : public WaitableHolderOfKernelObject {
|
||||||
|
private:
|
||||||
|
InterruptEvent *event;
|
||||||
|
public:
|
||||||
|
explicit WaitableHolderOfInterruptEvent(InterruptEvent *e) : event(e) { /* ... */ }
|
||||||
|
|
||||||
|
/* IsSignaled, GetHandle both implemented. */
|
||||||
|
virtual TriBool IsSignaled() const override {
|
||||||
|
return TriBool::Undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Handle GetHandle() const override {
|
||||||
|
STS_ASSERT(this->event->is_initialized);
|
||||||
|
return this->event->handle.Get();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "os_waitable_holder_base.hpp"
|
||||||
|
#include "os_waitable_object_list.hpp"
|
||||||
|
|
||||||
|
namespace sts::os::impl {
|
||||||
|
|
||||||
|
template<MessageQueueWaitKind WaitKind>
|
||||||
|
class WaitableHolderOfMessageQueue : public WaitableHolderOfUserObject {
|
||||||
|
static_assert(WaitKind == MessageQueueWaitKind::ForNotEmpty || WaitKind == MessageQueueWaitKind::ForNotFull, "MessageQueueHolder WaitKind!");
|
||||||
|
private:
|
||||||
|
MessageQueue *message_queue;
|
||||||
|
private:
|
||||||
|
TriBool IsSignaledImpl() const {
|
||||||
|
if constexpr (WaitKind == MessageQueueWaitKind::ForNotEmpty) {
|
||||||
|
/* ForNotEmpty. */
|
||||||
|
return this->message_queue->IsEmpty() ? TriBool::False : TriBool::True;
|
||||||
|
} else /* if constexpr (WaitKind == MessageQueueWaitKind::ForNotFull) */ {
|
||||||
|
/* ForNotFull */
|
||||||
|
return this->message_queue->IsFull() ? TriBool::False : TriBool::True;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
explicit WaitableHolderOfMessageQueue(MessageQueue *mq) : message_queue(mq) { /* ... */ }
|
||||||
|
|
||||||
|
/* IsSignaled, Link, Unlink implemented. */
|
||||||
|
virtual TriBool IsSignaled() const override {
|
||||||
|
std::scoped_lock lk(this->message_queue->queue_lock);
|
||||||
|
return this->IsSignaledImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual TriBool LinkToObjectList() override {
|
||||||
|
std::scoped_lock lk(this->message_queue->queue_lock);
|
||||||
|
|
||||||
|
GetReference(this->message_queue->waitable_object_list_storage).LinkWaitableHolder(*this);
|
||||||
|
return this->IsSignaledImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void UnlinkFromObjectList() override {
|
||||||
|
std::scoped_lock lk(this->message_queue->queue_lock);
|
||||||
|
|
||||||
|
GetReference(this->message_queue->waitable_object_list_storage).UnlinkWaitableHolder(*this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using WaitableHolderOfMessageQueueForNotEmpty = WaitableHolderOfMessageQueue<MessageQueueWaitKind::ForNotEmpty>;
|
||||||
|
using WaitableHolderOfMessageQueueForNotFull = WaitableHolderOfMessageQueue<MessageQueueWaitKind::ForNotFull>;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "os_waitable_holder_base.hpp"
|
||||||
|
|
||||||
|
namespace sts::os::impl {
|
||||||
|
|
||||||
|
/* Nintendo implements this as a user wait object, operating on Thread state. */
|
||||||
|
/* Libnx doesn't have an equivalent, so we'll use the thread's handle for kernel semantics. */
|
||||||
|
class WaitableHolderOfThread : public WaitableHolderOfKernelObject {
|
||||||
|
private:
|
||||||
|
Thread *thread;
|
||||||
|
public:
|
||||||
|
explicit WaitableHolderOfThread(Thread *t) : thread(t) { /* ... */ }
|
||||||
|
|
||||||
|
/* IsSignaled, GetHandle both implemented. */
|
||||||
|
virtual TriBool IsSignaled() const override {
|
||||||
|
return TriBool::Undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Handle GetHandle() const override {
|
||||||
|
return this->thread->GetHandle();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,177 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#include "os_waitable_manager_impl.hpp"
|
||||||
|
#include "os_waitable_object_list.hpp"
|
||||||
|
|
||||||
|
namespace sts::os::impl{
|
||||||
|
|
||||||
|
WaitableHolderBase *WaitableManagerImpl::WaitAnyImpl(bool infinite, u64 timeout) {
|
||||||
|
/* Set processing thread handle while in scope. */
|
||||||
|
this->waiting_thread_handle = threadGetCurHandle();
|
||||||
|
ON_SCOPE_EXIT { this->waiting_thread_handle = INVALID_HANDLE; };
|
||||||
|
|
||||||
|
/* Prepare for processing. */
|
||||||
|
this->signaled_holder = nullptr;
|
||||||
|
WaitableHolderBase *result = this->LinkHoldersToObjectList();
|
||||||
|
|
||||||
|
/* Check if we've been signaled. */
|
||||||
|
{
|
||||||
|
std::scoped_lock lk(this->lock);
|
||||||
|
if (this->signaled_holder != nullptr) {
|
||||||
|
result = this->signaled_holder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process object array. */
|
||||||
|
if (result == nullptr) {
|
||||||
|
result = this->WaitAnyHandleImpl(infinite, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unlink holders from the current object list. */
|
||||||
|
this->UnlinkHoldersFromObjectList();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitableHolderBase *WaitableManagerImpl::WaitAnyHandleImpl(bool infinite, u64 timeout) {
|
||||||
|
Handle object_handles[MaximumHandleCount];
|
||||||
|
WaitableHolderBase *objects[MaximumHandleCount];
|
||||||
|
|
||||||
|
const size_t count = this->BuildHandleArray(object_handles, objects);
|
||||||
|
const u64 end_time = infinite ? U64_MAX : armTicksToNs(armGetSystemTick());
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
this->current_time = armTicksToNs(armGetSystemTick());
|
||||||
|
|
||||||
|
u64 min_timeout = 0;
|
||||||
|
WaitableHolderBase *min_timeout_object = this->RecalculateNextTimeout(&min_timeout, end_time);
|
||||||
|
|
||||||
|
s32 index;
|
||||||
|
if (count == 0 && min_timeout == 0) {
|
||||||
|
index = WaitTimedOut;
|
||||||
|
} else {
|
||||||
|
index = this->WaitSynchronization(object_handles, count, min_timeout);
|
||||||
|
STS_ASSERT(index != WaitInvalid);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (index) {
|
||||||
|
case WaitTimedOut:
|
||||||
|
if (min_timeout_object) {
|
||||||
|
this->current_time = armTicksToNs(armGetSystemTick());
|
||||||
|
if (min_timeout_object->IsSignaled() == TriBool::True) {
|
||||||
|
std::scoped_lock lk(this->lock);
|
||||||
|
this->signaled_holder = min_timeout_object;
|
||||||
|
return this->signaled_holder;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
case WaitCancelled:
|
||||||
|
if (this->signaled_holder) {
|
||||||
|
return this->signaled_holder;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
default: /* 0 - 0x3F, valid. */
|
||||||
|
{
|
||||||
|
std::scoped_lock lk(this->lock);
|
||||||
|
this->signaled_holder = objects[index];
|
||||||
|
return this->signaled_holder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 WaitableManagerImpl::WaitSynchronization(Handle *handles, size_t count, u64 timeout) {
|
||||||
|
s32 index = WaitInvalid;
|
||||||
|
|
||||||
|
R_TRY_CATCH(svcWaitSynchronization(&index, handles, count, timeout)) {
|
||||||
|
R_CATCH(ResultKernelTimedOut) { return WaitTimedOut; }
|
||||||
|
R_CATCH(ResultKernelCancelled) { return WaitCancelled; }
|
||||||
|
/* All other results are critical errors. */
|
||||||
|
/* 7601: Thread termination requested. */
|
||||||
|
/* E401: Handle is dead. */
|
||||||
|
/* E601: Handle list address invalid. */
|
||||||
|
/* EE01: Too many handles. */
|
||||||
|
} R_END_TRY_CATCH_WITH_ASSERT;
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t WaitableManagerImpl::BuildHandleArray(Handle *out_handles, WaitableHolderBase **out_objects) {
|
||||||
|
size_t count = 0;
|
||||||
|
|
||||||
|
for (WaitableHolderBase &holder_base : this->waitable_list) {
|
||||||
|
if (Handle handle = holder_base.GetHandle(); handle != INVALID_HANDLE) {
|
||||||
|
STS_ASSERT(count < MaximumHandleCount);
|
||||||
|
|
||||||
|
out_handles[count] = handle;
|
||||||
|
out_objects[count] = &holder_base;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitableHolderBase *WaitableManagerImpl::LinkHoldersToObjectList() {
|
||||||
|
WaitableHolderBase *signaled_holder = nullptr;
|
||||||
|
|
||||||
|
for (WaitableHolderBase &holder_base : this->waitable_list) {
|
||||||
|
TriBool is_signaled = holder_base.LinkToObjectList();
|
||||||
|
|
||||||
|
if (signaled_holder == nullptr && is_signaled == TriBool::True) {
|
||||||
|
signaled_holder = &holder_base;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return signaled_holder;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaitableManagerImpl::UnlinkHoldersFromObjectList() {
|
||||||
|
for (WaitableHolderBase &holder_base : this->waitable_list) {
|
||||||
|
holder_base.UnlinkFromObjectList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitableHolderBase *WaitableManagerImpl::RecalculateNextTimeout(u64 *out_min_timeout, u64 end_time) {
|
||||||
|
WaitableHolderBase *min_timeout_holder = nullptr;
|
||||||
|
u64 min_time = end_time;
|
||||||
|
|
||||||
|
for (WaitableHolderBase &holder_base : this->waitable_list) {
|
||||||
|
if (const u64 cur_time = holder_base.GetWakeupTime(); cur_time < min_time) {
|
||||||
|
min_timeout_holder = &holder_base;
|
||||||
|
min_time = cur_time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (min_time < this->current_time) {
|
||||||
|
*out_min_timeout = 0;
|
||||||
|
} else {
|
||||||
|
*out_min_timeout = min_time - this->current_time;
|
||||||
|
}
|
||||||
|
return min_timeout_holder;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaitableManagerImpl::SignalAndWakeupThread(WaitableHolderBase *holder_base) {
|
||||||
|
std::scoped_lock lk(this->lock);
|
||||||
|
|
||||||
|
if (this->signaled_holder == nullptr) {
|
||||||
|
this->signaled_holder = holder_base;
|
||||||
|
R_ASSERT(svcCancelSynchronization(this->waiting_thread_handle));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "os_waitable_holder_base.hpp"
|
||||||
|
|
||||||
|
namespace sts::os::impl {
|
||||||
|
|
||||||
|
class WaitableManagerImpl {
|
||||||
|
public:
|
||||||
|
static constexpr size_t MaximumHandleCount = 0x40;
|
||||||
|
static constexpr s32 WaitInvalid = -3;
|
||||||
|
static constexpr s32 WaitCancelled = -2;
|
||||||
|
static constexpr s32 WaitTimedOut = -1;
|
||||||
|
using ListType = util::IntrusiveListMemberTraits<&WaitableHolderBase::manager_node>::ListType;
|
||||||
|
private:
|
||||||
|
ListType waitable_list;
|
||||||
|
WaitableHolderBase *signaled_holder;
|
||||||
|
u64 current_time;
|
||||||
|
Mutex lock;
|
||||||
|
Handle waiting_thread_handle;
|
||||||
|
private:
|
||||||
|
WaitableHolderBase *WaitAnyImpl(bool infinite, u64 timeout);
|
||||||
|
WaitableHolderBase *WaitAnyHandleImpl(bool infinite, u64 timeout);
|
||||||
|
s32 WaitSynchronization(Handle *handles, size_t count, u64 timeout);
|
||||||
|
size_t BuildHandleArray(Handle *out_handles, WaitableHolderBase **out_objects);
|
||||||
|
|
||||||
|
WaitableHolderBase *LinkHoldersToObjectList();
|
||||||
|
void UnlinkHoldersFromObjectList();
|
||||||
|
|
||||||
|
WaitableHolderBase *RecalculateNextTimeout(u64 *out_min_timeout, u64 end_time);
|
||||||
|
public:
|
||||||
|
/* Wait. */
|
||||||
|
WaitableHolderBase *WaitAny() {
|
||||||
|
return this->WaitAnyImpl(true, U64_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitableHolderBase *TryWaitAny() {
|
||||||
|
return this->WaitAnyImpl(false, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitableHolderBase *TimedWaitAny(u64 timeout) {
|
||||||
|
return this->WaitAnyImpl(false, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* List management. */
|
||||||
|
bool IsEmpty() const {
|
||||||
|
return this->waitable_list.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LinkWaitableHolder(WaitableHolderBase &holder_base) {
|
||||||
|
this->waitable_list.push_back(holder_base);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnlinkWaitableHolder(WaitableHolderBase &holder_base) {
|
||||||
|
this->waitable_list.erase(this->waitable_list.iterator_to(holder_base));
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnlinkAll() {
|
||||||
|
while (!this->IsEmpty()) {
|
||||||
|
this->waitable_list.front().SetManager(nullptr);
|
||||||
|
this->waitable_list.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MoveAllFrom(WaitableManagerImpl &other) {
|
||||||
|
/* Set manager for all of the other's waitables. */
|
||||||
|
for (auto &w : other.waitable_list) {
|
||||||
|
w.SetManager(this);
|
||||||
|
}
|
||||||
|
this->waitable_list.splice(this->waitable_list.end(), other.waitable_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Other. */
|
||||||
|
u64 GetCurrentTime() const {
|
||||||
|
return this->current_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SignalAndWakeupThread(WaitableHolderBase *holder_base);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "os_waitable_holder_base.hpp"
|
||||||
|
#include "os_waitable_manager_impl.hpp"
|
||||||
|
|
||||||
|
namespace sts::os::impl {
|
||||||
|
|
||||||
|
class WaitableObjectList {
|
||||||
|
public:
|
||||||
|
using ListType = util::IntrusiveListMemberTraits<&WaitableHolderBase::object_list_node>::ListType;
|
||||||
|
private:
|
||||||
|
ListType object_list;
|
||||||
|
public:
|
||||||
|
void SignalAllThreads() {
|
||||||
|
for (WaitableHolderBase &holder_base : this->object_list) {
|
||||||
|
holder_base.GetManager()->SignalAndWakeupThread(&holder_base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WakeupAllThreads() {
|
||||||
|
for (WaitableHolderBase &holder_base : this->object_list) {
|
||||||
|
holder_base.GetManager()->SignalAndWakeupThread(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsEmpty() const {
|
||||||
|
return this->object_list.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LinkWaitableHolder(WaitableHolderBase &holder_base) {
|
||||||
|
this->object_list.push_back(holder_base);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnlinkWaitableHolder(WaitableHolderBase &holder_base) {
|
||||||
|
this->object_list.erase(this->object_list.iterator_to(holder_base));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
106
stratosphere/libstratosphere/source/os/os_event.cpp
Normal file
106
stratosphere/libstratosphere/source/os/os_event.cpp
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "impl/os_waitable_object_list.hpp"
|
||||||
|
|
||||||
|
namespace sts::os {
|
||||||
|
|
||||||
|
Event::Event(bool a, bool s) : auto_clear(a), signaled(s) {
|
||||||
|
new (GetPointer(this->waitable_object_list_storage)) impl::WaitableObjectList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Event::~Event() {
|
||||||
|
GetReference(this->waitable_object_list_storage).~WaitableObjectList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Event::Signal() {
|
||||||
|
std::scoped_lock lk(this->lock);
|
||||||
|
|
||||||
|
/* If we're already signaled, nothing more to do. */
|
||||||
|
if (this->signaled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->signaled = true;
|
||||||
|
|
||||||
|
/* Signal! */
|
||||||
|
if (this->auto_clear) {
|
||||||
|
/* If we're auto clear, signal one thread, which will clear. */
|
||||||
|
this->cv.Signal();
|
||||||
|
} else {
|
||||||
|
/* If we're manual clear, increment counter and wake all. */
|
||||||
|
this->counter++;
|
||||||
|
this->cv.Broadcast();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wake up whatever manager, if any. */
|
||||||
|
GetReference(this->waitable_object_list_storage).SignalAllThreads();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Event::Reset() {
|
||||||
|
std::scoped_lock lk(this->lock);
|
||||||
|
this->signaled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Event::Wait() {
|
||||||
|
std::scoped_lock lk(this->lock);
|
||||||
|
|
||||||
|
u64 cur_counter = this->counter;
|
||||||
|
while (!this->signaled) {
|
||||||
|
if (this->counter != cur_counter) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this->cv.Wait(&this->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->auto_clear) {
|
||||||
|
this->signaled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Event::TryWait() {
|
||||||
|
std::scoped_lock lk(this->lock);
|
||||||
|
|
||||||
|
const bool success = this->signaled;
|
||||||
|
if (this->auto_clear) {
|
||||||
|
this->signaled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Event::TimedWait(u64 ns) {
|
||||||
|
TimeoutHelper timeout_helper(ns);
|
||||||
|
std::scoped_lock lk(this->lock);
|
||||||
|
|
||||||
|
u64 cur_counter = this->counter;
|
||||||
|
while (!this->signaled) {
|
||||||
|
if (this->counter != cur_counter) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (R_FAILED(this->cv.TimedWait(&this->lock, timeout_helper.NsUntilTimeout()))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->auto_clear) {
|
||||||
|
this->signaled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
112
stratosphere/libstratosphere/source/os/os_interrupt_event.cpp
Normal file
112
stratosphere/libstratosphere/source/os/os_interrupt_event.cpp
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "impl/os_waitable_object_list.hpp"
|
||||||
|
|
||||||
|
namespace sts::os {
|
||||||
|
|
||||||
|
Result InterruptEvent::Initialize(u32 interrupt_id, bool autoclear) {
|
||||||
|
STS_ASSERT(!this->is_initialized);
|
||||||
|
this->auto_clear = autoclear;
|
||||||
|
|
||||||
|
const auto type = this->auto_clear ? svc::InterruptType_Edge : svc::InterruptType_Level;
|
||||||
|
R_TRY(svcCreateInterruptEvent(this->handle.GetPointer(), interrupt_id, type));
|
||||||
|
|
||||||
|
this->is_initialized = true;
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterruptEvent::Finalize() {
|
||||||
|
STS_ASSERT(this->is_initialized);
|
||||||
|
R_ASSERT(svcCloseHandle(this->handle.Move()));
|
||||||
|
this->auto_clear = true;
|
||||||
|
this->is_initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
InterruptEvent::InterruptEvent(u32 interrupt_id, bool autoclear) {
|
||||||
|
this->is_initialized = false;
|
||||||
|
R_ASSERT(this->Initialize(interrupt_id, autoclear));
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterruptEvent::Reset() {
|
||||||
|
R_ASSERT(svcClearEvent(this->handle.Get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterruptEvent::Wait() {
|
||||||
|
STS_ASSERT(this->is_initialized);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
/* Continuously wait, until success. */
|
||||||
|
R_TRY_CATCH(svcWaitSynchronizationSingle(this->handle.Get(), U64_MAX)) {
|
||||||
|
R_CATCH(ResultKernelCancelled) { continue; }
|
||||||
|
} R_END_TRY_CATCH_WITH_ASSERT;
|
||||||
|
|
||||||
|
/* Clear, if we must. */
|
||||||
|
if (this->auto_clear) {
|
||||||
|
R_TRY_CATCH(svcResetSignal(this->handle.Get())) {
|
||||||
|
/* Some other thread might have caught this before we did. */
|
||||||
|
R_CATCH(ResultKernelInvalidState) { continue; }
|
||||||
|
} R_END_TRY_CATCH_WITH_ASSERT;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterruptEvent::TryWait() {
|
||||||
|
STS_ASSERT(this->is_initialized);
|
||||||
|
|
||||||
|
if (this->auto_clear) {
|
||||||
|
/* Auto-clear. Just try to reset. */
|
||||||
|
return R_SUCCEEDED(svcResetSignal(this->handle.Get()));
|
||||||
|
} else {
|
||||||
|
/* Not auto-clear. */
|
||||||
|
while (true) {
|
||||||
|
/* Continuously wait, until success or timeout. */
|
||||||
|
R_TRY_CATCH(svcWaitSynchronizationSingle(this->handle.Get(), 0)) {
|
||||||
|
R_CATCH(ResultKernelTimedOut) { return false; }
|
||||||
|
R_CATCH(ResultKernelCancelled) { continue; }
|
||||||
|
} R_END_TRY_CATCH_WITH_ASSERT;
|
||||||
|
|
||||||
|
/* We succeeded, so we're signaled. */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterruptEvent::TimedWait(u64 ns) {
|
||||||
|
STS_ASSERT(this->is_initialized);
|
||||||
|
|
||||||
|
TimeoutHelper timeout_helper(ns);
|
||||||
|
while (true) {
|
||||||
|
/* Continuously wait, until success or timeout. */
|
||||||
|
R_TRY_CATCH(svcWaitSynchronizationSingle(this->handle.Get(), timeout_helper.NsUntilTimeout())) {
|
||||||
|
R_CATCH(ResultKernelTimedOut) { return false; }
|
||||||
|
R_CATCH(ResultKernelCancelled) { continue; }
|
||||||
|
} R_END_TRY_CATCH_WITH_ASSERT;
|
||||||
|
|
||||||
|
/* Clear, if we must. */
|
||||||
|
if (this->auto_clear) {
|
||||||
|
R_TRY_CATCH(svcResetSignal(this->handle.Get())) {
|
||||||
|
/* Some other thread might have caught this before we did. */
|
||||||
|
R_CATCH(ResultKernelInvalidState) { continue; }
|
||||||
|
} R_END_TRY_CATCH_WITH_ASSERT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -14,14 +14,21 @@
|
||||||
* 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 <stratosphere.hpp>
|
||||||
|
#include "impl/os_waitable_object_list.hpp"
|
||||||
|
|
||||||
namespace sts::os {
|
namespace sts::os {
|
||||||
|
|
||||||
|
MessageQueue::MessageQueue(std::unique_ptr<uintptr_t[]> buf, size_t c): buffer(std::move(buf)), capacity(c) {
|
||||||
|
new (GetPointer(this->waitable_object_list_storage)) impl::WaitableObjectList();
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageQueue::~MessageQueue() {
|
||||||
|
GetReference(this->waitable_object_list_storage).~WaitableObjectList();
|
||||||
|
}
|
||||||
|
|
||||||
void MessageQueue::SendInternal(uintptr_t data) {
|
void MessageQueue::SendInternal(uintptr_t data) {
|
||||||
/* Ensure we don't corrupt the queue, but this should never happen. */
|
/* Ensure we don't corrupt the queue, but this should never happen. */
|
||||||
if (this->count >= this->capacity) {
|
STS_ASSERT(this->count < this->capacity);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write data to tail of queue. */
|
/* Write data to tail of queue. */
|
||||||
this->buffer[(this->count++ + this->offset) % this->capacity] = data;
|
this->buffer[(this->count++ + this->offset) % this->capacity] = data;
|
||||||
|
@ -29,9 +36,7 @@ namespace sts::os {
|
||||||
|
|
||||||
void MessageQueue::SendNextInternal(uintptr_t data) {
|
void MessageQueue::SendNextInternal(uintptr_t data) {
|
||||||
/* Ensure we don't corrupt the queue, but this should never happen. */
|
/* Ensure we don't corrupt the queue, but this should never happen. */
|
||||||
if (this->count >= this->capacity) {
|
STS_ASSERT(this->count < this->capacity);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write data to head of queue. */
|
/* Write data to head of queue. */
|
||||||
this->offset = (this->offset + this->capacity - 1) % this->capacity;
|
this->offset = (this->offset + this->capacity - 1) % this->capacity;
|
||||||
|
@ -41,9 +46,7 @@ namespace sts::os {
|
||||||
|
|
||||||
uintptr_t MessageQueue::ReceiveInternal() {
|
uintptr_t MessageQueue::ReceiveInternal() {
|
||||||
/* Ensure we don't corrupt the queue, but this should never happen. */
|
/* Ensure we don't corrupt the queue, but this should never happen. */
|
||||||
if (this->count == 0) {
|
STS_ASSERT(this->count > 0);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
uintptr_t data = this->buffer[this->offset];
|
uintptr_t data = this->buffer[this->offset];
|
||||||
this->offset = (this->offset + 1) % this->capacity;
|
this->offset = (this->offset + 1) % this->capacity;
|
||||||
|
@ -53,9 +56,7 @@ namespace sts::os {
|
||||||
|
|
||||||
uintptr_t MessageQueue::PeekInternal() {
|
uintptr_t MessageQueue::PeekInternal() {
|
||||||
/* Ensure we don't corrupt the queue, but this should never happen. */
|
/* Ensure we don't corrupt the queue, but this should never happen. */
|
||||||
if (this->count == 0) {
|
STS_ASSERT(this->count > 0);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
return this->buffer[this->offset];
|
return this->buffer[this->offset];
|
||||||
}
|
}
|
||||||
|
|
189
stratosphere/libstratosphere/source/os/os_system_event.cpp
Normal file
189
stratosphere/libstratosphere/source/os/os_system_event.cpp
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "impl/os_waitable_holder_impl.hpp"
|
||||||
|
|
||||||
|
namespace sts::os {
|
||||||
|
|
||||||
|
SystemEvent::SystemEvent(bool inter_process, bool autoclear) : state(SystemEventState::Uninitialized) {
|
||||||
|
if (inter_process) {
|
||||||
|
R_ASSERT(this->InitializeAsInterProcessEvent(autoclear));
|
||||||
|
} else {
|
||||||
|
R_ASSERT(this->InitializeAsEvent(autoclear));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemEvent::SystemEvent(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear) : state(SystemEventState::Uninitialized) {
|
||||||
|
this->AttachHandles(read_handle, manage_read_handle, write_handle, manage_write_handle, autoclear);
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemEvent::~SystemEvent() {
|
||||||
|
this->Finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
Event &SystemEvent::GetEvent() {
|
||||||
|
STS_ASSERT(this->state == SystemEventState::Event);
|
||||||
|
return GetReference(this->storage_for_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Event &SystemEvent::GetEvent() const {
|
||||||
|
STS_ASSERT(this->state == SystemEventState::Event);
|
||||||
|
return GetReference(this->storage_for_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl::InterProcessEvent &SystemEvent::GetInterProcessEvent() {
|
||||||
|
STS_ASSERT(this->state == SystemEventState::InterProcessEvent);
|
||||||
|
return GetReference(this->storage_for_inter_process_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
const impl::InterProcessEvent &SystemEvent::GetInterProcessEvent() const {
|
||||||
|
STS_ASSERT(this->state == SystemEventState::InterProcessEvent);
|
||||||
|
return GetReference(this->storage_for_inter_process_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SystemEvent::InitializeAsEvent(bool autoclear) {
|
||||||
|
STS_ASSERT(this->state == SystemEventState::Uninitialized);
|
||||||
|
new (GetPointer(this->storage_for_event)) Event(autoclear);
|
||||||
|
this->state = SystemEventState::Event;
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SystemEvent::InitializeAsInterProcessEvent(bool autoclear) {
|
||||||
|
STS_ASSERT(this->state == SystemEventState::Uninitialized);
|
||||||
|
new (GetPointer(this->storage_for_inter_process_event)) impl::InterProcessEvent();
|
||||||
|
this->state = SystemEventState::InterProcessEvent;
|
||||||
|
R_TRY_CLEANUP(this->GetInterProcessEvent().Initialize(autoclear), {
|
||||||
|
this->Finalize();
|
||||||
|
});
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemEvent::AttachHandles(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear) {
|
||||||
|
STS_ASSERT(this->state == SystemEventState::Uninitialized);
|
||||||
|
new (GetPointer(this->storage_for_inter_process_event)) impl::InterProcessEvent();
|
||||||
|
this->state = SystemEventState::InterProcessEvent;
|
||||||
|
this->GetInterProcessEvent().Initialize(read_handle, manage_read_handle, write_handle, manage_write_handle, autoclear);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemEvent::AttachReadableHandle(Handle read_handle, bool manage_read_handle, bool autoclear) {
|
||||||
|
this->AttachHandles(read_handle, manage_read_handle, INVALID_HANDLE, false, autoclear);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemEvent::AttachWritableHandle(Handle write_handle, bool manage_write_handle, bool autoclear) {
|
||||||
|
this->AttachHandles(INVALID_HANDLE, false, write_handle, manage_write_handle, autoclear);
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle SystemEvent::DetachReadableHandle() {
|
||||||
|
STS_ASSERT(this->state == SystemEventState::InterProcessEvent);
|
||||||
|
return this->GetInterProcessEvent().DetachReadableHandle();
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle SystemEvent::DetachWritableHandle() {
|
||||||
|
STS_ASSERT(this->state == SystemEventState::InterProcessEvent);
|
||||||
|
return this->GetInterProcessEvent().DetachWritableHandle();
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle SystemEvent::GetReadableHandle() const {
|
||||||
|
STS_ASSERT(this->state == SystemEventState::InterProcessEvent);
|
||||||
|
return this->GetInterProcessEvent().GetReadableHandle();
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle SystemEvent::GetWritableHandle() const {
|
||||||
|
STS_ASSERT(this->state == SystemEventState::InterProcessEvent);
|
||||||
|
return this->GetInterProcessEvent().GetWritableHandle();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemEvent::Finalize() {
|
||||||
|
switch (this->state) {
|
||||||
|
case SystemEventState::Uninitialized:
|
||||||
|
break;
|
||||||
|
case SystemEventState::Event:
|
||||||
|
this->GetEvent().~Event();
|
||||||
|
break;
|
||||||
|
case SystemEventState::InterProcessEvent:
|
||||||
|
this->GetInterProcessEvent().~InterProcessEvent();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
this->state = SystemEventState::Uninitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemEvent::Signal() {
|
||||||
|
switch (this->state) {
|
||||||
|
case SystemEventState::Event:
|
||||||
|
this->GetEvent().Signal();
|
||||||
|
break;
|
||||||
|
case SystemEventState::InterProcessEvent:
|
||||||
|
this->GetInterProcessEvent().Signal();
|
||||||
|
break;
|
||||||
|
case SystemEventState::Uninitialized:
|
||||||
|
default:
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemEvent::Reset() {
|
||||||
|
switch (this->state) {
|
||||||
|
case SystemEventState::Event:
|
||||||
|
this->GetEvent().Reset();
|
||||||
|
break;
|
||||||
|
case SystemEventState::InterProcessEvent:
|
||||||
|
this->GetInterProcessEvent().Reset();
|
||||||
|
break;
|
||||||
|
case SystemEventState::Uninitialized:
|
||||||
|
default:
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void SystemEvent::Wait() {
|
||||||
|
switch (this->state) {
|
||||||
|
case SystemEventState::Event:
|
||||||
|
this->GetEvent().Wait();
|
||||||
|
break;
|
||||||
|
case SystemEventState::InterProcessEvent:
|
||||||
|
this->GetInterProcessEvent().Wait();
|
||||||
|
break;
|
||||||
|
case SystemEventState::Uninitialized:
|
||||||
|
default:
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SystemEvent::TryWait() {
|
||||||
|
switch (this->state) {
|
||||||
|
case SystemEventState::Event:
|
||||||
|
return this->GetEvent().TryWait();
|
||||||
|
case SystemEventState::InterProcessEvent:
|
||||||
|
return this->GetInterProcessEvent().TryWait();
|
||||||
|
case SystemEventState::Uninitialized:
|
||||||
|
default:
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SystemEvent::TimedWait(u64 ns) {
|
||||||
|
switch (this->state) {
|
||||||
|
case SystemEventState::Event:
|
||||||
|
return this->GetEvent().TimedWait(ns);
|
||||||
|
case SystemEventState::InterProcessEvent:
|
||||||
|
return this->GetInterProcessEvent().TimedWait(ns);
|
||||||
|
case SystemEventState::Uninitialized:
|
||||||
|
default:
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
111
stratosphere/libstratosphere/source/os/os_waitable_holder.cpp
Normal file
111
stratosphere/libstratosphere/source/os/os_waitable_holder.cpp
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "impl/os_waitable_holder_impl.hpp"
|
||||||
|
#include "impl/os_waitable_manager_impl.hpp"
|
||||||
|
|
||||||
|
namespace sts::os {
|
||||||
|
|
||||||
|
WaitableHolder::WaitableHolder(Handle handle) {
|
||||||
|
/* Don't allow invalid handles. */
|
||||||
|
STS_ASSERT(handle != INVALID_HANDLE);
|
||||||
|
|
||||||
|
/* Initialize appropriate holder. */
|
||||||
|
new (GetPointer(this->impl_storage)) impl::WaitableHolderOfHandle(handle);
|
||||||
|
|
||||||
|
/* Set user-data. */
|
||||||
|
this->user_data = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitableHolder::WaitableHolder(Event *event) {
|
||||||
|
/* Initialize appropriate holder. */
|
||||||
|
new (GetPointer(this->impl_storage)) impl::WaitableHolderOfEvent(event);
|
||||||
|
|
||||||
|
/* Set user-data. */
|
||||||
|
this->user_data = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitableHolder::WaitableHolder(SystemEvent *event) {
|
||||||
|
/* Initialize appropriate holder. */
|
||||||
|
switch (event->GetState()) {
|
||||||
|
case SystemEventState::Event:
|
||||||
|
new (GetPointer(this->impl_storage)) impl::WaitableHolderOfEvent(&event->GetEvent());
|
||||||
|
break;
|
||||||
|
case SystemEventState::InterProcessEvent:
|
||||||
|
new (GetPointer(this->impl_storage)) impl::WaitableHolderOfInterProcessEvent(&event->GetInterProcessEvent());
|
||||||
|
break;
|
||||||
|
case SystemEventState::Uninitialized:
|
||||||
|
default:
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set user-data. */
|
||||||
|
this->user_data = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitableHolder::WaitableHolder(InterruptEvent *event) {
|
||||||
|
/* Initialize appropriate holder. */
|
||||||
|
new (GetPointer(this->impl_storage)) impl::WaitableHolderOfInterruptEvent(event);
|
||||||
|
|
||||||
|
/* Set user-data. */
|
||||||
|
this->user_data = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitableHolder::WaitableHolder(Thread *thread) {
|
||||||
|
/* Initialize appropriate holder. */
|
||||||
|
new (GetPointer(this->impl_storage)) impl::WaitableHolderOfThread(thread);
|
||||||
|
|
||||||
|
/* Set user-data. */
|
||||||
|
this->user_data = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitableHolder::WaitableHolder(MessageQueue *message_queue, MessageQueueWaitKind wait_kind) {
|
||||||
|
/* Initialize appropriate holder. */
|
||||||
|
switch (wait_kind) {
|
||||||
|
case MessageQueueWaitKind::ForNotFull:
|
||||||
|
new (GetPointer(this->impl_storage)) impl::WaitableHolderOfMessageQueueForNotFull(message_queue);
|
||||||
|
break;
|
||||||
|
case MessageQueueWaitKind::ForNotEmpty:
|
||||||
|
new (GetPointer(this->impl_storage)) impl::WaitableHolderOfMessageQueueForNotEmpty(message_queue);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set user-data. */
|
||||||
|
this->user_data = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitableHolder::~WaitableHolder() {
|
||||||
|
auto holder_base = reinterpret_cast<impl::WaitableHolderBase *>(GetPointer(this->impl_storage));
|
||||||
|
|
||||||
|
/* Don't allow destruction of a linked waitable holder. */
|
||||||
|
STS_ASSERT(!holder_base->IsLinkedToManager());
|
||||||
|
|
||||||
|
holder_base->~WaitableHolderBase();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaitableHolder::UnlinkFromWaitableManager() {
|
||||||
|
auto holder_base = reinterpret_cast<impl::WaitableHolderBase *>(GetPointer(this->impl_storage));
|
||||||
|
|
||||||
|
/* Don't allow unlinking of an unlinked holder. */
|
||||||
|
STS_ASSERT(holder_base->IsLinkedToManager());
|
||||||
|
|
||||||
|
holder_base->GetManager()->UnlinkWaitableHolder(*holder_base);
|
||||||
|
holder_base->SetManager(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "impl/os_waitable_holder_impl.hpp"
|
||||||
|
#include "impl/os_waitable_manager_impl.hpp"
|
||||||
|
|
||||||
|
namespace sts::os {
|
||||||
|
|
||||||
|
WaitableManager::WaitableManager() {
|
||||||
|
/* Initialize storage. */
|
||||||
|
new (GetPointer(this->impl_storage)) impl::WaitableManagerImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitableManager::~WaitableManager() {
|
||||||
|
auto &impl = GetReference(this->impl_storage);
|
||||||
|
|
||||||
|
/* Don't allow destruction of a non-empty waitable holder. */
|
||||||
|
STS_ASSERT(impl.IsEmpty());
|
||||||
|
|
||||||
|
impl.~WaitableManagerImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Wait. */
|
||||||
|
WaitableHolder *WaitableManager::WaitAny() {
|
||||||
|
auto &impl = GetReference(this->impl_storage);
|
||||||
|
|
||||||
|
/* Don't allow waiting on empty list. */
|
||||||
|
STS_ASSERT(!impl.IsEmpty());
|
||||||
|
|
||||||
|
return reinterpret_cast<WaitableHolder *>(impl.WaitAny());
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitableHolder *WaitableManager::TryWaitAny() {
|
||||||
|
auto &impl = GetReference(this->impl_storage);
|
||||||
|
|
||||||
|
/* Don't allow waiting on empty list. */
|
||||||
|
STS_ASSERT(!impl.IsEmpty());
|
||||||
|
|
||||||
|
return reinterpret_cast<WaitableHolder *>(impl.TryWaitAny());
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitableHolder *WaitableManager::TimedWaitAny(u64 timeout) {
|
||||||
|
auto &impl = GetReference(this->impl_storage);
|
||||||
|
|
||||||
|
/* Don't allow waiting on empty list. */
|
||||||
|
STS_ASSERT(!impl.IsEmpty());
|
||||||
|
|
||||||
|
return reinterpret_cast<WaitableHolder *>(impl.TimedWaitAny(timeout));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Link. */
|
||||||
|
void WaitableManager::LinkWaitableHolder(WaitableHolder *holder) {
|
||||||
|
auto &impl = GetReference(this->impl_storage);
|
||||||
|
auto holder_base = reinterpret_cast<impl::WaitableHolderBase *>(GetPointer(holder->impl_storage));
|
||||||
|
|
||||||
|
/* Don't allow double-linking a holder. */
|
||||||
|
STS_ASSERT(!holder_base->IsLinkedToManager());
|
||||||
|
|
||||||
|
impl.LinkWaitableHolder(*holder_base);
|
||||||
|
holder_base->SetManager(&impl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaitableManager::UnlinkAll() {
|
||||||
|
auto &impl = GetReference(this->impl_storage);
|
||||||
|
impl.UnlinkAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaitableManager::MoveAllFrom(WaitableManager *other) {
|
||||||
|
auto &dst_impl = GetReference(this->impl_storage);
|
||||||
|
auto &src_impl = GetReference(other->impl_storage);
|
||||||
|
|
||||||
|
dst_impl.MoveAllFrom(src_impl);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -101,15 +101,11 @@ namespace sts::patcher {
|
||||||
|
|
||||||
void ApplyIpsPatch(u8 *mapped_module, size_t mapped_size, size_t protected_size, size_t offset, bool is_ips32, FILE *f_ips) {
|
void ApplyIpsPatch(u8 *mapped_module, size_t mapped_size, size_t protected_size, size_t offset, bool is_ips32, FILE *f_ips) {
|
||||||
/* Validate offset/protected size. */
|
/* Validate offset/protected size. */
|
||||||
if (offset > protected_size) {
|
STS_ASSERT(offset <= protected_size);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 buffer[sizeof(Ips32TailMagic)];
|
u8 buffer[sizeof(Ips32TailMagic)];
|
||||||
while (true) {
|
while (true) {
|
||||||
if (fread(buffer, is_ips32 ? sizeof(Ips32TailMagic) : sizeof(IpsTailMagic), 1, f_ips) != 1) {
|
STS_ASSERT(fread(buffer, is_ips32 ? sizeof(Ips32TailMagic) : sizeof(IpsTailMagic), 1, f_ips) == 1);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsIpsTail(is_ips32, buffer)) {
|
if (IsIpsTail(is_ips32, buffer)) {
|
||||||
break;
|
break;
|
||||||
|
@ -119,24 +115,18 @@ namespace sts::patcher {
|
||||||
u32 patch_offset = GetIpsPatchOffset(is_ips32, buffer);
|
u32 patch_offset = GetIpsPatchOffset(is_ips32, buffer);
|
||||||
|
|
||||||
/* Size of patch. */
|
/* Size of patch. */
|
||||||
if (fread(buffer, 2, 1, f_ips) != 1) {
|
STS_ASSERT(fread(buffer, 2, 1, f_ips) == 1);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
u32 patch_size = GetIpsPatchSize(is_ips32, buffer);
|
u32 patch_size = GetIpsPatchSize(is_ips32, buffer);
|
||||||
|
|
||||||
/* Check for RLE encoding. */
|
/* Check for RLE encoding. */
|
||||||
if (patch_size == 0) {
|
if (patch_size == 0) {
|
||||||
/* Size of RLE. */
|
/* Size of RLE. */
|
||||||
if (fread(buffer, 2, 1, f_ips) != 1) {
|
STS_ASSERT(fread(buffer, 2, 1, f_ips) == 1);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 rle_size = (buffer[0] << 8) | (buffer[1]);
|
u32 rle_size = (buffer[0] << 8) | (buffer[1]);
|
||||||
|
|
||||||
/* Value for RLE. */
|
/* Value for RLE. */
|
||||||
if (fread(buffer, 1, 1, f_ips) != 1) {
|
STS_ASSERT(fread(buffer, 1, 1, f_ips) == 1);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ensure we don't write to protected region. */
|
/* Ensure we don't write to protected region. */
|
||||||
if (patch_offset < protected_size) {
|
if (patch_offset < protected_size) {
|
||||||
|
@ -179,9 +169,7 @@ namespace sts::patcher {
|
||||||
if (patch_offset + read_size > mapped_size) {
|
if (patch_offset + read_size > mapped_size) {
|
||||||
read_size = mapped_size - patch_offset;
|
read_size = mapped_size - patch_offset;
|
||||||
}
|
}
|
||||||
if (fread(mapped_module + patch_offset, read_size, 1, f_ips) != 1) {
|
STS_ASSERT(fread(mapped_module + patch_offset, read_size, 1, f_ips) == 1);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
if (patch_size > read_size) {
|
if (patch_size > read_size) {
|
||||||
fseek(f_ips, patch_size - read_size, SEEK_CUR);
|
fseek(f_ips, patch_size - read_size, SEEK_CUR);
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,9 +142,7 @@ namespace sts::updater {
|
||||||
Result GetBootImagePackageDataId(u64 *out_data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size) {
|
Result GetBootImagePackageDataId(u64 *out_data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size) {
|
||||||
/* Ensure we can read content metas. */
|
/* Ensure we can read content metas. */
|
||||||
constexpr size_t MaxContentMetas = 0x40;
|
constexpr size_t MaxContentMetas = 0x40;
|
||||||
if (work_buffer_size < sizeof(NcmMetaRecord) * MaxContentMetas) {
|
STS_ASSERT(work_buffer_size >= sizeof(NcmMetaRecord) * MaxContentMetas);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Open NAND System meta database, list contents. */
|
/* Open NAND System meta database, list contents. */
|
||||||
NcmContentMetaDatabase meta_db;
|
NcmContentMetaDatabase meta_db;
|
||||||
|
@ -161,9 +159,7 @@ namespace sts::updater {
|
||||||
return ResultUpdaterBootImagePackageNotFound;
|
return ResultUpdaterBootImagePackageNotFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (total_entries != written_entries) {
|
STS_ASSERT(total_entries == written_entries);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Output is sorted, return the lowest valid exfat entry. */
|
/* Output is sorted, return the lowest valid exfat entry. */
|
||||||
if (total_entries > 1) {
|
if (total_entries > 1) {
|
||||||
|
@ -203,7 +199,7 @@ namespace sts::updater {
|
||||||
return ResultUpdaterBootImagePackageNotFound;
|
return ResultUpdaterBootImagePackageNotFound;
|
||||||
}
|
}
|
||||||
} R_END_TRY_CATCH;
|
} R_END_TRY_CATCH;
|
||||||
ON_SCOPE_EXIT { if (R_FAILED(romfsUnmount(GetBootImagePackageMountPath()))) { std::abort(); } };
|
ON_SCOPE_EXIT { R_ASSERT(romfsUnmount(GetBootImagePackageMountPath())); };
|
||||||
|
|
||||||
/* Read and validate hashes of boot images. */
|
/* Read and validate hashes of boot images. */
|
||||||
{
|
{
|
||||||
|
@ -258,7 +254,7 @@ namespace sts::updater {
|
||||||
return ResultUpdaterBootImagePackageNotFound;
|
return ResultUpdaterBootImagePackageNotFound;
|
||||||
}
|
}
|
||||||
} R_END_TRY_CATCH;
|
} R_END_TRY_CATCH;
|
||||||
ON_SCOPE_EXIT { if (R_FAILED(romfsUnmount(GetBootImagePackageMountPath()))) { std::abort(); } };
|
ON_SCOPE_EXIT { R_ASSERT(romfsUnmount(GetBootImagePackageMountPath())); };
|
||||||
|
|
||||||
/* Read and validate hashes of boot images. */
|
/* Read and validate hashes of boot images. */
|
||||||
{
|
{
|
||||||
|
@ -329,7 +325,7 @@ namespace sts::updater {
|
||||||
return ResultUpdaterBootImagePackageNotFound;
|
return ResultUpdaterBootImagePackageNotFound;
|
||||||
}
|
}
|
||||||
} R_END_TRY_CATCH;
|
} R_END_TRY_CATCH;
|
||||||
ON_SCOPE_EXIT { if (R_FAILED(romfsUnmount(GetBootImagePackageMountPath()))) { std::abort(); } };
|
ON_SCOPE_EXIT { R_ASSERT(romfsUnmount(GetBootImagePackageMountPath())); };
|
||||||
|
|
||||||
{
|
{
|
||||||
Boot0Accessor boot0_accessor;
|
Boot0Accessor boot0_accessor;
|
||||||
|
@ -386,7 +382,7 @@ namespace sts::updater {
|
||||||
return ResultUpdaterBootImagePackageNotFound;
|
return ResultUpdaterBootImagePackageNotFound;
|
||||||
}
|
}
|
||||||
} R_END_TRY_CATCH;
|
} R_END_TRY_CATCH;
|
||||||
ON_SCOPE_EXIT { if (R_FAILED(romfsUnmount(GetBootImagePackageMountPath()))) { std::abort(); } };
|
ON_SCOPE_EXIT { R_ASSERT(romfsUnmount(GetBootImagePackageMountPath())); };
|
||||||
|
|
||||||
{
|
{
|
||||||
Boot0Accessor boot0_accessor;
|
Boot0Accessor boot0_accessor;
|
||||||
|
@ -533,9 +529,7 @@ namespace sts::updater {
|
||||||
|
|
||||||
/* Get a session to ncm. */
|
/* Get a session to ncm. */
|
||||||
DoWithSmSession([&]() {
|
DoWithSmSession([&]() {
|
||||||
if (R_FAILED(ncmInitialize())) {
|
R_ASSERT(ncmInitialize());
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
ON_SCOPE_EXIT { ncmExit(); };
|
ON_SCOPE_EXIT { ncmExit(); };
|
||||||
|
|
||||||
|
|
|
@ -35,23 +35,18 @@ namespace sts::updater {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result BisAccessor::Read(void *dst, size_t size, u64 offset) {
|
Result BisAccessor::Read(void *dst, size_t size, u64 offset) {
|
||||||
if (offset % SectorAlignment) {
|
STS_ASSERT((offset % SectorAlignment) == 0);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
return fsStorageRead(&this->storage, offset, dst, size);
|
return fsStorageRead(&this->storage, offset, dst, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result BisAccessor::Write(u64 offset, const void *src, size_t size) {
|
Result BisAccessor::Write(u64 offset, const void *src, size_t size) {
|
||||||
if (offset % SectorAlignment) {
|
STS_ASSERT((offset % SectorAlignment) == 0);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
return fsStorageWrite(&this->storage, offset, src, size);
|
return fsStorageWrite(&this->storage, offset, src, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result BisAccessor::Write(u64 offset, size_t size, const char *bip_path, void *work_buffer, size_t work_buffer_size) {
|
Result BisAccessor::Write(u64 offset, size_t size, const char *bip_path, void *work_buffer, size_t work_buffer_size) {
|
||||||
if (offset % SectorAlignment != 0 || work_buffer_size % SectorAlignment != 0) {
|
STS_ASSERT((offset % SectorAlignment) == 0);
|
||||||
std::abort();
|
STS_ASSERT((work_buffer_size % SectorAlignment) == 0);
|
||||||
}
|
|
||||||
|
|
||||||
FILE *bip_fp = fopen(bip_path, "rb");
|
FILE *bip_fp = fopen(bip_path, "rb");
|
||||||
if (bip_fp == NULL) {
|
if (bip_fp == NULL) {
|
||||||
|
@ -68,9 +63,7 @@ namespace sts::updater {
|
||||||
return fsdevGetLastResult();
|
return fsdevGetLastResult();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (written + read_size > size) {
|
STS_ASSERT(written + read_size <= size);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t aligned_size = ((read_size + SectorAlignment - 1) / SectorAlignment) * SectorAlignment;
|
size_t aligned_size = ((read_size + SectorAlignment - 1) / SectorAlignment) * SectorAlignment;
|
||||||
R_TRY(this->Write(offset + written, work_buffer, aligned_size));
|
R_TRY(this->Write(offset + written, work_buffer, aligned_size));
|
||||||
|
@ -84,9 +77,8 @@ namespace sts::updater {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result BisAccessor::Clear(u64 offset, u64 size, void *work_buffer, size_t work_buffer_size) {
|
Result BisAccessor::Clear(u64 offset, u64 size, void *work_buffer, size_t work_buffer_size) {
|
||||||
if (offset % SectorAlignment != 0 || work_buffer_size % SectorAlignment != 0) {
|
STS_ASSERT((offset % SectorAlignment) == 0);
|
||||||
std::abort();
|
STS_ASSERT((work_buffer_size % SectorAlignment) == 0);
|
||||||
}
|
|
||||||
|
|
||||||
std::memset(work_buffer, 0, work_buffer_size);
|
std::memset(work_buffer, 0, work_buffer_size);
|
||||||
|
|
||||||
|
@ -100,9 +92,8 @@ namespace sts::updater {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result BisAccessor::GetHash(void *dst, u64 offset, u64 size, u64 hash_size, void *work_buffer, size_t work_buffer_size) {
|
Result BisAccessor::GetHash(void *dst, u64 offset, u64 size, u64 hash_size, void *work_buffer, size_t work_buffer_size) {
|
||||||
if (offset % SectorAlignment != 0 || work_buffer_size % SectorAlignment != 0) {
|
STS_ASSERT((offset % SectorAlignment) == 0);
|
||||||
std::abort();
|
STS_ASSERT((work_buffer_size % SectorAlignment) == 0);
|
||||||
}
|
|
||||||
|
|
||||||
Sha256Context sha_ctx;
|
Sha256Context sha_ctx;
|
||||||
sha256ContextCreate(&sha_ctx);
|
sha256ContextCreate(&sha_ctx);
|
||||||
|
@ -122,18 +113,12 @@ namespace sts::updater {
|
||||||
|
|
||||||
size_t Boot0Accessor::GetBootloaderVersion(void *bct) {
|
size_t Boot0Accessor::GetBootloaderVersion(void *bct) {
|
||||||
u32 version = *reinterpret_cast<u32 *>(reinterpret_cast<uintptr_t>(bct) + BctVersionOffset);
|
u32 version = *reinterpret_cast<u32 *>(reinterpret_cast<uintptr_t>(bct) + BctVersionOffset);
|
||||||
if (version > BctVersionMax) {
|
STS_ASSERT(version <= BctVersionMax);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
return static_cast<size_t>(version);
|
return static_cast<size_t>(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Boot0Accessor::GetEksIndex(size_t bootloader_version) {
|
size_t Boot0Accessor::GetEksIndex(size_t bootloader_version) {
|
||||||
if (bootloader_version > BctVersionMax) {
|
STS_ASSERT(bootloader_version <= BctVersionMax);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (bootloader_version > 0) ? bootloader_version - 1 : 0;
|
return (bootloader_version > 0) ? bootloader_version - 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -140,9 +140,7 @@ namespace sts::updater {
|
||||||
public:
|
public:
|
||||||
Result Read(size_t *out_size, void *dst, size_t size, EnumType which) {
|
Result Read(size_t *out_size, void *dst, size_t size, EnumType which) {
|
||||||
const auto entry = FindEntry(which);
|
const auto entry = FindEntry(which);
|
||||||
if (size < entry->size) {
|
STS_ASSERT(size >= entry->size);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
R_TRY(BisAccessor::Read(dst, entry->size, entry->offset));
|
R_TRY(BisAccessor::Read(dst, entry->size, entry->offset));
|
||||||
|
|
||||||
|
@ -152,10 +150,8 @@ namespace sts::updater {
|
||||||
|
|
||||||
Result Write(const void *src, size_t size, EnumType which) {
|
Result Write(const void *src, size_t size, EnumType which) {
|
||||||
const auto entry = FindEntry(which);
|
const auto entry = FindEntry(which);
|
||||||
if (size > entry->size || size % BisAccessor::SectorAlignment != 0) {
|
STS_ASSERT(size <= entry->size);
|
||||||
std::abort();
|
STS_ASSERT((size % BisAccessor::SectorAlignment) == 0);
|
||||||
}
|
|
||||||
|
|
||||||
return BisAccessor::Write(entry->offset, src, size);
|
return BisAccessor::Write(entry->offset, src, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,9 +33,9 @@ namespace sts::updater {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result BisSave::Initialize(void *work_buffer, size_t work_buffer_size) {
|
Result BisSave::Initialize(void *work_buffer, size_t work_buffer_size) {
|
||||||
if (work_buffer_size < SaveSize || reinterpret_cast<uintptr_t>(work_buffer) & 0xFFF || work_buffer_size & 0x1FF) {
|
STS_ASSERT(work_buffer_size >= SaveSize);
|
||||||
std::abort();
|
STS_ASSERT(util::IsAligned(reinterpret_cast<uintptr_t>(work_buffer), 0x1000));
|
||||||
}
|
STS_ASSERT(util::IsAligned(work_buffer_size, 0x200));
|
||||||
|
|
||||||
R_TRY(this->accessor.Initialize());
|
R_TRY(this->accessor.Initialize());
|
||||||
this->save_buffer = work_buffer;
|
this->save_buffer = work_buffer;
|
||||||
|
|
|
@ -34,9 +34,7 @@ namespace sts::updater {
|
||||||
constexpr const char *Package2PathA = "bip:/a/package2";
|
constexpr const char *Package2PathA = "bip:/a/package2";
|
||||||
|
|
||||||
const char *ChooseCandidatePath(const char * const *candidates, size_t num_candidates) {
|
const char *ChooseCandidatePath(const char * const *candidates, size_t num_candidates) {
|
||||||
if (num_candidates == 0) {
|
STS_ASSERT(num_candidates > 0);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < num_candidates; i++) {
|
for (size_t i = 0; i < num_candidates; i++) {
|
||||||
struct stat buf;
|
struct stat buf;
|
||||||
|
|
|
@ -25,9 +25,8 @@ namespace sts::util {
|
||||||
/* Compression utilities. */
|
/* Compression utilities. */
|
||||||
int CompressLZ4(void *dst, size_t dst_size, const void *src, size_t src_size) {
|
int CompressLZ4(void *dst, size_t dst_size, const void *src, size_t src_size) {
|
||||||
/* Size checks. */
|
/* Size checks. */
|
||||||
if (dst_size > std::numeric_limits<int>::max() || src_size > std::numeric_limits<int>::max()) {
|
STS_ASSERT(dst_size <= std::numeric_limits<int>::max());
|
||||||
std::abort();
|
STS_ASSERT(src_size <= std::numeric_limits<int>::max());
|
||||||
}
|
|
||||||
|
|
||||||
/* This is just a thin wrapper around LZ4. */
|
/* This is just a thin wrapper around LZ4. */
|
||||||
return LZ4_compress_default(reinterpret_cast<const char *>(src), reinterpret_cast<char *>(dst), static_cast<int>(src_size), static_cast<int>(dst_size));
|
return LZ4_compress_default(reinterpret_cast<const char *>(src), reinterpret_cast<char *>(dst), static_cast<int>(src_size), static_cast<int>(dst_size));
|
||||||
|
@ -36,9 +35,8 @@ namespace sts::util {
|
||||||
/* Decompression utilities. */
|
/* Decompression utilities. */
|
||||||
int DecompressLZ4(void *dst, size_t dst_size, const void *src, size_t src_size) {
|
int DecompressLZ4(void *dst, size_t dst_size, const void *src, size_t src_size) {
|
||||||
/* Size checks. */
|
/* Size checks. */
|
||||||
if (dst_size > std::numeric_limits<int>::max() || src_size > std::numeric_limits<int>::max()) {
|
STS_ASSERT(dst_size <= std::numeric_limits<int>::max());
|
||||||
std::abort();
|
STS_ASSERT(src_size <= std::numeric_limits<int>::max());
|
||||||
}
|
|
||||||
|
|
||||||
/* This is just a thin wrapper around LZ4. */
|
/* This is just a thin wrapper around LZ4. */
|
||||||
return LZ4_decompress_safe(reinterpret_cast<const char *>(src), reinterpret_cast<char *>(dst), static_cast<int>(src_size), static_cast<int>(dst_size));
|
return LZ4_decompress_safe(reinterpret_cast<const char *>(src), reinterpret_cast<char *>(dst), static_cast<int>(src_size), static_cast<int>(dst_size));
|
||||||
|
|
|
@ -50,9 +50,7 @@ namespace sts::util::ini {
|
||||||
size_t try_read = std::min(size_t(num - 1), ctx->num_left);
|
size_t try_read = std::min(size_t(num - 1), ctx->num_left);
|
||||||
size_t actually_read;
|
size_t actually_read;
|
||||||
R_ASSERT(fsFileRead(ctx->f, ctx->offset, str, try_read, FS_READOPTION_NONE, &actually_read));
|
R_ASSERT(fsFileRead(ctx->f, ctx->offset, str, try_read, FS_READOPTION_NONE, &actually_read));
|
||||||
if (actually_read != try_read) {
|
STS_ASSERT(actually_read == try_read);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Only "read" up to the first \n. */
|
/* Only "read" up to the first \n. */
|
||||||
size_t offset = actually_read;
|
size_t offset = actually_read;
|
||||||
|
|
|
@ -83,9 +83,7 @@ namespace sts::ldr {
|
||||||
Result MountNspFileSystem(const char *device_name, const char *path) {
|
Result MountNspFileSystem(const char *device_name, const char *path) {
|
||||||
FsFileSystem fs;
|
FsFileSystem fs;
|
||||||
R_TRY(fsOpenFileSystemWithId(&fs, 0, FsFileSystemType_ApplicationPackage, path));
|
R_TRY(fsOpenFileSystemWithId(&fs, 0, FsFileSystemType_ApplicationPackage, path));
|
||||||
if(fsdevMountDevice(device_name, fs) < 0) {
|
STS_ASSERT(fsdevMountDevice(device_name, fs) >= 0);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,9 +176,7 @@ namespace sts::ldr {
|
||||||
/* Try to mount the content path. */
|
/* Try to mount the content path. */
|
||||||
FsFileSystem fs;
|
FsFileSystem fs;
|
||||||
R_TRY(fsldrOpenCodeFileSystem(static_cast<u64>(loc.title_id), path, &fs));
|
R_TRY(fsldrOpenCodeFileSystem(static_cast<u64>(loc.title_id), path, &fs));
|
||||||
if(fsdevMountDevice(CodeFileSystemDeviceName, fs) == -1) {
|
STS_ASSERT(fsdevMountDevice(CodeFileSystemDeviceName, fs) != -1);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Note that we mounted code. */
|
/* Note that we mounted code. */
|
||||||
this->is_code_mounted = true;
|
this->is_code_mounted = true;
|
||||||
|
|
|
@ -73,7 +73,7 @@ namespace sts::ldr::ecs {
|
||||||
std::snprintf(device_name, DeviceNameSizeMax, "ecs-%016lx", static_cast<u64>(title_id));
|
std::snprintf(device_name, DeviceNameSizeMax, "ecs-%016lx", static_cast<u64>(title_id));
|
||||||
|
|
||||||
/* Create session. */
|
/* Create session. */
|
||||||
AutoHandle server, client;
|
os::ManagedHandle server, client;
|
||||||
R_TRY(svcCreateSession(server.GetPointer(), client.GetPointer(), 0, 0));
|
R_TRY(svcCreateSession(server.GetPointer(), client.GetPointer(), 0, 0));
|
||||||
|
|
||||||
/* Create service. */
|
/* Create service. */
|
||||||
|
|
|
@ -27,7 +27,7 @@ namespace sts::ldr {
|
||||||
|
|
||||||
/* Official commands. */
|
/* Official commands. */
|
||||||
Result LoaderService::CreateProcess(Out<MovedHandle> proc_h, PinId id, u32 flags, CopiedHandle reslimit) {
|
Result LoaderService::CreateProcess(Out<MovedHandle> proc_h, PinId id, u32 flags, CopiedHandle reslimit) {
|
||||||
AutoHandle reslimit_holder(reslimit.GetValue());
|
os::ManagedHandle reslimit_holder(reslimit.GetValue());
|
||||||
ncm::TitleLocation loc;
|
ncm::TitleLocation loc;
|
||||||
char path[FS_MAX_PATH];
|
char path[FS_MAX_PATH];
|
||||||
|
|
||||||
|
|
|
@ -56,9 +56,7 @@ namespace sts::ldr {
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr const char *GetNsoName(size_t idx) {
|
constexpr const char *GetNsoName(size_t idx) {
|
||||||
if (idx >= Nso_Count) {
|
STS_ASSERT(idx < Nso_Count);
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr const char *NsoNames[Nso_Count] = {
|
constexpr const char *NsoNames[Nso_Count] = {
|
||||||
"rtld",
|
"rtld",
|
||||||
|
@ -91,7 +89,7 @@ namespace sts::ldr {
|
||||||
static_assert(sizeof(CreateProcessInfo) == 0x30, "CreateProcessInfo definition!");
|
static_assert(sizeof(CreateProcessInfo) == 0x30, "CreateProcessInfo definition!");
|
||||||
|
|
||||||
struct ProcessInfo {
|
struct ProcessInfo {
|
||||||
AutoHandle process_handle;
|
os::ManagedHandle process_handle;
|
||||||
uintptr_t args_address;
|
uintptr_t args_address;
|
||||||
size_t args_size;
|
size_t args_size;
|
||||||
uintptr_t nso_address[Nso_Count];
|
uintptr_t nso_address[Nso_Count];
|
||||||
|
|
|
@ -284,9 +284,7 @@ namespace sts::boot2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Don't allow invalid lines. */
|
/* Don't allow invalid lines. */
|
||||||
if (name_len > sizeof(sm::ServiceName)) {
|
STS_ASSERT(name_len <= sizeof(sm::ServiceName));
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Declare the service. */
|
/* Declare the service. */
|
||||||
R_ASSERT(sm::mitm::DeclareFutureMitm(sm::ServiceName::Encode(mitm_list + offset, name_len)));
|
R_ASSERT(sm::mitm::DeclareFutureMitm(sm::ServiceName::Encode(mitm_list + offset, name_len)));
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue