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();