Merge pull request #567 from Atmosphere-NX/emunand_dev
Implement support for emummc.
This commit is contained in:
@@ -38,7 +38,7 @@ bool mount_sd(void)
|
||||
|
||||
if (!g_sd_initialized) {
|
||||
/* Initialize SD. */
|
||||
if (sdmmc_device_sd_init(&g_sd_device, &g_sd_sdmmc, SDMMC_BUS_WIDTH_4BIT, SDMMC_SPEED_SDR104))
|
||||
if (sdmmc_device_sd_init(&g_sd_device, &g_sd_sdmmc, SDMMC_BUS_WIDTH_4BIT, SDMMC_SPEED_UHS_SDR104))
|
||||
{
|
||||
g_sd_initialized = true;
|
||||
|
||||
|
||||
@@ -173,4 +173,4 @@ int main(void) {
|
||||
/* Finally, after the cleanup routines (__libc_fini_array, etc.) are called, jump to Stage2. */
|
||||
__program_exit_callback = exit_callback;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -727,11 +727,11 @@ static int sdmmc_sd_switch_hs_low(sdmmc_device_t *device, uint8_t *status)
|
||||
return 0;
|
||||
|
||||
/* Reconfigure the internal clock. */
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_SDR104))
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_UHS_SDR104))
|
||||
return 0;
|
||||
|
||||
/* Run tuning. */
|
||||
if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_SDR104, MMC_SEND_TUNING_BLOCK))
|
||||
if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_UHS_SDR104, MMC_SEND_TUNING_BLOCK))
|
||||
return 0;
|
||||
}
|
||||
else if (status[13] & SD_MODE_UHS_SDR50) /* High-speed SDR50 is supported. */
|
||||
@@ -741,11 +741,11 @@ static int sdmmc_sd_switch_hs_low(sdmmc_device_t *device, uint8_t *status)
|
||||
return 0;
|
||||
|
||||
/* Reconfigure the internal clock. */
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_SDR50))
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_UHS_SDR50))
|
||||
return 0;
|
||||
|
||||
/* Run tuning. */
|
||||
if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_SDR50, MMC_SEND_TUNING_BLOCK))
|
||||
if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_UHS_SDR50, MMC_SEND_TUNING_BLOCK))
|
||||
return 0;
|
||||
}
|
||||
else if (status[13] & SD_MODE_UHS_SDR12) /* High-speed SDR12 is supported. */
|
||||
@@ -755,11 +755,11 @@ static int sdmmc_sd_switch_hs_low(sdmmc_device_t *device, uint8_t *status)
|
||||
return 0;
|
||||
|
||||
/* Reconfigure the internal clock. */
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_SDR12))
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_UHS_SDR12))
|
||||
return 0;
|
||||
|
||||
/* Run tuning. */
|
||||
if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_SDR12, MMC_SEND_TUNING_BLOCK))
|
||||
if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_UHS_SDR12, MMC_SEND_TUNING_BLOCK))
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
@@ -784,7 +784,7 @@ static int sdmmc_sd_switch_hs_high(sdmmc_device_t *device, uint8_t *status)
|
||||
return 0;
|
||||
|
||||
/* Reconfigure the internal clock. */
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_SDR25))
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_UHS_SDR25))
|
||||
return 0;
|
||||
|
||||
/* Peek the SD card's status. */
|
||||
@@ -841,7 +841,7 @@ int sdmmc_device_sd_init(sdmmc_device_t *device, sdmmc_t *sdmmc, SdmmcBusWidth b
|
||||
memset(device, 0, sizeof(sdmmc_device_t));
|
||||
|
||||
/* Try to initialize the driver. */
|
||||
if (!sdmmc_init(sdmmc, SDMMC_1, SDMMC_VOLTAGE_3V3, SDMMC_BUS_WIDTH_1BIT, SDMMC_SPEED_INIT_SDR))
|
||||
if (!sdmmc_init(sdmmc, SDMMC_1, SDMMC_VOLTAGE_3V3, SDMMC_BUS_WIDTH_1BIT, SDMMC_SPEED_SD_INIT))
|
||||
{
|
||||
sdmmc_error(sdmmc, "Failed to initialize the SDMMC driver!");
|
||||
return 0;
|
||||
@@ -874,7 +874,7 @@ int sdmmc_device_sd_init(sdmmc_device_t *device, sdmmc_t *sdmmc, SdmmcBusWidth b
|
||||
sdmmc_info(sdmmc, "Sent if cond to SD card!");
|
||||
|
||||
/* Get the SD card's operating conditions. */
|
||||
if (!sdmmc_sd_send_op_cond(device, is_sd_ver2, (bus_width == SDMMC_BUS_WIDTH_4BIT) && (bus_speed == SDMMC_SPEED_SDR104)))
|
||||
if (!sdmmc_sd_send_op_cond(device, is_sd_ver2, (bus_width == SDMMC_BUS_WIDTH_4BIT) && (bus_speed == SDMMC_SPEED_UHS_SDR104)))
|
||||
{
|
||||
sdmmc_error(sdmmc, "Failed to send op cond!");
|
||||
return 0;
|
||||
@@ -920,7 +920,7 @@ int sdmmc_device_sd_init(sdmmc_device_t *device, sdmmc_t *sdmmc, SdmmcBusWidth b
|
||||
if (!device->is_180v)
|
||||
{
|
||||
/* Reconfigure the internal clock. */
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_UNK6))
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_SD_LEGACY))
|
||||
{
|
||||
sdmmc_error(sdmmc, "Failed to apply the correct bus speed!");
|
||||
return 0;
|
||||
@@ -998,7 +998,7 @@ int sdmmc_device_sd_init(sdmmc_device_t *device, sdmmc_t *sdmmc, SdmmcBusWidth b
|
||||
|
||||
sdmmc_info(sdmmc, "Switched to high-speed from low voltage!");
|
||||
}
|
||||
else if ((device->scr.sda_vsn & (SD_SCR_SPEC_VER_1 | SD_SCR_SPEC_VER_2)) && ((bus_speed != SDMMC_SPEED_UNK6)))
|
||||
else if ((device->scr.sda_vsn & (SD_SCR_SPEC_VER_1 | SD_SCR_SPEC_VER_2)) && ((bus_speed != SDMMC_SPEED_SD_LEGACY)))
|
||||
{
|
||||
/* Switch to high-speed from high voltage (if possible). */
|
||||
if (!sdmmc_sd_switch_hs_high(device, switch_status))
|
||||
@@ -1273,7 +1273,7 @@ static int sdmmc_mmc_select_hs(sdmmc_device_t *device, bool ignore_status)
|
||||
return 0;
|
||||
|
||||
/* Reconfigure the internal clock. */
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_HS52))
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_MMC_HS))
|
||||
return 0;
|
||||
|
||||
/* Check the status if necessary. */
|
||||
@@ -1292,11 +1292,11 @@ static int sdmmc_mmc_select_hs200(sdmmc_device_t *device)
|
||||
return 0;
|
||||
|
||||
/* Reconfigure the internal clock. */
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_HS200))
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_MMC_HS200))
|
||||
return 0;
|
||||
|
||||
/* Execute tuning procedure. */
|
||||
if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_HS200, MMC_SEND_TUNING_BLOCK_HS200))
|
||||
if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_MMC_HS200, MMC_SEND_TUNING_BLOCK_HS200))
|
||||
return 0;
|
||||
|
||||
/* Peek the current status. */
|
||||
@@ -1331,7 +1331,7 @@ static int sdmmc_mmc_select_hs400(sdmmc_device_t *device)
|
||||
return 0;
|
||||
|
||||
/* Reconfigure the internal clock. */
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_HS400))
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_MMC_HS400))
|
||||
return 0;
|
||||
|
||||
/* Peek the current status. */
|
||||
@@ -1340,14 +1340,14 @@ static int sdmmc_mmc_select_hs400(sdmmc_device_t *device)
|
||||
|
||||
static int sdmmc_mmc_select_timing(sdmmc_device_t *device, SdmmcBusSpeed bus_speed)
|
||||
{
|
||||
if ((bus_speed == SDMMC_SPEED_HS400) &&
|
||||
if ((bus_speed == SDMMC_SPEED_MMC_HS400) &&
|
||||
(device->sdmmc->bus_width == SDMMC_BUS_WIDTH_8BIT) &&
|
||||
(device->ext_csd.raw_card_type & EXT_CSD_CARD_TYPE_HS400_1_8V))
|
||||
{
|
||||
/* Switch to HS400. */
|
||||
return sdmmc_mmc_select_hs400(device);
|
||||
}
|
||||
else if (((bus_speed == SDMMC_SPEED_HS400) || (bus_speed == SDMMC_SPEED_HS200)) &&
|
||||
else if (((bus_speed == SDMMC_SPEED_MMC_HS400) || (bus_speed == SDMMC_SPEED_MMC_HS200)) &&
|
||||
((device->sdmmc->bus_width == SDMMC_BUS_WIDTH_8BIT) || (device->sdmmc->bus_width == SDMMC_BUS_WIDTH_4BIT)) &&
|
||||
(device->ext_csd.raw_card_type & EXT_CSD_CARD_TYPE_HS200_1_8V))
|
||||
{
|
||||
@@ -1397,7 +1397,7 @@ int sdmmc_device_mmc_init(sdmmc_device_t *device, sdmmc_t *sdmmc, SdmmcBusWidth
|
||||
memset(device, 0, sizeof(sdmmc_device_t));
|
||||
|
||||
/* Try to initialize the driver. */
|
||||
if (!sdmmc_init(sdmmc, SDMMC_4, SDMMC_VOLTAGE_1V8, SDMMC_BUS_WIDTH_1BIT, SDMMC_SPEED_INIT_HS))
|
||||
if (!sdmmc_init(sdmmc, SDMMC_4, SDMMC_VOLTAGE_1V8, SDMMC_BUS_WIDTH_1BIT, SDMMC_SPEED_MMC_INIT))
|
||||
{
|
||||
sdmmc_error(sdmmc, "Failed to initialize the SDMMC driver!");
|
||||
return 0;
|
||||
@@ -1464,7 +1464,7 @@ int sdmmc_device_mmc_init(sdmmc_device_t *device, sdmmc_t *sdmmc, SdmmcBusWidth
|
||||
sdmmc_warn(sdmmc, "Got unknown CSD structure (0x%08x)!", device->csd.structure);
|
||||
|
||||
/* Reconfigure the internal clock. */
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_HS26))
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_MMC_LEGACY))
|
||||
{
|
||||
sdmmc_error(sdmmc, "Failed to apply the correct bus speed!");
|
||||
return 0;
|
||||
|
||||
@@ -289,26 +289,28 @@ static int sdmmc_get_sdclk_freq(SdmmcBusSpeed bus_speed)
|
||||
{
|
||||
switch (bus_speed)
|
||||
{
|
||||
case SDMMC_SPEED_INIT_HS:
|
||||
case SDMMC_SPEED_HS26:
|
||||
case SDMMC_SPEED_MMC_INIT:
|
||||
case SDMMC_SPEED_MMC_LEGACY:
|
||||
return 26000;
|
||||
case SDMMC_SPEED_HS52:
|
||||
case SDMMC_SPEED_MMC_HS:
|
||||
return 52000;
|
||||
case SDMMC_SPEED_HS200:
|
||||
case SDMMC_SPEED_HS400:
|
||||
case SDMMC_SPEED_SDR104:
|
||||
case SDMMC_SPEED_MMC_HS200:
|
||||
case SDMMC_SPEED_MMC_HS400:
|
||||
case SDMMC_SPEED_UHS_SDR104:
|
||||
case SDMMC_SPEED_EMU_SDR104:
|
||||
return 200000;
|
||||
case SDMMC_SPEED_INIT_SDR:
|
||||
case SDMMC_SPEED_UNK6:
|
||||
case SDMMC_SPEED_SDR12:
|
||||
case SDMMC_SPEED_SD_INIT:
|
||||
case SDMMC_SPEED_SD_LEGACY:
|
||||
case SDMMC_SPEED_UHS_SDR12:
|
||||
return 25000;
|
||||
case SDMMC_SPEED_SDR25:
|
||||
case SDMMC_SPEED_SD_HS:
|
||||
case SDMMC_SPEED_UHS_SDR25:
|
||||
return 50000;
|
||||
case SDMMC_SPEED_SDR50:
|
||||
case SDMMC_SPEED_UHS_SDR50:
|
||||
return 100000;
|
||||
case SDMMC_SPEED_DDR50:
|
||||
case SDMMC_SPEED_UHS_DDR50:
|
||||
return 40800;
|
||||
case SDMMC_SPEED_UNK14:
|
||||
case SDMMC_SPEED_MMC_DDR52:
|
||||
return 200000;
|
||||
default:
|
||||
return 0;
|
||||
@@ -320,22 +322,23 @@ static int sdmmc_get_sdclk_div(SdmmcBusSpeed bus_speed)
|
||||
{
|
||||
switch (bus_speed)
|
||||
{
|
||||
case SDMMC_SPEED_INIT_HS:
|
||||
case SDMMC_SPEED_MMC_INIT:
|
||||
return 66;
|
||||
case SDMMC_SPEED_INIT_SDR:
|
||||
// TODO: TRM says return 64?
|
||||
case SDMMC_SPEED_HS26:
|
||||
case SDMMC_SPEED_HS52:
|
||||
case SDMMC_SPEED_HS200:
|
||||
case SDMMC_SPEED_HS400:
|
||||
case SDMMC_SPEED_UNK6:
|
||||
case SDMMC_SPEED_SDR25:
|
||||
case SDMMC_SPEED_SDR12:
|
||||
case SDMMC_SPEED_SDR50:
|
||||
case SDMMC_SPEED_SDR104:
|
||||
case SDMMC_SPEED_DDR50:
|
||||
case SDMMC_SPEED_SD_INIT:
|
||||
case SDMMC_SPEED_MMC_LEGACY:
|
||||
case SDMMC_SPEED_MMC_HS:
|
||||
case SDMMC_SPEED_MMC_HS200:
|
||||
case SDMMC_SPEED_MMC_HS400:
|
||||
case SDMMC_SPEED_SD_LEGACY:
|
||||
case SDMMC_SPEED_SD_HS:
|
||||
case SDMMC_SPEED_UHS_SDR12:
|
||||
case SDMMC_SPEED_UHS_SDR25:
|
||||
case SDMMC_SPEED_UHS_SDR50:
|
||||
case SDMMC_SPEED_UHS_SDR104:
|
||||
case SDMMC_SPEED_UHS_DDR50:
|
||||
case SDMMC_SPEED_EMU_SDR104:
|
||||
return 1;
|
||||
case SDMMC_SPEED_UNK14:
|
||||
case SDMMC_SPEED_MMC_DDR52:
|
||||
return 2;
|
||||
default:
|
||||
return 0;
|
||||
@@ -354,35 +357,35 @@ static int sdmmc_clk_set_source(SdmmcControllerNum controller, uint32_t clk_freq
|
||||
{
|
||||
case 25000:
|
||||
out_freq = 24728;
|
||||
car_div = SDMMC_CAR_DIVIDER_SDR12;
|
||||
car_div = SDMMC_CAR_DIVIDER_UHS_SDR12;
|
||||
break;
|
||||
case 26000:
|
||||
out_freq = 25500;
|
||||
car_div = SDMMC_CAR_DIVIDER_HS26;
|
||||
car_div = SDMMC_CAR_DIVIDER_MMC_LEGACY;
|
||||
break;
|
||||
case 40800:
|
||||
out_freq = 40800;
|
||||
car_div = SDMMC_CAR_DIVIDER_DDR50;
|
||||
car_div = SDMMC_CAR_DIVIDER_UHS_DDR50;
|
||||
break;
|
||||
case 50000:
|
||||
out_freq = 48000;
|
||||
car_div = SDMMC_CAR_DIVIDER_SDR25;
|
||||
car_div = SDMMC_CAR_DIVIDER_UHS_SDR25;
|
||||
break;
|
||||
case 52000:
|
||||
out_freq = 51000;
|
||||
car_div = SDMMC_CAR_DIVIDER_HS52;
|
||||
car_div = SDMMC_CAR_DIVIDER_MMC_HS;
|
||||
break;
|
||||
case 100000:
|
||||
out_freq = 90667;
|
||||
car_div = SDMMC_CAR_DIVIDER_SDR50;
|
||||
car_div = SDMMC_CAR_DIVIDER_UHS_SDR50;
|
||||
break;
|
||||
case 200000:
|
||||
out_freq = 163200;
|
||||
car_div = SDMMC_CAR_DIVIDER_HS200;
|
||||
car_div = SDMMC_CAR_DIVIDER_MMC_HS200;
|
||||
break;
|
||||
case 208000:
|
||||
out_freq = 204000;
|
||||
car_div = SDMMC_CAR_DIVIDER_SDR104;
|
||||
car_div = SDMMC_CAR_DIVIDER_UHS_SDR104;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
@@ -747,7 +750,7 @@ void sdmmc_select_voltage(sdmmc_t *sdmmc, SdmmcBusVoltage voltage)
|
||||
|
||||
static void sdmmc_tap_config(sdmmc_t *sdmmc, SdmmcBusSpeed bus_speed)
|
||||
{
|
||||
if (bus_speed == SDMMC_SPEED_HS400)
|
||||
if (bus_speed == SDMMC_SPEED_MMC_HS400)
|
||||
{
|
||||
/* Clear and set DQS_TRIM_VAL (used in HS400) */
|
||||
sdmmc->regs->vendor_cap_overrides &= ~(0x3F00);
|
||||
@@ -757,7 +760,7 @@ static void sdmmc_tap_config(sdmmc_t *sdmmc, SdmmcBusSpeed bus_speed)
|
||||
/* Clear TAP_VAL_UPDATED_BY_HW */
|
||||
sdmmc->regs->vendor_tuning_cntrl0 &= ~(0x20000);
|
||||
|
||||
if (bus_speed == SDMMC_SPEED_HS400)
|
||||
if (bus_speed == SDMMC_SPEED_MMC_HS400)
|
||||
{
|
||||
/* We must have obtained the tap value from the tuning procedure here. */
|
||||
if (sdmmc->is_tuning_tap_val_set)
|
||||
@@ -863,41 +866,43 @@ int sdmmc_select_speed(sdmmc_t *sdmmc, SdmmcBusSpeed bus_speed)
|
||||
/* Set the appropriate host speed. */
|
||||
switch (bus_speed) {
|
||||
/* 400kHz initialization mode and a few others. */
|
||||
case SDMMC_SPEED_INIT_HS:
|
||||
case SDMMC_SPEED_HS26:
|
||||
case SDMMC_SPEED_INIT_SDR:
|
||||
case SDMMC_SPEED_UNK6:
|
||||
case SDMMC_SPEED_MMC_INIT:
|
||||
case SDMMC_SPEED_MMC_LEGACY:
|
||||
case SDMMC_SPEED_SD_INIT:
|
||||
case SDMMC_SPEED_SD_LEGACY:
|
||||
sdmmc->regs->host_control &= ~(SDHCI_CTRL_HISPD);
|
||||
sdmmc->regs->host_control2 &= ~(SDHCI_CTRL_VDD_180);
|
||||
break;
|
||||
|
||||
/* 50MHz high speed (SD) and 52MHz high speed (MMC). */
|
||||
case SDMMC_SPEED_SDR25:
|
||||
case SDMMC_SPEED_HS52:
|
||||
case SDMMC_SPEED_SD_HS:
|
||||
case SDMMC_SPEED_MMC_HS:
|
||||
case SDMMC_SPEED_UHS_SDR25:
|
||||
sdmmc->regs->host_control |= SDHCI_CTRL_HISPD;
|
||||
sdmmc->regs->host_control2 &= ~(SDHCI_CTRL_VDD_180);
|
||||
break;
|
||||
|
||||
/* 200MHz UHS-I (SD) and other modes due to errata. */
|
||||
case SDMMC_SPEED_HS200:
|
||||
case SDMMC_SPEED_SDR104:
|
||||
case SDMMC_SPEED_DDR50:
|
||||
case SDMMC_SPEED_SDR50:
|
||||
case SDMMC_SPEED_UNK14:
|
||||
case SDMMC_SPEED_MMC_HS200:
|
||||
case SDMMC_SPEED_UHS_SDR104:
|
||||
case SDMMC_SPEED_UHS_DDR50:
|
||||
case SDMMC_SPEED_UHS_SDR50:
|
||||
case SDMMC_SPEED_MMC_DDR52:
|
||||
case SDMMC_SPEED_EMU_SDR104:
|
||||
sdmmc->regs->host_control2 &= ~(SDHCI_CTRL_UHS_MASK);
|
||||
sdmmc->regs->host_control2 |= SDHCI_CTRL_UHS_SDR104;
|
||||
sdmmc->regs->host_control2 |= SDHCI_CTRL_VDD_180;
|
||||
break;
|
||||
|
||||
/* 200MHz single-data rate (MMC). */
|
||||
case SDMMC_SPEED_HS400:
|
||||
case SDMMC_SPEED_MMC_HS400:
|
||||
sdmmc->regs->host_control2 &= ~(SDHCI_CTRL_UHS_MASK);
|
||||
sdmmc->regs->host_control2 |= SDHCI_CTRL_HS400;
|
||||
sdmmc->regs->host_control2 |= SDHCI_CTRL_VDD_180;
|
||||
break;
|
||||
|
||||
/* 25MHz default speed (SD). */
|
||||
case SDMMC_SPEED_SDR12:
|
||||
case SDMMC_SPEED_UHS_SDR12:
|
||||
sdmmc->regs->host_control2 &= ~(SDHCI_CTRL_UHS_MASK);
|
||||
sdmmc->regs->host_control2 |= SDHCI_CTRL_UHS_SDR12;
|
||||
sdmmc->regs->host_control2 |= SDHCI_CTRL_VDD_180;
|
||||
@@ -936,7 +941,7 @@ int sdmmc_select_speed(sdmmc_t *sdmmc, SdmmcBusSpeed bus_speed)
|
||||
sdmmc_enable_sd_clock(sdmmc);
|
||||
|
||||
/* Run DLLCAL for HS400 only */
|
||||
if (bus_speed == SDMMC_SPEED_HS400)
|
||||
if (bus_speed == SDMMC_SPEED_MMC_HS400)
|
||||
return sdmmc_dllcal_run(sdmmc);
|
||||
|
||||
return 1;
|
||||
@@ -1720,7 +1725,7 @@ int sdmmc_switch_voltage(sdmmc_t *sdmmc)
|
||||
sdmmc_disable_sd_clock(sdmmc);
|
||||
|
||||
/* Reconfigure the internal clock. */
|
||||
if (!sdmmc_select_speed(sdmmc, SDMMC_SPEED_SDR12))
|
||||
if (!sdmmc_select_speed(sdmmc, SDMMC_SPEED_UHS_SDR12))
|
||||
{
|
||||
sdmmc_error(sdmmc, "Failed to apply the correct bus speed for low voltage support!");
|
||||
return 0;
|
||||
@@ -1883,15 +1888,16 @@ int sdmmc_execute_tuning(sdmmc_t *sdmmc, SdmmcBusSpeed bus_speed, uint32_t opcod
|
||||
|
||||
switch (bus_speed)
|
||||
{
|
||||
case SDMMC_SPEED_HS200:
|
||||
case SDMMC_SPEED_HS400:
|
||||
case SDMMC_SPEED_SDR104:
|
||||
case SDMMC_SPEED_MMC_HS200:
|
||||
case SDMMC_SPEED_MMC_HS400:
|
||||
case SDMMC_SPEED_UHS_SDR104:
|
||||
case SDMMC_SPEED_EMU_SDR104:
|
||||
max_tuning_loop = 0x80;
|
||||
tuning_cntrl_flag = 0x4000;
|
||||
break;
|
||||
case SDMMC_SPEED_SDR50:
|
||||
case SDMMC_SPEED_DDR50:
|
||||
case SDMMC_SPEED_UNK14:
|
||||
case SDMMC_SPEED_UHS_SDR50:
|
||||
case SDMMC_SPEED_UHS_DDR50:
|
||||
case SDMMC_SPEED_MMC_DDR52:
|
||||
max_tuning_loop = 0x100;
|
||||
tuning_cntrl_flag = 0x8000;
|
||||
break;
|
||||
|
||||
@@ -210,33 +210,33 @@ typedef enum {
|
||||
} SdmmcBusWidth;
|
||||
|
||||
typedef enum {
|
||||
SDMMC_SPEED_INIT_HS = 0,
|
||||
SDMMC_SPEED_HS26 = 1,
|
||||
SDMMC_SPEED_HS52 = 2,
|
||||
SDMMC_SPEED_HS200 = 3,
|
||||
SDMMC_SPEED_HS400 = 4,
|
||||
SDMMC_SPEED_INIT_SDR = 5,
|
||||
SDMMC_SPEED_UNK6 = 6,
|
||||
SDMMC_SPEED_SDR25 = 7,
|
||||
SDMMC_SPEED_SDR12 = 8,
|
||||
SDMMC_SPEED_UNK9 = 9,
|
||||
SDMMC_SPEED_SDR50 = 10,
|
||||
SDMMC_SPEED_SDR104 = 11,
|
||||
SDMMC_SPEED_UNK12 = 12,
|
||||
SDMMC_SPEED_DDR50 = 13,
|
||||
SDMMC_SPEED_UNK14 = 14,
|
||||
SDMMC_SPEED_MMC_INIT = 0,
|
||||
SDMMC_SPEED_MMC_LEGACY = 1,
|
||||
SDMMC_SPEED_MMC_HS = 2,
|
||||
SDMMC_SPEED_MMC_HS200 = 3,
|
||||
SDMMC_SPEED_MMC_HS400 = 4,
|
||||
SDMMC_SPEED_SD_INIT = 5,
|
||||
SDMMC_SPEED_SD_LEGACY = 6,
|
||||
SDMMC_SPEED_SD_HS = 7,
|
||||
SDMMC_SPEED_UHS_SDR12 = 8,
|
||||
SDMMC_SPEED_UHS_SDR25 = 9,
|
||||
SDMMC_SPEED_UHS_SDR50 = 10,
|
||||
SDMMC_SPEED_UHS_SDR104 = 11,
|
||||
SDMMC_SPEED_UHS_RESERVED = 12,
|
||||
SDMMC_SPEED_UHS_DDR50 = 13,
|
||||
SDMMC_SPEED_MMC_DDR52 = 14,
|
||||
SDMMC_SPEED_EMU_SDR104 = 255, /* Custom speed mode. Prevents low voltage switch in MMC emulation. */
|
||||
} SdmmcBusSpeed;
|
||||
|
||||
typedef enum {
|
||||
SDMMC_CAR_DIVIDER_SDR12 = 31, // (16.5 * 2) - 2
|
||||
SDMMC_CAR_DIVIDER_SDR25 = 15, // (8.5 * 2) - 2
|
||||
SDMMC_CAR_DIVIDER_SDR50 = 7, // (4.5 * 2) - 2
|
||||
SDMMC_CAR_DIVIDER_SDR104 = 2, // (2 * 2) - 2
|
||||
SDMMC_CAR_DIVIDER_DDR50 = 18, // (5 * 2 * 2) - 2
|
||||
SDMMC_CAR_DIVIDER_HS26 = 30, // (16 * 2) - 2
|
||||
SDMMC_CAR_DIVIDER_HS52 = 14, // (8 * 2) - 2
|
||||
SDMMC_CAR_DIVIDER_HS200 = 3, // (2.5 * 2) - 2 (for PLLP_OUT0)
|
||||
SDMMC_CAR_DIVIDER_HS400 = 3, // (2.5 * 2) - 2 (for PLLP_OUT0)
|
||||
SDMMC_CAR_DIVIDER_UHS_SDR12 = 31, /* (16.5 * 2) - 2 */
|
||||
SDMMC_CAR_DIVIDER_UHS_SDR25 = 15, /* (8.5 * 2) - 2 */
|
||||
SDMMC_CAR_DIVIDER_UHS_SDR50 = 7, /* (4.5 * 2) - 2 */
|
||||
SDMMC_CAR_DIVIDER_UHS_SDR104 = 2, /* (2 * 2) - 2 */
|
||||
SDMMC_CAR_DIVIDER_UHS_DDR50 = 18, /* (5 * 2 * 2) - 2 */
|
||||
SDMMC_CAR_DIVIDER_MMC_LEGACY = 30, /* (16 * 2) - 2 */
|
||||
SDMMC_CAR_DIVIDER_MMC_HS = 14, /* (8 * 2) - 2 */
|
||||
SDMMC_CAR_DIVIDER_MMC_HS200 = 3, /* (2.5 * 2) - 2 (for PLLP_OUT0, same as HS400) */
|
||||
} SdmmcCarDivider;
|
||||
|
||||
/* Structure for describing a SDMMC device. */
|
||||
@@ -272,7 +272,7 @@ typedef struct {
|
||||
uint32_t opcode;
|
||||
uint32_t arg;
|
||||
uint32_t resp[4];
|
||||
uint32_t flags; /* expected response type */
|
||||
uint32_t flags; /* Expected response type. */
|
||||
} sdmmc_command_t;
|
||||
|
||||
/* Structure for describing a SDMMC request. */
|
||||
|
||||
@@ -52,4 +52,4 @@ typedef struct {
|
||||
const char *stage2_get_program_path(void);
|
||||
void load_stage2(const char *bct0);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -89,7 +89,7 @@ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir)) \
|
||||
$(AMS)/exosphere $(AMS)/exosphere/lp0fw $(AMS)/exosphere/rebootstub \
|
||||
$(AMS)/thermosphere $(AMS)/fusee/fusee-primary $(AMS)/sept/sept-primary \
|
||||
$(AMS)/sept/sept-secondary $(KIPDIRS)
|
||||
$(AMS)/sept/sept-secondary $(AMS)/emummc $(KIPDIRS)
|
||||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
@@ -99,7 +99,7 @@ SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
KIPFILES := loader.kip pm.kip sm.kip ams_mitm.kip spl.kip boot.kip
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) fusee-primary.bin \
|
||||
exosphere.bin lp0fw.bin rebootstub.bin thermosphere.bin splash_screen.bmp \
|
||||
sept-primary.bin sept-secondary.enc \
|
||||
sept-primary.bin sept-secondary.enc emummc.kip \
|
||||
$(KIPFILES)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
@@ -128,7 +128,7 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
.PHONY: $(BUILD) clean all
|
||||
.PHONY: check_fusee_primary check_exosphere check_sept check_thermosphere check_stratosphere
|
||||
.PHONY: check_fusee_primary check_exosphere check_sept check_emummc check_thermosphere check_stratosphere
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(BUILD)
|
||||
@@ -142,6 +142,9 @@ check_exosphere:
|
||||
check_sept:
|
||||
@$(MAKE) -C $(AMS)/sept all
|
||||
|
||||
check_emummc:
|
||||
@$(MAKE) -C $(AMS)/emummc EMUMMCDIR=$(AMS)/emummc all
|
||||
|
||||
check_thermosphere:
|
||||
@$(MAKE) -C $(AMS)/thermosphere all
|
||||
|
||||
@@ -149,7 +152,7 @@ check_stratosphere:
|
||||
@$(MAKE) -C $(AMS)/stratosphere all
|
||||
|
||||
|
||||
$(BUILD): check_fusee_primary check_exosphere check_sept check_thermosphere check_stratosphere
|
||||
$(BUILD): check_fusee_primary check_exosphere check_sept check_emummc check_thermosphere check_stratosphere
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
@@ -161,6 +164,7 @@ clean:
|
||||
@$(MAKE) -C $(AMS)/thermosphere clean
|
||||
@$(MAKE) -C $(AMS)/stratosphere clean
|
||||
@$(MAKE) -C $(AMS)/sept clean
|
||||
@$(MAKE) -C $(AMS)/emummc clean
|
||||
@rm -fr $(BUILD) $(TARGET).bin $(TARGET).elf
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
@@ -246,4 +246,6 @@ SECTIONS
|
||||
PROVIDE(__splash_screen_bmp_size__ = splash_screen_bmp_end - splash_screen_bmp);
|
||||
PROVIDE(__thermosphere_bin_start__ = thermosphere_bin - __start__);
|
||||
PROVIDE(__thermosphere_bin_size__ = thermosphere_bin_end - thermosphere_bin);
|
||||
PROVIDE(__emummc_kip_start__ = emummc_kip - __start__);
|
||||
PROVIDE(__emummc_kip_size__ = emummc_kip_end - emummc_kip);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
* 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include "device_partition.h"
|
||||
|
||||
@@ -26,7 +27,7 @@ int device_partition_read_data(device_partition_t *devpart, void *dst, uint64_t
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
if (devpart->read_cipher != NULL && devpart->crypto_mode != DevicePartitionCryptoMode_None) {
|
||||
if ((devpart->read_cipher != NULL) && (devpart->crypto_mode != DevicePartitionCryptoMode_None)) {
|
||||
for (uint64_t i = 0; i < num_sectors; i += devpart->crypto_work_buffer_num_sectors) {
|
||||
uint64_t n = (i + devpart->crypto_work_buffer_num_sectors > num_sectors) ? (num_sectors - i) : devpart->crypto_work_buffer_num_sectors;
|
||||
rc = devpart->reader(devpart, devpart->crypto_work_buffer, sector + i, n);
|
||||
@@ -54,7 +55,7 @@ int device_partition_write_data(device_partition_t *devpart, const void *src, ui
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
if (devpart->read_cipher != NULL && devpart->crypto_mode != DevicePartitionCryptoMode_None) {
|
||||
if ((devpart->write_cipher != NULL) && (devpart->crypto_mode != DevicePartitionCryptoMode_None)) {
|
||||
for (uint64_t i = 0; i < num_sectors; i += devpart->crypto_work_buffer_num_sectors) {
|
||||
uint64_t n = (i + devpart->crypto_work_buffer_num_sectors > num_sectors) ? (num_sectors - i) : devpart->crypto_work_buffer_num_sectors;
|
||||
memcpy(devpart->crypto_work_buffer, src + (size_t)(devpart->sector_size * i), (size_t)(devpart->sector_size * n));
|
||||
@@ -72,3 +73,149 @@ int device_partition_write_data(device_partition_t *devpart, const void *src, ui
|
||||
return devpart->writer(devpart, src, sector, 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 num_parts, uint64_t part_limit)
|
||||
{
|
||||
int rc = 0;
|
||||
uint64_t target_sector = sector;
|
||||
char target_path[0x300 + 1] = {0};
|
||||
|
||||
/* Perform initialization steps, if necessary. */
|
||||
if (!devpart->initialized) {
|
||||
rc = devpart->initializer(devpart);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
/* Prepare the right file path if using file mode. */
|
||||
if (devpart->emu_use_file && (origin_path != NULL)) {
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
/* Treat the path as a folder with each part inside. */
|
||||
snprintf(target_path, sizeof(target_path) - 1, "%s/%02d", origin_path, target_part);
|
||||
} else {
|
||||
target_sector = sector;
|
||||
strcpy(target_path, origin_path);
|
||||
}
|
||||
|
||||
/* Update the target file path. */
|
||||
devpart->emu_file_path = target_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. */
|
||||
rc = devpart->reader(devpart, devpart->crypto_work_buffer, target_sector + i, n);
|
||||
|
||||
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. */
|
||||
rc = devpart->reader(devpart, dst, target_sector, num_sectors);
|
||||
}
|
||||
|
||||
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 num_parts, uint64_t part_limit)
|
||||
{
|
||||
int rc = 0;
|
||||
uint64_t target_sector = sector;
|
||||
char target_path[0x300 + 1] = {0};
|
||||
|
||||
/* Perform initialization steps, if necessary. */
|
||||
if (!devpart->initialized) {
|
||||
rc = devpart->initializer(devpart);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
/* Prepare the right file path if using file mode. */
|
||||
if (devpart->emu_use_file && (origin_path != NULL)) {
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
/* Treat the path as a folder with each part inside. */
|
||||
snprintf(target_path, sizeof(target_path) - 1, "%s/%02d", origin_path, target_part);
|
||||
} else {
|
||||
target_sector = sector;
|
||||
strcpy(target_path, origin_path);
|
||||
}
|
||||
|
||||
/* Update the target file path. */
|
||||
devpart->emu_file_path = target_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. */
|
||||
rc = devpart->writer(devpart, devpart->crypto_work_buffer, target_sector + i, n);
|
||||
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Write partition data. */
|
||||
rc = devpart->writer(devpart, src, target_sector, num_sectors);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -65,9 +65,14 @@ typedef struct device_partition_t {
|
||||
uint8_t __attribute__((aligned(16))) keys[DEVPART_KEY_MAX][DEVPART_KEY_MAX_SIZE]; /* Key. */
|
||||
uint8_t __attribute__((aligned(16))) iv[DEVPART_IV_MAX_SIZE]; /* IV. */
|
||||
bool initialized;
|
||||
|
||||
char *emu_file_path; /* Emulated device file path. */
|
||||
bool emu_use_file;
|
||||
} device_partition_t;
|
||||
|
||||
int device_partition_read_data(device_partition_t *devpart, void *dst, uint64_t sector, uint64_t num_sectors);
|
||||
int device_partition_write_data(device_partition_t *devpart, const void *src, uint64_t sector, uint64_t num_sectors);
|
||||
int emu_device_partition_read_data(device_partition_t *devpart, void *dst, uint64_t sector, uint64_t num_sectors, const char *origin_path, int num_parts, uint64_t part_limit);
|
||||
int emu_device_partition_write_data(device_partition_t *devpart, const void *src, uint64_t sector, uint64_t num_sectors, const char *origin_path, int num_parts, uint64_t part_limit);
|
||||
|
||||
#endif
|
||||
|
||||
563
fusee/fusee-secondary/src/emu_dev.c
Normal file
563
fusee/fusee-secondary/src/emu_dev.c
Normal file
@@ -0,0 +1,563 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2019 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"
|
||||
#include "utils.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;
|
||||
|
||||
char origin_path[0x300+1];
|
||||
int num_parts;
|
||||
uint64_t part_limit;
|
||||
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 device_partition_t *devpart, const char *origin_path) {
|
||||
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, ":/");
|
||||
|
||||
/* Copy the file path for file mode. */
|
||||
if (devpart->emu_use_file)
|
||||
strcpy(device->origin_path, origin_path);
|
||||
|
||||
device->num_parts = 0;
|
||||
device->part_limit = 0;
|
||||
|
||||
device->devoptab.name = device->name;
|
||||
device->devoptab.deviceData = device;
|
||||
|
||||
/* Initialize immediately. */
|
||||
int rc = device->devpart.initializer(&device->devpart);
|
||||
if (rc != 0) {
|
||||
errno = rc;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Allocate memory for our intermediate sector. */
|
||||
device->tmp_sector = (uint8_t *)malloc(devpart->sector_size);
|
||||
if (device->tmp_sector == NULL) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
device->setup = true;
|
||||
device->registered = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int emudev_mount_device_multipart(const char *name, const device_partition_t *devpart, const char *origin_path, int num_parts, uint64_t part_limit) {
|
||||
emudev_device_t *device = NULL;
|
||||
|
||||
if (name[0] == '\0' || devpart == NULL) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strlen(name) > 32) {
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
if (emudev_find_device(name) != NULL) {
|
||||
errno = EEXIST; /* Device already exists */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Invalid number of parts. */
|
||||
if (num_parts <= 1) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Part limit is invalid. */
|
||||
if ((part_limit % (1ull << 30)) != 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Find an unused slot. */
|
||||
for (size_t i = 0; i < EMUDEV_MAX_DEVICES; i++) {
|
||||
if (!g_emudev_devices[i].setup) {
|
||||
device = &g_emudev_devices[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (device == NULL) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(device, 0, sizeof(emudev_device_t));
|
||||
device->devoptab = g_emudev_devoptab;
|
||||
device->devpart = *devpart;
|
||||
strcpy(device->name, name);
|
||||
strcpy(device->root_path, name);
|
||||
strcat(device->root_path, ":/");
|
||||
strcpy(device->origin_path, origin_path);
|
||||
device->num_parts = num_parts;
|
||||
device->part_limit = part_limit;
|
||||
|
||||
device->devoptab.name = device->name;
|
||||
device->devoptab.deviceData = device;
|
||||
|
||||
/* Initialize immediately. */
|
||||
int rc = device->devpart.initializer(&device->devpart);
|
||||
if (rc != 0) {
|
||||
errno = rc;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Allocate memory for our intermediate sector. */
|
||||
device->tmp_sector = (uint8_t *)malloc(devpart->sector_size);
|
||||
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;
|
||||
}
|
||||
|
||||
free(device->tmp_sector);
|
||||
device->devpart.finalizer(&device->devpart);
|
||||
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));
|
||||
no = emu_device_partition_read_data(&device->devpart, device->tmp_sector, sector_begin, 1, device->origin_path, device->num_parts, device->part_limit);
|
||||
if (no != 0) {
|
||||
r->_errno = no;
|
||||
return -1;
|
||||
}
|
||||
|
||||
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, device->num_parts, device->part_limit);
|
||||
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) {
|
||||
no = emu_device_partition_write_data(&device->devpart, data, current_sector, sector_end_aligned - current_sector, device->origin_path, device->num_parts, device->part_limit);
|
||||
if (no != 0) {
|
||||
r->_errno = no;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
no = emu_device_partition_read_data(&device->devpart, device->tmp_sector, sector_end_aligned, 1, device->origin_path, device->num_parts, device->part_limit);
|
||||
if (no != 0) {
|
||||
r->_errno = no;
|
||||
return -1;
|
||||
}
|
||||
|
||||
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, device->num_parts, device->part_limit);
|
||||
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));
|
||||
no = emu_device_partition_read_data(&device->devpart, device->tmp_sector, sector_begin, 1, device->origin_path, device->num_parts, device->part_limit);
|
||||
if (no != 0) {
|
||||
r->_errno = no;
|
||||
return -1;
|
||||
}
|
||||
|
||||
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) {
|
||||
no = emu_device_partition_read_data(&device->devpart, data, current_sector, sector_end_aligned - current_sector, device->origin_path, device->num_parts, device->part_limit);
|
||||
if (no != 0) {
|
||||
r->_errno = no;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
no = emu_device_partition_read_data(&device->devpart, device->tmp_sector, sector_end_aligned, 1, device->origin_path, device->num_parts, device->part_limit);
|
||||
if (no != 0) {
|
||||
r->_errno = no;
|
||||
return -1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
35
fusee/fusee-secondary/src/emu_dev.h
Normal file
35
fusee/fusee-secondary/src/emu_dev.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2019 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 device_partition_t *devpart, const char *origin_path);
|
||||
int emudev_mount_device_multipart(const char *name, const device_partition_t *devpart, const char *origin_path, int num_parts, uint64_t part_limit);
|
||||
int emudev_register_device(const char *name);
|
||||
int emudev_unregister_device(const char *name);
|
||||
int emudev_unmount_device(const char *name); /* also unregisters. */
|
||||
|
||||
int emudev_unmount_all(void);
|
||||
|
||||
#endif
|
||||
106
fusee/fusee-secondary/src/emummc_cfg.h
Normal file
106
fusee/fusee-secondary/src/emummc_cfg.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2019 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 EXOSPHERE_EMUMMC_CONFIG_H
|
||||
#define EXOSPHERE_EMUMMC_CONFIG_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <atmosphere.h>
|
||||
|
||||
/* "EFS0" */
|
||||
#define MAGIC_EMUMMC_CONFIG (0x30534645)
|
||||
|
||||
#define EMUMMC_FILE_PATH_MAX (0x80)
|
||||
|
||||
typedef enum {
|
||||
EMUMMC_TYPE_NONE = 0,
|
||||
EMUMMC_TYPE_PARTITION = 1,
|
||||
EMUMMC_TYPE_FILES = 2,
|
||||
} emummc_type_t;
|
||||
|
||||
typedef enum {
|
||||
EMUMMC_MMC_NAND = 0,
|
||||
EMUMMC_MMC_SD = 1,
|
||||
EMUMMC_MMC_GC = 2,
|
||||
} emummc_mmc_t;
|
||||
|
||||
typedef enum {
|
||||
FS_VER_1_0_0 = 0,
|
||||
|
||||
FS_VER_2_0_0,
|
||||
FS_VER_2_0_0_EXFAT,
|
||||
|
||||
FS_VER_2_1_0,
|
||||
FS_VER_2_1_0_EXFAT,
|
||||
|
||||
FS_VER_3_0_0,
|
||||
FS_VER_3_0_0_EXFAT,
|
||||
|
||||
FS_VER_3_0_1,
|
||||
FS_VER_3_0_1_EXFAT,
|
||||
|
||||
FS_VER_4_0_0,
|
||||
FS_VER_4_0_0_EXFAT,
|
||||
|
||||
FS_VER_4_1_0,
|
||||
FS_VER_4_1_0_EXFAT,
|
||||
|
||||
FS_VER_5_0_0,
|
||||
FS_VER_5_0_0_EXFAT,
|
||||
|
||||
FS_VER_5_1_0,
|
||||
FS_VER_5_1_0_EXFAT,
|
||||
|
||||
FS_VER_6_0_0,
|
||||
FS_VER_6_0_0_EXFAT,
|
||||
|
||||
FS_VER_7_0_0,
|
||||
FS_VER_7_0_0_EXFAT,
|
||||
|
||||
FS_VER_8_0_0,
|
||||
FS_VER_8_0_0_EXFAT,
|
||||
|
||||
FS_VER_MAX,
|
||||
} emummc_fs_ver_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint32_t magic;
|
||||
uint32_t type;
|
||||
uint32_t id;
|
||||
uint32_t fs_version;
|
||||
} emummc_base_config_t;
|
||||
|
||||
typedef struct {
|
||||
uint64_t start_sector;
|
||||
} emummc_partition_config_t;
|
||||
|
||||
typedef struct {
|
||||
char path[EMUMMC_FILE_PATH_MAX];
|
||||
} emummc_file_config_t;
|
||||
|
||||
typedef struct {
|
||||
emummc_base_config_t base_cfg;
|
||||
union {
|
||||
emummc_partition_config_t partition_cfg;
|
||||
emummc_file_config_t file_cfg;
|
||||
};
|
||||
char emu_dir_path[EMUMMC_FILE_PATH_MAX];
|
||||
} exo_emummc_config_t;
|
||||
|
||||
_Static_assert(sizeof(exo_emummc_config_t) == 0x110, "exo_emummc_config_t definition!");
|
||||
|
||||
#endif
|
||||
@@ -17,7 +17,9 @@
|
||||
#ifndef FUSEE_EXOSPHERE_CONFIG_H
|
||||
#define FUSEE_EXOSPHERE_CONFIG_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <atmosphere.h>
|
||||
#include "emummc_cfg.h"
|
||||
|
||||
/* This serves to set configuration for *exosphere itself*, separate from the SecMon Exosphere mimics. */
|
||||
|
||||
@@ -31,12 +33,15 @@
|
||||
#define EXOSPHERE_FLAGS_DEFAULT (EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV)
|
||||
|
||||
typedef struct {
|
||||
unsigned int magic;
|
||||
unsigned int target_firmware;
|
||||
unsigned int flags;
|
||||
unsigned int reserved;
|
||||
uint32_t magic;
|
||||
uint32_t target_firmware;
|
||||
uint32_t flags;
|
||||
uint32_t reserved[5];
|
||||
exo_emummc_config_t emummc_cfg;
|
||||
} exosphere_config_t;
|
||||
|
||||
_Static_assert(sizeof(exosphere_config_t) == 0x20 + sizeof(exo_emummc_config_t), "exosphere config definition");
|
||||
|
||||
#define MAILBOX_EXOSPHERE_CONFIGURATION ((volatile exosphere_config_t *)(0x8000F000ull))
|
||||
|
||||
#define EXOSPHERE_TARGETFW_KEY "target_firmware"
|
||||
|
||||
@@ -246,6 +246,19 @@ int fsdev_set_default_device(const char *name) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fsdev_is_exfat(const char *name) {
|
||||
fsdev_device_t *device = fsdev_find_device(name);
|
||||
|
||||
if (device != NULL) {
|
||||
if (device->registered) {
|
||||
return ((device->fatfs.fs_type == FS_EXFAT) ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fsdev_unmount_device(const char *name) {
|
||||
int ret;
|
||||
char drname[40];
|
||||
@@ -287,6 +300,21 @@ int fsdev_unmount_all(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fsdev_set_attr(const char *file, int attr, int mask) {
|
||||
return fsdev_convert_rc(NULL, f_chmod(file, (BYTE)attr, (BYTE)mask));
|
||||
}
|
||||
|
||||
int fsdev_get_attr(const char *file) {
|
||||
FILINFO info;
|
||||
FRESULT rc = f_stat(file, &info);
|
||||
|
||||
if (rc == FR_OK) {
|
||||
return info.fattrib;
|
||||
}
|
||||
|
||||
return fsdev_convert_rc(NULL, rc);
|
||||
}
|
||||
|
||||
/* Adapted from https://github.com/benemorius/openOBC-devboard/blob/bf0a4a33e22d24e7c299f921d185da27377310e0/lib/fatfs/FatFS.cpp#L173 */
|
||||
static int fsdev_convert_rc(struct _reent *r, FRESULT rc) {
|
||||
int errornumber;
|
||||
|
||||
@@ -24,10 +24,14 @@
|
||||
|
||||
int fsdev_mount_device(const char *name, const device_partition_t *devpart, bool initialize_immediately);
|
||||
int fsdev_register_device(const char *name);
|
||||
int fsdev_set_default_device(const char *name); /* must be registered. */
|
||||
|
||||
int fsdev_unregister_device(const char *name);
|
||||
int fsdev_unmount_device(const char *name); /* also unregisters. */
|
||||
int fsdev_unmount_device(const char *name); /* also unregisters. */
|
||||
|
||||
int fsdev_set_attr(const char *file, int attr, int mask); /* Non-standard function to set file DOS attributes. */
|
||||
int fsdev_get_attr(const char *file); /* Non-standard function to get file DOS attributes. */
|
||||
|
||||
int fsdev_set_default_device(const char *name); /* must be registered. */
|
||||
int fsdev_is_exfat(const char *name); /* must be registered. */
|
||||
|
||||
int fsdev_unmount_all(void);
|
||||
|
||||
|
||||
@@ -48,3 +48,21 @@ size_t dump_to_file(const void *src, size_t src_size, const char *filename) {
|
||||
return sz;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_valid_folder(const char *path) {
|
||||
struct stat st;
|
||||
if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_valid_file(const char *path) {
|
||||
struct stat st;
|
||||
if (stat(path, &st) == 0 && S_ISREG(st.st_mode)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -18,10 +18,11 @@
|
||||
#define FUSEE_FS_UTILS_H
|
||||
|
||||
#include "utils.h"
|
||||
#include "sdmmc/sdmmc.h"
|
||||
|
||||
size_t get_file_size(const char *filename);
|
||||
size_t read_from_file(void *dst, size_t dst_size, const char *filename);
|
||||
size_t dump_to_file(const void *src, size_t src_size, const char *filename);
|
||||
bool is_valid_folder(const char *path);
|
||||
bool is_valid_file(const char *path);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -112,3 +112,42 @@ int gpt_iterate_through_entries(FILE *disk, size_t sector_size, gpt_entry_iterat
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpt_iterate_through_emu_entries(FILE *disk, size_t sector_size, gpt_emu_entry_iterator_t callback, void *param, const char *origin_path, bool is_multipart, int num_parts, uint64_t part_limit) {
|
||||
efi_header_t hdr;
|
||||
efi_entry_t entry;
|
||||
size_t offset = 2 * 512; /* Sector #2. */
|
||||
size_t delta;
|
||||
|
||||
/* Get the header. */
|
||||
if (gpt_get_header(&hdr, disk, sector_size) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Seek to the entry table. */
|
||||
if (fseek(disk, sector_size * hdr.entries_first_lba - offset, SEEK_CUR) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
offset = sector_size * hdr.entries_first_lba;
|
||||
delta = hdr.entry_size - sizeof(efi_entry_t);
|
||||
|
||||
/* Iterate through the entries. */
|
||||
for (uint32_t i = 0; i < hdr.entry_count; i++) {
|
||||
if (!fread(&entry, sizeof(efi_entry_t), 1, disk)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (callback(&entry, param, offset, disk, origin_path, is_multipart, num_parts, part_limit) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (delta != 0 && fseek(disk, delta, SEEK_CUR) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
offset += hdr.entry_size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
#define FUSEE_GPT_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct efi_entry_t {
|
||||
uint8_t type_uuid[16];
|
||||
@@ -52,8 +52,10 @@ typedef struct efi_header {
|
||||
} __attribute__((packed, aligned(4))) efi_header_t;
|
||||
|
||||
typedef int (*gpt_entry_iterator_t)(const efi_entry_t *entry, void *param, size_t entry_offset, FILE *disk);
|
||||
typedef int (*gpt_emu_entry_iterator_t)(const efi_entry_t *entry, void *param, size_t entry_offset, FILE *disk, const char *origin_path, bool is_multipart, int num_parts, uint64_t part_limit);
|
||||
|
||||
int gpt_get_header(efi_header_t *out, FILE *disk, size_t sector_size);
|
||||
int gpt_iterate_through_entries(FILE *disk, size_t sector_size, gpt_entry_iterator_t callback, void *param);
|
||||
int gpt_iterate_through_emu_entries(FILE *disk, size_t sector_size, gpt_emu_entry_iterator_t callback, void *param, const char *origin_path, bool is_multipart, int num_parts, uint64_t part_limit);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "utils.h"
|
||||
#include "exocfg.h"
|
||||
#include "se.h"
|
||||
#include "lib/log.h"
|
||||
#include "ips.h"
|
||||
@@ -343,7 +344,7 @@ static void kip1_blz_uncompress(void *hdr_end) {
|
||||
}
|
||||
}
|
||||
|
||||
static kip1_header_t *kip1_uncompress(kip1_header_t *kip, size_t *size) {
|
||||
kip1_header_t *kip1_uncompress(kip1_header_t *kip, size_t *size) {
|
||||
kip1_header_t new_header = *kip;
|
||||
for (unsigned int i = 0; i < 3; i++) {
|
||||
new_header.section_headers[i].compressed_size = new_header.section_headers[i].out_size;
|
||||
@@ -372,10 +373,60 @@ static kip1_header_t *kip1_uncompress(kip1_header_t *kip, size_t *size) {
|
||||
return (kip1_header_t *)new_kip;
|
||||
}
|
||||
|
||||
kip1_header_t *apply_kip_ips_patches(kip1_header_t *kip, size_t kip_size) {
|
||||
static const uint8_t g_fs_hashes[FS_VER_MAX][0x8] = {
|
||||
"\xde\x9f\xdd\xa4\x08\x5d\xd5\xfe", /* FS_VER_1_0_0 */
|
||||
|
||||
"\xcd\x7b\xbe\x18\xd6\x13\x0b\x28", /* FS_VER_2_0_0 */
|
||||
"\xe7\x66\x92\xdf\xaa\x04\x20\xe9", /* FS_VER_2_0_0_EXFAT */
|
||||
|
||||
"\x0d\x70\x05\x62\x7b\x07\x76\x7c", /* FS_VER_2_1_0 */
|
||||
"\xdb\xd8\x5f\xca\xcc\x19\x3d\xa8", /* FS_VER_2_1_0_EXFAT */
|
||||
|
||||
"\xa8\x6d\xa5\xe8\x7e\xf1\x09\x7b", /* FS_VER_3_0_0 */
|
||||
"\x98\x1c\x57\xe7\xf0\x2f\x70\xf7", /* FS_VER_3_0_0_EXFAT */
|
||||
|
||||
"\x57\x39\x7c\x06\x3f\x10\xb6\x31", /* FS_VER_3_0_1 */
|
||||
"\x07\x30\x99\xd7\xc6\xad\x7d\x89", /* FS_VER_3_0_1_EXFAT */
|
||||
|
||||
"\x06\xe9\x07\x19\x59\x5a\x01\x0c", /* FS_VER_4_0_0 */
|
||||
"\x54\x9b\x0f\x8d\x6f\x72\xc4\xe9", /* FS_VER_4_0_0_EXFAT */
|
||||
|
||||
"\x80\x96\xaf\x7c\x6a\x35\xaa\x82", /* FS_VER_4_1_0 */
|
||||
"\x02\xd5\xab\xaa\xfd\x20\xc8\xb0", /* FS_VER_4_1_0_EXFAT */
|
||||
|
||||
"\xa6\xf2\x7a\xd9\xac\x7c\x73\xad", /* FS_VER_5_0_0 */
|
||||
"\xce\x3e\xcb\xa2\xf2\xf0\x62\xf5", /* FS_VER_5_0_0_EXFAT */
|
||||
|
||||
"\x76\xf8\x74\x02\xc9\x38\x7c\x0f", /* FS_VER_5_1_0 */
|
||||
"\x10\xb2\xd8\x16\x05\x48\x85\x99", /* FS_VER_5_1_0_EXFAT */
|
||||
|
||||
"\x3a\x57\x4d\x43\x61\x86\x19\x1d", /* FS_VER_6_0_0 */
|
||||
"\x33\x05\x53\xf6\xb5\xfb\x55\xc4", /* FS_VER_6_0_0_EXFAT */
|
||||
|
||||
"\x2A\xDB\xE9\x7E\x9B\x5F\x41\x77", /* FS_VER_7_0_0 */
|
||||
"\x2C\xCE\x65\x9C\xEC\x53\x6A\x8E", /* FS_VER_7_0_0_EXFAT */
|
||||
|
||||
"\xB2\xF5\x17\x6B\x35\x48\x36\x4D", /* FS_VER_8_0_0 */
|
||||
"\xDB\xD9\x41\xC0\xC5\x3C\x52\xCC", /* FS_VER_8_0_0_EXFAT */
|
||||
};
|
||||
|
||||
kip1_header_t *apply_kip_ips_patches(kip1_header_t *kip, size_t kip_size, emummc_fs_ver_t *out_fs_ver) {
|
||||
uint8_t hash[0x20];
|
||||
se_calculate_sha256(hash, kip, kip_size);
|
||||
|
||||
if (kip->title_id == FS_TITLE_ID) {
|
||||
bool found = false;
|
||||
for (size_t i = 0; i < FS_VER_MAX; i++) {
|
||||
if (memcmp(hash, g_fs_hashes[i], 0x8) == 0) {
|
||||
found = true;
|
||||
*out_fs_ver = (emummc_fs_ver_t)i;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
fatal_error("[NXBOOT]: Failed to identify FS version...");
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_needed_default_kip_patches(kip->title_id, hash, sizeof(hash))) {
|
||||
fatal_error("[NXBOOT]: Missing default patch for KIP %08x%08x...\n", (uint32_t)(kip->title_id >> 32), (uint32_t)kip->title_id);
|
||||
}
|
||||
|
||||
@@ -17,12 +17,16 @@
|
||||
#ifndef FUSEE_IPS_H
|
||||
#define FUSEE_IPS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "utils.h"
|
||||
#include "kip.h"
|
||||
#include <stdint.h>
|
||||
#include "exocfg.h"
|
||||
|
||||
#define FS_TITLE_ID 0x0100000000000000ull
|
||||
|
||||
void apply_kernel_ips_patches(void *kernel, size_t kernel_size);
|
||||
kip1_header_t *apply_kip_ips_patches(kip1_header_t *kip, size_t kip_size);
|
||||
kip1_header_t *apply_kip_ips_patches(kip1_header_t *kip, size_t kip_size, emummc_fs_ver_t *out_fs_ver);
|
||||
kip1_header_t *kip1_uncompress(kip1_header_t *kip, size_t *size);
|
||||
|
||||
void kip_patches_set_enable_nogc(void);
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
/* This option switches f_expand function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define FF_USE_CHMOD 0
|
||||
#define FF_USE_CHMOD 1
|
||||
/* This option switches attribute manipulation functions, f_chmod() and f_utime().
|
||||
/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
|
||||
|
||||
|
||||
@@ -52,8 +52,9 @@ static void setup_env(void) {
|
||||
/* Set up exception handlers. */
|
||||
setup_exception_handlers();
|
||||
|
||||
if (nxfs_mount_all() < 0) {
|
||||
fatal_error("Failed to mount at least one parition: %s\n", strerror(errno));
|
||||
/* Initialize the file system by mounting the SD card. */
|
||||
if (nxfs_init() < 0) {
|
||||
fatal_error("Failed to initialize the file system: %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
/* Train DRAM. */
|
||||
@@ -63,7 +64,8 @@ static void setup_env(void) {
|
||||
|
||||
static void cleanup_env(void) {
|
||||
/* Unmount everything (this causes all open files to be flushed and closed) */
|
||||
nxfs_unmount_all();
|
||||
nxfs_end();
|
||||
//console_end();
|
||||
}
|
||||
|
||||
static void exit_callback(int rc) {
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
#include "exosphere_bin.h"
|
||||
#include "sept_secondary_enc.h"
|
||||
#include "lp0fw_bin.h"
|
||||
#include "emummc_kip.h"
|
||||
#include "lib/log.h"
|
||||
#undef u8
|
||||
#undef u32
|
||||
@@ -100,12 +101,41 @@ static const uint8_t dev_pkc_modulus[0x100] = {
|
||||
0xD5, 0x52, 0xDA, 0xEC, 0x41, 0xA4, 0xAD, 0x7B, 0x36, 0x86, 0x18, 0xB4, 0x5B, 0xD1, 0x30, 0xBB
|
||||
};
|
||||
|
||||
static int emummc_ini_handler(void *user, const char *section, const char *name, const char *value) {
|
||||
emummc_config_t *emummc_cfg = (emummc_config_t *)user;
|
||||
if (strcmp(section, "emummc") == 0) {
|
||||
if (strcmp(name, EMUMMC_ENABLED_KEY) == 0) {
|
||||
int tmp = 0;
|
||||
sscanf(value, "%d", &tmp);
|
||||
emummc_cfg->enabled = (tmp != 0);
|
||||
}
|
||||
if (strcmp(name, EMUMMC_SECTOR_KEY) == 0) {
|
||||
uintptr_t sector = 0;
|
||||
sscanf(value, "%x", §or);
|
||||
emummc_cfg->sector = sector;
|
||||
} else if (strcmp(name, EMUMMC_ID_KEY) == 0) {
|
||||
sscanf(value, "%lx", &emummc_cfg->id);
|
||||
} else if (strcmp(name, EMUMMC_PATH_KEY) == 0) {
|
||||
strncpy(emummc_cfg->path, value, sizeof(emummc_cfg->path) - 1);
|
||||
emummc_cfg->path[sizeof(emummc_cfg->path) - 1] = '\0';
|
||||
} else if (strcmp(name, EMUMMC_NINTENDO_PATH_KEY) == 0) {
|
||||
strncpy(emummc_cfg->nintendo_path, value, sizeof(emummc_cfg->nintendo_path) - 1);
|
||||
emummc_cfg->nintendo_path[sizeof(emummc_cfg->nintendo_path) - 1] = '\0';
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int exosphere_ini_handler(void *user, const char *section, const char *name, const char *value) {
|
||||
exosphere_config_t *exo_cfg = (exosphere_config_t *)user;
|
||||
int tmp = 0;
|
||||
if (strcmp(section, "exosphere") == 0) {
|
||||
if (strcmp(name, EXOSPHERE_TARGETFW_KEY) == 0) {
|
||||
sscanf(value, "%d", &exo_cfg->target_firmware);
|
||||
sscanf(value, "%ld", &exo_cfg->target_firmware);
|
||||
} else if (strcmp(name, EXOSPHERE_DEBUGMODE_PRIV_KEY) == 0) {
|
||||
sscanf(value, "%d", &tmp);
|
||||
if (tmp) {
|
||||
@@ -172,7 +202,7 @@ static uint32_t nxboot_get_target_firmware(const void *package1loader) {
|
||||
} else if (memcmp(package1loader_header->build_timestamp, "20181107", 8) == 0) {
|
||||
return ATMOSPHERE_TARGET_FIRMWARE_620;
|
||||
} else {
|
||||
fatal_error("[NXBOOT]: Unable to identify package1!\n");
|
||||
fatal_error("[NXBOOT] Unable to identify package1!\n");
|
||||
}
|
||||
}
|
||||
case 0x0F: /* 7.0.0 - 7.0.1 */
|
||||
@@ -180,15 +210,108 @@ static uint32_t nxboot_get_target_firmware(const void *package1loader) {
|
||||
case 0x10: /* 8.0.0 */
|
||||
return ATMOSPHERE_TARGET_FIRMWARE_800;
|
||||
default:
|
||||
fatal_error("[NXBOOT]: Unable to identify package1!\n");
|
||||
fatal_error("[NXBOOT] Unable to identify package1!\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void nxboot_configure_exosphere(uint32_t target_firmware, unsigned int keygen_type) {
|
||||
static bool nxboot_configure_emummc(exo_emummc_config_t *exo_emummc_config) {
|
||||
emummc_config_t emummc_cfg = {.enabled = false, .id = 0, .sector = -1, .path = "", .nintendo_path = ""};
|
||||
|
||||
char *emummc_ini = calloc(1, 0x10000);
|
||||
if (!read_from_file(emummc_ini, 0xFFFF, "emummc/emummc.ini")) {
|
||||
free(emummc_ini);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Load emummc settings from emummc.ini file. */
|
||||
if (ini_parse_string(emummc_ini, emummc_ini_handler, &emummc_cfg) < 0) {
|
||||
fatal_error("[NXBOOT] Failed to parse emummc.ini!\n");
|
||||
}
|
||||
|
||||
free(emummc_ini);
|
||||
|
||||
memset(exo_emummc_config, 0, sizeof(*exo_emummc_config));
|
||||
exo_emummc_config->base_cfg.magic = MAGIC_EMUMMC_CONFIG;
|
||||
exo_emummc_config->base_cfg.type = EMUMMC_TYPE_NONE;
|
||||
exo_emummc_config->base_cfg.id = emummc_cfg.id;
|
||||
exo_emummc_config->base_cfg.fs_version = FS_VER_1_0_0; /* Will be filled out later. */
|
||||
strncpy(exo_emummc_config->emu_dir_path, emummc_cfg.nintendo_path, sizeof(exo_emummc_config->emu_dir_path));
|
||||
exo_emummc_config->emu_dir_path[sizeof(exo_emummc_config->emu_dir_path) - 1] = '\0';
|
||||
|
||||
if (emummc_cfg.enabled) {
|
||||
if (emummc_cfg.sector >= 0) {
|
||||
exo_emummc_config->base_cfg.type = EMUMMC_TYPE_PARTITION;
|
||||
exo_emummc_config->partition_cfg.start_sector = emummc_cfg.sector;
|
||||
|
||||
/* Mount emulated NAND from SD card partition. */
|
||||
if (nxfs_mount_emummc_partition(emummc_cfg.sector) < 0) {
|
||||
fatal_error("[NXBOOT] Failed to mount EmuMMC from SD card partition!\n");
|
||||
}
|
||||
} else if (is_valid_folder(emummc_cfg.path)) {
|
||||
exo_emummc_config->base_cfg.type = EMUMMC_TYPE_FILES;
|
||||
strncpy(exo_emummc_config->file_cfg.path, emummc_cfg.path, sizeof(exo_emummc_config->file_cfg.path));
|
||||
exo_emummc_config->file_cfg.path[sizeof(exo_emummc_config->file_cfg.path) - 1] = '\0';
|
||||
|
||||
int num_parts = 0;
|
||||
uint64_t part_limit = 0;
|
||||
char emummc_path[0x300 + 1] = {0};
|
||||
char emummc_boot0_path[0x300 + 1] = {0};
|
||||
char emummc_boot1_path[0x300 + 1] = {0};
|
||||
char emummc_rawnand_path[0x300 + 1] = {0};
|
||||
|
||||
/* Prepare base folder path. */
|
||||
snprintf(emummc_path, sizeof(emummc_path) - 1, "sdmc:/%s/%s", emummc_cfg.path, "eMMC");
|
||||
|
||||
/* Check if eMMC folder is present. */
|
||||
if (!is_valid_folder(emummc_path)) {
|
||||
fatal_error("[NXBOOT] Failed to find EmuMMC eMMC folder!\n");
|
||||
}
|
||||
|
||||
/* Prepare expected file paths. */
|
||||
snprintf(emummc_boot0_path, sizeof(emummc_boot0_path) - 1, "sdmc:/%s/%s", emummc_path, "boot0");
|
||||
snprintf(emummc_boot1_path, sizeof(emummc_boot1_path) - 1, "sdmc:/%s/%s", emummc_path, "boot1");
|
||||
|
||||
/* Check if boot0 and boot1 image files are present. */
|
||||
if (!is_valid_file(emummc_boot0_path) || !is_valid_file(emummc_boot1_path)) {
|
||||
fatal_error("[NXBOOT] Failed to find EmuMMC boot0/boot1 image files!\n");
|
||||
}
|
||||
|
||||
/* Find raw image files (single or multi part). */
|
||||
for (int i = 0; i < 64; i++) {
|
||||
snprintf(emummc_rawnand_path, sizeof(emummc_rawnand_path) - 1, "sdmc:/%s/%02d", emummc_path, i);
|
||||
if (is_valid_file(emummc_rawnand_path)) {
|
||||
if (i == 0) {
|
||||
/* The size of the first file should tell us the part limit. */
|
||||
part_limit = get_file_size(emummc_rawnand_path);
|
||||
}
|
||||
num_parts++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if at least one raw image file is present. */
|
||||
if ((num_parts == 0) || (part_limit == 0)) {
|
||||
fatal_error("[NXBOOT] Failed to find EmuMMC raw image files!\n");
|
||||
}
|
||||
|
||||
/* Mount emulated NAND from files. */
|
||||
if (nxfs_mount_emummc_file(emummc_path, num_parts, part_limit) < 0) {
|
||||
fatal_error("[NXBOOT] Failed to mount EmuMMC from files!\n");
|
||||
}
|
||||
} else {
|
||||
fatal_error("[NXBOOT] Invalid EmuMMC setting!\n");
|
||||
}
|
||||
}
|
||||
|
||||
return emummc_cfg.enabled;
|
||||
}
|
||||
|
||||
static void nxboot_configure_exosphere(uint32_t target_firmware, unsigned int keygen_type, exo_emummc_config_t *exo_emummc_cfg) {
|
||||
exosphere_config_t exo_cfg = {0};
|
||||
|
||||
exo_cfg.magic = MAGIC_EXOSPHERE_CONFIG;
|
||||
exo_cfg.target_firmware = target_firmware;
|
||||
memcpy(&exo_cfg.emummc_cfg, exo_emummc_cfg, sizeof(*exo_emummc_cfg));
|
||||
|
||||
if (keygen_type) {
|
||||
exo_cfg.flags = EXOSPHERE_FLAGS_DEFAULT | EXOSPHERE_FLAG_PERFORM_620_KEYGEN;
|
||||
} else {
|
||||
@@ -196,11 +319,11 @@ static void nxboot_configure_exosphere(uint32_t target_firmware, unsigned int ke
|
||||
}
|
||||
|
||||
if (ini_parse_string(get_loader_ctx()->bct0, exosphere_ini_handler, &exo_cfg) < 0) {
|
||||
fatal_error("[NXBOOT]: Failed to parse BCT.ini!\n");
|
||||
fatal_error("[NXBOOT] Failed to parse BCT.ini!\n");
|
||||
}
|
||||
|
||||
if ((exo_cfg.target_firmware < ATMOSPHERE_TARGET_FIRMWARE_MIN) || (exo_cfg.target_firmware > ATMOSPHERE_TARGET_FIRMWARE_MAX)) {
|
||||
fatal_error("[NXBOOT]: Invalid Exosphere target firmware!\n");
|
||||
fatal_error("[NXBOOT] Invalid Exosphere target firmware!\n");
|
||||
}
|
||||
|
||||
*(MAILBOX_EXOSPHERE_CONFIGURATION) = exo_cfg;
|
||||
@@ -209,7 +332,7 @@ static void nxboot_configure_exosphere(uint32_t target_firmware, unsigned int ke
|
||||
static void nxboot_configure_stratosphere(uint32_t target_firmware) {
|
||||
stratosphere_cfg_t strat_cfg = {0};
|
||||
if (ini_parse_string(get_loader_ctx()->bct0, stratosphere_ini_handler, &strat_cfg) < 0) {
|
||||
fatal_error("[NXBOOT]: Failed to parse BCT.ini!\n");
|
||||
fatal_error("[NXBOOT] Failed to parse BCT.ini!\n");
|
||||
}
|
||||
|
||||
/* Enable NOGC patches if the user requested it, or if the user is booting into 4.0.0+ with 3.0.2- fuses. */
|
||||
@@ -234,18 +357,18 @@ static void nxboot_set_bootreason(void *bootreason_base) {
|
||||
/* Allocate memory for the BCT. */
|
||||
bct = malloc(sizeof(nvboot_config_table));
|
||||
if (bct == NULL) {
|
||||
fatal_error("[NXBOOT]: Out of memory!\n");
|
||||
fatal_error("[NXBOOT] Out of memory!\n");
|
||||
}
|
||||
|
||||
/* Open boot0. */
|
||||
boot0 = fopen("boot0:/", "rb");
|
||||
if (boot0 == NULL) {
|
||||
fatal_error("[NXBOOT]: Failed to open boot0!\n");
|
||||
fatal_error("[NXBOOT] Failed to open boot0!\n");
|
||||
}
|
||||
|
||||
/* Read the BCT. */
|
||||
if (fread(bct, sizeof(nvboot_config_table), 1, boot0) == 0) {
|
||||
fatal_error("[NXBOOT]: Failed to read the BCT!\n");
|
||||
fatal_error("[NXBOOT] Failed to read the BCT!\n");
|
||||
}
|
||||
|
||||
/* Close boot0. */
|
||||
@@ -292,17 +415,17 @@ static void nxboot_move_bootconfig() {
|
||||
/* Allocate memory for reading BootConfig. */
|
||||
bootconfig = memalign(0x1000, 0x4000);
|
||||
if (bootconfig == NULL) {
|
||||
fatal_error("[NXBOOT]: Out of memory!\n");
|
||||
fatal_error("[NXBOOT] Out of memory!\n");
|
||||
}
|
||||
|
||||
/* Get BootConfig from the Package2 partition. */
|
||||
bcfile = fopen("bcpkg21:/", "rb");
|
||||
if (bcfile == NULL) {
|
||||
fatal_error("[NXBOOT]: Failed to open BootConfig from eMMC!\n");
|
||||
fatal_error("[NXBOOT] Failed to open BootConfig from eMMC!\n");
|
||||
}
|
||||
if (fread(bootconfig, 0x4000, 1, bcfile) < 1) {
|
||||
fclose(bcfile);
|
||||
fatal_error("[NXBOOT]: Failed to read BootConfig!\n");
|
||||
fatal_error("[NXBOOT] Failed to read BootConfig!\n");
|
||||
}
|
||||
fclose(bcfile);
|
||||
|
||||
@@ -338,86 +461,121 @@ uint32_t nxboot_main(void) {
|
||||
void *warmboot_memaddr;
|
||||
void *package1loader;
|
||||
size_t package1loader_size;
|
||||
void *emummc;
|
||||
size_t emummc_size;
|
||||
uint32_t available_revision;
|
||||
FILE *boot0, *pk2file;
|
||||
void *exosphere_memaddr;
|
||||
exo_emummc_config_t exo_emummc_cfg;
|
||||
|
||||
/* Configure emummc or mount the real NAND. */
|
||||
if (!nxboot_configure_emummc(&exo_emummc_cfg)) {
|
||||
emummc = NULL;
|
||||
emummc_size = 0;
|
||||
if (nxfs_mount_emmc() < 0) {
|
||||
fatal_error("[NXBOOT] Failed to mount eMMC!\n");
|
||||
}
|
||||
} else {
|
||||
emummc_size = get_file_size("atmosphere/emummc.kip");
|
||||
if (emummc_size != 0) {
|
||||
/* Allocate memory for the TSEC firmware. */
|
||||
emummc = memalign(0x100, emummc_size);
|
||||
|
||||
if (emummc == NULL) {
|
||||
fatal_error("[NXBOOT] Out of memory!\n");
|
||||
}
|
||||
if (read_from_file(emummc, emummc_size, "atmosphere/emummc.kip") != emummc_size) {
|
||||
fatal_error("[NXBOOT] Could not read the emummc kip!\n");
|
||||
}
|
||||
} else {
|
||||
/* Use embedded copy. */
|
||||
emummc_size = emummc_kip_size;
|
||||
emummc = memalign(0x100, emummc_size);
|
||||
|
||||
if (emummc == NULL) {
|
||||
fatal_error("[NXBOOT] Out of memory!\n");
|
||||
}
|
||||
|
||||
memcpy(emummc, emummc_kip, emummc_size);
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate memory for reading Package2. */
|
||||
package2 = memalign(0x1000, PACKAGE2_SIZE_MAX);
|
||||
if (package2 == NULL) {
|
||||
fatal_error("[NXBOOT]: Out of memory!\n");
|
||||
fatal_error("[NXBOOT] Out of memory!\n");
|
||||
}
|
||||
|
||||
/* Read Package2 from a file, otherwise from its partition(s). */
|
||||
print(SCREEN_LOG_LEVEL_INFO, "[NXBOOT]: Reading package2...\n");
|
||||
print(SCREEN_LOG_LEVEL_INFO, "[NXBOOT] Reading package2...\n");
|
||||
if (loader_ctx->package2_path[0] != '\0') {
|
||||
pk2file = fopen(loader_ctx->package2_path, "rb");
|
||||
if (pk2file == NULL) {
|
||||
fatal_error("[NXBOOT]: Failed to open Package2 from %s: %s!\n", loader_ctx->package2_path, strerror(errno));
|
||||
fatal_error("[NXBOOT] Failed to open Package2 from %s: %s!\n", loader_ctx->package2_path, strerror(errno));
|
||||
}
|
||||
} else {
|
||||
pk2file = fopen("bcpkg21:/", "rb");
|
||||
if (pk2file == NULL) {
|
||||
fatal_error("[NXBOOT]: Failed to open Package2 from eMMC: %s!\n", strerror(errno));
|
||||
fatal_error("[NXBOOT] Failed to open Package2 from eMMC: %s!\n", strerror(errno));
|
||||
}
|
||||
if (fseek(pk2file, 0x4000, SEEK_SET) != 0) {
|
||||
fclose(pk2file);
|
||||
fatal_error("[NXBOOT]: Failed to seek Package2 in eMMC: %s!\n", strerror(errno));
|
||||
fatal_error("[NXBOOT] Failed to seek Package2 in eMMC: %s!\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
setvbuf(pk2file, NULL, _IONBF, 0); /* Workaround. */
|
||||
if (fread(package2, sizeof(package2_header_t), 1, pk2file) < 1) {
|
||||
fclose(pk2file);
|
||||
fatal_error("[NXBOOT]: Failed to read Package2!\n");
|
||||
fatal_error("[NXBOOT] Failed to read Package2!\n");
|
||||
}
|
||||
package2_size = package2_meta_get_size(&package2->metadata);
|
||||
if ((package2_size > PACKAGE2_SIZE_MAX) || (package2_size <= sizeof(package2_header_t))) {
|
||||
fclose(pk2file);
|
||||
fatal_error("[NXBOOT]: Package2 is too big or too small!\n");
|
||||
fatal_error("[NXBOOT] Package2 is too big or too small!\n");
|
||||
}
|
||||
if (fread(package2->data, package2_size - sizeof(package2_header_t), 1, pk2file) < 1) {
|
||||
fclose(pk2file);
|
||||
fatal_error("[NXBOOT]: Failed to read Package2!\n");
|
||||
fatal_error("[NXBOOT] Failed to read Package2!\n");
|
||||
}
|
||||
fclose(pk2file);
|
||||
|
||||
/* Read and parse boot0. */
|
||||
print(SCREEN_LOG_LEVEL_INFO, "[NXBOOT]: Reading boot0...\n");
|
||||
print(SCREEN_LOG_LEVEL_INFO, "[NXBOOT] Reading boot0...\n");
|
||||
boot0 = fopen("boot0:/", "rb");
|
||||
if ((boot0 == NULL) || (package1_read_and_parse_boot0(&package1loader, &package1loader_size, g_keyblobs, &available_revision, boot0) == -1)) {
|
||||
fatal_error("[NXBOOT]: Couldn't parse boot0: %s!\n", strerror(errno));
|
||||
fatal_error("[NXBOOT] Couldn't parse boot0: %s!\n", strerror(errno));
|
||||
}
|
||||
fclose(boot0);
|
||||
|
||||
/* Find the system's target firmware. */
|
||||
uint32_t target_firmware = nxboot_get_target_firmware(package1loader);
|
||||
if (!target_firmware)
|
||||
fatal_error("[NXBOOT]: Failed to detect target firmware!\n");
|
||||
fatal_error("[NXBOOT] Failed to detect target firmware!\n");
|
||||
else
|
||||
print(SCREEN_LOG_LEVEL_INFO, "[NXBOOT]: Detected target firmware %ld!\n", target_firmware);
|
||||
print(SCREEN_LOG_LEVEL_INFO, "[NXBOOT] Detected target firmware %ld!\n", target_firmware);
|
||||
|
||||
/* Read the TSEC firmware from a file, otherwise from PK1L. */
|
||||
if (loader_ctx->tsecfw_path[0] != '\0') {
|
||||
tsec_fw_size = get_file_size(loader_ctx->tsecfw_path);
|
||||
if ((tsec_fw_size != 0) && (tsec_fw_size != 0xF00 && tsec_fw_size != 0x2900 && tsec_fw_size != 0x3000)) {
|
||||
fatal_error("[NXBOOT]: TSEC firmware from %s has a wrong size!\n", loader_ctx->tsecfw_path);
|
||||
fatal_error("[NXBOOT] TSEC firmware from %s has a wrong size!\n", loader_ctx->tsecfw_path);
|
||||
} else if (tsec_fw_size == 0) {
|
||||
fatal_error("[NXBOOT]: Could not read the TSEC firmware from %s!\n", loader_ctx->tsecfw_path);
|
||||
fatal_error("[NXBOOT] Could not read the TSEC firmware from %s!\n", loader_ctx->tsecfw_path);
|
||||
}
|
||||
|
||||
/* Allocate memory for the TSEC firmware. */
|
||||
tsec_fw = memalign(0x100, tsec_fw_size);
|
||||
|
||||
if (tsec_fw == NULL) {
|
||||
fatal_error("[NXBOOT]: Out of memory!\n");
|
||||
fatal_error("[NXBOOT] Out of memory!\n");
|
||||
}
|
||||
if (read_from_file(tsec_fw, tsec_fw_size, loader_ctx->tsecfw_path) != tsec_fw_size) {
|
||||
fatal_error("[NXBOOT]: Could not read the TSEC firmware from %s!\n", loader_ctx->tsecfw_path);
|
||||
fatal_error("[NXBOOT] Could not read the TSEC firmware from %s!\n", loader_ctx->tsecfw_path);
|
||||
}
|
||||
} else {
|
||||
if (!package1_get_tsec_fw(&tsec_fw, package1loader, package1loader_size)) {
|
||||
fatal_error("[NXBOOT]: Failed to read the TSEC firmware from Package1loader!\n");
|
||||
fatal_error("[NXBOOT] Failed to read the TSEC firmware from Package1loader!\n");
|
||||
}
|
||||
if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_700) {
|
||||
tsec_fw_size = 0x3000;
|
||||
@@ -428,7 +586,7 @@ uint32_t nxboot_main(void) {
|
||||
}
|
||||
}
|
||||
|
||||
print(SCREEN_LOG_LEVEL_INFO, "[NXBOOT]: Loaded firmware from eMMC...\n");
|
||||
print(SCREEN_LOG_LEVEL_INFO, "[NXBOOT] Loaded firmware from eMMC...\n");
|
||||
|
||||
/* Get the TSEC keys. */
|
||||
uint8_t tsec_key[0x10] = {0};
|
||||
@@ -439,7 +597,7 @@ uint32_t nxboot_main(void) {
|
||||
reboot_to_sept(tsec_fw, tsec_fw_size, sept_secondary_enc, sept_secondary_enc_size);
|
||||
} else {
|
||||
if (mkey_detect_revision(fuse_get_retail_type() != 0) != 0) {
|
||||
fatal_error("[NXBOOT]: Sept derived incorrect keys!\n");
|
||||
fatal_error("[NXBOOT] Sept derived incorrect keys!\n");
|
||||
}
|
||||
}
|
||||
get_and_clear_has_run_sept();
|
||||
@@ -455,7 +613,7 @@ uint32_t nxboot_main(void) {
|
||||
} else {
|
||||
/* Run the TSEC payload and get the key. */
|
||||
if (tsec_get_key(tsec_key, 1, tsec_fw, tsec_fw_size) != 0) {
|
||||
fatal_error("[NXBOOT]: Failed to get TSEC key!\n");
|
||||
fatal_error("[NXBOOT] Failed to get TSEC key!\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -467,16 +625,16 @@ uint32_t nxboot_main(void) {
|
||||
unsigned int keygen_type = 0;
|
||||
if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_700) {
|
||||
if (derive_nx_keydata(target_firmware, g_keyblobs, available_revision, tsec_key, tsec_root_keys, &keygen_type) != 0) {
|
||||
fatal_error("[NXBOOT]: Key derivation failed!\n");
|
||||
fatal_error("[NXBOOT] Key derivation failed!\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup boot configuration for Exosphère. */
|
||||
nxboot_configure_exosphere(target_firmware, keygen_type);
|
||||
nxboot_configure_exosphere(target_firmware, keygen_type, &exo_emummc_cfg);
|
||||
|
||||
/* Initialize Boot Reason on older firmware versions. */
|
||||
if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_400) {
|
||||
print(SCREEN_LOG_LEVEL_INFO, "[NXBOOT]: Initializing Boot Reason...\n");
|
||||
print(SCREEN_LOG_LEVEL_INFO, "[NXBOOT] Initializing Boot Reason...\n");
|
||||
nxboot_set_bootreason((void *)MAILBOX_NX_BOOTLOADER_BOOT_REASON_BASE(target_firmware));
|
||||
}
|
||||
|
||||
@@ -484,17 +642,17 @@ uint32_t nxboot_main(void) {
|
||||
if (loader_ctx->warmboot_path[0] != '\0') {
|
||||
warmboot_fw_size = get_file_size(loader_ctx->warmboot_path);
|
||||
if (warmboot_fw_size == 0) {
|
||||
fatal_error("[NXBOOT]: Could not read the warmboot firmware from %s!\n", loader_ctx->warmboot_path);
|
||||
fatal_error("[NXBOOT] Could not read the warmboot firmware from %s!\n", loader_ctx->warmboot_path);
|
||||
}
|
||||
|
||||
/* Allocate memory for the warmboot firmware. */
|
||||
warmboot_fw = malloc(warmboot_fw_size);
|
||||
|
||||
if (warmboot_fw == NULL) {
|
||||
fatal_error("[NXBOOT]: Out of memory!\n");
|
||||
fatal_error("[NXBOOT] Out of memory!\n");
|
||||
}
|
||||
if (read_from_file(warmboot_fw, warmboot_fw_size, loader_ctx->warmboot_path) != warmboot_fw_size) {
|
||||
fatal_error("[NXBOOT]: Could not read the warmboot firmware from %s!\n", loader_ctx->warmboot_path);
|
||||
fatal_error("[NXBOOT] Could not read the warmboot firmware from %s!\n", loader_ctx->warmboot_path);
|
||||
}
|
||||
} else {
|
||||
/* Use Atmosphere's warmboot firmware implementation. */
|
||||
@@ -502,13 +660,13 @@ uint32_t nxboot_main(void) {
|
||||
warmboot_fw = malloc(warmboot_fw_size);
|
||||
|
||||
if (warmboot_fw == NULL) {
|
||||
fatal_error("[NXBOOT]: Out of memory!\n");
|
||||
fatal_error("[NXBOOT] Out of memory!\n");
|
||||
}
|
||||
|
||||
memcpy(warmboot_fw, lp0fw_bin, warmboot_fw_size);
|
||||
|
||||
if (warmboot_fw_size == 0) {
|
||||
fatal_error("[NXBOOT]: Could not read the warmboot firmware from Package1!\n");
|
||||
fatal_error("[NXBOOT] Could not read the warmboot firmware from Package1!\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -537,7 +695,7 @@ uint32_t nxboot_main(void) {
|
||||
warmboot_memaddr = (void *)0x4003E000;
|
||||
}
|
||||
|
||||
print(SCREEN_LOG_LEVEL_INFO, "[NXBOOT]: Copying warmboot firmware...\n");
|
||||
print(SCREEN_LOG_LEVEL_INFO, "[NXBOOT] Copying warmboot firmware...\n");
|
||||
|
||||
/* Copy the warmboot firmware and set the address in PMC if necessary. */
|
||||
if (warmboot_fw && (warmboot_fw_size > 0)) {
|
||||
@@ -546,17 +704,20 @@ uint32_t nxboot_main(void) {
|
||||
pmc->scratch1 = (uint32_t)warmboot_memaddr;
|
||||
}
|
||||
|
||||
print(SCREEN_LOG_LEVEL_INFO, "[NXBOOT]: Rebuilding package2...\n");
|
||||
print(SCREEN_LOG_LEVEL_INFO, "[NXBOOT] Rebuilding package2...\n");
|
||||
|
||||
/* Parse stratosphere config. */
|
||||
nxboot_configure_stratosphere(MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware);
|
||||
|
||||
print(SCREEN_LOG_LEVEL_INFO, u8"[NXBOOT]: Configured Stratosphere...\n");
|
||||
print(SCREEN_LOG_LEVEL_INFO, u8"[NXBOOT] Configured Stratosphere...\n");
|
||||
|
||||
/* Patch package2, adding Thermosphère + custom KIPs. */
|
||||
package2_rebuild_and_copy(package2, MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware);
|
||||
package2_rebuild_and_copy(package2, MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware, emummc, emummc_size);
|
||||
|
||||
print(SCREEN_LOG_LEVEL_INFO, u8"[NXBOOT]: Reading Exosphère...\n");
|
||||
/* Set detected FS version. */
|
||||
MAILBOX_EXOSPHERE_CONFIGURATION->emummc_cfg.base_cfg.fs_version = stratosphere_get_fs_version();
|
||||
|
||||
print(SCREEN_LOG_LEVEL_INFO, u8"[NXBOOT] Reading Exosphère...\n");
|
||||
|
||||
/* Select the right address for Exosphère. */
|
||||
if (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < ATMOSPHERE_TARGET_FIRMWARE_400) {
|
||||
@@ -569,21 +730,21 @@ uint32_t nxboot_main(void) {
|
||||
if (loader_ctx->exosphere_path[0] != '\0') {
|
||||
size_t exosphere_size = get_file_size(loader_ctx->exosphere_path);
|
||||
if (exosphere_size == 0) {
|
||||
fatal_error(u8"[NXBOOT]: Could not read Exosphère from %s!\n", loader_ctx->exosphere_path);
|
||||
fatal_error(u8"[NXBOOT] Could not read Exosphère from %s!\n", loader_ctx->exosphere_path);
|
||||
} else if (exosphere_size > 0x10000) {
|
||||
/* The maximum is actually a bit less than that. */
|
||||
fatal_error(u8"[NXBOOT]: Exosphère from %s is too big!\n", loader_ctx->exosphere_path);
|
||||
fatal_error(u8"[NXBOOT] Exosphère from %s is too big!\n", loader_ctx->exosphere_path);
|
||||
}
|
||||
|
||||
if (read_from_file(exosphere_memaddr, exosphere_size, loader_ctx->exosphere_path) != exosphere_size) {
|
||||
fatal_error(u8"[NXBOOT]: Could not read Exosphère from %s!\n", loader_ctx->exosphere_path);
|
||||
fatal_error(u8"[NXBOOT] Could not read Exosphère from %s!\n", loader_ctx->exosphere_path);
|
||||
}
|
||||
} else {
|
||||
memcpy(exosphere_memaddr, exosphere_bin, exosphere_bin_size);
|
||||
}
|
||||
|
||||
/* Move BootConfig. */
|
||||
print(SCREEN_LOG_LEVEL_INFO, "[NXBOOT]: Moving BootConfig...\n");
|
||||
print(SCREEN_LOG_LEVEL_INFO, "[NXBOOT] Moving BootConfig...\n");
|
||||
nxboot_move_bootconfig();
|
||||
|
||||
/* Set 3.0.0/3.0.1/3.0.2 warmboot security check. */
|
||||
@@ -605,10 +766,10 @@ uint32_t nxboot_main(void) {
|
||||
}
|
||||
free(package2);
|
||||
|
||||
print(SCREEN_LOG_LEVEL_INFO, "[NXBOOT]: Powering on the CCPLEX...\n");
|
||||
print(SCREEN_LOG_LEVEL_INFO, "[NXBOOT] Powering on the CCPLEX...\n");
|
||||
|
||||
/* Unmount everything. */
|
||||
nxfs_unmount_all();
|
||||
nxfs_end();
|
||||
|
||||
/* Return the memory address for booting CPU0. */
|
||||
return (uint32_t)exosphere_memaddr;
|
||||
|
||||
@@ -19,6 +19,20 @@
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#define EMUMMC_ENABLED_KEY "emummc_enabled"
|
||||
#define EMUMMC_SECTOR_KEY "emummc_sector"
|
||||
#define EMUMMC_PATH_KEY "emummc_path"
|
||||
#define EMUMMC_NINTENDO_PATH_KEY "emummc_nintendo_path"
|
||||
#define EMUMMC_ID_KEY "emummc_id"
|
||||
|
||||
typedef struct {
|
||||
bool enabled;
|
||||
uint32_t id;
|
||||
uint64_t sector;
|
||||
char path[0x80];
|
||||
char nintendo_path[0x80];
|
||||
} emummc_config_t;
|
||||
|
||||
#define MAILBOX_NX_BOOTLOADER_BASE_100_620 0x40002E00
|
||||
#define MAILBOX_NX_BOOTLOADER_BASE_700 0x40000000
|
||||
#define MAILBOX_NX_BOOTLOADER_BASE(targetfw) ((targetfw >= ATMOSPHERE_TARGET_FIRMWARE_700) ? (MAILBOX_NX_BOOTLOADER_BASE_700) : (MAILBOX_NX_BOOTLOADER_BASE_100_620))
|
||||
|
||||
@@ -26,10 +26,18 @@
|
||||
#include "utils.h"
|
||||
#include "sdmmc/sdmmc.h"
|
||||
|
||||
#include "lib/fatfs/ff.h"
|
||||
|
||||
static bool g_ahb_redirect_enabled = false;
|
||||
static bool g_sd_device_initialized = false;
|
||||
static bool g_emmc_device_initialized = false;
|
||||
|
||||
static bool g_fsdev_ready = false;
|
||||
static bool g_rawdev_ready = false;
|
||||
static bool g_emudev_ready = false;
|
||||
|
||||
static bool g_is_emummc = false;
|
||||
|
||||
static sdmmc_t g_sd_sdmmc = {0};
|
||||
static sdmmc_t g_emmc_sdmmc = {0};
|
||||
|
||||
@@ -74,7 +82,7 @@ static int mmc_partition_initialize(device_partition_t *devpart) {
|
||||
/* Initialize hardware. */
|
||||
if (mmcpart->device == &g_sd_device) {
|
||||
if (!g_sd_device_initialized) {
|
||||
int rc = sdmmc_device_sd_init(mmcpart->device, &g_sd_sdmmc, SDMMC_BUS_WIDTH_4BIT, SDMMC_SPEED_SDR104) ? 0 : EIO;
|
||||
int rc = sdmmc_device_sd_init(mmcpart->device, &g_sd_sdmmc, SDMMC_BUS_WIDTH_4BIT, SDMMC_SPEED_EMU_SDR104) ? 0 : EIO;
|
||||
if (rc)
|
||||
return rc;
|
||||
g_sd_device_initialized = true;
|
||||
@@ -83,7 +91,7 @@ static int mmc_partition_initialize(device_partition_t *devpart) {
|
||||
return 0;
|
||||
} else if (mmcpart->device == &g_emmc_device) {
|
||||
if (!g_emmc_device_initialized) {
|
||||
int rc = sdmmc_device_mmc_init(mmcpart->device, &g_emmc_sdmmc, SDMMC_BUS_WIDTH_8BIT, SDMMC_SPEED_HS400) ? 0 : EIO;
|
||||
int rc = sdmmc_device_mmc_init(mmcpart->device, &g_emmc_sdmmc, SDMMC_BUS_WIDTH_8BIT, SDMMC_SPEED_MMC_HS400) ? 0 : EIO;
|
||||
if (rc)
|
||||
return rc;
|
||||
g_emmc_device_initialized = true;
|
||||
@@ -100,6 +108,9 @@ static void mmc_partition_finalize(device_partition_t *devpart) {
|
||||
|
||||
/* Finalize hardware. */
|
||||
if (mmcpart->device == &g_sd_device) {
|
||||
if (g_is_emummc) {
|
||||
return;
|
||||
}
|
||||
if (g_sd_device_initialized) {
|
||||
sdmmc_device_finish(&g_sd_device);
|
||||
g_sd_device_initialized = false;
|
||||
@@ -149,6 +160,61 @@ static int mmc_partition_write(device_partition_t *devpart, const void *src, uin
|
||||
return sdmmc_device_write(mmcpart->device, (uint32_t)(devpart->start_sector + sector), (uint32_t)num_sectors, (void *)src) ? 0 : EIO;
|
||||
}
|
||||
|
||||
static int emummc_partition_initialize(device_partition_t *devpart) {
|
||||
/* Allocate the crypto work buffer. */
|
||||
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 the crypto work buffer. */
|
||||
if (devpart->crypto_work_buffer != NULL) {
|
||||
free(devpart->crypto_work_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static int emummc_partition_read(device_partition_t *devpart, void *dst, uint64_t sector, uint64_t num_sectors) {
|
||||
if (devpart->emu_use_file) {
|
||||
/* Read partition data using our backing file. */
|
||||
int rc = 0;
|
||||
FILE *emummc_file = fopen(devpart->emu_file_path, "rb");
|
||||
fseek(emummc_file, sector * devpart->sector_size, SEEK_CUR);
|
||||
rc = (fread(dst, devpart->sector_size, num_sectors, emummc_file) > 0) ? 0 : -1;
|
||||
fclose(emummc_file);
|
||||
return rc;
|
||||
} else {
|
||||
/* Read partition data directly from the SD card device. */
|
||||
return sdmmc_device_read(&g_sd_device, (uint32_t)(devpart->start_sector + sector), (uint32_t)num_sectors, dst) ? 0 : EIO;
|
||||
}
|
||||
}
|
||||
|
||||
static int emummc_partition_write(device_partition_t *devpart, const void *src, uint64_t sector, uint64_t num_sectors) {
|
||||
if (devpart->emu_use_file) {
|
||||
/* Write partition data using our backing file. */
|
||||
int rc = 0;
|
||||
FILE *emummc_file = fopen(devpart->emu_file_path, "wb");
|
||||
fseek(emummc_file, sector * devpart->sector_size, SEEK_CUR);
|
||||
rc = (fwrite(src, devpart->sector_size, num_sectors, emummc_file) > 0) ? 0 : -1;
|
||||
fclose(emummc_file);
|
||||
return rc;
|
||||
} else {
|
||||
/* Write partition data directly to the SD card device. */
|
||||
return sdmmc_device_write(&g_sd_device, (uint32_t)(devpart->start_sector + sector), (uint32_t)num_sectors, (void *)src) ? 0 : EIO;
|
||||
}
|
||||
}
|
||||
|
||||
static int nxfs_bis_crypto_decrypt(device_partition_t *devpart, uint64_t sector, uint64_t num_sectors) {
|
||||
unsigned int keyslot_a = 4; /* These keyslots are never used by exosphere, and should be safe. */
|
||||
unsigned int keyslot_b = 5;
|
||||
@@ -197,6 +263,14 @@ static const device_partition_t g_mmc_devpart_template = {
|
||||
.writer = mmc_partition_write,
|
||||
};
|
||||
|
||||
static const device_partition_t g_emummc_devpart_template = {
|
||||
.sector_size = 512,
|
||||
.initializer = emummc_partition_initialize,
|
||||
.finalizer = emummc_partition_finalize,
|
||||
.reader = emummc_partition_read,
|
||||
.writer = emummc_partition_write,
|
||||
};
|
||||
|
||||
static int nxfs_mount_partition_gpt_callback(const efi_entry_t *entry, void *param, size_t entry_offset, FILE *disk) {
|
||||
(void)entry_offset;
|
||||
(void)disk;
|
||||
@@ -278,53 +352,158 @@ static int nxfs_mount_partition_gpt_callback(const efi_entry_t *entry, void *par
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nxfs_mount_all(void) {
|
||||
static int nxfs_mount_emu_partition_gpt_callback(const efi_entry_t *entry, void *param, size_t entry_offset, FILE *disk, const char *origin_path, bool is_multipart, int num_parts, uint64_t part_limit) {
|
||||
(void)entry_offset;
|
||||
(void)disk;
|
||||
device_partition_t *parent = (device_partition_t *)param;
|
||||
device_partition_t devpart = *parent;
|
||||
char name_buffer[128];
|
||||
const uint16_t *utf16name = entry->name;
|
||||
uint32_t name_len;
|
||||
int rc;
|
||||
|
||||
static const struct {
|
||||
const char *partition_name;
|
||||
const char *mount_point;
|
||||
bool is_fat;
|
||||
bool is_encrypted;
|
||||
bool register_immediately;
|
||||
} known_partitions[] = {
|
||||
{"PRODINFO", "prodinfo", false, true, false},
|
||||
{"PRODINFOF", "prodinfof", true, true, false},
|
||||
{"BCPKG2-1-Normal-Main", "bcpkg21", false, false, true},
|
||||
{"BCPKG2-2-Normal-Sub", "bcpkg22", false, false, false},
|
||||
{"BCPKG2-3-SafeMode-Main", "bcpkg23", false, false, false},
|
||||
{"BCPKG2-4-SafeMode-Sub", "bcpkg24", false, false, false},
|
||||
{"BCPKG2-5-Repair-Main", "bcpkg25", false, false, false},
|
||||
{"BCPKG2-6-Repair-Sub", "bcpkg26", false, false, false},
|
||||
{"SAFE", "safe", true, true, false},
|
||||
{"SYSTEM", "system", true, true, false},
|
||||
{"USER", "user", true, true, false},
|
||||
};
|
||||
|
||||
/* Convert the partition name to ASCII, for comparison. */
|
||||
for (name_len = 0; name_len < sizeof(entry->name) && *utf16name != 0; name_len++) {
|
||||
name_buffer[name_len] = (char)*utf16name++;
|
||||
}
|
||||
name_buffer[name_len] = '\0';
|
||||
|
||||
/* Mount the partition, if we know about it. */
|
||||
for (size_t i = 0; i < sizeof(known_partitions)/sizeof(known_partitions[0]); i++) {
|
||||
if (strcmp(name_buffer, known_partitions[i].partition_name) == 0) {
|
||||
devpart.start_sector += entry->first_lba;
|
||||
devpart.num_sectors = (entry->last_lba + 1) - entry->first_lba;
|
||||
if (parent->num_sectors < devpart.num_sectors) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (known_partitions[i].is_encrypted) {
|
||||
devpart.read_cipher = nxfs_bis_crypto_decrypt;
|
||||
devpart.write_cipher = nxfs_bis_crypto_encrypt;
|
||||
devpart.crypto_mode = DevicePartitionCryptoMode_Xts;
|
||||
}
|
||||
|
||||
if (known_partitions[i].is_fat) {
|
||||
rc = fsdev_mount_device(known_partitions[i].mount_point, &devpart, false);
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
if (known_partitions[i].register_immediately) {
|
||||
rc = fsdev_register_device(known_partitions[i].mount_point);
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (is_multipart) {
|
||||
rc = emudev_mount_device_multipart(known_partitions[i].mount_point, &devpart, origin_path, num_parts, part_limit);
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
rc = emudev_mount_device(known_partitions[i].mount_point, &devpart, origin_path);
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (known_partitions[i].register_immediately) {
|
||||
rc = emudev_register_device(known_partitions[i].mount_point);
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nxfs_mount_sd() {
|
||||
device_partition_t model;
|
||||
int rc;
|
||||
FILE *rawnand;
|
||||
|
||||
/* Initialize the SD card and its primary partition. */
|
||||
/* Setup a template for the SD card. */
|
||||
model = g_mmc_devpart_template;
|
||||
model.device_struct = &g_sd_mmcpart;
|
||||
model.start_sector = 0;
|
||||
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);
|
||||
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Register the SD card device. */
|
||||
rc = fsdev_register_device("sdmc");
|
||||
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* All fs devices are ready. */
|
||||
if (rc == 0) {
|
||||
g_fsdev_ready = true;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Boot0. */
|
||||
int nxfs_mount_emmc() {
|
||||
device_partition_t model;
|
||||
int rc;
|
||||
FILE *rawnand;
|
||||
|
||||
/* Setup a template for boot0. */
|
||||
model = g_mmc_devpart_template;
|
||||
model.device_struct = &g_emmc_boot0_mmcpart;
|
||||
model.start_sector = 0;
|
||||
model.num_sectors = 0x184000 / model.sector_size;
|
||||
|
||||
/* Mount boot0 device. */
|
||||
rc = rawdev_mount_device("boot0", &model, true);
|
||||
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Register boot0 device. */
|
||||
rc = rawdev_register_device("boot0");
|
||||
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Boot1. */
|
||||
/* Setup a template for boot1. */
|
||||
model = g_mmc_devpart_template;
|
||||
model.device_struct = &g_emmc_boot1_mmcpart;
|
||||
model.start_sector = 0;
|
||||
model.num_sectors = 0x80000 / model.sector_size;
|
||||
|
||||
/* Mount boot1 device. */
|
||||
rc = rawdev_mount_device("boot1", &model, false);
|
||||
|
||||
if (rc == -1) {
|
||||
@@ -333,33 +512,321 @@ int nxfs_mount_all(void) {
|
||||
|
||||
/* Don't register boot1 for now. */
|
||||
|
||||
/* Raw NAND (excluding boot partitions), and its partitions. */
|
||||
model = g_mmc_devpart_template;
|
||||
/* Setup a template for raw NAND. */
|
||||
model = g_mmc_devpart_template;
|
||||
model.device_struct = &g_emmc_user_mmcpart;
|
||||
model.start_sector = 0;
|
||||
model.num_sectors = (256ull << 30) / model.sector_size;
|
||||
|
||||
/* Mount raw NAND device. */
|
||||
rc = rawdev_mount_device("rawnand", &model, false);
|
||||
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Register raw NAND device. */
|
||||
rc = rawdev_register_device("rawnand");
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Open raw NAND device. */
|
||||
rawnand = fopen("rawnand:/", "rb");
|
||||
|
||||
if (rawnand == NULL) {
|
||||
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);
|
||||
|
||||
/* Close raw NAND device. */
|
||||
fclose(rawnand);
|
||||
|
||||
/* All raw devices are ready. */
|
||||
if (rc == 0) {
|
||||
g_rawdev_ready = true;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int nxfs_mount_emummc_partition(uint64_t emummc_start_sector) {
|
||||
device_partition_t model;
|
||||
int rc;
|
||||
FILE *rawnand;
|
||||
|
||||
/* Setup an emulation template for boot0. */
|
||||
model = g_emummc_devpart_template;
|
||||
model.start_sector = emummc_start_sector + (0x400000 * 0 / model.sector_size);
|
||||
model.num_sectors = 0x400000 / model.sector_size;
|
||||
model.emu_use_file = false;
|
||||
|
||||
/* Mount emulated boot0 device. */
|
||||
rc = emudev_mount_device("boot0", &model, NULL);
|
||||
|
||||
/* Failed to mount boot0 device. */
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Register emulated boot0 device. */
|
||||
rc = emudev_register_device("boot0");
|
||||
|
||||
/* Failed to register boot0 device. */
|
||||
if (rc == -1) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Setup an emulation template for boot1. */
|
||||
model = g_emummc_devpart_template;
|
||||
model.start_sector = emummc_start_sector + (0x400000 * 1 / model.sector_size);
|
||||
model.num_sectors = 0x400000 / model.sector_size;
|
||||
model.emu_use_file = false;
|
||||
|
||||
/* Mount emulated boot1 device. */
|
||||
rc = emudev_mount_device("boot1", &model, NULL);
|
||||
|
||||
/* Failed to mount boot1. */
|
||||
if (rc == -1) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
/* Don't register emulated boot1 for now. */
|
||||
|
||||
/* Setup a template for raw NAND. */
|
||||
model = g_emummc_devpart_template;
|
||||
model.start_sector = emummc_start_sector + (0x400000 * 2 / model.sector_size);
|
||||
model.num_sectors = (256ull << 30) / model.sector_size;
|
||||
model.emu_use_file = false;
|
||||
|
||||
/* Mount emulated raw NAND device. */
|
||||
rc = emudev_mount_device("rawnand", &model, NULL);
|
||||
|
||||
/* Failed to mount raw NAND. */
|
||||
if (rc == -1) {
|
||||
return -4;
|
||||
}
|
||||
|
||||
/* Register emulated raw NAND device. */
|
||||
rc = emudev_register_device("rawnand");
|
||||
|
||||
/* Failed to register raw NAND device. */
|
||||
if (rc == -1) {
|
||||
return -5;
|
||||
}
|
||||
|
||||
/* Open emulated raw NAND device. */
|
||||
rawnand = fopen("rawnand:/", "rb");
|
||||
|
||||
/* Failed to open emulated raw NAND device. */
|
||||
if (rawnand == NULL) {
|
||||
return -6;
|
||||
}
|
||||
|
||||
/* 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, NULL, false, 0, 0);
|
||||
|
||||
/* Close emulated raw NAND device. */
|
||||
fclose(rawnand);
|
||||
|
||||
/* All emulated devices are ready. */
|
||||
if (rc == 0) {
|
||||
g_emudev_ready = true;
|
||||
g_is_emummc = true;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int nxfs_mount_emummc_file(const char *emummc_path, int num_parts, uint64_t part_limit) {
|
||||
device_partition_t model;
|
||||
int rc;
|
||||
FILE *rawnand;
|
||||
bool is_exfat;
|
||||
char emummc_boot0_path[0x300 + 1] = {0};
|
||||
char emummc_boot1_path[0x300 + 1] = {0};
|
||||
char emummc_rawnand_path[0x300 + 1] = {0};
|
||||
|
||||
/* Check if the SD card is EXFAT formatted. */
|
||||
rc = fsdev_is_exfat("sdmc");
|
||||
|
||||
/* Failed to detect file system type. */
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set EXFAT status. */
|
||||
is_exfat = (rc == 1);
|
||||
|
||||
/* We want a folder with the archive bit set. */
|
||||
rc = fsdev_get_attr(emummc_path);
|
||||
|
||||
/* Failed to get file DOS attributes. */
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Our path is not a directory. */
|
||||
if (!(rc & AM_DIR)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check if the archive bit is not set. */
|
||||
if (!(rc & AM_ARC)) {
|
||||
/* Try to set the archive bit. */
|
||||
rc = fsdev_set_attr(emummc_path, AM_ARC, AM_ARC);
|
||||
|
||||
/* Failed to set file DOS attributes. */
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup an emulation template for boot0. */
|
||||
model = g_emummc_devpart_template;
|
||||
model.start_sector = 0;
|
||||
model.num_sectors = 0x400000 / model.sector_size;
|
||||
model.emu_use_file = true;
|
||||
|
||||
/* Prepare boot0 file path. */
|
||||
snprintf(emummc_boot0_path, sizeof(emummc_boot0_path) - 1, "sdmc:/%s/%s", emummc_path, "boot0");
|
||||
|
||||
/* Mount emulated boot0 device. */
|
||||
rc = emudev_mount_device("boot0", &model, emummc_boot0_path);
|
||||
|
||||
/* Failed to mount boot0 device. */
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Register emulated boot0 device. */
|
||||
rc = emudev_register_device("boot0");
|
||||
|
||||
/* Failed to register boot0 device. */
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Setup an emulation template for boot1. */
|
||||
model = g_emummc_devpart_template;
|
||||
model.start_sector = 0;
|
||||
model.num_sectors = 0x400000 / model.sector_size;
|
||||
model.emu_use_file = true;
|
||||
|
||||
/* Prepare boot1 file path. */
|
||||
snprintf(emummc_boot1_path, sizeof(emummc_boot1_path) - 1, "sdmc:/%s/%s", emummc_path, "boot1");
|
||||
|
||||
/* Mount emulated boot1 device. */
|
||||
rc = emudev_mount_device("boot1", &model, emummc_boot1_path);
|
||||
|
||||
/* Failed to mount boot1. */
|
||||
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;
|
||||
model.emu_use_file = true;
|
||||
|
||||
/* Prepare single raw NAND file path. */
|
||||
snprintf(emummc_rawnand_path, sizeof(emummc_rawnand_path) - 1, "sdmc:/%s/%02d", emummc_path, 0);
|
||||
|
||||
/* Mount emulated raw NAND device from single or multiple parts. */
|
||||
if (!is_exfat) {
|
||||
rc = emudev_mount_device_multipart("rawnand", &model, emummc_path, num_parts, part_limit);
|
||||
} else {
|
||||
rc = emudev_mount_device("rawnand", &model, emummc_rawnand_path);
|
||||
}
|
||||
|
||||
/* Failed to mount raw NAND. */
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Register emulated raw NAND device. */
|
||||
rc = emudev_register_device("rawnand");
|
||||
|
||||
/* Failed to register raw NAND device. */
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Open emulated raw NAND device. */
|
||||
rawnand = fopen("rawnand:/", "rb");
|
||||
|
||||
/* Failed to open emulated raw NAND device. */
|
||||
if (rawnand == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Iterate the GPT and mount each emulated raw NAND partition. */
|
||||
if (!is_exfat) {
|
||||
rc = gpt_iterate_through_emu_entries(rawnand, model.sector_size, nxfs_mount_emu_partition_gpt_callback, &model, emummc_path, true, num_parts, part_limit);
|
||||
} else {
|
||||
rc = gpt_iterate_through_emu_entries(rawnand, model.sector_size, nxfs_mount_emu_partition_gpt_callback, &model, emummc_rawnand_path, false, 0, 0);
|
||||
}
|
||||
|
||||
/* Close emulated raw NAND device. */
|
||||
fclose(rawnand);
|
||||
|
||||
/* All emulated devices are ready. */
|
||||
if (rc == 0) {
|
||||
g_emudev_ready = true;
|
||||
g_is_emummc = true;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int nxfs_unmount_sd() {
|
||||
int rc = 0;
|
||||
|
||||
/* Unmount all fs devices. */
|
||||
if (g_fsdev_ready) {
|
||||
rc = fsdev_unmount_all();
|
||||
g_fsdev_ready = false;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int nxfs_unmount_emmc() {
|
||||
int rc = 0;
|
||||
|
||||
/* Unmount all raw devices. */
|
||||
if (g_rawdev_ready) {
|
||||
rc = rawdev_unmount_all();
|
||||
g_rawdev_ready = false;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int nxfs_unmount_emummc() {
|
||||
int rc = 0;
|
||||
|
||||
/* Unmount all emulated devices. */
|
||||
if (g_emudev_ready) {
|
||||
rc = emudev_unmount_all();
|
||||
g_emudev_ready = false;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int nxfs_init() {
|
||||
int rc;
|
||||
|
||||
/* Mount and register the SD card. */
|
||||
rc = nxfs_mount_sd();
|
||||
|
||||
/* Set the SD card as the default file system device. */
|
||||
if (rc == 0) {
|
||||
rc = fsdev_set_default_device("sdmc");
|
||||
}
|
||||
@@ -367,6 +834,6 @@ int nxfs_mount_all(void) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
int nxfs_unmount_all(void) {
|
||||
return ((fsdev_unmount_all() || rawdev_unmount_all()) ? -1 : 0);
|
||||
int nxfs_end() {
|
||||
return ((nxfs_unmount_sd() || nxfs_unmount_emmc() || nxfs_unmount_emummc()) ? -1 : 0);
|
||||
}
|
||||
|
||||
@@ -19,8 +19,17 @@
|
||||
|
||||
#include "fs_dev.h"
|
||||
#include "raw_dev.h"
|
||||
#include "emu_dev.h"
|
||||
|
||||
int nxfs_mount_all(void);
|
||||
int nxfs_unmount_all(void);
|
||||
int nxfs_init();
|
||||
int nxfs_end();
|
||||
|
||||
int nxfs_mount_sd();
|
||||
int nxfs_mount_emmc();
|
||||
int nxfs_mount_emummc_partition(uint64_t emummc_start_sector);
|
||||
int nxfs_mount_emummc_file(const char *emummc_path, int num_parts, uint64_t part_limit);
|
||||
int nxfs_unmount_sd();
|
||||
int nxfs_unmount_emmc();
|
||||
int nxfs_unmount_emummc();
|
||||
|
||||
#endif
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
static void package2_decrypt(package2_header_t *package2);
|
||||
static size_t package2_get_src_section(void **section, package2_header_t *package2, unsigned int id);
|
||||
static size_t package2_get_thermosphere(void **thermosphere);
|
||||
static ini1_header_t *package2_rebuild_ini1(ini1_header_t *ini1, uint32_t target_firmware);
|
||||
static ini1_header_t *package2_rebuild_ini1(ini1_header_t *ini1, uint32_t target_firmware, void *emummc, size_t emummc_size);
|
||||
static void package2_append_section(unsigned int id, package2_header_t *package2, void *data, size_t size);
|
||||
static void package2_fixup_header_and_section_hashes(package2_header_t *package2, size_t size);
|
||||
|
||||
@@ -44,7 +44,7 @@ static inline size_t align_to_4(size_t s) {
|
||||
return ((s + 3) >> 2) << 2;
|
||||
}
|
||||
|
||||
void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firmware) {
|
||||
void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firmware, void *emummc, size_t emummc_size) {
|
||||
package2_header_t *rebuilt_package2;
|
||||
size_t rebuilt_package2_size;
|
||||
void *kernel;
|
||||
@@ -104,7 +104,7 @@ void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firm
|
||||
}
|
||||
|
||||
/* Perform any patches to the INI1, rebuilding it (This is where our built-in sysmodules will be added.) */
|
||||
rebuilt_ini1 = package2_rebuild_ini1(orig_ini1, target_firmware);
|
||||
rebuilt_ini1 = package2_rebuild_ini1(orig_ini1, target_firmware, emummc, emummc_size);
|
||||
print(SCREEN_LOG_LEVEL_DEBUG, "Rebuilt INI1...\n");
|
||||
|
||||
/* Allocate the rebuilt package2. */
|
||||
@@ -317,7 +317,7 @@ static size_t package2_get_thermosphere(void **thermosphere) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ini1_header_t *package2_rebuild_ini1(ini1_header_t *ini1, uint32_t target_firmware) {
|
||||
static ini1_header_t *package2_rebuild_ini1(ini1_header_t *ini1, uint32_t target_firmware, void *emummc, size_t emummc_size) {
|
||||
/* TODO: Do we want to support loading another INI from sd:/whatever/INI1.bin? */
|
||||
ini1_header_t *inis_to_merge[STRATOSPHERE_INI1_MAX] = {0};
|
||||
ini1_header_t *merged;
|
||||
@@ -327,7 +327,7 @@ static ini1_header_t *package2_rebuild_ini1(ini1_header_t *ini1, uint32_t target
|
||||
inis_to_merge[STRATOSPHERE_INI1_PACKAGE2] = ini1;
|
||||
|
||||
/* Merge all of the INI1s. */
|
||||
merged = stratosphere_merge_inis(inis_to_merge, STRATOSPHERE_INI1_MAX);
|
||||
merged = stratosphere_merge_inis(inis_to_merge, STRATOSPHERE_INI1_MAX, emummc, emummc_size);
|
||||
|
||||
/* Free temporary buffer. */
|
||||
stratosphere_free_ini1();
|
||||
|
||||
@@ -86,6 +86,6 @@ static inline uint8_t package2_meta_get_header_version(const package2_meta_t *me
|
||||
return (uint8_t)((metadata->ctr_dwords[1] ^ (metadata->ctr_dwords[1] >> 16) ^ (metadata->ctr_dwords[1] >> 24)) & 0xFF);
|
||||
}
|
||||
|
||||
void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firmware);
|
||||
void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firmware, void *emummc, size_t emummc_size);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
|
||||
int rawdev_mount_device(const char *name, const device_partition_t *devpart, bool initialize_immediately);
|
||||
int rawdev_register_device(const char *name);
|
||||
|
||||
int rawdev_unregister_device(const char *name);
|
||||
int rawdev_unmount_device(const char *name); /* also unregisters. */
|
||||
int rawdev_unmount_device(const char *name); /* also unregisters. */
|
||||
|
||||
int rawdev_unmount_all(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -727,11 +727,11 @@ static int sdmmc_sd_switch_hs_low(sdmmc_device_t *device, uint8_t *status)
|
||||
return 0;
|
||||
|
||||
/* Reconfigure the internal clock. */
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_SDR104))
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_UHS_SDR104))
|
||||
return 0;
|
||||
|
||||
/* Run tuning. */
|
||||
if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_SDR104, MMC_SEND_TUNING_BLOCK))
|
||||
if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_UHS_SDR104, MMC_SEND_TUNING_BLOCK))
|
||||
return 0;
|
||||
}
|
||||
else if (status[13] & SD_MODE_UHS_SDR50) /* High-speed SDR50 is supported. */
|
||||
@@ -741,11 +741,11 @@ static int sdmmc_sd_switch_hs_low(sdmmc_device_t *device, uint8_t *status)
|
||||
return 0;
|
||||
|
||||
/* Reconfigure the internal clock. */
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_SDR50))
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_UHS_SDR50))
|
||||
return 0;
|
||||
|
||||
/* Run tuning. */
|
||||
if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_SDR50, MMC_SEND_TUNING_BLOCK))
|
||||
if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_UHS_SDR50, MMC_SEND_TUNING_BLOCK))
|
||||
return 0;
|
||||
}
|
||||
else if (status[13] & SD_MODE_UHS_SDR12) /* High-speed SDR12 is supported. */
|
||||
@@ -755,11 +755,11 @@ static int sdmmc_sd_switch_hs_low(sdmmc_device_t *device, uint8_t *status)
|
||||
return 0;
|
||||
|
||||
/* Reconfigure the internal clock. */
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_SDR12))
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_UHS_SDR12))
|
||||
return 0;
|
||||
|
||||
/* Run tuning. */
|
||||
if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_SDR12, MMC_SEND_TUNING_BLOCK))
|
||||
if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_UHS_SDR12, MMC_SEND_TUNING_BLOCK))
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
@@ -784,7 +784,7 @@ static int sdmmc_sd_switch_hs_high(sdmmc_device_t *device, uint8_t *status)
|
||||
return 0;
|
||||
|
||||
/* Reconfigure the internal clock. */
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_SDR25))
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_UHS_SDR25))
|
||||
return 0;
|
||||
|
||||
/* Peek the SD card's status. */
|
||||
@@ -841,7 +841,7 @@ int sdmmc_device_sd_init(sdmmc_device_t *device, sdmmc_t *sdmmc, SdmmcBusWidth b
|
||||
memset(device, 0, sizeof(sdmmc_device_t));
|
||||
|
||||
/* Try to initialize the driver. */
|
||||
if (!sdmmc_init(sdmmc, SDMMC_1, SDMMC_VOLTAGE_3V3, SDMMC_BUS_WIDTH_1BIT, SDMMC_SPEED_INIT_SDR))
|
||||
if (!sdmmc_init(sdmmc, SDMMC_1, SDMMC_VOLTAGE_3V3, SDMMC_BUS_WIDTH_1BIT, SDMMC_SPEED_SD_INIT))
|
||||
{
|
||||
sdmmc_error(sdmmc, "Failed to initialize the SDMMC driver!");
|
||||
return 0;
|
||||
@@ -874,7 +874,7 @@ int sdmmc_device_sd_init(sdmmc_device_t *device, sdmmc_t *sdmmc, SdmmcBusWidth b
|
||||
sdmmc_info(sdmmc, "Sent if cond to SD card!");
|
||||
|
||||
/* Get the SD card's operating conditions. */
|
||||
if (!sdmmc_sd_send_op_cond(device, is_sd_ver2, (bus_width == SDMMC_BUS_WIDTH_4BIT) && (bus_speed == SDMMC_SPEED_SDR104)))
|
||||
if (!sdmmc_sd_send_op_cond(device, is_sd_ver2, (bus_width == SDMMC_BUS_WIDTH_4BIT) && (bus_speed == SDMMC_SPEED_UHS_SDR104)))
|
||||
{
|
||||
sdmmc_error(sdmmc, "Failed to send op cond!");
|
||||
return 0;
|
||||
@@ -920,7 +920,7 @@ int sdmmc_device_sd_init(sdmmc_device_t *device, sdmmc_t *sdmmc, SdmmcBusWidth b
|
||||
if (!device->is_180v)
|
||||
{
|
||||
/* Reconfigure the internal clock. */
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_UNK6))
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_SD_LEGACY))
|
||||
{
|
||||
sdmmc_error(sdmmc, "Failed to apply the correct bus speed!");
|
||||
return 0;
|
||||
@@ -998,7 +998,7 @@ int sdmmc_device_sd_init(sdmmc_device_t *device, sdmmc_t *sdmmc, SdmmcBusWidth b
|
||||
|
||||
sdmmc_info(sdmmc, "Switched to high-speed from low voltage!");
|
||||
}
|
||||
else if ((device->scr.sda_vsn & (SD_SCR_SPEC_VER_1 | SD_SCR_SPEC_VER_2)) && ((bus_speed != SDMMC_SPEED_UNK6)))
|
||||
else if ((device->scr.sda_vsn & (SD_SCR_SPEC_VER_1 | SD_SCR_SPEC_VER_2)) && ((bus_speed != SDMMC_SPEED_SD_LEGACY)))
|
||||
{
|
||||
/* Switch to high-speed from high voltage (if possible). */
|
||||
if (!sdmmc_sd_switch_hs_high(device, switch_status))
|
||||
@@ -1273,7 +1273,7 @@ static int sdmmc_mmc_select_hs(sdmmc_device_t *device, bool ignore_status)
|
||||
return 0;
|
||||
|
||||
/* Reconfigure the internal clock. */
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_HS52))
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_MMC_HS))
|
||||
return 0;
|
||||
|
||||
/* Check the status if necessary. */
|
||||
@@ -1292,11 +1292,11 @@ static int sdmmc_mmc_select_hs200(sdmmc_device_t *device)
|
||||
return 0;
|
||||
|
||||
/* Reconfigure the internal clock. */
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_HS200))
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_MMC_HS200))
|
||||
return 0;
|
||||
|
||||
/* Execute tuning procedure. */
|
||||
if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_HS200, MMC_SEND_TUNING_BLOCK_HS200))
|
||||
if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_MMC_HS200, MMC_SEND_TUNING_BLOCK_HS200))
|
||||
return 0;
|
||||
|
||||
/* Peek the current status. */
|
||||
@@ -1331,7 +1331,7 @@ static int sdmmc_mmc_select_hs400(sdmmc_device_t *device)
|
||||
return 0;
|
||||
|
||||
/* Reconfigure the internal clock. */
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_HS400))
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_MMC_HS400))
|
||||
return 0;
|
||||
|
||||
/* Peek the current status. */
|
||||
@@ -1340,14 +1340,14 @@ static int sdmmc_mmc_select_hs400(sdmmc_device_t *device)
|
||||
|
||||
static int sdmmc_mmc_select_timing(sdmmc_device_t *device, SdmmcBusSpeed bus_speed)
|
||||
{
|
||||
if ((bus_speed == SDMMC_SPEED_HS400) &&
|
||||
if ((bus_speed == SDMMC_SPEED_MMC_HS400) &&
|
||||
(device->sdmmc->bus_width == SDMMC_BUS_WIDTH_8BIT) &&
|
||||
(device->ext_csd.raw_card_type & EXT_CSD_CARD_TYPE_HS400_1_8V))
|
||||
{
|
||||
/* Switch to HS400. */
|
||||
return sdmmc_mmc_select_hs400(device);
|
||||
}
|
||||
else if (((bus_speed == SDMMC_SPEED_HS400) || (bus_speed == SDMMC_SPEED_HS200)) &&
|
||||
else if (((bus_speed == SDMMC_SPEED_MMC_HS400) || (bus_speed == SDMMC_SPEED_MMC_HS200)) &&
|
||||
((device->sdmmc->bus_width == SDMMC_BUS_WIDTH_8BIT) || (device->sdmmc->bus_width == SDMMC_BUS_WIDTH_4BIT)) &&
|
||||
(device->ext_csd.raw_card_type & EXT_CSD_CARD_TYPE_HS200_1_8V))
|
||||
{
|
||||
@@ -1397,7 +1397,7 @@ int sdmmc_device_mmc_init(sdmmc_device_t *device, sdmmc_t *sdmmc, SdmmcBusWidth
|
||||
memset(device, 0, sizeof(sdmmc_device_t));
|
||||
|
||||
/* Try to initialize the driver. */
|
||||
if (!sdmmc_init(sdmmc, SDMMC_4, SDMMC_VOLTAGE_1V8, SDMMC_BUS_WIDTH_1BIT, SDMMC_SPEED_INIT_HS))
|
||||
if (!sdmmc_init(sdmmc, SDMMC_4, SDMMC_VOLTAGE_1V8, SDMMC_BUS_WIDTH_1BIT, SDMMC_SPEED_MMC_INIT))
|
||||
{
|
||||
sdmmc_error(sdmmc, "Failed to initialize the SDMMC driver!");
|
||||
return 0;
|
||||
@@ -1464,7 +1464,7 @@ int sdmmc_device_mmc_init(sdmmc_device_t *device, sdmmc_t *sdmmc, SdmmcBusWidth
|
||||
sdmmc_warn(sdmmc, "Got unknown CSD structure (0x%08x)!", device->csd.structure);
|
||||
|
||||
/* Reconfigure the internal clock. */
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_HS26))
|
||||
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_MMC_LEGACY))
|
||||
{
|
||||
sdmmc_error(sdmmc, "Failed to apply the correct bus speed!");
|
||||
return 0;
|
||||
|
||||
@@ -289,26 +289,28 @@ static int sdmmc_get_sdclk_freq(SdmmcBusSpeed bus_speed)
|
||||
{
|
||||
switch (bus_speed)
|
||||
{
|
||||
case SDMMC_SPEED_INIT_HS:
|
||||
case SDMMC_SPEED_HS26:
|
||||
case SDMMC_SPEED_MMC_INIT:
|
||||
case SDMMC_SPEED_MMC_LEGACY:
|
||||
return 26000;
|
||||
case SDMMC_SPEED_HS52:
|
||||
case SDMMC_SPEED_MMC_HS:
|
||||
return 52000;
|
||||
case SDMMC_SPEED_HS200:
|
||||
case SDMMC_SPEED_HS400:
|
||||
case SDMMC_SPEED_SDR104:
|
||||
case SDMMC_SPEED_MMC_HS200:
|
||||
case SDMMC_SPEED_MMC_HS400:
|
||||
case SDMMC_SPEED_UHS_SDR104:
|
||||
case SDMMC_SPEED_EMU_SDR104:
|
||||
return 200000;
|
||||
case SDMMC_SPEED_INIT_SDR:
|
||||
case SDMMC_SPEED_UNK6:
|
||||
case SDMMC_SPEED_SDR12:
|
||||
case SDMMC_SPEED_SD_INIT:
|
||||
case SDMMC_SPEED_SD_LEGACY:
|
||||
case SDMMC_SPEED_UHS_SDR12:
|
||||
return 25000;
|
||||
case SDMMC_SPEED_SDR25:
|
||||
case SDMMC_SPEED_SD_HS:
|
||||
case SDMMC_SPEED_UHS_SDR25:
|
||||
return 50000;
|
||||
case SDMMC_SPEED_SDR50:
|
||||
case SDMMC_SPEED_UHS_SDR50:
|
||||
return 100000;
|
||||
case SDMMC_SPEED_DDR50:
|
||||
case SDMMC_SPEED_UHS_DDR50:
|
||||
return 40800;
|
||||
case SDMMC_SPEED_UNK14:
|
||||
case SDMMC_SPEED_MMC_DDR52:
|
||||
return 200000;
|
||||
default:
|
||||
return 0;
|
||||
@@ -320,22 +322,23 @@ static int sdmmc_get_sdclk_div(SdmmcBusSpeed bus_speed)
|
||||
{
|
||||
switch (bus_speed)
|
||||
{
|
||||
case SDMMC_SPEED_INIT_HS:
|
||||
case SDMMC_SPEED_MMC_INIT:
|
||||
return 66;
|
||||
case SDMMC_SPEED_INIT_SDR:
|
||||
// TODO: TRM says return 64?
|
||||
case SDMMC_SPEED_HS26:
|
||||
case SDMMC_SPEED_HS52:
|
||||
case SDMMC_SPEED_HS200:
|
||||
case SDMMC_SPEED_HS400:
|
||||
case SDMMC_SPEED_UNK6:
|
||||
case SDMMC_SPEED_SDR25:
|
||||
case SDMMC_SPEED_SDR12:
|
||||
case SDMMC_SPEED_SDR50:
|
||||
case SDMMC_SPEED_SDR104:
|
||||
case SDMMC_SPEED_DDR50:
|
||||
case SDMMC_SPEED_SD_INIT:
|
||||
case SDMMC_SPEED_MMC_LEGACY:
|
||||
case SDMMC_SPEED_MMC_HS:
|
||||
case SDMMC_SPEED_MMC_HS200:
|
||||
case SDMMC_SPEED_MMC_HS400:
|
||||
case SDMMC_SPEED_SD_LEGACY:
|
||||
case SDMMC_SPEED_SD_HS:
|
||||
case SDMMC_SPEED_UHS_SDR12:
|
||||
case SDMMC_SPEED_UHS_SDR25:
|
||||
case SDMMC_SPEED_UHS_SDR50:
|
||||
case SDMMC_SPEED_UHS_SDR104:
|
||||
case SDMMC_SPEED_UHS_DDR50:
|
||||
case SDMMC_SPEED_EMU_SDR104:
|
||||
return 1;
|
||||
case SDMMC_SPEED_UNK14:
|
||||
case SDMMC_SPEED_MMC_DDR52:
|
||||
return 2;
|
||||
default:
|
||||
return 0;
|
||||
@@ -354,35 +357,35 @@ static int sdmmc_clk_set_source(SdmmcControllerNum controller, uint32_t clk_freq
|
||||
{
|
||||
case 25000:
|
||||
out_freq = 24728;
|
||||
car_div = SDMMC_CAR_DIVIDER_SDR12;
|
||||
car_div = SDMMC_CAR_DIVIDER_UHS_SDR12;
|
||||
break;
|
||||
case 26000:
|
||||
out_freq = 25500;
|
||||
car_div = SDMMC_CAR_DIVIDER_HS26;
|
||||
car_div = SDMMC_CAR_DIVIDER_MMC_LEGACY;
|
||||
break;
|
||||
case 40800:
|
||||
out_freq = 40800;
|
||||
car_div = SDMMC_CAR_DIVIDER_DDR50;
|
||||
car_div = SDMMC_CAR_DIVIDER_UHS_DDR50;
|
||||
break;
|
||||
case 50000:
|
||||
out_freq = 48000;
|
||||
car_div = SDMMC_CAR_DIVIDER_SDR25;
|
||||
car_div = SDMMC_CAR_DIVIDER_UHS_SDR25;
|
||||
break;
|
||||
case 52000:
|
||||
out_freq = 51000;
|
||||
car_div = SDMMC_CAR_DIVIDER_HS52;
|
||||
car_div = SDMMC_CAR_DIVIDER_MMC_HS;
|
||||
break;
|
||||
case 100000:
|
||||
out_freq = 90667;
|
||||
car_div = SDMMC_CAR_DIVIDER_SDR50;
|
||||
car_div = SDMMC_CAR_DIVIDER_UHS_SDR50;
|
||||
break;
|
||||
case 200000:
|
||||
out_freq = 163200;
|
||||
car_div = SDMMC_CAR_DIVIDER_HS200;
|
||||
car_div = SDMMC_CAR_DIVIDER_MMC_HS200;
|
||||
break;
|
||||
case 208000:
|
||||
out_freq = 204000;
|
||||
car_div = SDMMC_CAR_DIVIDER_SDR104;
|
||||
car_div = SDMMC_CAR_DIVIDER_UHS_SDR104;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
@@ -747,7 +750,7 @@ void sdmmc_select_voltage(sdmmc_t *sdmmc, SdmmcBusVoltage voltage)
|
||||
|
||||
static void sdmmc_tap_config(sdmmc_t *sdmmc, SdmmcBusSpeed bus_speed)
|
||||
{
|
||||
if (bus_speed == SDMMC_SPEED_HS400)
|
||||
if (bus_speed == SDMMC_SPEED_MMC_HS400)
|
||||
{
|
||||
/* Clear and set DQS_TRIM_VAL (used in HS400) */
|
||||
sdmmc->regs->vendor_cap_overrides &= ~(0x3F00);
|
||||
@@ -757,7 +760,7 @@ static void sdmmc_tap_config(sdmmc_t *sdmmc, SdmmcBusSpeed bus_speed)
|
||||
/* Clear TAP_VAL_UPDATED_BY_HW */
|
||||
sdmmc->regs->vendor_tuning_cntrl0 &= ~(0x20000);
|
||||
|
||||
if (bus_speed == SDMMC_SPEED_HS400)
|
||||
if (bus_speed == SDMMC_SPEED_MMC_HS400)
|
||||
{
|
||||
/* We must have obtained the tap value from the tuning procedure here. */
|
||||
if (sdmmc->is_tuning_tap_val_set)
|
||||
@@ -863,41 +866,43 @@ int sdmmc_select_speed(sdmmc_t *sdmmc, SdmmcBusSpeed bus_speed)
|
||||
/* Set the appropriate host speed. */
|
||||
switch (bus_speed) {
|
||||
/* 400kHz initialization mode and a few others. */
|
||||
case SDMMC_SPEED_INIT_HS:
|
||||
case SDMMC_SPEED_HS26:
|
||||
case SDMMC_SPEED_INIT_SDR:
|
||||
case SDMMC_SPEED_UNK6:
|
||||
case SDMMC_SPEED_MMC_INIT:
|
||||
case SDMMC_SPEED_MMC_LEGACY:
|
||||
case SDMMC_SPEED_SD_INIT:
|
||||
case SDMMC_SPEED_SD_LEGACY:
|
||||
sdmmc->regs->host_control &= ~(SDHCI_CTRL_HISPD);
|
||||
sdmmc->regs->host_control2 &= ~(SDHCI_CTRL_VDD_180);
|
||||
break;
|
||||
|
||||
/* 50MHz high speed (SD) and 52MHz high speed (MMC). */
|
||||
case SDMMC_SPEED_SDR25:
|
||||
case SDMMC_SPEED_HS52:
|
||||
case SDMMC_SPEED_SD_HS:
|
||||
case SDMMC_SPEED_MMC_HS:
|
||||
case SDMMC_SPEED_UHS_SDR25:
|
||||
sdmmc->regs->host_control |= SDHCI_CTRL_HISPD;
|
||||
sdmmc->regs->host_control2 &= ~(SDHCI_CTRL_VDD_180);
|
||||
break;
|
||||
|
||||
/* 200MHz UHS-I (SD) and other modes due to errata. */
|
||||
case SDMMC_SPEED_HS200:
|
||||
case SDMMC_SPEED_SDR104:
|
||||
case SDMMC_SPEED_DDR50:
|
||||
case SDMMC_SPEED_SDR50:
|
||||
case SDMMC_SPEED_UNK14:
|
||||
case SDMMC_SPEED_MMC_HS200:
|
||||
case SDMMC_SPEED_UHS_SDR104:
|
||||
case SDMMC_SPEED_UHS_DDR50:
|
||||
case SDMMC_SPEED_UHS_SDR50:
|
||||
case SDMMC_SPEED_MMC_DDR52:
|
||||
case SDMMC_SPEED_EMU_SDR104:
|
||||
sdmmc->regs->host_control2 &= ~(SDHCI_CTRL_UHS_MASK);
|
||||
sdmmc->regs->host_control2 |= SDHCI_CTRL_UHS_SDR104;
|
||||
sdmmc->regs->host_control2 |= SDHCI_CTRL_VDD_180;
|
||||
break;
|
||||
|
||||
/* 200MHz single-data rate (MMC). */
|
||||
case SDMMC_SPEED_HS400:
|
||||
case SDMMC_SPEED_MMC_HS400:
|
||||
sdmmc->regs->host_control2 &= ~(SDHCI_CTRL_UHS_MASK);
|
||||
sdmmc->regs->host_control2 |= SDHCI_CTRL_HS400;
|
||||
sdmmc->regs->host_control2 |= SDHCI_CTRL_VDD_180;
|
||||
break;
|
||||
|
||||
/* 25MHz default speed (SD). */
|
||||
case SDMMC_SPEED_SDR12:
|
||||
case SDMMC_SPEED_UHS_SDR12:
|
||||
sdmmc->regs->host_control2 &= ~(SDHCI_CTRL_UHS_MASK);
|
||||
sdmmc->regs->host_control2 |= SDHCI_CTRL_UHS_SDR12;
|
||||
sdmmc->regs->host_control2 |= SDHCI_CTRL_VDD_180;
|
||||
@@ -936,7 +941,7 @@ int sdmmc_select_speed(sdmmc_t *sdmmc, SdmmcBusSpeed bus_speed)
|
||||
sdmmc_enable_sd_clock(sdmmc);
|
||||
|
||||
/* Run DLLCAL for HS400 only */
|
||||
if (bus_speed == SDMMC_SPEED_HS400)
|
||||
if (bus_speed == SDMMC_SPEED_MMC_HS400)
|
||||
return sdmmc_dllcal_run(sdmmc);
|
||||
|
||||
return 1;
|
||||
@@ -1720,7 +1725,7 @@ int sdmmc_switch_voltage(sdmmc_t *sdmmc)
|
||||
sdmmc_disable_sd_clock(sdmmc);
|
||||
|
||||
/* Reconfigure the internal clock. */
|
||||
if (!sdmmc_select_speed(sdmmc, SDMMC_SPEED_SDR12))
|
||||
if (!sdmmc_select_speed(sdmmc, SDMMC_SPEED_UHS_SDR12))
|
||||
{
|
||||
sdmmc_error(sdmmc, "Failed to apply the correct bus speed for low voltage support!");
|
||||
return 0;
|
||||
@@ -1883,15 +1888,16 @@ int sdmmc_execute_tuning(sdmmc_t *sdmmc, SdmmcBusSpeed bus_speed, uint32_t opcod
|
||||
|
||||
switch (bus_speed)
|
||||
{
|
||||
case SDMMC_SPEED_HS200:
|
||||
case SDMMC_SPEED_HS400:
|
||||
case SDMMC_SPEED_SDR104:
|
||||
case SDMMC_SPEED_MMC_HS200:
|
||||
case SDMMC_SPEED_MMC_HS400:
|
||||
case SDMMC_SPEED_UHS_SDR104:
|
||||
case SDMMC_SPEED_EMU_SDR104:
|
||||
max_tuning_loop = 0x80;
|
||||
tuning_cntrl_flag = 0x4000;
|
||||
break;
|
||||
case SDMMC_SPEED_SDR50:
|
||||
case SDMMC_SPEED_DDR50:
|
||||
case SDMMC_SPEED_UNK14:
|
||||
case SDMMC_SPEED_UHS_SDR50:
|
||||
case SDMMC_SPEED_UHS_DDR50:
|
||||
case SDMMC_SPEED_MMC_DDR52:
|
||||
max_tuning_loop = 0x100;
|
||||
tuning_cntrl_flag = 0x8000;
|
||||
break;
|
||||
|
||||
@@ -210,33 +210,33 @@ typedef enum {
|
||||
} SdmmcBusWidth;
|
||||
|
||||
typedef enum {
|
||||
SDMMC_SPEED_INIT_HS = 0,
|
||||
SDMMC_SPEED_HS26 = 1,
|
||||
SDMMC_SPEED_HS52 = 2,
|
||||
SDMMC_SPEED_HS200 = 3,
|
||||
SDMMC_SPEED_HS400 = 4,
|
||||
SDMMC_SPEED_INIT_SDR = 5,
|
||||
SDMMC_SPEED_UNK6 = 6,
|
||||
SDMMC_SPEED_SDR25 = 7,
|
||||
SDMMC_SPEED_SDR12 = 8,
|
||||
SDMMC_SPEED_UNK9 = 9,
|
||||
SDMMC_SPEED_SDR50 = 10,
|
||||
SDMMC_SPEED_SDR104 = 11,
|
||||
SDMMC_SPEED_UNK12 = 12,
|
||||
SDMMC_SPEED_DDR50 = 13,
|
||||
SDMMC_SPEED_UNK14 = 14,
|
||||
SDMMC_SPEED_MMC_INIT = 0,
|
||||
SDMMC_SPEED_MMC_LEGACY = 1,
|
||||
SDMMC_SPEED_MMC_HS = 2,
|
||||
SDMMC_SPEED_MMC_HS200 = 3,
|
||||
SDMMC_SPEED_MMC_HS400 = 4,
|
||||
SDMMC_SPEED_SD_INIT = 5,
|
||||
SDMMC_SPEED_SD_LEGACY = 6,
|
||||
SDMMC_SPEED_SD_HS = 7,
|
||||
SDMMC_SPEED_UHS_SDR12 = 8,
|
||||
SDMMC_SPEED_UHS_SDR25 = 9,
|
||||
SDMMC_SPEED_UHS_SDR50 = 10,
|
||||
SDMMC_SPEED_UHS_SDR104 = 11,
|
||||
SDMMC_SPEED_UHS_RESERVED = 12,
|
||||
SDMMC_SPEED_UHS_DDR50 = 13,
|
||||
SDMMC_SPEED_MMC_DDR52 = 14,
|
||||
SDMMC_SPEED_EMU_SDR104 = 255, /* Custom speed mode. Prevents low voltage switch in MMC emulation. */
|
||||
} SdmmcBusSpeed;
|
||||
|
||||
typedef enum {
|
||||
SDMMC_CAR_DIVIDER_SDR12 = 31, // (16.5 * 2) - 2
|
||||
SDMMC_CAR_DIVIDER_SDR25 = 15, // (8.5 * 2) - 2
|
||||
SDMMC_CAR_DIVIDER_SDR50 = 7, // (4.5 * 2) - 2
|
||||
SDMMC_CAR_DIVIDER_SDR104 = 2, // (2 * 2) - 2
|
||||
SDMMC_CAR_DIVIDER_DDR50 = 18, // (5 * 2 * 2) - 2
|
||||
SDMMC_CAR_DIVIDER_HS26 = 30, // (16 * 2) - 2
|
||||
SDMMC_CAR_DIVIDER_HS52 = 14, // (8 * 2) - 2
|
||||
SDMMC_CAR_DIVIDER_HS200 = 3, // (2.5 * 2) - 2 (for PLLP_OUT0)
|
||||
SDMMC_CAR_DIVIDER_HS400 = 3, // (2.5 * 2) - 2 (for PLLP_OUT0)
|
||||
SDMMC_CAR_DIVIDER_UHS_SDR12 = 31, /* (16.5 * 2) - 2 */
|
||||
SDMMC_CAR_DIVIDER_UHS_SDR25 = 15, /* (8.5 * 2) - 2 */
|
||||
SDMMC_CAR_DIVIDER_UHS_SDR50 = 7, /* (4.5 * 2) - 2 */
|
||||
SDMMC_CAR_DIVIDER_UHS_SDR104 = 2, /* (2 * 2) - 2 */
|
||||
SDMMC_CAR_DIVIDER_UHS_DDR50 = 18, /* (5 * 2 * 2) - 2 */
|
||||
SDMMC_CAR_DIVIDER_MMC_LEGACY = 30, /* (16 * 2) - 2 */
|
||||
SDMMC_CAR_DIVIDER_MMC_HS = 14, /* (8 * 2) - 2 */
|
||||
SDMMC_CAR_DIVIDER_MMC_HS200 = 3, /* (2.5 * 2) - 2 (for PLLP_OUT0, same as HS400) */
|
||||
} SdmmcCarDivider;
|
||||
|
||||
/* Structure for describing a SDMMC device. */
|
||||
@@ -272,7 +272,7 @@ typedef struct {
|
||||
uint32_t opcode;
|
||||
uint32_t arg;
|
||||
uint32_t resp[4];
|
||||
uint32_t flags; /* expected response type */
|
||||
uint32_t flags; /* Expected response type. */
|
||||
} sdmmc_command_t;
|
||||
|
||||
/* Structure for describing a SDMMC request. */
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#define FUSEE_STAGE2_H
|
||||
|
||||
#include "lib/log.h"
|
||||
#include "sdmmc/sdmmc.h"
|
||||
#include "utils.h"
|
||||
|
||||
/* TODO: Is there a more concise way to do this? */
|
||||
@@ -35,4 +34,4 @@ typedef struct {
|
||||
char bct0[BCTO_MAX_SIZE];
|
||||
} stage2_args_t;
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -93,6 +93,7 @@ _metadata:
|
||||
#define CONTENT_TYPE_SP2 5
|
||||
#define CONTENT_TYPE_KIP 6
|
||||
#define CONTENT_TYPE_BMP 7
|
||||
#define CONTENT_TYPE_EMC 8
|
||||
|
||||
_content_headers:
|
||||
/* ams_mitm content header */
|
||||
@@ -191,6 +192,14 @@ _content_headers:
|
||||
.asciz "spl"
|
||||
.align 5
|
||||
|
||||
/* emummc content header */
|
||||
.word __emummc_kip_start__
|
||||
.word __emummc_kip_size__
|
||||
.word CONTENT_TYPE_EMC
|
||||
.word 0xCCCCCCCC
|
||||
.asciz "emummc"
|
||||
.align 5
|
||||
|
||||
/* splash_screen content header */
|
||||
.word __splash_screen_bmp_start__
|
||||
.word __splash_screen_bmp_size__
|
||||
|
||||
@@ -51,6 +51,12 @@ static bool g_stratosphere_boot_enabled = true;
|
||||
extern const uint8_t loader_kip[], pm_kip[], sm_kip[], spl_kip[], boot_kip[], ams_mitm_kip[];
|
||||
extern const uint32_t loader_kip_size, pm_kip_size, sm_kip_size, spl_kip_size, boot_kip_size, ams_mitm_kip_size;
|
||||
|
||||
static emummc_fs_ver_t g_fs_ver = FS_VER_1_0_0;
|
||||
|
||||
emummc_fs_ver_t stratosphere_get_fs_version(void) {
|
||||
return g_fs_ver;
|
||||
}
|
||||
|
||||
/* GCC doesn't consider the size as const... we have to write it ourselves. */
|
||||
|
||||
ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
|
||||
@@ -152,8 +158,6 @@ void stratosphere_free_ini1(void) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void try_add_sd_kip(ini1_header_t *ini1, const char *kip_path) {
|
||||
size_t file_size = get_file_size(kip_path);
|
||||
|
||||
@@ -180,6 +184,60 @@ static void try_add_sd_kip(ini1_header_t *ini1, const char *kip_path) {
|
||||
ini1->num_processes++;
|
||||
}
|
||||
|
||||
static kip1_header_t *inject_emummc_kip(kip1_header_t *fs_kip, kip1_header_t *emummc_kip) {
|
||||
/* Ensure KIPs are uncompressed. */
|
||||
size_t fs_kip_size, emummc_kip_size;
|
||||
fs_kip = kip1_uncompress(fs_kip, &fs_kip_size);
|
||||
emummc_kip = kip1_uncompress(emummc_kip, &emummc_kip_size);
|
||||
|
||||
/* Allocate kip. */
|
||||
kip1_header_t *injected_kip = calloc(1, fs_kip_size + emummc_kip_size);
|
||||
if (injected_kip == NULL) {
|
||||
fatal_error("Failed to allocate memory for emummc kip injection!");
|
||||
}
|
||||
memcpy(injected_kip, fs_kip, sizeof(*fs_kip));
|
||||
|
||||
const size_t fs_contents_size = kip1_get_size_from_header(fs_kip) - sizeof(kip1_header_t);
|
||||
|
||||
const size_t emummc_data_size = emummc_kip->section_headers[3].out_offset + emummc_kip->section_headers[3].out_size;
|
||||
if (emummc_data_size & 0xFFF) {
|
||||
fatal_error("Invalid emummc kip!");
|
||||
}
|
||||
|
||||
/* Copy over capabilities. */
|
||||
memcpy(injected_kip->capabilities, emummc_kip->capabilities, sizeof(emummc_kip->capabilities));
|
||||
|
||||
/* Add extra cap for 1.0.0 */
|
||||
if (stratosphere_get_fs_version() == FS_VER_1_0_0) {
|
||||
for (size_t i = 0; i < 0x20; i++) {
|
||||
if (injected_kip->capabilities[i] == 0xFFFFFFFF) {
|
||||
/* Map PMC registers in. */
|
||||
injected_kip->capabilities[i] = 0x07000E7F;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Update sections. */
|
||||
injected_kip->section_headers[0].out_size += emummc_data_size;
|
||||
injected_kip->section_headers[0].compressed_size += emummc_data_size;
|
||||
for (size_t i = 1; i < 4; i++) {
|
||||
injected_kip->section_headers[i].out_offset += emummc_data_size;
|
||||
}
|
||||
|
||||
/* Copy data. */
|
||||
{
|
||||
size_t ofs = 0;
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
memcpy(injected_kip->data + emummc_kip->section_headers[i].out_offset, emummc_kip->data + ofs, emummc_kip->section_headers[i].compressed_size);
|
||||
ofs += emummc_kip->section_headers[i].compressed_size;
|
||||
}
|
||||
}
|
||||
memcpy(injected_kip->data + emummc_data_size, fs_kip->data, fs_contents_size);
|
||||
|
||||
return injected_kip;
|
||||
}
|
||||
|
||||
ini1_header_t *stratosphere_get_sd_files_ini1(void) {
|
||||
if (g_sd_files_ini1 != NULL) {
|
||||
return g_sd_files_ini1;
|
||||
@@ -246,7 +304,7 @@ ini1_header_t *stratosphere_get_sd_files_ini1(void) {
|
||||
}
|
||||
|
||||
/* Merges some number of INI1s into a single INI1. It's assumed that the INIs are in order of preference. */
|
||||
ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, size_t num_inis) {
|
||||
ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, size_t num_inis, void *emummc, size_t emummc_size) {
|
||||
uint32_t total_num_processes = 0;
|
||||
|
||||
/* Validate all ini headers. */
|
||||
@@ -305,11 +363,16 @@ ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, size_t num_inis) {
|
||||
fatal_error("Not enough space for all the KIP1s!\n");
|
||||
}
|
||||
|
||||
kip1_header_t *patched_kip = apply_kip_ips_patches(current_kip, current_kip_size);
|
||||
kip1_header_t *patched_kip = apply_kip_ips_patches(current_kip, current_kip_size, &g_fs_ver);
|
||||
|
||||
if (current_kip->title_id == FS_TITLE_ID && emummc != NULL) {
|
||||
patched_kip = inject_emummc_kip(patched_kip != NULL ? patched_kip : current_kip, (kip1_header_t *)emummc);
|
||||
}
|
||||
|
||||
if (patched_kip != NULL) {
|
||||
size_t patched_kip_size = kip1_get_size_from_header(patched_kip);
|
||||
if (patched_kip_size > remaining_size) {
|
||||
fatal_error("Not enough space for all the KIP1s!\n");
|
||||
fatal_error("Not enough space for all the KIP1s! %08x %08x\n", remaining_size, patched_kip_size);
|
||||
}
|
||||
memcpy(current_dst_kip, patched_kip, patched_kip_size);
|
||||
remaining_size -= patched_kip_size;
|
||||
@@ -326,6 +389,9 @@ ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, size_t num_inis) {
|
||||
}
|
||||
}
|
||||
merged->size = sizeof(ini1_header_t) + (uint32_t)(current_dst_kip - merged->kip_data);
|
||||
if (merged->size & 3) {
|
||||
merged->size = (merged->size + 3) & ~3;
|
||||
}
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "utils.h"
|
||||
#include "kip.h"
|
||||
#include "exocfg.h"
|
||||
|
||||
#define STRATOSPHERE_INI1_SDFILES 0x0
|
||||
#define STRATOSPHERE_INI1_EMBEDDED 0x1
|
||||
@@ -29,7 +30,9 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware);
|
||||
ini1_header_t *stratosphere_get_sd_files_ini1(void);
|
||||
void stratosphere_free_ini1(void);
|
||||
|
||||
ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, unsigned int num_inis);
|
||||
emummc_fs_ver_t stratosphere_get_fs_version(void);
|
||||
|
||||
ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, unsigned int num_inis, void *emummc, size_t emummc_size);
|
||||
|
||||
typedef struct {
|
||||
bool has_nogc_config;
|
||||
|
||||
Reference in New Issue
Block a user