Merge pull request #567 from Atmosphere-NX/emunand_dev

Implement support for emummc.
This commit is contained in:
SciresM
2019-06-15 21:41:40 -07:00
committed by GitHub
161 changed files with 24612 additions and 486 deletions

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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. */

View File

@@ -52,4 +52,4 @@ typedef struct {
const char *stage2_get_program_path(void);
void load_stage2(const char *bct0);
#endif
#endif

View File

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

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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

View 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;
}

View 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

View 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

View File

@@ -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"

View File

@@ -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;

View File

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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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);
}

View File

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

View File

@@ -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. */

View File

@@ -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) {

View File

@@ -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", &sector);
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;

View File

@@ -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))

View File

@@ -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);
}

View File

@@ -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

View File

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

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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. */

View File

@@ -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

View File

@@ -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__

View File

@@ -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;
}

View File

@@ -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;