/* * Atmosphère Fusée Secondary Storage parser. * * Copyright (c) 2019-2020 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 <http://www.gnu.org/licenses/>. */ #include <string.h> #include "fss.h" #include "hos.h" #include "../config.h" #include <libs/fatfs/ff.h> #include <mem/heap.h> #include "../storage/emummc.h" #include <storage/nx_sd.h> #include <gfx_utils.h> #define DPRINTF(...) extern hekate_config h_cfg; extern bool is_ipl_updated(void *buf, char *path, bool force); // FSS0 Magic and Meta header offset. #define FSS0_MAGIC 0x30535346 #define FSS0_META_OFFSET 0x4 #define FSS0_VERSION_0_17_0 0x110000 // FSS0 Content Types. #define CNT_TYPE_FSP 0 #define CNT_TYPE_EXO 1 // Exosphere (Secure Monitor). #define CNT_TYPE_WBT 2 // Warmboot (SC7Exit fw). #define CNT_TYPE_RBT 3 // Rebootstub (Warmboot based reboot fw). #define CNT_TYPE_SP1 4 // Sept Primary (TSEC and Sept Secondary loader). #define CNT_TYPE_SP2 5 // Sept Secondary (Acts as pkg11 and derives keys). #define CNT_TYPE_KIP 6 // KIP1 (Used for replacement or addition). #define CNT_TYPE_BMP 7 #define CNT_TYPE_EMC 8 #define CNT_TYPE_KLD 9 // Kernel Loader. #define CNT_TYPE_KRN 10 // Kernel. #define CNT_TYPE_EXF 11 // Exosphere Mariko fatal payload. // FSS0 Content Flags. #define CNT_FLAG0_EXPERIMENTAL BIT(0) // FSS0 Meta Header. typedef struct _fss_meta_t { u32 magic; u32 size; u32 crt0_off; u32 cnt_off; u32 cnt_count; u32 hos_ver; u32 version; u32 git_rev; } fss_meta_t; // FSS0 Content Header. typedef struct _fss_content_t { u32 offset; u32 size; u8 type; u8 flags0; u8 flags1; u8 flags2; u32 rsvd1; char name[0x10]; } fss_content_t; int parse_fss(launch_ctxt_t *ctxt, const char *path, fss0_sept_t *sept_ctxt) { FIL fp; bool stock = false; int sept_used = 0; // Skip if stock and Exosphere and warmboot are not needed. if (!sept_ctxt) { bool pkg1_old = ctxt->pkg1_id->kb <= KB_FIRMWARE_VERSION_620; bool emummc_disabled = !emu_cfg.enabled || h_cfg.emummc_force_disable; LIST_FOREACH_ENTRY(ini_kv_t, kv, &ctxt->cfg->kvs, link) { if (!strcmp("stock", kv->key)) if (kv->val[0] == '1') stock = true; } #ifdef HOS_MARIKO_STOCK_SECMON if (stock && emummc_disabled && (pkg1_old || h_cfg.t210b01)) #else if (stock && emummc_disabled && pkg1_old) #endif return 1; } if (f_open(&fp, path, FA_READ) != FR_OK) return 0; void *fss = malloc(f_size(&fp)); // Read first 1024 bytes of the fss file. f_read(&fp, fss, 1024, NULL); // Get FSS0 Meta header offset. u32 fss_meta_addr = *(u32 *)(fss + FSS0_META_OFFSET); fss_meta_t *fss_meta = (fss_meta_t *)(fss + fss_meta_addr); // Check if valid FSS0 and parse it. if (fss_meta->magic == FSS0_MAGIC) { bool mariko_not_supported = false; if (h_cfg.t210b01 && (fss_meta->version < FSS0_VERSION_0_17_0)) { gfx_con.mute = false; mariko_not_supported = true; } gfx_printf("Found FSS0, Atmosphere %d.%d.%d-%08x\n" "Max HOS supported: %d.%d.%d\n" "Unpacking and loading components.. ", fss_meta->version >> 24, (fss_meta->version >> 16) & 0xFF, (fss_meta->version >> 8) & 0xFF, fss_meta->git_rev, fss_meta->hos_ver >> 24, (fss_meta->hos_ver >> 16) & 0xFF, (fss_meta->hos_ver >> 8) & 0xFF); if (mariko_not_supported) { EPRINTF("Mariko not supported on < 0.17.0!"); goto fail; } if (!sept_ctxt) { ctxt->atmosphere = true; ctxt->fss0_hosver = fss_meta->hos_ver; } // Parse FSS0 contents. fss_content_t *curr_fss_cnt = (fss_content_t *)(fss + fss_meta->cnt_off); void *content; for (u32 i = 0; i < fss_meta->cnt_count; i++) { content = (void *)(fss + curr_fss_cnt[i].offset); // Check if offset is inside limits. if ((curr_fss_cnt[i].offset + curr_fss_cnt[i].size) > fss_meta->size) continue; // If content is experimental and experimental flag is not enabled, skip it. if ((curr_fss_cnt[i].flags0 & CNT_FLAG0_EXPERIMENTAL) && !ctxt->fss0_experimental) continue; // Parse content. if (!sept_ctxt) { // Prepare content context. switch (curr_fss_cnt[i].type) { case CNT_TYPE_KIP: if (stock) continue; merge_kip_t *mkip1 = (merge_kip_t *)malloc(sizeof(merge_kip_t)); mkip1->kip1 = content; list_append(&ctxt->kip1_list, &mkip1->link); DPRINTF("Loaded %s.kip1 from FSS0 (size %08X)\n", curr_fss_cnt[i].name, curr_fss_cnt[i].size); break; case CNT_TYPE_KRN: if (stock) continue; ctxt->kernel_size = curr_fss_cnt[i].size; ctxt->kernel = content; break; case CNT_TYPE_EXO: ctxt->secmon_size = curr_fss_cnt[i].size; ctxt->secmon = content; break; case CNT_TYPE_EXF: ctxt->exofatal_size = curr_fss_cnt[i].size; ctxt->exofatal = content; break; case CNT_TYPE_WBT: if (h_cfg.t210b01) continue; ctxt->warmboot_size = curr_fss_cnt[i].size; ctxt->warmboot = content; break; default: continue; } // Load content to launch context. f_lseek(&fp, curr_fss_cnt[i].offset); f_read(&fp, content, curr_fss_cnt[i].size, NULL); } else { // Load sept content directly to launch context. switch (curr_fss_cnt[i].type) { case CNT_TYPE_SP1: f_lseek(&fp, curr_fss_cnt[i].offset); f_read(&fp, sept_ctxt->sept_primary, curr_fss_cnt[i].size, NULL); break; case CNT_TYPE_SP2: if (!memcmp(curr_fss_cnt[i].name, (sept_ctxt->kb < KB_FIRMWARE_VERSION_810) ? "septsecondary00" : "septsecondary01", 15)) { f_lseek(&fp, curr_fss_cnt[i].offset); f_read(&fp, sept_ctxt->sept_secondary, curr_fss_cnt[i].size, NULL); sept_used = 1; goto out; } break; default: break; } } } out: gfx_printf("Done!\n"); f_close(&fp); return (!sept_ctxt ? 1 : sept_used); } fail: f_close(&fp); free(fss); return 0; } int load_sept_from_ffs0(fss0_sept_t *sept_ctxt) { LIST_FOREACH_ENTRY(ini_kv_t, kv, &sept_ctxt->cfg_sec->kvs, link) { if (!strcmp("fss0", kv->key)) return parse_fss(NULL, kv->val, sept_ctxt); } return 0; }