mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 20:31:14 +00:00
emunand: Preliminary skeleton for emunand in fusee
This commit is contained in:
parent
5bedcd305d
commit
e5045556bf
8 changed files with 747 additions and 66 deletions
|
@ -35,7 +35,14 @@ extern void (*__program_exit_callback)(int rc);
|
||||||
static void *g_framebuffer;
|
static void *g_framebuffer;
|
||||||
static char g_bct0_buffer[BCTO_MAX_SIZE];
|
static char g_bct0_buffer[BCTO_MAX_SIZE];
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool enabled;
|
||||||
|
char* path;
|
||||||
|
} emunand_config_t;
|
||||||
|
|
||||||
#define CONFIG_LOG_LEVEL_KEY "log_level"
|
#define CONFIG_LOG_LEVEL_KEY "log_level"
|
||||||
|
#define EMUNAND_ENABLED_KEY "emunand_enabled"
|
||||||
|
#define EMUNAND_PATH_KEY "emunand_path"
|
||||||
|
|
||||||
#define DEFAULT_BCT0_FOR_DEBUG \
|
#define DEFAULT_BCT0_FOR_DEBUG \
|
||||||
"BCT0\n"\
|
"BCT0\n"\
|
||||||
|
@ -84,6 +91,26 @@ static int config_ini_handler(void *user, const char *section, const char *name,
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int emunand_ini_handler(void *user, const char *section, const char *name, const char *value) {
|
||||||
|
emunand_config_t *emunand_cfg = (emunand_config_t *)user;
|
||||||
|
if (strcmp(section, "emunand") == 0) {
|
||||||
|
if (strcmp(name, EMUNAND_ENABLED_KEY) == 0) {
|
||||||
|
int tmp = 0;
|
||||||
|
sscanf(value, "%d", &tmp);
|
||||||
|
emunand_cfg->enabled = (tmp != 0);
|
||||||
|
}
|
||||||
|
if (strcmp(name, EMUNAND_PATH_KEY) == 0) {
|
||||||
|
strncpy(emunand_cfg->path, value, sizeof(emunand_cfg->path) - 1);
|
||||||
|
emunand_cfg->path[sizeof(emunand_cfg->path) - 1] = '\0';
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static void setup_env(void) {
|
static void setup_env(void) {
|
||||||
g_framebuffer = (void *)0xC0000000;
|
g_framebuffer = (void *)0xC0000000;
|
||||||
|
|
||||||
|
@ -132,6 +159,11 @@ int main(void) {
|
||||||
stage2_args_t *stage2_args;
|
stage2_args_t *stage2_args;
|
||||||
uint32_t stage2_version = 0;
|
uint32_t stage2_version = 0;
|
||||||
ScreenLogLevel log_level = SCREEN_LOG_LEVEL_MANDATORY;
|
ScreenLogLevel log_level = SCREEN_LOG_LEVEL_MANDATORY;
|
||||||
|
emunand_config_t emunand_cfg = {0};
|
||||||
|
|
||||||
|
/* Set default values for emunand settings. */
|
||||||
|
emunand_cfg.enabled = false;
|
||||||
|
emunand_cfg.path = "atmosphere/emunand";
|
||||||
|
|
||||||
/* Override the global logging level. */
|
/* Override the global logging level. */
|
||||||
log_set_log_level(log_level);
|
log_set_log_level(log_level);
|
||||||
|
@ -142,8 +174,9 @@ int main(void) {
|
||||||
/* Load the BCT0 configuration ini off of the SD. */
|
/* Load the BCT0 configuration ini off of the SD. */
|
||||||
bct0 = load_config();
|
bct0 = load_config();
|
||||||
|
|
||||||
/* Extract the logging level from the BCT.ini file. */
|
/* Extract the logging level and emunand settings from the BCT.ini file. */
|
||||||
if (ini_parse_string(bct0, config_ini_handler, &log_level) < 0) {
|
if ((ini_parse_string(bct0, config_ini_handler, &log_level) < 0) ||
|
||||||
|
(ini_parse_string(bct0, emunand_ini_handler, &emunand_cfg) < 0)) {
|
||||||
fatal_error("Failed to parse BCT.ini!\n");
|
fatal_error("Failed to parse BCT.ini!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,6 +194,9 @@ int main(void) {
|
||||||
memcpy(&stage2_args->version, &stage2_version, 4);
|
memcpy(&stage2_args->version, &stage2_version, 4);
|
||||||
memcpy(&stage2_args->log_level, &log_level, sizeof(log_level));
|
memcpy(&stage2_args->log_level, &log_level, sizeof(log_level));
|
||||||
stage2_args->display_initialized = false;
|
stage2_args->display_initialized = false;
|
||||||
|
stage2_args->emunand_enabled = emunand_cfg.enabled;
|
||||||
|
strncpy(stage2_args->emunand_path, emunand_cfg.path, sizeof(stage2_args->emunand_path) - 1);
|
||||||
|
stage2_args->emunand_path[sizeof(stage2_args->emunand_path) - 1] = '\0';
|
||||||
strcpy(stage2_args->bct0, bct0);
|
strcpy(stage2_args->bct0, bct0);
|
||||||
g_chainloader_argc = 2;
|
g_chainloader_argc = 2;
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,8 @@ typedef struct {
|
||||||
uint32_t version;
|
uint32_t version;
|
||||||
ScreenLogLevel log_level;
|
ScreenLogLevel log_level;
|
||||||
bool display_initialized;
|
bool display_initialized;
|
||||||
|
bool emunand_enabled;
|
||||||
|
char emunand_path[0x100];
|
||||||
char bct0[BCTO_MAX_SIZE];
|
char bct0[BCTO_MAX_SIZE];
|
||||||
} stage2_args_t;
|
} stage2_args_t;
|
||||||
|
|
||||||
|
|
513
fusee/fusee-secondary/src/emu_dev.c
Normal file
513
fusee/fusee-secondary/src/emu_dev.c
Normal file
|
@ -0,0 +1,513 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/iosupport.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "emu_dev.h"
|
||||||
|
|
||||||
|
static int emudev_open(struct _reent *r, void *fileStruct, const char *path, int flags, int mode);
|
||||||
|
static int emudev_close(struct _reent *r, void *fd);
|
||||||
|
static ssize_t emudev_write(struct _reent *r, void *fd, const char *ptr, size_t len);
|
||||||
|
static ssize_t emudev_read(struct _reent *r, void *fd, char *ptr, size_t len);
|
||||||
|
static off_t emudev_seek(struct _reent *r, void *fd, off_t pos, int whence);
|
||||||
|
static int emudev_fstat(struct _reent *r, void *fd, struct stat *st);
|
||||||
|
static int emudev_stat(struct _reent *r, const char *file, struct stat *st);
|
||||||
|
static int emudev_fsync(struct _reent *r, void *fd);
|
||||||
|
|
||||||
|
typedef struct emudev_device_t {
|
||||||
|
devoptab_t devoptab;
|
||||||
|
|
||||||
|
FILE *origin;
|
||||||
|
uint8_t *tmp_sector;
|
||||||
|
device_partition_t devpart;
|
||||||
|
char name[32+1];
|
||||||
|
char root_path[34+1];
|
||||||
|
bool setup, registered;
|
||||||
|
} emudev_device_t;
|
||||||
|
|
||||||
|
typedef struct emudev_file_t {
|
||||||
|
emudev_device_t *device;
|
||||||
|
int open_flags;
|
||||||
|
uint64_t offset;
|
||||||
|
} emudev_file_t;
|
||||||
|
|
||||||
|
static emudev_device_t g_emudev_devices[EMUDEV_MAX_DEVICES] = {0};
|
||||||
|
|
||||||
|
static devoptab_t g_emudev_devoptab = {
|
||||||
|
.structSize = sizeof(emudev_file_t),
|
||||||
|
.open_r = emudev_open,
|
||||||
|
.close_r = emudev_close,
|
||||||
|
.write_r = emudev_write,
|
||||||
|
.read_r = emudev_read,
|
||||||
|
.seek_r = emudev_seek,
|
||||||
|
.fstat_r = emudev_fstat,
|
||||||
|
.stat_r = emudev_stat,
|
||||||
|
.fsync_r = emudev_fsync,
|
||||||
|
.deviceData = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static emudev_device_t *emudev_find_device(const char *name) {
|
||||||
|
for (size_t i = 0; i < EMUDEV_MAX_DEVICES; i++) {
|
||||||
|
if (g_emudev_devices[i].setup && strcmp(g_emudev_devices[i].name, name) == 0) {
|
||||||
|
return &g_emudev_devices[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int emudev_mount_device(const char *name, const char *origin_path, const device_partition_t *devpart) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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, ":/");
|
||||||
|
|
||||||
|
device->devoptab.name = device->name;
|
||||||
|
device->devoptab.deviceData = device;
|
||||||
|
|
||||||
|
/* Try to open the backing file for this emulated device. */
|
||||||
|
FILE *origin = fopen(origin_path, "rb");
|
||||||
|
|
||||||
|
/* Return invalid if we can't open the file. */
|
||||||
|
if (origin == NULL) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bind the backing file to this device. */
|
||||||
|
device->origin = origin;
|
||||||
|
|
||||||
|
/* 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_register_device(const char *name) {
|
||||||
|
emudev_device_t *device = emudev_find_device(name);
|
||||||
|
if (device == NULL) {
|
||||||
|
errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device->registered) {
|
||||||
|
/* Do nothing if the device is already registered. */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AddDevice(&device->devoptab) == -1) {
|
||||||
|
errno = ENOMEM;
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
device->registered = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int emudev_unregister_device(const char *name) {
|
||||||
|
emudev_device_t *device = emudev_find_device(name);
|
||||||
|
char drname[40];
|
||||||
|
|
||||||
|
if (device == NULL) {
|
||||||
|
errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!device->registered) {
|
||||||
|
/* Do nothing if the device is not registered. */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(drname, name);
|
||||||
|
strcat(drname, ":");
|
||||||
|
|
||||||
|
if (RemoveDevice(drname) == -1) {
|
||||||
|
errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
device->registered = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int emudev_unmount_device(const char *name) {
|
||||||
|
int rc;
|
||||||
|
emudev_device_t *device = emudev_find_device(name);
|
||||||
|
|
||||||
|
if (device == NULL) {
|
||||||
|
errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = emudev_unregister_device(name);
|
||||||
|
if (rc == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close the backing file, if there is one. */
|
||||||
|
if (device->origin != NULL)
|
||||||
|
fclose(device->origin);
|
||||||
|
|
||||||
|
free(device->tmp_sector);
|
||||||
|
memset(device, 0, sizeof(emudev_device_t));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int emudev_unmount_all(void) {
|
||||||
|
for (size_t i = 0; i < EMUDEV_MAX_DEVICES; i++) {
|
||||||
|
int rc = emudev_unmount_device(g_emudev_devices[i].name);
|
||||||
|
if (rc != 0) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int emudev_open(struct _reent *r, void *fileStruct, const char *path, int flags, int mode) {
|
||||||
|
(void)mode;
|
||||||
|
emudev_file_t *f = (emudev_file_t *)fileStruct;
|
||||||
|
emudev_device_t *device = (emudev_device_t *)(r->deviceData);
|
||||||
|
|
||||||
|
/* Only allow "device:/". */
|
||||||
|
if (strcmp(path, device->root_path) != 0) {
|
||||||
|
r->_errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forbid some flags that we explicitly don't support.*/
|
||||||
|
if (flags & (O_APPEND | O_TRUNC | O_EXCL)) {
|
||||||
|
r->_errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(f, 0, sizeof(emudev_file_t));
|
||||||
|
f->device = device;
|
||||||
|
f->open_flags = flags;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int emudev_close(struct _reent *r, void *fd) {
|
||||||
|
(void)r;
|
||||||
|
emudev_file_t *f = (emudev_file_t *)fd;
|
||||||
|
memset(f, 0, sizeof(emudev_file_t));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t emudev_write(struct _reent *r, void *fd, const char *ptr, size_t len) {
|
||||||
|
emudev_file_t *f = (emudev_file_t *)fd;
|
||||||
|
emudev_device_t *device = f->device;
|
||||||
|
size_t sector_size = device->devpart.sector_size;
|
||||||
|
uint64_t sector_begin = f->offset / sector_size;
|
||||||
|
uint64_t sector_end = (f->offset + len + sector_size - 1) / sector_size;
|
||||||
|
uint64_t sector_end_aligned;
|
||||||
|
uint64_t current_sector = sector_begin;
|
||||||
|
const uint8_t *data = (const uint8_t *)ptr;
|
||||||
|
|
||||||
|
int no = 0;
|
||||||
|
|
||||||
|
if (sector_end >= device->devpart.num_sectors) {
|
||||||
|
len = (size_t)(sector_size * device->devpart.num_sectors - f->offset);
|
||||||
|
sector_end = device->devpart.num_sectors;
|
||||||
|
}
|
||||||
|
|
||||||
|
sector_end_aligned = sector_end - ((f->offset + len) % sector_size != 0 ? 1 : 0);
|
||||||
|
|
||||||
|
if (len == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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));
|
||||||
|
|
||||||
|
/* Read partition data using our backing file. */
|
||||||
|
fseek(device->origin, sector_begin, SEEK_CUR);
|
||||||
|
no = (fread(device->tmp_sector, sector_size, 1, device->origin) > 0) ? 0 : EIO;
|
||||||
|
|
||||||
|
if (no != 0) {
|
||||||
|
r->_errno = no;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(device->tmp_sector + (f->offset % sector_size), data, nb);
|
||||||
|
|
||||||
|
/* Write partition data using our backing file. */
|
||||||
|
fseek(device->origin, sector_begin, SEEK_CUR);
|
||||||
|
no = (fwrite(device->tmp_sector, sector_size, 1, device->origin) > 0) ? 0 : EIO;
|
||||||
|
|
||||||
|
if (no != 0) {
|
||||||
|
r->_errno = no;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Advance */
|
||||||
|
data += sector_size - (f->offset % sector_size);
|
||||||
|
current_sector++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if we're already done (otherwise this causes a bug in handling the last sector of the range). */
|
||||||
|
if (current_sector == sector_end) {
|
||||||
|
f->offset += len;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write all of the sector-aligned data. */
|
||||||
|
if (current_sector != sector_end_aligned) {
|
||||||
|
/* Write partition data using our backing file. */
|
||||||
|
fseek(device->origin, current_sector, SEEK_CUR);
|
||||||
|
no = (fwrite(device->tmp_sector, sector_size, sector_end_aligned - current_sector, device->origin) > 0) ? 0 : EIO;
|
||||||
|
|
||||||
|
if (no != 0) {
|
||||||
|
r->_errno = no;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data += sector_size * (sector_end_aligned - current_sector);
|
||||||
|
current_sector = sector_end_aligned;
|
||||||
|
|
||||||
|
/* Unaligned at the end, we need to read the sector and incorporate the data. */
|
||||||
|
if (sector_end != sector_end_aligned) {
|
||||||
|
/* Read partition data using our backing file. */
|
||||||
|
fseek(device->origin, sector_end_aligned, SEEK_CUR);
|
||||||
|
no = (fread(device->tmp_sector, sector_size, 1, device->origin) > 0) ? 0 : EIO;
|
||||||
|
|
||||||
|
if (no != 0) {
|
||||||
|
r->_errno = no;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(device->tmp_sector, data, (size_t)((f->offset + len) % sector_size));
|
||||||
|
|
||||||
|
/* Write partition data using our backing file. */
|
||||||
|
fseek(device->origin, sector_end_aligned, SEEK_CUR);
|
||||||
|
no = (fwrite(device->tmp_sector, sector_size, 1, device->origin) > 0) ? 0 : EIO;
|
||||||
|
|
||||||
|
if (no != 0) {
|
||||||
|
r->_errno = no;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Advance */
|
||||||
|
data += sector_size - ((f->offset + len) % sector_size);
|
||||||
|
current_sector++;
|
||||||
|
}
|
||||||
|
|
||||||
|
f->offset += len;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t emudev_read(struct _reent *r, void *fd, char *ptr, size_t len) {
|
||||||
|
emudev_file_t *f = (emudev_file_t *)fd;
|
||||||
|
emudev_device_t *device = f->device;
|
||||||
|
size_t sector_size = device->devpart.sector_size;
|
||||||
|
uint64_t sector_begin = f->offset / sector_size;
|
||||||
|
uint64_t sector_end = (f->offset + len + sector_size - 1) / sector_size;
|
||||||
|
uint64_t sector_end_aligned;
|
||||||
|
uint64_t current_sector = sector_begin;
|
||||||
|
uint8_t *data = (uint8_t *)ptr;
|
||||||
|
|
||||||
|
int no = 0;
|
||||||
|
|
||||||
|
if (sector_end >= device->devpart.num_sectors) {
|
||||||
|
len = (size_t)(sector_size * device->devpart.num_sectors - f->offset);
|
||||||
|
sector_end = device->devpart.num_sectors;
|
||||||
|
}
|
||||||
|
|
||||||
|
sector_end_aligned = sector_end - ((f->offset + len) % sector_size != 0 ? 1 : 0);
|
||||||
|
|
||||||
|
if (len == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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));
|
||||||
|
|
||||||
|
/* Read partition data using our backing file. */
|
||||||
|
fseek(device->origin, sector_begin, SEEK_CUR);
|
||||||
|
no = (fread(device->tmp_sector, sector_size, 1, device->origin) > 0) ? 0 : EIO;
|
||||||
|
|
||||||
|
if (no != 0) {
|
||||||
|
r->_errno = no;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(data, device->tmp_sector + (f->offset % sector_size), nb);
|
||||||
|
|
||||||
|
/* Advance */
|
||||||
|
data += sector_size - (f->offset % sector_size);
|
||||||
|
current_sector++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if we're already done (otherwise this causes a bug in handling the last sector of the range). */
|
||||||
|
if (current_sector == sector_end) {
|
||||||
|
f->offset += len;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read all of the sector-aligned data. */
|
||||||
|
if (current_sector != sector_end_aligned) {
|
||||||
|
/* Read partition data using our backing file. */
|
||||||
|
fseek(device->origin, current_sector, SEEK_CUR);
|
||||||
|
no = (fread(device->tmp_sector, sector_size, sector_end_aligned - current_sector, device->origin) > 0) ? 0 : EIO;
|
||||||
|
|
||||||
|
if (no != 0) {
|
||||||
|
r->_errno = no;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data += sector_size * (sector_end_aligned - current_sector);
|
||||||
|
current_sector = sector_end_aligned;
|
||||||
|
|
||||||
|
/* Unaligned at the end, we need to read the sector and incorporate the data. */
|
||||||
|
if (sector_end != sector_end_aligned) {
|
||||||
|
/* Read partition data using our backing file. */
|
||||||
|
fseek(device->origin, sector_end_aligned, SEEK_CUR);
|
||||||
|
no = (fread(device->tmp_sector, sector_size, 1, device->origin) > 0) ? 0 : EIO;
|
||||||
|
|
||||||
|
if (no != 0) {
|
||||||
|
r->_errno = no;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(data, device->tmp_sector, (size_t)((f->offset + len) % sector_size));
|
||||||
|
|
||||||
|
/* Advance */
|
||||||
|
data += sector_size - ((f->offset + len) % sector_size);
|
||||||
|
current_sector++;
|
||||||
|
}
|
||||||
|
|
||||||
|
f->offset += len;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static off_t emudev_seek(struct _reent *r, void *fd, off_t pos, int whence) {
|
||||||
|
emudev_file_t *f = (emudev_file_t *)fd;
|
||||||
|
emudev_device_t *device = f->device;
|
||||||
|
uint64_t off;
|
||||||
|
|
||||||
|
switch (whence) {
|
||||||
|
case SEEK_SET:
|
||||||
|
off = 0;
|
||||||
|
break;
|
||||||
|
case SEEK_CUR:
|
||||||
|
off = f->offset;
|
||||||
|
break;
|
||||||
|
case SEEK_END:
|
||||||
|
off = device->devpart.num_sectors * device->devpart.sector_size;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
r->_errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos < 0 && pos + off < 0) {
|
||||||
|
/* don't allow seek to before the beginning of the file */
|
||||||
|
r->_errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
f->offset = (uint64_t)(pos + off);
|
||||||
|
return (off_t)(pos + off);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void emudev_stat_impl(emudev_device_t *device, struct stat *st) {
|
||||||
|
memset(st, 0, sizeof(struct stat));
|
||||||
|
st->st_size = (off_t)(device->devpart.num_sectors * device->devpart.sector_size);
|
||||||
|
st->st_nlink = 1;
|
||||||
|
|
||||||
|
st->st_blksize = device->devpart.sector_size;
|
||||||
|
st->st_blocks = st->st_size / st->st_blksize;
|
||||||
|
|
||||||
|
st->st_mode = S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int emudev_fstat(struct _reent *r, void *fd, struct stat *st) {
|
||||||
|
(void)r;
|
||||||
|
emudev_file_t *f = (emudev_file_t *)fd;
|
||||||
|
emudev_device_t *device = f->device;
|
||||||
|
emudev_stat_impl(device, st);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int emudev_stat(struct _reent *r, const char *file, struct stat *st) {
|
||||||
|
emudev_device_t *device = (emudev_device_t *)(r->deviceData);
|
||||||
|
if (strcmp(file, device->root_path) != 0) {
|
||||||
|
r->_errno = ENOENT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
emudev_stat_impl(device, st);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int emudev_fsync(struct _reent *r, void *fd) {
|
||||||
|
/* Nothing to do. */
|
||||||
|
(void)r;
|
||||||
|
(void)fd;
|
||||||
|
return 0;
|
||||||
|
}
|
34
fusee/fusee-secondary/src/emu_dev.h
Normal file
34
fusee/fusee-secondary/src/emu_dev.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FUSEE_EMU_DEV_H
|
||||||
|
#define FUSEE_EMU_DEV_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "device_partition.h"
|
||||||
|
|
||||||
|
#define EMUDEV_MAX_DEVICES 16
|
||||||
|
|
||||||
|
int emudev_mount_device(const char *name, const char *origin_path, const device_partition_t *devpart);
|
||||||
|
int emudev_register_device(const char *name);
|
||||||
|
|
||||||
|
int emudev_unregister_device(const char *name);
|
||||||
|
int emudev_unmount_device(const char *name); /* also unregisters. */
|
||||||
|
int emudev_unmount_all(void);
|
||||||
|
|
||||||
|
#endif
|
|
@ -51,8 +51,8 @@ static void setup_env(void) {
|
||||||
/* Set up exception handlers. */
|
/* Set up exception handlers. */
|
||||||
setup_exception_handlers();
|
setup_exception_handlers();
|
||||||
|
|
||||||
if (nxfs_mount_all() < 0) {
|
if (nxfs_mount_all(g_stage2_args->emunand_enabled, g_stage2_args->emunand_path) < 0) {
|
||||||
fatal_error("Failed to mount at least one parition: %s\n", strerror(errno));
|
fatal_error("Failed to mount at least one partition: %s\n", strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Train DRAM. */
|
/* Train DRAM. */
|
||||||
|
|
|
@ -175,6 +175,14 @@ static const device_partition_t g_mmc_devpart_template = {
|
||||||
.writer = mmc_partition_write,
|
.writer = mmc_partition_write,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const device_partition_t g_emummc_devpart_template = {
|
||||||
|
.sector_size = 512,
|
||||||
|
.initializer = NULL,
|
||||||
|
.finalizer = NULL,
|
||||||
|
.reader = NULL,
|
||||||
|
.writer = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
static int nxfs_mount_partition_gpt_callback(const efi_entry_t *entry, void *param, size_t entry_offset, FILE *disk) {
|
static int nxfs_mount_partition_gpt_callback(const efi_entry_t *entry, void *param, size_t entry_offset, FILE *disk) {
|
||||||
(void)entry_offset;
|
(void)entry_offset;
|
||||||
(void)disk;
|
(void)disk;
|
||||||
|
@ -256,53 +264,131 @@ static int nxfs_mount_partition_gpt_callback(const efi_entry_t *entry, void *par
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nxfs_mount_all(void) {
|
int nxfs_mount_all(bool emunand_enabled, const char *emunand_path) {
|
||||||
device_partition_t model;
|
device_partition_t model;
|
||||||
int rc;
|
int rc;
|
||||||
FILE *rawnand;
|
FILE *rawnand;
|
||||||
|
|
||||||
/* Initialize the SD card and its primary partition. */
|
/* Setup a template for the SD card. */
|
||||||
model = g_mmc_devpart_template;
|
model = g_mmc_devpart_template;
|
||||||
model.device_struct = &g_sd_mmcpart;
|
model.device_struct = &g_sd_mmcpart;
|
||||||
model.start_sector = 0;
|
model.start_sector = 0;
|
||||||
model.num_sectors = 1u << 30; /* arbitrary numbers of sectors. TODO: find the size of the SD in sectors. */
|
model.num_sectors = 1u << 30; /* arbitrary numbers of sectors. TODO: find the size of the SD in sectors. */
|
||||||
|
|
||||||
|
/* Mount the SD card device. */
|
||||||
rc = fsdev_mount_device("sdmc", &model, true);
|
rc = fsdev_mount_device("sdmc", &model, true);
|
||||||
|
|
||||||
if (rc == -1) {
|
if (rc == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Register the SD card device. */
|
||||||
rc = fsdev_register_device("sdmc");
|
rc = fsdev_register_device("sdmc");
|
||||||
|
|
||||||
if (rc == -1) {
|
if (rc == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Boot0. */
|
if (emunand_enabled) {
|
||||||
|
/* Setup emunand paths. */
|
||||||
|
char emu_boot0_path[0x100];
|
||||||
|
char emu_boot1_path[0x100];
|
||||||
|
char emu_rawnand_path[0x100];
|
||||||
|
snprintf(emu_boot0_path, sizeof(emu_boot0_path) - 1, "sdmc:/%s/%s", emunand_path, "boot0");
|
||||||
|
snprintf(emu_boot1_path, sizeof(emu_boot1_path) - 1, "sdmc:/%s/%s", emunand_path, "boot1");
|
||||||
|
snprintf(emu_rawnand_path, sizeof(emu_rawnand_path) - 1, "sdmc:/%s/%s", emunand_path, "rawnand");
|
||||||
|
|
||||||
|
/* Setup an emulation template for boot0. */
|
||||||
|
model = g_emummc_devpart_template;
|
||||||
|
model.start_sector = 0;
|
||||||
|
model.num_sectors = 0x184000 / model.sector_size;
|
||||||
|
|
||||||
|
/* Mount emulated boot0 device. */
|
||||||
|
rc = emudev_mount_device("boot0", emu_boot0_path, &model);
|
||||||
|
|
||||||
|
if (rc == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Register emulated boot0 device. */
|
||||||
|
rc = emudev_register_device("boot0");
|
||||||
|
|
||||||
|
if (rc == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup an emulation template for boot1. */
|
||||||
|
model = g_emummc_devpart_template;
|
||||||
|
model.start_sector = 0;
|
||||||
|
model.num_sectors = 0x80000 / model.sector_size;
|
||||||
|
|
||||||
|
/* Mount emulated boot1 device. */
|
||||||
|
rc = emudev_mount_device("boot1", emu_boot1_path, &model);
|
||||||
|
|
||||||
|
if (rc == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Don't register emulated boot1 for now. */
|
||||||
|
|
||||||
|
/* Setup a template for raw NAND. */
|
||||||
|
model = g_emummc_devpart_template;
|
||||||
|
model.start_sector = 0;
|
||||||
|
model.num_sectors = (256ull << 30) / model.sector_size;
|
||||||
|
|
||||||
|
/* Mount emulated raw NAND device. */
|
||||||
|
rc = emudev_mount_device("rawnand", emu_rawnand_path, &model);
|
||||||
|
|
||||||
|
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);
|
||||||
|
} else {
|
||||||
|
/* Setup a template for boot0. */
|
||||||
model = g_mmc_devpart_template;
|
model = g_mmc_devpart_template;
|
||||||
model.device_struct = &g_emmc_boot0_mmcpart;
|
model.device_struct = &g_emmc_boot0_mmcpart;
|
||||||
model.start_sector = 0;
|
model.start_sector = 0;
|
||||||
model.num_sectors = 0x184000 / model.sector_size;
|
model.num_sectors = 0x184000 / model.sector_size;
|
||||||
|
|
||||||
|
/* Mount boot0 device. */
|
||||||
rc = rawdev_mount_device("boot0", &model, true);
|
rc = rawdev_mount_device("boot0", &model, true);
|
||||||
|
|
||||||
if (rc == -1) {
|
if (rc == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Register boot0 device. */
|
||||||
rc = rawdev_register_device("boot0");
|
rc = rawdev_register_device("boot0");
|
||||||
|
|
||||||
if (rc == -1) {
|
if (rc == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Boot1. */
|
/* Setup a template for boot1. */
|
||||||
model = g_mmc_devpart_template;
|
model = g_mmc_devpart_template;
|
||||||
model.device_struct = &g_emmc_boot1_mmcpart;
|
model.device_struct = &g_emmc_boot1_mmcpart;
|
||||||
model.start_sector = 0;
|
model.start_sector = 0;
|
||||||
model.num_sectors = 0x80000 / model.sector_size;
|
model.num_sectors = 0x80000 / model.sector_size;
|
||||||
|
|
||||||
|
/* Mount boot1 device. */
|
||||||
rc = rawdev_mount_device("boot1", &model, false);
|
rc = rawdev_mount_device("boot1", &model, false);
|
||||||
|
|
||||||
if (rc == -1) {
|
if (rc == -1) {
|
||||||
|
@ -311,33 +397,40 @@ int nxfs_mount_all(void) {
|
||||||
|
|
||||||
/* Don't register boot1 for now. */
|
/* Don't register boot1 for now. */
|
||||||
|
|
||||||
/* Raw NAND (excluding boot partitions), and its partitions. */
|
/* Setup a template for raw NAND. */
|
||||||
model = g_mmc_devpart_template;
|
|
||||||
model = g_mmc_devpart_template;
|
model = g_mmc_devpart_template;
|
||||||
model.device_struct = &g_emmc_user_mmcpart;
|
model.device_struct = &g_emmc_user_mmcpart;
|
||||||
model.start_sector = 0;
|
model.start_sector = 0;
|
||||||
model.num_sectors = (256ull << 30) / model.sector_size;
|
model.num_sectors = (256ull << 30) / model.sector_size;
|
||||||
|
|
||||||
|
/* Mount raw NAND device. */
|
||||||
rc = rawdev_mount_device("rawnand", &model, false);
|
rc = rawdev_mount_device("rawnand", &model, false);
|
||||||
|
|
||||||
if (rc == -1) {
|
if (rc == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Register raw NAND device. */
|
||||||
rc = rawdev_register_device("rawnand");
|
rc = rawdev_register_device("rawnand");
|
||||||
if (rc == -1) {
|
if (rc == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Open raw NAND device. */
|
||||||
rawnand = fopen("rawnand:/", "rb");
|
rawnand = fopen("rawnand:/", "rb");
|
||||||
|
|
||||||
if (rawnand == NULL) {
|
if (rawnand == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Iterate the GPT and mount each raw NAND partition. */
|
||||||
rc = gpt_iterate_through_entries(rawnand, model.sector_size, nxfs_mount_partition_gpt_callback, &model);
|
rc = gpt_iterate_through_entries(rawnand, model.sector_size, nxfs_mount_partition_gpt_callback, &model);
|
||||||
fclose(rawnand);
|
|
||||||
|
|
||||||
|
/* Close raw NAND device. */
|
||||||
|
fclose(rawnand);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the default file system device. */
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
rc = fsdev_set_default_device("sdmc");
|
rc = fsdev_set_default_device("sdmc");
|
||||||
}
|
}
|
||||||
|
@ -345,6 +438,6 @@ int nxfs_mount_all(void) {
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nxfs_unmount_all(void) {
|
int nxfs_unmount_all() {
|
||||||
return ((fsdev_unmount_all() || rawdev_unmount_all()) ? -1 : 0);
|
return ((fsdev_unmount_all() || rawdev_unmount_all() || emudev_unmount_all()) ? -1 : 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,9 @@
|
||||||
|
|
||||||
#include "fs_dev.h"
|
#include "fs_dev.h"
|
||||||
#include "raw_dev.h"
|
#include "raw_dev.h"
|
||||||
|
#include "emu_dev.h"
|
||||||
|
|
||||||
int nxfs_mount_all(void);
|
int nxfs_mount_all(bool emunand_enabled, const char *emunand_path);
|
||||||
int nxfs_unmount_all(void);
|
int nxfs_unmount_all();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -32,6 +32,8 @@ typedef struct {
|
||||||
uint32_t version;
|
uint32_t version;
|
||||||
ScreenLogLevel log_level;
|
ScreenLogLevel log_level;
|
||||||
bool display_initialized;
|
bool display_initialized;
|
||||||
|
bool emunand_enabled;
|
||||||
|
char emunand_path[0x100];
|
||||||
char bct0[BCTO_MAX_SIZE];
|
char bct0[BCTO_MAX_SIZE];
|
||||||
} stage2_args_t;
|
} stage2_args_t;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue