diff --git a/fusee/fusee-secondary/src/device_partition.c b/fusee/fusee-secondary/src/device_partition.c index afc1d3ba8..274d51929 100644 --- a/fusee/fusee-secondary/src/device_partition.c +++ b/fusee/fusee-secondary/src/device_partition.c @@ -14,7 +14,7 @@ * along with this program. If not, see . */ -#include +#include #include #include "device_partition.h" @@ -27,7 +27,7 @@ int device_partition_read_data(device_partition_t *devpart, void *dst, uint64_t return rc; } } - if (devpart->read_cipher != NULL && devpart->crypto_mode != DevicePartitionCryptoMode_None) { + if ((devpart->read_cipher != NULL) && (devpart->crypto_mode != DevicePartitionCryptoMode_None)) { for (uint64_t i = 0; i < num_sectors; i += devpart->crypto_work_buffer_num_sectors) { uint64_t n = (i + devpart->crypto_work_buffer_num_sectors > num_sectors) ? (num_sectors - i) : devpart->crypto_work_buffer_num_sectors; rc = devpart->reader(devpart, devpart->crypto_work_buffer, sector + i, n); @@ -55,7 +55,7 @@ int device_partition_write_data(device_partition_t *devpart, const void *src, ui return rc; } } - if (devpart->read_cipher != NULL && devpart->crypto_mode != DevicePartitionCryptoMode_None) { + if ((devpart->write_cipher != NULL) && (devpart->crypto_mode != DevicePartitionCryptoMode_None)) { for (uint64_t i = 0; i < num_sectors; i += devpart->crypto_work_buffer_num_sectors) { uint64_t n = (i + devpart->crypto_work_buffer_num_sectors > num_sectors) ? (num_sectors - i) : devpart->crypto_work_buffer_num_sectors; memcpy(devpart->crypto_work_buffer, src + (size_t)(devpart->sector_size * i), (size_t)(devpart->sector_size * n)); @@ -74,28 +74,146 @@ int device_partition_write_data(device_partition_t *devpart, const void *src, ui } } -int emu_device_partition_read_data(device_partition_t *devpart, void *dst, uint64_t sector, uint64_t num_sectors, const char *origin_path) +int emu_device_partition_read_data(device_partition_t *devpart, void *dst, uint64_t sector, uint64_t num_sectors, const char *origin_path, int num_parts, uint64_t part_limit) { int rc = 0; + uint64_t target_sector = 0; + char target_path[0x300 + 1] = {0}; - /* Read partition data using our backing file. */ - FILE *origin = fopen(origin_path, "rb"); - fseek(origin, sector * devpart->sector_size, SEEK_CUR); - rc = (fread(dst, devpart->sector_size, num_sectors, origin) > 0) ? 0 : -1; - fclose(origin); + /* Perform initialization steps, if necessary. */ + if (!devpart->initialized) { + rc = devpart->initializer(devpart); + if (rc != 0) { + return rc; + } + } + + /* Handle data in multiple parts, if necessary. */ + if (num_parts > 0) { + int target_part = 0; + uint64_t data_offset = sector * devpart->sector_size; + + if (data_offset >= part_limit) { + uint64_t data_offset_aligned = (data_offset + (part_limit - 1)) & ~(part_limit - 1); + target_part = (data_offset_aligned == data_offset) ? (data_offset / part_limit) : (data_offset_aligned / part_limit) - 1; + target_sector = (data_offset - (target_part * part_limit)) / devpart->sector_size; + + /* Target part is invalid. */ + if (target_part > num_parts) { + return -1; + } + } + + snprintf(target_path, sizeof(target_path) - 1, "%s%02d", origin_path, target_part); + } else { + target_sector = sector; + strcpy(target_path, origin_path); + } + + /* Read the partition data. */ + if ((devpart->read_cipher != NULL) && (devpart->crypto_mode != DevicePartitionCryptoMode_None)) { + for (uint64_t i = 0; i < num_sectors; i += devpart->crypto_work_buffer_num_sectors) { + uint64_t n = (i + devpart->crypto_work_buffer_num_sectors > num_sectors) ? (num_sectors - i) : devpart->crypto_work_buffer_num_sectors; + + /* Read partition data using our backing file. */ + FILE *origin = fopen(target_path, "rb"); + fseek(origin, (target_sector + i) * devpart->sector_size, SEEK_CUR); + rc = (fread(dst, devpart->sector_size, n, origin) > 0) ? 0 : -1; + fclose(origin); + + if (rc != 0) { + return rc; + } + + /* Decrypt partition data. */ + rc = devpart->read_cipher(devpart, target_sector + i, n); + + if (rc != 0) { + return rc; + } + + /* Copy partition data to destination. */ + memcpy(dst + (size_t)(devpart->sector_size * i), devpart->crypto_work_buffer, (size_t)(devpart->sector_size * n)); + } + } else { + /* Read partition data using our backing file. */ + FILE *origin = fopen(target_path, "rb"); + fseek(origin, target_sector * devpart->sector_size, SEEK_CUR); + rc = (fread(dst, devpart->sector_size, num_sectors, origin) > 0) ? 0 : -1; + fclose(origin); + } return rc; } -int emu_device_partition_write_data(device_partition_t *devpart, const void *src, uint64_t sector, uint64_t num_sectors, const char *origin_path) +int emu_device_partition_write_data(device_partition_t *devpart, const void *src, uint64_t sector, uint64_t num_sectors, const char *origin_path, int num_parts, uint64_t part_limit) { int rc = 0; + uint64_t target_sector = 0; + char target_path[0x300 + 1] = {0}; - /* Write partition data using our backing file. */ - FILE *origin = fopen(origin_path, "wb"); - fseek(origin, sector * devpart->sector_size, SEEK_CUR); - rc = (fwrite(src, devpart->sector_size, num_sectors, origin) > 0) ? 0 : -1; - fclose(origin); + /* Perform initialization steps, if necessary. */ + if (!devpart->initialized) { + rc = devpart->initializer(devpart); + if (rc != 0) { + return rc; + } + } + + /* Handle data in multiple parts, if necessary. */ + if (num_parts > 0) { + int target_part = 0; + uint64_t data_offset = sector * devpart->sector_size; + + if (data_offset >= part_limit) { + uint64_t data_offset_aligned = (data_offset + (part_limit - 1)) & ~(part_limit - 1); + target_part = (data_offset_aligned == data_offset) ? (data_offset / part_limit) : (data_offset_aligned / part_limit) - 1; + target_sector = (data_offset - (target_part * part_limit)) / devpart->sector_size; + + /* Target part is invalid. */ + if (target_part > num_parts) { + return -1; + } + } + + snprintf(target_path, sizeof(target_path) - 1, "%s%02d", origin_path, target_part); + } else { + target_sector = sector; + strcpy(target_path, origin_path); + } + + /* Write the partition data. */ + if ((devpart->write_cipher != NULL) && (devpart->crypto_mode != DevicePartitionCryptoMode_None)) { + for (uint64_t i = 0; i < num_sectors; i += devpart->crypto_work_buffer_num_sectors) { + uint64_t n = (i + devpart->crypto_work_buffer_num_sectors > num_sectors) ? (num_sectors - i) : devpart->crypto_work_buffer_num_sectors; + + /* Copy partition data from source. */ + memcpy(devpart->crypto_work_buffer, src + (size_t)(devpart->sector_size * i), (size_t)(devpart->sector_size * n)); + + /* Encrypt data. */ + rc = devpart->write_cipher(devpart, target_sector + i, n); + + if (rc != 0) { + return rc; + } + + /* Write partition data using our backing file. */ + FILE *origin = fopen(target_path, "wb"); + fseek(origin, (target_sector + i) * devpart->sector_size, SEEK_CUR); + rc = (fwrite(src, devpart->sector_size, n, origin) > 0) ? 0 : -1; + fclose(origin); + + if (rc != 0) { + return rc; + } + } + } else { + /* Write partition data using our backing file. */ + FILE *origin = fopen(target_path, "wb"); + fseek(origin, sector * devpart->sector_size, SEEK_CUR); + rc = (fwrite(src, devpart->sector_size, num_sectors, origin) > 0) ? 0 : -1; + fclose(origin); + } return rc; } diff --git a/fusee/fusee-secondary/src/device_partition.h b/fusee/fusee-secondary/src/device_partition.h index b2a58cef3..6b4607e56 100644 --- a/fusee/fusee-secondary/src/device_partition.h +++ b/fusee/fusee-secondary/src/device_partition.h @@ -69,7 +69,7 @@ typedef struct device_partition_t { int device_partition_read_data(device_partition_t *devpart, void *dst, uint64_t sector, uint64_t num_sectors); int device_partition_write_data(device_partition_t *devpart, const void *src, uint64_t sector, uint64_t num_sectors); -int emu_device_partition_read_data(device_partition_t *devpart, void *dst, uint64_t sector, uint64_t num_sectors, const char *origin_path); -int emu_device_partition_write_data(device_partition_t *devpart, const void *src, uint64_t sector, uint64_t num_sectors, const char *origin_path); +int emu_device_partition_read_data(device_partition_t *devpart, void *dst, uint64_t sector, uint64_t num_sectors, const char *origin_path, int num_parts, uint64_t part_limit); +int emu_device_partition_write_data(device_partition_t *devpart, const void *src, uint64_t sector, uint64_t num_sectors, const char *origin_path, int num_parts, uint64_t part_limit); #endif diff --git a/fusee/fusee-secondary/src/emu_dev.c b/fusee/fusee-secondary/src/emu_dev.c index 0ae9b2b24..9b49db619 100644 --- a/fusee/fusee-secondary/src/emu_dev.c +++ b/fusee/fusee-secondary/src/emu_dev.c @@ -39,6 +39,8 @@ typedef struct emudev_device_t { devoptab_t devoptab; char origin_path[0x300+1]; + int num_parts; + uint64_t part_limit; uint8_t *tmp_sector; device_partition_t devpart; char name[32+1]; @@ -113,9 +115,92 @@ int emudev_mount_device(const char *name, const device_partition_t *devpart, con strcpy(device->root_path, name); strcat(device->root_path, ":/"); strcpy(device->origin_path, origin_path); + device->num_parts = 0; + device->part_limit = 0; device->devoptab.name = device->name; device->devoptab.deviceData = device; + + /* Initialize immediately. */ + int rc = device->devpart.initializer(&device->devpart); + if (rc != 0) { + errno = rc; + return -1; + } + + /* Allocate memory for our intermediate sector. */ + device->tmp_sector = (uint8_t *)malloc(devpart->sector_size); + if (device->tmp_sector == NULL) { + errno = ENOMEM; + return -1; + } + + device->setup = true; + device->registered = false; + + return 0; +} + +int emudev_mount_device_multipart(const char *name, const device_partition_t *devpart, const char *origin_path, int num_parts, uint64_t part_limit) { + emudev_device_t *device = NULL; + + if (name[0] == '\0' || devpart == NULL) { + errno = EINVAL; + return -1; + } + + if (strlen(name) > 32) { + errno = ENAMETOOLONG; + return -1; + } + if (emudev_find_device(name) != NULL) { + errno = EEXIST; /* Device already exists */ + return -1; + } + + /* Invalid number of parts. */ + if (num_parts <= 1) { + errno = EINVAL; + return -1; + } + + /* Part limit is invalid. */ + if ((part_limit % (1ull << 30)) != 0) { + errno = EINVAL; + return -1; + } + + /* Find an unused slot. */ + for (size_t i = 0; i < EMUDEV_MAX_DEVICES; i++) { + if (!g_emudev_devices[i].setup) { + device = &g_emudev_devices[i]; + break; + } + } + if (device == NULL) { + errno = ENOMEM; + return -1; + } + + memset(device, 0, sizeof(emudev_device_t)); + device->devoptab = g_emudev_devoptab; + device->devpart = *devpart; + strcpy(device->name, name); + strcpy(device->root_path, name); + strcat(device->root_path, ":/"); + strcpy(device->origin_path, origin_path); + device->num_parts = num_parts; + device->part_limit = part_limit; + + device->devoptab.name = device->name; + device->devoptab.deviceData = device; + + /* Initialize immediately. */ + int rc = device->devpart.initializer(&device->devpart); + if (rc != 0) { + errno = rc; + return -1; + } /* Allocate memory for our intermediate sector. */ device->tmp_sector = (uint8_t *)malloc(devpart->sector_size); @@ -192,6 +277,7 @@ int emudev_unmount_device(const char *name) { } free(device->tmp_sector); + device->devpart.finalizer(&device->devpart); memset(device, 0, sizeof(emudev_device_t)); return 0; @@ -265,7 +351,7 @@ static ssize_t emudev_write(struct _reent *r, void *fd, const char *ptr, size_t /* Unaligned at the start, we need to read the sector and incorporate the data. */ if (f->offset % sector_size != 0) { size_t nb = (size_t)(len <= (sector_size - (f->offset % sector_size)) ? len : sector_size - (f->offset % sector_size)); - no = emu_device_partition_read_data(&device->devpart, device->tmp_sector, sector_begin, 1, device->origin_path); + no = emu_device_partition_read_data(&device->devpart, device->tmp_sector, sector_begin, 1, device->origin_path, device->num_parts, device->part_limit); if (no != 0) { r->_errno = no; return -1; @@ -273,7 +359,7 @@ static ssize_t emudev_write(struct _reent *r, void *fd, const char *ptr, size_t memcpy(device->tmp_sector + (f->offset % sector_size), data, nb); - no = emu_device_partition_write_data(&device->devpart, device->tmp_sector, sector_begin, 1, device->origin_path); + no = emu_device_partition_write_data(&device->devpart, device->tmp_sector, sector_begin, 1, device->origin_path, device->num_parts, device->part_limit); if (no != 0) { r->_errno = no; return -1; @@ -292,7 +378,7 @@ static ssize_t emudev_write(struct _reent *r, void *fd, const char *ptr, size_t /* Write all of the sector-aligned data. */ if (current_sector != sector_end_aligned) { - no = emu_device_partition_write_data(&device->devpart, data, current_sector, sector_end_aligned - current_sector, device->origin_path); + no = emu_device_partition_write_data(&device->devpart, data, current_sector, sector_end_aligned - current_sector, device->origin_path, device->num_parts, device->part_limit); if (no != 0) { r->_errno = no; return -1; @@ -304,7 +390,7 @@ static ssize_t emudev_write(struct _reent *r, void *fd, const char *ptr, size_t /* Unaligned at the end, we need to read the sector and incorporate the data. */ if (sector_end != sector_end_aligned) { - no = emu_device_partition_read_data(&device->devpart, device->tmp_sector, sector_end_aligned, 1, device->origin_path); + no = emu_device_partition_read_data(&device->devpart, device->tmp_sector, sector_end_aligned, 1, device->origin_path, device->num_parts, device->part_limit); if (no != 0) { r->_errno = no; return -1; @@ -312,7 +398,7 @@ static ssize_t emudev_write(struct _reent *r, void *fd, const char *ptr, size_t memcpy(device->tmp_sector, data, (size_t)((f->offset + len) % sector_size)); - no = emu_device_partition_write_data(&device->devpart, device->tmp_sector, sector_end_aligned, 1, device->origin_path); + no = emu_device_partition_write_data(&device->devpart, device->tmp_sector, sector_end_aligned, 1, device->origin_path, device->num_parts, device->part_limit); if (no != 0) { r->_errno = no; return -1; @@ -353,7 +439,7 @@ static ssize_t emudev_read(struct _reent *r, void *fd, char *ptr, size_t len) { /* Unaligned at the start, we need to read the sector and incorporate the data. */ if (f->offset % sector_size != 0) { size_t nb = (size_t)(len <= (sector_size - (f->offset % sector_size)) ? len : sector_size - (f->offset % sector_size)); - no = emu_device_partition_read_data(&device->devpart, device->tmp_sector, sector_begin, 1, device->origin_path); + no = emu_device_partition_read_data(&device->devpart, device->tmp_sector, sector_begin, 1, device->origin_path, device->num_parts, device->part_limit); if (no != 0) { r->_errno = no; return -1; @@ -374,7 +460,7 @@ static ssize_t emudev_read(struct _reent *r, void *fd, char *ptr, size_t len) { /* Read all of the sector-aligned data. */ if (current_sector != sector_end_aligned) { - no = emu_device_partition_read_data(&device->devpart, data, current_sector, sector_end_aligned - current_sector, device->origin_path); + no = emu_device_partition_read_data(&device->devpart, data, current_sector, sector_end_aligned - current_sector, device->origin_path, device->num_parts, device->part_limit); if (no != 0) { r->_errno = no; return -1; @@ -386,7 +472,7 @@ static ssize_t emudev_read(struct _reent *r, void *fd, char *ptr, size_t len) { /* Unaligned at the end, we need to read the sector and incorporate the data. */ if (sector_end != sector_end_aligned) { - no = emu_device_partition_read_data(&device->devpart, device->tmp_sector, sector_end_aligned, 1, device->origin_path); + no = emu_device_partition_read_data(&device->devpart, device->tmp_sector, sector_end_aligned, 1, device->origin_path, device->num_parts, device->part_limit); if (no != 0) { r->_errno = no; return -1; diff --git a/fusee/fusee-secondary/src/emu_dev.h b/fusee/fusee-secondary/src/emu_dev.h index 08241731b..8aa6901a6 100644 --- a/fusee/fusee-secondary/src/emu_dev.h +++ b/fusee/fusee-secondary/src/emu_dev.h @@ -25,6 +25,7 @@ #define EMUDEV_MAX_DEVICES 16 int emudev_mount_device(const char *name, const device_partition_t *devpart, const char *origin_path); +int emudev_mount_device_multipart(const char *name, const device_partition_t *devpart, const char *origin_path, int num_parts, uint64_t part_limit); int emudev_register_device(const char *name); int emudev_unregister_device(const char *name); int emudev_unmount_device(const char *name); /* also unregisters. */ diff --git a/fusee/fusee-secondary/src/gpt.c b/fusee/fusee-secondary/src/gpt.c index 2f3ae2aed..52c7843b6 100644 --- a/fusee/fusee-secondary/src/gpt.c +++ b/fusee/fusee-secondary/src/gpt.c @@ -112,3 +112,42 @@ int gpt_iterate_through_entries(FILE *disk, size_t sector_size, gpt_entry_iterat return 0; } + +int gpt_iterate_through_emu_entries(FILE *disk, size_t sector_size, gpt_emu_entry_iterator_t callback, void *param, const char *origin_path, bool is_multipart, int num_parts, uint64_t part_limit) { + efi_header_t hdr; + efi_entry_t entry; + size_t offset = 2 * 512; /* Sector #2. */ + size_t delta; + + /* Get the header. */ + if (gpt_get_header(&hdr, disk, sector_size) == -1) { + return -1; + } + + /* Seek to the entry table. */ + if (fseek(disk, sector_size * hdr.entries_first_lba - offset, SEEK_CUR) != 0) { + return -1; + } + + offset = sector_size * hdr.entries_first_lba; + delta = hdr.entry_size - sizeof(efi_entry_t); + + /* Iterate through the entries. */ + for (uint32_t i = 0; i < hdr.entry_count; i++) { + if (!fread(&entry, sizeof(efi_entry_t), 1, disk)) { + return -1; + } + + if (callback(&entry, param, offset, disk, origin_path, is_multipart, num_parts, part_limit) != 0) { + return -1; + } + + if (delta != 0 && fseek(disk, delta, SEEK_CUR) != 0) { + return -1; + } + + offset += hdr.entry_size; + } + + return 0; +} diff --git a/fusee/fusee-secondary/src/gpt.h b/fusee/fusee-secondary/src/gpt.h index ff8a2b52f..862f00727 100644 --- a/fusee/fusee-secondary/src/gpt.h +++ b/fusee/fusee-secondary/src/gpt.h @@ -18,8 +18,8 @@ #define FUSEE_GPT_H #include - #include +#include typedef struct efi_entry_t { uint8_t type_uuid[16]; @@ -52,8 +52,10 @@ typedef struct efi_header { } __attribute__((packed, aligned(4))) efi_header_t; typedef int (*gpt_entry_iterator_t)(const efi_entry_t *entry, void *param, size_t entry_offset, FILE *disk); +typedef int (*gpt_emu_entry_iterator_t)(const efi_entry_t *entry, void *param, size_t entry_offset, FILE *disk, const char *origin_path, bool is_multipart, int num_parts, uint64_t part_limit); int gpt_get_header(efi_header_t *out, FILE *disk, size_t sector_size); int gpt_iterate_through_entries(FILE *disk, size_t sector_size, gpt_entry_iterator_t callback, void *param); +int gpt_iterate_through_emu_entries(FILE *disk, size_t sector_size, gpt_emu_entry_iterator_t callback, void *param, const char *origin_path, bool is_multipart, int num_parts, uint64_t part_limit); #endif diff --git a/fusee/fusee-secondary/src/nxboot.c b/fusee/fusee-secondary/src/nxboot.c index e18cd99e6..1dcb8c81b 100644 --- a/fusee/fusee-secondary/src/nxboot.c +++ b/fusee/fusee-secondary/src/nxboot.c @@ -207,6 +207,8 @@ static bool nxboot_configure_emunand() { if (emunand_cfg.enabled) { bool do_nand_backup = false; + int num_parts = 0; + uint64_t part_limit = 0; char emunand_boot0_path[0x300 + 1] = {0}; char emunand_boot1_path[0x300 + 1] = {0}; char emunand_rawnand_base_path[0x300 + 1] = {0}; @@ -219,9 +221,32 @@ static bool nxboot_configure_emunand() { fatal_error("[NXBOOT] Failed to find EmuNAND folder!\n"); } - /* Check if all emunand image files are present. */ - if (!is_valid_file(emunand_boot0_path) || !is_valid_file(emunand_boot1_path) || !is_valid_file(emunand_rawnand_base_path)) { - fatal_error("[NXBOOT] Failed to find EmuNAND image files!\n"); + /* Check if boot0 and boot1 image files are present. */ + if (!is_valid_file(emunand_boot0_path) || !is_valid_file(emunand_boot1_path)) { + fatal_error("[NXBOOT] Failed to find EmuNAND boot0/boot1 image files!\n"); + } + + /* Check if full rawnand image file is present. */ + if (!is_valid_file(emunand_rawnand_base_path)) { + char emunand_rawnand_path[0x300 + 3 + 1] = {0}; + + /* Search for rawnand part files instead. */ + for (int i = 0; i < 64; i++) { + snprintf(emunand_rawnand_path, sizeof(emunand_rawnand_path) - 1, "%s%02d", emunand_rawnand_base_path, i); + if (is_valid_file(emunand_rawnand_path)) { + if (i == 0) { + /* The size of the first part file should tell us the part limit. */ + part_limit = get_file_size(emunand_rawnand_path); + } + num_parts++; + } + } + + /* Check if at least the first part of the rawnand image file is present. */ + /* TODO: This fully trusts the user to provide properly split files. Do better checks? */ + if ((num_parts == 0) || (part_limit == 0)) { + fatal_error("[NXBOOT] Failed to find EmuNAND rawnand image files!\n"); + } } if (do_nand_backup) { @@ -239,7 +264,7 @@ static bool nxboot_configure_emunand() { } /* Mount emulated NAND. */ - if (nxfs_mount_emu_emmc(emunand_boot0_path, emunand_boot1_path, emunand_rawnand_base_path) < 0) { + if (nxfs_mount_emu_emmc(emunand_boot0_path, emunand_boot1_path, emunand_rawnand_base_path, num_parts, part_limit) < 0) { fatal_error("[NXBOOT] Failed to mount emulated eMMC!\n"); } } diff --git a/fusee/fusee-secondary/src/nxfs.c b/fusee/fusee-secondary/src/nxfs.c index a45b8580b..c993133ea 100644 --- a/fusee/fusee-secondary/src/nxfs.c +++ b/fusee/fusee-secondary/src/nxfs.c @@ -131,6 +131,26 @@ static int mmc_partition_write(device_partition_t *devpart, const void *src, uin return sdmmc_device_write(mmcpart->device, (uint32_t)(devpart->start_sector + sector), (uint32_t)num_sectors, (void *)src) ? 0 : EIO; } +static int emummc_partition_initialize(device_partition_t *devpart) { + if ((devpart->read_cipher != NULL) || (devpart->write_cipher != NULL)) { + devpart->crypto_work_buffer = memalign(16, devpart->sector_size * 16); + if (devpart->crypto_work_buffer == NULL) { + return ENOMEM; + } else { + devpart->crypto_work_buffer_num_sectors = devpart->sector_size * 16; + } + } else { + devpart->crypto_work_buffer = NULL; + devpart->crypto_work_buffer_num_sectors = 0; + } + devpart->initialized = true; + return 0; +} + +static void emummc_partition_finalize(device_partition_t *devpart) { + free(devpart->crypto_work_buffer); +} + static int nxfs_bis_crypto_decrypt(device_partition_t *devpart, uint64_t sector, uint64_t num_sectors) { unsigned int keyslot_a = 4; /* These keyslots are never used by exosphere, and should be safe. */ unsigned int keyslot_b = 5; @@ -181,8 +201,8 @@ static const device_partition_t g_mmc_devpart_template = { static const device_partition_t g_emummc_devpart_template = { .sector_size = 512, - .initializer = NULL, - .finalizer = NULL, + .initializer = emummc_partition_initialize, + .finalizer = emummc_partition_finalize, .reader = NULL, .writer = NULL, }; @@ -268,6 +288,94 @@ static int nxfs_mount_partition_gpt_callback(const efi_entry_t *entry, void *par return 0; } +static int nxfs_mount_emu_partition_gpt_callback(const efi_entry_t *entry, void *param, size_t entry_offset, FILE *disk, const char *origin_path, bool is_multipart, int num_parts, uint64_t part_limit) { + (void)entry_offset; + (void)disk; + device_partition_t *parent = (device_partition_t *)param; + device_partition_t devpart = *parent; + char name_buffer[128]; + const uint16_t *utf16name = entry->name; + uint32_t name_len; + int rc; + + static const struct { + const char *partition_name; + const char *mount_point; + bool is_fat; + bool is_encrypted; + bool register_immediately; + } known_partitions[] = { + {"PRODINFO", "prodinfo", false, true, false}, + {"PRODINFOF", "prodinfof", true, true, false}, + {"BCPKG2-1-Normal-Main", "bcpkg21", false, false, true}, + {"BCPKG2-2-Normal-Sub", "bcpkg22", false, false, false}, + {"BCPKG2-3-SafeMode-Main", "bcpkg23", false, false, false}, + {"BCPKG2-4-SafeMode-Sub", "bcpkg24", false, false, false}, + {"BCPKG2-5-Repair-Main", "bcpkg25", false, false, false}, + {"BCPKG2-6-Repair-Sub", "bcpkg26", false, false, false}, + {"SAFE", "safe", true, true, false}, + {"SYSTEM", "system", true, true, false}, + {"USER", "user", true, true, false}, + }; + + /* Convert the partition name to ASCII, for comparison. */ + for (name_len = 0; name_len < sizeof(entry->name) && *utf16name != 0; name_len++) { + name_buffer[name_len] = (char)*utf16name++; + } + name_buffer[name_len] = '\0'; + + /* Mount the partition, if we know about it. */ + for (size_t i = 0; i < sizeof(known_partitions)/sizeof(known_partitions[0]); i++) { + if (strcmp(name_buffer, known_partitions[i].partition_name) == 0) { + devpart.start_sector += entry->first_lba; + devpart.num_sectors = (entry->last_lba + 1) - entry->first_lba; + if (parent->num_sectors < devpart.num_sectors) { + errno = EINVAL; + return -1; + } + + if (known_partitions[i].is_encrypted) { + devpart.read_cipher = nxfs_bis_crypto_decrypt; + devpart.write_cipher = nxfs_bis_crypto_encrypt; + devpart.crypto_mode = DevicePartitionCryptoMode_Xts; + } + + if (known_partitions[i].is_fat) { + rc = fsdev_mount_device(known_partitions[i].mount_point, &devpart, false); + if (rc == -1) { + return -1; + } + if (known_partitions[i].register_immediately) { + rc = fsdev_register_device(known_partitions[i].mount_point); + if (rc == -1) { + return -1; + } + } + } else { + if (is_multipart) { + rc = emudev_mount_device_multipart(known_partitions[i].mount_point, &devpart, origin_path, num_parts, part_limit); + if (rc == -1) { + return -1; + } + } else { + rc = emudev_mount_device(known_partitions[i].mount_point, &devpart, origin_path); + if (rc == -1) { + return -1; + } + } + if (known_partitions[i].register_immediately) { + rc = emudev_register_device(known_partitions[i].mount_point); + if (rc == -1) { + return -1; + } + } + } + } + } + + return 0; +} + int nxfs_mount_sd() { device_partition_t model; int rc; @@ -380,7 +488,7 @@ int nxfs_mount_emmc() { return rc; } -int nxfs_mount_emu_emmc(const char *emunand_boot0_path, const char *emunand_boot1_path, const char *emunand_rawnand_base_path) { +int nxfs_mount_emu_emmc(const char *emunand_boot0_path, const char *emunand_boot1_path, const char *emunand_rawnand_base_path, int num_parts, uint64_t part_limit) { device_partition_t model; int rc; FILE *rawnand; @@ -436,7 +544,12 @@ int nxfs_mount_emu_emmc(const char *emunand_boot0_path, const char *emunand_boot model.num_sectors = (256ull << 30) / model.sector_size; if (!is_exfat) { - /* TODO: Use file concatenation for FAT32. */ + /* Mount emulated raw NAND device from multiple parts. */ + rc = emudev_mount_device_multipart("rawnand", &model, emunand_rawnand_base_path, num_parts, part_limit); + + if (rc == -1) { + return -1; + } } else { /* Mount emulated raw NAND device. */ rc = emudev_mount_device("rawnand", &model, emunand_rawnand_base_path); @@ -444,27 +557,27 @@ int nxfs_mount_emu_emmc(const char *emunand_boot0_path, const char *emunand_boot if (rc == -1) { return -1; } - - /* Register emulated raw NAND device. */ - rc = emudev_register_device("rawnand"); - if (rc == -1) { - return -1; - } - - /* Open emulated raw NAND device. */ - rawnand = fopen("rawnand:/", "rb"); - - if (rawnand == NULL) { - return -1; - } - - /* Iterate the GPT and mount each emulated raw NAND partition. */ - rc = gpt_iterate_through_entries(rawnand, model.sector_size, nxfs_mount_partition_gpt_callback, &model); - - /* Close emulated raw NAND device. */ - fclose(rawnand); } + /* Register emulated raw NAND device. */ + rc = emudev_register_device("rawnand"); + if (rc == -1) { + return -1; + } + + /* Open emulated raw NAND device. */ + rawnand = fopen("rawnand:/", "rb"); + + if (rawnand == NULL) { + return -1; + } + + /* Iterate the GPT and mount each emulated raw NAND partition. */ + rc = gpt_iterate_through_emu_entries(rawnand, model.sector_size, nxfs_mount_emu_partition_gpt_callback, &model, emunand_rawnand_base_path, !is_exfat, num_parts, part_limit); + + /* Close emulated raw NAND device. */ + fclose(rawnand); + /* All emulated devices are ready. */ if (rc == 0) { g_emudev_ready = true; diff --git a/fusee/fusee-secondary/src/nxfs.h b/fusee/fusee-secondary/src/nxfs.h index e26d47497..5e1ed8546 100644 --- a/fusee/fusee-secondary/src/nxfs.h +++ b/fusee/fusee-secondary/src/nxfs.h @@ -26,7 +26,7 @@ int nxfs_end(); int nxfs_mount_sd(); int nxfs_mount_emmc(); -int nxfs_mount_emu_emmc(const char *emunand_boot0_path, const char *emunand_boot1_path, const char *emunand_rawnand_base_path); +int nxfs_mount_emu_emmc(const char *emunand_boot0_path, const char *emunand_boot1_path, const char *emunand_rawnand_base_path, int num_parts, uint64_t part_limit); int nxfs_unmount_sd(); int nxfs_unmount_emmc(); int nxfs_unmount_emu_emmc();