You aint taking this down
This commit is contained in:
parent
0bcd59d0cb
commit
a2679d92c9
398 changed files with 116325 additions and 35 deletions
51
source/config.c
Normal file
51
source/config.c
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (c) 2018-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 <stdlib.h>
|
||||
|
||||
#include "config.h"
|
||||
#include <utils/ini.h>
|
||||
#include <gfx_utils.h>
|
||||
#include "gfx/tui.h"
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include <soc/fuse.h>
|
||||
#include <soc/hw_init.h>
|
||||
#include <soc/t210.h>
|
||||
#include <storage/nx_sd.h>
|
||||
#include <storage/sdmmc.h>
|
||||
#include <utils/btn.h>
|
||||
#include <utils/list.h>
|
||||
#include <utils/util.h>
|
||||
|
||||
extern hekate_config h_cfg;
|
||||
|
||||
void set_default_configuration()
|
||||
{
|
||||
h_cfg.autoboot = 0;
|
||||
h_cfg.autoboot_list = 0;
|
||||
h_cfg.bootwait = 3;
|
||||
h_cfg.backlight = 100;
|
||||
h_cfg.autohosoff = 0;
|
||||
h_cfg.autonogc = 1;
|
||||
h_cfg.updater2p = 0;
|
||||
h_cfg.bootprotect = 0;
|
||||
h_cfg.errors = 0;
|
||||
h_cfg.aes_slots_new = false;
|
||||
h_cfg.rcm_patched = fuse_check_patched_rcm();
|
||||
h_cfg.emummc_force_disable = false;
|
||||
h_cfg.t210b01 = hw_get_chip_id() == GP_HIDREV_MAJOR_T210B01;
|
||||
}
|
49
source/config.h
Normal file
49
source/config.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (c) 2018-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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _CONFIG_H_
|
||||
#define _CONFIG_H_
|
||||
|
||||
#include <utils/types.h>
|
||||
|
||||
typedef struct _hekate_config
|
||||
{
|
||||
// Non-volatile config.
|
||||
u32 autoboot;
|
||||
u32 autoboot_list;
|
||||
u32 bootwait;
|
||||
u32 backlight;
|
||||
u32 autohosoff;
|
||||
u32 autonogc;
|
||||
u32 updater2p;
|
||||
u32 bootprotect;
|
||||
// Global temporary config.
|
||||
bool t210b01;
|
||||
bool aes_slots_new;
|
||||
bool emummc_force_disable;
|
||||
bool rcm_patched;
|
||||
u32 errors;
|
||||
} hekate_config;
|
||||
|
||||
void set_default_configuration();
|
||||
int create_config_entry();
|
||||
void config_autoboot();
|
||||
void config_bootdelay();
|
||||
void config_backlight();
|
||||
void config_auto_hos_poweroff();
|
||||
void config_nogc();
|
||||
|
||||
#endif /* _CONFIG_H_ */
|
99
source/frontend/gui.c
Normal file
99
source/frontend/gui.c
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2021 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 "../gfx/gfx.h"
|
||||
#include <mem/heap.h>
|
||||
#include <rtc/max77620-rtc.h>
|
||||
#include <storage/nx_sd.h>
|
||||
#include <utils/util.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
int save_fb_to_bmp()
|
||||
{
|
||||
// Disallow screenshots if less than 2s passed.
|
||||
static u32 timer = 0;
|
||||
if (get_tmr_ms() < timer)
|
||||
return 1;
|
||||
|
||||
const u32 file_size = 0x384000 + 0x36;
|
||||
u8 *bitmap = malloc(file_size);
|
||||
u32 *fb = malloc(0x384000);
|
||||
u32 *fb_ptr = gfx_ctxt.fb;
|
||||
|
||||
// Reconstruct FB for bottom-top, portrait bmp.
|
||||
for (int y = 1279; y > -1; y--)
|
||||
{
|
||||
for (u32 x = 0; x < 720; x++)
|
||||
fb[y * 720 + x] = *fb_ptr++;
|
||||
}
|
||||
|
||||
memcpy(bitmap + 0x36, fb, 0x384000);
|
||||
|
||||
typedef struct _bmp_t
|
||||
{
|
||||
u16 magic;
|
||||
u32 size;
|
||||
u32 rsvd;
|
||||
u32 data_off;
|
||||
u32 hdr_size;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u16 planes;
|
||||
u16 pxl_bits;
|
||||
u32 comp;
|
||||
u32 img_size;
|
||||
u32 res_h;
|
||||
u32 res_v;
|
||||
u64 rsvd2;
|
||||
} __attribute__((packed)) bmp_t;
|
||||
|
||||
bmp_t *bmp = (bmp_t *)bitmap;
|
||||
|
||||
bmp->magic = 0x4D42;
|
||||
bmp->size = file_size;
|
||||
bmp->rsvd = 0;
|
||||
bmp->data_off = 0x36;
|
||||
bmp->hdr_size = 40;
|
||||
bmp->width = 720;
|
||||
bmp->height = 1280;
|
||||
bmp->planes = 1;
|
||||
bmp->pxl_bits = 32;
|
||||
bmp->comp = 0;
|
||||
bmp->img_size = 0x384000;
|
||||
bmp->res_h = 2834;
|
||||
bmp->res_v = 2834;
|
||||
bmp->rsvd2 = 0;
|
||||
|
||||
sd_mount();
|
||||
|
||||
f_mkdir("sd:/switch");
|
||||
|
||||
char path[0x80] = "sd:/switch/picklock_rcm.bmp";
|
||||
|
||||
// Save screenshot and log.
|
||||
int res = sd_save_to_file(bitmap, file_size, path);
|
||||
|
||||
// sd_unmount();
|
||||
|
||||
free(bitmap);
|
||||
free(fb);
|
||||
|
||||
// Set timer to 2s.
|
||||
timer = get_tmr_ms() + 2000;
|
||||
|
||||
return res;
|
||||
}
|
17
source/frontend/gui.h
Normal file
17
source/frontend/gui.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2021 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/>.
|
||||
*/
|
||||
|
||||
int save_fb_to_bmp();
|
592
source/gfx/gfx.c
Normal file
592
source/gfx/gfx.c
Normal file
|
@ -0,0 +1,592 @@
|
|||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
* Copyright (c) 2018-2021 CTCaer
|
||||
* Copyright (c) 2019-2021 shchmue
|
||||
*
|
||||
* 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 <stdarg.h>
|
||||
#include <string.h>
|
||||
#include "gfx.h"
|
||||
|
||||
// Global gfx console and context.
|
||||
gfx_ctxt_t gfx_ctxt;
|
||||
gfx_con_t gfx_con;
|
||||
|
||||
static bool gfx_con_init_done = false;
|
||||
|
||||
static const u8 _gfx_font[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Char 032 ( )
|
||||
0x00, 0x30, 0x30, 0x18, 0x18, 0x00, 0x0C, 0x00, // Char 033 (!)
|
||||
0x00, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, // Char 034 (")
|
||||
0x00, 0x66, 0x66, 0xFF, 0x66, 0xFF, 0x66, 0x66, // Char 035 (#)
|
||||
0x00, 0x18, 0x7C, 0x06, 0x3C, 0x60, 0x3E, 0x18, // Char 036 ($)
|
||||
0x00, 0x46, 0x66, 0x30, 0x18, 0x0C, 0x66, 0x62, // Char 037 (%)
|
||||
0x00, 0x3C, 0x66, 0x3C, 0x1C, 0xE6, 0x66, 0xFC, // Char 038 (&)
|
||||
0x00, 0x18, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, // Char 039 (')
|
||||
0x00, 0x30, 0x18, 0x0C, 0x0C, 0x18, 0x30, 0x00, // Char 040 (()
|
||||
0x00, 0x0C, 0x18, 0x30, 0x30, 0x18, 0x0C, 0x00, // Char 041 ())
|
||||
0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, // Char 042 (*)
|
||||
0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, // Char 043 (+)
|
||||
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x0C, 0x00, // Char 044 (,)
|
||||
0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, // Char 045 (-)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, // Char 046 (.)
|
||||
0x00, 0x40, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x00, // Char 047 (/)
|
||||
0x00, 0x3C, 0x66, 0x76, 0x6E, 0x66, 0x3C, 0x00, // Char 048 (0)
|
||||
0x00, 0x18, 0x1C, 0x18, 0x18, 0x18, 0x7E, 0x00, // Char 049 (1)
|
||||
0x00, 0x3C, 0x62, 0x30, 0x0C, 0x06, 0x7E, 0x00, // Char 050 (2)
|
||||
0x00, 0x3C, 0x62, 0x38, 0x60, 0x66, 0x3C, 0x00, // Char 051 (3)
|
||||
0x00, 0x6C, 0x6C, 0x66, 0xFE, 0x60, 0x60, 0x00, // Char 052 (4)
|
||||
0x00, 0x7E, 0x06, 0x7E, 0x60, 0x66, 0x3C, 0x00, // Char 053 (5)
|
||||
0x00, 0x3C, 0x06, 0x3E, 0x66, 0x66, 0x3C, 0x00, // Char 054 (6)
|
||||
0x00, 0x7E, 0x30, 0x30, 0x18, 0x18, 0x18, 0x00, // Char 055 (7)
|
||||
0x00, 0x3C, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00, // Char 056 (8)
|
||||
0x00, 0x3C, 0x66, 0x7C, 0x60, 0x66, 0x3C, 0x00, // Char 057 (9)
|
||||
0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, // Char 058 (:)
|
||||
0x00, 0x00, 0x18, 0x00, 0x18, 0x18, 0x0C, 0x00, // Char 059 (;)
|
||||
0x00, 0x70, 0x1C, 0x06, 0x06, 0x1C, 0x70, 0x00, // Char 060 (<)
|
||||
0x00, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x00, 0x00, // Char 061 (=)
|
||||
0x00, 0x0E, 0x38, 0x60, 0x60, 0x38, 0x0E, 0x00, // Char 062 (>)
|
||||
0x00, 0x3C, 0x66, 0x30, 0x18, 0x00, 0x18, 0x00, // Char 063 (?)
|
||||
0x00, 0x3C, 0x66, 0x76, 0x76, 0x06, 0x46, 0x3C, // Char 064 (@)
|
||||
0x00, 0x3C, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, // Char 065 (A)
|
||||
0x00, 0x3E, 0x66, 0x3E, 0x66, 0x66, 0x3E, 0x00, // Char 066 (B)
|
||||
0x00, 0x3C, 0x66, 0x06, 0x06, 0x66, 0x3C, 0x00, // Char 067 (C)
|
||||
0x00, 0x1E, 0x36, 0x66, 0x66, 0x36, 0x1E, 0x00, // Char 068 (D)
|
||||
0x00, 0x7E, 0x06, 0x1E, 0x06, 0x06, 0x7E, 0x00, // Char 069 (E)
|
||||
0x00, 0x3E, 0x06, 0x1E, 0x06, 0x06, 0x06, 0x00, // Char 070 (F)
|
||||
0x00, 0x3C, 0x66, 0x06, 0x76, 0x66, 0x3C, 0x00, // Char 071 (G)
|
||||
0x00, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, // Char 072 (H)
|
||||
0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, // Char 073 (I)
|
||||
0x00, 0x78, 0x30, 0x30, 0x30, 0x36, 0x1C, 0x00, // Char 074 (J)
|
||||
0x00, 0x66, 0x36, 0x1E, 0x1E, 0x36, 0x66, 0x00, // Char 075 (K)
|
||||
0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x7E, 0x00, // Char 076 (L)
|
||||
0x00, 0x46, 0x6E, 0x7E, 0x56, 0x46, 0x46, 0x00, // Char 077 (M)
|
||||
0x00, 0x66, 0x6E, 0x7E, 0x76, 0x66, 0x66, 0x00, // Char 078 (N)
|
||||
0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // Char 079 (O)
|
||||
0x00, 0x3E, 0x66, 0x3E, 0x06, 0x06, 0x06, 0x00, // Char 080 (P)
|
||||
0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x70, 0x00, // Char 081 (Q)
|
||||
0x00, 0x3E, 0x66, 0x3E, 0x1E, 0x36, 0x66, 0x00, // Char 082 (R)
|
||||
0x00, 0x3C, 0x66, 0x0C, 0x30, 0x66, 0x3C, 0x00, // Char 083 (S)
|
||||
0x00, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // Char 084 (T)
|
||||
0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // Char 085 (U)
|
||||
0x00, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, // Char 086 (V)
|
||||
0x00, 0x46, 0x46, 0x56, 0x7E, 0x6E, 0x46, 0x00, // Char 087 (W)
|
||||
0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00, // Char 088 (X)
|
||||
0x00, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x00, // Char 089 (Y)
|
||||
0x00, 0x7E, 0x30, 0x18, 0x0C, 0x06, 0x7E, 0x00, // Char 090 (Z)
|
||||
0x00, 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, // Char 091 ([)
|
||||
0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00, // Char 092 (\)
|
||||
0x00, 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, // Char 093 (])
|
||||
0x00, 0x18, 0x3C, 0x66, 0x00, 0x00, 0x00, 0x00, // Char 094 (^)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, // Char 095 (_)
|
||||
0x00, 0x0C, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, // Char 096 (`)
|
||||
0x00, 0x00, 0x3C, 0x60, 0x7C, 0x66, 0x7C, 0x00, // Char 097 (a)
|
||||
0x00, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3E, 0x00, // Char 098 (b)
|
||||
0x00, 0x00, 0x3C, 0x06, 0x06, 0x06, 0x3C, 0x00, // Char 099 (c)
|
||||
0x00, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, // Char 100 (d)
|
||||
0x00, 0x00, 0x3C, 0x66, 0x7E, 0x06, 0x3C, 0x00, // Char 101 (e)
|
||||
0x00, 0x38, 0x0C, 0x3E, 0x0C, 0x0C, 0x0C, 0x00, // Char 102 (f)
|
||||
0x00, 0x00, 0x7C, 0x66, 0x7C, 0x40, 0x3C, 0x00, // Char 103 (g)
|
||||
0x00, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x00, // Char 104 (h)
|
||||
0x00, 0x18, 0x00, 0x1C, 0x18, 0x18, 0x3C, 0x00, // Char 105 (i)
|
||||
0x00, 0x30, 0x00, 0x30, 0x30, 0x30, 0x1E, 0x00, // Char 106 (j)
|
||||
0x00, 0x06, 0x06, 0x36, 0x1E, 0x36, 0x66, 0x00, // Char 107 (k)
|
||||
0x00, 0x1C, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, // Char 108 (l)
|
||||
0x00, 0x00, 0x66, 0xFE, 0xFE, 0xD6, 0xC6, 0x00, // Char 109 (m)
|
||||
0x00, 0x00, 0x3E, 0x66, 0x66, 0x66, 0x66, 0x00, // Char 110 (n)
|
||||
0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, // Char 111 (o)
|
||||
0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x00, // Char 112 (p)
|
||||
0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x00, // Char 113 (q)
|
||||
0x00, 0x00, 0x3E, 0x66, 0x06, 0x06, 0x06, 0x00, // Char 114 (r)
|
||||
0x00, 0x00, 0x7C, 0x06, 0x3C, 0x60, 0x3E, 0x00, // Char 115 (s)
|
||||
0x00, 0x18, 0x7E, 0x18, 0x18, 0x18, 0x70, 0x00, // Char 116 (t)
|
||||
0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, // Char 117 (u)
|
||||
0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, // Char 118 (v)
|
||||
0x00, 0x00, 0xC6, 0xD6, 0xFE, 0x7C, 0x6C, 0x00, // Char 119 (w)
|
||||
0x00, 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x00, // Char 120 (x)
|
||||
0x00, 0x00, 0x66, 0x66, 0x7C, 0x60, 0x3C, 0x00, // Char 121 (y)
|
||||
0x00, 0x00, 0x7E, 0x30, 0x18, 0x0C, 0x7E, 0x00, // Char 122 (z)
|
||||
0x00, 0x18, 0x08, 0x08, 0x04, 0x08, 0x08, 0x18, // Char 123 ({)
|
||||
0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, // Char 124 (|)
|
||||
0x00, 0x0C, 0x08, 0x08, 0x10, 0x08, 0x08, 0x0C, // Char 125 (})
|
||||
0x00, 0x00, 0x00, 0x4C, 0x32, 0x00, 0x00, 0x00 // Char 126 (~)
|
||||
};
|
||||
|
||||
void gfx_clear_grey(u8 color)
|
||||
{
|
||||
memset(gfx_ctxt.fb, color, gfx_ctxt.width * gfx_ctxt.height * 4);
|
||||
}
|
||||
|
||||
void gfx_clear_partial_grey(u8 color, u32 pos_x, u32 height)
|
||||
{
|
||||
memset(gfx_ctxt.fb + pos_x * gfx_ctxt.stride, color, height * 4 * gfx_ctxt.stride);
|
||||
}
|
||||
|
||||
void gfx_clear_color(u32 color)
|
||||
{
|
||||
for (u32 i = 0; i < gfx_ctxt.width * gfx_ctxt.height; i++)
|
||||
gfx_ctxt.fb[i] = color;
|
||||
}
|
||||
|
||||
void gfx_init_ctxt(u32 *fb, u32 width, u32 height, u32 stride)
|
||||
{
|
||||
gfx_ctxt.fb = fb;
|
||||
gfx_ctxt.width = width;
|
||||
gfx_ctxt.height = height;
|
||||
gfx_ctxt.stride = stride;
|
||||
}
|
||||
|
||||
void gfx_con_init()
|
||||
{
|
||||
gfx_con.gfx_ctxt = &gfx_ctxt;
|
||||
gfx_con.fntsz = 16;
|
||||
gfx_con.x = 0;
|
||||
gfx_con.y = 0;
|
||||
gfx_con.savedx = 0;
|
||||
gfx_con.savedy = 0;
|
||||
gfx_con.fgcol = 0xFFCCCCCC;
|
||||
gfx_con.fillbg = 1;
|
||||
gfx_con.bgcol = 0xFF1B1B1B;
|
||||
gfx_con.mute = 0;
|
||||
|
||||
gfx_con_init_done = true;
|
||||
}
|
||||
|
||||
void gfx_con_setcol(u32 fgcol, int fillbg, u32 bgcol)
|
||||
{
|
||||
gfx_con.fgcol = fgcol;
|
||||
gfx_con.fillbg = fillbg;
|
||||
gfx_con.bgcol = bgcol;
|
||||
}
|
||||
|
||||
void gfx_con_getpos(u32 *x, u32 *y)
|
||||
{
|
||||
*x = gfx_con.x;
|
||||
*y = gfx_con.y;
|
||||
}
|
||||
|
||||
void gfx_con_setpos(u32 x, u32 y)
|
||||
{
|
||||
gfx_con.x = x;
|
||||
gfx_con.y = y;
|
||||
}
|
||||
|
||||
void gfx_putc(char c)
|
||||
{
|
||||
// Duplicate code for performance reasons.
|
||||
switch (gfx_con.fntsz)
|
||||
{
|
||||
case 16:
|
||||
if (c >= 32 && c <= 126)
|
||||
{
|
||||
u8 *cbuf = (u8 *)&_gfx_font[8 * (c - 32)];
|
||||
u32 *fb = gfx_ctxt.fb + gfx_con.x + gfx_con.y * gfx_ctxt.stride;
|
||||
|
||||
for (u32 i = 0; i < 16; i+=2)
|
||||
{
|
||||
u8 v = *cbuf;
|
||||
for (u32 k = 0; k < 2; k++)
|
||||
{
|
||||
for (u32 j = 0; j < 8; j++)
|
||||
{
|
||||
if (v & 1)
|
||||
{
|
||||
*fb = gfx_con.fgcol;
|
||||
fb++;
|
||||
*fb = gfx_con.fgcol;
|
||||
}
|
||||
else if (gfx_con.fillbg)
|
||||
{
|
||||
*fb = gfx_con.bgcol;
|
||||
fb++;
|
||||
*fb = gfx_con.bgcol;
|
||||
}
|
||||
else
|
||||
fb++;
|
||||
v >>= 1;
|
||||
fb++;
|
||||
}
|
||||
fb += gfx_ctxt.stride - 16;
|
||||
v = *cbuf;
|
||||
}
|
||||
cbuf++;
|
||||
}
|
||||
gfx_con.x += 16;
|
||||
if (gfx_con.x > gfx_ctxt.width - 16)
|
||||
{
|
||||
gfx_con.x = 0;
|
||||
gfx_con.y += 16;
|
||||
}
|
||||
}
|
||||
else if (c == '\n')
|
||||
{
|
||||
gfx_con.x = 0;
|
||||
gfx_con.y += 16;
|
||||
if (gfx_con.y > gfx_ctxt.height - 16)
|
||||
gfx_con.y = 0;
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
default:
|
||||
if (c >= 32 && c <= 126)
|
||||
{
|
||||
u8 *cbuf = (u8 *)&_gfx_font[8 * (c - 32)];
|
||||
u32 *fb = gfx_ctxt.fb + gfx_con.x + gfx_con.y * gfx_ctxt.stride;
|
||||
for (u32 i = 0; i < 8; i++)
|
||||
{
|
||||
u8 v = *cbuf++;
|
||||
for (u32 j = 0; j < 8; j++)
|
||||
{
|
||||
if (v & 1)
|
||||
*fb = gfx_con.fgcol;
|
||||
else if (gfx_con.fillbg)
|
||||
*fb = gfx_con.bgcol;
|
||||
v >>= 1;
|
||||
fb++;
|
||||
}
|
||||
fb += gfx_ctxt.stride - 8;
|
||||
}
|
||||
gfx_con.x += 8;
|
||||
if (gfx_con.x > gfx_ctxt.width - 8)
|
||||
{
|
||||
gfx_con.x = 0;
|
||||
gfx_con.y += 8;
|
||||
}
|
||||
}
|
||||
else if (c == '\n')
|
||||
{
|
||||
gfx_con.x = 0;
|
||||
gfx_con.y += 8;
|
||||
if (gfx_con.y > gfx_ctxt.height - 8)
|
||||
gfx_con.y = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void gfx_puts(const char *s)
|
||||
{
|
||||
if (!s || !gfx_con_init_done || gfx_con.mute)
|
||||
return;
|
||||
|
||||
for (; *s; s++)
|
||||
gfx_putc(*s);
|
||||
}
|
||||
|
||||
static void _gfx_putn(u32 v, int base, char fill, int fcnt)
|
||||
{
|
||||
char buf[65];
|
||||
static const char digits[] = "0123456789ABCDEFghijklmnopqrstuvwxyz";
|
||||
char *p;
|
||||
int c = fcnt;
|
||||
|
||||
if (base > 36)
|
||||
return;
|
||||
|
||||
p = buf + 64;
|
||||
*p = 0;
|
||||
do
|
||||
{
|
||||
c--;
|
||||
*--p = digits[v % base];
|
||||
v /= base;
|
||||
} while (v);
|
||||
|
||||
if (fill != 0)
|
||||
{
|
||||
while (c > 0)
|
||||
{
|
||||
*--p = fill;
|
||||
c--;
|
||||
}
|
||||
}
|
||||
|
||||
gfx_puts(p);
|
||||
}
|
||||
|
||||
void gfx_put_small_sep()
|
||||
{
|
||||
u8 prevFontSize = gfx_con.fntsz;
|
||||
gfx_con.fntsz = 8;
|
||||
gfx_putc('\n');
|
||||
gfx_con.fntsz = prevFontSize;
|
||||
}
|
||||
|
||||
void gfx_put_big_sep()
|
||||
{
|
||||
u8 prevFontSize = gfx_con.fntsz;
|
||||
gfx_con.fntsz = 16;
|
||||
gfx_putc('\n');
|
||||
gfx_con.fntsz = prevFontSize;
|
||||
}
|
||||
|
||||
void gfx_printf(const char *fmt, ...)
|
||||
{
|
||||
if (!gfx_con_init_done || gfx_con.mute)
|
||||
return;
|
||||
|
||||
va_list ap;
|
||||
int fill, fcnt;
|
||||
|
||||
va_start(ap, fmt);
|
||||
while(*fmt)
|
||||
{
|
||||
if(*fmt == '%')
|
||||
{
|
||||
fmt++;
|
||||
fill = 0;
|
||||
fcnt = 0;
|
||||
if ((*fmt >= '0' && *fmt <= '9') || *fmt == ' ')
|
||||
{
|
||||
fcnt = *fmt;
|
||||
fmt++;
|
||||
if (*fmt >= '0' && *fmt <= '9')
|
||||
{
|
||||
fill = fcnt;
|
||||
fcnt = *fmt - '0';
|
||||
fmt++;
|
||||
}
|
||||
else
|
||||
{
|
||||
fill = ' ';
|
||||
fcnt -= '0';
|
||||
}
|
||||
}
|
||||
switch(*fmt)
|
||||
{
|
||||
case 'c':
|
||||
gfx_putc(va_arg(ap, u32));
|
||||
break;
|
||||
case 's':
|
||||
gfx_puts(va_arg(ap, char *));
|
||||
break;
|
||||
case 'd':
|
||||
_gfx_putn(va_arg(ap, u32), 10, fill, fcnt);
|
||||
break;
|
||||
case 'p':
|
||||
case 'P':
|
||||
case 'x':
|
||||
case 'X':
|
||||
_gfx_putn(va_arg(ap, u32), 16, fill, fcnt);
|
||||
break;
|
||||
case 'k':
|
||||
gfx_con.fgcol = va_arg(ap, u32);
|
||||
break;
|
||||
case 'K':
|
||||
gfx_con.bgcol = va_arg(ap, u32);
|
||||
gfx_con.fillbg = 1;
|
||||
break;
|
||||
case '%':
|
||||
gfx_putc('%');
|
||||
break;
|
||||
case '\0':
|
||||
goto out;
|
||||
default:
|
||||
gfx_putc('%');
|
||||
gfx_putc(*fmt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
gfx_putc(*fmt);
|
||||
fmt++;
|
||||
}
|
||||
|
||||
out:
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void gfx_hexdump(u32 base, const void *buf, u32 len)
|
||||
{
|
||||
if (!gfx_con_init_done || gfx_con.mute)
|
||||
return;
|
||||
|
||||
u8 *buff = (u8 *)buf;
|
||||
|
||||
u8 prevFontSize = gfx_con.fntsz;
|
||||
gfx_con.fntsz = 8;
|
||||
for(u32 i = 0; i < len; i++)
|
||||
{
|
||||
if(i % 0x10 == 0)
|
||||
{
|
||||
if(i != 0)
|
||||
{
|
||||
gfx_puts("| ");
|
||||
for(u32 j = 0; j < 0x10; j++)
|
||||
{
|
||||
u8 c = buff[i - 0x10 + j];
|
||||
if(c >= 32 && c <= 126)
|
||||
gfx_putc(c);
|
||||
else
|
||||
gfx_putc('.');
|
||||
}
|
||||
gfx_putc('\n');
|
||||
}
|
||||
gfx_printf("%08x: ", base + i);
|
||||
}
|
||||
gfx_printf("%02x ", buff[i]);
|
||||
if (i == len - 1)
|
||||
{
|
||||
int ln = len % 0x10 != 0;
|
||||
u32 k = 0x10 - 1;
|
||||
if (ln)
|
||||
{
|
||||
k = (len & 0xF) - 1;
|
||||
for (u32 j = 0; j < 0x10 - k; j++)
|
||||
gfx_puts(" ");
|
||||
}
|
||||
gfx_puts("| ");
|
||||
for(u32 j = 0; j < (ln ? k : k + 1); j++)
|
||||
{
|
||||
u8 c = buff[i - k + j];
|
||||
if(c >= 32 && c <= 126)
|
||||
gfx_putc(c);
|
||||
else
|
||||
gfx_putc('.');
|
||||
}
|
||||
gfx_putc('\n');
|
||||
}
|
||||
}
|
||||
gfx_putc('\n');
|
||||
gfx_con.fntsz = prevFontSize;
|
||||
}
|
||||
|
||||
void gfx_hexdiff(u32 base, const void *buf1, const void *buf2, u32 len)
|
||||
{
|
||||
if (!gfx_con_init_done || gfx_con.mute)
|
||||
return;
|
||||
|
||||
u8 *buff1 = (u8 *)buf1;
|
||||
u8 *buff2 = (u8 *)buf2;
|
||||
|
||||
if (memcmp(buff1, buff2, len) == 0)
|
||||
{
|
||||
gfx_printf("Diff: No differences found.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
u8 prevFontSize = gfx_con.fntsz;
|
||||
gfx_con.fntsz = 8;
|
||||
for(u32 i = 0; i < len; i+=0x10)
|
||||
{
|
||||
u32 bytes_left = len - i < 0x10 ? len - i : 0x10;
|
||||
if (memcmp(buff1 + i, buff2 + i, bytes_left) == 0)
|
||||
continue;
|
||||
gfx_printf("Diff 1: %08x: ", base + i);
|
||||
for (u32 j = 0; j < bytes_left; j++)
|
||||
{
|
||||
if (buff1[i+j] != buff2[i+j])
|
||||
gfx_con.fgcol = COLOR_ORANGE;
|
||||
gfx_printf("%02x ", buff1[i+j]);
|
||||
gfx_con.fgcol = 0xFFCCCCCC;
|
||||
}
|
||||
gfx_puts("| ");
|
||||
gfx_putc('\n');
|
||||
gfx_printf("Diff 2: %08x: ", base + i);
|
||||
for (u32 j = 0; j < bytes_left; j++)
|
||||
{
|
||||
if (buff1[i+j] != buff2[i+j])
|
||||
gfx_con.fgcol = COLOR_ORANGE;
|
||||
gfx_printf("%02x ", buff2[i+j]);
|
||||
gfx_con.fgcol = 0xFFCCCCCC;
|
||||
}
|
||||
gfx_puts("| ");
|
||||
gfx_putc('\n');
|
||||
gfx_putc('\n');
|
||||
}
|
||||
gfx_putc('\n');
|
||||
gfx_con.fntsz = prevFontSize;
|
||||
}
|
||||
|
||||
static int abs(int x)
|
||||
{
|
||||
if (x < 0)
|
||||
return -x;
|
||||
return x;
|
||||
}
|
||||
|
||||
void gfx_set_pixel(u32 x, u32 y, u32 color)
|
||||
{
|
||||
gfx_ctxt.fb[x + y * gfx_ctxt.stride] = color;
|
||||
}
|
||||
|
||||
void gfx_line(int x0, int y0, int x1, int y1, u32 color)
|
||||
{
|
||||
int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
|
||||
int dy = abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
|
||||
int err = (dx > dy ? dx : -dy) / 2, e2;
|
||||
|
||||
while (1)
|
||||
{
|
||||
gfx_set_pixel(x0, y0, color);
|
||||
if (x0 == x1 && y0 == y1)
|
||||
break;
|
||||
e2 = err;
|
||||
if (e2 >-dx)
|
||||
{
|
||||
err -= dy;
|
||||
x0 += sx;
|
||||
}
|
||||
if (e2 < dy)
|
||||
{
|
||||
err += dx;
|
||||
y0 += sy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gfx_set_rect_grey(const u8 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y)
|
||||
{
|
||||
u32 pos = 0;
|
||||
for (u32 y = pos_y; y < (pos_y + size_y); y++)
|
||||
{
|
||||
for (u32 x = pos_x; x < (pos_x + size_x); x++)
|
||||
{
|
||||
memset(&gfx_ctxt.fb[x + y*gfx_ctxt.stride], buf[pos], 4);
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void gfx_set_rect_rgb(const u8 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y)
|
||||
{
|
||||
u32 pos = 0;
|
||||
for (u32 y = pos_y; y < (pos_y + size_y); y++)
|
||||
{
|
||||
for (u32 x = pos_x; x < (pos_x + size_x); x++)
|
||||
{
|
||||
gfx_ctxt.fb[x + y * gfx_ctxt.stride] = buf[pos + 2] | (buf[pos + 1] << 8) | (buf[pos] << 16);
|
||||
pos+=3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gfx_set_rect_argb(const u32 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y)
|
||||
{
|
||||
u32 *ptr = (u32 *)buf;
|
||||
for (u32 y = pos_y; y < (pos_y + size_y); y++)
|
||||
for (u32 x = pos_x; x < (pos_x + size_x); x++)
|
||||
gfx_ctxt.fb[x + y * gfx_ctxt.stride] = *ptr++;
|
||||
}
|
||||
|
||||
void gfx_render_bmp_argb(const u32 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y)
|
||||
{
|
||||
for (u32 y = pos_y; y < (pos_y + size_y); y++)
|
||||
{
|
||||
for (u32 x = pos_x; x < (pos_x + size_x); x++)
|
||||
gfx_ctxt.fb[x + y * gfx_ctxt.stride] = buf[(size_y + pos_y - 1 - y ) * size_x + x - pos_x];
|
||||
}
|
||||
}
|
79
source/gfx/gfx.h
Normal file
79
source/gfx/gfx.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
* Copyright (c) 2018-2021 CTCaer
|
||||
* Copyright (c) 2019-2021 shchmue
|
||||
* Copyright (c) 2018 M4xw
|
||||
*
|
||||
* 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 _GFX_H_
|
||||
#define _GFX_H_
|
||||
|
||||
#include <utils/types.h>
|
||||
|
||||
#define EPRINTF(text) gfx_printf("%k"text"%k\n", 0xFFFF0000, 0xFFCCCCCC)
|
||||
#define EPRINTFARGS(text, args...) gfx_printf("%k"text"%k\n", 0xFFFF0000, args, 0xFFCCCCCC)
|
||||
#define WPRINTF(text) gfx_printf("%k"text"%k\n", 0xFFFFDD00, 0xFFCCCCCC)
|
||||
#define WPRINTFARGS(text, args...) gfx_printf("%k"text"%k\n", 0xFFFFDD00, args, 0xFFCCCCCC)
|
||||
|
||||
typedef struct _gfx_ctxt_t
|
||||
{
|
||||
u32 *fb;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 stride;
|
||||
} gfx_ctxt_t;
|
||||
|
||||
typedef struct _gfx_con_t
|
||||
{
|
||||
gfx_ctxt_t *gfx_ctxt;
|
||||
u32 fntsz;
|
||||
u32 x;
|
||||
u32 y;
|
||||
u32 savedx;
|
||||
u32 savedy;
|
||||
u32 fgcol;
|
||||
int fillbg;
|
||||
u32 bgcol;
|
||||
bool mute;
|
||||
} gfx_con_t;
|
||||
|
||||
// Global gfx console and context.
|
||||
extern gfx_ctxt_t gfx_ctxt;
|
||||
extern gfx_con_t gfx_con;
|
||||
|
||||
void gfx_init_ctxt(u32 *fb, u32 width, u32 height, u32 stride);
|
||||
void gfx_clear_grey(u8 color);
|
||||
void gfx_clear_partial_grey(u8 color, u32 pos_x, u32 height);
|
||||
void gfx_clear_color(u32 color);
|
||||
void gfx_con_init();
|
||||
void gfx_con_setcol(u32 fgcol, int fillbg, u32 bgcol);
|
||||
void gfx_con_getpos(u32 *x, u32 *y);
|
||||
void gfx_con_setpos(u32 x, u32 y);
|
||||
void gfx_putc(char c);
|
||||
void gfx_puts(const char *s);
|
||||
void gfx_printf(const char *fmt, ...);
|
||||
void gfx_hexdump(u32 base, const void *buf, u32 len);
|
||||
void gfx_hexdiff(u32 base, const void *buf1, const void *buf2, u32 len);
|
||||
|
||||
void gfx_set_pixel(u32 x, u32 y, u32 color);
|
||||
void gfx_line(int x0, int y0, int x1, int y1, u32 color);
|
||||
void gfx_put_small_sep();
|
||||
void gfx_put_big_sep();
|
||||
void gfx_set_rect_grey(const u8 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y);
|
||||
void gfx_set_rect_rgb(const u8 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y);
|
||||
void gfx_set_rect_argb(const u32 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y);
|
||||
void gfx_render_bmp_argb(const u32 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y);
|
||||
|
||||
#endif
|
210
source/gfx/tui.c
Normal file
210
source/gfx/tui.c
Normal file
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
* Copyright (c) 2018 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 <display/di.h>
|
||||
#include "tui.h"
|
||||
#include "../config.h"
|
||||
#include <power/max17050.h>
|
||||
#include <utils/btn.h>
|
||||
#include <utils/util.h>
|
||||
|
||||
extern hekate_config h_cfg;
|
||||
|
||||
void tui_sbar(bool force_update)
|
||||
{
|
||||
u32 cx, cy;
|
||||
static u32 sbar_time_keeping = 0;
|
||||
|
||||
u32 timePassed = get_tmr_s() - sbar_time_keeping;
|
||||
if (!force_update)
|
||||
if (timePassed < 5)
|
||||
return;
|
||||
|
||||
u8 prevFontSize = gfx_con.fntsz;
|
||||
gfx_con.fntsz = 16;
|
||||
sbar_time_keeping = get_tmr_s();
|
||||
|
||||
u32 battPercent = 0;
|
||||
int battVoltCurr = 0;
|
||||
|
||||
gfx_con_getpos(&cx, &cy);
|
||||
gfx_con_setpos(0, 1260);
|
||||
|
||||
max17050_get_property(MAX17050_RepSOC, (int *)&battPercent);
|
||||
max17050_get_property(MAX17050_VCELL, &battVoltCurr);
|
||||
|
||||
gfx_clear_partial_grey(0x30, 1256, 24);
|
||||
gfx_printf("%K%k Battery: %d.%d%% (%d mV) - Charge:", 0xFF303030, 0xFF888888,
|
||||
(battPercent >> 8) & 0xFF, (battPercent & 0xFF) / 26, battVoltCurr);
|
||||
|
||||
max17050_get_property(MAX17050_Current, &battVoltCurr);
|
||||
|
||||
if (battVoltCurr >= 0)
|
||||
gfx_printf(" %k+%d mA%k%K\n",
|
||||
0xFF008800, battVoltCurr / 1000, 0xFFCCCCCC, 0xFF1B1B1B);
|
||||
else
|
||||
gfx_printf(" %k-%d mA%k%K\n",
|
||||
0xFF880000, (~battVoltCurr) / 1000, 0xFFCCCCCC, 0xFF1B1B1B);
|
||||
gfx_con.fntsz = prevFontSize;
|
||||
gfx_con_setpos(cx, cy);
|
||||
}
|
||||
|
||||
void tui_pbar(int x, int y, u32 val, u32 fgcol, u32 bgcol)
|
||||
{
|
||||
u32 cx, cy;
|
||||
if (val > 200)
|
||||
val = 200;
|
||||
|
||||
gfx_con_getpos(&cx, &cy);
|
||||
|
||||
gfx_con_setpos(x, y);
|
||||
|
||||
gfx_printf("%k[%3d%%]%k", fgcol, val, 0xFFCCCCCC);
|
||||
|
||||
x += 7 * gfx_con.fntsz;
|
||||
|
||||
for (u32 i = 0; i < (gfx_con.fntsz >> 3) * 6; i++)
|
||||
{
|
||||
gfx_line(x, y + i + 1, x + 3 * val, y + i + 1, fgcol);
|
||||
gfx_line(x + 3 * val, y + i + 1, x + 3 * 100, y + i + 1, bgcol);
|
||||
}
|
||||
|
||||
gfx_con_setpos(cx, cy);
|
||||
|
||||
// Update status bar.
|
||||
tui_sbar(false);
|
||||
}
|
||||
|
||||
void *tui_do_menu(menu_t *menu)
|
||||
{
|
||||
int idx = 0, prev_idx = 0, cnt = 0x7FFFFFFF;
|
||||
|
||||
gfx_clear_partial_grey(0x1B, 0, 1256);
|
||||
tui_sbar(true);
|
||||
|
||||
while (true)
|
||||
{
|
||||
gfx_con_setcol(0xFFCCCCCC, 1, 0xFF1B1B1B);
|
||||
gfx_con_setpos(menu->x, menu->y);
|
||||
gfx_printf("[%kPi%kck%klo%kck%k_R%kCM%k v%d.%d.%d%k]\n\n",
|
||||
colors[0], colors[1], colors[2], colors[3], colors[4], colors[5], 0xFFFF00FF, LP_VER_MJ, LP_VER_MN, LP_VER_BF, 0xFFCCCCCC);
|
||||
|
||||
// Skip caption or seperator lines selection.
|
||||
while (menu->ents[idx].type == MENT_CAPTION ||
|
||||
menu->ents[idx].type == MENT_CHGLINE)
|
||||
{
|
||||
if (prev_idx <= idx || (!idx && prev_idx == cnt - 1))
|
||||
{
|
||||
idx++;
|
||||
if (idx > (cnt - 1))
|
||||
{
|
||||
idx = 0;
|
||||
prev_idx = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
idx--;
|
||||
if (idx < 0)
|
||||
{
|
||||
idx = cnt - 1;
|
||||
prev_idx = cnt;
|
||||
}
|
||||
}
|
||||
}
|
||||
prev_idx = idx;
|
||||
|
||||
// Draw the menu.
|
||||
for (cnt = 0; menu->ents[cnt].type != MENT_END; cnt++)
|
||||
{
|
||||
if (cnt == idx)
|
||||
gfx_con_setcol(0xFF1B1B1B, 1, 0xFFCCCCCC);
|
||||
else
|
||||
gfx_con_setcol(0xFFCCCCCC, 1, 0xFF1B1B1B);
|
||||
if (menu->ents[cnt].type != MENT_CHGLINE)
|
||||
{
|
||||
if (cnt == idx)
|
||||
gfx_printf(" %s", menu->ents[cnt].caption);
|
||||
else
|
||||
gfx_printf("%k %s", menu->ents[cnt].color, menu->ents[cnt].caption);
|
||||
}
|
||||
if(menu->ents[cnt].type == MENT_MENU)
|
||||
gfx_printf("%k...", 0xFF0099EE);
|
||||
gfx_printf(" \n");
|
||||
}
|
||||
gfx_con_setcol(0xFFCCCCCC, 1, 0xFF1B1B1B);
|
||||
gfx_putc('\n');
|
||||
|
||||
// Print help and battery status.
|
||||
gfx_con_setpos(0, 1127);
|
||||
if (h_cfg.emummc_force_disable)
|
||||
gfx_printf("%kNo emuMMC config found.\n", 0xFF800000);
|
||||
gfx_con_setpos(0, 1191);
|
||||
gfx_printf("%k VOL: Move up/down\n PWR: Select option%k", 0xFF555555, 0xFFCCCCCC);
|
||||
|
||||
display_backlight_brightness(h_cfg.backlight, 1000);
|
||||
|
||||
// Wait for user command.
|
||||
u32 btn = btn_wait();
|
||||
|
||||
if (btn & BTN_VOL_DOWN && idx < (cnt - 1))
|
||||
idx++;
|
||||
else if (btn & BTN_VOL_DOWN && idx == (cnt - 1))
|
||||
{
|
||||
idx = 0;
|
||||
prev_idx = -1;
|
||||
}
|
||||
if (btn & BTN_VOL_UP && idx > 0)
|
||||
idx--;
|
||||
else if (btn & BTN_VOL_UP && idx == 0)
|
||||
{
|
||||
idx = cnt - 1;
|
||||
prev_idx = cnt;
|
||||
}
|
||||
if (btn & BTN_POWER)
|
||||
{
|
||||
ment_t *ent = &menu->ents[idx];
|
||||
switch (ent->type)
|
||||
{
|
||||
case MENT_HANDLER:
|
||||
ent->handler(ent->data);
|
||||
break;
|
||||
case MENT_MENU:
|
||||
return tui_do_menu(ent->menu);
|
||||
break;
|
||||
case MENT_DATA:
|
||||
return ent->data;
|
||||
break;
|
||||
case MENT_BACK:
|
||||
return NULL;
|
||||
break;
|
||||
case MENT_HDLR_RE:
|
||||
ent->handler(ent);
|
||||
if (!ent->data)
|
||||
return NULL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
gfx_con.fntsz = 16;
|
||||
gfx_clear_partial_grey(0x1B, 0, 1256);
|
||||
}
|
||||
tui_sbar(false);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
66
source/gfx/tui.h
Normal file
66
source/gfx/tui.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
* Copyright (c) 2018 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/>.
|
||||
*/
|
||||
|
||||
#ifndef _TUI_H_
|
||||
#define _TUI_H_
|
||||
|
||||
#include <utils/types.h>
|
||||
#include <gfx_utils.h>
|
||||
|
||||
#define MENT_END 0
|
||||
#define MENT_HANDLER 1
|
||||
#define MENT_MENU 2
|
||||
#define MENT_DATA 3
|
||||
#define MENT_BACK 4
|
||||
#define MENT_CAPTION 5
|
||||
#define MENT_CHGLINE 6
|
||||
#define MENT_HDLR_RE 7
|
||||
|
||||
typedef struct _ment_t
|
||||
{
|
||||
u32 type;
|
||||
const char *caption;
|
||||
u32 color;
|
||||
void *data;
|
||||
union
|
||||
{
|
||||
void(*handler)(void *);
|
||||
struct _menu_t *menu;
|
||||
};
|
||||
} ment_t;
|
||||
|
||||
typedef struct _menu_t
|
||||
{
|
||||
ment_t *ents;
|
||||
const char *caption;
|
||||
u32 x;
|
||||
u32 y;
|
||||
} menu_t;
|
||||
|
||||
#define MDEF_END() {MENT_END}
|
||||
#define MDEF_HANDLER(caption, _handler, color) { MENT_HANDLER, caption, color, NULL, { .handler = _handler } }
|
||||
#define MDEF_HANDLER_EX(caption, data, _handler, color) { MENT_HANDLER, caption, color, data, { .handler = _handler } }
|
||||
#define MDEF_MENU(caption, _menu, color) { MENT_MENU, caption, color, NULL, { .menu = _menu } }
|
||||
#define MDEF_BACK(color) { MENT_BACK, "Back", color }
|
||||
#define MDEF_CAPTION(caption, color) { MENT_CAPTION, caption, color }
|
||||
#define MDEF_CHGLINE() {MENT_CHGLINE}
|
||||
|
||||
void tui_sbar(bool force_update);
|
||||
void tui_pbar(int x, int y, u32 val, u32 fgcol, u32 bgcol);
|
||||
void *tui_do_menu(menu_t *menu);
|
||||
|
||||
#endif
|
41
source/hos/hos.h
Normal file
41
source/hos/hos.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
* Copyright (c) 2018-2021 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/>.
|
||||
*/
|
||||
|
||||
#ifndef _HOS_H_
|
||||
#define _HOS_H_
|
||||
|
||||
#define KEYBLOB_OFFSET 0x180000
|
||||
|
||||
#define KB_FIRMWARE_VERSION_100 0
|
||||
#define KB_FIRMWARE_VERSION_300 1
|
||||
#define KB_FIRMWARE_VERSION_301 2
|
||||
#define KB_FIRMWARE_VERSION_400 3
|
||||
#define KB_FIRMWARE_VERSION_500 4
|
||||
#define KB_FIRMWARE_VERSION_600 5
|
||||
#define KB_FIRMWARE_VERSION_620 6
|
||||
#define KB_FIRMWARE_VERSION_700 7
|
||||
#define KB_FIRMWARE_VERSION_810 8
|
||||
#define KB_FIRMWARE_VERSION_900 9
|
||||
#define KB_FIRMWARE_VERSION_910 10
|
||||
#define KB_FIRMWARE_VERSION_1210 11
|
||||
#define KB_FIRMWARE_VERSION_1300 12
|
||||
#define KB_FIRMWARE_VERSION_1400 13
|
||||
#define KB_FIRMWARE_VERSION_1500 14
|
||||
#define KB_FIRMWARE_VERSION_1600 15
|
||||
#define KB_FIRMWARE_VERSION_MAX KB_FIRMWARE_VERSION_1600 //!TODO: Update on mkey changes.
|
||||
|
||||
#endif
|
96
source/keys/cal0_read.c
Normal file
96
source/keys/cal0_read.c
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright (c) 2022 shchmue
|
||||
*
|
||||
* 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 "cal0_read.h"
|
||||
|
||||
#include <gfx_utils.h>
|
||||
#include <sec/se.h>
|
||||
#include <sec/se_t210.h>
|
||||
#include "../storage/emummc.h"
|
||||
#include "../storage/nx_emmc.h"
|
||||
#include <utils/util.h>
|
||||
|
||||
bool cal0_read(u32 tweak_ks, u32 crypt_ks, void *read_buffer) {
|
||||
nx_emmc_cal0_t *cal0 = (nx_emmc_cal0_t *)read_buffer;
|
||||
|
||||
// Check if CAL0 was already read into this buffer
|
||||
if (cal0->magic == MAGIC_CAL0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!emummc_storage_read(NX_EMMC_CALIBRATION_OFFSET / NX_EMMC_BLOCKSIZE, NX_EMMC_CALIBRATION_SIZE / NX_EMMC_BLOCKSIZE, read_buffer)) {
|
||||
EPRINTF("Unable to read PRODINFO.");
|
||||
return false;
|
||||
}
|
||||
|
||||
se_aes_xts_crypt(tweak_ks, crypt_ks, DECRYPT, 0, read_buffer, read_buffer, XTS_CLUSTER_SIZE, NX_EMMC_CALIBRATION_SIZE / XTS_CLUSTER_SIZE);
|
||||
|
||||
if (cal0->magic != MAGIC_CAL0) {
|
||||
EPRINTF("Invalid CAL0 magic. Check BIS key 0.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cal0_get_ssl_rsa_key(const nx_emmc_cal0_t *cal0, const void **out_key, u32 *out_key_size, const void **out_iv, u32 *out_generation) {
|
||||
const u32 ext_key_size = sizeof(cal0->ext_ssl_key_iv) + sizeof(cal0->ext_ssl_key);
|
||||
const u32 ext_key_crc_size = ext_key_size + sizeof(cal0->ext_ssl_key_ver) + sizeof(cal0->crc16_pad39);
|
||||
const u32 key_size = sizeof(cal0->ssl_key_iv) + sizeof(cal0->ssl_key);
|
||||
const u32 key_crc_size = key_size + sizeof(cal0->crc16_pad18);
|
||||
|
||||
if (cal0->ext_ssl_key_crc == crc16_calc(cal0->ext_ssl_key_iv, ext_key_crc_size)) {
|
||||
*out_key = cal0->ext_ssl_key;
|
||||
*out_key_size = ext_key_size;
|
||||
*out_iv = cal0->ext_ssl_key_iv;
|
||||
// Settings sysmodule manually zeroes this out below cal version 9
|
||||
*out_generation = cal0->version <= 8 ? 0 : cal0->ext_ssl_key_ver;
|
||||
} else if (cal0->ssl_key_crc == crc16_calc(cal0->ssl_key_iv, key_crc_size)) {
|
||||
*out_key = cal0->ssl_key;
|
||||
*out_key_size = key_size;
|
||||
*out_iv = cal0->ssl_key_iv;
|
||||
*out_generation = 0;
|
||||
} else {
|
||||
EPRINTF("Crc16 error reading device key.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool cal0_get_eticket_rsa_key(const nx_emmc_cal0_t *cal0, const void **out_key, u32 *out_key_size, const void **out_iv, u32 *out_generation) {
|
||||
const u32 ext_key_size = sizeof(cal0->ext_ecc_rsa2048_eticket_key_iv) + sizeof(cal0->ext_ecc_rsa2048_eticket_key);
|
||||
const u32 ext_key_crc_size = ext_key_size + sizeof(cal0->ext_ecc_rsa2048_eticket_key_ver) + sizeof(cal0->crc16_pad38);
|
||||
const u32 key_size = sizeof(cal0->rsa2048_eticket_key_iv) + sizeof(cal0->rsa2048_eticket_key);
|
||||
const u32 key_crc_size = key_size + sizeof(cal0->crc16_pad21);
|
||||
|
||||
if (cal0->ext_ecc_rsa2048_eticket_key_crc == crc16_calc(cal0->ext_ecc_rsa2048_eticket_key_iv, ext_key_crc_size)) {
|
||||
*out_key = cal0->ext_ecc_rsa2048_eticket_key;
|
||||
*out_key_size = ext_key_size;
|
||||
*out_iv = cal0->ext_ecc_rsa2048_eticket_key_iv;
|
||||
// Settings sysmodule manually zeroes this out below cal version 9
|
||||
*out_generation = cal0->version <= 8 ? 0 : cal0->ext_ecc_rsa2048_eticket_key_ver;
|
||||
} else if (cal0->rsa2048_eticket_key_crc == crc16_calc(cal0->rsa2048_eticket_key_iv, key_crc_size)) {
|
||||
*out_key = cal0->rsa2048_eticket_key;
|
||||
*out_key_size = key_size;
|
||||
*out_iv = cal0->rsa2048_eticket_key_iv;
|
||||
*out_generation = 0;
|
||||
} else {
|
||||
EPRINTF("Crc16 error reading device key.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
27
source/keys/cal0_read.h
Normal file
27
source/keys/cal0_read.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2022 shchmue
|
||||
*
|
||||
* 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 _CAL0_READ_H_
|
||||
#define _CAL0_READ_H_
|
||||
|
||||
#include "../storage/nx_emmc_bis.h"
|
||||
#include <utils/types.h>
|
||||
|
||||
bool cal0_read(u32 tweak_ks, u32 crypt_ks, void *read_buffer);
|
||||
bool cal0_get_ssl_rsa_key(const nx_emmc_cal0_t *cal0, const void **out_key, u32 *out_key_size, const void **out_iv, u32 *out_generation);
|
||||
bool cal0_get_eticket_rsa_key(const nx_emmc_cal0_t *cal0, const void **out_key, u32 *out_key_size, const void **out_iv, u32 *out_generation);
|
||||
|
||||
#endif
|
244
source/keys/crypto.c
Normal file
244
source/keys/crypto.c
Normal file
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
* Copyright (c) 2022 shchmue
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "crypto.h"
|
||||
|
||||
#include "../../keygen/tsec_keygen.h"
|
||||
|
||||
#include "../config.h"
|
||||
#include "../hos/hos.h"
|
||||
#include <sec/se.h>
|
||||
#include <sec/se_t210.h>
|
||||
#include <sec/tsec.h>
|
||||
#include <soc/fuse.h>
|
||||
#include <utils/util.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
extern hekate_config h_cfg;
|
||||
|
||||
int key_exists(const void *data) {
|
||||
return memcmp(data, "\x00\x00\x00\x00\x00\x00\x00\x00", 8) != 0;
|
||||
}
|
||||
|
||||
int run_ams_keygen() {
|
||||
tsec_ctxt_t tsec_ctxt;
|
||||
tsec_ctxt.fw = tsec_keygen;
|
||||
tsec_ctxt.size = sizeof(tsec_keygen);
|
||||
tsec_ctxt.type = TSEC_FW_TYPE_NEW;
|
||||
|
||||
u32 retries = 0;
|
||||
u32 temp_key[SE_KEY_128_SIZE / 4];
|
||||
while (tsec_query(temp_key, &tsec_ctxt) < 0) {
|
||||
retries++;
|
||||
if (retries > 15) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool check_keyslot_access() {
|
||||
u8 test_data[SE_KEY_128_SIZE] = {0};
|
||||
const u8 test_ciphertext[SE_KEY_128_SIZE] = {0};
|
||||
se_aes_key_set(KS_AES_ECB, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", SE_KEY_128_SIZE);
|
||||
se_aes_crypt_block_ecb(KS_AES_ECB, DECRYPT, test_data, test_ciphertext);
|
||||
|
||||
return memcmp(test_data, "\x7b\x1d\x29\xa1\x6c\xf8\xcc\xab\x84\xf0\xb8\xa5\x98\xe4\x2f\xa6", SE_KEY_128_SIZE) == 0;
|
||||
}
|
||||
|
||||
bool test_rsa_keypair(const void *public_exponent, const void *private_exponent, const void *modulus) {
|
||||
u32 plaintext[SE_RSA2048_DIGEST_SIZE / 4] = {0},
|
||||
ciphertext[SE_RSA2048_DIGEST_SIZE / 4] = {0},
|
||||
work[SE_RSA2048_DIGEST_SIZE / 4] = {0};
|
||||
|
||||
plaintext[63] = 0xCAFEBABE;
|
||||
|
||||
se_rsa_key_set(0, modulus, SE_RSA2048_DIGEST_SIZE, private_exponent, SE_RSA2048_DIGEST_SIZE);
|
||||
se_rsa_exp_mod(0, ciphertext, SE_RSA2048_DIGEST_SIZE, plaintext, SE_RSA2048_DIGEST_SIZE);
|
||||
|
||||
se_rsa_key_set(0, modulus, SE_RSA2048_DIGEST_SIZE, public_exponent, 4);
|
||||
se_rsa_exp_mod(0, work, SE_RSA2048_DIGEST_SIZE, ciphertext, SE_RSA2048_DIGEST_SIZE);
|
||||
|
||||
return memcmp(plaintext, work, SE_RSA2048_DIGEST_SIZE) == 0;
|
||||
}
|
||||
|
||||
// _mgf1_xor() and rsa_oaep_decode were derived from Atmosphère
|
||||
static void _mgf1_xor(void *masked, u32 masked_size, const void *seed, u32 seed_size) {
|
||||
u8 cur_hash[0x20] __attribute__((aligned(4)));
|
||||
u8 hash_buf[0xe4] __attribute__((aligned(4)));
|
||||
|
||||
u32 hash_buf_size = seed_size + 4;
|
||||
memcpy(hash_buf, seed, seed_size);
|
||||
u32 round_num = 0;
|
||||
|
||||
u8 *p_out = (u8 *)masked;
|
||||
|
||||
while (masked_size) {
|
||||
u32 cur_size = MIN(masked_size, 0x20);
|
||||
|
||||
for (u32 i = 0; i < 4; i++)
|
||||
hash_buf[seed_size + 3 - i] = (round_num >> (8 * i)) & 0xff;
|
||||
round_num++;
|
||||
|
||||
se_calc_sha256_oneshot(cur_hash, hash_buf, hash_buf_size);
|
||||
|
||||
for (unsigned int i = 0; i < cur_size; i++) {
|
||||
*p_out ^= cur_hash[i];
|
||||
p_out++;
|
||||
}
|
||||
|
||||
masked_size -= cur_size;
|
||||
}
|
||||
}
|
||||
|
||||
u32 rsa_oaep_decode(void *dst, u32 dst_size, const void *label_digest, u32 label_digest_size, u8 *buf, u32 buf_size) {
|
||||
if (dst_size <= 0 || buf_size < 0x43 || label_digest_size != 0x20)
|
||||
return 0;
|
||||
|
||||
bool is_valid = buf[0] == 0;
|
||||
|
||||
u32 db_len = buf_size - 0x21;
|
||||
u8 *seed = buf + 1;
|
||||
u8 *db = seed + 0x20;
|
||||
_mgf1_xor(seed, 0x20, db, db_len);
|
||||
_mgf1_xor(db, db_len, seed, 0x20);
|
||||
|
||||
is_valid &= memcmp(label_digest, db, 0x20) ? 0 : 1;
|
||||
|
||||
db += 0x20;
|
||||
db_len -= 0x20;
|
||||
|
||||
int msg_ofs = 0;
|
||||
int looking_for_one = 1;
|
||||
int invalid_db_padding = 0;
|
||||
int is_zero;
|
||||
int is_one;
|
||||
for (int i = 0; i < db_len; ) {
|
||||
is_zero = (db[i] == 0);
|
||||
is_one = (db[i] == 1);
|
||||
msg_ofs += (looking_for_one & is_one) * (++i);
|
||||
looking_for_one &= ~is_one;
|
||||
invalid_db_padding |= (looking_for_one & ~is_zero);
|
||||
}
|
||||
|
||||
is_valid &= (invalid_db_padding == 0);
|
||||
|
||||
const u32 msg_size = MIN(dst_size, is_valid * (db_len - msg_ofs));
|
||||
memcpy(dst, db + msg_ofs, msg_size);
|
||||
|
||||
return msg_size;
|
||||
}
|
||||
|
||||
void derive_rsa_kek(u32 ks, key_storage_t *keys, void *out_rsa_kek, const void *kekek_source, const void *kek_source, u32 generation, u32 option) {
|
||||
u32 access_key[SE_KEY_128_SIZE / 4] = {0};
|
||||
generate_aes_kek(ks, keys, access_key, kekek_source, generation, option);
|
||||
get_device_unique_data_key(ks, out_rsa_kek, access_key, kek_source);
|
||||
}
|
||||
|
||||
// Equivalent to spl::GenerateAesKek
|
||||
void generate_aes_kek(u32 ks, key_storage_t *keys, void *out_kek, const void *kek_source, u32 generation, u32 option) {
|
||||
bool device_unique = GET_IS_DEVICE_UNIQUE(option);
|
||||
u32 seal_key_index = GET_SEAL_KEY_INDEX(option);
|
||||
|
||||
if (generation)
|
||||
generation--;
|
||||
|
||||
u8 static_source[SE_KEY_128_SIZE] __attribute__((aligned(4)));
|
||||
for (u32 i = 0; i < SE_KEY_128_SIZE; i++)
|
||||
static_source[i] = aes_kek_generation_source[i] ^ seal_key_masks[seal_key_index][i];
|
||||
|
||||
if (device_unique) {
|
||||
get_device_key(ks, keys, keys->temp_key, generation);
|
||||
} else {
|
||||
memcpy(keys->temp_key, keys->master_key[generation], sizeof(keys->temp_key));
|
||||
}
|
||||
se_aes_key_set(ks, keys->temp_key, SE_KEY_128_SIZE);
|
||||
se_aes_unwrap_key(ks, ks, static_source);
|
||||
se_aes_crypt_block_ecb(ks, DECRYPT, out_kek, kek_source);
|
||||
}
|
||||
|
||||
// Based on spl::LoadAesKey but instead of prepping keyslot, returns calculated key
|
||||
void load_aes_key(u32 ks, void *out_key, const void *access_key, const void *key_source) {
|
||||
se_aes_key_set(ks, access_key, SE_KEY_128_SIZE);
|
||||
se_aes_crypt_block_ecb(ks, DECRYPT, out_key, key_source);
|
||||
}
|
||||
|
||||
// Equivalent to spl::GenerateAesKey
|
||||
void generate_aes_key(u32 ks, key_storage_t *keys, void *out_key, u32 key_size, const void *access_key, const void *key_source) {
|
||||
u32 aes_key[SE_KEY_128_SIZE / 4] = {0};
|
||||
load_aes_key(ks, aes_key, access_key, aes_key_generation_source);
|
||||
se_aes_key_set(ks, aes_key, SE_KEY_128_SIZE);
|
||||
se_aes_crypt_ecb(ks, DECRYPT, out_key, key_size, key_source, key_size);
|
||||
}
|
||||
|
||||
// Equivalent to smc::PrepareDeviceUniqueDataKey but with no sealing
|
||||
void get_device_unique_data_key(u32 ks, void *out_key, const void *access_key, const void *key_source) {
|
||||
load_aes_key(ks, out_key, access_key, key_source);
|
||||
}
|
||||
|
||||
// Equivalent to spl::DecryptAesKey.
|
||||
void decrypt_aes_key(u32 ks, key_storage_t *keys, void *out_key, const void *key_source, u32 generation, u32 option) {
|
||||
u32 access_key[SE_KEY_128_SIZE / 4] = {0};
|
||||
generate_aes_kek(ks, keys, access_key, aes_key_decryption_source, generation, option);
|
||||
generate_aes_key(ks, keys, out_key, SE_KEY_128_SIZE, access_key, key_source);
|
||||
}
|
||||
|
||||
// Equivalent to smc::GetSecureData
|
||||
void get_secure_data(key_storage_t *keys, void *out_data) {
|
||||
se_aes_key_set(KS_AES_CTR, keys->device_key, SE_KEY_128_SIZE);
|
||||
u8 *d = (u8 *)out_data;
|
||||
se_aes_crypt_ctr(KS_AES_CTR, d + SE_KEY_128_SIZE * 0, SE_KEY_128_SIZE, secure_data_source, SE_KEY_128_SIZE, secure_data_counters[0]);
|
||||
se_aes_crypt_ctr(KS_AES_CTR, d + SE_KEY_128_SIZE * 1, SE_KEY_128_SIZE, secure_data_source, SE_KEY_128_SIZE, secure_data_counters[0]);
|
||||
|
||||
// Apply tweak
|
||||
for (u32 i = 0; i < SE_KEY_128_SIZE; i++) {
|
||||
d[SE_KEY_128_SIZE + i] ^= secure_data_tweaks[0][i];
|
||||
}
|
||||
}
|
||||
|
||||
// Equivalent to spl::GenerateSpecificAesKey
|
||||
void generate_specific_aes_key(u32 ks, key_storage_t *keys, void *out_key, const void *key_source, u32 generation) {
|
||||
if (fuse_read_bootrom_rev() >= 0x7F) {
|
||||
get_device_key(ks, keys, keys->temp_key, generation == 0 ? 0 : generation - 1);
|
||||
se_aes_key_set(ks, keys->temp_key, SE_KEY_128_SIZE);
|
||||
se_aes_unwrap_key(ks, ks, retail_specific_aes_key_source);
|
||||
se_aes_crypt_ecb(ks, DECRYPT, out_key, SE_KEY_128_SIZE * 2, key_source, SE_KEY_128_SIZE * 2);
|
||||
} else {
|
||||
get_secure_data(keys, out_key);
|
||||
}
|
||||
}
|
||||
|
||||
void get_device_key(u32 ks, key_storage_t *keys, void *out_device_key, u32 generation) {
|
||||
if (generation == KB_FIRMWARE_VERSION_100 && !h_cfg.t210b01) {
|
||||
memcpy(out_device_key, keys->device_key, SE_KEY_128_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (generation >= KB_FIRMWARE_VERSION_400) {
|
||||
generation -= KB_FIRMWARE_VERSION_400;
|
||||
} else {
|
||||
generation = 0;
|
||||
}
|
||||
u32 temp_key_source[SE_KEY_128_SIZE / 4] = {0};
|
||||
load_aes_key(ks, temp_key_source, keys->device_key_4x, device_master_key_source_sources[generation]);
|
||||
const void *kek_source = fuse_read_hw_state() == FUSE_NX_HW_STATE_PROD ? device_master_kek_sources[generation] : device_master_kek_sources_dev[generation];
|
||||
se_aes_key_set(ks, keys->master_key[0], SE_KEY_128_SIZE);
|
||||
se_aes_unwrap_key(ks, ks, kek_source);
|
||||
se_aes_crypt_block_ecb(ks, DECRYPT, out_device_key, temp_key_source);
|
||||
}
|
240
source/keys/crypto.h
Normal file
240
source/keys/crypto.h
Normal file
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
* Copyright (c) 2022 shchmue
|
||||
*
|
||||
* 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 _CRYPTO_H_
|
||||
#define _CRYPTO_H_
|
||||
|
||||
#include "es_types.h"
|
||||
|
||||
#include "../hos/hos.h"
|
||||
#include <sec/se_t210.h>
|
||||
#include "../storage/nx_emmc.h"
|
||||
#include <utils/types.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
// Sha256 hash of the null string.
|
||||
static const u8 null_hash[SE_SHA_256_SIZE] __attribute__((aligned(4))) = {
|
||||
0xE3, 0xB0, 0xC4, 0x42, 0x98, 0xFC, 0x1C, 0x14, 0x9A, 0xFB, 0xF4, 0xC8, 0x99, 0x6F, 0xB9, 0x24,
|
||||
0x27, 0xAE, 0x41, 0xE4, 0x64, 0x9B, 0x93, 0x4C, 0xA4, 0x95, 0x99, 0x1B, 0x78, 0x52, 0xB8, 0x55};
|
||||
|
||||
static const u8 aes_kek_generation_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x4D, 0x87, 0x09, 0x86, 0xC4, 0x5D, 0x20, 0x72, 0x2F, 0xBA, 0x10, 0x53, 0xDA, 0x92, 0xE8, 0xA9};
|
||||
|
||||
static const u8 aes_key_generation_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x89, 0x61, 0x5E, 0xE0, 0x5C, 0x31, 0xB6, 0x80, 0x5F, 0xE5, 0x8F, 0x3D, 0xA2, 0x4F, 0x7A, 0xA8};
|
||||
|
||||
static const u8 aes_key_decryption_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x11, 0x70, 0x24, 0x2B, 0x48, 0x69, 0x11, 0xF1, 0x11, 0xB0, 0x0C, 0x47, 0x7C, 0xC3, 0xEF, 0x7E};
|
||||
|
||||
static const u8 device_master_kek_sources[KB_FIRMWARE_VERSION_MAX - KB_FIRMWARE_VERSION_400 + 1][0x10] __attribute__((aligned(4))) = {
|
||||
{0x88, 0x62, 0x34, 0x6E, 0xFA, 0xF7, 0xD8, 0x3F, 0xE1, 0x30, 0x39, 0x50, 0xF0, 0xB7, 0x5D, 0x5D}, /* 4.0.0 Device Master Kek Source. */
|
||||
{0x06, 0x1E, 0x7B, 0xE9, 0x6D, 0x47, 0x8C, 0x77, 0xC5, 0xC8, 0xE7, 0x94, 0x9A, 0xA8, 0x5F, 0x2E}, /* 5.0.0 Device Master Kek Source. */
|
||||
{0x99, 0xFA, 0x98, 0xBD, 0x15, 0x1C, 0x72, 0xFD, 0x7D, 0x9A, 0xD5, 0x41, 0x00, 0xFD, 0xB2, 0xEF}, /* 6.0.0 Device Master Kek Source. */
|
||||
{0x81, 0x3C, 0x6C, 0xBF, 0x5D, 0x21, 0xDE, 0x77, 0x20, 0xD9, 0x6C, 0xE3, 0x22, 0x06, 0xAE, 0xBB}, /* 6.2.0 Device Master Kek Source. */
|
||||
{0x86, 0x61, 0xB0, 0x16, 0xFA, 0x7A, 0x9A, 0xEA, 0xF6, 0xF5, 0xBE, 0x1A, 0x13, 0x5B, 0x6D, 0x9E}, /* 7.0.0 Device Master Kek Source. */
|
||||
{0xA6, 0x81, 0x71, 0xE7, 0xB5, 0x23, 0x74, 0xB0, 0x39, 0x8C, 0xB7, 0xFF, 0xA0, 0x62, 0x9F, 0x8D}, /* 8.1.0 Device Master Kek Source. */
|
||||
{0x03, 0xE7, 0xEB, 0x43, 0x1B, 0xCF, 0x5F, 0xB5, 0xED, 0xDC, 0x97, 0xAE, 0x21, 0x8D, 0x19, 0xED}, /* 9.0.0 Device Master Kek Source. */
|
||||
{0xCE, 0xFE, 0x41, 0x0F, 0x46, 0x9A, 0x30, 0xD6, 0xF2, 0xE9, 0x0C, 0x6B, 0xB7, 0x15, 0x91, 0x36}, /* 9.1.0 Device Master Kek Source. */
|
||||
{0xC2, 0x65, 0x34, 0x6E, 0xC7, 0xC6, 0x5D, 0x97, 0x3E, 0x34, 0x5C, 0x6B, 0xB3, 0x7E, 0xC6, 0xE3}, /* 12.1.0 Device Master Kek Source. */
|
||||
{0x77, 0x52, 0x92, 0xF0, 0xAA, 0xE3, 0xFB, 0xE0, 0x60, 0x16, 0xB3, 0x78, 0x68, 0x53, 0xF7, 0xA8}, /* 13.0.0 Device Master Kek Source. */
|
||||
{0x67, 0xD5, 0xD6, 0x0C, 0x08, 0xF5, 0xA3, 0x11, 0xBD, 0x6D, 0x5A, 0xEB, 0x96, 0x24, 0xB0, 0xD2}, /* 14.0.0 Device Master Kek Source. */
|
||||
{0x7C, 0x30, 0xED, 0x8B, 0x39, 0x25, 0x2C, 0x08, 0x8F, 0x48, 0xDC, 0x28, 0xE6, 0x1A, 0x6B, 0x49}, /* 15.0.0 Device Master Kek Source. */
|
||||
{0xF0, 0xF3, 0xFF, 0x52, 0x75, 0x2F, 0xBA, 0x4D, 0x09, 0x72, 0x30, 0x89, 0xA9, 0xDF, 0xFE, 0x1F}, /* 16.0.0 Device Master Kek Source. */
|
||||
}; //!TODO: Update on mkey changes.
|
||||
|
||||
static const u8 device_master_kek_sources_dev[KB_FIRMWARE_VERSION_MAX - KB_FIRMWARE_VERSION_400 + 1][0x10] __attribute__((aligned(4))) = {
|
||||
{0xD6, 0xBD, 0x9F, 0xC6, 0x18, 0x09, 0xE1, 0x96, 0x20, 0x39, 0x60, 0xD2, 0x89, 0x83, 0x31, 0x34}, /* 4.0.0 Device Master Kek Source. */
|
||||
{0x59, 0x2D, 0x20, 0x69, 0x33, 0xB5, 0x17, 0xBA, 0xCF, 0xB1, 0x4E, 0xFD, 0xE4, 0xC2, 0x7B, 0xA8}, /* 5.0.0 Device Master Kek Source. */
|
||||
{0xF6, 0xD8, 0x59, 0x63, 0x8F, 0x47, 0xCB, 0x4A, 0xD8, 0x74, 0x05, 0x7F, 0x88, 0x92, 0x33, 0xA5}, /* 6.0.0 Device Master Kek Source. */
|
||||
{0x20, 0xAB, 0xF2, 0x0F, 0x05, 0xE3, 0xDE, 0x2E, 0xA1, 0xFB, 0x37, 0x5E, 0x8B, 0x22, 0x1A, 0x38}, /* 6.2.0 Device Master Kek Source. */
|
||||
{0x60, 0xAE, 0x56, 0x68, 0x11, 0xE2, 0x0C, 0x99, 0xDE, 0x05, 0xAE, 0x68, 0x78, 0x85, 0x04, 0xAE}, /* 7.0.0 Device Master Kek Source. */
|
||||
{0x94, 0xD6, 0xA8, 0xC0, 0x95, 0xAF, 0xD0, 0xA6, 0x27, 0x53, 0x5E, 0xE5, 0x8E, 0x70, 0x1F, 0x87}, /* 8.1.0 Device Master Kek Source. */
|
||||
{0x61, 0x6A, 0x88, 0x21, 0xA3, 0x52, 0xB0, 0x19, 0x16, 0x25, 0xA4, 0xE3, 0x4C, 0x54, 0x02, 0x0F}, /* 9.0.0 Device Master Kek Source. */
|
||||
{0x9D, 0xB1, 0xAE, 0xCB, 0xF6, 0xF6, 0xE3, 0xFE, 0xAB, 0x6F, 0xCB, 0xAF, 0x38, 0x03, 0xFC, 0x7B}, /* 9.1.0 Device Master Kek Source. */
|
||||
{0xC4, 0xBB, 0xF3, 0x9F, 0xA3, 0xAA, 0x00, 0x99, 0x7C, 0x97, 0xAD, 0x91, 0x8F, 0xE8, 0x45, 0xCB}, /* 12.1.0 Device Master Kek Source. */
|
||||
{0x20, 0x20, 0xAA, 0xFB, 0x89, 0xC2, 0xF0, 0x70, 0xB5, 0xE0, 0xA3, 0x11, 0x8A, 0x29, 0x8D, 0x0F}, /* 13.0.0 Device Master Kek Source. */
|
||||
{0xCE, 0x14, 0x74, 0x66, 0x98, 0xA8, 0x6D, 0x7D, 0xBD, 0x54, 0x91, 0x68, 0x5F, 0x1D, 0x0E, 0xEA}, /* 14.0.0 Device Master Kek Source. */
|
||||
{0xAE, 0x05, 0x48, 0x65, 0xAB, 0x17, 0x9D, 0x3D, 0x51, 0xB7, 0x56, 0xBD, 0x9B, 0x0B, 0x5B, 0x6E}, /* 15.0.0 Device Master Kek Source. */
|
||||
{0xFF, 0xF6, 0x4B, 0x0F, 0xFF, 0x0D, 0xC0, 0x4F, 0x56, 0x8A, 0x40, 0x74, 0x67, 0xC5, 0xFE, 0x9F}, /* 16.0.0 Device Master Kek Source. */
|
||||
}; //!TODO: Update on mkey changes.
|
||||
|
||||
static const u8 device_master_key_source_sources[KB_FIRMWARE_VERSION_MAX - KB_FIRMWARE_VERSION_400 + 1][0x10] __attribute__((aligned(4))) = {
|
||||
{0x8B, 0x4E, 0x1C, 0x22, 0x42, 0x07, 0xC8, 0x73, 0x56, 0x94, 0x08, 0x8B, 0xCC, 0x47, 0x0F, 0x5D}, /* 4.0.0 Device Master Key Source Source. */
|
||||
{0x6C, 0xEF, 0xC6, 0x27, 0x8B, 0xEC, 0x8A, 0x91, 0x99, 0xAB, 0x24, 0xAC, 0x4F, 0x1C, 0x8F, 0x1C}, /* 5.0.0 Device Master Key Source Source. */
|
||||
{0x70, 0x08, 0x1B, 0x97, 0x44, 0x64, 0xF8, 0x91, 0x54, 0x9D, 0xC6, 0x84, 0x8F, 0x1A, 0xB2, 0xE4}, /* 6.0.0 Device Master Key Source Source. */
|
||||
{0x8E, 0x09, 0x1F, 0x7A, 0xBB, 0xCA, 0x6A, 0xFB, 0xB8, 0x9B, 0xD5, 0xC1, 0x25, 0x9C, 0xA9, 0x17}, /* 6.2.0 Device Master Key Source Source. */
|
||||
{0x8F, 0x77, 0x5A, 0x96, 0xB0, 0x94, 0xFD, 0x8D, 0x28, 0xE4, 0x19, 0xC8, 0x16, 0x1C, 0xDB, 0x3D}, /* 7.0.0 Device Master Key Source Source. */
|
||||
{0x67, 0x62, 0xD4, 0x8E, 0x55, 0xCF, 0xFF, 0x41, 0x31, 0x15, 0x3B, 0x24, 0x0C, 0x7C, 0x07, 0xAE}, /* 8.1.0 Device Master Key Source Source. */
|
||||
{0x4A, 0xC3, 0x4E, 0x14, 0x8B, 0x96, 0x4A, 0xD5, 0xD4, 0x99, 0x73, 0xC4, 0x45, 0xAB, 0x8B, 0x49}, /* 9.0.0 Device Master Key Source Source. */
|
||||
{0x14, 0xB8, 0x74, 0x12, 0xCB, 0xBD, 0x0B, 0x8F, 0x20, 0xFB, 0x30, 0xDA, 0x27, 0xE4, 0x58, 0x94}, /* 9.1.0 Device Master Key Source Source. */
|
||||
{0xAA, 0xFD, 0xBC, 0xBB, 0x25, 0xC3, 0xA4, 0xEF, 0xE3, 0xEE, 0x58, 0x53, 0xB7, 0xF8, 0xDD, 0xD6}, /* 12.1.0 Device Master Key Source Source. */
|
||||
{0xE4, 0xF3, 0x45, 0x6F, 0x18, 0xA1, 0x89, 0xF8, 0xDA, 0x4C, 0x64, 0x75, 0x68, 0xE6, 0xBD, 0x4F}, /* 13.0.0 Device Master Key Source Source. */
|
||||
{0x5B, 0x94, 0x63, 0xF7, 0xAD, 0x96, 0x1B, 0xA6, 0x23, 0x30, 0x06, 0x4D, 0x01, 0xE4, 0xCE, 0x1D}, /* 14.0.0 Device Master Key Source Source. */
|
||||
{0x5E, 0xC9, 0xC5, 0x0A, 0xD0, 0x5F, 0x8B, 0x7B, 0xA7, 0x39, 0xEA, 0xBC, 0x60, 0x0F, 0x74, 0xE6}, /* 15.0.0 Device Master Key Source Source. */
|
||||
{0xEA, 0x90, 0x6E, 0xA8, 0xAE, 0x92, 0x99, 0x64, 0x36, 0xC1, 0xF3, 0x1C, 0xC6, 0x32, 0x83, 0x8C}, /* 16.0.0 Device Master Key Source Source. */
|
||||
}; //!TODO: Update on mkey changes.
|
||||
|
||||
static const u8 seal_key_masks[][0x10] __attribute__((aligned(4))) = {
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // SealKey_LoadAesKey
|
||||
{0xA2, 0xAB, 0xBF, 0x9C, 0x92, 0x2F, 0xBB, 0xE3, 0x78, 0x79, 0x9B, 0xC0, 0xCC, 0xEA, 0xA5, 0x74}, // SealKey_DecryptDeviceUniqueData
|
||||
{0x57, 0xE2, 0xD9, 0x45, 0xE4, 0x92, 0xF4, 0xFD, 0xC3, 0xF9, 0x86, 0x38, 0x89, 0x78, 0x9F, 0x3C}, // SealKey_ImportLotusKey
|
||||
{0xE5, 0x4D, 0x9A, 0x02, 0xF0, 0x4F, 0x5F, 0xA8, 0xAD, 0x76, 0x0A, 0xF6, 0x32, 0x95, 0x59, 0xBB}, // SealKey_ImportEsDeviceKey
|
||||
{0x59, 0xD9, 0x31, 0xF4, 0xA7, 0x97, 0xB8, 0x14, 0x40, 0xD6, 0xA2, 0x60, 0x2B, 0xED, 0x15, 0x31}, // SealKey_ReencryptDeviceUniqueData
|
||||
{0xFD, 0x6A, 0x25, 0xE5, 0xD8, 0x38, 0x7F, 0x91, 0x49, 0xDA, 0xF8, 0x59, 0xA8, 0x28, 0xE6, 0x75}, // SealKey_ImportSslKey
|
||||
{0x89, 0x96, 0x43, 0x9A, 0x7C, 0xD5, 0x59, 0x55, 0x24, 0xD5, 0x24, 0x18, 0xAB, 0x6C, 0x04, 0x61}, // SealKey_ImportEsClientCertKey
|
||||
};
|
||||
|
||||
static const u8 retail_specific_aes_key_source[0x10] __attribute__((aligned(4))) = {
|
||||
0xE2, 0xD6, 0xB8, 0x7A, 0x11, 0x9C, 0xB8, 0x80, 0xE8, 0x22, 0x88, 0x8A, 0x46, 0xFB, 0xA1, 0x95};
|
||||
|
||||
static const u8 secure_data_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
static const u8 secure_data_counters[1][0x10] __attribute__((aligned(4))) = {
|
||||
{0x3C, 0xD5, 0x92, 0xEC, 0x68, 0x31, 0x4A, 0x06, 0xD4, 0x1B, 0x0C, 0xD9, 0xF6, 0x2E, 0xD9, 0xE9}
|
||||
};
|
||||
|
||||
static const u8 secure_data_tweaks[1][0x10] __attribute__((aligned(4))) = {
|
||||
{0xAC, 0xCA, 0x9A, 0xCA, 0xFF, 0x2E, 0xB9, 0x22, 0xCC, 0x1F, 0x4F, 0xAD, 0xDD, 0x77, 0x21, 0x1E}
|
||||
};
|
||||
|
||||
//!TODO: Update on keygen changes.
|
||||
#define TSEC_ROOT_KEY_VERSION 2
|
||||
|
||||
// Picklock_RCM keyslots
|
||||
#define KS_BIS_00_CRYPT 0
|
||||
#define KS_BIS_00_TWEAK 1
|
||||
#define KS_BIS_01_CRYPT 2
|
||||
#define KS_BIS_01_TWEAK 3
|
||||
#define KS_BIS_02_CRYPT 4
|
||||
#define KS_BIS_02_TWEAK 5
|
||||
#define KS_AES_CTR 6
|
||||
#define KS_AES_ECB 8
|
||||
#define KS_AES_CMAC 10
|
||||
|
||||
// Mariko keyslots
|
||||
#define KS_MARIKO_KEK 12
|
||||
#define KS_MARIKO_BEK 13
|
||||
|
||||
// Other Switch keyslots
|
||||
#define KS_TSEC 12
|
||||
#define KS_SECURE_BOOT 14
|
||||
|
||||
// Atmosphere keygen keyslots
|
||||
#define KS_TSEC_ROOT_DEV 11
|
||||
#define KS_TSEC_ROOT 13
|
||||
|
||||
#define RSA_PUBLIC_EXPONENT 65537
|
||||
|
||||
#define KEYBLOB_UNK_DATA_SIZE 0x70
|
||||
#define KEYBLOB_UNUSED_SIZE (NX_EMMC_BLOCKSIZE - SE_AES_CMAC_DIGEST_SIZE - SE_AES_IV_SIZE - sizeof(keyblob_t))
|
||||
|
||||
typedef struct {
|
||||
u8 master_kek[SE_KEY_128_SIZE];
|
||||
u8 data[KEYBLOB_UNK_DATA_SIZE];
|
||||
u8 package1_key[SE_KEY_128_SIZE];
|
||||
} keyblob_t;
|
||||
|
||||
typedef struct {
|
||||
u8 cmac[SE_AES_CMAC_DIGEST_SIZE];
|
||||
u8 iv[SE_AES_IV_SIZE];
|
||||
keyblob_t key_data;
|
||||
u8 unused[KEYBLOB_UNUSED_SIZE];
|
||||
} encrypted_keyblob_t;
|
||||
|
||||
typedef struct {
|
||||
u8 temp_key[SE_KEY_128_SIZE],
|
||||
bis_key[4][SE_KEY_128_SIZE * 2],
|
||||
device_key[SE_KEY_128_SIZE],
|
||||
device_key_4x[SE_KEY_128_SIZE],
|
||||
sd_seed[SE_KEY_128_SIZE],
|
||||
// FS-related keys
|
||||
header_key[SE_KEY_128_SIZE * 2],
|
||||
save_mac_key[SE_KEY_128_SIZE],
|
||||
// other sysmodule keys
|
||||
eticket_rsa_kek[SE_KEY_128_SIZE],
|
||||
eticket_rsa_kek_personalized[SE_KEY_128_SIZE],
|
||||
ssl_rsa_kek[SE_KEY_128_SIZE],
|
||||
ssl_rsa_kek_legacy[SE_KEY_128_SIZE],
|
||||
ssl_rsa_kek_personalized[SE_KEY_128_SIZE],
|
||||
ssl_rsa_key[SE_RSA2048_DIGEST_SIZE + 0x20],
|
||||
// keyblob-derived families
|
||||
keyblob_key[KB_FIRMWARE_VERSION_600 + 1][SE_KEY_128_SIZE],
|
||||
keyblob_mac_key[KB_FIRMWARE_VERSION_600 + 1][SE_KEY_128_SIZE],
|
||||
package1_key[KB_FIRMWARE_VERSION_600 + 1][SE_KEY_128_SIZE],
|
||||
// master key-derived families
|
||||
key_area_key[3][KB_FIRMWARE_VERSION_MAX + 1][SE_KEY_128_SIZE],
|
||||
master_kek[KB_FIRMWARE_VERSION_MAX + 1][SE_KEY_128_SIZE],
|
||||
master_key[KB_FIRMWARE_VERSION_MAX + 1][SE_KEY_128_SIZE],
|
||||
package2_key[KB_FIRMWARE_VERSION_MAX + 1][SE_KEY_128_SIZE],
|
||||
titlekek[KB_FIRMWARE_VERSION_MAX + 1][SE_KEY_128_SIZE],
|
||||
tsec_key[SE_KEY_128_SIZE],
|
||||
tsec_root_key[SE_KEY_128_SIZE];
|
||||
u32 secure_boot_key[4];
|
||||
keyblob_t keyblob[KB_FIRMWARE_VERSION_600 + 1];
|
||||
eticket_rsa_keypair_t eticket_rsa_keypair;
|
||||
} key_storage_t;
|
||||
|
||||
typedef enum {
|
||||
SEAL_KEY_LOAD_AES_KEY = 0,
|
||||
SEAL_KEY_DECRYPT_DEVICE_UNIQUE_DATA = 1,
|
||||
SEAL_KEY_IMPORT_LOTUS_KEY = 2,
|
||||
SEAL_KEY_IMPORT_ES_DEVICE_KEY = 3,
|
||||
SEAL_KEY_REENCRYPT_DEVICE_UNIQUE_DATA = 4,
|
||||
SEAL_KEY_IMPORT_SSL_KEY = 5,
|
||||
SEAL_KEY_IMPORT_ES_CLIENT_CERT_KEY = 6,
|
||||
} seal_key_t;
|
||||
|
||||
typedef enum {
|
||||
NOT_DEVICE_UNIQUE = 0,
|
||||
IS_DEVICE_UNIQUE = 1,
|
||||
} device_unique_t;
|
||||
|
||||
#define SET_SEAL_KEY_INDEX(x) (((x) & 7) << 5)
|
||||
#define GET_SEAL_KEY_INDEX(x) (((x) >> 5) & 7)
|
||||
#define GET_IS_DEVICE_UNIQUE(x) ((x) & 1)
|
||||
|
||||
int key_exists(const void *data);
|
||||
|
||||
int run_ams_keygen();
|
||||
|
||||
bool check_keyslot_access();
|
||||
|
||||
bool test_rsa_keypair(const void *public_exponent, const void *private_exponent, const void *modulus);
|
||||
u32 rsa_oaep_decode(void *dst, u32 dst_size, const void *label_digest, u32 label_digest_size, u8 *buf, u32 buf_size);
|
||||
|
||||
void derive_rsa_kek(u32 ks, key_storage_t *keys, void *out_rsa_kek, const void *kekek_source, const void *kek_source, u32 generation, u32 option);
|
||||
|
||||
// Equivalent to spl::GenerateAesKek
|
||||
void generate_aes_kek(u32 ks, key_storage_t *keys, void *out_kek, const void *kek_source, u32 generation, u32 option);
|
||||
// Equivalent to spl::GenerateAesKey
|
||||
void generate_aes_key(u32 ks, key_storage_t *keys, void *out_key, u32 key_size, const void *access_key, const void *key_source);
|
||||
// Equivalent to spl::GenerateSpecificAesKey
|
||||
void generate_specific_aes_key(u32 ks, key_storage_t *keys, void *out_key, const void *key_source, u32 generation);
|
||||
// Equivalent to spl::DecryptAesKey.
|
||||
void decrypt_aes_key(u32 ks, key_storage_t *keys, void *out_key, const void *key_source, u32 generation, u32 option);
|
||||
// Based on spl::LoadAesKey but instead of prepping keyslot, returns calculated key
|
||||
void load_aes_key(u32 ks, void *out_key, const void *access_key, const void *key_source);
|
||||
|
||||
// Equivalent to smc::PrepareDeviceUniqueDataKey but with no sealing
|
||||
void get_device_unique_data_key(u32 ks, void *out_key, const void *access_key, const void *key_source);
|
||||
// Equivalent to smc::GetSecureData
|
||||
void get_secure_data(key_storage_t *keys, void *out_data);
|
||||
// Equivalent to smc::PrepareDeviceMasterKey
|
||||
void get_device_key(u32 ks, key_storage_t *keys, void *out_device_key, u32 generation);
|
||||
|
||||
#endif
|
146
source/keys/es_crypto.c
Normal file
146
source/keys/es_crypto.c
Normal file
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright (c) 2022 shchmue
|
||||
*
|
||||
* 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 "es_crypto.h"
|
||||
|
||||
#include "cal0_read.h"
|
||||
|
||||
#include "../config.h"
|
||||
#include <gfx_utils.h>
|
||||
#include "../gfx/tui.h"
|
||||
#include <mem/minerva.h>
|
||||
#include <sec/se.h>
|
||||
#include <sec/se_t210.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
extern hekate_config h_cfg;
|
||||
|
||||
bool test_eticket_rsa_keypair(const eticket_rsa_keypair_t *keypair) {
|
||||
if (byte_swap_32(keypair->public_exponent) != RSA_PUBLIC_EXPONENT)
|
||||
return false;
|
||||
return test_rsa_keypair(&keypair->public_exponent, keypair->private_exponent, keypair->modulus);
|
||||
}
|
||||
|
||||
void es_derive_rsa_kek_device_unique(key_storage_t *keys, void *out_rsa_kek, u32 generation, bool is_dev) {
|
||||
if ((!h_cfg.t210b01 && !key_exists(keys->device_key)) || (h_cfg.t210b01 && (!key_exists(keys->master_key[0]) || !key_exists(keys->device_key_4x)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const void *kek_source = is_dev ? eticket_rsa_kek_source_dev : eticket_rsa_kek_source;
|
||||
const u32 option = SET_SEAL_KEY_INDEX(SEAL_KEY_IMPORT_ES_DEVICE_KEY) | IS_DEVICE_UNIQUE;
|
||||
derive_rsa_kek(KS_AES_ECB, keys, out_rsa_kek, eticket_rsa_kekek_source, kek_source, generation, option);
|
||||
}
|
||||
|
||||
void es_derive_rsa_kek_legacy(key_storage_t *keys, void *out_rsa_kek) {
|
||||
if (!key_exists(keys->master_key[0])) {
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 generation = 0;
|
||||
const u32 option = SET_SEAL_KEY_INDEX(SEAL_KEY_IMPORT_ES_DEVICE_KEY) | NOT_DEVICE_UNIQUE;
|
||||
derive_rsa_kek(KS_AES_ECB, keys, out_rsa_kek, eticket_rsa_kekek_source, eticket_rsa_kek_source_legacy, generation, option);
|
||||
}
|
||||
|
||||
void es_derive_rsa_kek_original(key_storage_t *keys, void *out_rsa_kek, bool is_dev) {
|
||||
if (!key_exists(keys->master_key[0])) {
|
||||
return;
|
||||
}
|
||||
|
||||
const void *kek_source = is_dev ? eticket_rsa_kek_source_dev : eticket_rsa_kek_source;
|
||||
const u32 generation = 0;
|
||||
const u32 option = SET_SEAL_KEY_INDEX(SEAL_KEY_IMPORT_ES_DEVICE_KEY) | NOT_DEVICE_UNIQUE;
|
||||
derive_rsa_kek(KS_AES_ECB, keys, out_rsa_kek, eticket_rsa_kekek_source, kek_source, generation, option);
|
||||
}
|
||||
|
||||
bool decrypt_eticket_rsa_key(key_storage_t *keys, void *buffer, bool is_dev) {
|
||||
if (!cal0_read(KS_BIS_00_TWEAK, KS_BIS_00_CRYPT, buffer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nx_emmc_cal0_t *cal0 = (nx_emmc_cal0_t *)buffer;
|
||||
u32 generation = 0;
|
||||
const void *encrypted_key = NULL;
|
||||
const void *iv = NULL;
|
||||
u32 key_size = 0;
|
||||
void *ctr_key = NULL;
|
||||
|
||||
if (!cal0_get_eticket_rsa_key(cal0, &encrypted_key, &key_size, &iv, &generation)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle legacy case
|
||||
if (key_size == ETICKET_RSA_KEYPAIR_SIZE) {
|
||||
u32 temp_key[SE_KEY_128_SIZE / 4] = {0};
|
||||
es_derive_rsa_kek_legacy(keys, temp_key);
|
||||
ctr_key = temp_key;
|
||||
|
||||
se_aes_key_set(KS_AES_CTR, ctr_key, SE_KEY_128_SIZE);
|
||||
se_aes_crypt_ctr(KS_AES_CTR, &keys->eticket_rsa_keypair, sizeof(keys->eticket_rsa_keypair), encrypted_key, sizeof(keys->eticket_rsa_keypair), iv);
|
||||
|
||||
if (test_eticket_rsa_keypair(&keys->eticket_rsa_keypair)) {
|
||||
memcpy(keys->eticket_rsa_kek, ctr_key, sizeof(keys->eticket_rsa_kek));
|
||||
return true;
|
||||
}
|
||||
// Fall through and try usual method if not applicable
|
||||
}
|
||||
|
||||
if (generation) {
|
||||
es_derive_rsa_kek_device_unique(keys, keys->eticket_rsa_kek_personalized, generation, is_dev);
|
||||
ctr_key = keys->eticket_rsa_kek_personalized;
|
||||
} else {
|
||||
ctr_key = keys->eticket_rsa_kek;
|
||||
}
|
||||
|
||||
se_aes_key_set(KS_AES_CTR, ctr_key, SE_KEY_128_SIZE);
|
||||
se_aes_crypt_ctr(KS_AES_CTR, &keys->eticket_rsa_keypair, sizeof(keys->eticket_rsa_keypair), encrypted_key, sizeof(keys->eticket_rsa_keypair), iv);
|
||||
|
||||
if (!test_eticket_rsa_keypair(&keys->eticket_rsa_keypair)) {
|
||||
EPRINTF("Invalid eticket keypair.");
|
||||
memset(&keys->eticket_rsa_keypair, 0, sizeof(keys->eticket_rsa_keypair));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void es_decode_tickets(u32 buf_size, titlekey_buffer_t *titlekey_buffer, u32 remaining, u32 total, u32 *titlekey_count, u32 x, u32 y, u32 *pct, u32 *last_pct, bool is_personalized) {
|
||||
ticket_t *curr_ticket = (ticket_t *)titlekey_buffer->read_buffer;
|
||||
for (u32 i = 0; i < MIN(buf_size / sizeof(ticket_t), remaining) * sizeof(ticket_t) && curr_ticket->signature_type != 0; i += sizeof(ticket_t), curr_ticket++) {
|
||||
minerva_periodic_training();
|
||||
*pct = (total - remaining) * 100 / total;
|
||||
if (*pct > *last_pct && *pct <= 100) {
|
||||
*last_pct = *pct;
|
||||
tui_pbar(x, y, *pct, COLOR_GREEN, 0xFF155500);
|
||||
}
|
||||
|
||||
// This is in case an encrypted volatile ticket is left behind
|
||||
if (curr_ticket->signature_type != TICKET_SIG_TYPE_RSA2048_SHA256)
|
||||
continue;
|
||||
|
||||
u8 *curr_titlekey = curr_ticket->titlekey_block;
|
||||
const u32 block_size = SE_RSA2048_DIGEST_SIZE;
|
||||
const u32 titlekey_size = sizeof(titlekey_buffer->titlekeys[0]);
|
||||
if (is_personalized) {
|
||||
se_rsa_exp_mod(0, curr_titlekey, block_size, curr_titlekey, block_size);
|
||||
if (rsa_oaep_decode(curr_titlekey, titlekey_size, null_hash, sizeof(null_hash), curr_titlekey, block_size) != titlekey_size)
|
||||
continue;
|
||||
}
|
||||
memcpy(titlekey_buffer->rights_ids[*titlekey_count], curr_ticket->rights_id, sizeof(titlekey_buffer->rights_ids[0]));
|
||||
memcpy(titlekey_buffer->titlekeys[*titlekey_count], curr_titlekey, titlekey_size);
|
||||
(*titlekey_count)++;
|
||||
}
|
||||
}
|
49
source/keys/es_crypto.h
Normal file
49
source/keys/es_crypto.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (c) 2022 shchmue
|
||||
*
|
||||
* 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 _ES_CRYPTO_H_
|
||||
#define _ES_CRYPTO_H_
|
||||
|
||||
#include "crypto.h"
|
||||
#include "es_types.h"
|
||||
|
||||
#include <sec/se_t210.h>
|
||||
#include <utils/types.h>
|
||||
|
||||
#define ETICKET_RSA_KEYPAIR_SIZE (SE_AES_IV_SIZE + SE_RSA2048_DIGEST_SIZE * 2 + SE_KEY_128_SIZE)
|
||||
|
||||
#define TICKET_SIG_TYPE_RSA2048_SHA256 0x10004
|
||||
|
||||
static const u8 eticket_rsa_kek_source[0x10] __attribute__((aligned(4))) = {
|
||||
0xDB, 0xA4, 0x51, 0x12, 0x4C, 0xA0, 0xA9, 0x83, 0x68, 0x14, 0xF5, 0xED, 0x95, 0xE3, 0x12, 0x5B};
|
||||
static const u8 eticket_rsa_kek_source_dev[0x10] __attribute__((aligned(4))) = {
|
||||
0xBE, 0xC0, 0xBC, 0x8E, 0x75, 0xA0, 0xF6, 0x0C, 0x4A, 0x56, 0x64, 0x02, 0x3E, 0xD4, 0x9C, 0xD5};
|
||||
static const u8 eticket_rsa_kek_source_legacy[0x10] __attribute__((aligned(4))) = {
|
||||
0x88, 0x87, 0x50, 0x90, 0xA6, 0x2F, 0x75, 0x70, 0xA2, 0xD7, 0x71, 0x51, 0xAE, 0x6D, 0x39, 0x87};
|
||||
static const u8 eticket_rsa_kekek_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x46, 0x6E, 0x57, 0xB7, 0x4A, 0x44, 0x7F, 0x02, 0xF3, 0x21, 0xCD, 0xE5, 0x8F, 0x2F, 0x55, 0x35};
|
||||
|
||||
bool test_eticket_rsa_keypair(const eticket_rsa_keypair_t *keypair);
|
||||
|
||||
void es_derive_rsa_kek_device_unique(key_storage_t *keys, void *out_rsa_kek, u32 generation, bool is_dev);
|
||||
void es_derive_rsa_kek_legacy(key_storage_t *keys, void *out_rsa_kek);
|
||||
void es_derive_rsa_kek_original(key_storage_t *keys, void *out_rsa_kek, bool is_dev);
|
||||
|
||||
bool decrypt_eticket_rsa_key(key_storage_t *keys, void *buffer, bool is_dev);
|
||||
|
||||
void es_decode_tickets(u32 buf_size, titlekey_buffer_t *titlekey_buffer, u32 remaining, u32 total, u32 *titlekey_count, u32 x, u32 y, u32 *pct, u32 *last_pct, bool is_personalized);
|
||||
|
||||
#endif
|
76
source/keys/es_types.h
Normal file
76
source/keys/es_types.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (c) 2022 shchmue
|
||||
*
|
||||
* 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 _ES_TYPES_H_
|
||||
#define _ES_TYPES_H_
|
||||
|
||||
#include <sec/se_t210.h>
|
||||
#include <utils/types.h>
|
||||
|
||||
typedef struct {
|
||||
u8 private_exponent[SE_RSA2048_DIGEST_SIZE];
|
||||
u8 modulus[SE_RSA2048_DIGEST_SIZE];
|
||||
u32 public_exponent;
|
||||
u8 reserved[0xC];
|
||||
} eticket_rsa_keypair_t;
|
||||
|
||||
// only tickets of type Rsa2048Sha256 are expected
|
||||
typedef struct {
|
||||
u32 signature_type; // always 0x10004
|
||||
u8 signature[SE_RSA2048_DIGEST_SIZE];
|
||||
u8 sig_padding[0x3C];
|
||||
char issuer[0x40];
|
||||
u8 titlekey_block[SE_RSA2048_DIGEST_SIZE];
|
||||
u8 format_version;
|
||||
u8 titlekey_type;
|
||||
u16 ticket_version;
|
||||
u8 license_type;
|
||||
u8 common_key_id;
|
||||
u16 property_mask;
|
||||
u64 reserved;
|
||||
u64 ticket_id;
|
||||
u64 device_id;
|
||||
u8 rights_id[0x10];
|
||||
u32 account_id;
|
||||
u32 sect_total_size;
|
||||
u32 sect_hdr_offset;
|
||||
u16 sect_hdr_count;
|
||||
u16 sect_hdr_entry_size;
|
||||
u8 padding[0x140];
|
||||
} ticket_t;
|
||||
|
||||
typedef struct {
|
||||
u8 rights_id[0x10];
|
||||
u64 ticket_id;
|
||||
u32 account_id;
|
||||
u16 property_mask;
|
||||
u16 reserved;
|
||||
} ticket_record_t;
|
||||
|
||||
typedef struct {
|
||||
u8 read_buffer[SZ_256K];
|
||||
u8 rights_ids[SZ_256K / 0x10][0x10];
|
||||
u8 titlekeys[SZ_256K / 0x10][0x10];
|
||||
} titlekey_buffer_t;
|
||||
|
||||
typedef struct {
|
||||
char rights_id[0x20];
|
||||
char equals[3];
|
||||
char titlekey[0x20];
|
||||
char newline[1];
|
||||
} titlekey_text_buffer_t;
|
||||
|
||||
#endif
|
69
source/keys/fs_crypto.c
Normal file
69
source/keys/fs_crypto.c
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (c) 2022 shchmue
|
||||
*
|
||||
* 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 "fs_crypto.h"
|
||||
|
||||
#include "../config.h"
|
||||
#include <sec/se_t210.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
extern hekate_config h_cfg;
|
||||
|
||||
void fs_derive_bis_keys(key_storage_t *keys, u8 out_bis_keys[4][32], u32 generation) {
|
||||
if ((!h_cfg.t210b01 && !key_exists(keys->device_key)) || (h_cfg.t210b01 && (!key_exists(keys->master_key[0]) || !key_exists(keys->device_key_4x)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
generate_specific_aes_key(KS_AES_ECB, keys, out_bis_keys[0], bis_key_sources[0], generation);
|
||||
u32 access_key[SE_KEY_128_SIZE / 4] = {0};
|
||||
const u32 option = IS_DEVICE_UNIQUE;
|
||||
generate_aes_kek(KS_AES_ECB, keys, access_key, bis_kek_source, generation, option);
|
||||
generate_aes_key(KS_AES_ECB, keys, out_bis_keys[1], sizeof(bis_key_sources[1]), access_key, bis_key_sources[1]);
|
||||
generate_aes_key(KS_AES_ECB, keys, out_bis_keys[2], sizeof(bis_key_sources[2]), access_key, bis_key_sources[2]);
|
||||
memcpy(out_bis_keys[3], out_bis_keys[2], sizeof(bis_key_sources[2]));
|
||||
}
|
||||
|
||||
void fs_derive_header_key(key_storage_t *keys, void *out_key) {
|
||||
if (!key_exists(keys->master_key[0])) {
|
||||
return;
|
||||
}
|
||||
|
||||
u32 access_key[SE_KEY_128_SIZE / 4] = {0};
|
||||
const u32 generation = 0;
|
||||
const u32 option = NOT_DEVICE_UNIQUE;
|
||||
generate_aes_kek(KS_AES_ECB, keys, access_key, header_kek_source, generation, option);
|
||||
generate_aes_key(KS_AES_ECB, keys, out_key, sizeof(header_key_source), access_key, header_key_source);
|
||||
}
|
||||
|
||||
void fs_derive_key_area_key(key_storage_t *keys, void *out_key, u32 source_type, u32 generation) {
|
||||
u32 access_key[SE_KEY_128_SIZE / 4] = {0};
|
||||
const u32 option = NOT_DEVICE_UNIQUE;
|
||||
generate_aes_kek(KS_AES_ECB, keys, access_key, key_area_key_sources[source_type], generation + 1, option);
|
||||
load_aes_key(KS_AES_ECB, out_key, access_key, aes_key_generation_source);
|
||||
}
|
||||
|
||||
void fs_derive_save_mac_key(key_storage_t *keys, void *out_key) {
|
||||
if ((!h_cfg.t210b01 && !key_exists(keys->device_key)) || (h_cfg.t210b01 && (!key_exists(keys->master_key[0]) || !key_exists(keys->device_key_4x)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
u32 access_key[SE_KEY_128_SIZE / 4] = {0};
|
||||
const u32 generation = 0;
|
||||
const u32 option = IS_DEVICE_UNIQUE;
|
||||
generate_aes_kek(KS_AES_ECB, keys, access_key, save_mac_kek_source, generation, option);
|
||||
load_aes_key(KS_AES_ECB, out_key, access_key, save_mac_key_source);
|
||||
}
|
74
source/keys/fs_crypto.h
Normal file
74
source/keys/fs_crypto.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright (c) 2022 shchmue
|
||||
*
|
||||
* 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 _FS_CRYPTO_H_
|
||||
#define _FS_CRYPTO_H_
|
||||
|
||||
#include "crypto.h"
|
||||
|
||||
#include <utils/types.h>
|
||||
|
||||
static const u8 bis_kek_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x34, 0xC1, 0xA0, 0xC4, 0x82, 0x58, 0xF8, 0xB4, 0xFA, 0x9E, 0x5E, 0x6A, 0xDA, 0xFC, 0x7E, 0x4F};
|
||||
static const u8 bis_key_sources[3][0x20] __attribute__((aligned(4))) = {
|
||||
{0xF8, 0x3F, 0x38, 0x6E, 0x2C, 0xD2, 0xCA, 0x32, 0xA8, 0x9A, 0xB9, 0xAA, 0x29, 0xBF, 0xC7, 0x48,
|
||||
0x7D, 0x92, 0xB0, 0x3A, 0xA8, 0xBF, 0xDE, 0xE1, 0xA7, 0x4C, 0x3B, 0x6E, 0x35, 0xCB, 0x71, 0x06},
|
||||
{0x41, 0x00, 0x30, 0x49, 0xDD, 0xCC, 0xC0, 0x65, 0x64, 0x7A, 0x7E, 0xB4, 0x1E, 0xED, 0x9C, 0x5F,
|
||||
0x44, 0x42, 0x4E, 0xDA, 0xB4, 0x9D, 0xFC, 0xD9, 0x87, 0x77, 0x24, 0x9A, 0xDC, 0x9F, 0x7C, 0xA4},
|
||||
{0x52, 0xC2, 0xE9, 0xEB, 0x09, 0xE3, 0xEE, 0x29, 0x32, 0xA1, 0x0C, 0x1F, 0xB6, 0xA0, 0x92, 0x6C,
|
||||
0x4D, 0x12, 0xE1, 0x4B, 0x2A, 0x47, 0x4C, 0x1C, 0x09, 0xCB, 0x03, 0x59, 0xF0, 0x15, 0xF4, 0xE4}
|
||||
};
|
||||
|
||||
static const u8 header_kek_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x1F, 0x12, 0x91, 0x3A, 0x4A, 0xCB, 0xF0, 0x0D, 0x4C, 0xDE, 0x3A, 0xF6, 0xD5, 0x23, 0x88, 0x2A};
|
||||
static const u8 header_key_source[0x20] __attribute__((aligned(4))) = {
|
||||
0x5A, 0x3E, 0xD8, 0x4F, 0xDE, 0xC0, 0xD8, 0x26, 0x31, 0xF7, 0xE2, 0x5D, 0x19, 0x7B, 0xF5, 0xD0,
|
||||
0x1C, 0x9B, 0x7B, 0xFA, 0xF6, 0x28, 0x18, 0x3D, 0x71, 0xF6, 0x4D, 0x73, 0xF1, 0x50, 0xB9, 0xD2};
|
||||
|
||||
static const u8 key_area_key_sources[3][0x10] __attribute__((aligned(4))) = {
|
||||
{0x7F, 0x59, 0x97, 0x1E, 0x62, 0x9F, 0x36, 0xA1, 0x30, 0x98, 0x06, 0x6F, 0x21, 0x44, 0xC3, 0x0D}, // application
|
||||
{0x32, 0x7D, 0x36, 0x08, 0x5A, 0xD1, 0x75, 0x8D, 0xAB, 0x4E, 0x6F, 0xBA, 0xA5, 0x55, 0xD8, 0x82}, // ocean
|
||||
{0x87, 0x45, 0xF1, 0xBB, 0xA6, 0xBE, 0x79, 0x64, 0x7D, 0x04, 0x8B, 0xA6, 0x7B, 0x5F, 0xDA, 0x4A}, // system
|
||||
};
|
||||
|
||||
static const u8 save_mac_kek_source[0x10] __attribute__((aligned(4))) = {
|
||||
0xD8, 0x9C, 0x23, 0x6E, 0xC9, 0x12, 0x4E, 0x43, 0xC8, 0x2B, 0x03, 0x87, 0x43, 0xF9, 0xCF, 0x1B};
|
||||
static const u8 save_mac_key_source[0x10] __attribute__((aligned(4))) = {
|
||||
0xE4, 0xCD, 0x3D, 0x4A, 0xD5, 0x0F, 0x74, 0x28, 0x45, 0xA4, 0x87, 0xE5, 0xA0, 0x63, 0xEA, 0x1F};
|
||||
|
||||
static const u8 save_mac_sd_card_kek_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x04, 0x89, 0xEF, 0x5D, 0x32, 0x6E, 0x1A, 0x59, 0xC4, 0xB7, 0xAB, 0x8C, 0x36, 0x7A, 0xAB, 0x17};
|
||||
static const u8 save_mac_sd_card_key_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x6F, 0x64, 0x59, 0x47, 0xC5, 0x61, 0x46, 0xF9, 0xFF, 0xA0, 0x45, 0xD5, 0x95, 0x33, 0x29, 0x18};
|
||||
|
||||
static const u8 sd_card_custom_storage_key_source[0x20] __attribute__((aligned(4))) = {
|
||||
0x37, 0x0C, 0x34, 0x5E, 0x12, 0xE4, 0xCE, 0xFE, 0x21, 0xB5, 0x8E, 0x64, 0xDB, 0x52, 0xAF, 0x35,
|
||||
0x4F, 0x2C, 0xA5, 0xA3, 0xFC, 0x99, 0x9A, 0x47, 0xC0, 0x3E, 0xE0, 0x04, 0x48, 0x5B, 0x2F, 0xD0};
|
||||
static const u8 sd_card_kek_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x88, 0x35, 0x8D, 0x9C, 0x62, 0x9B, 0xA1, 0xA0, 0x01, 0x47, 0xDB, 0xE0, 0x62, 0x1B, 0x54, 0x32};
|
||||
static const u8 sd_card_nca_key_source[0x20] __attribute__((aligned(4))) = {
|
||||
0x58, 0x41, 0xA2, 0x84, 0x93, 0x5B, 0x56, 0x27, 0x8B, 0x8E, 0x1F, 0xC5, 0x18, 0xE9, 0x9F, 0x2B,
|
||||
0x67, 0xC7, 0x93, 0xF0, 0xF2, 0x4F, 0xDE, 0xD0, 0x75, 0x49, 0x5D, 0xCA, 0x00, 0x6D, 0x99, 0xC2};
|
||||
static const u8 sd_card_save_key_source[0x20] __attribute__((aligned(4))) = {
|
||||
0x24, 0x49, 0xB7, 0x22, 0x72, 0x67, 0x03, 0xA8, 0x19, 0x65, 0xE6, 0xE3, 0xEA, 0x58, 0x2F, 0xDD,
|
||||
0x9A, 0x95, 0x15, 0x17, 0xB1, 0x6E, 0x8F, 0x7F, 0x1F, 0x68, 0x26, 0x31, 0x52, 0xEA, 0x29, 0x6A};
|
||||
|
||||
void fs_derive_bis_keys(key_storage_t *keys, u8 out_bis_keys[4][32], u32 generation);
|
||||
void fs_derive_header_key(key_storage_t *keys, void *out_key);
|
||||
void fs_derive_key_area_key(key_storage_t *keys, void *out_key, u32 source_type, u32 generation);
|
||||
void fs_derive_save_mac_key(key_storage_t *keys, void *out_key);
|
||||
|
||||
#endif
|
130
source/keys/gmac.c
Normal file
130
source/keys/gmac.c
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
* Copyright (c) 2019-2022 shchmue
|
||||
*
|
||||
* 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 "gmac.h"
|
||||
|
||||
#include <sec/se.h>
|
||||
#include <sec/se_t210.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Shifts right a little endian 128-bit value. */
|
||||
static void _shr_128(uint64_t *val) {
|
||||
val[0] >>= 1;
|
||||
val[0] |= (val[1] & 1) << 63;
|
||||
val[1] >>= 1;
|
||||
}
|
||||
|
||||
/* Shifts left a little endian 128-bit value. */
|
||||
static void _shl_128(uint64_t *val) {
|
||||
val[1] <<= 1;
|
||||
val[1] |= (val[0] & (1ull << 63)) >> 63;
|
||||
val[0] <<= 1;
|
||||
}
|
||||
|
||||
/* Multiplies two 128-bit numbers X,Y in the GF(128) Galois Field. */
|
||||
static void _gf128_mul(uint8_t *dst, const uint8_t *x, const uint8_t *y) {
|
||||
uint8_t x_work[0x10];
|
||||
uint8_t y_work[0x10];
|
||||
uint8_t dst_work[0x10];
|
||||
|
||||
uint64_t *p_x = (uint64_t *)(&x_work[0]);
|
||||
uint64_t *p_y = (uint64_t *)(&y_work[0]);
|
||||
uint64_t *p_dst = (uint64_t *)(&dst_work[0]);
|
||||
|
||||
/* Initialize buffers. */
|
||||
for (unsigned int i = 0; i < 0x10; i++) {
|
||||
x_work[i] = x[0xF-i];
|
||||
y_work[i] = y[0xF-i];
|
||||
dst_work[i] = 0;
|
||||
}
|
||||
|
||||
/* Perform operation for each bit in y. */
|
||||
for (unsigned int round = 0; round < 0x80; round++) {
|
||||
p_dst[0] ^= p_x[0] * ((y_work[0xF] & 0x80) >> 7);
|
||||
p_dst[1] ^= p_x[1] * ((y_work[0xF] & 0x80) >> 7);
|
||||
_shl_128(p_y);
|
||||
uint8_t xval = 0xE1 * (x_work[0] & 1);
|
||||
_shr_128(p_x);
|
||||
x_work[0xF] ^= xval;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < 0x10; i++) {
|
||||
dst[i] = dst_work[0xF-i];
|
||||
}
|
||||
}
|
||||
|
||||
static void _ghash(u32 ks, void *dst, const void *src, u32 src_size, const void *j_block, bool encrypt) {
|
||||
uint8_t x[0x10] = {0};
|
||||
uint8_t h[0x10];
|
||||
|
||||
uint64_t *p_x = (uint64_t *)(&x[0]);
|
||||
uint64_t *p_data = (uint64_t *)src;
|
||||
|
||||
/* H = aes_ecb_encrypt(zeroes) */
|
||||
se_aes_crypt_block_ecb(ks, ENCRYPT, h, x);
|
||||
|
||||
u64 total_size = src_size;
|
||||
|
||||
while (src_size >= 0x10) {
|
||||
/* X = (X ^ current_block) * H */
|
||||
p_x[0] ^= p_data[0];
|
||||
p_x[1] ^= p_data[1];
|
||||
_gf128_mul(x, x, h);
|
||||
|
||||
/* Increment p_data by 0x10 bytes. */
|
||||
p_data += 2;
|
||||
src_size -= 0x10;
|
||||
}
|
||||
|
||||
/* Nintendo's code *discards all data in the last block* if unaligned. */
|
||||
/* And treats that block as though it were all-zero. */
|
||||
/* This is a bug, they just forget to XOR with the copy of the last block they save. */
|
||||
if (src_size & 0xF) {
|
||||
_gf128_mul(x, x, h);
|
||||
}
|
||||
|
||||
uint64_t xor_size = total_size << 3;
|
||||
xor_size = __builtin_bswap64(xor_size);
|
||||
|
||||
/* Due to a Nintendo bug, the wrong QWORD gets XOR'd in the "final output block" case. */
|
||||
if (encrypt) {
|
||||
p_x[0] ^= xor_size;
|
||||
} else {
|
||||
p_x[1] ^= xor_size;
|
||||
}
|
||||
|
||||
_gf128_mul(x, x, h);
|
||||
|
||||
/* If final output block, XOR with encrypted J block. */
|
||||
if (encrypt) {
|
||||
se_aes_crypt_block_ecb(ks, ENCRYPT, h, j_block);
|
||||
for (unsigned int i = 0; i < 0x10; i++) {
|
||||
x[i] ^= h[i];
|
||||
}
|
||||
}
|
||||
/* Copy output. */
|
||||
memcpy(dst, x, 0x10);
|
||||
}
|
||||
|
||||
void calc_gmac(u32 ks, void *out_gmac, const void *data, u32 size, const void *key, const void *iv) {
|
||||
u32 j_block[4] = {0};
|
||||
se_aes_key_set(ks, key, 0x10);
|
||||
_ghash(ks, j_block, iv, 0x10, NULL, false);
|
||||
_ghash(ks, out_gmac, data, size, j_block, true);
|
||||
}
|
24
source/keys/gmac.h
Normal file
24
source/keys/gmac.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2022 shchmue
|
||||
*
|
||||
* 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 _GMAC_H_
|
||||
#define _GMAC_H_
|
||||
|
||||
#include <utils/types.h>
|
||||
|
||||
void calc_gmac(u32 ks, void *out_gmac, const void *data, u32 size, const void *key, const void *iv);
|
||||
|
||||
#endif
|
132
source/keys/key_sources.inl
Normal file
132
source/keys/key_sources.inl
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2022 shchmue
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
static const u8 master_kek_sources[KB_FIRMWARE_VERSION_MAX - KB_FIRMWARE_VERSION_620 + 1][0x10] __attribute__((aligned(4))) = {
|
||||
{0x37, 0x4B, 0x77, 0x29, 0x59, 0xB4, 0x04, 0x30, 0x81, 0xF6, 0xE5, 0x8C, 0x6D, 0x36, 0x17, 0x9A}, //6.2.0
|
||||
{0x9A, 0x3E, 0xA9, 0xAB, 0xFD, 0x56, 0x46, 0x1C, 0x9B, 0xF6, 0x48, 0x7F, 0x5C, 0xFA, 0x09, 0x5C}, //7.0.0
|
||||
{0xDE, 0xDC, 0xE3, 0x39, 0x30, 0x88, 0x16, 0xF8, 0xAE, 0x97, 0xAD, 0xEC, 0x64, 0x2D, 0x41, 0x41}, //8.1.0
|
||||
{0x1A, 0xEC, 0x11, 0x82, 0x2B, 0x32, 0x38, 0x7A, 0x2B, 0xED, 0xBA, 0x01, 0x47, 0x7E, 0x3B, 0x67}, //9.0.0
|
||||
{0x30, 0x3F, 0x02, 0x7E, 0xD8, 0x38, 0xEC, 0xD7, 0x93, 0x25, 0x34, 0xB5, 0x30, 0xEB, 0xCA, 0x7A}, //9.1.0
|
||||
{0x84, 0x67, 0xB6, 0x7F, 0x13, 0x11, 0xAE, 0xE6, 0x58, 0x9B, 0x19, 0xAF, 0x13, 0x6C, 0x80, 0x7A}, //12.1.0
|
||||
{0x68, 0x3B, 0xCA, 0x54, 0xB8, 0x6F, 0x92, 0x48, 0xC3, 0x05, 0x76, 0x87, 0x88, 0x70, 0x79, 0x23}, //13.0.0
|
||||
{0xF0, 0x13, 0x37, 0x9A, 0xD5, 0x63, 0x51, 0xC3, 0xB4, 0x96, 0x35, 0xBC, 0x9C, 0xE8, 0x76, 0x81}, //14.0.0
|
||||
{0x6E, 0x77, 0x86, 0xAC, 0x83, 0x0A, 0x8D, 0x3E, 0x7D, 0xB7, 0x66, 0xA0, 0x22, 0xB7, 0x6E, 0x67}, //15.0.0
|
||||
{0x99, 0x22, 0x09, 0x57, 0xA7, 0xF9, 0x5E, 0x94, 0xFE, 0x78, 0x7F, 0x41, 0xD6, 0xE7, 0x56, 0xE6}, //16.0.0
|
||||
}; //!TODO: Update on mkey changes.
|
||||
|
||||
static const u8 master_key_vectors[KB_FIRMWARE_VERSION_MAX + 1][0x10] __attribute__((aligned(4))) = {
|
||||
{0x0C, 0xF0, 0x59, 0xAC, 0x85, 0xF6, 0x26, 0x65, 0xE1, 0xE9, 0x19, 0x55, 0xE6, 0xF2, 0x67, 0x3D}, /* Zeroes encrypted with Master Key 00. */
|
||||
{0x29, 0x4C, 0x04, 0xC8, 0xEB, 0x10, 0xED, 0x9D, 0x51, 0x64, 0x97, 0xFB, 0xF3, 0x4D, 0x50, 0xDD}, /* Master key 00 encrypted with Master key 01. */
|
||||
{0xDE, 0xCF, 0xEB, 0xEB, 0x10, 0xAE, 0x74, 0xD8, 0xAD, 0x7C, 0xF4, 0x9E, 0x62, 0xE0, 0xE8, 0x72}, /* Master key 01 encrypted with Master key 02. */
|
||||
{0x0A, 0x0D, 0xDF, 0x34, 0x22, 0x06, 0x6C, 0xA4, 0xE6, 0xB1, 0xEC, 0x71, 0x85, 0xCA, 0x4E, 0x07}, /* Master key 02 encrypted with Master key 03. */
|
||||
{0x6E, 0x7D, 0x2D, 0xC3, 0x0F, 0x59, 0xC8, 0xFA, 0x87, 0xA8, 0x2E, 0xD5, 0x89, 0x5E, 0xF3, 0xE9}, /* Master key 03 encrypted with Master key 04. */
|
||||
{0xEB, 0xF5, 0x6F, 0x83, 0x61, 0x9E, 0xF8, 0xFA, 0xE0, 0x87, 0xD7, 0xA1, 0x4E, 0x25, 0x36, 0xEE}, /* Master key 04 encrypted with Master key 05. */
|
||||
{0x1E, 0x1E, 0x22, 0xC0, 0x5A, 0x33, 0x3C, 0xB9, 0x0B, 0xA9, 0x03, 0x04, 0xBA, 0xDB, 0x07, 0x57}, /* Master key 05 encrypted with Master key 06. */
|
||||
{0xA4, 0xD4, 0x52, 0x6F, 0xD1, 0xE4, 0x36, 0xAA, 0x9F, 0xCB, 0x61, 0x27, 0x1C, 0x67, 0x65, 0x1F}, /* Master key 06 encrypted with Master key 07. */
|
||||
{0xEA, 0x60, 0xB3, 0xEA, 0xCE, 0x8F, 0x24, 0x46, 0x7D, 0x33, 0x9C, 0xD1, 0xBC, 0x24, 0x98, 0x29}, /* Master key 07 encrypted with Master key 08. */
|
||||
{0x4D, 0xD9, 0x98, 0x42, 0x45, 0x0D, 0xB1, 0x3C, 0x52, 0x0C, 0x9A, 0x44, 0xBB, 0xAD, 0xAF, 0x80}, /* Master key 08 encrypted with Master key 09. */
|
||||
{0xB8, 0x96, 0x9E, 0x4A, 0x00, 0x0D, 0xD6, 0x28, 0xB3, 0xD1, 0xDB, 0x68, 0x5F, 0xFB, 0xE1, 0x2A}, /* Master key 09 encrypted with Master key 0A. */
|
||||
{0xC1, 0x8D, 0x16, 0xBB, 0x2A, 0xE4, 0x1D, 0xD4, 0xC2, 0xC1, 0xB6, 0x40, 0x94, 0x35, 0x63, 0x98}, /* Master key 0A encrypted with Master key 0B. */
|
||||
{0xA3, 0x24, 0x65, 0x75, 0xEA, 0xCC, 0x6E, 0x8D, 0xFB, 0x5A, 0x16, 0x50, 0x74, 0xD2, 0x15, 0x06}, /* Master key 0B encrypted with Master key 0C. */
|
||||
{0x83, 0x67, 0xAF, 0x01, 0xCF, 0x93, 0xA1, 0xAB, 0x80, 0x45, 0xF7, 0x3F, 0x72, 0xFD, 0x3B, 0x38}, /* Master key 0C encrypted with Master key 0D. */
|
||||
{0xB1, 0x81, 0xA6, 0x0D, 0x72, 0xC7, 0xEE, 0x15, 0x21, 0xF3, 0xC0, 0xB5, 0x6B, 0x61, 0x6D, 0xE7}, /* Master key 0D encrypted with Master key 0E. */
|
||||
{0xAF, 0x11, 0x4C, 0x67, 0x17, 0x7A, 0x52, 0x43, 0xF7, 0x70, 0x2F, 0xC7, 0xEF, 0x81, 0x72, 0x16}, /* Master key 0E encrypted with Master key 0F. */
|
||||
}; //!TODO: Update on mkey changes.
|
||||
|
||||
static const u8 master_key_vectors_dev[KB_FIRMWARE_VERSION_MAX + 1][0x10] __attribute__((aligned(4))) = {
|
||||
{0x46, 0x22, 0xB4, 0x51, 0x9A, 0x7E, 0xA7, 0x7F, 0x62, 0xA1, 0x1F, 0x8F, 0xC5, 0x3A, 0xDB, 0xFE}, /* Zeroes encrypted with Master Key 00. */
|
||||
{0x39, 0x33, 0xF9, 0x31, 0xBA, 0xE4, 0xA7, 0x21, 0x2C, 0xDD, 0xB7, 0xD8, 0xB4, 0x4E, 0x37, 0x23}, /* Master key 00 encrypted with Master key 01. */
|
||||
{0x97, 0x29, 0xB0, 0x32, 0x43, 0x14, 0x8C, 0xA6, 0x85, 0xE9, 0x5A, 0x94, 0x99, 0x39, 0xAC, 0x5D}, /* Master key 01 encrypted with Master key 02. */
|
||||
{0x2C, 0xCA, 0x9C, 0x31, 0x1E, 0x07, 0xB0, 0x02, 0x97, 0x0A, 0xD8, 0x03, 0xA2, 0x76, 0x3F, 0xA3}, /* Master key 02 encrypted with Master key 03. */
|
||||
{0x9B, 0x84, 0x76, 0x14, 0x72, 0x94, 0x52, 0xCB, 0x54, 0x92, 0x9B, 0xC4, 0x8C, 0x5B, 0x0F, 0xBA}, /* Master key 03 encrypted with Master key 04. */
|
||||
{0x78, 0xD5, 0xF1, 0x20, 0x3D, 0x16, 0xE9, 0x30, 0x32, 0x27, 0x34, 0x6F, 0xCF, 0xE0, 0x27, 0xDC}, /* Master key 04 encrypted with Master key 05. */
|
||||
{0x6F, 0xD2, 0x84, 0x1D, 0x05, 0xEC, 0x40, 0x94, 0x5F, 0x18, 0xB3, 0x81, 0x09, 0x98, 0x8D, 0x4E}, /* Master key 05 encrypted with Master key 06. */
|
||||
{0x37, 0xAF, 0xAB, 0x35, 0x79, 0x09, 0xD9, 0x48, 0x29, 0xD2, 0xDB, 0xA5, 0xA5, 0xF5, 0x30, 0x19}, /* Master key 06 encrypted with Master key 07. */
|
||||
{0xEC, 0xE1, 0x46, 0x89, 0x37, 0xFD, 0xD2, 0x15, 0x8C, 0x3F, 0x24, 0x82, 0xEF, 0x49, 0x68, 0x04}, /* Master key 07 encrypted with Master key 08. */
|
||||
{0x43, 0x3D, 0xC5, 0x3B, 0xEF, 0x91, 0x02, 0x21, 0x61, 0x54, 0x63, 0x8A, 0x35, 0xE7, 0xCA, 0xEE}, /* Master key 08 encrypted with Master key 09. */
|
||||
{0x6C, 0x2E, 0xCD, 0xB3, 0x34, 0x61, 0x77, 0xF5, 0xF9, 0xB1, 0xDD, 0x61, 0x98, 0x19, 0x3E, 0xD4}, /* Master key 09 encrypted with Master key 0A. */
|
||||
{0x21, 0x88, 0x6B, 0x10, 0x9E, 0x83, 0xD6, 0x52, 0xAB, 0x08, 0xDB, 0x6D, 0x39, 0xFF, 0x1C, 0x9C}, /* Master key 0A encrypted with Master key 0B. */
|
||||
{0x8A, 0xCE, 0xC4, 0x7F, 0xBE, 0x08, 0x61, 0x88, 0xD3, 0x73, 0x64, 0x51, 0xE2, 0xB6, 0x53, 0x15}, /* Master key 0B encrypted with Master key 0C. */
|
||||
{0x08, 0xE0, 0xF4, 0xBE, 0xAA, 0x6E, 0x5A, 0xC3, 0xA6, 0xBC, 0xFE, 0xB9, 0xE2, 0xA3, 0x24, 0x12}, /* Master key 0C encrypted with Master key 0D. */
|
||||
{0xD6, 0x80, 0x98, 0xC0, 0xFA, 0xC7, 0x13, 0xCB, 0x93, 0xD2, 0x0B, 0x82, 0x4C, 0xA1, 0x7B, 0x8D}, /* Master key 0D encrypted with Master key 0E. */
|
||||
{0x78, 0x66, 0x19, 0xBD, 0x86, 0xE7, 0xC1, 0x09, 0x9B, 0x6F, 0x92, 0xB2, 0x58, 0x7D, 0xCF, 0x26}, /* Master key 0E encrypted with Master key 0F. */
|
||||
}; //!TODO: Update on mkey changes.
|
||||
|
||||
static const u8 mariko_key_vectors[][0x10] __attribute__((aligned(4))) = {
|
||||
{0x20, 0x9E, 0x97, 0xAE, 0xAF, 0x7E, 0x6A, 0xF6, 0x9E, 0xF5, 0xA7, 0x17, 0x2F, 0xF4, 0x49, 0xA6}, /* Zeroes encrypted with AES Class Key 00. */
|
||||
{0x83, 0x1C, 0xC7, 0x7F, 0xB8, 0xB2, 0x66, 0x16, 0xFC, 0x6B, 0x81, 0xBB, 0xF6, 0x05, 0x07, 0x49}, /* Zeroes encrypted with AES Class Key 01. */
|
||||
{0x61, 0x19, 0xBA, 0x39, 0x6D, 0xFA, 0xF4, 0x63, 0x27, 0x8E, 0x9E, 0xB1, 0xEA, 0xD4, 0x65, 0xCC}, /* Zeroes encrypted with AES Class Key 02. */
|
||||
{0x6C, 0xDB, 0x10, 0xD4, 0x14, 0x3A, 0xBD, 0x22, 0xC9, 0xCC, 0xEF, 0xE4, 0xA0, 0x94, 0x85, 0x87}, /* Zeroes encrypted with AES Class Key 03. */
|
||||
{0xD3, 0x40, 0xC7, 0x86, 0xDC, 0x77, 0x50, 0x7C, 0x01, 0x56, 0x11, 0xDE, 0x18, 0xDF, 0x30, 0xCA}, /* Zeroes encrypted with AES Class Key 04. */
|
||||
{0xC4, 0x8B, 0xD7, 0x5A, 0x22, 0x6C, 0xF7, 0x85, 0xE4, 0xC0, 0x68, 0xFC, 0xB4, 0xD8, 0x76, 0x6C}, /* Zeroes encrypted with AES Class Key 05. */
|
||||
{0x83, 0x86, 0xEF, 0xE6, 0x6B, 0x38, 0x24, 0xD3, 0xC9, 0xB0, 0xE7, 0x03, 0x59, 0xC8, 0x54, 0xB9}, /* Zeroes encrypted with AES Class Key 06. */
|
||||
{0xDA, 0xC0, 0xD3, 0x27, 0x1D, 0x35, 0xAB, 0x4B, 0x01, 0x63, 0x90, 0xED, 0x1B, 0x5D, 0xA7, 0x6C}, /* Zeroes encrypted with AES Class Key 07. */
|
||||
{0x96, 0x75, 0x0E, 0x4F, 0xF5, 0x1A, 0xAF, 0xD4, 0x30, 0x73, 0x8C, 0x61, 0x03, 0xE5, 0xF7, 0x80}, /* Zeroes encrypted with AES Class Key 08. */
|
||||
{0x74, 0xF2, 0x1D, 0xA1, 0x4C, 0x48, 0x91, 0xE6, 0xE0, 0xD5, 0x19, 0x42, 0x2A, 0x2B, 0x5D, 0xF8}, /* Zeroes encrypted with AES Class Key 09. */
|
||||
{0x7D, 0xA6, 0xFE, 0xDA, 0xF9, 0xEF, 0x83, 0xD8, 0x29, 0x40, 0x24, 0x6D, 0x70, 0x8D, 0x99, 0x93}, /* Zeroes encrypted with AES Class Key 0A. */
|
||||
{0xF6, 0x71, 0xAD, 0xC3, 0xCD, 0xD4, 0x2F, 0x37, 0xAB, 0x50, 0x1C, 0x9B, 0xAF, 0x3B, 0xE6, 0xC3}, /* Zeroes encrypted with AES Class Key 0B. */
|
||||
{0x01, 0x96, 0x59, 0x07, 0x62, 0xF4, 0xF4, 0x2D, 0x13, 0x60, 0xD8, 0x03, 0xFB, 0xF0, 0xAE, 0xC8}, /* Zeroes encrypted with Mariko KEK. */
|
||||
{0x95, 0x48, 0xC1, 0x59, 0x0F, 0x84, 0x19, 0xC4, 0xAB, 0x69, 0x05, 0x88, 0x01, 0x31, 0x52, 0x59}, /* Zeroes encrypted with Mariko BEK. */
|
||||
};
|
||||
|
||||
static const u8 package2_key_source[0x10] __attribute__((aligned(4))) = {
|
||||
0xFB, 0x8B, 0x6A, 0x9C, 0x79, 0x00, 0xC8, 0x49, 0xEF, 0xD2, 0x4D, 0x85, 0x4D, 0x30, 0xA0, 0xC7};
|
||||
static const u8 titlekek_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x1E, 0xDC, 0x7B, 0x3B, 0x60, 0xE6, 0xB4, 0xD8, 0x78, 0xB8, 0x17, 0x15, 0x98, 0x5E, 0x62, 0x9B};
|
||||
|
||||
static const u8 keyblob_key_sources[][0x10] __attribute__((aligned(4))) = {
|
||||
{0xDF, 0x20, 0x6F, 0x59, 0x44, 0x54, 0xEF, 0xDC, 0x70, 0x74, 0x48, 0x3B, 0x0D, 0xED, 0x9F, 0xD3}, //1.0.0
|
||||
{0x0C, 0x25, 0x61, 0x5D, 0x68, 0x4C, 0xEB, 0x42, 0x1C, 0x23, 0x79, 0xEA, 0x82, 0x25, 0x12, 0xAC}, //3.0.0
|
||||
{0x33, 0x76, 0x85, 0xEE, 0x88, 0x4A, 0xAE, 0x0A, 0xC2, 0x8A, 0xFD, 0x7D, 0x63, 0xC0, 0x43, 0x3B}, //3.0.1
|
||||
{0x2D, 0x1F, 0x48, 0x80, 0xED, 0xEC, 0xED, 0x3E, 0x3C, 0xF2, 0x48, 0xB5, 0x65, 0x7D, 0xF7, 0xBE}, //4.0.0
|
||||
{0xBB, 0x5A, 0x01, 0xF9, 0x88, 0xAF, 0xF5, 0xFC, 0x6C, 0xFF, 0x07, 0x9E, 0x13, 0x3C, 0x39, 0x80}, //5.0.0
|
||||
{0xD8, 0xCC, 0xE1, 0x26, 0x6A, 0x35, 0x3F, 0xCC, 0x20, 0xF3, 0x2D, 0x3B, 0x51, 0x7D, 0xE9, 0xC0} //6.0.0
|
||||
};
|
||||
static const u8 keyblob_mac_key_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x59, 0xC7, 0xFB, 0x6F, 0xBE, 0x9B, 0xBE, 0x87, 0x65, 0x6B, 0x15, 0xC0, 0x53, 0x73, 0x36, 0xA5};
|
||||
|
||||
static const u8 master_key_source[0x10] __attribute__((aligned(4))) = {
|
||||
0xD8, 0xA2, 0x41, 0x0A, 0xC6, 0xC5, 0x90, 0x01, 0xC6, 0x1D, 0x6A, 0x26, 0x7C, 0x51, 0x3F, 0x3C};
|
||||
static const u8 per_console_key_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x4F, 0x02, 0x5F, 0x0E, 0xB6, 0x6D, 0x11, 0x0E, 0xDC, 0x32, 0x7D, 0x41, 0x86, 0xC2, 0xF4, 0x78};
|
||||
static const u8 device_master_key_source_kek_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x0C, 0x91, 0x09, 0xDB, 0x93, 0x93, 0x07, 0x81, 0x07, 0x3C, 0xC4, 0x16, 0x22, 0x7C, 0x6C, 0x28};
|
||||
static const u8 mariko_master_kek_sources[KB_FIRMWARE_VERSION_MAX - KB_FIRMWARE_VERSION_600 + 1][0x10] __attribute__((aligned(4))) = {
|
||||
{0x77, 0x60, 0x5A, 0xD2, 0xEE, 0x6E, 0xF8, 0x3C, 0x3F, 0x72, 0xE2, 0x59, 0x9D, 0xAC, 0x5E, 0x56}, // 6.0.0.
|
||||
{0x1E, 0x80, 0xB8, 0x17, 0x3E, 0xC0, 0x60, 0xAA, 0x11, 0xBE, 0x1A, 0x4A, 0xA6, 0x6F, 0xE4, 0xAE}, // 6.2.0.
|
||||
{0x94, 0x08, 0x67, 0xBD, 0x0A, 0x00, 0x38, 0x84, 0x11, 0xD3, 0x1A, 0xDB, 0xDD, 0x8D, 0xF1, 0x8A}, // 7.0.0.
|
||||
{0x5C, 0x24, 0xE3, 0xB8, 0xB4, 0xF7, 0x00, 0xC2, 0x3C, 0xFD, 0x0A, 0xCE, 0x13, 0xC3, 0xDC, 0x23}, // 8.1.0.
|
||||
{0x86, 0x69, 0xF0, 0x09, 0x87, 0xC8, 0x05, 0xAE, 0xB5, 0x7B, 0x48, 0x74, 0xDE, 0x62, 0xA6, 0x13}, // 9.0.0.
|
||||
{0x0E, 0x44, 0x0C, 0xED, 0xB4, 0x36, 0xC0, 0x3F, 0xAA, 0x1D, 0xAE, 0xBF, 0x62, 0xB1, 0x09, 0x82}, // 9.1.0.
|
||||
{0xE5, 0x41, 0xAC, 0xEC, 0xD1, 0xA7, 0xD1, 0xAB, 0xED, 0x03, 0x77, 0xF1, 0x27, 0xCA, 0xF8, 0xF1}, // 12.1.0.
|
||||
{0x52, 0x71, 0x9B, 0xDF, 0xA7, 0x8B, 0x61, 0xD8, 0xD5, 0x85, 0x11, 0xE4, 0x8E, 0x4F, 0x74, 0xC6}, // 13.0.0.
|
||||
{0xD2, 0x68, 0xC6, 0x53, 0x9D, 0x94, 0xF9, 0xA8, 0xA5, 0xA8, 0xA7, 0xC8, 0x8F, 0x53, 0x4B, 0x7A}, // 14.0.0.
|
||||
{0xEC, 0x61, 0xBC, 0x82, 0x1E, 0x0F, 0x5A, 0xC3, 0x2B, 0x64, 0x3F, 0x9D, 0xD6, 0x19, 0x22, 0x2D}, // 15.0.0.
|
||||
{0xA5, 0xEC, 0x16, 0x39, 0x1A, 0x30, 0x16, 0x08, 0x2E, 0xCF, 0x09, 0x6F, 0x5E, 0x7C, 0xEE, 0xA9}, // 16.0.0.
|
||||
}; //!TODO: Update on mkey changes.
|
||||
static const u8 mariko_master_kek_sources_dev[KB_FIRMWARE_VERSION_MAX - KB_FIRMWARE_VERSION_600 + 1][0x10] __attribute__((aligned(4))) = {
|
||||
{0x32, 0xC0, 0x97, 0x6B, 0x63, 0x6D, 0x44, 0x64, 0xF2, 0x3A, 0xA5, 0xC0, 0xDE, 0x46, 0xCC, 0xE9}, // 6.0.0.
|
||||
{0xCC, 0x97, 0x4C, 0x46, 0x2A, 0x0C, 0xB0, 0xA6, 0xC9, 0xC0, 0xB7, 0xBE, 0x30, 0x2E, 0xC3, 0x68}, // 6.2.0.
|
||||
{0x86, 0xBD, 0x1D, 0x76, 0x50, 0xDF, 0x6D, 0xFA, 0x2C, 0x7D, 0x33, 0x22, 0xAB, 0xF1, 0x82, 0x18}, // 7.0.0.
|
||||
{0xA3, 0xB1, 0xE0, 0xA9, 0x58, 0xA2, 0x26, 0x7F, 0x40, 0xBF, 0x5B, 0xBB, 0x87, 0x33, 0x0B, 0x66}, // 8.1.0.
|
||||
{0x82, 0x72, 0x91, 0x65, 0x40, 0x3B, 0x9D, 0x66, 0x60, 0xD0, 0x1B, 0x3D, 0x4D, 0xA5, 0x70, 0xE1}, // 9.0.0.
|
||||
{0xF9, 0x37, 0xCF, 0x9A, 0xBD, 0x86, 0xBB, 0xA9, 0x9C, 0x9E, 0x03, 0xC4, 0xFC, 0xBC, 0x3B, 0xCE}, // 9.1.0.
|
||||
{0x75, 0x2D, 0x2E, 0xF3, 0x2F, 0x3F, 0xFE, 0x65, 0xF4, 0xA9, 0x83, 0xB4, 0xED, 0x42, 0x63, 0xBA}, // 12.1.0.
|
||||
{0x4D, 0x5A, 0xB2, 0xC9, 0xE9, 0xE4, 0x4E, 0xA4, 0xD3, 0xBF, 0x94, 0x12, 0x36, 0x30, 0xD0, 0x7F}, // 13.0.0.
|
||||
{0xEC, 0x5E, 0xB5, 0x11, 0xD5, 0x43, 0x1E, 0x6A, 0x4E, 0x54, 0x6F, 0xD4, 0xD3, 0x22, 0xCE, 0x87}, // 14.0.0.
|
||||
{0x18, 0xA5, 0x6F, 0xEF, 0x72, 0x11, 0x62, 0xC5, 0x1A, 0x14, 0xF1, 0x8C, 0x21, 0x83, 0x27, 0xB7}, // 15.0.0.
|
||||
{0x3A, 0x9C, 0xF0, 0x39, 0x70, 0x23, 0xF6, 0xAF, 0x71, 0x44, 0x60, 0xF4, 0x6D, 0xED, 0xA1, 0xD6}, // 16.0.0.
|
||||
}; //!TODO: Update on mkey changes.
|
850
source/keys/keys.c
Normal file
850
source/keys/keys.c
Normal file
|
@ -0,0 +1,850 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2022 shchmue
|
||||
*
|
||||
* 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 "keys.h"
|
||||
|
||||
#include "es_crypto.h"
|
||||
#include "fs_crypto.h"
|
||||
#include "nfc_crypto.h"
|
||||
#include "ssl_crypto.h"
|
||||
|
||||
#include "../config.h"
|
||||
#include <display/di.h>
|
||||
#include "../frontend/gui.h"
|
||||
#include <gfx_utils.h>
|
||||
#include "../gfx/tui.h"
|
||||
#include "../hos/hos.h"
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include <libs/nx_savedata/header.h>
|
||||
#include <libs/nx_savedata/save.h>
|
||||
#include <mem/heap.h>
|
||||
#include <mem/minerva.h>
|
||||
#include <mem/sdram.h>
|
||||
#include <sec/se.h>
|
||||
#include <sec/se_t210.h>
|
||||
#include <soc/fuse.h>
|
||||
#include <soc/t210.h>
|
||||
#include "../storage/emummc.h"
|
||||
#include "../storage/nx_emmc.h"
|
||||
#include "../storage/nx_emmc_bis.h"
|
||||
#include <storage/nx_sd.h>
|
||||
#include <storage/sdmmc.h>
|
||||
#include <utils/btn.h>
|
||||
#include <utils/list.h>
|
||||
#include <utils/sprintf.h>
|
||||
#include <utils/util.h>
|
||||
|
||||
#include "key_sources.inl"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
extern hekate_config h_cfg;
|
||||
|
||||
static u32 _key_count = 0, _titlekey_count = 0;
|
||||
static u32 start_time, end_time;
|
||||
u32 color_idx = 0;
|
||||
|
||||
static void _save_key(const char *name, const void *data, u32 len, char *outbuf) {
|
||||
if (!key_exists(data))
|
||||
return;
|
||||
u32 pos = strlen(outbuf);
|
||||
pos += s_printf(&outbuf[pos], "%s = ", name);
|
||||
for (u32 i = 0; i < len; i++)
|
||||
pos += s_printf(&outbuf[pos], "%02x", *(u8*)(data + i));
|
||||
s_printf(&outbuf[pos], "\n");
|
||||
_key_count++;
|
||||
}
|
||||
|
||||
static void _save_key_family(const char *name, const void *data, u32 start_key, u32 num_keys, u32 len, char *outbuf) {
|
||||
char *temp_name = calloc(1, 0x40);
|
||||
for (u32 i = 0; i < num_keys; i++) {
|
||||
s_printf(temp_name, "%s_%02x", name, i + start_key);
|
||||
_save_key(temp_name, data + i * len, len, outbuf);
|
||||
}
|
||||
free(temp_name);
|
||||
}
|
||||
|
||||
static void _derive_master_keys_mariko(key_storage_t *keys, bool is_dev) {
|
||||
minerva_periodic_training();
|
||||
// Relies on the SBK being properly set in slot 14
|
||||
se_aes_crypt_block_ecb(KS_SECURE_BOOT, DECRYPT, keys->device_key_4x, device_master_key_source_kek_source);
|
||||
// Derive all master keys based on Mariko KEK
|
||||
for (u32 i = KB_FIRMWARE_VERSION_600; i < ARRAY_SIZE(mariko_master_kek_sources) + KB_FIRMWARE_VERSION_600; i++) {
|
||||
// Relies on the Mariko KEK being properly set in slot 12
|
||||
u32 kek_source_index = i - KB_FIRMWARE_VERSION_600;
|
||||
const void *kek_source = is_dev ? &mariko_master_kek_sources_dev[kek_source_index] : &mariko_master_kek_sources[kek_source_index];
|
||||
se_aes_crypt_block_ecb(KS_MARIKO_KEK, DECRYPT, keys->master_kek[i], kek_source);
|
||||
load_aes_key(KS_AES_ECB, keys->master_key[i], keys->master_kek[i], master_key_source);
|
||||
}
|
||||
}
|
||||
|
||||
static void _derive_master_keys_from_latest_key(key_storage_t *keys, bool is_dev) {
|
||||
minerva_periodic_training();
|
||||
if (!h_cfg.t210b01) {
|
||||
u32 tsec_root_key_slot = is_dev ? KS_TSEC_ROOT_DEV : KS_TSEC_ROOT;
|
||||
// Derive all master keys based on current root key
|
||||
for (u32 i = KB_FIRMWARE_VERSION_810 - KB_FIRMWARE_VERSION_620; i < ARRAY_SIZE(master_kek_sources); i++) {
|
||||
u32 key_index = i + KB_FIRMWARE_VERSION_620;
|
||||
se_aes_crypt_block_ecb(tsec_root_key_slot, DECRYPT, keys->master_kek[key_index], master_kek_sources[i]);
|
||||
load_aes_key(KS_AES_ECB, keys->master_key[key_index], keys->master_kek[key_index], master_key_source);
|
||||
}
|
||||
}
|
||||
|
||||
minerva_periodic_training();
|
||||
|
||||
// Derive all lower master keys
|
||||
for (u32 i = KB_FIRMWARE_VERSION_MAX; i > 0; i--) {
|
||||
load_aes_key(KS_AES_ECB, keys->master_key[i - 1], keys->master_key[i], is_dev ? master_key_vectors_dev[i] : master_key_vectors[i]);
|
||||
}
|
||||
load_aes_key(KS_AES_ECB, keys->temp_key, keys->master_key[0], is_dev ? master_key_vectors_dev[0] : master_key_vectors[0]);
|
||||
|
||||
if (key_exists(keys->temp_key)) {
|
||||
EPRINTFARGS("Unable to derive master keys for %s.", is_dev ? "dev" : "prod");
|
||||
memset(keys->master_key, 0, sizeof(keys->master_key));
|
||||
}
|
||||
}
|
||||
|
||||
static void _derive_keyblob_keys(key_storage_t *keys) {
|
||||
minerva_periodic_training();
|
||||
|
||||
encrypted_keyblob_t *keyblob_buffer = (encrypted_keyblob_t *)calloc(KB_FIRMWARE_VERSION_600 + 1, sizeof(encrypted_keyblob_t));
|
||||
u32 keyblob_mac[SE_AES_CMAC_DIGEST_SIZE / 4] = {0};
|
||||
bool have_keyblobs = true;
|
||||
|
||||
if (FUSE(FUSE_PRIVATE_KEY0) != 0xFFFFFFFF) {
|
||||
keys->secure_boot_key[0] = FUSE(FUSE_PRIVATE_KEY0);
|
||||
keys->secure_boot_key[1] = FUSE(FUSE_PRIVATE_KEY1);
|
||||
keys->secure_boot_key[2] = FUSE(FUSE_PRIVATE_KEY2);
|
||||
keys->secure_boot_key[3] = FUSE(FUSE_PRIVATE_KEY3);
|
||||
}
|
||||
|
||||
if (!emmc_storage.initialized) {
|
||||
have_keyblobs = false;
|
||||
} else if (!emummc_storage_read(KEYBLOB_OFFSET / NX_EMMC_BLOCKSIZE, KB_FIRMWARE_VERSION_600 + 1, keyblob_buffer)) {
|
||||
EPRINTF("Unable to read keyblobs.");
|
||||
have_keyblobs = false;
|
||||
} else {
|
||||
have_keyblobs = true;
|
||||
}
|
||||
|
||||
encrypted_keyblob_t *current_keyblob = keyblob_buffer;
|
||||
for (u32 i = 0; i < ARRAY_SIZE(keyblob_key_sources); i++, current_keyblob++) {
|
||||
minerva_periodic_training();
|
||||
se_aes_crypt_block_ecb(KS_TSEC, DECRYPT, keys->keyblob_key[i], keyblob_key_sources[i]);
|
||||
se_aes_crypt_block_ecb(KS_SECURE_BOOT, DECRYPT, keys->keyblob_key[i], keys->keyblob_key[i]);
|
||||
load_aes_key(KS_AES_ECB, keys->keyblob_mac_key[i], keys->keyblob_key[i], keyblob_mac_key_source);
|
||||
if (i == 0) {
|
||||
se_aes_crypt_block_ecb(KS_AES_ECB, DECRYPT, keys->device_key, per_console_key_source);
|
||||
se_aes_crypt_block_ecb(KS_AES_ECB, DECRYPT, keys->device_key_4x, device_master_key_source_kek_source);
|
||||
}
|
||||
|
||||
if (!have_keyblobs) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Verify keyblob is not corrupt
|
||||
se_aes_key_set(KS_AES_CMAC, keys->keyblob_mac_key[i], sizeof(keys->keyblob_mac_key[i]));
|
||||
se_aes_cmac(KS_AES_CMAC, keyblob_mac, sizeof(keyblob_mac), current_keyblob->iv, sizeof(current_keyblob->iv) + sizeof(keyblob_t));
|
||||
if (memcmp(current_keyblob->cmac, keyblob_mac, sizeof(keyblob_mac)) != 0) {
|
||||
EPRINTFARGS("Keyblob %x corrupt.", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Decrypt keyblobs
|
||||
se_aes_key_set(KS_AES_CTR, keys->keyblob_key[i], sizeof(keys->keyblob_key[i]));
|
||||
se_aes_crypt_ctr(KS_AES_CTR, &keys->keyblob[i], sizeof(keyblob_t), ¤t_keyblob->key_data, sizeof(keyblob_t), current_keyblob->iv);
|
||||
|
||||
memcpy(keys->package1_key[i], keys->keyblob[i].package1_key, sizeof(keys->package1_key[i]));
|
||||
memcpy(keys->master_kek[i], keys->keyblob[i].master_kek, sizeof(keys->master_kek[i]));
|
||||
if (!key_exists(keys->master_key[i])) {
|
||||
load_aes_key(KS_AES_ECB, keys->master_key[i], keys->master_kek[i], master_key_source);
|
||||
}
|
||||
}
|
||||
free(keyblob_buffer);
|
||||
}
|
||||
|
||||
static void _derive_master_keys(key_storage_t *prod_keys, key_storage_t *dev_keys, bool is_dev) {
|
||||
key_storage_t *keys = is_dev ? dev_keys : prod_keys;
|
||||
|
||||
if (h_cfg.t210b01) {
|
||||
_derive_master_keys_mariko(keys, is_dev);
|
||||
_derive_master_keys_from_latest_key(keys, is_dev);
|
||||
} else {
|
||||
if (run_ams_keygen()) {
|
||||
EPRINTF("Failed to run keygen.");
|
||||
return;
|
||||
}
|
||||
|
||||
u8 *aes_keys = (u8 *)calloc(1, SZ_4K);
|
||||
se_get_aes_keys(aes_keys + SZ_2K, aes_keys, SE_KEY_128_SIZE);
|
||||
memcpy(&dev_keys->tsec_root_key, aes_keys + KS_TSEC_ROOT_DEV * SE_KEY_128_SIZE, SE_KEY_128_SIZE);
|
||||
memcpy(&dev_keys->tsec_key, aes_keys + KS_TSEC * SE_KEY_128_SIZE, SE_KEY_128_SIZE);
|
||||
memcpy(&prod_keys->tsec_key, aes_keys + KS_TSEC * SE_KEY_128_SIZE, SE_KEY_128_SIZE);
|
||||
memcpy(&prod_keys->tsec_root_key, aes_keys + KS_TSEC_ROOT * SE_KEY_128_SIZE, SE_KEY_128_SIZE);
|
||||
if (FUSE(FUSE_PRIVATE_KEY0) != 0xFFFFFFFF) {
|
||||
memcpy(&dev_keys->secure_boot_key, aes_keys + KS_SECURE_BOOT * SE_KEY_128_SIZE, SE_KEY_128_SIZE);
|
||||
memcpy(&prod_keys->secure_boot_key, aes_keys + KS_SECURE_BOOT * SE_KEY_128_SIZE, SE_KEY_128_SIZE);
|
||||
}
|
||||
free(aes_keys);
|
||||
|
||||
_derive_master_keys_from_latest_key(prod_keys, false);
|
||||
_derive_master_keys_from_latest_key(dev_keys, true);
|
||||
_derive_keyblob_keys(keys);
|
||||
}
|
||||
}
|
||||
|
||||
static void _derive_bis_keys(key_storage_t *keys) {
|
||||
minerva_periodic_training();
|
||||
u32 generation = fuse_read_odm_keygen_rev();
|
||||
fs_derive_bis_keys(keys, keys->bis_key, generation);
|
||||
}
|
||||
|
||||
static void _derive_misc_keys(key_storage_t *keys) {
|
||||
minerva_periodic_training();
|
||||
fs_derive_save_mac_key(keys, keys->save_mac_key);
|
||||
}
|
||||
|
||||
static void _derive_non_unique_keys(key_storage_t *keys, bool is_dev) {
|
||||
minerva_periodic_training();
|
||||
fs_derive_header_key(keys, keys->header_key);
|
||||
es_derive_rsa_kek_original(keys, keys->eticket_rsa_kek, is_dev);
|
||||
ssl_derive_rsa_kek_original(keys, keys->ssl_rsa_kek, is_dev);
|
||||
|
||||
for (u32 generation = 0; generation < ARRAY_SIZE(keys->master_key); generation++) {
|
||||
minerva_periodic_training();
|
||||
if (!key_exists(keys->master_key[generation]))
|
||||
continue;
|
||||
for (u32 source_type = 0; source_type < ARRAY_SIZE(key_area_key_sources); source_type++) {
|
||||
fs_derive_key_area_key(keys, keys->key_area_key[source_type][generation], source_type, generation);
|
||||
}
|
||||
load_aes_key(KS_AES_ECB, keys->package2_key[generation], keys->master_key[generation], package2_key_source);
|
||||
load_aes_key(KS_AES_ECB, keys->titlekek[generation], keys->master_key[generation], titlekek_source);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true when terminator is found
|
||||
static bool _count_ticket_records(u32 buf_size, titlekey_buffer_t *titlekey_buffer, u32 *tkey_count) {
|
||||
ticket_record_t *curr_ticket_record = (ticket_record_t *)titlekey_buffer->read_buffer;
|
||||
for (u32 i = 0; i < buf_size; i += sizeof(ticket_record_t), curr_ticket_record++) {
|
||||
if (curr_ticket_record->rights_id[0] == 0xFF)
|
||||
return true;
|
||||
(*tkey_count)++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool _get_titlekeys_from_save(u32 buf_size, const u8 *save_mac_key, titlekey_buffer_t *titlekey_buffer, eticket_rsa_keypair_t *rsa_keypair) {
|
||||
FIL fp;
|
||||
u64 br = buf_size;
|
||||
u64 offset = 0;
|
||||
u32 file_tkey_count = 0;
|
||||
u32 save_x = gfx_con.x, save_y = gfx_con.y;
|
||||
bool is_personalized = rsa_keypair != NULL;
|
||||
const char ticket_bin_path[32] = "/ticket.bin";
|
||||
const char ticket_list_bin_path[32] = "/ticket_list.bin";
|
||||
char titlekey_save_path[32] = "bis:/save/80000000000000E1";
|
||||
save_data_file_ctx_t ticket_file;
|
||||
|
||||
if (is_personalized) {
|
||||
titlekey_save_path[25] = '2';
|
||||
gfx_printf("\n%kPersonalized... ", colors[color_idx % 6]);
|
||||
} else {
|
||||
gfx_printf("\n%kCommon... ", colors[color_idx % 6]);
|
||||
}
|
||||
|
||||
if (f_open(&fp, titlekey_save_path, FA_READ | FA_OPEN_EXISTING)) {
|
||||
EPRINTF("Unable to open e1 save. Skipping.");
|
||||
return false;
|
||||
}
|
||||
|
||||
save_ctx_t *save_ctx = calloc(1, sizeof(save_ctx_t));
|
||||
save_init(save_ctx, &fp, save_mac_key, 0);
|
||||
|
||||
bool save_process_success = save_process(save_ctx);
|
||||
TPRINTF("\n Save process...");
|
||||
|
||||
if (!save_process_success) {
|
||||
EPRINTF("Failed to process es save.");
|
||||
f_close(&fp);
|
||||
save_free_contexts(save_ctx);
|
||||
free(save_ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!save_open_file(save_ctx, &ticket_file, ticket_list_bin_path, OPEN_MODE_READ)) {
|
||||
EPRINTF("Unable to locate ticket_list.bin in save.");
|
||||
f_close(&fp);
|
||||
save_free_contexts(save_ctx);
|
||||
free(save_ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read ticket list to get ticket count
|
||||
while (offset < ticket_file.size) {
|
||||
minerva_periodic_training();
|
||||
if (!save_data_file_read(&ticket_file, &br, offset, titlekey_buffer->read_buffer, buf_size) ||
|
||||
titlekey_buffer->read_buffer[0] == 0 ||
|
||||
br != buf_size ||
|
||||
_count_ticket_records(buf_size, titlekey_buffer, &file_tkey_count)
|
||||
) {
|
||||
break;
|
||||
}
|
||||
offset += br;
|
||||
}
|
||||
TPRINTF(" Count titlekeys...");
|
||||
|
||||
if (!save_open_file(save_ctx, &ticket_file, ticket_bin_path, OPEN_MODE_READ)) {
|
||||
EPRINTF("Unable to locate ticket.bin in save.");
|
||||
f_close(&fp);
|
||||
save_free_contexts(save_ctx);
|
||||
free(save_ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_personalized)
|
||||
se_rsa_key_set(0, rsa_keypair->modulus, sizeof(rsa_keypair->modulus), rsa_keypair->private_exponent, sizeof(rsa_keypair->private_exponent));
|
||||
|
||||
offset = 0;
|
||||
u32 pct = 0, last_pct = 0, remaining = file_tkey_count;
|
||||
while (offset < ticket_file.size && remaining) {
|
||||
if (!save_data_file_read(&ticket_file, &br, offset, titlekey_buffer->read_buffer, buf_size) || titlekey_buffer->read_buffer[0] == 0 || br != buf_size)
|
||||
break;
|
||||
offset += br;
|
||||
es_decode_tickets(buf_size, titlekey_buffer, remaining, file_tkey_count, &_titlekey_count, save_x, save_y, &pct, &last_pct, is_personalized);
|
||||
remaining -= MIN(buf_size / sizeof(ticket_t), remaining);
|
||||
}
|
||||
tui_pbar(save_x, save_y, 100, COLOR_GREEN, 0xFF155500);
|
||||
f_close(&fp);
|
||||
save_free_contexts(save_ctx);
|
||||
free(save_ctx);
|
||||
|
||||
gfx_con_setpos(0, save_y);
|
||||
|
||||
if (is_personalized) {
|
||||
TPRINTFARGS("\n%kPersonalized... ", colors[(color_idx++) % 6]);
|
||||
} else {
|
||||
TPRINTFARGS("\n%kCommon... ", colors[(color_idx++) % 6]);
|
||||
}
|
||||
|
||||
gfx_printf("\n\n\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _derive_sd_seed(key_storage_t *keys) {
|
||||
FIL fp;
|
||||
u32 read_bytes = 0;
|
||||
char *private_path = malloc(200);
|
||||
strcpy(private_path, "sd:/");
|
||||
|
||||
if (emu_cfg.nintendo_path && (emu_cfg.enabled || !h_cfg.emummc_force_disable)) {
|
||||
strcat(private_path, emu_cfg.nintendo_path);
|
||||
} else {
|
||||
strcat(private_path, "Nintendo");
|
||||
}
|
||||
strcat(private_path, "/Contents/private");
|
||||
FRESULT fr = f_open(&fp, private_path, FA_READ | FA_OPEN_EXISTING);
|
||||
free(private_path);
|
||||
if (fr) {
|
||||
EPRINTF("Unable to open SD seed vector. Skipping.");
|
||||
return false;
|
||||
}
|
||||
// Get sd seed verification vector
|
||||
if (f_read(&fp, keys->temp_key, SE_KEY_128_SIZE, &read_bytes) || read_bytes != SE_KEY_128_SIZE) {
|
||||
EPRINTF("Unable to read SD seed vector. Skipping.");
|
||||
f_close(&fp);
|
||||
return false;
|
||||
}
|
||||
f_close(&fp);
|
||||
|
||||
// This file is small enough that parsing the savedata properly is slower
|
||||
if (f_open(&fp, "bis:/save/8000000000000043", FA_READ | FA_OPEN_EXISTING)) {
|
||||
EPRINTF("Unable to open ns_appman save.\nSkipping SD seed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
u8 read_buf[0x20] __attribute__((aligned(4))) = {0};
|
||||
// Skip the two header blocks and only check the first bytes of each block
|
||||
// File contents are always block-aligned
|
||||
for (u32 i = SAVE_BLOCK_SIZE_DEFAULT * 2; i < f_size(&fp); i += SAVE_BLOCK_SIZE_DEFAULT) {
|
||||
if (f_lseek(&fp, i) || f_read(&fp, read_buf, 0x20, &read_bytes) || read_bytes != 0x20)
|
||||
break;
|
||||
if (memcmp(keys->temp_key, read_buf, sizeof(keys->temp_key)) == 0) {
|
||||
memcpy(keys->sd_seed, read_buf + 0x10, sizeof(keys->sd_seed));
|
||||
break;
|
||||
}
|
||||
}
|
||||
f_close(&fp);
|
||||
|
||||
TPRINTFARGS("%kSD Seed... ", colors[(color_idx++) % 6]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _derive_titlekeys(key_storage_t *keys, titlekey_buffer_t *titlekey_buffer, bool is_dev) {
|
||||
if (!key_exists(&keys->eticket_rsa_keypair)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
gfx_printf("%kTitlekeys... \n", colors[(color_idx++) % 6]);
|
||||
|
||||
const u32 buf_size = SAVE_BLOCK_SIZE_DEFAULT;
|
||||
_get_titlekeys_from_save(buf_size, keys->save_mac_key, titlekey_buffer, NULL);
|
||||
_get_titlekeys_from_save(buf_size, keys->save_mac_key, titlekey_buffer, &keys->eticket_rsa_keypair);
|
||||
|
||||
gfx_printf("\n%k Found %d titlekeys.\n\n", colors[(color_idx++) % 6], _titlekey_count);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void _derive_emmc_keys(key_storage_t *keys, titlekey_buffer_t *titlekey_buffer, bool is_dev) {
|
||||
// Set BIS keys.
|
||||
// PRODINFO/PRODINFOF
|
||||
se_aes_key_set(KS_BIS_00_CRYPT, keys->bis_key[0] + 0x00, SE_KEY_128_SIZE);
|
||||
se_aes_key_set(KS_BIS_00_TWEAK, keys->bis_key[0] + 0x10, SE_KEY_128_SIZE);
|
||||
// SAFE
|
||||
se_aes_key_set(KS_BIS_01_CRYPT, keys->bis_key[1] + 0x00, SE_KEY_128_SIZE);
|
||||
se_aes_key_set(KS_BIS_01_TWEAK, keys->bis_key[1] + 0x10, SE_KEY_128_SIZE);
|
||||
// SYSTEM/USER
|
||||
se_aes_key_set(KS_BIS_02_CRYPT, keys->bis_key[2] + 0x00, SE_KEY_128_SIZE);
|
||||
se_aes_key_set(KS_BIS_02_TWEAK, keys->bis_key[2] + 0x10, SE_KEY_128_SIZE);
|
||||
|
||||
if (!emummc_storage_set_mmc_partition(EMMC_GPP)) {
|
||||
EPRINTF("Unable to set partition.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!decrypt_ssl_rsa_key(keys, titlekey_buffer)) {
|
||||
EPRINTF("Unable to derive SSL key.");
|
||||
}
|
||||
|
||||
if (!decrypt_eticket_rsa_key(keys, titlekey_buffer, is_dev)) {
|
||||
EPRINTF("Unable to derive ETicket key.");
|
||||
}
|
||||
|
||||
// Parse eMMC GPT
|
||||
LIST_INIT(gpt);
|
||||
nx_emmc_gpt_parse(&gpt, &emmc_storage);
|
||||
|
||||
emmc_part_t *system_part = nx_emmc_part_find(&gpt, "SYSTEM");
|
||||
if (!system_part) {
|
||||
EPRINTF("Unable to locate System partition.");
|
||||
nx_emmc_gpt_free(&gpt);
|
||||
return;
|
||||
}
|
||||
|
||||
nx_emmc_bis_init(system_part);
|
||||
|
||||
if (f_mount(&emmc_fs, "bis:", 1)) {
|
||||
EPRINTF("Unable to mount system partition.");
|
||||
nx_emmc_gpt_free(&gpt);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sd_mount()) {
|
||||
EPRINTF("Unable to mount SD.");
|
||||
} else if (!_derive_sd_seed(keys)) {
|
||||
EPRINTF("Unable to get SD seed.");
|
||||
}
|
||||
|
||||
if (!_derive_titlekeys(keys, titlekey_buffer, is_dev)) {
|
||||
EPRINTF("Unable to derive titlekeys.");
|
||||
}
|
||||
|
||||
f_mount(NULL, "bis:", 1);
|
||||
nx_emmc_gpt_free(&gpt);
|
||||
}
|
||||
|
||||
// The security engine supports partial key override for locked keyslots
|
||||
// This allows for a manageable brute force on a PC
|
||||
// Then the Mariko AES class keys, KEK, BEK, unique SBK and SSK can be recovered
|
||||
int save_mariko_partial_keys(u32 start, u32 count, bool append) {
|
||||
const char *keyfile_path = "sd:/switch/partialaes.keys";
|
||||
if (!f_stat(keyfile_path, NULL)) {
|
||||
f_unlink(keyfile_path);
|
||||
}
|
||||
|
||||
if (start + count > SE_AES_KEYSLOT_COUNT) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
display_backlight_brightness(h_cfg.backlight, 1000);
|
||||
gfx_clear_partial_grey(0x1B, 32, 1224);
|
||||
gfx_con_setpos(0, 32);
|
||||
|
||||
color_idx = 0;
|
||||
|
||||
u32 pos = 0;
|
||||
u32 zeros[SE_KEY_128_SIZE / 4] = {0};
|
||||
u8 *data = malloc(4 * SE_KEY_128_SIZE);
|
||||
char *text_buffer = calloc(count, 0x100);
|
||||
|
||||
for (u32 ks = start; ks < start + count; ks++) {
|
||||
// Check if key is as expected
|
||||
if (ks < ARRAY_SIZE(mariko_key_vectors)) {
|
||||
se_aes_crypt_block_ecb(ks, DECRYPT, &data[0], mariko_key_vectors[ks]);
|
||||
if (key_exists(data)) {
|
||||
EPRINTFARGS("Failed to validate keyslot %d.", ks);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Encrypt zeros with complete key
|
||||
se_aes_crypt_block_ecb(ks, ENCRYPT, &data[3 * SE_KEY_128_SIZE], zeros);
|
||||
|
||||
// We only need to overwrite 3 of the dwords of the key
|
||||
for (u32 i = 0; i < 3; i++) {
|
||||
// Overwrite ith dword of key with zeros
|
||||
se_aes_key_partial_set(ks, i, 0);
|
||||
// Encrypt zeros with more of the key zeroed out
|
||||
se_aes_crypt_block_ecb(ks, ENCRYPT, &data[(2 - i) * SE_KEY_128_SIZE], zeros);
|
||||
}
|
||||
|
||||
// Skip saving key if two results are the same indicating unsuccessful overwrite or empty slot
|
||||
if (memcmp(&data[0], &data[SE_KEY_128_SIZE], SE_KEY_128_SIZE) == 0) {
|
||||
EPRINTFARGS("Failed to overwrite keyslot %d.", ks);
|
||||
continue;
|
||||
}
|
||||
|
||||
pos += s_printf(&text_buffer[pos], "%d\n", ks);
|
||||
for (u32 i = 0; i < 4; i++) {
|
||||
for (u32 j = 0; j < SE_KEY_128_SIZE; j++)
|
||||
pos += s_printf(&text_buffer[pos], "%02x", data[i * SE_KEY_128_SIZE + j]);
|
||||
pos += s_printf(&text_buffer[pos], " ");
|
||||
}
|
||||
pos += s_printf(&text_buffer[pos], "\n");
|
||||
}
|
||||
free(data);
|
||||
|
||||
if (strlen(text_buffer) == 0) {
|
||||
EPRINTFARGS("Failed to dump partial keys %d-%d.", start, start + count - 1);
|
||||
free(text_buffer);
|
||||
return 2;
|
||||
}
|
||||
|
||||
FIL fp;
|
||||
BYTE mode = FA_WRITE;
|
||||
|
||||
if (append) {
|
||||
mode |= FA_OPEN_APPEND;
|
||||
} else {
|
||||
mode |= FA_CREATE_ALWAYS;
|
||||
}
|
||||
|
||||
if (!sd_mount()) {
|
||||
EPRINTF("Unable to mount SD.");
|
||||
free(text_buffer);
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (f_open(&fp, keyfile_path, mode)) {
|
||||
EPRINTF("Unable to write partial keys to SD.");
|
||||
free(text_buffer);
|
||||
return 3;
|
||||
}
|
||||
|
||||
f_write(&fp, text_buffer, strlen(text_buffer), NULL);
|
||||
f_close(&fp);
|
||||
|
||||
gfx_printf("%kWrote partials to %s\n", colors[(color_idx++) % 6], keyfile_path);
|
||||
|
||||
free(text_buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _save_keys_to_sd(key_storage_t *keys, titlekey_buffer_t *titlekey_buffer, bool is_dev) {
|
||||
if (!sd_mount()) {
|
||||
EPRINTF("Unable to mount SD.");
|
||||
return;
|
||||
}
|
||||
|
||||
u32 text_buffer_size = MAX(_titlekey_count * sizeof(titlekey_text_buffer_t) + 1, SZ_16K);
|
||||
char *text_buffer = (char *)calloc(1, text_buffer_size);
|
||||
|
||||
SAVE_KEY(aes_kek_generation_source);
|
||||
SAVE_KEY(aes_key_generation_source);
|
||||
SAVE_KEY(bis_kek_source);
|
||||
SAVE_KEY_FAMILY_VAR(bis_key, keys->bis_key, 0);
|
||||
SAVE_KEY_FAMILY_VAR(bis_key_source, bis_key_sources, 0);
|
||||
SAVE_KEY_VAR(device_key, keys->device_key);
|
||||
SAVE_KEY_VAR(device_key_4x, keys->device_key_4x);
|
||||
SAVE_KEY_VAR(eticket_rsa_kek, keys->eticket_rsa_kek);
|
||||
SAVE_KEY_VAR(eticket_rsa_kek_personalized, keys->eticket_rsa_kek_personalized);
|
||||
if (is_dev) {
|
||||
SAVE_KEY_VAR(eticket_rsa_kek_source, eticket_rsa_kek_source_dev);
|
||||
} else {
|
||||
SAVE_KEY(eticket_rsa_kek_source);
|
||||
}
|
||||
SAVE_KEY(eticket_rsa_kekek_source);
|
||||
_save_key("eticket_rsa_keypair", &keys->eticket_rsa_keypair, sizeof(keys->eticket_rsa_keypair), text_buffer);
|
||||
SAVE_KEY(header_kek_source);
|
||||
SAVE_KEY_VAR(header_key, keys->header_key);
|
||||
SAVE_KEY(header_key_source);
|
||||
SAVE_KEY_FAMILY_VAR(key_area_key_application, keys->key_area_key[0], 0);
|
||||
SAVE_KEY_VAR(key_area_key_application_source, key_area_key_sources[0]);
|
||||
SAVE_KEY_FAMILY_VAR(key_area_key_ocean, keys->key_area_key[1], 0);
|
||||
SAVE_KEY_VAR(key_area_key_ocean_source, key_area_key_sources[1]);
|
||||
SAVE_KEY_FAMILY_VAR(key_area_key_system, keys->key_area_key[2], 0);
|
||||
SAVE_KEY_VAR(key_area_key_system_source, key_area_key_sources[2]);
|
||||
SAVE_KEY_FAMILY_VAR(keyblob, keys->keyblob, 0);
|
||||
SAVE_KEY_FAMILY_VAR(keyblob_key, keys->keyblob_key, 0);
|
||||
SAVE_KEY_FAMILY_VAR(keyblob_key_source, keyblob_key_sources, 0);
|
||||
SAVE_KEY_FAMILY_VAR(keyblob_mac_key, keys->keyblob_mac_key, 0);
|
||||
SAVE_KEY(keyblob_mac_key_source);
|
||||
if (is_dev) {
|
||||
SAVE_KEY_FAMILY_VAR(mariko_master_kek_source, mariko_master_kek_sources_dev, KB_FIRMWARE_VERSION_600);
|
||||
} else {
|
||||
SAVE_KEY_FAMILY_VAR(mariko_master_kek_source, mariko_master_kek_sources, KB_FIRMWARE_VERSION_600);
|
||||
}
|
||||
SAVE_KEY_FAMILY_VAR(master_kek, keys->master_kek, 0);
|
||||
SAVE_KEY_FAMILY_VAR(master_kek_source, master_kek_sources, KB_FIRMWARE_VERSION_620);
|
||||
SAVE_KEY_FAMILY_VAR(master_key, keys->master_key, 0);
|
||||
SAVE_KEY(master_key_source);
|
||||
SAVE_KEY_FAMILY_VAR(package1_key, keys->package1_key, 0);
|
||||
SAVE_KEY_FAMILY_VAR(package2_key, keys->package2_key, 0);
|
||||
SAVE_KEY(package2_key_source);
|
||||
SAVE_KEY(per_console_key_source);
|
||||
SAVE_KEY(retail_specific_aes_key_source);
|
||||
SAVE_KEY(save_mac_kek_source);
|
||||
SAVE_KEY_VAR(save_mac_key, keys->save_mac_key);
|
||||
SAVE_KEY(save_mac_key_source);
|
||||
SAVE_KEY(save_mac_sd_card_kek_source);
|
||||
SAVE_KEY(save_mac_sd_card_key_source);
|
||||
SAVE_KEY(sd_card_custom_storage_key_source);
|
||||
SAVE_KEY(sd_card_kek_source);
|
||||
SAVE_KEY(sd_card_nca_key_source);
|
||||
SAVE_KEY(sd_card_save_key_source);
|
||||
SAVE_KEY_VAR(sd_seed, keys->sd_seed);
|
||||
SAVE_KEY_VAR(secure_boot_key, keys->secure_boot_key);
|
||||
SAVE_KEY_VAR(ssl_rsa_kek, keys->ssl_rsa_kek);
|
||||
SAVE_KEY_VAR(ssl_rsa_kek_personalized, keys->ssl_rsa_kek_personalized);
|
||||
if (is_dev) {
|
||||
SAVE_KEY_VAR(ssl_rsa_kek_source, ssl_rsa_kek_source_dev);
|
||||
} else {
|
||||
SAVE_KEY(ssl_rsa_kek_source);
|
||||
}
|
||||
SAVE_KEY(ssl_rsa_kekek_source);
|
||||
_save_key("ssl_rsa_key", keys->ssl_rsa_key, SE_RSA2048_DIGEST_SIZE, text_buffer);
|
||||
SAVE_KEY_FAMILY_VAR(titlekek, keys->titlekek, 0);
|
||||
SAVE_KEY(titlekek_source);
|
||||
SAVE_KEY_VAR(tsec_key, keys->tsec_key);
|
||||
|
||||
char root_key_name[21] = "tsec_root_key_00";
|
||||
s_printf(root_key_name + 14, "%02x", TSEC_ROOT_KEY_VERSION);
|
||||
_save_key(root_key_name, keys->tsec_root_key, SE_KEY_128_SIZE, text_buffer);
|
||||
|
||||
gfx_printf("\n%k Found %d %s keys.\n\n", colors[(color_idx++) % 6], _key_count, is_dev ? "dev" : "prod");
|
||||
gfx_printf("%kFound through master_key_%02x.\n\n", colors[(color_idx++) % 6], KB_FIRMWARE_VERSION_MAX);
|
||||
|
||||
f_mkdir("sd:/switch");
|
||||
|
||||
const char *keyfile_path = is_dev ? "sd:/switch/dev.keys" : "sd:/switch/prod.keys";
|
||||
|
||||
FILINFO fno;
|
||||
if (!sd_save_to_file(text_buffer, strlen(text_buffer), keyfile_path) && !f_stat(keyfile_path, &fno)) {
|
||||
gfx_printf("%kWrote %d bytes to %s\n", colors[(color_idx++) % 6], (u32)fno.fsize, keyfile_path);
|
||||
} else {
|
||||
EPRINTF("Unable to save keys to SD.");
|
||||
}
|
||||
|
||||
if (_titlekey_count == 0 || !titlekey_buffer) {
|
||||
free(text_buffer);
|
||||
return;
|
||||
}
|
||||
memset(text_buffer, 0, text_buffer_size);
|
||||
|
||||
titlekey_text_buffer_t *titlekey_text = (titlekey_text_buffer_t *)text_buffer;
|
||||
|
||||
for (u32 i = 0; i < _titlekey_count; i++) {
|
||||
for (u32 j = 0; j < SE_KEY_128_SIZE; j++)
|
||||
s_printf(&titlekey_text[i].rights_id[j * 2], "%02x", titlekey_buffer->rights_ids[i][j]);
|
||||
s_printf(titlekey_text[i].equals, " = ");
|
||||
for (u32 j = 0; j < SE_KEY_128_SIZE; j++)
|
||||
s_printf(&titlekey_text[i].titlekey[j * 2], "%02x", titlekey_buffer->titlekeys[i][j]);
|
||||
s_printf(titlekey_text[i].newline, "\n");
|
||||
}
|
||||
|
||||
keyfile_path = "sd:/switch/title.keys";
|
||||
if (!sd_save_to_file(text_buffer, strlen(text_buffer), keyfile_path) && !f_stat(keyfile_path, &fno)) {
|
||||
gfx_printf("%kWrote %d bytes to %s\n", colors[(color_idx++) % 6], (u32)fno.fsize, keyfile_path);
|
||||
} else {
|
||||
EPRINTF("Unable to save titlekeys to SD.");
|
||||
}
|
||||
|
||||
free(text_buffer);
|
||||
}
|
||||
|
||||
static void _derive_keys() {
|
||||
minerva_periodic_training();
|
||||
|
||||
if (!check_keyslot_access()) {
|
||||
EPRINTF("Unable to set crypto keyslots!\nTry launching payload differently\n or flash Spacecraft-NX if using a modchip.");
|
||||
return;
|
||||
}
|
||||
|
||||
u32 start_whole_operation_time = get_tmr_us();
|
||||
|
||||
if (emummc_storage_init_mmc()) {
|
||||
EPRINTF("Unable to init MMC.");
|
||||
} else {
|
||||
TPRINTFARGS("%kMMC init... ", colors[(color_idx++) % 6]);
|
||||
}
|
||||
|
||||
minerva_periodic_training();
|
||||
|
||||
if (emmc_storage.initialized && !emummc_storage_set_mmc_partition(EMMC_BOOT0)) {
|
||||
EPRINTF("Unable to set partition.");
|
||||
emummc_storage_end();
|
||||
}
|
||||
|
||||
bool is_dev = fuse_read_hw_state() == FUSE_NX_HW_STATE_DEV;
|
||||
|
||||
key_storage_t __attribute__((aligned(4))) prod_keys = {0}, dev_keys = {0};
|
||||
key_storage_t *keys = is_dev ? &dev_keys : &prod_keys;
|
||||
|
||||
_derive_master_keys(&prod_keys, &dev_keys, is_dev);
|
||||
|
||||
TPRINTFARGS("%kMaster keys... ", colors[(color_idx++) % 6]);
|
||||
|
||||
_derive_bis_keys(keys);
|
||||
|
||||
TPRINTFARGS("%kBIS keys... ", colors[(color_idx++) % 6]);
|
||||
|
||||
_derive_misc_keys(keys);
|
||||
_derive_non_unique_keys(&prod_keys, is_dev);
|
||||
_derive_non_unique_keys(&dev_keys, is_dev);
|
||||
|
||||
titlekey_buffer_t *titlekey_buffer = (titlekey_buffer_t *)TITLEKEY_BUF_ADR;
|
||||
|
||||
// Requires BIS key for SYSTEM partition
|
||||
if (!emmc_storage.initialized) {
|
||||
EPRINTF("eMMC not initialized.\nSkipping SD seed and titlekeys.");
|
||||
} else if (key_exists(keys->bis_key[2])) {
|
||||
_derive_emmc_keys(keys, titlekey_buffer, is_dev);
|
||||
} else {
|
||||
EPRINTF("Missing needed BIS keys.\nSkipping SD seed and titlekeys.");
|
||||
}
|
||||
|
||||
end_time = get_tmr_us();
|
||||
gfx_printf("%Picklock totally done in %d us\n", colors[(color_idx++) % 6], end_time - start_whole_operation_time);
|
||||
|
||||
if (h_cfg.t210b01) {
|
||||
// On Mariko, save only relevant key set
|
||||
_save_keys_to_sd(keys, titlekey_buffer, is_dev);
|
||||
} else {
|
||||
// On Erista, save both prod and dev key sets
|
||||
_save_keys_to_sd(&prod_keys, titlekey_buffer, false);
|
||||
_key_count = 0;
|
||||
_save_keys_to_sd(&dev_keys, NULL, true);
|
||||
}
|
||||
}
|
||||
|
||||
void derive_amiibo_keys() {
|
||||
minerva_change_freq(FREQ_1600);
|
||||
|
||||
bool is_dev = fuse_read_hw_state() == FUSE_NX_HW_STATE_DEV;
|
||||
|
||||
key_storage_t __attribute__((aligned(4))) prod_keys = {0}, dev_keys = {0};
|
||||
key_storage_t *keys = is_dev ? &dev_keys : &prod_keys;
|
||||
|
||||
_derive_master_keys(&prod_keys, &dev_keys, is_dev);
|
||||
|
||||
minerva_periodic_training();
|
||||
|
||||
display_backlight_brightness(h_cfg.backlight, 1000);
|
||||
gfx_clear_partial_grey(0x1B, 32, 1224);
|
||||
gfx_con_setpos(0, 32);
|
||||
|
||||
color_idx = 0;
|
||||
|
||||
minerva_periodic_training();
|
||||
|
||||
if (!key_exists(keys->master_key[0])) {
|
||||
EPRINTF("Unable to derive master keys for NFC.");
|
||||
minerva_change_freq(FREQ_800);
|
||||
btn_wait();
|
||||
return;
|
||||
}
|
||||
|
||||
nfc_save_key_t __attribute__((aligned(4))) nfc_save_keys[2] = {0};
|
||||
|
||||
nfc_decrypt_amiibo_keys(keys, nfc_save_keys, is_dev);
|
||||
|
||||
minerva_periodic_training();
|
||||
|
||||
u32 hash[SE_SHA_256_SIZE / 4] = {0};
|
||||
se_calc_sha256_oneshot(hash, &nfc_save_keys[0], sizeof(nfc_save_keys));
|
||||
|
||||
if (memcmp(hash, is_dev ? nfc_blob_hash_dev : nfc_blob_hash, sizeof(hash)) != 0) {
|
||||
EPRINTF("Amiibo hash mismatch. Skipping save.");
|
||||
} else {
|
||||
const char *keyfile_path = is_dev ? "sd:/switch/key_dev.bin" : "sd:/switch/key_retail.bin";
|
||||
|
||||
if (!sd_save_to_file(&nfc_save_keys[0], sizeof(nfc_save_keys), keyfile_path)) {
|
||||
gfx_printf("%kWrote Amiibo keys to\n %s\n", colors[(color_idx++) % 6], keyfile_path);
|
||||
} else {
|
||||
EPRINTF("Unable to save Amiibo keys to SD.");
|
||||
}
|
||||
}
|
||||
|
||||
gfx_printf("\n%kPress a button to return to the menu.", colors[(color_idx++) % 6]);
|
||||
minerva_change_freq(FREQ_800);
|
||||
btn_wait();
|
||||
gfx_clear_grey(0x1B);
|
||||
}
|
||||
|
||||
void dump_keys() {
|
||||
minerva_change_freq(FREQ_1600);
|
||||
|
||||
display_backlight_brightness(h_cfg.backlight, 1000);
|
||||
gfx_clear_grey(0x1B);
|
||||
gfx_con_setpos(0, 0);
|
||||
|
||||
gfx_printf("[%kLo%kck%kpi%kck%k_R%kCM%k v%d.%d.%d%k]\n\n",
|
||||
colors[0], colors[1], colors[2], colors[3], colors[4], colors[5], 0xFFFF00FF, LP_VER_MJ, LP_VER_MN, LP_VER_BF, 0xFFCCCCCC);
|
||||
|
||||
_key_count = 0;
|
||||
_titlekey_count = 0;
|
||||
color_idx = 0;
|
||||
|
||||
start_time = get_tmr_us();
|
||||
|
||||
_derive_keys();
|
||||
|
||||
emummc_load_cfg();
|
||||
// Ignore whether emummc is enabled.
|
||||
h_cfg.emummc_force_disable = emu_cfg.sector == 0 && !emu_cfg.path;
|
||||
emu_cfg.enabled = !h_cfg.emummc_force_disable;
|
||||
if (emmc_storage.initialized) {
|
||||
sdmmc_storage_end(&emmc_storage);
|
||||
}
|
||||
|
||||
minerva_change_freq(FREQ_800);
|
||||
gfx_printf("\n%kPress VOL+ to save a screenshot\n or another button to return to the menu.\n\n", colors[(color_idx++) % 6]);
|
||||
u8 btn = btn_wait();
|
||||
if (btn == BTN_VOL_UP) {
|
||||
int res = save_fb_to_bmp();
|
||||
if (!res) {
|
||||
gfx_printf("%kScreenshot sd:/switch/picklock_rcm.bmp saved.", colors[(color_idx++) % 6]);
|
||||
} else {
|
||||
EPRINTF("Screenshot failed.");
|
||||
}
|
||||
gfx_printf("\n%kPress a button to return to the menu.", colors[(color_idx++) % 6]);
|
||||
btn_wait();
|
||||
}
|
||||
gfx_clear_grey(0x1B);
|
||||
}
|
51
source/keys/keys.h
Normal file
51
source/keys/keys.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2022 shchmue
|
||||
*
|
||||
* 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 _KEYS_H_
|
||||
#define _KEYS_H_
|
||||
|
||||
#include "crypto.h"
|
||||
|
||||
#include "../hos/hos.h"
|
||||
#include <sec/se_t210.h>
|
||||
#include <utils/types.h>
|
||||
|
||||
#define TPRINTF(text) \
|
||||
end_time = get_tmr_us(); \
|
||||
gfx_printf(text" done in %d us\n", end_time - start_time); \
|
||||
start_time = get_tmr_us(); \
|
||||
minerva_periodic_training()
|
||||
|
||||
#define TPRINTFARGS(text, args...) \
|
||||
end_time = get_tmr_us(); \
|
||||
gfx_printf(text" done in %d us\n", args, end_time - start_time); \
|
||||
start_time = get_tmr_us(); \
|
||||
minerva_periodic_training()
|
||||
|
||||
// save key wrapper
|
||||
#define SAVE_KEY(name) _save_key(#name, name, sizeof(name), text_buffer)
|
||||
// save key with different name than variable
|
||||
#define SAVE_KEY_VAR(name, varname) _save_key(#name, varname, sizeof(varname), text_buffer)
|
||||
// save key family wrapper
|
||||
#define SAVE_KEY_FAMILY(name, start) _save_key_family(#name, name, start, ARRAY_SIZE(name), sizeof(*(name)), text_buffer)
|
||||
// save key family with different name than variable
|
||||
#define SAVE_KEY_FAMILY_VAR(name, varname, start) _save_key_family(#name, varname, start, ARRAY_SIZE(varname), sizeof(*(varname)), text_buffer)
|
||||
|
||||
void dump_keys();
|
||||
int save_mariko_partial_keys(u32 start, u32 count, bool append);
|
||||
void derive_amiibo_keys();
|
||||
|
||||
#endif
|
54
source/keys/nfc_crypto.c
Normal file
54
source/keys/nfc_crypto.c
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (c) 2022 shchmue
|
||||
*
|
||||
* 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 "nfc_crypto.h"
|
||||
|
||||
#include <mem/minerva.h>
|
||||
#include <sec/se.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
void nfc_decrypt_amiibo_keys(key_storage_t *keys, nfc_save_key_t out_nfc_save_keys[2], bool is_dev) {
|
||||
const u8 *encrypted_keys = is_dev ? encrypted_nfc_keys_dev : encrypted_nfc_keys;
|
||||
u32 kek[SE_KEY_128_SIZE / 4] = {0};
|
||||
decrypt_aes_key(KS_AES_ECB, keys, kek, nfc_key_source, 0, 0);
|
||||
|
||||
nfc_keyblob_t __attribute__((aligned(4))) nfc_keyblob;
|
||||
static const u8 nfc_iv[SE_AES_IV_SIZE] = {
|
||||
0xB9, 0x1D, 0xC1, 0xCF, 0x33, 0x5F, 0xA6, 0x13, 0x2A, 0xEF, 0x90, 0x99, 0xAA, 0xCA, 0x93, 0xC8};
|
||||
se_aes_key_set(KS_AES_CTR, kek, SE_KEY_128_SIZE);
|
||||
se_aes_crypt_ctr(KS_AES_CTR, &nfc_keyblob, sizeof(nfc_keyblob), encrypted_keys, sizeof(nfc_keyblob), &nfc_iv);
|
||||
|
||||
minerva_periodic_training();
|
||||
|
||||
u32 xor_pad[0x20 / 4] = {0};
|
||||
se_aes_key_set(KS_AES_CTR, nfc_keyblob.ctr_key, SE_KEY_128_SIZE);
|
||||
se_aes_crypt_ctr(KS_AES_CTR, xor_pad, sizeof(xor_pad), xor_pad, sizeof(xor_pad), nfc_keyblob.ctr_iv);
|
||||
|
||||
minerva_periodic_training();
|
||||
|
||||
memcpy(out_nfc_save_keys[0].hmac_key, nfc_keyblob.hmac_key, sizeof(nfc_keyblob.hmac_key));
|
||||
memcpy(out_nfc_save_keys[0].phrase, nfc_keyblob.phrase, sizeof(nfc_keyblob.phrase));
|
||||
out_nfc_save_keys[0].seed_size = sizeof(nfc_keyblob.seed);
|
||||
memcpy(out_nfc_save_keys[0].seed, nfc_keyblob.seed, sizeof(nfc_keyblob.seed));
|
||||
memcpy(out_nfc_save_keys[0].xor_pad, xor_pad, sizeof(xor_pad));
|
||||
|
||||
memcpy(out_nfc_save_keys[1].hmac_key, nfc_keyblob.hmac_key_for_verif, sizeof(nfc_keyblob.hmac_key_for_verif));
|
||||
memcpy(out_nfc_save_keys[1].phrase, nfc_keyblob.phrase_for_verif, sizeof(nfc_keyblob.phrase_for_verif));
|
||||
out_nfc_save_keys[1].seed_size = sizeof(nfc_keyblob.seed_for_verif);
|
||||
memcpy(out_nfc_save_keys[1].seed, nfc_keyblob.seed_for_verif, sizeof(nfc_keyblob.seed_for_verif));
|
||||
memcpy(out_nfc_save_keys[1].xor_pad, xor_pad, sizeof(xor_pad));
|
||||
}
|
75
source/keys/nfc_crypto.h
Normal file
75
source/keys/nfc_crypto.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 2022 shchmue
|
||||
*
|
||||
* 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 _NFC_CRYPTO_H_
|
||||
#define _NFC_CRYPTO_H_
|
||||
|
||||
#include "crypto.h"
|
||||
|
||||
#include <sec/se_t210.h>
|
||||
#include <utils/types.h>
|
||||
|
||||
static const u8 nfc_key_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x83, 0xF6, 0xEF, 0xD8, 0x13, 0x26, 0x49, 0xAB, 0x97, 0x5F, 0xEA, 0xBA, 0x65, 0x71, 0xCA, 0xCA};
|
||||
static const u8 encrypted_nfc_keys[0x80] __attribute__((aligned(4))) = {
|
||||
0x76, 0x50, 0x87, 0x02, 0x40, 0xA6, 0x5A, 0x98, 0xCE, 0x39, 0x2F, 0xC8, 0x83, 0xAF, 0x54, 0x76,
|
||||
0x28, 0xFF, 0x50, 0xFC, 0xC1, 0xFB, 0x26, 0x14, 0xA2, 0x4A, 0xA6, 0x74, 0x90, 0xA4, 0x37, 0x06,
|
||||
0x03, 0x63, 0xC2, 0xB1, 0xAF, 0x9F, 0xF7, 0x07, 0xFC, 0x8A, 0xB9, 0xCA, 0x28, 0x68, 0x6E, 0xF7,
|
||||
0x42, 0xCD, 0x68, 0x13, 0xCD, 0x7B, 0x3A, 0x60, 0x3E, 0x8B, 0xAB, 0x3A, 0xCC, 0xED, 0xE0, 0xDD,
|
||||
0x71, 0x1F, 0xA5, 0xDE, 0xB8, 0xB1, 0xF5, 0x1D, 0x14, 0x73, 0xBE, 0x27, 0xCC, 0xA1, 0x9B, 0x23,
|
||||
0x06, 0x91, 0x89, 0x05, 0xED, 0xD6, 0x92, 0x76, 0x3F, 0x42, 0xFB, 0xD1, 0x8F, 0x2D, 0x6D, 0x72,
|
||||
0xC8, 0x9E, 0x48, 0xE8, 0x03, 0x64, 0xF0, 0x3C, 0x0E, 0x2A, 0xF1, 0x26, 0x83, 0x02, 0x4F, 0xE2,
|
||||
0x41, 0xAA, 0xC8, 0x33, 0x68, 0x84, 0x3A, 0xFB, 0x87, 0x18, 0xEA, 0xF7, 0x36, 0xA2, 0x4E, 0xA9};
|
||||
static const u8 encrypted_nfc_keys_dev[0x80] __attribute__((aligned(4))) = {
|
||||
0x13, 0xB0, 0xFB, 0xC2, 0x91, 0x6D, 0x6E, 0x5A, 0x10, 0x31, 0x40, 0xB7, 0xDF, 0xCF, 0x69, 0x69,
|
||||
0xB0, 0xFA, 0xAE, 0x7F, 0xB2, 0x4D, 0x27, 0xC9, 0xE9, 0x3F, 0x5B, 0x38, 0x39, 0x24, 0x98, 0xCE,
|
||||
0xED, 0xD2, 0xA9, 0x6C, 0x6F, 0xA7, 0x72, 0xD7, 0x11, 0x31, 0x17, 0x93, 0x12, 0x49, 0x32, 0x85,
|
||||
0x21, 0xE5, 0xE1, 0x88, 0x0F, 0x08, 0xF2, 0x30, 0x5C, 0xC3, 0xAA, 0xFF, 0xC0, 0xAB, 0x21, 0x96,
|
||||
0x74, 0x39, 0xED, 0xE0, 0x5A, 0xB6, 0x75, 0xC2, 0x3B, 0x08, 0x61, 0xE4, 0xA7, 0xD6, 0xED, 0x8C,
|
||||
0xA9, 0x02, 0x12, 0xA6, 0xCC, 0x27, 0x4C, 0x1C, 0x41, 0x9C, 0xD8, 0x4C, 0x00, 0xC7, 0x5B, 0x5D,
|
||||
0xED, 0xC2, 0x3D, 0x5E, 0x00, 0xF5, 0x49, 0xFA, 0x6C, 0x75, 0x67, 0xCF, 0x1F, 0x73, 0x1A, 0xE8,
|
||||
0x47, 0xD4, 0x3D, 0x9B, 0x83, 0x5B, 0x18, 0x2F, 0x95, 0xA9, 0x04, 0xBC, 0x2E, 0xBB, 0x64, 0x4A};
|
||||
static const u8 nfc_blob_hash[SE_SHA_256_SIZE] __attribute__((aligned(4))) = {
|
||||
0x7F, 0x92, 0x83, 0x65, 0x4E, 0xC1, 0x09, 0x7F, 0xBD, 0xFF, 0x31, 0xDE, 0x94, 0x66, 0x51, 0xAE,
|
||||
0x60, 0xC2, 0x85, 0x4A, 0xFB, 0x54, 0x4A, 0xBE, 0x89, 0x63, 0xD3, 0x89, 0x63, 0x9C, 0x71, 0x0E};
|
||||
static const u8 nfc_blob_hash_dev[SE_SHA_256_SIZE] __attribute__((aligned(4))) = {
|
||||
0x4E, 0x36, 0x59, 0x1C, 0x75, 0x80, 0x23, 0x03, 0x98, 0x2D, 0x45, 0xD9, 0x85, 0xB8, 0x60, 0x18,
|
||||
0x7C, 0x85, 0x37, 0x9B, 0xCB, 0xBA, 0xF3, 0xDC, 0x25, 0x38, 0x73, 0xDB, 0x2F, 0xFA, 0xAE, 0x26};
|
||||
|
||||
typedef struct {
|
||||
char phrase[0xE];
|
||||
u8 seed[0xE];
|
||||
u8 hmac_key[0x10];
|
||||
char phrase_for_verif[0xE];
|
||||
u8 seed_for_verif[0x10];
|
||||
u8 hmac_key_for_verif[0x10];
|
||||
u8 ctr_key[0x10];
|
||||
u8 ctr_iv[0x10];
|
||||
u8 pad[6];
|
||||
} nfc_keyblob_t;
|
||||
|
||||
typedef struct {
|
||||
u8 hmac_key[0x10];
|
||||
char phrase[0xE];
|
||||
u8 rsvd;
|
||||
u8 seed_size;
|
||||
u8 seed[0x10];
|
||||
u8 xor_pad[0x20];
|
||||
} nfc_save_key_t;
|
||||
|
||||
void nfc_decrypt_amiibo_keys(key_storage_t *keys, nfc_save_key_t out_nfc_save_keys[2], bool is_dev);
|
||||
|
||||
#endif
|
120
source/keys/ssl_crypto.c
Normal file
120
source/keys/ssl_crypto.c
Normal file
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Copyright (c) 2022 shchmue
|
||||
*
|
||||
* 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 "ssl_crypto.h"
|
||||
|
||||
#include "cal0_read.h"
|
||||
#include "gmac.h"
|
||||
|
||||
#include "../config.h"
|
||||
#include <gfx_utils.h>
|
||||
#include <sec/se.h>
|
||||
#include <sec/se_t210.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
extern hekate_config h_cfg;
|
||||
|
||||
void ssl_derive_rsa_kek_device_unique(key_storage_t *keys, void *out_rsa_kek, u32 generation) {
|
||||
if ((!h_cfg.t210b01 && !key_exists(keys->device_key)) || (h_cfg.t210b01 && (!key_exists(keys->master_key[0]) || !key_exists(keys->device_key_4x)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 option = SET_SEAL_KEY_INDEX(SEAL_KEY_IMPORT_SSL_KEY) | IS_DEVICE_UNIQUE;
|
||||
derive_rsa_kek(KS_AES_ECB, keys, out_rsa_kek, ssl_client_cert_kek_source, ssl_client_cert_key_source, generation, option);
|
||||
}
|
||||
|
||||
void ssl_derive_rsa_kek_legacy(key_storage_t *keys, void *out_rsa_kek) {
|
||||
if (!key_exists(keys->master_key[0])) {
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 generation = 0;
|
||||
const u32 option = SET_SEAL_KEY_INDEX(SEAL_KEY_DECRYPT_DEVICE_UNIQUE_DATA) | NOT_DEVICE_UNIQUE;
|
||||
derive_rsa_kek(KS_AES_ECB, keys, out_rsa_kek, ssl_rsa_kekek_source, ssl_rsa_kek_source_legacy, generation, option);
|
||||
}
|
||||
|
||||
void ssl_derive_rsa_kek_original(key_storage_t *keys, void *out_rsa_kek, bool is_dev) {
|
||||
if (!key_exists(keys->master_key[0])) {
|
||||
return;
|
||||
}
|
||||
|
||||
const void *ssl_kek_source = is_dev ? ssl_rsa_kek_source_dev : ssl_rsa_kek_source;
|
||||
const u32 generation = 0;
|
||||
u32 option = SET_SEAL_KEY_INDEX(SEAL_KEY_DECRYPT_DEVICE_UNIQUE_DATA) | NOT_DEVICE_UNIQUE;
|
||||
derive_rsa_kek(KS_AES_ECB, keys, out_rsa_kek, ssl_rsa_kekek_source, ssl_kek_source, generation, option);
|
||||
}
|
||||
|
||||
bool decrypt_ssl_rsa_key(key_storage_t *keys, void *buffer) {
|
||||
if (!cal0_read(KS_BIS_00_TWEAK, KS_BIS_00_CRYPT, buffer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nx_emmc_cal0_t *cal0 = (nx_emmc_cal0_t *)buffer;
|
||||
u32 generation = 0;
|
||||
const void *encrypted_key = NULL;
|
||||
const void *iv = NULL;
|
||||
u32 key_size = 0;
|
||||
void *ctr_key = NULL;
|
||||
bool enforce_unique = true;
|
||||
|
||||
if (!cal0_get_ssl_rsa_key(cal0, &encrypted_key, &key_size, &iv, &generation)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (key_size == SSL_RSA_KEY_SIZE) {
|
||||
bool all_zero = true;
|
||||
const u8 *key8 = (const u8 *)encrypted_key;
|
||||
for (u32 i = SE_RSA2048_DIGEST_SIZE; i < SSL_RSA_KEY_SIZE; i++) {
|
||||
if (key8[i] != 0) {
|
||||
all_zero = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (all_zero) {
|
||||
// Keys of this form are not encrypted
|
||||
memcpy(keys->ssl_rsa_key, encrypted_key, SE_RSA2048_DIGEST_SIZE);
|
||||
return true;
|
||||
}
|
||||
|
||||
ssl_derive_rsa_kek_legacy(keys, keys->ssl_rsa_kek_legacy);
|
||||
ctr_key = keys->ssl_rsa_kek_legacy;
|
||||
enforce_unique = false;
|
||||
} else if (generation) {
|
||||
ssl_derive_rsa_kek_device_unique(keys, keys->ssl_rsa_kek_personalized, generation);
|
||||
ctr_key = keys->ssl_rsa_kek_personalized;
|
||||
} else {
|
||||
ctr_key = keys->ssl_rsa_kek;
|
||||
}
|
||||
|
||||
u32 ctr_size = enforce_unique ? key_size - 0x20 : key_size - 0x10;
|
||||
se_aes_key_set(KS_AES_CTR, ctr_key, SE_KEY_128_SIZE);
|
||||
se_aes_crypt_ctr(KS_AES_CTR, keys->ssl_rsa_key, ctr_size, encrypted_key, ctr_size, iv);
|
||||
|
||||
if (enforce_unique) {
|
||||
u32 calc_mac[SE_KEY_128_SIZE / 4] = {0};
|
||||
calc_gmac(KS_AES_ECB, calc_mac, keys->ssl_rsa_key, ctr_size, ctr_key, iv);
|
||||
|
||||
const u8 *key8 = (const u8 *)encrypted_key;
|
||||
if (memcmp(calc_mac, &key8[ctr_size], 0x10) != 0) {
|
||||
EPRINTF("SSL keypair has invalid GMac.");
|
||||
memset(keys->ssl_rsa_key, 0, sizeof(keys->ssl_rsa_key));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
45
source/keys/ssl_crypto.h
Normal file
45
source/keys/ssl_crypto.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2022 shchmue
|
||||
*
|
||||
* 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 _SSL_CRYPTO_H_
|
||||
#define _SSL_CRYPTO_H_
|
||||
|
||||
#include "crypto.h"
|
||||
|
||||
#include <utils/types.h>
|
||||
|
||||
#define SSL_RSA_KEY_SIZE (SE_AES_IV_SIZE + SE_RSA2048_DIGEST_SIZE)
|
||||
|
||||
static const u8 ssl_rsa_kekek_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x7F, 0x5B, 0xB0, 0x84, 0x7B, 0x25, 0xAA, 0x67, 0xFA, 0xC8, 0x4B, 0xE2, 0x3D, 0x7B, 0x69, 0x03};
|
||||
static const u8 ssl_rsa_kek_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x9A, 0x38, 0x3B, 0xF4, 0x31, 0xD0, 0xBD, 0x81, 0x32, 0x53, 0x4B, 0xA9, 0x64, 0x39, 0x7D, 0xE3};
|
||||
static const u8 ssl_rsa_kek_source_dev[0x10] __attribute__((aligned(4))) = {
|
||||
0xD5, 0xD2, 0xFC, 0x00, 0xFD, 0x49, 0xDD, 0xF8, 0xEE, 0x7B, 0xC4, 0x4B, 0xE1, 0x4C, 0xAA, 0x99};
|
||||
static const u8 ssl_rsa_kek_source_legacy[0x10] __attribute__((aligned(4))) = {
|
||||
0xED, 0x36, 0xB1, 0x32, 0x27, 0x17, 0xD2, 0xB0, 0xBA, 0x1F, 0xC1, 0xBD, 0x4D, 0x38, 0x0F, 0x5E};
|
||||
static const u8 ssl_client_cert_kek_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x64, 0xB8, 0x30, 0xDD, 0x0F, 0x3C, 0xB7, 0xFB, 0x4C, 0x16, 0x01, 0x97, 0xEA, 0x9D, 0x12, 0x10};
|
||||
static const u8 ssl_client_cert_key_source[0x10] __attribute__((aligned(4))) = {
|
||||
0x4D, 0x92, 0x5A, 0x69, 0x42, 0x23, 0xBB, 0x92, 0x59, 0x16, 0x3E, 0x51, 0x8C, 0x78, 0x14, 0x0F};
|
||||
|
||||
void ssl_derive_rsa_kek_device_unique(key_storage_t *keys, void *out_rsa_kek, u32 generation);
|
||||
void ssl_derive_rsa_kek_legacy(key_storage_t *keys, void *out_rsa_kek);
|
||||
void ssl_derive_rsa_kek_original(key_storage_t *keys, void *out_rsa_kek, bool is_dev);
|
||||
|
||||
bool decrypt_ssl_rsa_key(key_storage_t *keys, void *buffer);
|
||||
|
||||
#endif
|
108
source/libs/fatfs/diskio.c
Normal file
108
source/libs/fatfs/diskio.c
Normal file
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020 shchmue
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2016 */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* If a working storage control module is available, it should be */
|
||||
/* attached to the FatFs via a glue function rather than modifying it. */
|
||||
/* This is an example of glue functions to attach various exsisting */
|
||||
/* storage control modules to the FatFs module with a defined API. */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <libs/fatfs/diskio.h> /* FatFs lower layer API */
|
||||
#include <memory_map.h>
|
||||
#include <storage/nx_sd.h>
|
||||
#include "../../storage/nx_emmc_bis.h"
|
||||
#include <storage/sdmmc.h>
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Get Drive Status */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
DSTATUS disk_status (
|
||||
BYTE pdrv /* Physical drive number to identify the drive */
|
||||
)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Inidialize a Drive */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
DSTATUS disk_initialize (
|
||||
BYTE pdrv /* Physical drive number to identify the drive */
|
||||
)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Read Sector(s) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
DRESULT disk_read (
|
||||
BYTE pdrv, /* Physical drive number to identify the drive */
|
||||
BYTE *buff, /* Data buffer to store read data */
|
||||
DWORD sector, /* Start sector in LBA */
|
||||
UINT count /* Number of sectors to read */
|
||||
)
|
||||
{
|
||||
switch (pdrv)
|
||||
{
|
||||
case DRIVE_SD:
|
||||
return sdmmc_storage_read(&sd_storage, sector, count, buff) ? RES_OK : RES_ERROR;
|
||||
|
||||
case DRIVE_BIS:
|
||||
return nx_emmc_bis_read(sector, count, buff);
|
||||
}
|
||||
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Write Sector(s) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
DRESULT disk_write (
|
||||
BYTE pdrv, /* Physical drive number to identify the drive */
|
||||
const BYTE *buff, /* Data to be written */
|
||||
DWORD sector, /* Start sector in LBA */
|
||||
UINT count /* Number of sectors to write */
|
||||
)
|
||||
{
|
||||
switch (pdrv)
|
||||
{
|
||||
case DRIVE_SD:
|
||||
return sdmmc_storage_write(&sd_storage, sector, count, (void *)buff) ? RES_OK : RES_ERROR;
|
||||
|
||||
case DRIVE_BIS:
|
||||
return nx_emmc_bis_write(sector, count, (void *)buff);
|
||||
}
|
||||
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Miscellaneous Functions */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
DRESULT disk_ioctl (
|
||||
BYTE pdrv, /* Physical drive number (0..) */
|
||||
BYTE cmd, /* Control code */
|
||||
void *buff /* Buffer to send/receive control data */
|
||||
)
|
||||
{
|
||||
return RES_OK;
|
||||
}
|
294
source/libs/fatfs/ffconf.h
Normal file
294
source/libs/fatfs/ffconf.h
Normal file
|
@ -0,0 +1,294 @@
|
|||
/*---------------------------------------------------------------------------/
|
||||
/ FatFs Functional Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FFCONF_DEF 86604 /* Revision ID */
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Function Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_FS_READONLY 0
|
||||
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
|
||||
/ Read-only configuration removes writing API functions, f_write(), f_sync(),
|
||||
/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
|
||||
/ and optional writing functions as well. */
|
||||
|
||||
|
||||
#define FF_FS_MINIMIZE 0
|
||||
/* This option defines minimization level to remove some basic API functions.
|
||||
/
|
||||
/ 0: Basic functions are fully enabled.
|
||||
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
|
||||
/ are removed.
|
||||
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
|
||||
/ 3: f_lseek() function is removed in addition to 2. */
|
||||
|
||||
|
||||
#define FF_USE_STRFUNC 2
|
||||
/* This option switches string functions, f_gets(), f_putc(), f_puts() and f_printf().
|
||||
/
|
||||
/ 0: Disable string functions.
|
||||
/ 1: Enable without LF-CRLF conversion.
|
||||
/ 2: Enable with LF-CRLF conversion. */
|
||||
|
||||
|
||||
#define FF_USE_FIND 1
|
||||
/* This option switches filtered directory read functions, f_findfirst() and
|
||||
/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
|
||||
|
||||
|
||||
#define FF_USE_MKFS 0
|
||||
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
|
||||
|
||||
#define FF_FASTFS 0
|
||||
|
||||
#if FF_FASTFS
|
||||
#define FF_USE_FASTSEEK 1
|
||||
#else
|
||||
#define FF_USE_FASTSEEK 0
|
||||
#endif
|
||||
/* This option switches fast seek function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define FF_USE_EXPAND 0
|
||||
/* This option switches f_expand function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#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. */
|
||||
|
||||
|
||||
#define FF_USE_LABEL 0
|
||||
/* This option switches volume label functions, f_getlabel() and f_setlabel().
|
||||
/ (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define FF_USE_FORWARD 0
|
||||
/* This option switches f_forward() function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Locale and Namespace Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_CODE_PAGE 850
|
||||
/* This option specifies the OEM code page to be used on the target system.
|
||||
/ Incorrect code page setting can cause a file open failure.
|
||||
/
|
||||
/ 437 - U.S.
|
||||
/ 720 - Arabic
|
||||
/ 737 - Greek
|
||||
/ 771 - KBL
|
||||
/ 775 - Baltic
|
||||
/ 850 - Latin 1
|
||||
/ 852 - Latin 2
|
||||
/ 855 - Cyrillic
|
||||
/ 857 - Turkish
|
||||
/ 860 - Portuguese
|
||||
/ 861 - Icelandic
|
||||
/ 862 - Hebrew
|
||||
/ 863 - Canadian French
|
||||
/ 864 - Arabic
|
||||
/ 865 - Nordic
|
||||
/ 866 - Russian
|
||||
/ 869 - Greek 2
|
||||
/ 932 - Japanese (DBCS)
|
||||
/ 936 - Simplified Chinese (DBCS)
|
||||
/ 949 - Korean (DBCS)
|
||||
/ 950 - Traditional Chinese (DBCS)
|
||||
/ 0 - Include all code pages above and configured by f_setcp()
|
||||
*/
|
||||
|
||||
|
||||
#define FF_USE_LFN 3
|
||||
#define FF_MAX_LFN 255
|
||||
/* The FF_USE_LFN switches the support for LFN (long file name).
|
||||
/
|
||||
/ 0: Disable LFN. FF_MAX_LFN has no effect.
|
||||
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
|
||||
/ 2: Enable LFN with dynamic working buffer on the STACK.
|
||||
/ 3: Enable LFN with dynamic working buffer on the HEAP.
|
||||
/
|
||||
/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN function
|
||||
/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
|
||||
/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
|
||||
/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
|
||||
/ be in range of 12 to 255. It is recommended to be set 255 to fully support LFN
|
||||
/ specification.
|
||||
/ When use stack for the working buffer, take care on stack overflow. When use heap
|
||||
/ memory for the working buffer, memory management functions, ff_memalloc() and
|
||||
/ ff_memfree() in ffsystem.c, need to be added to the project. */
|
||||
|
||||
|
||||
#define FF_LFN_UNICODE 0
|
||||
/* This option switches the character encoding on the API when LFN is enabled.
|
||||
/
|
||||
/ 0: ANSI/OEM in current CP (TCHAR = char)
|
||||
/ 1: Unicode in UTF-16 (TCHAR = WCHAR)
|
||||
/ 2: Unicode in UTF-8 (TCHAR = char)
|
||||
/ 3: Unicode in UTF-32 (TCHAR = DWORD)
|
||||
/
|
||||
/ Also behavior of string I/O functions will be affected by this option.
|
||||
/ When LFN is not enabled, this option has no effect. */
|
||||
|
||||
|
||||
#define FF_LFN_BUF 255
|
||||
#define FF_SFN_BUF 12
|
||||
/* This set of options defines size of file name members in the FILINFO structure
|
||||
/ which is used to read out directory items. These values should be suffcient for
|
||||
/ the file names to read. The maximum possible length of the read file name depends
|
||||
/ on character encoding. When LFN is not enabled, these options have no effect. */
|
||||
|
||||
|
||||
#define FF_STRF_ENCODE 0
|
||||
/* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, f_gets(),
|
||||
/ f_putc(), f_puts and f_printf() convert the character encoding in it.
|
||||
/ This option selects assumption of character encoding ON THE FILE to be
|
||||
/ read/written via those functions.
|
||||
/
|
||||
/ 0: ANSI/OEM in current CP
|
||||
/ 1: Unicode in UTF-16LE
|
||||
/ 2: Unicode in UTF-16BE
|
||||
/ 3: Unicode in UTF-8
|
||||
*/
|
||||
|
||||
|
||||
#define FF_FS_RPATH 0
|
||||
/* This option configures support for relative path.
|
||||
/
|
||||
/ 0: Disable relative path and remove related functions.
|
||||
/ 1: Enable relative path. f_chdir() and f_chdrive() are available.
|
||||
/ 2: f_getcwd() function is available in addition to 1.
|
||||
*/
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Drive/Volume Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_VOLUMES 4
|
||||
/* Number of volumes (logical drives) to be used. (1-10) */
|
||||
|
||||
|
||||
#define FF_STR_VOLUME_ID 1
|
||||
// Order is important. Any change to order, must also be reflected to diskio drive enum.
|
||||
#define FF_VOLUME_STRS "sd","ram","emmc","bis"
|
||||
/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
|
||||
/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
|
||||
/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
|
||||
/ logical drives. Number of items must not be less than FF_VOLUMES. Valid
|
||||
/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
|
||||
/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
|
||||
/ not defined, a user defined volume string table needs to be defined as:
|
||||
/
|
||||
/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
|
||||
*/
|
||||
|
||||
|
||||
#define FF_MULTI_PARTITION 0
|
||||
/* This option switches support for multiple volumes on the physical drive.
|
||||
/ By default (0), each logical drive number is bound to the same physical drive
|
||||
/ number and only an FAT volume found on the physical drive will be mounted.
|
||||
/ When this function is enabled (1), each logical drive number can be bound to
|
||||
/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
|
||||
/ funciton will be available. */
|
||||
|
||||
|
||||
#define FF_MIN_SS 512
|
||||
#define FF_MAX_SS 512
|
||||
/* This set of options configures the range of sector size to be supported. (512,
|
||||
/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
|
||||
/ harddisk. But a larger value may be required for on-board flash memory and some
|
||||
/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured
|
||||
/ for variable sector size mode and disk_ioctl() function needs to implement
|
||||
/ GET_SECTOR_SIZE command. */
|
||||
|
||||
|
||||
#define FF_USE_TRIM 0
|
||||
/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
|
||||
/ To enable Trim function, also CTRL_TRIM command should be implemented to the
|
||||
/ disk_ioctl() function. */
|
||||
|
||||
|
||||
#define FF_FS_NOFSINFO 1
|
||||
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
|
||||
/ option, and f_getfree() function at first time after volume mount will force
|
||||
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
|
||||
/
|
||||
/ bit0=0: Use free cluster count in the FSINFO if available.
|
||||
/ bit0=1: Do not trust free cluster count in the FSINFO.
|
||||
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
|
||||
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ System Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_FS_TINY 0
|
||||
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
|
||||
/ At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes.
|
||||
/ Instead of private sector buffer eliminated from the file object, common sector
|
||||
/ buffer in the filesystem object (FATFS) is used for the file data transfer. */
|
||||
|
||||
|
||||
#define FF_FS_EXFAT 1
|
||||
/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
|
||||
/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
|
||||
/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
|
||||
|
||||
|
||||
#define FF_FS_NORTC 1
|
||||
#define FF_NORTC_MON 1
|
||||
#define FF_NORTC_MDAY 1
|
||||
#define FF_NORTC_YEAR 2021
|
||||
/* The option FF_FS_NORTC switches timestamp function. If the system does not have
|
||||
/ any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable
|
||||
/ the timestamp function. Every object modified by FatFs will have a fixed timestamp
|
||||
/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
|
||||
/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
|
||||
/ added to the project to read current time form real-time clock. FF_NORTC_MON,
|
||||
/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
|
||||
/ These options have no effect at read-only configuration (FF_FS_READONLY = 1). */
|
||||
|
||||
|
||||
#define FF_FS_LOCK 0
|
||||
/* The option FF_FS_LOCK switches file lock function to control duplicated file open
|
||||
/ and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
|
||||
/ is 1.
|
||||
/
|
||||
/ 0: Disable file lock function. To avoid volume corruption, application program
|
||||
/ should avoid illegal open, remove and rename to the open objects.
|
||||
/ >0: Enable file lock function. The value defines how many files/sub-directories
|
||||
/ can be opened simultaneously under file lock control. Note that the file
|
||||
/ lock control is independent of re-entrancy. */
|
||||
|
||||
|
||||
/* #include <somertos.h> // O/S definitions */
|
||||
#define FF_FS_REENTRANT 0
|
||||
#define FF_FS_TIMEOUT 1000
|
||||
#define FF_SYNC_t HANDLE
|
||||
/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
|
||||
/ module itself. Note that regardless of this option, file access to different
|
||||
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
|
||||
/ and f_fdisk() function, are always not re-entrant. Only file/directory access
|
||||
/ to the same volume is under control of this function.
|
||||
/
|
||||
/ 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect.
|
||||
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
|
||||
/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
|
||||
/ function, must be added to the project. Samples are available in
|
||||
/ option/syscall.c.
|
||||
/
|
||||
/ The FF_FS_TIMEOUT defines timeout period in unit of time tick.
|
||||
/ The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
|
||||
/ SemaphoreHandle_t and etc. A header file for O/S definitions needs to be
|
||||
/ included somewhere in the scope of ff.h. */
|
||||
|
||||
|
||||
|
||||
/*--- End of configuration options ---*/
|
39
source/libs/fatfs/ffsystem.c
Normal file
39
source/libs/fatfs/ffsystem.c
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*------------------------------------------------------------------------*/
|
||||
/* Sample Code of OS Dependent Functions for FatFs */
|
||||
/* (C) ChaN, 2018 */
|
||||
/* (C) CTCaer, 2018 */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include <mem/heap.h>
|
||||
|
||||
|
||||
|
||||
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Allocate a memory block */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void* ff_memalloc ( /* Returns pointer to the allocated memory block (null if not enough core) */
|
||||
UINT msize /* Number of bytes to allocate */
|
||||
)
|
||||
{
|
||||
return malloc(msize); /* Allocate a new memory block with POSIX API */
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Free a memory block */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void ff_memfree (
|
||||
void* mblock /* Pointer to the memory block to free (nothing to do if null) */
|
||||
)
|
||||
{
|
||||
free(mblock); /* Free the memory block with POSIX API */
|
||||
}
|
||||
|
||||
#endif
|
||||
|
25
source/link.ld
Normal file
25
source/link.ld
Normal file
|
@ -0,0 +1,25 @@
|
|||
ENTRY(_start)
|
||||
|
||||
SECTIONS {
|
||||
PROVIDE(__ipl_start = IPL_LOAD_ADDR);
|
||||
. = __ipl_start;
|
||||
.text : {
|
||||
*(.text._start);
|
||||
KEEP(*(._boot_cfg));
|
||||
KEEP(*(._ipl_version));
|
||||
*(.text._irq_setup);
|
||||
*(.text*);
|
||||
}
|
||||
.data : {
|
||||
*(.data*);
|
||||
*(.rodata*);
|
||||
}
|
||||
. = ALIGN(0x10);
|
||||
__ipl_end = .;
|
||||
.bss : {
|
||||
__bss_start = .;
|
||||
*(COMMON)
|
||||
*(.bss*)
|
||||
__bss_end = .;
|
||||
}
|
||||
}
|
464
source/main.c
Normal file
464
source/main.c
Normal file
|
@ -0,0 +1,464 @@
|
|||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
*
|
||||
* Copyright (c) 2018-2021 CTCaer
|
||||
* Copyright (c) 2019-2021 shchmue
|
||||
*
|
||||
* 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 "config.h"
|
||||
#include <display/di.h>
|
||||
#include <gfx_utils.h>
|
||||
#include "gfx/tui.h"
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include <mem/heap.h>
|
||||
#include <mem/minerva.h>
|
||||
#include <power/bq24193.h>
|
||||
#include <power/max17050.h>
|
||||
#include <power/max77620.h>
|
||||
#include <rtc/max77620-rtc.h>
|
||||
#include <soc/bpmp.h>
|
||||
#include <soc/hw_init.h>
|
||||
#include "storage/emummc.h"
|
||||
#include "storage/nx_emmc.h"
|
||||
#include <storage/nx_sd.h>
|
||||
#include <storage/sdmmc.h>
|
||||
#include <utils/btn.h>
|
||||
#include <utils/dirlist.h>
|
||||
#include <utils/ini.h>
|
||||
#include <utils/sprintf.h>
|
||||
#include <utils/util.h>
|
||||
|
||||
#include "keys/keys.h"
|
||||
|
||||
hekate_config h_cfg;
|
||||
boot_cfg_t __attribute__((section ("._boot_cfg"))) b_cfg;
|
||||
const volatile ipl_ver_meta_t __attribute__((section ("._ipl_version"))) ipl_ver = {
|
||||
.magic = LP_MAGIC,
|
||||
.version = (LP_VER_MJ + '0') | ((LP_VER_MN + '0') << 8) | ((LP_VER_BF + '0') << 16),
|
||||
.rsvd0 = 0,
|
||||
.rsvd1 = 0
|
||||
};
|
||||
|
||||
volatile nyx_storage_t *nyx_str = (nyx_storage_t *)NYX_STORAGE_ADDR;
|
||||
|
||||
// This is a safe and unused DRAM region for our payloads.
|
||||
#define RELOC_META_OFF 0x7C
|
||||
#define PATCHED_RELOC_SZ 0x94
|
||||
#define PATCHED_RELOC_STACK 0x40007000
|
||||
#define PATCHED_RELOC_ENTRY 0x40010000
|
||||
#define EXT_PAYLOAD_ADDR 0xC0000000
|
||||
#define RCM_PAYLOAD_ADDR (EXT_PAYLOAD_ADDR + ALIGN(PATCHED_RELOC_SZ, 0x10))
|
||||
#define COREBOOT_END_ADDR 0xD0000000
|
||||
#define COREBOOT_VER_OFF 0x41
|
||||
#define CBFS_DRAM_EN_ADDR 0x4003e000
|
||||
#define CBFS_DRAM_MAGIC 0x4452414D // "DRAM"
|
||||
|
||||
static void *coreboot_addr;
|
||||
|
||||
void reloc_patcher(u32 payload_dst, u32 payload_src, u32 payload_size)
|
||||
{
|
||||
memcpy((u8 *)payload_src, (u8 *)IPL_LOAD_ADDR, PATCHED_RELOC_SZ);
|
||||
|
||||
volatile reloc_meta_t *relocator = (reloc_meta_t *)(payload_src + RELOC_META_OFF);
|
||||
|
||||
relocator->start = payload_dst - ALIGN(PATCHED_RELOC_SZ, 0x10);
|
||||
relocator->stack = PATCHED_RELOC_STACK;
|
||||
relocator->end = payload_dst + payload_size;
|
||||
relocator->ep = payload_dst;
|
||||
|
||||
if (payload_size == 0x7000)
|
||||
{
|
||||
memcpy((u8 *)(payload_src + ALIGN(PATCHED_RELOC_SZ, 0x10)), coreboot_addr, 0x7000); //Bootblock
|
||||
*(vu32 *)CBFS_DRAM_EN_ADDR = CBFS_DRAM_MAGIC;
|
||||
}
|
||||
}
|
||||
|
||||
int launch_payload(char *path, bool clear_screen)
|
||||
{
|
||||
if (clear_screen)
|
||||
gfx_clear_grey(0x1B);
|
||||
gfx_con_setpos(0, 0);
|
||||
if (!path)
|
||||
return 1;
|
||||
|
||||
if (sd_mount())
|
||||
{
|
||||
FIL fp;
|
||||
if (f_open(&fp, path, FA_READ))
|
||||
{
|
||||
gfx_con.mute = false;
|
||||
EPRINTFARGS("Payload file is missing!\n(%s)", path);
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
// Read and copy the payload to our chosen address
|
||||
void *buf;
|
||||
u32 size = f_size(&fp);
|
||||
|
||||
if (size < 0x30000)
|
||||
buf = (void *)RCM_PAYLOAD_ADDR;
|
||||
else
|
||||
{
|
||||
coreboot_addr = (void *)(COREBOOT_END_ADDR - size);
|
||||
buf = coreboot_addr;
|
||||
if (h_cfg.t210b01)
|
||||
{
|
||||
f_close(&fp);
|
||||
|
||||
gfx_con.mute = false;
|
||||
EPRINTF("Coreboot not allowed on Mariko!");
|
||||
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (f_read(&fp, buf, size, NULL))
|
||||
{
|
||||
f_close(&fp);
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
f_close(&fp);
|
||||
|
||||
sd_end();
|
||||
|
||||
if (size < 0x30000)
|
||||
{
|
||||
reloc_patcher(PATCHED_RELOC_ENTRY, EXT_PAYLOAD_ADDR, ALIGN(size, 0x10));
|
||||
|
||||
hw_reinit_workaround(false, byte_swap_32(*(u32 *)(buf + size - sizeof(u32))));
|
||||
}
|
||||
else
|
||||
{
|
||||
reloc_patcher(PATCHED_RELOC_ENTRY, EXT_PAYLOAD_ADDR, 0x7000);
|
||||
|
||||
// Get coreboot seamless display magic.
|
||||
u32 magic = 0;
|
||||
char *magic_ptr = buf + COREBOOT_VER_OFF;
|
||||
memcpy(&magic, magic_ptr + strlen(magic_ptr) - 4, 4);
|
||||
hw_reinit_workaround(true, magic);
|
||||
}
|
||||
|
||||
// Some cards (Sandisk U1), do not like a fast power cycle. Wait min 100ms.
|
||||
sdmmc_storage_init_wait_sd();
|
||||
|
||||
void (*ext_payload_ptr)() = (void *)EXT_PAYLOAD_ADDR;
|
||||
|
||||
// Launch our payload.
|
||||
(*ext_payload_ptr)();
|
||||
}
|
||||
|
||||
out:
|
||||
sd_end();
|
||||
return 1;
|
||||
}
|
||||
|
||||
void launch_tools()
|
||||
{
|
||||
u8 max_entries = 61;
|
||||
char *filelist = NULL;
|
||||
char *file_sec = NULL;
|
||||
char *dir = NULL;
|
||||
|
||||
ment_t *ments = (ment_t *)malloc(sizeof(ment_t) * (max_entries + 3));
|
||||
|
||||
gfx_clear_grey(0x1B);
|
||||
gfx_con_setpos(0, 0);
|
||||
|
||||
if (sd_mount())
|
||||
{
|
||||
dir = (char *)malloc(256);
|
||||
|
||||
memcpy(dir, "sd:/bootloader/payloads", 24);
|
||||
|
||||
filelist = dirlist(dir, NULL, false, false);
|
||||
|
||||
u32 i = 0;
|
||||
u32 i_off = 2;
|
||||
|
||||
if (filelist)
|
||||
{
|
||||
// Build configuration menu.
|
||||
u32 color_idx = 0;
|
||||
|
||||
ments[0].type = MENT_BACK;
|
||||
ments[0].caption = "Back";
|
||||
ments[0].color = colors[(color_idx++) % 6];
|
||||
ments[1].type = MENT_CHGLINE;
|
||||
ments[1].color = colors[(color_idx++) % 6];
|
||||
if (!f_stat("sd:/atmosphere/reboot_payload.bin", NULL))
|
||||
{
|
||||
ments[i_off].type = INI_CHOICE;
|
||||
ments[i_off].caption = "reboot_payload.bin";
|
||||
ments[i_off].color = colors[(color_idx++) % 6];
|
||||
ments[i_off].data = "sd:/atmosphere/reboot_payload.bin";
|
||||
i_off++;
|
||||
}
|
||||
if (!f_stat("sd:/ReiNX.bin", NULL))
|
||||
{
|
||||
ments[i_off].type = INI_CHOICE;
|
||||
ments[i_off].caption = "ReiNX.bin";
|
||||
ments[i_off].color = colors[(color_idx++) % 6];
|
||||
ments[i_off].data = "sd:/ReiNX.bin";
|
||||
i_off++;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (i > max_entries || !filelist[i * 256])
|
||||
break;
|
||||
ments[i + i_off].type = INI_CHOICE;
|
||||
ments[i + i_off].caption = &filelist[i * 256];
|
||||
ments[i + i_off].color = colors[(color_idx++) % 6];
|
||||
ments[i + i_off].data = &filelist[i * 256];
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
memset(&ments[i + i_off], 0, sizeof(ment_t));
|
||||
menu_t menu = { ments, "Choose a file to launch", 0, 0 };
|
||||
|
||||
file_sec = (char *)tui_do_menu(&menu);
|
||||
|
||||
if (!file_sec)
|
||||
{
|
||||
free(ments);
|
||||
free(dir);
|
||||
free(filelist);
|
||||
sd_end();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
EPRINTF("No payloads or modules found.");
|
||||
|
||||
free(ments);
|
||||
free(filelist);
|
||||
}
|
||||
else
|
||||
{
|
||||
free(ments);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (file_sec)
|
||||
{
|
||||
if (memcmp("sd:/", file_sec, 4) != 0)
|
||||
{
|
||||
memcpy(dir + strlen(dir), "/", 2);
|
||||
memcpy(dir + strlen(dir), file_sec, strlen(file_sec) + 1);
|
||||
}
|
||||
else
|
||||
memcpy(dir, file_sec, strlen(file_sec) + 1);
|
||||
|
||||
launch_payload(dir, true);
|
||||
EPRINTF("Failed to launch payload.");
|
||||
}
|
||||
|
||||
out:
|
||||
sd_end();
|
||||
free(dir);
|
||||
|
||||
btn_wait();
|
||||
}
|
||||
|
||||
void launch_hekate()
|
||||
{
|
||||
sd_mount();
|
||||
if (!f_stat("bootloader/update.bin", NULL))
|
||||
launch_payload("bootloader/update.bin", false);
|
||||
}
|
||||
|
||||
void dump_sysnand()
|
||||
{
|
||||
h_cfg.emummc_force_disable = true;
|
||||
emu_cfg.enabled = false;
|
||||
dump_keys();
|
||||
}
|
||||
|
||||
void dump_emunand()
|
||||
{
|
||||
if (h_cfg.emummc_force_disable)
|
||||
return;
|
||||
emu_cfg.enabled = true;
|
||||
dump_keys();
|
||||
}
|
||||
|
||||
void dump_amiibo_keys()
|
||||
{
|
||||
derive_amiibo_keys();
|
||||
}
|
||||
|
||||
void dump_mariko_partial_keys();
|
||||
|
||||
ment_t ment_partials[] = {
|
||||
MDEF_BACK(colors[0]),
|
||||
MDEF_CHGLINE(),
|
||||
MDEF_CAPTION("This dumps the results of writing zeros", colors[1]),
|
||||
MDEF_CAPTION("over consecutive 32-bit portions of each", colors[1]),
|
||||
MDEF_CAPTION("keyslot, the results of which can then", colors[1]),
|
||||
MDEF_CAPTION("be bruteforced quickly on a computer", colors[1]),
|
||||
MDEF_CAPTION("to recover keys from unreadable keyslots.", colors[1]),
|
||||
MDEF_CHGLINE(),
|
||||
MDEF_CAPTION("This includes the Mariko KEK and BEK", colors[2]),
|
||||
MDEF_CAPTION("as well as the unique SBK.", colors[2]),
|
||||
MDEF_CHGLINE(),
|
||||
MDEF_CAPTION("These are not useful for most users", colors[3]),
|
||||
MDEF_CAPTION("but are included for archival purposes.", colors[3]),
|
||||
MDEF_CHGLINE(),
|
||||
MDEF_CAPTION("Warning: this wipes keyslots!", colors[4]),
|
||||
MDEF_CAPTION("The console must be completely restarted!", colors[4]),
|
||||
MDEF_CAPTION("Modchip must run again to fix the keys!", colors[4]),
|
||||
MDEF_CAPTION("---------------", colors[5]),
|
||||
MDEF_HANDLER("Dump Mariko Partials", dump_mariko_partial_keys, colors[0]),
|
||||
MDEF_END()
|
||||
};
|
||||
|
||||
menu_t menu_partials = { ment_partials, NULL, 0, 0 };
|
||||
|
||||
power_state_t STATE_POWER_OFF = POWER_OFF_RESET;
|
||||
power_state_t STATE_REBOOT_FULL = POWER_OFF_REBOOT;
|
||||
power_state_t STATE_REBOOT_RCM = REBOOT_RCM;
|
||||
power_state_t STATE_REBOOT_BYPASS_FUSES = REBOOT_BYPASS_FUSES;
|
||||
|
||||
ment_t ment_top[] = {
|
||||
MDEF_HANDLER("Dump from SysNAND", dump_sysnand, colors[0]),
|
||||
MDEF_HANDLER("Dump from EmuNAND", dump_emunand, colors[1]),
|
||||
MDEF_CAPTION("---------------", colors[2]),
|
||||
MDEF_HANDLER("Dump Amiibo Keys", dump_amiibo_keys, colors[3]),
|
||||
MDEF_MENU("Dump Mariko Partials (requires reboot)", &menu_partials, colors[4]),
|
||||
MDEF_CAPTION("---------------", colors[5]),
|
||||
MDEF_HANDLER("Payloads...", launch_tools, colors[0]),
|
||||
MDEF_HANDLER("Reboot to hekate", launch_hekate, colors[1]),
|
||||
MDEF_CAPTION("---------------", colors[2]),
|
||||
MDEF_HANDLER_EX("Reboot (OFW)", &STATE_REBOOT_BYPASS_FUSES, power_set_state_ex, colors[3]),
|
||||
MDEF_HANDLER_EX("Reboot (RCM)", &STATE_REBOOT_RCM, power_set_state_ex, colors[4]),
|
||||
MDEF_HANDLER_EX("Power off", &STATE_POWER_OFF, power_set_state_ex, colors[5]),
|
||||
MDEF_END()
|
||||
};
|
||||
|
||||
menu_t menu_top = { ment_top, NULL, 0, 0 };
|
||||
|
||||
void grey_out_menu_item(ment_t *menu)
|
||||
{
|
||||
menu->type = MENT_CAPTION;
|
||||
menu->color = 0xFF555555;
|
||||
menu->handler = NULL;
|
||||
}
|
||||
|
||||
void dump_mariko_partial_keys()
|
||||
{
|
||||
if (h_cfg.t210b01) {
|
||||
int res = save_mariko_partial_keys(0, 16, false);
|
||||
if (res == 0 || res == 3)
|
||||
{
|
||||
// Grey out dumping menu items as the keyslots have been invalidated.
|
||||
grey_out_menu_item(&ment_top[0]);
|
||||
grey_out_menu_item(&ment_top[1]);
|
||||
grey_out_menu_item(&ment_top[4]);
|
||||
grey_out_menu_item(&ment_partials[18]);
|
||||
}
|
||||
|
||||
gfx_printf("\n%kPress a button to return to the menu.", COLOR_ORANGE);
|
||||
btn_wait();
|
||||
}
|
||||
}
|
||||
|
||||
extern void pivot_stack(u32 stack_top);
|
||||
|
||||
void ipl_main()
|
||||
{
|
||||
// Do initial HW configuration. This is compatible with consecutive reruns without a reset.
|
||||
hw_init();
|
||||
|
||||
// Pivot the stack so we have enough space.
|
||||
pivot_stack(IPL_STACK_TOP);
|
||||
|
||||
// Tegra/Horizon configuration goes to 0x80000000+, package2 goes to 0xA9800000, we place our heap in between.
|
||||
heap_init(IPL_HEAP_START);
|
||||
|
||||
#ifdef DEBUG_UART_PORT
|
||||
uart_send(DEBUG_UART_PORT, (u8 *)"hekate: Hello!\r\n", 16);
|
||||
uart_wait_idle(DEBUG_UART_PORT, UART_TX_IDLE);
|
||||
#endif
|
||||
|
||||
// Set bootloader's default configuration.
|
||||
set_default_configuration();
|
||||
|
||||
// Mount SD Card.
|
||||
h_cfg.errors |= !sd_mount() ? ERR_SD_BOOT_EN : 0;
|
||||
|
||||
// Train DRAM and switch to max frequency.
|
||||
if (minerva_init()) //!TODO: Add Tegra210B01 support to minerva.
|
||||
h_cfg.errors |= ERR_LIBSYS_MTC;
|
||||
|
||||
display_init();
|
||||
|
||||
u32 *fb = display_init_framebuffer_pitch();
|
||||
gfx_init_ctxt(fb, 720, 1280, 720);
|
||||
|
||||
gfx_con_init();
|
||||
|
||||
display_backlight_pwm_init();
|
||||
|
||||
// Overclock BPMP.
|
||||
bpmp_clk_rate_set(h_cfg.t210b01 ? BPMP_CLK_DEFAULT_BOOST : BPMP_CLK_LOWER_BOOST);
|
||||
|
||||
// Load emuMMC configuration from SD.
|
||||
emummc_load_cfg();
|
||||
// Ignore whether emummc is enabled.
|
||||
h_cfg.emummc_force_disable = emu_cfg.sector == 0 && !emu_cfg.path;
|
||||
emu_cfg.enabled = !h_cfg.emummc_force_disable;
|
||||
|
||||
// Grey out emummc option if not present.
|
||||
if (h_cfg.emummc_force_disable)
|
||||
{
|
||||
grey_out_menu_item(&ment_top[1]);
|
||||
}
|
||||
|
||||
// Grey out reboot to RCM option if on Mariko or patched console.
|
||||
if (h_cfg.t210b01 || h_cfg.rcm_patched)
|
||||
{
|
||||
grey_out_menu_item(&ment_top[10]);
|
||||
}
|
||||
|
||||
// Grey out Mariko partial dump option on Erista.
|
||||
if (!h_cfg.t210b01) {
|
||||
grey_out_menu_item(&ment_top[4]);
|
||||
}
|
||||
|
||||
// Grey out reboot to hekate option if no update.bin found.
|
||||
if (f_stat("bootloader/update.bin", NULL))
|
||||
{
|
||||
grey_out_menu_item(&ment_top[7]);
|
||||
}
|
||||
|
||||
minerva_change_freq(FREQ_800);
|
||||
|
||||
while (true)
|
||||
tui_do_menu(&menu_top);
|
||||
|
||||
// Halt BPMP if we managed to get out of execution.
|
||||
while (true)
|
||||
bpmp_halt();
|
||||
}
|
77
source/start.S
Normal file
77
source/start.S
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
.section .text._start
|
||||
.arm
|
||||
|
||||
.extern _reloc_ipl
|
||||
.type _reloc_ipl, %function
|
||||
|
||||
.extern memset
|
||||
.type memset, %function
|
||||
|
||||
.extern _irq_setup
|
||||
.type _irq_setup, %function
|
||||
|
||||
.globl _start
|
||||
.type _start, %function
|
||||
_start:
|
||||
ADR R0, _start
|
||||
LDR R1, =__ipl_start
|
||||
CMP R0, R1
|
||||
BEQ _real_start
|
||||
|
||||
/* If we are not in the right location already, copy a relocator to upper IRAM. */
|
||||
ADR R2, _reloc_ipl
|
||||
LDR R3, =0x4003FF00
|
||||
MOV R4, #(_real_start - _reloc_ipl)
|
||||
_copy_loop:
|
||||
LDMIA R2!, {R5}
|
||||
STMIA R3!, {R5}
|
||||
SUBS R4, #4
|
||||
BNE _copy_loop
|
||||
|
||||
/* Use the relocator to copy ourselves into the right place. */
|
||||
LDR R2, =__ipl_end
|
||||
SUB R2, R2, R1
|
||||
LDR R3, =_real_start
|
||||
LDR R4, =0x4003FF00
|
||||
BX R4
|
||||
|
||||
_reloc_ipl:
|
||||
LDMIA R0!, {R4-R7}
|
||||
STMIA R1!, {R4-R7}
|
||||
SUBS R2, #0x10
|
||||
BNE _reloc_ipl
|
||||
/* Jump to the relocated entry. */
|
||||
BX R3
|
||||
|
||||
_real_start:
|
||||
/* Initially, we place our stack in IRAM but will move it to SDRAM later. */
|
||||
LDR SP, =0x4003FF00
|
||||
LDR R0, =__bss_start
|
||||
EOR R1, R1, R1
|
||||
LDR R2, =__bss_end
|
||||
SUB R2, R2, R0
|
||||
BL memset
|
||||
BL _irq_setup
|
||||
B .
|
||||
|
||||
.globl pivot_stack
|
||||
.type pivot_stack, %function
|
||||
pivot_stack:
|
||||
MOV SP, R0
|
||||
BX LR
|
308
source/storage/emummc.c
Normal file
308
source/storage/emummc.c
Normal file
|
@ -0,0 +1,308 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2021 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 <stdlib.h>
|
||||
|
||||
#include "emummc.h"
|
||||
#include <storage/sdmmc.h>
|
||||
#include "../config.h"
|
||||
#include <utils/ini.h>
|
||||
#include <gfx_utils.h>
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include <mem/heap.h>
|
||||
#include "../storage/nx_emmc.h"
|
||||
#include <storage/nx_sd.h>
|
||||
#include <utils/list.h>
|
||||
#include <utils/types.h>
|
||||
|
||||
extern hekate_config h_cfg;
|
||||
emummc_cfg_t emu_cfg = { 0 };
|
||||
|
||||
void emummc_load_cfg()
|
||||
{
|
||||
emu_cfg.enabled = 0;
|
||||
emu_cfg.path = NULL;
|
||||
emu_cfg.sector = 0;
|
||||
emu_cfg.id = 0;
|
||||
emu_cfg.file_based_part_size = 0;
|
||||
emu_cfg.active_part = 0;
|
||||
emu_cfg.fs_ver = 0;
|
||||
if (!emu_cfg.nintendo_path)
|
||||
emu_cfg.nintendo_path = (char *)malloc(0x200);
|
||||
if (!emu_cfg.emummc_file_based_path)
|
||||
emu_cfg.emummc_file_based_path = (char *)malloc(0x200);
|
||||
|
||||
emu_cfg.nintendo_path[0] = 0;
|
||||
emu_cfg.emummc_file_based_path[0] = 0;
|
||||
|
||||
LIST_INIT(ini_sections);
|
||||
if (ini_parse(&ini_sections, "emuMMC/emummc.ini", false))
|
||||
{
|
||||
LIST_FOREACH_ENTRY(ini_sec_t, ini_sec, &ini_sections, link)
|
||||
{
|
||||
if (ini_sec->type == INI_CHOICE)
|
||||
{
|
||||
if (strcmp(ini_sec->name, "emummc"))
|
||||
continue;
|
||||
|
||||
LIST_FOREACH_ENTRY(ini_kv_t, kv, &ini_sec->kvs, link)
|
||||
{
|
||||
if (!strcmp("enabled", kv->key))
|
||||
emu_cfg.enabled = atoi(kv->val);
|
||||
else if (!strcmp("sector", kv->key))
|
||||
emu_cfg.sector = strtol(kv->val, NULL, 16);
|
||||
else if (!strcmp("id", kv->key))
|
||||
emu_cfg.id = strtol(kv->val, NULL, 16);
|
||||
else if (!strcmp("path", kv->key))
|
||||
emu_cfg.path = kv->val;
|
||||
else if (!strcmp("nintendo_path", kv->key))
|
||||
strcpy(emu_cfg.nintendo_path, kv->val);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool emummc_set_path(char *path)
|
||||
{
|
||||
FIL fp;
|
||||
bool found = false;
|
||||
|
||||
strcpy(emu_cfg.emummc_file_based_path, path);
|
||||
strcat(emu_cfg.emummc_file_based_path, "/raw_based");
|
||||
|
||||
if (!f_open(&fp, emu_cfg.emummc_file_based_path, FA_READ))
|
||||
{
|
||||
if (!f_read(&fp, &emu_cfg.sector, 4, NULL))
|
||||
if (emu_cfg.sector)
|
||||
found = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy(emu_cfg.emummc_file_based_path, path);
|
||||
strcat(emu_cfg.emummc_file_based_path, "/file_based");
|
||||
|
||||
if (!f_stat(emu_cfg.emummc_file_based_path, NULL))
|
||||
{
|
||||
emu_cfg.sector = 0;
|
||||
emu_cfg.path = path;
|
||||
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
emu_cfg.enabled = 1;
|
||||
|
||||
// Get ID from path.
|
||||
u32 id_from_path = 0;
|
||||
u32 path_size = strlen(path);
|
||||
if (path_size >= 4)
|
||||
memcpy(&id_from_path, path + path_size - 4, 4);
|
||||
emu_cfg.id = id_from_path;
|
||||
|
||||
strcpy(emu_cfg.nintendo_path, path);
|
||||
strcat(emu_cfg.nintendo_path, "/Nintendo");
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
static int emummc_raw_get_part_off(int part_idx)
|
||||
{
|
||||
switch (part_idx)
|
||||
{
|
||||
case 0:
|
||||
return 2;
|
||||
case 1:
|
||||
return 0;
|
||||
case 2:
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
int emummc_storage_init_mmc()
|
||||
{
|
||||
FILINFO fno;
|
||||
emu_cfg.active_part = 0;
|
||||
|
||||
// Always init eMMC even when in emuMMC. eMMC is needed from the emuMMC driver anyway.
|
||||
if (!sdmmc_storage_init_mmc(&emmc_storage, &emmc_sdmmc, SDMMC_BUS_WIDTH_8, SDHCI_TIMING_MMC_HS400))
|
||||
return 2;
|
||||
|
||||
if (!emu_cfg.enabled || h_cfg.emummc_force_disable)
|
||||
return 0;
|
||||
|
||||
if (!sd_mount())
|
||||
goto out;
|
||||
|
||||
if (!emu_cfg.sector)
|
||||
{
|
||||
strcpy(emu_cfg.emummc_file_based_path, emu_cfg.path);
|
||||
strcat(emu_cfg.emummc_file_based_path, "/eMMC");
|
||||
|
||||
if (f_stat(emu_cfg.emummc_file_based_path, &fno))
|
||||
{
|
||||
EPRINTF("Failed to open eMMC folder.");
|
||||
goto out;
|
||||
}
|
||||
f_chmod(emu_cfg.emummc_file_based_path, AM_ARC, AM_ARC);
|
||||
|
||||
strcat(emu_cfg.emummc_file_based_path, "/00");
|
||||
if (f_stat(emu_cfg.emummc_file_based_path, &fno))
|
||||
{
|
||||
EPRINTF("Failed to open emuMMC rawnand.");
|
||||
goto out;
|
||||
}
|
||||
emu_cfg.file_based_part_size = fno.fsize >> 9;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
return 1;
|
||||
}
|
||||
|
||||
int emummc_storage_end()
|
||||
{
|
||||
if (!emu_cfg.enabled || h_cfg.emummc_force_disable)
|
||||
sdmmc_storage_end(&emmc_storage);
|
||||
else
|
||||
sd_end();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int emummc_storage_read(u32 sector, u32 num_sectors, void *buf)
|
||||
{
|
||||
FIL fp;
|
||||
if (!emu_cfg.enabled || h_cfg.emummc_force_disable)
|
||||
return sdmmc_storage_read(&emmc_storage, sector, num_sectors, buf);
|
||||
else if (emu_cfg.sector)
|
||||
{
|
||||
sector += emu_cfg.sector;
|
||||
sector += emummc_raw_get_part_off(emu_cfg.active_part) * 0x2000;
|
||||
return sdmmc_storage_read(&sd_storage, sector, num_sectors, buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!emu_cfg.active_part)
|
||||
{
|
||||
u32 file_part = sector / emu_cfg.file_based_part_size;
|
||||
sector = sector % emu_cfg.file_based_part_size;
|
||||
if (file_part >= 10)
|
||||
itoa(file_part, emu_cfg.emummc_file_based_path + strlen(emu_cfg.emummc_file_based_path) - 2, 10);
|
||||
else
|
||||
{
|
||||
emu_cfg.emummc_file_based_path[strlen(emu_cfg.emummc_file_based_path) - 2] = '0';
|
||||
itoa(file_part, emu_cfg.emummc_file_based_path + strlen(emu_cfg.emummc_file_based_path) - 1, 10);
|
||||
}
|
||||
}
|
||||
if (f_open(&fp, emu_cfg.emummc_file_based_path, FA_READ))
|
||||
{
|
||||
EPRINTF("Failed to open emuMMC image.");
|
||||
return 0;
|
||||
}
|
||||
f_lseek(&fp, (u64)sector << 9);
|
||||
if (f_read(&fp, buf, (u64)num_sectors << 9, NULL))
|
||||
{
|
||||
EPRINTF("Failed to read emuMMC image.");
|
||||
f_close(&fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
f_close(&fp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int emummc_storage_write(u32 sector, u32 num_sectors, void *buf)
|
||||
{
|
||||
FIL fp;
|
||||
if (!emu_cfg.enabled || h_cfg.emummc_force_disable)
|
||||
return sdmmc_storage_write(&emmc_storage, sector, num_sectors, buf);
|
||||
else if (emu_cfg.sector)
|
||||
{
|
||||
sector += emu_cfg.sector;
|
||||
sector += emummc_raw_get_part_off(emu_cfg.active_part) * 0x2000;
|
||||
return sdmmc_storage_write(&sd_storage, sector, num_sectors, buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!emu_cfg.active_part)
|
||||
{
|
||||
u32 file_part = sector / emu_cfg.file_based_part_size;
|
||||
sector = sector % emu_cfg.file_based_part_size;
|
||||
if (file_part >= 10)
|
||||
itoa(file_part, emu_cfg.emummc_file_based_path + strlen(emu_cfg.emummc_file_based_path) - 2, 10);
|
||||
else
|
||||
{
|
||||
emu_cfg.emummc_file_based_path[strlen(emu_cfg.emummc_file_based_path) - 2] = '0';
|
||||
itoa(file_part, emu_cfg.emummc_file_based_path + strlen(emu_cfg.emummc_file_based_path) - 1, 10);
|
||||
}
|
||||
}
|
||||
|
||||
if (f_open(&fp, emu_cfg.emummc_file_based_path, FA_WRITE))
|
||||
return 0;
|
||||
|
||||
f_lseek(&fp, (u64)sector << 9);
|
||||
if (f_write(&fp, buf, (u64)num_sectors << 9, NULL))
|
||||
{
|
||||
f_close(&fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
f_close(&fp);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int emummc_storage_set_mmc_partition(u32 partition)
|
||||
{
|
||||
emu_cfg.active_part = partition;
|
||||
sdmmc_storage_set_mmc_partition(&emmc_storage, partition);
|
||||
|
||||
if (!emu_cfg.enabled || h_cfg.emummc_force_disable || emu_cfg.sector)
|
||||
return 1;
|
||||
else
|
||||
{
|
||||
strcpy(emu_cfg.emummc_file_based_path, emu_cfg.path);
|
||||
strcat(emu_cfg.emummc_file_based_path, "/eMMC");
|
||||
|
||||
switch (partition)
|
||||
{
|
||||
case 0:
|
||||
strcat(emu_cfg.emummc_file_based_path, "/00");
|
||||
break;
|
||||
case 1:
|
||||
strcat(emu_cfg.emummc_file_based_path, "/BOOT0");
|
||||
break;
|
||||
case 2:
|
||||
strcat(emu_cfg.emummc_file_based_path, "/BOOT1");
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
60
source/storage/emummc.h
Normal file
60
source/storage/emummc.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2021 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/>.
|
||||
*/
|
||||
|
||||
#ifndef EMUMMC_H
|
||||
#define EMUMMC_H
|
||||
|
||||
#include <storage/sdmmc.h>
|
||||
#include <utils/types.h>
|
||||
|
||||
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 struct _emummc_cfg_t
|
||||
{
|
||||
int enabled;
|
||||
u64 sector;
|
||||
u32 id;
|
||||
char *path;
|
||||
char *nintendo_path;
|
||||
// Internal.
|
||||
char *emummc_file_based_path;
|
||||
u32 file_based_part_size;
|
||||
u32 active_part;
|
||||
int fs_ver;
|
||||
} emummc_cfg_t;
|
||||
|
||||
extern emummc_cfg_t emu_cfg;
|
||||
|
||||
void emummc_load_cfg();
|
||||
bool emummc_set_path(char *path);
|
||||
int emummc_storage_init_mmc();
|
||||
int emummc_storage_end();
|
||||
int emummc_storage_read(u32 sector, u32 num_sectors, void *buf);
|
||||
int emummc_storage_write(u32 sector, u32 num_sectors, void *buf);
|
||||
int emummc_storage_set_mmc_partition(u32 partition);
|
||||
|
||||
#endif
|
110
source/storage/nx_emmc.c
Normal file
110
source/storage/nx_emmc.c
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
* Copyright (c) 2019-2021 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 "nx_emmc.h"
|
||||
#include "emummc.h"
|
||||
#include <mem/heap.h>
|
||||
#include <soc/fuse.h>
|
||||
#include <storage/mbr_gpt.h>
|
||||
#include <utils/list.h>
|
||||
|
||||
sdmmc_t emmc_sdmmc;
|
||||
sdmmc_storage_t emmc_storage;
|
||||
FATFS emmc_fs;
|
||||
|
||||
void nx_emmc_gpt_parse(link_t *gpt, sdmmc_storage_t *storage)
|
||||
{
|
||||
gpt_t *gpt_buf = (gpt_t *)calloc(NX_GPT_NUM_BLOCKS, NX_EMMC_BLOCKSIZE);
|
||||
|
||||
emummc_storage_read(NX_GPT_FIRST_LBA, NX_GPT_NUM_BLOCKS, gpt_buf);
|
||||
|
||||
// Check if no GPT or more than max allowed entries.
|
||||
if (memcmp(&gpt_buf->header.signature, "EFI PART", 8) || gpt_buf->header.num_part_ents > 128)
|
||||
goto out;
|
||||
|
||||
for (u32 i = 0; i < gpt_buf->header.num_part_ents; i++)
|
||||
{
|
||||
emmc_part_t *part = (emmc_part_t *)calloc(sizeof(emmc_part_t), 1);
|
||||
|
||||
if (gpt_buf->entries[i].lba_start < gpt_buf->header.first_use_lba)
|
||||
continue;
|
||||
|
||||
part->index = i;
|
||||
part->lba_start = gpt_buf->entries[i].lba_start;
|
||||
part->lba_end = gpt_buf->entries[i].lba_end;
|
||||
part->attrs = gpt_buf->entries[i].attrs;
|
||||
|
||||
// ASCII conversion. Copy only the LSByte of the UTF-16LE name.
|
||||
for (u32 j = 0; j < 36; j++)
|
||||
part->name[j] = gpt_buf->entries[i].name[j];
|
||||
part->name[35] = 0;
|
||||
|
||||
list_append(gpt, &part->link);
|
||||
}
|
||||
|
||||
out:
|
||||
free(gpt_buf);
|
||||
}
|
||||
|
||||
void nx_emmc_gpt_free(link_t *gpt)
|
||||
{
|
||||
LIST_FOREACH_SAFE(iter, gpt)
|
||||
free(CONTAINER_OF(iter, emmc_part_t, link));
|
||||
}
|
||||
|
||||
emmc_part_t *nx_emmc_part_find(link_t *gpt, const char *name)
|
||||
{
|
||||
LIST_FOREACH_ENTRY(emmc_part_t, part, gpt, link)
|
||||
if (!strcmp(part->name, name))
|
||||
return part;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int nx_emmc_part_read(sdmmc_storage_t *storage, emmc_part_t *part, u32 sector_off, u32 num_sectors, void *buf)
|
||||
{
|
||||
// The last LBA is inclusive.
|
||||
if (part->lba_start + sector_off > part->lba_end)
|
||||
return 0;
|
||||
|
||||
return emummc_storage_read(part->lba_start + sector_off, num_sectors, buf);
|
||||
}
|
||||
|
||||
int nx_emmc_part_write(sdmmc_storage_t *storage, emmc_part_t *part, u32 sector_off, u32 num_sectors, void *buf)
|
||||
{
|
||||
// The last LBA is inclusive.
|
||||
if (part->lba_start + sector_off > part->lba_end)
|
||||
return 0;
|
||||
|
||||
return emummc_storage_write(part->lba_start + sector_off, num_sectors, buf);
|
||||
}
|
||||
|
||||
void nx_emmc_get_autorcm_masks(u8 *mod0, u8 *mod1)
|
||||
{
|
||||
if (fuse_read_hw_state() == FUSE_NX_HW_STATE_PROD)
|
||||
{
|
||||
*mod0 = 0xF7;
|
||||
*mod1 = 0x86;
|
||||
}
|
||||
else
|
||||
{
|
||||
*mod0 = 0x37;
|
||||
*mod1 = 0x84;
|
||||
}
|
||||
}
|
52
source/storage/nx_emmc.h
Normal file
52
source/storage/nx_emmc.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef _NX_EMMC_H_
|
||||
#define _NX_EMMC_H_
|
||||
|
||||
#include <storage/sdmmc.h>
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include <utils/types.h>
|
||||
#include <utils/list.h>
|
||||
|
||||
#define NX_GPT_FIRST_LBA 1
|
||||
#define NX_GPT_NUM_BLOCKS 33
|
||||
#define NX_EMMC_BLOCKSIZE 512
|
||||
|
||||
typedef struct _emmc_part_t
|
||||
{
|
||||
u32 index;
|
||||
u32 lba_start;
|
||||
u32 lba_end;
|
||||
u64 attrs;
|
||||
char name[37];
|
||||
link_t link;
|
||||
} emmc_part_t;
|
||||
|
||||
extern sdmmc_t emmc_sdmmc;
|
||||
extern sdmmc_storage_t emmc_storage;
|
||||
extern FATFS emmc_fs;
|
||||
|
||||
void nx_emmc_gpt_parse(link_t *gpt, sdmmc_storage_t *storage);
|
||||
void nx_emmc_gpt_free(link_t *gpt);
|
||||
emmc_part_t *nx_emmc_part_find(link_t *gpt, const char *name);
|
||||
int nx_emmc_part_read(sdmmc_storage_t *storage, emmc_part_t *part, u32 sector_off, u32 num_sectors, void *buf);
|
||||
int nx_emmc_part_write(sdmmc_storage_t *storage, emmc_part_t *part, u32 sector_off, u32 num_sectors, void *buf);
|
||||
|
||||
void nx_emmc_get_autorcm_masks(u8 *mod0, u8 *mod1);
|
||||
|
||||
#endif
|
383
source/storage/nx_emmc_bis.c
Normal file
383
source/storage/nx_emmc_bis.c
Normal file
|
@ -0,0 +1,383 @@
|
|||
/*
|
||||
* eMMC BIS driver for Nintendo Switch
|
||||
*
|
||||
* Copyright (c) 2019 shchmue
|
||||
* 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 <memory_map.h>
|
||||
|
||||
#include <mem/heap.h>
|
||||
#include <sec/se.h>
|
||||
#include <sec/se_t210.h>
|
||||
#include "../storage/nx_emmc.h"
|
||||
#include "nx_emmc_bis.h"
|
||||
#include <storage/sdmmc.h>
|
||||
#include <utils/types.h>
|
||||
|
||||
#define MAX_CLUSTER_CACHE_ENTRIES 32768
|
||||
#define CLUSTER_LOOKUP_EMPTY_ENTRY 0xFFFFFFFF
|
||||
#define SECTORS_PER_CLUSTER 0x20
|
||||
|
||||
typedef struct _cluster_cache_t
|
||||
{
|
||||
u32 cluster_num; // index of the cluster in the partition
|
||||
u32 visit_count; // used for debugging/access analysis
|
||||
u8 dirty; // has been modified without writeback flag
|
||||
u8 align[7];
|
||||
u8 cluster[XTS_CLUSTER_SIZE]; // the cached cluster itself
|
||||
} cluster_cache_t;
|
||||
|
||||
typedef struct _bis_cache_t
|
||||
{
|
||||
u8 emmc_buffer[XTS_CLUSTER_SIZE];
|
||||
cluster_cache_t cluster_cache[];
|
||||
} bis_cache_t;
|
||||
|
||||
static u8 ks_crypt = 0;
|
||||
static u8 ks_tweak = 0;
|
||||
static u8 cache_filled = 0;
|
||||
static u32 dirty_cluster_count = 0;
|
||||
static u32 cluster_cache_end_index = 0;
|
||||
static emmc_part_t *system_part = NULL;
|
||||
static bis_cache_t *bis_cache = (bis_cache_t *)NX_BIS_CACHE_ADDR;
|
||||
static u32 *cluster_lookup_buf = NULL;
|
||||
static u32 *cluster_lookup = NULL;
|
||||
static bool lock_cluster_cache = false;
|
||||
|
||||
static void _gf256_mul_x_le(void *block)
|
||||
{
|
||||
u32 *pdata = (u32 *)block;
|
||||
u32 carry = 0;
|
||||
|
||||
for (u32 i = 0; i < 4; i++)
|
||||
{
|
||||
u32 b = pdata[i];
|
||||
pdata[i] = (b << 1) | carry;
|
||||
carry = b >> 31;
|
||||
}
|
||||
|
||||
if (carry)
|
||||
pdata[0x0] ^= 0x87;
|
||||
}
|
||||
|
||||
static int _nx_aes_xts_crypt_sec(u32 tweak_ks, u32 crypt_ks, u32 enc, u8 *tweak, bool regen_tweak, u32 tweak_exp, u32 sec, void *dst, const void *src, u32 sec_size)
|
||||
{
|
||||
u32 *pdst = (u32 *)dst;
|
||||
u32 *psrc = (u32 *)src;
|
||||
u32 *ptweak = (u32 *)tweak;
|
||||
|
||||
if (regen_tweak)
|
||||
{
|
||||
for (int i = 0xF; i >= 0; i--)
|
||||
{
|
||||
tweak[i] = sec & 0xFF;
|
||||
sec >>= 8;
|
||||
}
|
||||
if (!se_aes_crypt_block_ecb(tweak_ks, 1, tweak, tweak))
|
||||
return 0;
|
||||
}
|
||||
|
||||
// tweak_exp allows us to use a saved tweak to reduce _gf256_mul_x_le calls.
|
||||
for (u32 i = 0; i < (tweak_exp << 5); i++)
|
||||
_gf256_mul_x_le(tweak);
|
||||
|
||||
u8 orig_tweak[0x10] __attribute__((aligned(4)));
|
||||
memcpy(orig_tweak, tweak, 0x10);
|
||||
|
||||
// We are assuming a 0x10-aligned sector size in this implementation.
|
||||
for (u32 i = 0; i < (sec_size >> 4); i++)
|
||||
{
|
||||
for (u32 j = 0; j < 4; j++)
|
||||
pdst[j] = psrc[j] ^ ptweak[j];
|
||||
|
||||
_gf256_mul_x_le(tweak);
|
||||
psrc += 4;
|
||||
pdst += 4;
|
||||
}
|
||||
|
||||
if (!se_aes_crypt_ecb(crypt_ks, enc, dst, sec_size, dst, sec_size))
|
||||
return 0;
|
||||
|
||||
pdst = (u32 *)dst;
|
||||
ptweak = (u32 *)orig_tweak;
|
||||
for (u32 i = 0; i < (sec_size >> 4); i++)
|
||||
{
|
||||
for (u32 j = 0; j < 4; j++)
|
||||
pdst[j] = pdst[j] ^ ptweak[j];
|
||||
|
||||
_gf256_mul_x_le(orig_tweak);
|
||||
pdst += 4;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nx_emmc_bis_write_block(u32 sector, u32 count, void *buff, bool force_flush)
|
||||
{
|
||||
if (!system_part)
|
||||
return 3; // Not ready.
|
||||
|
||||
u8 tweak[0x10] __attribute__((aligned(4)));
|
||||
u32 cluster = sector / SECTORS_PER_CLUSTER;
|
||||
u32 aligned_sector = cluster * SECTORS_PER_CLUSTER;
|
||||
u32 sector_index_in_cluster = sector % SECTORS_PER_CLUSTER;
|
||||
u32 cluster_lookup_index = cluster_lookup[cluster];
|
||||
bool is_cached = cluster_lookup_index != CLUSTER_LOOKUP_EMPTY_ENTRY;
|
||||
|
||||
// Write to cached cluster.
|
||||
if (is_cached)
|
||||
{
|
||||
if (buff)
|
||||
memcpy(bis_cache->cluster_cache[cluster_lookup_index].cluster + sector_index_in_cluster * NX_EMMC_BLOCKSIZE, buff, count * NX_EMMC_BLOCKSIZE);
|
||||
else
|
||||
buff = bis_cache->cluster_cache[cluster_lookup_index].cluster;
|
||||
bis_cache->cluster_cache[cluster_lookup_index].visit_count++;
|
||||
if (bis_cache->cluster_cache[cluster_lookup_index].dirty == 0)
|
||||
dirty_cluster_count++;
|
||||
bis_cache->cluster_cache[cluster_lookup_index].dirty = 1;
|
||||
if (!force_flush)
|
||||
return 0; // Success.
|
||||
|
||||
// Reset args to trigger a full cluster flush to emmc.
|
||||
sector_index_in_cluster = 0;
|
||||
sector = aligned_sector;
|
||||
count = SECTORS_PER_CLUSTER;
|
||||
}
|
||||
|
||||
// Encrypt and write.
|
||||
if (!_nx_aes_xts_crypt_sec(ks_tweak, ks_crypt, ENCRYPT, tweak, true, sector_index_in_cluster, cluster, bis_cache->emmc_buffer, buff, count * NX_EMMC_BLOCKSIZE) ||
|
||||
!nx_emmc_part_write(&emmc_storage, system_part, sector, count, bis_cache->emmc_buffer)
|
||||
)
|
||||
return 1; // R/W error.
|
||||
|
||||
// Mark cache entry not dirty if write succeeds.
|
||||
if (is_cached)
|
||||
{
|
||||
bis_cache->cluster_cache[cluster_lookup_index].dirty = 0;
|
||||
dirty_cluster_count--;
|
||||
}
|
||||
|
||||
return 0; // Success.
|
||||
}
|
||||
|
||||
static void _nx_emmc_bis_flush_cluster(cluster_cache_t *cache_entry)
|
||||
{
|
||||
nx_emmc_bis_write_block(cache_entry->cluster_num * SECTORS_PER_CLUSTER, SECTORS_PER_CLUSTER, NULL, true);
|
||||
}
|
||||
|
||||
static int nx_emmc_bis_read_block(u32 sector, u32 count, void *buff)
|
||||
{
|
||||
if (!system_part)
|
||||
return 3; // Not ready.
|
||||
|
||||
static u32 prev_cluster = -1;
|
||||
static u32 prev_sector = 0;
|
||||
static u8 tweak[0x10] __attribute__((aligned(4)));
|
||||
u8 cache_tweak[0x10] __attribute__((aligned(4)));
|
||||
|
||||
u32 tweak_exp = 0;
|
||||
bool regen_tweak = true;
|
||||
|
||||
u32 cluster = sector / SECTORS_PER_CLUSTER;
|
||||
u32 aligned_sector = cluster * SECTORS_PER_CLUSTER;
|
||||
u32 sector_index_in_cluster = sector % SECTORS_PER_CLUSTER;
|
||||
u32 cluster_lookup_index = cluster_lookup[cluster];
|
||||
|
||||
// Read from cached cluster.
|
||||
if (cluster_lookup_index != CLUSTER_LOOKUP_EMPTY_ENTRY)
|
||||
{
|
||||
memcpy(buff, bis_cache->cluster_cache[cluster_lookup_index].cluster + sector_index_in_cluster * NX_EMMC_BLOCKSIZE, count * NX_EMMC_BLOCKSIZE);
|
||||
bis_cache->cluster_cache[cluster_lookup_index].visit_count++;
|
||||
prev_sector = sector + count - 1;
|
||||
prev_cluster = cluster;
|
||||
return 0; // Success.
|
||||
}
|
||||
|
||||
// Cache cluster.
|
||||
if (!lock_cluster_cache)
|
||||
{
|
||||
// Roll the cache index over and flush if full.
|
||||
if (cluster_cache_end_index >= MAX_CLUSTER_CACHE_ENTRIES)
|
||||
{
|
||||
cluster_cache_end_index = 0;
|
||||
cache_filled = 1;
|
||||
}
|
||||
// Check if cache entry was previously in use in case of cache loop.
|
||||
if (cache_filled == 1 && bis_cache->cluster_cache[cluster_cache_end_index].dirty == 1)
|
||||
_nx_emmc_bis_flush_cluster(&bis_cache->cluster_cache[cluster_cache_end_index]);
|
||||
bis_cache->cluster_cache[cluster_cache_end_index].cluster_num = cluster;
|
||||
bis_cache->cluster_cache[cluster_cache_end_index].visit_count = 1;
|
||||
bis_cache->cluster_cache[cluster_cache_end_index].dirty = 0;
|
||||
cluster_lookup[cluster] = cluster_cache_end_index;
|
||||
|
||||
// Read and decrypt the whole cluster the sector resides in.
|
||||
if (!nx_emmc_part_read(&emmc_storage, system_part, aligned_sector, SECTORS_PER_CLUSTER, bis_cache->emmc_buffer) ||
|
||||
!_nx_aes_xts_crypt_sec(ks_tweak, ks_crypt, DECRYPT, cache_tweak, true, 0, cluster, bis_cache->emmc_buffer, bis_cache->emmc_buffer, XTS_CLUSTER_SIZE)
|
||||
)
|
||||
return 1; // R/W error.
|
||||
|
||||
// Copy to cluster cache.
|
||||
memcpy(bis_cache->cluster_cache[cluster_cache_end_index].cluster, bis_cache->emmc_buffer, XTS_CLUSTER_SIZE);
|
||||
memcpy(buff, bis_cache->emmc_buffer + sector_index_in_cluster * NX_EMMC_BLOCKSIZE, count * NX_EMMC_BLOCKSIZE);
|
||||
cluster_cache_end_index++;
|
||||
return 0; // Success.
|
||||
}
|
||||
|
||||
// If not reading from or writing to cache, do a regular read and decrypt.
|
||||
if (!nx_emmc_part_read(&emmc_storage, system_part, sector, count, bis_cache->emmc_buffer))
|
||||
return 1; // R/W error.
|
||||
|
||||
if (prev_cluster != cluster) // Sector in different cluster than last read.
|
||||
{
|
||||
prev_cluster = cluster;
|
||||
tweak_exp = sector_index_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_index_in_cluster;
|
||||
|
||||
// Maximum one cluster (1 XTS crypto block 16KB).
|
||||
if (!_nx_aes_xts_crypt_sec(ks_tweak, ks_crypt, DECRYPT, tweak, regen_tweak, tweak_exp, prev_cluster, buff, bis_cache->emmc_buffer, count * NX_EMMC_BLOCKSIZE))
|
||||
return 1; // R/W error.
|
||||
prev_sector = sector + count - 1;
|
||||
|
||||
return 0; // Success.
|
||||
}
|
||||
|
||||
int nx_emmc_bis_read(u32 sector, u32 count, void *buff)
|
||||
{
|
||||
int res = 1;
|
||||
u8 *buf = (u8 *)buff;
|
||||
u32 curr_sct = sector;
|
||||
|
||||
while (count)
|
||||
{
|
||||
u32 sct_cnt = MIN(count, 0x20);
|
||||
res = nx_emmc_bis_read_block(curr_sct, sct_cnt, buf);
|
||||
if (res)
|
||||
return 1;
|
||||
|
||||
count -= sct_cnt;
|
||||
curr_sct += sct_cnt;
|
||||
buf += NX_EMMC_BLOCKSIZE * sct_cnt;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int nx_emmc_bis_write(u32 sector, u32 count, void *buff)
|
||||
{
|
||||
int res = 1;
|
||||
u8 *buf = (u8 *)buff;
|
||||
u32 curr_sct = sector;
|
||||
|
||||
while (count)
|
||||
{
|
||||
u32 sct_cnt = MIN(count, 0x20);
|
||||
res = nx_emmc_bis_write_block(curr_sct, sct_cnt, buf, false);
|
||||
if (res)
|
||||
return 1;
|
||||
|
||||
count -= sct_cnt;
|
||||
curr_sct += sct_cnt;
|
||||
buf += NX_EMMC_BLOCKSIZE * sct_cnt;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void nx_emmc_bis_cluster_cache_init()
|
||||
{
|
||||
u32 cluster_lookup_size = (system_part->lba_end - system_part->lba_start + 1) / SECTORS_PER_CLUSTER * sizeof(*cluster_lookup);
|
||||
|
||||
if (cluster_lookup_buf)
|
||||
free(cluster_lookup_buf);
|
||||
|
||||
// Check if carveout protected, in case of old hwinit (pre 4.0.0) chainload.
|
||||
*(vu32 *)NX_BIS_LOOKUP_ADDR = 0;
|
||||
if (*(vu32 *)NX_BIS_LOOKUP_ADDR != 0)
|
||||
{
|
||||
cluster_lookup_buf = (u32 *)malloc(cluster_lookup_size + 0x2000);
|
||||
cluster_lookup = (u32 *)ALIGN((u32)cluster_lookup_buf, 0x1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
cluster_lookup_buf = NULL;
|
||||
cluster_lookup = (u32 *)NX_BIS_LOOKUP_ADDR;
|
||||
}
|
||||
|
||||
// Clear cluster lookup table and reset end index.
|
||||
memset(cluster_lookup, -1, cluster_lookup_size);
|
||||
cluster_cache_end_index = 0;
|
||||
lock_cluster_cache = false;
|
||||
|
||||
dirty_cluster_count = 0;
|
||||
cache_filled = 0;
|
||||
}
|
||||
|
||||
void nx_emmc_bis_init(emmc_part_t *part)
|
||||
{
|
||||
system_part = part;
|
||||
|
||||
nx_emmc_bis_cluster_cache_init();
|
||||
|
||||
switch (part->index)
|
||||
{
|
||||
case 0: // PRODINFO.
|
||||
case 1: // PRODINFOF.
|
||||
ks_crypt = 0;
|
||||
ks_tweak = 1;
|
||||
break;
|
||||
case 8: // SAFE.
|
||||
ks_crypt = 2;
|
||||
ks_tweak = 3;
|
||||
break;
|
||||
case 9: // SYSTEM.
|
||||
case 10: // USER.
|
||||
ks_crypt = 4;
|
||||
ks_tweak = 5;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void nx_emmc_bis_finalize()
|
||||
{
|
||||
if (dirty_cluster_count == 0)
|
||||
return;
|
||||
|
||||
u32 limit = cache_filled == 1 ? MAX_CLUSTER_CACHE_ENTRIES : cluster_cache_end_index;
|
||||
u32 clusters_to_flush = dirty_cluster_count;
|
||||
for (u32 i = 0; i < limit && clusters_to_flush; i++)
|
||||
{
|
||||
if (bis_cache->cluster_cache[i].dirty) {
|
||||
_nx_emmc_bis_flush_cluster(&bis_cache->cluster_cache[i]);
|
||||
clusters_to_flush--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set cluster cache lock according to arg.
|
||||
void nx_emmc_bis_cache_lock(bool lock)
|
||||
{
|
||||
lock_cluster_cache = lock;
|
||||
}
|
248
source/storage/nx_emmc_bis.h
Normal file
248
source/storage/nx_emmc_bis.h
Normal file
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2022 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef NX_EMMC_BIS_H
|
||||
#define NX_EMMC_BIS_H
|
||||
|
||||
#include "../storage/nx_emmc.h"
|
||||
#include <storage/sdmmc.h>
|
||||
|
||||
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_iv[0x10];
|
||||
u8 ssl_key[0x100];
|
||||
u8 crc16_pad18[0xE];
|
||||
u16 ssl_key_crc;
|
||||
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_iv[0x10];
|
||||
u8 rsa2048_eticket_key[0x210];
|
||||
u8 crc16_pad21[0xE];
|
||||
u16 rsa2048_eticket_key_crc;
|
||||
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_iv[0x10];
|
||||
u8 ext_ecc_rsa2048_eticket_key[0x230];
|
||||
u32 ext_ecc_rsa2048_eticket_key_ver;
|
||||
u8 crc16_pad38[0xA];
|
||||
u16 ext_ecc_rsa2048_eticket_key_crc;
|
||||
u8 ext_ssl_key_iv[0x10];
|
||||
u8 ext_ssl_key[0x120];
|
||||
u32 ext_ssl_key_ver;
|
||||
u8 crc16_pad39[0xA];
|
||||
u16 ext_ssl_key_crc;
|
||||
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;
|
||||
|
||||
#define MAGIC_CAL0 0x304C4143
|
||||
#define NX_EMMC_CALIBRATION_OFFSET 0x4400
|
||||
#define NX_EMMC_CALIBRATION_SIZE 0x8000
|
||||
#define XTS_CLUSTER_SIZE 0x4000
|
||||
|
||||
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_cluster_cache_init();
|
||||
void nx_emmc_bis_init(emmc_part_t *part);
|
||||
void nx_emmc_bis_finalize();
|
||||
void nx_emmc_bis_cache_lock(bool lock);
|
||||
|
||||
#endif
|
233
source/storage/nx_sd.c
Normal file
233
source/storage/nx_sd.c
Normal file
|
@ -0,0 +1,233 @@
|
|||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
* Copyright (c) 2018-2021 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 <storage/nx_sd.h>
|
||||
#include <storage/sdmmc.h>
|
||||
#include <storage/sdmmc_driver.h>
|
||||
#include <gfx_utils.h>
|
||||
#include <libs/fatfs/ff.h>
|
||||
#include <mem/heap.h>
|
||||
|
||||
static bool sd_mounted = false;
|
||||
static u16 sd_errors[3] = { 0 }; // Init and Read/Write errors.
|
||||
static u32 sd_mode = SD_UHS_SDR82;
|
||||
|
||||
sdmmc_t sd_sdmmc;
|
||||
sdmmc_storage_t sd_storage;
|
||||
FATFS sd_fs;
|
||||
|
||||
void sd_error_count_increment(u8 type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case SD_ERROR_INIT_FAIL:
|
||||
sd_errors[0]++;
|
||||
break;
|
||||
case SD_ERROR_RW_FAIL:
|
||||
sd_errors[1]++;
|
||||
break;
|
||||
case SD_ERROR_RW_RETRY:
|
||||
sd_errors[2]++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
u16 *sd_get_error_count()
|
||||
{
|
||||
return sd_errors;
|
||||
}
|
||||
|
||||
bool sd_get_card_removed()
|
||||
{
|
||||
if (!sdmmc_get_sd_inserted())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sd_get_card_mounted()
|
||||
{
|
||||
return sd_mounted;
|
||||
}
|
||||
|
||||
u32 sd_get_mode()
|
||||
{
|
||||
return sd_mode;
|
||||
}
|
||||
|
||||
int sd_init_retry(bool power_cycle)
|
||||
{
|
||||
u32 bus_width = SDMMC_BUS_WIDTH_4;
|
||||
u32 type = SDHCI_TIMING_UHS_SDR82;
|
||||
|
||||
// Power cycle SD card.
|
||||
if (power_cycle)
|
||||
{
|
||||
sd_mode--;
|
||||
sdmmc_storage_end(&sd_storage);
|
||||
}
|
||||
|
||||
// Get init parameters.
|
||||
switch (sd_mode)
|
||||
{
|
||||
case SD_INIT_FAIL: // Reset to max.
|
||||
return 0;
|
||||
case SD_1BIT_HS25:
|
||||
bus_width = SDMMC_BUS_WIDTH_1;
|
||||
type = SDHCI_TIMING_SD_HS25;
|
||||
break;
|
||||
case SD_4BIT_HS25:
|
||||
type = SDHCI_TIMING_SD_HS25;
|
||||
break;
|
||||
case SD_UHS_SDR82:
|
||||
type = SDHCI_TIMING_UHS_SDR82;
|
||||
break;
|
||||
default:
|
||||
sd_mode = SD_UHS_SDR82;
|
||||
}
|
||||
|
||||
return sdmmc_storage_init_sd(&sd_storage, &sd_sdmmc, bus_width, type);
|
||||
}
|
||||
|
||||
bool sd_initialize(bool power_cycle)
|
||||
{
|
||||
if (power_cycle)
|
||||
sdmmc_storage_end(&sd_storage);
|
||||
|
||||
int res = !sd_init_retry(false);
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (!res)
|
||||
return true;
|
||||
else if (!sdmmc_get_sd_inserted()) // SD Card is not inserted.
|
||||
{
|
||||
sd_mode = SD_UHS_SDR82;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
sd_errors[SD_ERROR_INIT_FAIL]++;
|
||||
|
||||
if (sd_mode == SD_INIT_FAIL)
|
||||
break;
|
||||
else
|
||||
res = !sd_init_retry(true);
|
||||
}
|
||||
}
|
||||
|
||||
sdmmc_storage_end(&sd_storage);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sd_mount()
|
||||
{
|
||||
if (sd_mounted)
|
||||
return true;
|
||||
|
||||
int res = !sd_initialize(false);
|
||||
|
||||
if (res)
|
||||
{
|
||||
gfx_con.mute = false;
|
||||
EPRINTF("Failed to init SD card.");
|
||||
if (!sdmmc_get_sd_inserted())
|
||||
EPRINTF("Make sure that it is inserted.");
|
||||
else
|
||||
EPRINTF("SD Card Reader is not properly seated!");
|
||||
}
|
||||
else
|
||||
{
|
||||
res = f_mount(&sd_fs, "", 1);
|
||||
if (res == FR_OK)
|
||||
{
|
||||
sd_mounted = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
gfx_con.mute = false;
|
||||
EPRINTFARGS("Failed to mount SD card (FatFS Error %d).\nMake sure that a FAT partition exists..", res);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void _sd_deinit()
|
||||
{
|
||||
if (sd_mode == SD_INIT_FAIL)
|
||||
sd_mode = SD_UHS_SDR82;
|
||||
|
||||
if (sd_mounted)
|
||||
{
|
||||
f_mount(NULL, "", 1);
|
||||
sdmmc_storage_end(&sd_storage);
|
||||
sd_mounted = false;
|
||||
}
|
||||
}
|
||||
|
||||
void sd_unmount() { _sd_deinit(); }
|
||||
void sd_end() { _sd_deinit(); }
|
||||
|
||||
bool sd_is_gpt()
|
||||
{
|
||||
return sd_fs.part_type;
|
||||
}
|
||||
|
||||
void *sd_file_read(const char *path, u32 *fsize)
|
||||
{
|
||||
FIL fp;
|
||||
if (f_open(&fp, path, FA_READ) != FR_OK)
|
||||
return NULL;
|
||||
|
||||
u32 size = f_size(&fp);
|
||||
if (fsize)
|
||||
*fsize = size;
|
||||
|
||||
void *buf = malloc(size);
|
||||
|
||||
if (f_read(&fp, buf, size, NULL) != FR_OK)
|
||||
{
|
||||
free(buf);
|
||||
f_close(&fp);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
f_close(&fp);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
int sd_save_to_file(void *buf, u32 size, const char *filename)
|
||||
{
|
||||
FIL fp;
|
||||
u32 res = 0;
|
||||
res = f_open(&fp, filename, FA_CREATE_ALWAYS | FA_WRITE);
|
||||
if (res)
|
||||
{
|
||||
EPRINTFARGS("Error (%d) creating file\n%s.\n", res, filename);
|
||||
return res;
|
||||
}
|
||||
|
||||
f_write(&fp, buf, size, NULL);
|
||||
f_close(&fp);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue