mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-12-22 18:32:05 +00:00
Write all the needed pk1 code and a large part of the nxboot function
This commit is contained in:
parent
17f4a17d17
commit
c2b22a6bf2
6 changed files with 192 additions and 24 deletions
|
@ -37,4 +37,6 @@ void cluster_enable_cpu0(u64 entry, u32 ns_disable);
|
|||
|
||||
void mc_enable_ahb_redirect();
|
||||
|
||||
int tsec_query(u32 carveout, u8 *dst, u32 rev);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "se.h"
|
||||
#include "exocfg.h"
|
||||
#include "fuse.h"
|
||||
#include "hwinit.h"
|
||||
|
||||
static const uint8_t keyblob_seeds[MASTERKEY_REVISION_MAX][0x10] = {
|
||||
{0xDF, 0x20, 0x6F, 0x59, 0x44, 0x54, 0xEF, 0xDC, 0x70, 0x74, 0x48, 0x3B, 0x0D, 0xED, 0x9F, 0xD3}, /* Keyblob seed 00. */
|
||||
|
@ -32,21 +33,26 @@ static const uint8_t masterkey_4x_seed[0x10] = {
|
|||
0x2D, 0xC1, 0xF4, 0x8D, 0xF3, 0x5B, 0x69, 0x33, 0x42, 0x10, 0xAC, 0x65, 0xDA, 0x90, 0x46, 0x66
|
||||
};
|
||||
|
||||
static void get_tsec_key(void *dst, const void *tsec_fw, size_t tsec_fw_size) {
|
||||
/* TODO: Implement this method. Attempt to read TSEC fw from NAND, or from SD if that fails. */
|
||||
static int get_tsec_key(void *dst, const void *tsec_fw, size_t tsec_fw_size, uint32_t revision) {
|
||||
static uint8_t __attribute__((aligned(256))) tsec_dma_buf[0xF00];
|
||||
memcpy(tsec_dma_buf, tsec_fw, tsec_fw_size);
|
||||
return tsec_query((u32)tsec_dma_buf, dst, revision);
|
||||
}
|
||||
|
||||
void get_keyblob(nx_keyblob_t *dst, uint32_t revision, const nx_keyblob_t *keyblobs, uint32_t available_revision) {
|
||||
static int get_keyblob(nx_keyblob_t *dst, uint32_t revision, const nx_keyblob_t *keyblobs, uint32_t available_revision) {
|
||||
if (revision >= 0x20) {
|
||||
return -1;
|
||||
generic_panic();
|
||||
}
|
||||
|
||||
if (keyblobs != NULL) {
|
||||
*dst = keyblobs[revision];
|
||||
} else {
|
||||
generic_panic();
|
||||
return -1;
|
||||
/* TODO: what should we do? */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool safe_memcmp(uint8_t *a, uint8_t *b, size_t sz) {
|
||||
|
@ -58,7 +64,7 @@ static bool safe_memcmp(uint8_t *a, uint8_t *b, size_t sz) {
|
|||
}
|
||||
|
||||
/* Derive all Switch keys. */
|
||||
void derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, uint32_t available_revision, const void *tsec_fw, size_t tsec_fw_size) {
|
||||
int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, uint32_t available_revision, const void *tsec_fw, size_t tsec_fw_size) {
|
||||
uint8_t work_buffer[0x10];
|
||||
nx_keyblob_t keyblob;
|
||||
|
||||
|
@ -67,12 +73,16 @@ void derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, u
|
|||
set_aes_keyslot_flags(0xD, 0x15);
|
||||
|
||||
/* Set TSEC key. */
|
||||
get_tsec_key(work_buffer, tsec_fw, tsec_fw_size);
|
||||
if (get_tsec_key(work_buffer, tsec_fw, tsec_fw_size, target_firmware) != 0) {
|
||||
return -1;
|
||||
}
|
||||
set_aes_keyslot(0xD, work_buffer, 0x10);
|
||||
|
||||
/* Get keyblob, always try to set up the highest possible master key. */
|
||||
/* TODO: Should we iterate, trying lower keys on failure? */
|
||||
get_keyblob(&keyblob, MASTERKEY_REVISION_500_CURRENT, keyblobs, available_revision);
|
||||
if (get_keyblob(&keyblob, MASTERKEY_REVISION_500_CURRENT, keyblobs, available_revision) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Derive both keyblob key 1, and keyblob key latest. */
|
||||
se_aes_ecb_decrypt_block(0xD, work_buffer, 0x10, keyblob_seeds[MASTERKEY_REVISION_100_230], 0x10);
|
||||
|
@ -88,7 +98,7 @@ void derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, u
|
|||
/* Validate keyblob. */
|
||||
se_compute_aes_128_cmac(0xB, work_buffer, 0x10, keyblob.mac + sizeof(keyblob.mac), sizeof(keyblob) - sizeof(keyblob.mac));
|
||||
if (safe_memcmp(keyblob.mac, work_buffer, 0x10)) {
|
||||
generic_panic();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Decrypt keyblob. */
|
||||
|
@ -122,11 +132,12 @@ void derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, u
|
|||
decrypt_data_into_keyslot(0xC, 0xC, masterkey_seed, 0x10);
|
||||
break;
|
||||
default:
|
||||
generic_panic();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Setup master key revision, derive older master keys for use. */
|
||||
mkey_detect_revision();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sets final keyslot flags, for handover to TZ/Exosphere. Setting these will prevent the BPMP from using the device key or master key. */
|
||||
|
|
|
@ -31,7 +31,7 @@ typedef struct nx_keyblob_t {
|
|||
};
|
||||
} nx_keyblob_t;
|
||||
|
||||
void derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, uint32_t available_revision, const void *tsec_fw, size_t tsec_fw_size);
|
||||
int derive_nx_keydata(uint32_t target_firmware, const nx_keyblob_t *keyblobs, uint32_t available_revision, const void *tsec_fw, size_t tsec_fw_size);
|
||||
void finalize_nx_keydata(uint32_t target_firmware);
|
||||
|
||||
void derive_bis_key(void *dst, BisPartition partition_id, uint32_t target_firmware);
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
#include "utils.h"
|
||||
#include "nxboot.h"
|
||||
#include "key_derivation.h"
|
||||
|
@ -46,28 +49,95 @@ void nxboot_configure_exosphere(void) {
|
|||
}
|
||||
|
||||
/* This is the main function responsible for booting Horizon. */
|
||||
static nx_keyblob_t __attribute__((aligned(16))) g_keyblobs[32];
|
||||
void nxboot_main(void) {
|
||||
loader_ctx_t *loader_ctx = get_loader_ctx();
|
||||
package2_header_t *package2_src = (package2_header_t *)loader_ctx->package2_loadfile.load_address;
|
||||
package2_header_t *package2 = NULL;
|
||||
void *tsec_fw = (void *)loader_ctx->tsecfw_loadfile.load_address;
|
||||
size_t tsec_fw_size = loader_ctx->tsecfw_loadfile.load_size;
|
||||
void *warmboot_fw = (void *)loader_ctx->warmboot_loadfile.load_address;
|
||||
size_t warmboot_fw_size = loader_ctx->warmboot_loadfile.load_size;
|
||||
void *package1loader = NULL;
|
||||
size_t package1loader_size = 0;
|
||||
package1_header_t *package1 = NULL;
|
||||
size_t package1_size = 0;
|
||||
uint32_t revision = EXOSPHERE_TARGET_FIRMWARE_MAX;
|
||||
FILE *boot0 = fopen("boot0:/", "rb");
|
||||
|
||||
if (boot0 == NULL || package1_read_and_parse_boot0(&package1loader, &package1loader_size, g_keyblobs, &revision, boot0) == -1) {
|
||||
printf("Error: Couldn't parse boot0: %s!\n", strerror(errno));
|
||||
generic_panic();
|
||||
}
|
||||
fclose(boot0);
|
||||
|
||||
if (tsec_fw == NULL) {
|
||||
tsec_fw_size = package1_get_tsec_fw(&tsec_fw, package1loader, package1loader_size);
|
||||
if (tsec_fw_size == 0) {
|
||||
printf("Error: Failed to read the TSEC firmware from Package1loader!\n");
|
||||
generic_panic();
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: Validate that we're capable of booting. */
|
||||
|
||||
/* TODO: Initialize Boot Reason. */
|
||||
|
||||
/* TODO: How should we deal with bootconfig? */
|
||||
|
||||
/* Setup boot configuration for Exosphere. */
|
||||
nxboot_configure_exosphere();
|
||||
|
||||
/* Derive keydata. */
|
||||
//derive_nx_keydata(MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware);
|
||||
if (derive_nx_keydata(MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware, g_keyblobs, revision, tsec_fw, tsec_fw_size) != 0) {
|
||||
printf("Error: Key derivation failed!\n");
|
||||
generic_panic();
|
||||
}
|
||||
|
||||
if (loader_ctx->package2_loadfile.load_address == 0) {
|
||||
if (warmboot_fw == NULL) {
|
||||
uint8_t ctr[16];
|
||||
package1_size = package1_get_encrypted_package1(&package1, ctr, package1loader, package1loader_size);
|
||||
if(package1_decrypt(package1, package1_size, ctr)) {
|
||||
warmboot_fw = package1_get_warmboot_fw(package1);
|
||||
warmboot_fw_size = package1->warmboot_size;
|
||||
} else {
|
||||
warmboot_fw = NULL;
|
||||
warmboot_fw_size = 0;
|
||||
}
|
||||
|
||||
/* TODO: read package2 somewhere. */
|
||||
if (warmboot_fw_size == 0) {
|
||||
printf("Error: Failed to read the warmboot firmware from Package1!\n");
|
||||
generic_panic();
|
||||
}
|
||||
}
|
||||
|
||||
free(package1loader);
|
||||
|
||||
package2 = memalign(16, PACKAGE2_SIZE_MAX);
|
||||
if (package2 == NULL) {
|
||||
printf("Error: nxboot: out of memory!\n");
|
||||
generic_panic();
|
||||
}
|
||||
|
||||
if (package2_src == NULL) {
|
||||
/* TODO: How should we deal with bootconfig? */
|
||||
FILE *bcpkg21 = fopen("bcpkg21:/", "rb");
|
||||
if (bcpkg21 == NULL) {
|
||||
printf("Error: Failed to read Package2 from NAND!\n");
|
||||
generic_panic();
|
||||
}
|
||||
if (fseek(bcpkg21, 0x4000, SEEK_SET) != 0 || fread(package2, 1, PACKAGE2_SIZE_MAX, bcpkg21) < sizeof(package2_header_t)) {
|
||||
printf("Error: Failed to read Package2 from NAND!\n");
|
||||
fclose(bcpkg21);
|
||||
generic_panic();
|
||||
}
|
||||
fclose(bcpkg21);
|
||||
} else {
|
||||
memcpy(package2, package2_src, loader_ctx->package2_loadfile.load_size);
|
||||
}
|
||||
|
||||
/* Patch package2, adding thermosphere + custom KIPs. */
|
||||
package2_rebuild_and_copy((void *)loader_ctx->package2_loadfile.load_address);
|
||||
package2_rebuild_and_copy(package2);
|
||||
|
||||
free(package2);
|
||||
|
||||
/* Boot up Exosphere. */
|
||||
MAILBOX_NX_BOOTLOADER_IS_SECMON_AWAKE = 0;
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include "package1.h"
|
||||
#include "bct.h"
|
||||
#include "se.h"
|
||||
|
||||
int package1_parse_boot0(void **package1, size_t *package1_size, nx_keyblob_t *keyblobs, uint32_t *revision, FILE *boot0) {
|
||||
int package1_read_and_parse_boot0(void **package1loader, size_t *package1loader_size, nx_keyblob_t *keyblobs, uint32_t *revision, FILE *boot0) {
|
||||
static nvboot_config_table bct = {0}; /* Normal firmware BCT, primary. TODO: check? */
|
||||
nv_bootloader_info *pk1_info = &bct.bootloader[0]; /* TODO: check? */
|
||||
|
||||
size_t fpos, pk1_offset;
|
||||
|
||||
if (package1 == NULL || package1_size != NULL || keyblobs == NULL || revision == NULL || boot0 == NULL) {
|
||||
if (package1loader == NULL || package1loader_size != NULL || keyblobs == NULL || revision == NULL || boot0 == NULL) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
@ -26,13 +29,13 @@ int package1_parse_boot0(void **package1, size_t *package1_size, nx_keyblob_t *k
|
|||
}
|
||||
|
||||
*revision = pk1_info->attribute;
|
||||
*package1_size = pk1_info->length;
|
||||
*package1loader_size = pk1_info->length;
|
||||
|
||||
pk1_offset = 0x4000 * pk1_info->start_blk + 0x200 * pk1_info->start_page;
|
||||
|
||||
(*package1) = malloc(*package1_size);
|
||||
(*package1loader) = memalign(16, *package1loader_size);
|
||||
|
||||
if (*package1 == NULL) {
|
||||
if (*package1loader == NULL) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
@ -41,12 +44,12 @@ int package1_parse_boot0(void **package1, size_t *package1_size, nx_keyblob_t *k
|
|||
if (fseek(boot0, fpos + pk1_offset, SEEK_SET) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (fread(*package1, *package1_size, 1, boot0) == 0) {
|
||||
if (fread(*package1loader, *package1loader_size, 1, boot0) == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Skip the backup pk1/pk1l. */
|
||||
if (fseek(boot0, *package1_size, SEEK_CUR) != 0) {
|
||||
if (fseek(boot0, *package1loader_size, SEEK_CUR) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -62,3 +65,66 @@ int package1_parse_boot0(void **package1, size_t *package1_size, nx_keyblob_t *k
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t package1_get_tsec_fw(void **tsec_fw, const void *package1loader, size_t package1loader_size) {
|
||||
/* The TSEC firmware is always located at a 256-byte aligned address. */
|
||||
/* We're looking for its 4 first bytes. We assume its size is always 0xF00 bytes. */
|
||||
const uint32_t *pos;
|
||||
uintptr_t pk1l = (uintptr_t)package1loader;
|
||||
for (pos = (const uint32_t *)pk1l; (uintptr_t)pos < pk1l + package1loader_size && *pos != 0xCF42004D; pos += 0x40);
|
||||
|
||||
(*tsec_fw) = (void *)pos;
|
||||
return 0xF00;
|
||||
}
|
||||
|
||||
size_t package1_get_encrypted_package1(package1_header_t **package1, uint8_t *ctr, const void *package1loader, size_t package1loader_size) {
|
||||
const uint32_t *pos;
|
||||
uintptr_t pk1l = (uintptr_t)package1loader;
|
||||
|
||||
if (package1loader_size < 0x4000) {
|
||||
return 0; /* Shouldn't happen, ever. */
|
||||
}
|
||||
|
||||
for (pos = (const uint32_t *)pk1l; (uintptr_t)pos < pk1l + 0x3FF8 && (pos[0] != 0x70012000 || pos[2] != 0x40007000); pos++);
|
||||
pos = (const uint32_t *)(pk1l + pos[1] - 0x40010000);
|
||||
|
||||
memcpy(ctr, pos + 4, 0x10);
|
||||
(*package1) = (package1_header_t *)(pos + 8);
|
||||
return *pos;
|
||||
}
|
||||
|
||||
bool package1_decrypt(package1_header_t *package1, size_t package1_size, const uint8_t *ctr) {
|
||||
uint8_t __attribute__((aligned(16))) ctrbuf[16];
|
||||
memcpy(ctrbuf, ctr, 16);
|
||||
se_aes_ctr_crypt(0xB, package1, package1_size, package1, package1_size, ctr, 16);
|
||||
return memcmp(package1->magic, "PK11", 4) == 0;
|
||||
}
|
||||
|
||||
void *package1_get_warmboot_fw(const package1_header_t *package1) {
|
||||
/*
|
||||
The layout of pk1 changes between versions.
|
||||
|
||||
However, the secmon always starts by this erratum code:
|
||||
https://github.com/ARM-software/arm-trusted-firmware/blob/master/plat/nvidia/tegra/common/aarch64/tegra_helpers.S#L312
|
||||
and thus by 0xD5034FDF.
|
||||
|
||||
Nx-bootloader seems to always start by 0xE328F0C0 (msr cpsr_f, 0xc0).
|
||||
*/
|
||||
const uint32_t *data = (const uint32_t *)package1->data;
|
||||
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
switch (*data) {
|
||||
case 0xD5034FDFu:
|
||||
data += package1->secmon_size / 4;
|
||||
break;
|
||||
case 0xE328F0C0:
|
||||
data += package1->nx_bootloader_size / 4;
|
||||
break;
|
||||
default:
|
||||
/* TODO: should we validate its signature? */
|
||||
return (void *)data;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,25 @@
|
|||
#include <stdio.h>
|
||||
#include "key_derivation.h"
|
||||
|
||||
int package1_parse_boot0(void **package1, size_t *package1_size, nx_keyblob_t *keyblobs, uint32_t *revision, FILE *boot0);
|
||||
typedef struct package1_header_t {
|
||||
char magic[4];
|
||||
uint32_t warmboot_size;
|
||||
uint32_t _0x8;
|
||||
uint32_t _0xC;
|
||||
uint32_t nx_bootloader_size;
|
||||
uint32_t _0x14;
|
||||
uint32_t secmon_size;
|
||||
uint32_t _0x1C;
|
||||
uint8_t data[];
|
||||
} package1_header_t;
|
||||
|
||||
int package1_read_and_parse_boot0(void **package1loader, size_t *package1loader_size, nx_keyblob_t *keyblobs, uint32_t *revision, FILE *boot0);
|
||||
|
||||
size_t package1_get_tsec_fw(void **tsec_fw, const void *package1loader, size_t package1loader_size);
|
||||
size_t package1_get_encrypted_package1(package1_header_t **package1, uint8_t *ctr, const void *package1loader, size_t package1loader_size);
|
||||
|
||||
/* Must be aligned to 16 bytes. */
|
||||
bool package1_decrypt(package1_header_t *package1, size_t package1_size, const uint8_t *ctr);
|
||||
void *package1_get_warmboot_fw(const package1_header_t *package1);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue