mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-11-24 15:50:29 +01:00
Stratosphere: Implement ldr:pm->GetProgramInfo()
This commit is contained in:
parent
0fb107fb86
commit
b6ba7b94b9
@ -14,6 +14,13 @@ Result ContentManagement::MountCode(u64 tid, FsStorageId sid) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Fix up path. */
|
||||
for (unsigned int i = 0; i < FS_MAX_PATH && path[i] != '\x00'; i++) {
|
||||
if (path[i] == '\\') {
|
||||
path[i] = '/';
|
||||
}
|
||||
}
|
||||
|
||||
/* Always re-initialize fsp-ldr, in case it's closed */
|
||||
if (R_FAILED(rc = fsldrInitialize())) {
|
||||
return rc;
|
||||
@ -31,7 +38,6 @@ Result ContentManagement::MountCode(u64 tid, FsStorageId sid) {
|
||||
|
||||
Result ContentManagement::UnmountCode() {
|
||||
fsdevUnmountDevice("code");
|
||||
serviceClose(&g_CodeFileSystem.s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -76,7 +82,7 @@ Result ContentManagement::GetContentPathForTidSid(char *out_path, Registration::
|
||||
return GetContentPath(out_path, tid_sid->title_id, tid_sid->storage_id);
|
||||
}
|
||||
|
||||
Result ContentManagement::SetContentPath(char *path, u64 tid, FsStorageId sid) {
|
||||
Result ContentManagement::SetContentPath(const char *path, u64 tid, FsStorageId sid) {
|
||||
Result rc;
|
||||
LrLocationResolver lr;
|
||||
|
||||
@ -91,6 +97,6 @@ Result ContentManagement::SetContentPath(char *path, u64 tid, FsStorageId sid) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result ContentManagement::SetContentPathForTidSid(char *path, Registration::TidSid *tid_sid) {
|
||||
Result ContentManagement::SetContentPathForTidSid(const char *path, Registration::TidSid *tid_sid) {
|
||||
return SetContentPath(path, tid_sid->title_id, tid_sid->storage_id);
|
||||
}
|
@ -5,12 +5,12 @@
|
||||
|
||||
class ContentManagement {
|
||||
public:
|
||||
Result MountCode(u64 tid, FsStorageId sid);
|
||||
Result UnmountCode();
|
||||
Result MountCodeForTidSid(Registration::TidSid *tid_sid);
|
||||
static Result MountCode(u64 tid, FsStorageId sid);
|
||||
static Result UnmountCode();
|
||||
static Result MountCodeForTidSid(Registration::TidSid *tid_sid);
|
||||
|
||||
Result GetContentPath(char *out_path, u64 tid, FsStorageId sid);
|
||||
Result SetContentPath(char *path, u64 tid, FsStorageId sid);
|
||||
Result GetContentPathForTidSid(char *out_path, Registration::TidSid *tid_sid);
|
||||
Result SetContentPathForTidSid(char *path, Registration::TidSid *tid_sid);
|
||||
static Result GetContentPath(char *out_path, u64 tid, FsStorageId sid);
|
||||
static Result SetContentPath(const char *path, u64 tid, FsStorageId sid);
|
||||
static Result GetContentPathForTidSid(char *out_path, Registration::TidSid *tid_sid);
|
||||
static Result SetContentPathForTidSid(const char *path, Registration::TidSid *tid_sid);
|
||||
};
|
||||
|
@ -21,6 +21,16 @@ Result LaunchQueue::add(u64 tid, const char *args, u64 arg_size) {
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
Result LaunchQueue::add_copy(u64 tid_base, u64 tid) {
|
||||
unsigned int idx = get_index(tid_base);
|
||||
if (idx == LAUNCH_QUEUE_FULL) {
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
return add(tid, g_launch_queue[idx].args, g_launch_queue[idx].arg_size);
|
||||
}
|
||||
|
||||
|
||||
Result LaunchQueue::add_item(const LaunchItem *item) {
|
||||
if(item->arg_size > LAUNCH_QUEUE_ARG_SIZE_MAX) {
|
||||
return 0x209;
|
||||
|
@ -16,6 +16,7 @@ class LaunchQueue {
|
||||
|
||||
static Result add(u64 tid, const char *args, u64 arg_size);
|
||||
static Result add_item(const LaunchItem *item);
|
||||
static Result add_copy(u64 tid_base, u64 new_tid);
|
||||
static int get_index(u64 tid);
|
||||
static int get_free_index(u64 tid);
|
||||
static bool contains(u64 tid);
|
||||
|
113
stratosphere/loader/source/ldr_npdm.cpp
Normal file
113
stratosphere/loader/source/ldr_npdm.cpp
Normal file
@ -0,0 +1,113 @@
|
||||
#include <switch.h>
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include "ldr_npdm.hpp"
|
||||
#include "ldr_registration.hpp"
|
||||
|
||||
static NpdmUtils::NpdmCache g_npdm_cache = {0};
|
||||
|
||||
|
||||
Result NpdmUtils::LoadNpdm(u64 tid, NpdmInfo *out) {
|
||||
Result rc;
|
||||
|
||||
g_npdm_cache.info = (const NpdmUtils::NpdmInfo){0};
|
||||
|
||||
FILE *f_npdm = fopen("code:/main.npdm", "rb");
|
||||
if (f_npdm == NULL) {
|
||||
/* For generic "Couldn't open the file" error, just say the file doesn't exist. */
|
||||
return 0x202;
|
||||
}
|
||||
|
||||
fseek(f_npdm, 0, SEEK_END);
|
||||
size_t npdm_size = ftell(f_npdm);
|
||||
fseek(f_npdm, 0, SEEK_SET);
|
||||
|
||||
if (npdm_size > sizeof(g_npdm_cache.buffer) || fread(g_npdm_cache.buffer, 1, npdm_size, f_npdm) != npdm_size) {
|
||||
return 0x609;
|
||||
}
|
||||
|
||||
fclose(f_npdm);
|
||||
|
||||
rc = 0x809;
|
||||
if (npdm_size < sizeof(NpdmUtils::NpdmHeader)) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* For ease of access... */
|
||||
g_npdm_cache.info.header = (NpdmUtils::NpdmHeader *)(g_npdm_cache.buffer);
|
||||
NpdmInfo *info = &g_npdm_cache.info;
|
||||
|
||||
if (info->header->magic != MAGIC_META) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (info->header->mmu_flags > 0xF) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (info->header->aci0_offset < sizeof(NpdmUtils::NpdmHeader) || info->header->aci0_size < sizeof(NpdmUtils::NpdmAci0) || info->header->aci0_offset + info->header->aci0_size > npdm_size) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
info->aci0 = (NpdmAci0 *)(g_npdm_cache.buffer + info->header->aci0_offset);
|
||||
|
||||
if (info->aci0->magic != MAGIC_ACI0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (info->aci0->fah_size > info->header->aci0_size || info->aci0->fah_offset < sizeof(NpdmUtils::NpdmAci0) || info->aci0->fah_offset + info->aci0->fah_size > info->header->aci0_size) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
info->aci0_fah = (void *)((uintptr_t)info->aci0 + info->aci0->fah_offset);
|
||||
|
||||
if (info->aci0->sac_size > info->header->aci0_size || info->aci0->sac_offset < sizeof(NpdmUtils::NpdmAci0) || info->aci0->sac_offset + info->aci0->sac_size > info->header->aci0_size) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
info->aci0_sac = (void *)((uintptr_t)info->aci0 + info->aci0->sac_offset);
|
||||
|
||||
if (info->aci0->kac_size > info->header->aci0_size || info->aci0->kac_offset < sizeof(NpdmUtils::NpdmAci0) || info->aci0->kac_offset + info->aci0->kac_size > info->header->aci0_size) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
info->aci0_kac = (void *)((uintptr_t)info->aci0 + info->aci0->kac_offset);
|
||||
|
||||
if (info->header->acid_offset < sizeof(NpdmUtils::NpdmHeader) || info->header->acid_size < sizeof(NpdmUtils::NpdmAcid) || info->header->acid_offset + info->header->acid_size > npdm_size) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
info->acid = (NpdmAcid *)(g_npdm_cache.buffer + info->header->acid_offset);
|
||||
|
||||
if (info->acid->magic != MAGIC_ACID) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* TODO: Check if retail flag is set if not development hardware. */
|
||||
|
||||
if (info->acid->fac_size > info->header->acid_size || info->acid->fac_offset < sizeof(NpdmUtils::NpdmAcid) || info->acid->fac_offset + info->acid->fac_size > info->header->acid_size) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
info->acid_fac = (void *)((uintptr_t)info->acid + info->acid->fac_offset);
|
||||
|
||||
if (info->acid->sac_size > info->header->acid_size || info->acid->sac_offset < sizeof(NpdmUtils::NpdmAcid) || info->acid->sac_offset + info->acid->sac_size > info->header->acid_size) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
info->acid_sac = (void *)((uintptr_t)info->acid + info->acid->sac_offset);
|
||||
|
||||
if (info->acid->kac_size > info->header->acid_size || info->acid->kac_offset < sizeof(NpdmUtils::NpdmAcid) || info->acid->kac_offset + info->acid->kac_size > info->header->acid_size) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
info->acid_kac = (void *)((uintptr_t)info->acid + info->acid->kac_offset);
|
||||
|
||||
|
||||
/* We validated! */
|
||||
info->title_id = tid;
|
||||
*out = *info;
|
||||
rc = 0;
|
||||
|
||||
return rc;
|
||||
}
|
81
stratosphere/loader/source/ldr_npdm.hpp
Normal file
81
stratosphere/loader/source/ldr_npdm.hpp
Normal file
@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
#include "ldr_registration.hpp"
|
||||
|
||||
#define MAGIC_META 0x4154454D
|
||||
#define MAGIC_ACI0 0x30494341
|
||||
#define MAGIC_ACID 0x44494341
|
||||
|
||||
class NpdmUtils {
|
||||
public:
|
||||
struct NpdmHeader {
|
||||
u32 magic;
|
||||
u32 _0x4;
|
||||
u32 _0x8;
|
||||
u8 mmu_flags;
|
||||
u8 _0xD;
|
||||
u8 main_thread_prio;
|
||||
u8 default_cpuid;
|
||||
u64 _0x10;
|
||||
u32 process_category;
|
||||
u32 main_stack_size;
|
||||
char title_name[0x50];
|
||||
u32 aci0_offset;
|
||||
u32 aci0_size;
|
||||
u32 acid_offset;
|
||||
u32 acid_size;
|
||||
};
|
||||
struct NpdmAcid {
|
||||
u8 signature[0x100];
|
||||
u8 modulus[0x100];
|
||||
u32 magic;
|
||||
u32 size;
|
||||
u32 _0x208;
|
||||
u32 is_retail;
|
||||
u64 title_id_range_min;
|
||||
u64 title_id_range_max;
|
||||
u32 fac_offset;
|
||||
u32 fac_size;
|
||||
u32 sac_offset;
|
||||
u32 sac_size;
|
||||
u32 kac_offset;
|
||||
u32 kac_size;
|
||||
u64 padding;
|
||||
};
|
||||
struct NpdmAci0 {
|
||||
u32 magic;
|
||||
u8 _0x4[0xC];
|
||||
u64 title_id;
|
||||
u64 _0x18;
|
||||
u32 fah_offset;
|
||||
u32 fah_size;
|
||||
u32 sac_offset;
|
||||
u32 sac_size;
|
||||
u32 kac_offset;
|
||||
u32 kac_size;
|
||||
u64 padding;
|
||||
};
|
||||
struct NpdmInfo {
|
||||
NpdmHeader *header;
|
||||
NpdmAcid *acid;
|
||||
NpdmAci0 *aci0;
|
||||
void *acid_fac;
|
||||
void *acid_sac;
|
||||
void *acid_kac;
|
||||
void *aci0_fah;
|
||||
void *aci0_sac;
|
||||
void *aci0_kac;
|
||||
u64 title_id;
|
||||
};
|
||||
struct NpdmCache {
|
||||
NpdmInfo info;
|
||||
u8 buffer[0x8000];
|
||||
};
|
||||
|
||||
static_assert(sizeof(NpdmHeader) == 0x80, "Incorrectly defined NpdmHeader!");
|
||||
static_assert(sizeof(NpdmAcid) == 0x240, "Incorrectly defined NpdmAcid!");
|
||||
static_assert(sizeof(NpdmAci0) == 0x40, "Incorrectly defined NpdmAci0!");
|
||||
|
||||
static Result LoadNpdm(u64 tid, NpdmInfo *out);
|
||||
};
|
@ -2,6 +2,8 @@
|
||||
#include "ldr_process_manager.hpp"
|
||||
#include "ldr_registration.hpp"
|
||||
#include "ldr_launch_queue.hpp"
|
||||
#include "ldr_content_management.hpp"
|
||||
#include "ldr_npdm.hpp"
|
||||
|
||||
Result ProcessManagerService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) {
|
||||
|
||||
@ -32,10 +34,32 @@ std::tuple<Result> ProcessManagerService::create_process() {
|
||||
}
|
||||
|
||||
std::tuple<Result> ProcessManagerService::get_program_info(Registration::TidSid tid_sid, OutPointerWithServerSize<ProcessManagerService::ProgramInfo, 0x1> out_program_info) {
|
||||
Result rc;
|
||||
char nca_path[FS_MAX_PATH] = {0};
|
||||
/* Zero output. */
|
||||
std::fill(out_program_info.pointer, out_program_info.pointer + out_program_info.num_elements, (const ProcessManagerService::ProgramInfo){0});
|
||||
|
||||
return std::make_tuple(0xA09);
|
||||
rc = populate_program_info_buffer(out_program_info.pointer, &tid_sid);
|
||||
|
||||
if (R_FAILED(rc)) {
|
||||
return std::make_tuple(rc);
|
||||
}
|
||||
|
||||
if (tid_sid.title_id != out_program_info.pointer->title_id_min) {
|
||||
rc = ContentManagement::GetContentPathForTidSid(nca_path, &tid_sid);
|
||||
if (R_FAILED(rc)) {
|
||||
return std::make_tuple(rc);
|
||||
}
|
||||
|
||||
rc = ContentManagement::SetContentPath(nca_path, out_program_info.pointer->title_id_min, tid_sid.storage_id);
|
||||
if (R_FAILED(rc)) {
|
||||
return std::make_tuple(rc);
|
||||
}
|
||||
|
||||
rc = LaunchQueue::add_copy(tid_sid.title_id, out_program_info.pointer->title_id_min);
|
||||
}
|
||||
|
||||
return std::make_tuple(rc);
|
||||
}
|
||||
|
||||
std::tuple<Result, u64> ProcessManagerService::register_title(Registration::TidSid tid_sid) {
|
||||
@ -54,3 +78,74 @@ std::tuple<Result> ProcessManagerService::unregister_title(u64 index) {
|
||||
return std::make_tuple(0x1009);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Result ProcessManagerService::populate_program_info_buffer(ProcessManagerService::ProgramInfo *out, Registration::TidSid *tid_sid) {
|
||||
NpdmUtils::NpdmInfo info;
|
||||
Result rc;
|
||||
|
||||
rc = ContentManagement::MountCodeForTidSid(tid_sid);
|
||||
if (R_FAILED(rc)) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = NpdmUtils::LoadNpdm(tid_sid->title_id, &info);
|
||||
if (R_FAILED(rc)) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
ContentManagement::UnmountCode();
|
||||
|
||||
out->main_thread_priority = info.header->main_thread_prio;
|
||||
out->default_cpu_id = info.header->default_cpuid;
|
||||
out->main_thread_stack_size = info.header->main_stack_size;
|
||||
out->title_id_min = info.acid->title_id_range_min;
|
||||
|
||||
out->acid_fac_size = info.acid->fac_size;
|
||||
out->aci0_sac_size = info.aci0->sac_size;
|
||||
out->aci0_fah_size = info.aci0->fah_size;
|
||||
|
||||
size_t offset = 0;
|
||||
rc = 0x19009;
|
||||
if (offset + info.acid->sac_size < sizeof(out->ac_buffer)) {
|
||||
out->acid_sac_size = info.acid->sac_size;
|
||||
std::memcpy(out->ac_buffer + offset, info.acid_sac, out->acid_sac_size);
|
||||
offset += out->acid_sac_size;
|
||||
if (offset + info.aci0->sac_size < sizeof(out->ac_buffer)) {
|
||||
out->aci0_sac_size = info.aci0->sac_size;
|
||||
std::memcpy(out->ac_buffer + offset, info.aci0_sac, out->aci0_sac_size);
|
||||
offset += out->aci0_sac_size;
|
||||
if (offset + info.acid->fac_size < sizeof(out->ac_buffer)) {
|
||||
out->acid_fac_size = info.acid->fac_size;
|
||||
std::memcpy(out->ac_buffer + offset, info.acid_fac, out->acid_fac_size);
|
||||
offset += out->acid_fac_size;
|
||||
if (offset + info.aci0->fah_size < sizeof(out->ac_buffer)) {
|
||||
out->aci0_fah_size = info.aci0->fah_size;
|
||||
std::memcpy(out->ac_buffer + offset, info.aci0_fah, out->aci0_fah_size);
|
||||
offset += out->aci0_fah_size;
|
||||
rc = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse application type. */
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
u32 *kac = (u32 *)info.acid_kac;
|
||||
u32 num_entries = info.acid->kac_size / sizeof(u32);
|
||||
out->application_type = 0;
|
||||
for (unsigned int i = 0; i < num_entries; i++) {
|
||||
if ((kac[i] & 0x3FFF) == 0x1FFF) {
|
||||
u16 app_type = (kac[i] >> 14) & 7;
|
||||
if (app_type == 1) {
|
||||
out->application_type |= 1;
|
||||
} else if (app_type == 2) {
|
||||
out->application_type |= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return rc;
|
||||
}
|
@ -21,7 +21,7 @@ class ProcessManagerService : IServiceObject {
|
||||
u32 acid_sac_size;
|
||||
u32 aci0_sac_size;
|
||||
u32 acid_fac_size;
|
||||
u32 aci0_fac_size;
|
||||
u32 aci0_fah_size;
|
||||
u8 ac_buffer[0x3E0];
|
||||
};
|
||||
|
||||
@ -36,4 +36,7 @@ class ProcessManagerService : IServiceObject {
|
||||
std::tuple<Result> get_program_info(Registration::TidSid tid_sid, OutPointerWithServerSize<ProcessManagerService::ProgramInfo, 0x1> out_program_info);
|
||||
std::tuple<Result, u64> register_title(Registration::TidSid tid_sid);
|
||||
std::tuple<Result> unregister_title(u64 index);
|
||||
|
||||
/* Utilities */
|
||||
Result populate_program_info_buffer(ProcessManagerService::ProgramInfo *out, Registration::TidSid *tid_sid);
|
||||
};
|
Loading…
Reference in New Issue
Block a user