emunand: Add multipart support for rawnand images in fusee

This commit is contained in:
hexkyz 2019-04-13 18:28:54 +01:00
parent a1512cf30f
commit 017d473b99
9 changed files with 438 additions and 54 deletions

View file

@ -27,7 +27,7 @@ int device_partition_read_data(device_partition_t *devpart, void *dst, uint64_t
return rc; 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) { 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; 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); 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; 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) { 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; 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)); 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; int rc = 0;
uint64_t target_sector = 0;
char target_path[0x300 + 1] = {0};
/* Read partition data using our backing file. */ /* Perform initialization steps, if necessary. */
FILE *origin = fopen(origin_path, "rb"); if (!devpart->initialized) {
fseek(origin, sector * devpart->sector_size, SEEK_CUR); rc = devpart->initializer(devpart);
rc = (fread(dst, devpart->sector_size, num_sectors, origin) > 0) ? 0 : -1; if (rc != 0) {
fclose(origin); 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; 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; int rc = 0;
uint64_t target_sector = 0;
char target_path[0x300 + 1] = {0};
/* Write partition data using our backing file. */ /* Perform initialization steps, if necessary. */
FILE *origin = fopen(origin_path, "wb"); if (!devpart->initialized) {
fseek(origin, sector * devpart->sector_size, SEEK_CUR); rc = devpart->initializer(devpart);
rc = (fwrite(src, devpart->sector_size, num_sectors, origin) > 0) ? 0 : -1; if (rc != 0) {
fclose(origin); 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; return rc;
} }

View file

@ -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_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 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_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 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 #endif

View file

@ -39,6 +39,8 @@ typedef struct emudev_device_t {
devoptab_t devoptab; devoptab_t devoptab;
char origin_path[0x300+1]; char origin_path[0x300+1];
int num_parts;
uint64_t part_limit;
uint8_t *tmp_sector; uint8_t *tmp_sector;
device_partition_t devpart; device_partition_t devpart;
char name[32+1]; char name[32+1];
@ -113,10 +115,93 @@ int emudev_mount_device(const char *name, const device_partition_t *devpart, con
strcpy(device->root_path, name); strcpy(device->root_path, name);
strcat(device->root_path, ":/"); strcat(device->root_path, ":/");
strcpy(device->origin_path, origin_path); strcpy(device->origin_path, origin_path);
device->num_parts = 0;
device->part_limit = 0;
device->devoptab.name = device->name; device->devoptab.name = device->name;
device->devoptab.deviceData = device; 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. */ /* Allocate memory for our intermediate sector. */
device->tmp_sector = (uint8_t *)malloc(devpart->sector_size); device->tmp_sector = (uint8_t *)malloc(devpart->sector_size);
if (device->tmp_sector == NULL) { if (device->tmp_sector == NULL) {
@ -192,6 +277,7 @@ int emudev_unmount_device(const char *name) {
} }
free(device->tmp_sector); free(device->tmp_sector);
device->devpart.finalizer(&device->devpart);
memset(device, 0, sizeof(emudev_device_t)); memset(device, 0, sizeof(emudev_device_t));
return 0; 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. */ /* Unaligned at the start, we need to read the sector and incorporate the data. */
if (f->offset % sector_size != 0) { 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)); 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) { if (no != 0) {
r->_errno = no; r->_errno = no;
return -1; 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); 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) { if (no != 0) {
r->_errno = no; r->_errno = no;
return -1; 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. */ /* Write all of the sector-aligned data. */
if (current_sector != sector_end_aligned) { 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) { if (no != 0) {
r->_errno = no; r->_errno = no;
return -1; 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. */ /* Unaligned at the end, we need to read the sector and incorporate the data. */
if (sector_end != sector_end_aligned) { 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) { if (no != 0) {
r->_errno = no; r->_errno = no;
return -1; 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)); 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) { if (no != 0) {
r->_errno = no; r->_errno = no;
return -1; 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. */ /* Unaligned at the start, we need to read the sector and incorporate the data. */
if (f->offset % sector_size != 0) { 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)); 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) { if (no != 0) {
r->_errno = no; r->_errno = no;
return -1; 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. */ /* Read all of the sector-aligned data. */
if (current_sector != sector_end_aligned) { 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) { if (no != 0) {
r->_errno = no; r->_errno = no;
return -1; 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. */ /* Unaligned at the end, we need to read the sector and incorporate the data. */
if (sector_end != sector_end_aligned) { 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) { if (no != 0) {
r->_errno = no; r->_errno = no;
return -1; return -1;

View file

@ -25,6 +25,7 @@
#define EMUDEV_MAX_DEVICES 16 #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(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_register_device(const char *name);
int emudev_unregister_device(const char *name); int emudev_unregister_device(const char *name);
int emudev_unmount_device(const char *name); /* also unregisters. */ int emudev_unmount_device(const char *name); /* also unregisters. */

View file

@ -112,3 +112,42 @@ int gpt_iterate_through_entries(FILE *disk, size_t sector_size, gpt_entry_iterat
return 0; 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;
}

View file

@ -18,8 +18,8 @@
#define FUSEE_GPT_H #define FUSEE_GPT_H
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
typedef struct efi_entry_t { typedef struct efi_entry_t {
uint8_t type_uuid[16]; uint8_t type_uuid[16];
@ -52,8 +52,10 @@ typedef struct efi_header {
} __attribute__((packed, aligned(4))) efi_header_t; } __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_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_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_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 #endif

View file

@ -207,6 +207,8 @@ static bool nxboot_configure_emunand() {
if (emunand_cfg.enabled) { if (emunand_cfg.enabled) {
bool do_nand_backup = false; bool do_nand_backup = false;
int num_parts = 0;
uint64_t part_limit = 0;
char emunand_boot0_path[0x300 + 1] = {0}; char emunand_boot0_path[0x300 + 1] = {0};
char emunand_boot1_path[0x300 + 1] = {0}; char emunand_boot1_path[0x300 + 1] = {0};
char emunand_rawnand_base_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"); fatal_error("[NXBOOT] Failed to find EmuNAND folder!\n");
} }
/* Check if all emunand image files are present. */ /* Check if boot0 and boot1 image files are present. */
if (!is_valid_file(emunand_boot0_path) || !is_valid_file(emunand_boot1_path) || !is_valid_file(emunand_rawnand_base_path)) { if (!is_valid_file(emunand_boot0_path) || !is_valid_file(emunand_boot1_path)) {
fatal_error("[NXBOOT] Failed to find EmuNAND image files!\n"); 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) { if (do_nand_backup) {
@ -239,7 +264,7 @@ static bool nxboot_configure_emunand() {
} }
/* Mount emulated NAND. */ /* 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"); fatal_error("[NXBOOT] Failed to mount emulated eMMC!\n");
} }
} }

View file

@ -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; 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) { 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_a = 4; /* These keyslots are never used by exosphere, and should be safe. */
unsigned int keyslot_b = 5; 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 = { static const device_partition_t g_emummc_devpart_template = {
.sector_size = 512, .sector_size = 512,
.initializer = NULL, .initializer = emummc_partition_initialize,
.finalizer = NULL, .finalizer = emummc_partition_finalize,
.reader = NULL, .reader = NULL,
.writer = NULL, .writer = NULL,
}; };
@ -268,6 +288,94 @@ static int nxfs_mount_partition_gpt_callback(const efi_entry_t *entry, void *par
return 0; 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() { int nxfs_mount_sd() {
device_partition_t model; device_partition_t model;
int rc; int rc;
@ -380,7 +488,7 @@ int nxfs_mount_emmc() {
return rc; 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; device_partition_t model;
int rc; int rc;
FILE *rawnand; 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; model.num_sectors = (256ull << 30) / model.sector_size;
if (!is_exfat) { 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 { } else {
/* Mount emulated raw NAND device. */ /* Mount emulated raw NAND device. */
rc = emudev_mount_device("rawnand", &model, emunand_rawnand_base_path); 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) { if (rc == -1) {
return -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. */ /* All emulated devices are ready. */
if (rc == 0) { if (rc == 0) {
g_emudev_ready = true; g_emudev_ready = true;

View file

@ -26,7 +26,7 @@ int nxfs_end();
int nxfs_mount_sd(); int nxfs_mount_sd();
int nxfs_mount_emmc(); 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_sd();
int nxfs_unmount_emmc(); int nxfs_unmount_emmc();
int nxfs_unmount_emu_emmc(); int nxfs_unmount_emu_emmc();