Skip to content

Instantly share code, notes, and snippets.

@SciresM
Created April 22, 2018 23:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save SciresM/8a2799069c960ac27b56b5051e799f90 to your computer and use it in GitHub Desktop.
Save SciresM/8a2799069c960ac27b56b5051e799f90 to your computer and use it in GitHub Desktop.
int cJSON_GetU8(const cJSON *obj, const char *field, u8 *out) {
const cJSON *config = cJSON_GetObjectItemCaseSensitive(obj, field);
if (cJSON_IsNumber(config)) {
*out = (u8)config->valueint;
return 1;
} else {
fprintf(stderr, "Failed to get %s (field not present).\n", field);
return 0;
}
}
int cJSON_GetU16(const cJSON *obj, const char *field, u16 *out) {
const cJSON *config = cJSON_GetObjectItemCaseSensitive(obj, field);
if (cJSON_IsNumber(config)) {
*out = (u16)config->valueint;
return 1;
} else {
fprintf(stderr, "Failed to get %s (field not present).\n", field);
return 0;
}
}
int cJSON_GetBoolean(const cJSON *obj, const char *field, int *out) {
const cJSON *config = cJSON_GetObjectItemCaseSensitive(obj, field);
if (cJSON_IsBool(config)) {
if (cJSON_IsTrue(config)) {
*out = 1;
} else if (cJSON_IsFalse(config) {
*out = 0;
} else {
fprintf(stderr, "Unknown boolean value in %s.\n", field);
return 0;
}
return 1;
} else {
fprintf(stderr, "Failed to get %s (field not present).\n", field);
return 0;
}
}
int cJSON_GetBooleanOptional(const cJSON *obj, const char *field, int *out) {
const cJSON *config = cJSON_GetObjectItemCaseSensitive(obj, field);
if (cJSON_IsBool(config)) {
if (cJSON_IsTrue(config)) {
*out = 1;
} else if (cJSON_IsFalse(config) {
*out = 0;
} else {
fprintf(stderr, "Unknown boolean value in %s.\n", field);
return 0;
}
} else {
*out = 0;
}
return 1;
}
int cJSON_GetU64(const cJSON *obj, const char *field, u64 *out) {
const cJSON *config = cJSON_GetObjectItemCaseSensitive(obj, field);
if (cJSON_IsString(title_id) && (title_id->valuestring != NULL)) {
char *endptr = NULL;
*out = strtoul(out, endptr, 16);
if (title_id->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;
int status = 0;
cJSON *npdm_json = cJSON_Parse(json);
if (npdm_json == NULL) {
const char *error_ptr = cJSON_GetErrorPtr();
if (error_ptr != NULL) {
fprintf(stderr, "JSON Parse Error: %s\n", error_ptr);
}
status = 0;
goto PARSE_CAPS_END;
}
/* Parse name. */
title_name = cJSON_GetObjectItemCaseSensitive(npdm_json, "name");
if (cJSON_IsString(title_name) && (title_name->valuestring != NULL)) {
strncpy(kip_hdr->Name, title_name->valuestring, sizeof(kip_hdr->Name));
} else {
fprintf(stderr, "Failed to get title name (name field not present).\n");
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 various config. */
if (!cJSON_GetU8(npdm_json, "main_thread_priority", &kip_hdr->MainThreadPriority)) {
status = 0;
goto PARSE_CAPS_END;
}
if (!cJSON_GetU8(npdm_json, "default_cpu_id", &kip_hdr->DefaultCpuId)) {
status = 0;
goto PARSE_CAPS_END;
}
if (!cJSON_GetU8(npdm_json, "process_category", (u8 *)&kip_hdr->ProcessCategory)) {
status = 0;
goto PARSE_CAPS_END;
}
/* Parse capabilities. */
capabilities = cJSON_GetObjectItemCaseSensitive(npdm_json, "kernel_capabilities");
if (!cJSON_IsArray(capabilities)) {
fprintf(stderr, "Kernel Capabilities must be an array!\n");
status = 0;
goto PARSE_CAPS_END;
}
u32 cur_cap = 0;
u32 desc;
cJSON_ArrayForEach(capability, capabilities) {
desc = 0;
if (!cJSON_IsObject(capability)) {
fprintf(stderr, "Kernel Capabilities must all be objects!\n");
status = 0;
goto PARSE_CAPS_END;
}
const cJSON *type = cJSON_GetObjectItemCaseSensitive(capability, "type");
char *type_str;
if (cJSON_IsString(type_str) && (type_str->valuestring != NULL)) {
type_str = type->valuestring;
} else {
fprintf(stderr, "Failed to get capability type (field not present).\n");
status = 0;
goto PARSE_CAPS_END;
}
const cJSON *value = cJSON_GetObjectItemCaseSensitive(capability, "value");
if (strcmp(type_str, "kernel_flags")) {
if (cur_cap + 1 > 0x20) {
fprintf(stderr, "Error: Too many capabilities!\n");
status = 0;
goto PARSE_CAPS_END;
}
if (!cJSON_IsObject(value)) {
fprintf(stderr, "Kernel Flags Capability value must be object!\n");
status = 0;
goto PARSE_CAPS_END;
}
u8 highest_prio = 0, lowest_prio = 0, lowest_cpu = 0, highest_cpu = 0;
if (!cJSON_GetU8(value, "highest_thread_priority", &highest_prio) ||
!cJSON_GetU8(value, "lowest_thread_priority", &lowest_prio) ||
!cJSON_GetU8(value, "highest_cpu_id", &highest_cpu) ||
!cJSON_GetU8(value, "lowest_cpu_id", &lowest_cpu)) {
status = 0;
goto PARSE_CAPS_END;
}
desc = highest_cpu_id;
desc <<= 8;
desc |= lowest_cpu;
desc <<= 8;
desc |= (lowest_thread_priority & 0x3F);
desc <<= 6;
desc |= (highest_thread_priority & 0x3F);
kip_hdr->capabilities[cur_cap++] = (u32)((desc << 4) | (0x0007));
} else if (strcmp(type_str, "syscalls")) {
if (!cJSON_IsObject(value)) {
fprintf(stderr, "Syscalls Capability value must be object!\n");
status = 0;
goto PARSE_CAPS_END;
}
u32 num_descriptors;
u32 descriptors[6] = {0}; /* alignup(0x80/0x18); */
char field_name[8] = {0};
int cur;
for (unsigned int i = 0; i < 0x80; i++) {
snprintf(field_name, 8, "0x%02x", i);
if (!cJSON_GetBooleanOptional(value, field_name, &cur)) {
status = 0;
goto PARSE_CAPS_END;
}
descriptors[i / 0x18] |= (1UL << (i % 0x18));
}
for (unsigned int i = 0; i < 6; i++) {
if (descriptors[i]) {
if (cur_cap + 1 > 0x20) {
fprintf(stderr, "Error: Too many capabilities!\n");
status = 0;
goto PARSE_CAPS_END;
}
desc = descriptors[i] | (i << 24);
kip_hdr->capabilities[cur_cap++] = (u32)((desc << 5) | (0x000F));
}
}
} else if (strcmp(type_str, "map")) {
if (cur_cap + 2 > 0x20) {
fprintf(stderr, "Error: Too many capabilities!\n");
status = 0;
goto PARSE_CAPS_END;
}
if (!cJSON_IsObject(value)) {
fprintf(stderr, "Map Capability value must be object!\n");
status = 0;
goto PARSE_CAPS_END;
}
u64 map_address = 0;
u64 map_size = 0;
int is_ro;
int is_io;
if (!cJSON_GetU64(value, "address", &map_address) ||
!cJSON_GetU64(value, "size", &map_size) ||
!cJSON_GetBoolean(value, "is_ro", &is_ro) ||
!cJSON_GetBoolean(value, "is_io", &is_io)) {
status = 0;
goto PARSE_CAPS_END;
}
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;
kip_hdr->capabilities[cur_cap++] = (u32)((desc << 7) | (0x003F));
} else if (strcmp(type_str, "map_page")) {
if (cur_cap + 1 > 0x20) {
fprintf(stderr, "Error: Too many capabilities!\n");
status = 0;
goto PARSE_CAPS_END;
}
u64 page_address = 0;
if (!cJSON_GetU64(capability, "value", &page_address)) {
status = 0;
goto PARSE_CAPS_END;
}
desc = (u32)((page_address >> 12) & 0x00FFFFFFULL);
kip_hdr->capabilities[cur_cap++] = (u32)((desc << 8) | (0x007F));
} else if (strcmp(type_str, "irq_pair")) {
if (cur_cap + 1 > 0x20) {
fprintf(stderr, "Error: Too many capabilities!\n");
status = 0;
goto PARSE_CAPS_END;
}
if (!cJSON_IsArray(value) || cJSON_GetArraySize(value) != 2) {
fprintf(stderr, "Error: IRQ Pairs must have size 2 array value.\n");
status = 0;
goto PARSE_CAPS_END;
}
const cJSON *irq = NULL;
cJSON_ArrayForEach(irq, value) {
desc <<= 10;
if (cJSON_IsNull(irq)) {
desc |= 0x3FF;
} else if (cJSON_IsNumber(config)) {
desc |= ((u16)(config->valueint)) & 0x3FF;
} else {
fprintf(stderr, "Failed to parse IRQ value.\n", field);
status = 0;
goto PARSE_CAPS_END;
}
}
kip_hdr->capabilities[cur_cap++] = (u32)((desc << 12) | (0x07FF));
} else if (strcmp(type_str, "application_type")) {
if (cur_cap + 1 > 0x20) {
fprintf(stderr, "Error: Too many capabilities!\n");
status = 0;
goto PARSE_CAPS_END;
}
if (!cJSON_GetU16(capability, "value", (u8 *)&desc)) {
status = 0;
goto PARSE_CAPS_END;
}
desc &= 7;
kip_hdr->capabilities[cur_cap++] = (u32)((desc << 14) | (0x1FFF));
} else if (strcmp(type_str, "min_kernel_version")) {
if (cur_cap + 1 > 0x20) {
fprintf(stderr, "Error: Too many capabilities!\n");
status = 0;
goto PARSE_CAPS_END;
}
if (!cJSON_GetU16(capability, "value", (u16 *)&desc)) {
status = 0;
goto PARSE_CAPS_END;
}
kip_hdr->capabilities[cur_cap++] = (u32)((desc << 15) | (0x3FFF));
} else if (strcmp(type_str, "handle_table_size")) {
if (cur_cap + 1 > 0x20) {
fprintf(stderr, "Error: Too many capabilities!\n");
status = 0;
goto PARSE_CAPS_END;
}
if (!cJSON_GetU16(capability, "value", (u16 *)&desc)) {
status = 0;
goto PARSE_CAPS_END;
}
kip_hdr->capabilities[cur_cap++] = (u32)((desc << 16) | (0x7FFF));
} else if (strcmp(type_str, "debug_flags")) {
if (cur_cap + 1 > 0x20) {
fprintf(stderr, "Error: Too many capabilities!\n");
status = 0;
goto PARSE_CAPS_END;
}
if (!cJSON_IsObject(value)) {
fprintf(stderr, "Debug Flag Capability value must be object!\n");
status = 0;
goto PARSE_CAPS_END;
}
int allow_debug = 0;
int force_debug = 0;
if (!cJSON_GetBoolean(value, "allow_debug", &allow_debug)) {
status = 0;
goto PARSE_CAPS_END;
}
if (!cJSON_GetBoolean(value, "force_debug", &force_debug)) {
status = 0;
goto PARSE_CAPS_END;
}
desc = (allow_debug & 1) | ((force_debug & 1) << 1);
kip_hdr->capabilities[cur_cap++] = (u32)((desc << 17) | (0xFFFF));
}
}
PARSE_CAPS_END:
cJSON_Delete(npdm_json);
return status;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment