mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-11-26 13:52:21 +00:00
emunand: Add multipart support for rawnand images in fusee
This commit is contained in:
parent
a1512cf30f
commit
017d473b99
9 changed files with 438 additions and 54 deletions
|
@ -14,7 +14,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "device_partition.h"
|
#include "device_partition.h"
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,9 +115,92 @@ 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);
|
||||||
|
@ -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;
|
||||||
|
|
|
@ -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. */
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in a new issue