elf2kip/npdmtool: update json format to reflect modern OS.

Version field was incorrectly labeled "process_category". This is now supported (and defaults to 0 if not present on npdm, 1 if not present on kip). process_category is alias.

Support was added for mesosphere large-address map extension (these bits are reserved in official OS).

Support was added for specifying the signature key generation, which determines modulus used to verify ACID.

Support was added for system call capabilities in range [0x80, 0xBF], which kernel allows since 11.0.0.

"title_id" (and min/max) were renamed to program_id. If program_id not present, title_id used as alias.
This commit is contained in:
Michael Scire 2022-03-13 15:35:33 -07:00 committed by Dave Murphy
parent 6bad2b90e6
commit 87f4744e62
2 changed files with 64 additions and 29 deletions

View File

@ -19,8 +19,8 @@ typedef struct {
typedef struct {
u8 Magic[4];
u8 Name[0xC];
u64 TitleId;
u32 ProcessCategory;
u64 ProgramId;
u32 Version;
u8 MainThreadPriority;
u8 DefaultCpuId;
u8 Unk;
@ -185,6 +185,32 @@ int cJSON_GetU64FromObjectValue(const cJSON *config, u64 *out) {
}
}
int cJSON_GetU32(const cJSON *obj, const char *field, u32 *out) {
const cJSON *config = cJSON_GetObjectItemCaseSensitive(obj, field);
if (cJSON_IsString(config) && (config->valuestring != NULL)) {
char *endptr = NULL;
*out = strtoul(config->valuestring, &endptr, 16);
if (config->valuestring == endptr) {
fprintf(stderr, "Failed to get %s (empty string)\n", field);
return 0;
} else if (errno == ERANGE) {
fprintf(stderr, "Failed to get %s (value out of range)\n", field);
return 0;
} else if (errno == EINVAL) {
fprintf(stderr, "Failed to get %s (not base16 string)\n", field);
return 0;
} else if (errno) {
fprintf(stderr, "Failed to get %s (unknown error)\n", field);
return 0;
} else {
return 1;
}
} else {
fprintf(stderr, "Failed to get %s (field not present).\n", field);
return 0;
}
}
int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) {
const cJSON *capability = NULL;
const cJSON *capabilities = NULL;
@ -209,8 +235,8 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) {
goto PARSE_CAPS_END;
}
/* Parse title_id. */
if (!cJSON_GetU64(npdm_json, "title_id", &kip_hdr->TitleId)) {
/* Parse program_id (or deprecated title_id). */
if (!cJSON_GetU64(npdm_json, "program_id", &kip_hdr->ProgramId) && !cJSON_GetU64(npdm_json, "title_id", &kip_hdr->ProgramId)) {
status = 0;
goto PARSE_CAPS_END;
}
@ -259,9 +285,8 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) {
status = 0;
goto PARSE_CAPS_END;
}
if (!cJSON_GetU8(npdm_json, "process_category", (u8 *)&kip_hdr->ProcessCategory)) {
status = 0;
goto PARSE_CAPS_END;
if (!cJSON_GetU32(npdm_json, "version", &kip_hdr->Version) && !cJSON_GetU32(npdm_json, "process_category", &kip_hdr->Version)) { // optional
kip_hdr->Version = 1;
}
/* Parse capabilities. */
@ -331,7 +356,7 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) {
goto PARSE_CAPS_END;
}
u32 num_descriptors;
u32 descriptors[6] = {0}; /* alignup(0x80/0x18); */
u32 descriptors[8] = {0}; /* alignup(0xC0/0x18); */
char field_name[8] = {0};
const cJSON *cur_syscall = NULL;
u64 syscall_value = 0;
@ -344,14 +369,14 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) {
goto PARSE_CAPS_END;
}
if (syscall_value >= 0x80) {
fprintf(stderr, "Error: All syscall entries must be numbers in [0, 0x7F]\n");
if (syscall_value >= 0xC0) {
fprintf(stderr, "Error: All syscall entries must be numbers in [0, 0xBF]\n");
status = 0;
goto PARSE_CAPS_END;
}
descriptors[syscall_value / 0x18] |= (1UL << (syscall_value % 0x18));
}
for (unsigned int i = 0; i < 6; i++) {
for (unsigned int i = 0; i < 8; i++) {
if (descriptors[i]) {
if (cur_cap + 1 > 0x20) {
fprintf(stderr, "Error: Too many capabilities!\n");
@ -388,7 +413,8 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) {
desc |= is_ro << 24;
kip_hdr->Capabilities[cur_cap++] = (u32)((desc << 7) | (0x003F));
desc = (u32)((map_size >> 12) & 0x00FFFFFFULL);
desc = (u32)((map_size >> 12) & 0x000FFFFFULL);
desc |= (u32)(((map_address >> 36) & 0xFULL) << 20);
is_io ^= 1;
desc |= is_io << 24;
kip_hdr->Capabilities[cur_cap++] = (u32)((desc << 7) | (0x003F));

View File

@ -45,7 +45,7 @@ typedef struct {
typedef struct {
u32 Magic;
u8 _0x4[0xC];
u64 TitleId;
u64 ProgramId;
u64 _0x18;
u32 FahOffset;
u32 FahSize;
@ -63,8 +63,8 @@ typedef struct {
u32 Size;
u32 _0x208;
u32 Flags;
u64 TitleIdRangeMin;
u64 TitleIdRangeMax;
u64 ProgramIdRangeMin;
u64 ProgramIdRangeMax;
u32 FacOffset;
u32 FacSize;
u32 SacOffset;
@ -76,7 +76,7 @@ typedef struct {
typedef struct {
u32 Magic;
u32 _0x4;
u32 SignatureKeyGeneration;
u32 _0x8;
u8 MmuFlags;
u8 _0xD;
@ -84,7 +84,7 @@ typedef struct {
u8 DefaultCpuId;
u32 _0x10;
u32 SystemResourceSize;
u32 ProcessCategory;
u32 Version;
u32 MainThreadStackSize;
char Name[0x10];
char ProductCode[0x10];
@ -345,10 +345,11 @@ int CreateNpdm(const char *json, void **dst, u32 *dst_size) {
cJSON_GetU32(npdm_json, "system_resource_size", &header.SystemResourceSize); // optional
if (!cJSON_GetU8(npdm_json, "process_category", (u8 *)&header.ProcessCategory)) {
status = 0;
goto NPDM_BUILD_END;
/* Get version (deprecated name "process_category"). */
if (!cJSON_GetU32(npdm_json, "version", &header.Version) && !cJSON_GetU32(npdm_json, "process_category", &header.Version)) { // optional
header.Version = 0;
}
if (!cJSON_GetU8(npdm_json, "address_space_type", (u8 *)&header.MmuFlags)) {
status = 0;
goto NPDM_BUILD_END;
@ -367,6 +368,13 @@ int CreateNpdm(const char *json, void **dst, u32 *dst_size) {
header.MmuFlags |= ((disable_device_address_space_merge & 1) << 5);
}
u8 signature_key_generation; // optional
if (cJSON_GetU8(npdm_json, "signature_key_generation", &signature_key_generation)) {
header.SignatureKeyGeneration = signature_key_generation;
} else {
header.SignatureKeyGeneration = 0;
}
/* ACID. */
memset(acid->Signature, 0, sizeof(acid->Signature));
memset(acid->Modulus, 0, sizeof(acid->Modulus));
@ -384,19 +392,19 @@ int CreateNpdm(const char *json, void **dst, u32 *dst_size) {
}
acid->Flags |= (pool_partition & 3) << 2;
if (!cJSON_GetU64(npdm_json, "title_id_range_min", &acid->TitleIdRangeMin)) {
if (!cJSON_GetU64(npdm_json, "program_id_range_min", &acid->ProgramIdRangeMin) && !cJSON_GetU64(npdm_json, "title_id_range_min", &acid->ProgramIdRangeMin)) {
status = 0;
goto NPDM_BUILD_END;
}
if (!cJSON_GetU64(npdm_json, "title_id_range_max", &acid->TitleIdRangeMax)) {
if (!cJSON_GetU64(npdm_json, "program_id_range_max", &acid->ProgramIdRangeMax) && !cJSON_GetU64(npdm_json, "title_id_range_max", &acid->ProgramIdRangeMax)) {
status = 0;
goto NPDM_BUILD_END;
}
/* ACI0. */
aci0->Magic = MAGIC_ACI0; /* "ACI0" */
/* Parse title_id. */
if (!cJSON_GetU64(npdm_json, "title_id", &aci0->TitleId)) {
/* Parse program_id (or deprecated title_id). */
if (!cJSON_GetU64(npdm_json, "program_id", &aci0->ProgramId) && !cJSON_GetU64(npdm_json, "title_id", &aci0->ProgramId)) {
status = 0;
goto NPDM_BUILD_END;
}
@ -633,7 +641,7 @@ int CreateNpdm(const char *json, void **dst, u32 *dst_size) {
goto NPDM_BUILD_END;
}
u32 num_descriptors;
u32 descriptors[6] = {0}; /* alignup(0x80/0x18); */
u32 descriptors[8] = {0}; /* alignup(0xC0/0x18); */
char field_name[8] = {0};
const cJSON *cur_syscall = NULL;
u64 syscall_value = 0;
@ -646,14 +654,14 @@ int CreateNpdm(const char *json, void **dst, u32 *dst_size) {
goto NPDM_BUILD_END;
}
if (syscall_value >= 0x80) {
fprintf(stderr, "Error: All syscall entries must be numbers in [0, 0x7F]\n");
if (syscall_value >= 0xC0) {
fprintf(stderr, "Error: All syscall entries must be numbers in [0, 0xBF]\n");
status = 0;
goto NPDM_BUILD_END;
}
descriptors[syscall_value / 0x18] |= (1UL << (syscall_value % 0x18));
}
for (unsigned int i = 0; i < 6; i++) {
for (unsigned int i = 0; i < 8; i++) {
if (descriptors[i]) {
desc = descriptors[i] | (i << 24);
caps[cur_cap++] = (u32)((desc << 5) | (0x000F));
@ -680,7 +688,8 @@ int CreateNpdm(const char *json, void **dst, u32 *dst_size) {
desc |= is_ro << 24;
caps[cur_cap++] = (u32)((desc << 7) | (0x003F));
desc = (u32)((map_size >> 12) & 0x00FFFFFFULL);
desc = (u32)((map_size >> 12) & 0x000FFFFFULL);
desc |= (u32)(((map_address >> 36) & 0xFULL) << 20);
is_io ^= 1;
desc |= is_io << 24;
caps[cur_cap++] = (u32)((desc << 7) | (0x003F));