Skip to content

Commit 87f4744

Browse files
SciresMWinterMute
authored andcommitted
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.
1 parent 6bad2b9 commit 87f4744

2 files changed

Lines changed: 64 additions & 29 deletions

File tree

src/elf2kip.c

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ typedef struct {
1919
typedef struct {
2020
u8 Magic[4];
2121
u8 Name[0xC];
22-
u64 TitleId;
23-
u32 ProcessCategory;
22+
u64 ProgramId;
23+
u32 Version;
2424
u8 MainThreadPriority;
2525
u8 DefaultCpuId;
2626
u8 Unk;
@@ -185,6 +185,32 @@ int cJSON_GetU64FromObjectValue(const cJSON *config, u64 *out) {
185185
}
186186
}
187187

188+
int cJSON_GetU32(const cJSON *obj, const char *field, u32 *out) {
189+
const cJSON *config = cJSON_GetObjectItemCaseSensitive(obj, field);
190+
if (cJSON_IsString(config) && (config->valuestring != NULL)) {
191+
char *endptr = NULL;
192+
*out = strtoul(config->valuestring, &endptr, 16);
193+
if (config->valuestring == endptr) {
194+
fprintf(stderr, "Failed to get %s (empty string)\n", field);
195+
return 0;
196+
} else if (errno == ERANGE) {
197+
fprintf(stderr, "Failed to get %s (value out of range)\n", field);
198+
return 0;
199+
} else if (errno == EINVAL) {
200+
fprintf(stderr, "Failed to get %s (not base16 string)\n", field);
201+
return 0;
202+
} else if (errno) {
203+
fprintf(stderr, "Failed to get %s (unknown error)\n", field);
204+
return 0;
205+
} else {
206+
return 1;
207+
}
208+
} else {
209+
fprintf(stderr, "Failed to get %s (field not present).\n", field);
210+
return 0;
211+
}
212+
}
213+
188214
int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) {
189215
const cJSON *capability = NULL;
190216
const cJSON *capabilities = NULL;
@@ -209,8 +235,8 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) {
209235
goto PARSE_CAPS_END;
210236
}
211237

212-
/* Parse title_id. */
213-
if (!cJSON_GetU64(npdm_json, "title_id", &kip_hdr->TitleId)) {
238+
/* Parse program_id (or deprecated title_id). */
239+
if (!cJSON_GetU64(npdm_json, "program_id", &kip_hdr->ProgramId) && !cJSON_GetU64(npdm_json, "title_id", &kip_hdr->ProgramId)) {
214240
status = 0;
215241
goto PARSE_CAPS_END;
216242
}
@@ -259,9 +285,8 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) {
259285
status = 0;
260286
goto PARSE_CAPS_END;
261287
}
262-
if (!cJSON_GetU8(npdm_json, "process_category", (u8 *)&kip_hdr->ProcessCategory)) {
263-
status = 0;
264-
goto PARSE_CAPS_END;
288+
if (!cJSON_GetU32(npdm_json, "version", &kip_hdr->Version) && !cJSON_GetU32(npdm_json, "process_category", &kip_hdr->Version)) { // optional
289+
kip_hdr->Version = 1;
265290
}
266291

267292
/* Parse capabilities. */
@@ -331,7 +356,7 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) {
331356
goto PARSE_CAPS_END;
332357
}
333358
u32 num_descriptors;
334-
u32 descriptors[6] = {0}; /* alignup(0x80/0x18); */
359+
u32 descriptors[8] = {0}; /* alignup(0xC0/0x18); */
335360
char field_name[8] = {0};
336361
const cJSON *cur_syscall = NULL;
337362
u64 syscall_value = 0;
@@ -344,14 +369,14 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) {
344369
goto PARSE_CAPS_END;
345370
}
346371

347-
if (syscall_value >= 0x80) {
348-
fprintf(stderr, "Error: All syscall entries must be numbers in [0, 0x7F]\n");
372+
if (syscall_value >= 0xC0) {
373+
fprintf(stderr, "Error: All syscall entries must be numbers in [0, 0xBF]\n");
349374
status = 0;
350375
goto PARSE_CAPS_END;
351376
}
352377
descriptors[syscall_value / 0x18] |= (1UL << (syscall_value % 0x18));
353378
}
354-
for (unsigned int i = 0; i < 6; i++) {
379+
for (unsigned int i = 0; i < 8; i++) {
355380
if (descriptors[i]) {
356381
if (cur_cap + 1 > 0x20) {
357382
fprintf(stderr, "Error: Too many capabilities!\n");
@@ -388,7 +413,8 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) {
388413
desc |= is_ro << 24;
389414
kip_hdr->Capabilities[cur_cap++] = (u32)((desc << 7) | (0x003F));
390415

391-
desc = (u32)((map_size >> 12) & 0x00FFFFFFULL);
416+
desc = (u32)((map_size >> 12) & 0x000FFFFFULL);
417+
desc |= (u32)(((map_address >> 36) & 0xFULL) << 20);
392418
is_io ^= 1;
393419
desc |= is_io << 24;
394420
kip_hdr->Capabilities[cur_cap++] = (u32)((desc << 7) | (0x003F));

src/npdmtool.c

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ typedef struct {
4545
typedef struct {
4646
u32 Magic;
4747
u8 _0x4[0xC];
48-
u64 TitleId;
48+
u64 ProgramId;
4949
u64 _0x18;
5050
u32 FahOffset;
5151
u32 FahSize;
@@ -63,8 +63,8 @@ typedef struct {
6363
u32 Size;
6464
u32 _0x208;
6565
u32 Flags;
66-
u64 TitleIdRangeMin;
67-
u64 TitleIdRangeMax;
66+
u64 ProgramIdRangeMin;
67+
u64 ProgramIdRangeMax;
6868
u32 FacOffset;
6969
u32 FacSize;
7070
u32 SacOffset;
@@ -76,15 +76,15 @@ typedef struct {
7676

7777
typedef struct {
7878
u32 Magic;
79-
u32 _0x4;
79+
u32 SignatureKeyGeneration;
8080
u32 _0x8;
8181
u8 MmuFlags;
8282
u8 _0xD;
8383
u8 MainThreadPriority;
8484
u8 DefaultCpuId;
8585
u32 _0x10;
8686
u32 SystemResourceSize;
87-
u32 ProcessCategory;
87+
u32 Version;
8888
u32 MainThreadStackSize;
8989
char Name[0x10];
9090
char ProductCode[0x10];
@@ -345,10 +345,11 @@ int CreateNpdm(const char *json, void **dst, u32 *dst_size) {
345345

346346
cJSON_GetU32(npdm_json, "system_resource_size", &header.SystemResourceSize); // optional
347347

348-
if (!cJSON_GetU8(npdm_json, "process_category", (u8 *)&header.ProcessCategory)) {
349-
status = 0;
350-
goto NPDM_BUILD_END;
348+
/* Get version (deprecated name "process_category"). */
349+
if (!cJSON_GetU32(npdm_json, "version", &header.Version) && !cJSON_GetU32(npdm_json, "process_category", &header.Version)) { // optional
350+
header.Version = 0;
351351
}
352+
352353
if (!cJSON_GetU8(npdm_json, "address_space_type", (u8 *)&header.MmuFlags)) {
353354
status = 0;
354355
goto NPDM_BUILD_END;
@@ -367,6 +368,13 @@ int CreateNpdm(const char *json, void **dst, u32 *dst_size) {
367368
header.MmuFlags |= ((disable_device_address_space_merge & 1) << 5);
368369
}
369370

371+
u8 signature_key_generation; // optional
372+
if (cJSON_GetU8(npdm_json, "signature_key_generation", &signature_key_generation)) {
373+
header.SignatureKeyGeneration = signature_key_generation;
374+
} else {
375+
header.SignatureKeyGeneration = 0;
376+
}
377+
370378
/* ACID. */
371379
memset(acid->Signature, 0, sizeof(acid->Signature));
372380
memset(acid->Modulus, 0, sizeof(acid->Modulus));
@@ -384,19 +392,19 @@ int CreateNpdm(const char *json, void **dst, u32 *dst_size) {
384392
}
385393
acid->Flags |= (pool_partition & 3) << 2;
386394

387-
if (!cJSON_GetU64(npdm_json, "title_id_range_min", &acid->TitleIdRangeMin)) {
395+
if (!cJSON_GetU64(npdm_json, "program_id_range_min", &acid->ProgramIdRangeMin) && !cJSON_GetU64(npdm_json, "title_id_range_min", &acid->ProgramIdRangeMin)) {
388396
status = 0;
389397
goto NPDM_BUILD_END;
390398
}
391-
if (!cJSON_GetU64(npdm_json, "title_id_range_max", &acid->TitleIdRangeMax)) {
399+
if (!cJSON_GetU64(npdm_json, "program_id_range_max", &acid->ProgramIdRangeMax) && !cJSON_GetU64(npdm_json, "title_id_range_max", &acid->ProgramIdRangeMax)) {
392400
status = 0;
393401
goto NPDM_BUILD_END;
394402
}
395403

396404
/* ACI0. */
397405
aci0->Magic = MAGIC_ACI0; /* "ACI0" */
398-
/* Parse title_id. */
399-
if (!cJSON_GetU64(npdm_json, "title_id", &aci0->TitleId)) {
406+
/* Parse program_id (or deprecated title_id). */
407+
if (!cJSON_GetU64(npdm_json, "program_id", &aci0->ProgramId) && !cJSON_GetU64(npdm_json, "title_id", &aci0->ProgramId)) {
400408
status = 0;
401409
goto NPDM_BUILD_END;
402410
}
@@ -633,7 +641,7 @@ int CreateNpdm(const char *json, void **dst, u32 *dst_size) {
633641
goto NPDM_BUILD_END;
634642
}
635643
u32 num_descriptors;
636-
u32 descriptors[6] = {0}; /* alignup(0x80/0x18); */
644+
u32 descriptors[8] = {0}; /* alignup(0xC0/0x18); */
637645
char field_name[8] = {0};
638646
const cJSON *cur_syscall = NULL;
639647
u64 syscall_value = 0;
@@ -646,14 +654,14 @@ int CreateNpdm(const char *json, void **dst, u32 *dst_size) {
646654
goto NPDM_BUILD_END;
647655
}
648656

649-
if (syscall_value >= 0x80) {
650-
fprintf(stderr, "Error: All syscall entries must be numbers in [0, 0x7F]\n");
657+
if (syscall_value >= 0xC0) {
658+
fprintf(stderr, "Error: All syscall entries must be numbers in [0, 0xBF]\n");
651659
status = 0;
652660
goto NPDM_BUILD_END;
653661
}
654662
descriptors[syscall_value / 0x18] |= (1UL << (syscall_value % 0x18));
655663
}
656-
for (unsigned int i = 0; i < 6; i++) {
664+
for (unsigned int i = 0; i < 8; i++) {
657665
if (descriptors[i]) {
658666
desc = descriptors[i] | (i << 24);
659667
caps[cur_cap++] = (u32)((desc << 5) | (0x000F));
@@ -680,7 +688,8 @@ int CreateNpdm(const char *json, void **dst, u32 *dst_size) {
680688
desc |= is_ro << 24;
681689
caps[cur_cap++] = (u32)((desc << 7) | (0x003F));
682690

683-
desc = (u32)((map_size >> 12) & 0x00FFFFFFULL);
691+
desc = (u32)((map_size >> 12) & 0x000FFFFFULL);
692+
desc |= (u32)(((map_address >> 36) & 0xFULL) << 20);
684693
is_io ^= 1;
685694
desc |= is_io << 24;
686695
caps[cur_cap++] = (u32)((desc << 7) | (0x003F));

0 commit comments

Comments
 (0)