diff --git a/configure.ac b/configure.ac index 2d55feb..56991b2 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.61) -AC_INIT([switch-tools],[1.6.0],[https://github.com/switchbrew/switch-tools/issues]) +AC_INIT([switch-tools],[1.7.0],[https://github.com/switchbrew/switch-tools/issues]) AC_CONFIG_SRCDIR([src/build_pfs0.c]) AM_INIT_AUTOMAKE([subdir-objects]) diff --git a/src/elf2kip.c b/src/elf2kip.c index 4f07b33..0b0d607 100644 --- a/src/elf2kip.c +++ b/src/elf2kip.c @@ -31,7 +31,7 @@ typedef struct { u8 Unk; u8 Flags; KipSegment Segments[6]; - u32 Capabilities[0x20]; + u32 Capabilities[0x20]; } KipHeader; uint8_t* ReadEntireFile(const char* fn, size_t* len_out) { @@ -132,10 +132,11 @@ int cJSON_GetBooleanOptional(const cJSON *obj, const char *field, int *out) { fprintf(stderr, "Unknown boolean value in %s.\n", field); return 0; } - } else { + return 1; + } else { *out = 0; + return 0; } - return 1; } int cJSON_GetU64(const cJSON *obj, const char *field, u64 *out) { @@ -202,7 +203,7 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) { status = 0; goto PARSE_CAPS_END; } - + /* Parse name. */ const cJSON *title_name = cJSON_GetObjectItemCaseSensitive(npdm_json, "name"); if (cJSON_IsString(title_name) && (title_name->valuestring != NULL)) { @@ -212,13 +213,24 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) { status = 0; goto PARSE_CAPS_END; } - + /* Parse title_id. */ if (!cJSON_GetU64(npdm_json, "title_id", &kip_hdr->TitleId)) { status = 0; goto PARSE_CAPS_END; } - + + /* Parse use secure memory. */ + /* This field is optional, and defaults to true (set before this function is called). */ + int use_secure_memory = 1; + if (cJSON_GetBooleanOptional(npdm_json, "use_secure_memory", &use_secure_memory)) { + if (use_secure_memory) { + kip_hdr->Flags |= 0x20; + } else { + kip_hdr->Flags &= ~0x20; + } + } + /* Parse main_thread_stack_size. */ u64 stack_size = 0; if (!cJSON_GetU64(npdm_json, "main_thread_stack_size", &stack_size)) { @@ -231,7 +243,7 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) { goto PARSE_CAPS_END; } kip_hdr->Segments[1].Attribute = (u32)(stack_size & 0xFFFFFFFF); - + /* Parse various config. */ if (!cJSON_GetU8(npdm_json, "main_thread_priority", &kip_hdr->MainThreadPriority)) { status = 0; @@ -319,13 +331,13 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) { u64 syscall_value = 0; cJSON_ArrayForEach(cur_syscall, value) { if (cJSON_IsNumber(cur_syscall)) { - syscall_value = (u64)cur_syscall->valueint; + syscall_value = (u64)cur_syscall->valueint; } else if (!cJSON_IsString(cur_syscall) || !cJSON_GetU64(value, cur_syscall->string, &syscall_value)) { fprintf(stderr, "Error: Syscall entries must be integers or hex strings.\n"); status = 0; goto PARSE_CAPS_END; } - + if (syscall_value >= 0x80) { fprintf(stderr, "Error: All syscall entries must be numbers in [0, 0x7F]\n"); status = 0; @@ -369,7 +381,7 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) { desc = (u32)((map_address >> 12) & 0x00FFFFFFULL); desc |= is_ro << 24; kip_hdr->Capabilities[cur_cap++] = (u32)((desc << 7) | (0x003F)); - + desc = (u32)((map_size >> 12) & 0x00FFFFFFULL); is_io ^= 1; desc |= is_io << 24; @@ -433,7 +445,7 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) { } u64 kern_ver = 0; if (cJSON_IsNumber(value)) { - kern_ver = (u64)value->valueint; + kern_ver = (u64)value->valueint; } else if (!cJSON_IsString(value) || !cJSON_GetU64FromObjectValue(value, &kern_ver)) { fprintf(stderr, "Error: Kernel version must be integer or hex strings.\n"); status = 0; @@ -481,11 +493,11 @@ int ParseKipConfiguration(const char *json, KipHeader *kip_hdr) { goto PARSE_CAPS_END; } } - + for (u32 i = cur_cap; i < 0x20; i++) { kip_hdr->Capabilities[i] = 0xFFFFFFFF; } - + status = 1; PARSE_CAPS_END: cJSON_Delete(npdm_json); @@ -506,14 +518,14 @@ int main(int argc, char* argv[]) { fprintf(stderr, "Bad compile environment!\n"); return EXIT_FAILURE; } - + size_t json_len; uint8_t* json = ReadEntireFile(argv[2], &json_len); if (json == NULL) { fprintf(stderr, "Failed to read descriptor json!\n"); return EXIT_FAILURE; } - + if (!ParseKipConfiguration(json, &kip_hdr)) { fprintf(stderr, "Failed to parse kip configuration!\n"); return EXIT_FAILURE; @@ -569,17 +581,17 @@ int main(int argc, char* argv[]) { fprintf(stderr, "Invalid ELF: expected 3 loadable phdrs and a bss!\n"); return EXIT_FAILURE; } - - + + kip_hdr.Segments[i].DstOff = dst_off; - + // .bss is special if (i == 3) { tmpsize = (phdr->p_filesz + 0xFFF) & ~0xFFF; if ( phdr->p_memsz > tmpsize) { kip_hdr.Segments[i].DecompSz = ((phdr->p_memsz - tmpsize) + 0xFFF) & ~0xFFF; } else { - kip_hdr.Segments[i].DecompSz = 0; + kip_hdr.Segments[i].DecompSz = 0; } kip_hdr.Segments[i].CompSz = 0; break; @@ -592,13 +604,13 @@ int main(int argc, char* argv[]) { if (buf[i] == NULL) { fprintf(stderr, "Out of memory!\n"); return EXIT_FAILURE; - } + } memset(buf[i], 0, kip_hdr.Segments[i].DecompSz); - + memcpy(buf[i], &elf[phdr->p_offset], phdr->p_filesz); cmp[i] = BLZ_Code(buf[i], phdr->p_filesz, &kip_hdr.Segments[i].CompSz, BLZ_BEST); - + file_off += kip_hdr.Segments[i].CompSz; dst_off += kip_hdr.Segments[i].DecompSz; dst_off = (dst_off + 0xFFF) & ~0xFFF; @@ -610,7 +622,7 @@ int main(int argc, char* argv[]) { fprintf(stderr, "Failed to open output file!\n"); return EXIT_FAILURE; } - + // TODO check retvals for (i=0; i<3; i++) diff --git a/src/npdmtool.c b/src/npdmtool.c index 866ccec..46fa231 100644 --- a/src/npdmtool.c +++ b/src/npdmtool.c @@ -19,9 +19,15 @@ typedef uint8_t u8; /* FAC, FAH need to be tightly packed. */ #pragma pack(push, 1) typedef struct { - u32 Version; + u8 Version; + u8 CoiCount; + u8 SdoiCount; + u8 pad; u64 Perms; - u8 _0xC[0x20]; + u64 CoiMin; + u64 CoiMax; + u64 SdoiMin; + u64 SdoiMax; } FilesystemAccessControl; #pragma pack(pop) @@ -29,10 +35,10 @@ typedef struct { typedef struct { u32 Version; u64 Perms; - u32 _0xC; - u32 _0x10; - u32 _0x14; - u32 _0x18; + u32 CoiOffset; + u32 CoiSize; + u32 SdoiOffset; + u32 SdoiSize; } FilesystemAccessHeader; #pragma pack(pop) @@ -76,7 +82,8 @@ typedef struct { u8 _0xD; u8 MainThreadPriority; u8 DefaultCpuId; - u64 _0x10; + u32 _0x10; + u32 SystemResourceSize; u32 ProcessCategory; u32 MainThreadStackSize; char Name[0x10]; @@ -219,6 +226,32 @@ int cJSON_GetU64(const cJSON *obj, const char *field, 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 cJSON_GetU64FromObjectValue(const cJSON *config, u64 *out) { if (cJSON_IsString(config) && (config->valuestring != NULL)) { char *endptr = NULL; @@ -257,6 +290,10 @@ int CreateNpdm(const char *json, void **dst, u32 *dst_size) { const cJSON *service = NULL; const cJSON *services = NULL; const cJSON *fsaccess = NULL; + const cJSON *cois = NULL; + const cJSON *coi = NULL; + const cJSON *sdois = NULL; + const cJSON *sdoi = NULL; int status = 0; cJSON *npdm_json = cJSON_Parse(json); @@ -305,6 +342,9 @@ int CreateNpdm(const char *json, void **dst, u32 *dst_size) { status = 0; goto NPDM_BUILD_END; } + + 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; @@ -370,20 +410,88 @@ int CreateNpdm(const char *json, void **dst, u32 *dst_size) { status = 0; goto NPDM_BUILD_END; } + + cJSON_GetU64(fsaccess, "content_owner_id_min", &fac->CoiMin); + cJSON_GetU64(fsaccess, "content_owner_id_max", &fac->CoiMax); + + cois = cJSON_GetObjectItemCaseSensitive(fsaccess, "content_owner_ids"); + if (cJSON_IsArray(cois)) { + int idx = 0; + u64 *content_owner_id = (u64 *)((u8 *)fac + sizeof(FilesystemAccessControl)); + cJSON_ArrayForEach(coi, cois) { + if (!cJSON_GetU64FromObjectValue(coi, content_owner_id)) { + status = 0; + goto NPDM_BUILD_END; + } + ++content_owner_id; + ++idx; + } + fac->CoiCount = idx; + } + + cJSON_GetU64(fsaccess, "save_data_owner_id_min", &fac->SdoiMin); + cJSON_GetU64(fsaccess, "save_data_owner_id_max", &fac->SdoiMax); + + sdois = cJSON_GetObjectItemCaseSensitive(fsaccess, "save_data_owner_ids"); + if (cJSON_IsArray(sdois)) { + int idx = 0; + u64 *save_data_owner_id = (u64 *)((u8 *)fac + sizeof(FilesystemAccessControl) + fac->CoiCount * sizeof(u64)); + cJSON_ArrayForEach(sdoi, sdois) { + if (!cJSON_GetU64FromObjectValue(sdoi, save_data_owner_id)) { + status = 0; + goto NPDM_BUILD_END; + } + ++save_data_owner_id; + ++idx; + } + fac->SdoiCount = idx; + } + acid->FacOffset = sizeof(NpdmAcid); - acid->FacSize = sizeof(FilesystemAccessControl); + acid->FacSize = sizeof(FilesystemAccessControl) + fac->CoiCount * sizeof(u64) + fac->SdoiCount * sizeof(u64); acid->SacOffset = (acid->FacOffset + acid->FacSize + 0xF) & ~0xF; - + /* Fah. */ FilesystemAccessHeader *fah = (FilesystemAccessHeader *)((u8 *)aci0 + sizeof(NpdmAci0)); fah->Version = 1; fah->Perms = fac->Perms; - fah->_0xC = 0x1C; - fah->_0x14 = 0x1C; + fah->CoiOffset = sizeof(FilesystemAccessHeader); + fah->CoiSize = fac->CoiCount ? 4 + fac->CoiCount * sizeof(u64) : 0; + fah->SdoiOffset = fah->CoiOffset + fah->CoiSize; + fah->SdoiSize = fac->SdoiCount ? 4 + fac->SdoiCount * sizeof(u64) : 0; + + if (fac->CoiCount) { + u32 *count = (u32 *)((u8 *)fah + fah->CoiOffset); + *count = fac->CoiCount; + + u64 *id = (u64 *)((u8 *)count + sizeof(u32)); + cJSON_ArrayForEach(coi, cois) { + if (!cJSON_GetU64FromObjectValue(coi, id)) { + status = 0; + goto NPDM_BUILD_END; + } + ++id; + } + } + + if (fac->SdoiCount) { + u32 *count = (u32 *)((u8 *)fah + fah->SdoiOffset); + *count = fac->SdoiCount; + + u64 *id = (u64 *)((u8 *)count + sizeof(u32)); + cJSON_ArrayForEach(sdoi, sdois) { + if (!cJSON_GetU64FromObjectValue(sdoi, id)) { + status = 0; + goto NPDM_BUILD_END; + } + ++id; + } + } + aci0->FahOffset = sizeof(NpdmAci0); - aci0->FahSize = sizeof(FilesystemAccessHeader); + aci0->FahSize = sizeof(FilesystemAccessHeader) + fah->CoiSize + fah->SdoiSize; aci0->SacOffset = (aci0->FahOffset + aci0->FahSize + 0xF) & ~0xF; - + /* Sac. */ u8 *sac = (u8*)aci0 + aci0->SacOffset; u32 sac_size = 0;