diff --git a/bdk/bdk.h b/bdk/bdk.h index 78ddf3e..9a96228 100644 --- a/bdk/bdk.h +++ b/bdk/bdk.h @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include diff --git a/bdk/storage/nx_emmc_bis.c b/bdk/storage/nx_emmc_bis.c new file mode 100644 index 0000000..4bcbc47 --- /dev/null +++ b/bdk/storage/nx_emmc_bis.c @@ -0,0 +1,315 @@ +/* + * eMMC BIS driver for Nintendo Switch + * + * Copyright (c) 2019-2020 shchmue + * Copyright (c) 2019-2022 CTCaer + * + * 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 . + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#define BIS_CLUSTER_SECTORS 32 +#define BIS_CLUSTER_SIZE 16384 +#define BIS_CACHE_MAX_ENTRIES 16384 +#define BIS_CACHE_LOOKUP_TBL_EMPTY_ENTRY -1 + +typedef struct _cluster_cache_t +{ + u32 cluster_idx; // Index of the cluster in the partition. + bool dirty; // Has been modified without write-back flag. + u8 data[BIS_CLUSTER_SIZE]; // The cached cluster itself. Aligned to 8 bytes for DMA engine. +} cluster_cache_t; + +typedef struct _bis_cache_t +{ + bool full; + bool enabled; + u32 dirty_cnt; + u32 top_idx; + u8 dma_buff[BIS_CLUSTER_SIZE]; // Aligned to 8 bytes for DMA engine. + cluster_cache_t clusters[]; +} bis_cache_t; + +static u8 ks_crypt = 0; +static u8 ks_tweak = 0; +static u32 emu_offset = 0; +static emmc_part_t *system_part = NULL; +static u32 *cache_lookup_tbl = (u32 *)NX_BIS_LOOKUP_ADDR; +static bis_cache_t *bis_cache = (bis_cache_t *)NX_BIS_CACHE_ADDR; + +static int nx_emmc_bis_write_block(u32 sector, u32 count, void *buff, bool flush) +{ + if (!system_part) + return 3; // Not ready. + + int res; + u8 tweak[SE_KEY_128_SIZE] __attribute__((aligned(4))); + u32 cluster = sector / BIS_CLUSTER_SECTORS; + u32 aligned_sector = cluster * BIS_CLUSTER_SECTORS; + u32 sector_in_cluster = sector % BIS_CLUSTER_SECTORS; + u32 lookup_idx = cache_lookup_tbl[cluster]; + bool is_cached = lookup_idx != (u32)BIS_CACHE_LOOKUP_TBL_EMPTY_ENTRY; + + // Write to cached cluster. + if (is_cached) + { + if (buff) + memcpy(bis_cache->clusters[lookup_idx].data + sector_in_cluster * EMMC_BLOCKSIZE, buff, count * EMMC_BLOCKSIZE); + else + buff = bis_cache->clusters[lookup_idx].data; + if (!bis_cache->clusters[lookup_idx].dirty) + bis_cache->dirty_cnt++; + bis_cache->clusters[lookup_idx].dirty = true; + + if (!flush) + return 0; // Success. + + // Reset args to trigger a full cluster flush to emmc. + sector_in_cluster = 0; + sector = aligned_sector; + count = BIS_CLUSTER_SECTORS; + } + + // Encrypt cluster. + if (!se_aes_xts_crypt_sec_nx(ks_tweak, ks_crypt, ENCRYPT, cluster, tweak, true, sector_in_cluster, bis_cache->dma_buff, buff, count * EMMC_BLOCKSIZE)) + return 1; // Encryption error. + + // If not reading from cache, do a regular read and decrypt. + if (!emu_offset) + res = emmc_part_write(system_part, sector, count, bis_cache->dma_buff); + else + res = sdmmc_storage_write(&sd_storage, emu_offset + system_part->lba_start + sector, count, bis_cache->dma_buff); + if (!res) + return 1; // R/W error. + + // Mark cache entry not dirty if write succeeds. + if (is_cached) + { + bis_cache->clusters[lookup_idx].dirty = false; + bis_cache->dirty_cnt--; + } + + return 0; // Success. +} + +static void _nx_emmc_bis_cluster_cache_init(bool enable_cache) +{ + u32 cache_lookup_tbl_size = (system_part->lba_end - system_part->lba_start + 1) / BIS_CLUSTER_SECTORS * sizeof(*cache_lookup_tbl); + + // Clear cache header. + memset(bis_cache, 0, sizeof(bis_cache_t)); + + // Clear cluster lookup table. + memset(cache_lookup_tbl, BIS_CACHE_LOOKUP_TBL_EMPTY_ENTRY, cache_lookup_tbl_size); + + // Enable cache. + bis_cache->enabled = enable_cache; +} + +static void _nx_emmc_bis_flush_cache() +{ + if (!bis_cache->enabled || !bis_cache->dirty_cnt) + return; + + for (u32 i = 0; i < bis_cache->top_idx && bis_cache->dirty_cnt; i++) + { + if (bis_cache->clusters[i].dirty) { + nx_emmc_bis_write_block(bis_cache->clusters[i].cluster_idx * BIS_CLUSTER_SECTORS, BIS_CLUSTER_SECTORS, NULL, true); + bis_cache->dirty_cnt--; + } + } + + _nx_emmc_bis_cluster_cache_init(true); +} + +static int nx_emmc_bis_read_block_normal(u32 sector, u32 count, void *buff) +{ + static u32 prev_cluster = -1; + static u32 prev_sector = 0; + static u8 tweak[SE_KEY_128_SIZE] __attribute__((aligned(4))); + + int res; + bool regen_tweak = true; + u32 tweak_exp = 0; + u32 cluster = sector / BIS_CLUSTER_SECTORS; + u32 sector_in_cluster = sector % BIS_CLUSTER_SECTORS; + + // If not reading from cache, do a regular read and decrypt. + if (!emu_offset) + res = emmc_part_read(system_part, sector, count, bis_cache->dma_buff); + else + res = sdmmc_storage_read(&sd_storage, emu_offset + system_part->lba_start + sector, count, bis_cache->dma_buff); + if (!res) + return 1; // R/W error. + + if (prev_cluster != cluster) // Sector in different cluster than last read. + { + prev_cluster = cluster; + tweak_exp = sector_in_cluster; + } + else if (sector > prev_sector) // Sector in same cluster and past last sector. + { + // Calculates the new tweak using the saved one, reducing expensive _gf256_mul_x_le calls. + tweak_exp = sector - prev_sector - 1; + regen_tweak = false; + } + else // Sector in same cluster and before or same as last sector. + tweak_exp = sector_in_cluster; + + // Maximum one cluster (1 XTS crypto block 16KB). + if (!se_aes_xts_crypt_sec_nx(ks_tweak, ks_crypt, DECRYPT, prev_cluster, tweak, regen_tweak, tweak_exp, buff, bis_cache->dma_buff, count * EMMC_BLOCKSIZE)) + return 1; // R/W error. + + prev_sector = sector + count - 1; + + return 0; // Success. +} + +static int nx_emmc_bis_read_block_cached(u32 sector, u32 count, void *buff) +{ + int res; + u8 cache_tweak[SE_KEY_128_SIZE] __attribute__((aligned(4))); + u32 cluster = sector / BIS_CLUSTER_SECTORS; + u32 cluster_sector = cluster * BIS_CLUSTER_SECTORS; + u32 sector_in_cluster = sector % BIS_CLUSTER_SECTORS; + u32 lookup_idx = cache_lookup_tbl[cluster]; + + // Read from cached cluster. + if (lookup_idx != (u32)BIS_CACHE_LOOKUP_TBL_EMPTY_ENTRY) + { + memcpy(buff, bis_cache->clusters[lookup_idx].data + sector_in_cluster * EMMC_BLOCKSIZE, count * EMMC_BLOCKSIZE); + + return 0; // Success. + } + + // Flush cache if full. + if (bis_cache->top_idx >= BIS_CACHE_MAX_ENTRIES) + _nx_emmc_bis_flush_cache(); + + // Set new cached cluster parameters. + bis_cache->clusters[bis_cache->top_idx].cluster_idx = cluster; + bis_cache->clusters[bis_cache->top_idx].dirty = false; + cache_lookup_tbl[cluster] = bis_cache->top_idx; + + // Read the whole cluster the sector resides in. + if (!emu_offset) + res = emmc_part_read(system_part, cluster_sector, BIS_CLUSTER_SECTORS, bis_cache->dma_buff); + else + res = sdmmc_storage_read(&sd_storage, emu_offset + system_part->lba_start + cluster_sector, BIS_CLUSTER_SECTORS, bis_cache->dma_buff); + if (!res) + return 1; // R/W error. + + // Decrypt cluster. + if (!se_aes_xts_crypt_sec_nx(ks_tweak, ks_crypt, DECRYPT, cluster, cache_tweak, true, 0, bis_cache->dma_buff, bis_cache->dma_buff, BIS_CLUSTER_SIZE)) + return 1; // Decryption error. + + // Copy to cluster cache. + memcpy(bis_cache->clusters[bis_cache->top_idx].data, bis_cache->dma_buff, BIS_CLUSTER_SIZE); + memcpy(buff, bis_cache->dma_buff + sector_in_cluster * EMMC_BLOCKSIZE, count * EMMC_BLOCKSIZE); + + // Increment cache count. + bis_cache->top_idx++; + + return 0; // Success. +} + +static int nx_emmc_bis_read_block(u32 sector, u32 count, void *buff) +{ + if (!system_part) + return 3; // Not ready. + + if (bis_cache->enabled) + return nx_emmc_bis_read_block_cached(sector, count, buff); + else + return nx_emmc_bis_read_block_normal(sector, count, buff); +} + +int nx_emmc_bis_read(u32 sector, u32 count, void *buff) +{ + u8 *buf = (u8 *)buff; + u32 curr_sct = sector; + + while (count) + { + u32 sct_cnt = MIN(count, BIS_CLUSTER_SECTORS); + if (nx_emmc_bis_read_block(curr_sct, sct_cnt, buf)) + return 0; + + count -= sct_cnt; + curr_sct += sct_cnt; + buf += sct_cnt * EMMC_BLOCKSIZE; + } + + return 1; +} + +int nx_emmc_bis_write(u32 sector, u32 count, void *buff) +{ + u8 *buf = (u8 *)buff; + u32 curr_sct = sector; + + while (count) + { + u32 sct_cnt = MIN(count, BIS_CLUSTER_SECTORS); + if (nx_emmc_bis_write_block(curr_sct, sct_cnt, buf, false)) + return 0; + + count -= sct_cnt; + curr_sct += sct_cnt; + buf += sct_cnt * EMMC_BLOCKSIZE; + } + + return 1; +} + +void nx_emmc_bis_init(emmc_part_t *part, bool enable_cache, u32 emummc_offset) +{ + system_part = part; + emu_offset = emummc_offset; + + _nx_emmc_bis_cluster_cache_init(enable_cache); + + if (!strcmp(part->name, "PRODINFO") || !strcmp(part->name, "PRODINFOF")) + { + ks_crypt = 0; + ks_tweak = 1; + } + else if (!strcmp(part->name, "SAFE")) + { + ks_crypt = 2; + ks_tweak = 3; + } + else if (!strcmp(part->name, "SYSTEM") || !strcmp(part->name, "USER")) + { + ks_crypt = 4; + ks_tweak = 5; + } + else + system_part = NULL; +} + +void nx_emmc_bis_end() +{ + _nx_emmc_bis_flush_cache(); + system_part = NULL; +} diff --git a/bdk/storage/nx_emmc_bis.h b/bdk/storage/nx_emmc_bis.h new file mode 100644 index 0000000..8b07008 --- /dev/null +++ b/bdk/storage/nx_emmc_bis.h @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2019 shchmue + * Copyright (c) 2019 CTCaer + * + * 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 . + */ + +#ifndef NX_EMMC_BIS_H +#define NX_EMMC_BIS_H + +#include +#include + +typedef struct _nx_emmc_cal0_spk_t +{ + u16 unk0; + u16 unk1; + u16 eq_bw_lop; + u16 eq_gn_lop; + u16 eq_fc_bp1; + u16 eq_bw_bp1; + u16 eq_gn_bp1; + u16 eq_fc_bp2; + u16 eq_bw_bp2; + u16 eq_gn_bp2; + u16 eq_fc_bp3; + u16 eq_bw_bp3; + u16 eq_gn_bp3; + u16 eq_fc_bp4; + u16 eq_bw_bp4; + u16 eq_gn_bp4; + u16 eq_fc_hip1; + u16 eq_gn_hip1; + u16 eq_fc_hip2; + u16 eq_bw_hip2; + u16 eq_gn_hip2; + u16 eq_pre_vol; + u16 eq_pst_vol; + u16 eq_ctrl2; + u16 eq_ctrl1; + u16 drc_agc_2; + u16 drc_agc_3; + u16 drc_agc_1; + u16 spk_vol; + u16 hp_vol; + u16 dac1_min_vol_spk; + u16 dac1_max_vol_spk; + u16 dac1_min_vol_hp; + u16 dac1_max_vol_hp; + u16 in1_in2; + u16 adc_vol_min; + u16 adc_vol_max; + u8 unk4[16]; +} __attribute__((packed)) nx_emmc_cal0_spk_t; + +typedef struct _nx_emmc_cal0_t +{ + u32 magic; // 'CAL0'. + u32 version; + u32 body_size; + u16 model; + u16 update_cnt; + u8 pad_crc16_0[0x10]; + u8 body_sha256[0x20]; + char cfg_id1[0x1E]; + u8 crc16_pad1[2]; + u8 rsvd0[0x20]; + u32 wlan_cc_num; + u32 wlan_cc_last; + char wlan_cc[128][3]; + u8 crc16_pad2[8]; + u8 wlan_mac[6]; + u8 crc16_pad3[2]; + u8 rsvd1[8]; + u8 bd_mac[6]; + u8 crc16_pad4[2]; + u8 rsvd2[8]; + u8 acc_offset[6]; + u8 crc16_pad5[2]; + u8 acc_scale[6]; + u8 crc16_pad6[2]; + u8 gyro_offset[6]; + u8 crc16_pad7[2]; + u8 gyro_scale[6]; + u8 crc16_pad8[2]; + char serial_number[0x18]; + u8 crc16_pad9[8]; + + u8 ecc_p256_device_key[0x30]; + u8 crc16_pad10[0x10]; + u8 ecc_p256_device_cert[0x180]; + u8 crc16_pad11[0x10]; + u8 ecc_p233_device_key[0x30]; + u8 crc16_pad12[0x10]; + u8 ecc_p33_device_cert[0x180]; + u8 crc16_pad13[0x10]; + u8 ecc_p256_ticket_key[0x30]; + u8 crc16_pad14[0x10]; + u8 ecc_p256_ticket_cert[0x180]; + u8 crc16_pad15[0x10]; + u8 ecc_p233_ticket_key[0x30]; + u8 crc16_pad16[0x10]; + u8 ecc_p33_ticket_cert[0x180]; + u8 crc16_pad17[0x10]; + u8 ssl_key[0x110]; + u8 crc16_pad18[0x10]; + u32 ssl_cert_size; + u8 crc16_pad19[0xC]; + u8 ssl_cert[0x800]; + u8 ssl_sha256[0x20]; + u8 random_number[0x1000]; + u8 random_number_sha256[0x20]; + u8 gc_key[0x110]; + u8 crc16_pad20[0x10]; + u8 gc_cert[0x400]; + u8 gc_cert_sha256[0x20]; + u8 rsa2048_eticket_key[0x220]; + u8 crc16_pad21[0x10]; + u8 rsa2048_eticket_cert[0x240]; + u8 crc16_pad22[0x10]; + + char battery_lot[0x1E]; + u8 crc16_pad23[2]; + nx_emmc_cal0_spk_t spk_cal; + u8 spk_cal_rsvd[0x800 - sizeof(nx_emmc_cal0_spk_t)]; + u8 crc16_pad24[0x10]; + u32 region_code; + u8 crc16_pad25[0xC]; + + u8 amiibo_key[0x50]; + u8 crc16_pad26[0x10]; + u8 amiibo_ecqv_cert[0x14]; + u8 crc16_pad27[0xC]; + u8 amiibo_ecqdsa_cert[0x70]; + u8 crc16_pad28[0x10]; + u8 amiibo_ecqv_bls_key[0x40]; + u8 crc16_pad29[0x10]; + u8 amiibo_ecqv_bls_cert[0x20]; + u8 crc16_pad30[0x10]; + u8 amiibo_ecqv_bls_root_cert[0x90]; + u8 crc16_pad31[0x10]; + + u32 product_model; // 1: Nx, 2: Copper, 4: Hoag. + u8 crc16_pad32[0xC]; + u8 home_menu_scheme_main_color[6]; + u8 crc16_pad33[0xA]; + u32 lcd_bl_brightness_mapping[3]; // Floats. Normally 100%, 0% and 2%. + u8 crc16_pad34[0x4]; + + u8 ext_ecc_b233_device_key[0x50]; + u8 crc16_pad35[0x10]; + u8 ext_ecc_p256_eticket_key[0x50]; + u8 crc16_pad36[0x10]; + u8 ext_ecc_b233_eticket_key[0x50]; + u8 crc16_pad37[0x10]; + u8 ext_ecc_rsa2048_eticket_key[0x240]; + u8 crc16_pad38[0x10]; + u8 ext_ssl_key[0x130]; + u8 crc16_pad39[0x10]; + u8 ext_gc_key[0x130]; + u8 crc16_pad40[0x10]; + + u32 lcd_vendor; + u8 crc16_pad41[0xC]; + + // 5.0.0 and up. + u8 ext_rsa2048_device_key[0x240]; + u8 crc16_pad42[0x10]; + u8 rsa2048_device_cert[0x240]; + u8 crc16_pad43[0x10]; + + u8 usbc_pwr_src_circuit_ver; + u8 crc16_pad44[0xF]; + + // 9.0.0 and up. + u32 home_menu_scheme_sub_color; + u8 crc16_pad45[0xC]; + u32 home_menu_scheme_bezel_color; + u8 crc16_pad46[0xC]; + u32 home_menu_scheme_main_color1; + u8 crc16_pad47[0xC]; + u32 home_menu_scheme_main_color2; + u8 crc16_pad48[0xC]; + u32 home_menu_scheme_main_color3; + u8 crc16_pad49[0xC]; + + u8 analog_stick_type_l; + u8 crc16_pad50[0xF]; + u8 analog_stick_param_l[0x12]; + u8 crc16_pad51[0xE]; + u8 analog_stick_cal_l[0x9]; + u8 crc16_pad52[0x7]; + u8 analog_stick_type_r; + u8 crc16_pad53[0xF]; + u8 analog_stick_param_r[0x12]; + u8 crc16_pad54[0xE]; + u8 analog_stick_cal_r[0x9]; + u8 crc16_pad55[0x7]; + u8 console_6axis_sensor_type; + u8 crc16_pad56[0xF]; + u8 console_6axis_sensor_hor_off[0x6]; + u8 crc16_pad57[0xA]; + + // 6.0.0 and up. + u8 battery_ver; + u8 crc16_pad58[0x1F]; + + // 9.0.0 and up. + u32 home_menu_scheme_model; + u8 crc16_pad59[0xC]; + + // 10.0.0 and up. + u8 console_6axis_sensor_mount_type; +} __attribute__((packed)) nx_emmc_cal0_t; + +int nx_emmc_bis_read(u32 sector, u32 count, void *buff); +int nx_emmc_bis_write(u32 sector, u32 count, void *buff); +void nx_emmc_bis_init(emmc_part_t *part, bool enable_cache, u32 emummc_offset); +void nx_emmc_bis_end(); + +#endif