hos: Utilize burnt fuse info instead of keyblob

Streamline identification of HOS version quirks
This commit is contained in:
CTCaer 2021-01-04 02:57:07 +02:00
parent 0959dc3a2d
commit 41f96d4305
7 changed files with 86 additions and 106 deletions

View file

@ -164,7 +164,7 @@ int parse_fss(launch_ctxt_t *ctxt, const char *path, fss0_sept_t *sept_ctxt)
if (mariko_not_supported)
{
EPRINTF("Mariko not supported on < 0.17.0!");
EPRINTF("\nMariko not supported on < 0.17.0!");
goto fail;
}

View file

@ -756,22 +756,20 @@ int hos_launch(ini_sec_t *cfg)
// Check if fuses lower than 4.0.0 or 9.0.0 or 11.0.0 and if yes apply NO Gamecard patch.
// Additionally check if running emuMMC and disable GC if v3/v4 fuses are burnt and HOS is <= 8.1.0 or != 11.0.0.
//TODO: Add better checks for 11.0.0 in case mkey doesn't change.
if (!ctxt.stock)
{
u32 fuses = fuse_read_odm(7);
bool is_hos_11000 = !memcmp(ctxt.pkg1_id->id, "20201030110855", 8);
if ((h_cfg.autonogc &&
(
(!(fuses & ~0xF) && (kb >= KB_FIRMWARE_VERSION_400)) || // LAFW v2.
(!(fuses & ~0x3FF) && (kb >= KB_FIRMWARE_VERSION_900)) || // LAFW v3.
(!(fuses & ~0x1FFF) && is_hos_11000) // LAFW v4.
(!(fuses & ~0xF) && (ctxt.pkg1_id->fuses >= 5)) || // LAFW v2, 4.0.0+
(!(fuses & ~0x3FF) && (ctxt.pkg1_id->fuses >= 11)) || // LAFW v3, 9.0.0+
(!(fuses & ~0x1FFF) && (ctxt.pkg1_id->fuses >= 14)) // LAFW v4, 11.0.0+
)
)
|| ((emummc_enabled) &&
(
((fuses & 0x400) && (kb <= KB_FIRMWARE_VERSION_810)) || // HOS 9.0.0 fuses burnt.
((fuses & 0x2000) && !is_hos_11000) // HOS 11.0.0 fuses burnt.
((fuses & 0x400) && (ctxt.pkg1_id->fuses <= 10)) || // HOS 9.0.0+ fuses burnt.
((fuses & 0x2000) && (ctxt.pkg1_id->fuses <= 13)) // HOS 11.0.0+ fuses burnt.
)
))
config_kip1patch(&ctxt, "nogc");
@ -845,7 +843,7 @@ int hos_launch(ini_sec_t *cfg)
}
// Configure and manage Warmboot binary.
pkg1_warmboot_config(&ctxt, kb, warmboot_base);
pkg1_warmboot_config(&ctxt, warmboot_base);
// Replace 'warmboot.bin' if requested.
if (ctxt.warmboot)

View file

@ -77,7 +77,7 @@ PATCHSET_DEF(_secmon_6_patchset,
// { 0x1A68 + 0x3A6C, _NOP() } // warmboot UARTA cfg.
);
PATCHSET_DEF(_secmon_620_patchset,
PATCHSET_DEF(_secmon_62_patchset,
// Patch package2 signature/hash checks.
{ 0xDC8 + 0xC74, _NOP() }
// Fix sleep mode for debug.
@ -153,23 +153,24 @@ static const u8 sec_map_100[3] = { PK11_SECTION_SM, PK11_SECTION_LD, PK11_SECTIO
static const u8 sec_map_2xx[3] = { PK11_SECTION_WB, PK11_SECTION_LD, PK11_SECTION_SM };
static const u8 sec_map_4xx[3] = { PK11_SECTION_LD, PK11_SECTION_SM, PK11_SECTION_WB };
// ID (Timestamp), KB, Fuses, TSEC, PK11, SECMON, Warmboot.
static const pkg1_id_t _pkg1_ids[] = {
{ "20161121183008", 0, 0x1900, 0x3FE0, SM_100_ADR, 0x8000D000, _secmon_1_patchset, _warmboot_1_patchset }, // 1.0.0 (Patched relocator).
{ "20170210155124", 0, 0x1900, 0x3FE0, 0x4002D000, 0x8000D000, _secmon_2_patchset, _warmboot_2_patchset }, // 2.0.0 - 2.3.0.
{ "20170519101410", 1, 0x1A00, 0x3FE0, 0x4002D000, 0x8000D000, _secmon_3_patchset, _warmboot_3_patchset }, // 3.0.0.
{ "20170710161758", 2, 0x1A00, 0x3FE0, 0x4002D000, 0x8000D000, _secmon_3_patchset, _warmboot_3_patchset }, // 3.0.1 - 3.0.2.
{ "20170921172629", 3, 0x1800, 0x3FE0, 0x4002B000, 0x4003B000, _secmon_4_patchset, _warmboot_4_patchset }, // 4.0.0 - 4.1.0.
{ "20180220163747", 4, 0x1900, 0x3FE0, 0x4002B000, 0x4003B000, _secmon_5_patchset, _warmboot_4_patchset }, // 5.0.0 - 5.1.0.
{ "20180802162753", 5, 0x1900, 0x3FE0, 0x4002B000, 0x4003D800, _secmon_6_patchset, _warmboot_4_patchset }, // 6.0.0 - 6.1.0.
{ "20181107105733", 6, 0x0E00, 0x6FE0, 0x4002B000, 0x4003D800, _secmon_620_patchset, _warmboot_4_patchset }, // 6.2.0.
{ "20181218175730", 7, 0x0F00, 0x6FE0, 0x40030000, 0x4003E000, NULL, NULL }, // 7.0.0.
{ "20190208150037", 7, 0x0F00, 0x6FE0, 0x40030000, 0x4003E000, NULL, NULL }, // 7.0.1.
{ "20190314172056", 7, 0x0E00, 0x6FE0, 0x40030000, 0x4003E000, NULL, NULL }, // 8.0.0 - 8.0.1.
{ "20190531152432", 8, 0x0E00, 0x6FE0, 0x40030000, 0x4003E000, NULL, NULL }, // 8.1.0.
{ "20190809135709", 9, 0x0E00, 0x6FE0, 0x40030000, 0x4003E000, NULL, NULL }, // 9.0.0 - 9.0.1.
{ "20191021113848", 10, 0x0E00, 0x6FE0, 0x40030000, 0x4003E000, NULL, NULL }, // 9.1.0.
{ "20200303104606", 10, 0x0E00, 0x6FE0, 0x40030000, 0x4003E000, NULL, NULL }, // 10.0.0.
{ "20201030110855", 10, 0x0E00, 0x6FE0, 0x40030000, 0x4003E000, NULL, NULL }, // 11.0.0.
{ "20161121183008", 0, 1, 0x1900, 0x3FE0, SM_100_ADR, 0x8000D000, _secmon_1_patchset, _warmboot_1_patchset }, // 1.0.0 (Patched relocator).
{ "20170210155124", 0, 2, 0x1900, 0x3FE0, 0x4002D000, 0x8000D000, _secmon_2_patchset, _warmboot_2_patchset }, // 2.0.0 - 2.3.0.
{ "20170519101410", 1, 3, 0x1A00, 0x3FE0, 0x4002D000, 0x8000D000, _secmon_3_patchset, _warmboot_3_patchset }, // 3.0.0.
{ "20170710161758", 2, 4, 0x1A00, 0x3FE0, 0x4002D000, 0x8000D000, _secmon_3_patchset, _warmboot_3_patchset }, // 3.0.1 - 3.0.2.
{ "20170921172629", 3, 5, 0x1800, 0x3FE0, 0x4002B000, 0x4003B000, _secmon_4_patchset, _warmboot_4_patchset }, // 4.0.0 - 4.1.0.
{ "20180220163747", 4, 6, 0x1900, 0x3FE0, 0x4002B000, 0x4003B000, _secmon_5_patchset, _warmboot_4_patchset }, // 5.0.0 - 5.1.0.
{ "20180802162753", 5, 7, 0x1900, 0x3FE0, 0x4002B000, 0x4003D800, _secmon_6_patchset, _warmboot_4_patchset }, // 6.0.0 - 6.1.0.
{ "20181107105733", 6, 8, 0x0E00, 0x6FE0, 0x4002B000, 0x4003D800, _secmon_62_patchset, _warmboot_4_patchset }, // 6.2.0.
{ "20181218175730", 7, 9, 0x0F00, 0x6FE0, 0x40030000, 0x4003E000, NULL, NULL }, // 7.0.0.
{ "20190208150037", 7, 9, 0x0F00, 0x6FE0, 0x40030000, 0x4003E000, NULL, NULL }, // 7.0.1.
{ "20190314172056", 7, 9, 0x0E00, 0x6FE0, 0x40030000, 0x4003E000, NULL, NULL }, // 8.0.0 - 8.0.1.
{ "20190531152432", 8, 10, 0x0E00, 0x6FE0, 0x40030000, 0x4003E000, NULL, NULL }, // 8.1.0 - 8.1.1.
{ "20190809135709", 9, 11, 0x0E00, 0x6FE0, 0x40030000, 0x4003E000, NULL, NULL }, // 9.0.0 - 9.0.1.
{ "20191021113848", 10, 12, 0x0E00, 0x6FE0, 0x40030000, 0x4003E000, NULL, NULL }, // 9.1.0 - 9.2.0.
{ "20200303104606", 10, 13, 0x0E00, 0x6FE0, 0x40030000, 0x4003E000, NULL, NULL }, // 10.0.0 - 10.2.0.
{ "20201030110855", 10, 14, 0x0E00, 0x6FE0, 0x40030000, 0x4003E000, NULL, NULL }, // 11.0.0+
{ NULL } // End.
};
@ -227,11 +228,11 @@ const u8 *pkg1_unpack(void *wm_dst, u32 *wb_sz, void *sm_dst, void *ldr_dst, con
//u32 sec_off[3] = { hdr->wb_off, hdr->ldr_off, hdr->sm_off };
// Get correct header mapping.
if (id->kb == KB_FIRMWARE_VERSION_100_200 && !strcmp(id->id, "20161121183008"))
if (id->fuses == 1) // 1.0.0.
sec_map = sec_map_100;
else if (id->kb >= KB_FIRMWARE_VERSION_100_200 && id->kb <= KB_FIRMWARE_VERSION_301)
else if (id->fuses >= 2 && id->fuses <= 4) // 2.0.0 - 3.0.2.
sec_map = sec_map_2xx;
else
else // 4.0.0+
sec_map = sec_map_4xx;
// Copy secmon, warmboot and nx bootloader payloads.
@ -329,9 +330,10 @@ static void _warmboot_filename(char *out, u32 fuses)
strcat(out, ".bin");
}
void pkg1_warmboot_config(void *hos_ctxt, u32 kb, u32 warmboot_base)
void pkg1_warmboot_config(void *hos_ctxt, u32 warmboot_base)
{
launch_ctxt_t *ctxt = (launch_ctxt_t *)hos_ctxt;
u32 kb = ctxt->pkg1_id->kb;
// Set warmboot address in PMC if required.
if (kb <= KB_FIRMWARE_VERSION_301)
@ -340,17 +342,10 @@ void pkg1_warmboot_config(void *hos_ctxt, u32 kb, u32 warmboot_base)
if (h_cfg.t210b01)
{
u32 pa_id;
u32 fuses_fw = kb + 2;
u32 fuses_max = 32; // Current ODM7 max.
u32 fuses_fw = ctxt->pkg1_id->fuses;
u8 burnt_fuses = fuse_count_burnt(fuse_read_odm(7));
// Add one more fuse for high versions.
//TODO: Add better checks for 10.0.0 and up in case mkey doesn't change.
if (kb > KB_FIRMWARE_VERSION_910 || !memcmp(ctxt->pkg1_id->id, "20200303104606", 8)) // 10.0.0.
fuses_fw++;
if (!memcmp(ctxt->pkg1_id->id, "20201030110855", 8)) // 11.0.0.
fuses_fw += 2;
// Save current warmboot in storage cache and check if another one is needed.
if (!ctxt->warmboot)
{

View file

@ -53,9 +53,10 @@ typedef struct _bl_hdr_t210b01_t
typedef struct _pkg1_id_t
{
const char *id;
u32 kb;
u32 tsec_off;
u32 pkg11_off;
u16 kb;
u16 fuses;
u16 tsec_off;
u16 pkg11_off;
u32 secmon_base;
u32 warmboot_base;
patch_t *secmon_patchset;
@ -80,6 +81,6 @@ int pkg1_decrypt(const pkg1_id_t *id, u8 *pkg1);
const u8 *pkg1_unpack(void *wm_dst, u32 *wb_sz, void *sm_dst, void *ldr_dst, const pkg1_id_t *id, u8 *pkg1);
void pkg1_secmon_patch(void *hos_ctxt, u32 secmon_base, bool t210b01);
void pkg1_warmboot_patch(void *hos_ctxt);
void pkg1_warmboot_config(void *hos_ctxt, u32 kb, u32 warmboot_base);
void pkg1_warmboot_config(void *hos_ctxt, u32 warmboot_base);
#endif

View file

@ -150,9 +150,8 @@ typedef struct _atm_fatal_error_ctx
void config_exosphere(launch_ctxt_t *ctxt, u32 warmboot_base, bool exo_new)
{
u32 exoFwNo = 0;
u32 exoFlags = 0;
u32 kb = ctxt->pkg1_id->kb;
u32 exo_fw_no = 0;
u32 exo_flags = 0;
bool user_debug = false;
bool cal0_blanking = false;
bool cal0_allow_writes_sys = false;
@ -161,71 +160,56 @@ void config_exosphere(launch_ctxt_t *ctxt, u32 warmboot_base, bool exo_new)
volatile exo_cfg_t *exo_cfg = (exo_cfg_t *)EXO_CFG_ADDR;
// Old exosphere target versioning.
switch (kb)
{
case KB_FIRMWARE_VERSION_100_200:
if (!memcmp(ctxt->pkg1_id->id, "20161121183008", 8))
exoFwNo = 1;
else
exoFwNo = 2;
break;
case KB_FIRMWARE_VERSION_300:
exoFwNo = 3;
break;
default:
exoFwNo = kb + 1;
if (!memcmp(ctxt->pkg1_id->id, "20190314172056", 8) || (kb >= KB_FIRMWARE_VERSION_810))
exoFwNo++; // ATM_TARGET_FW_800 and up.
if (!memcmp(ctxt->pkg1_id->id, "20200303104606", 8))
exoFwNo++; // ATM_TARGET_FW_1000.
else if (!memcmp(ctxt->pkg1_id->id, "20201030110855", 8)) //TODO: Add better checks in case mkey doesn't change.
exoFwNo += 2; // ATM_TARGET_FW_1100.
break;
}
// Old exosphere target versioning. Use fuses for a simpler encoding.
if (ctxt->pkg1_id->fuses <= 3 || ctxt->pkg1_id->fuses >= 10) // 1.0.0 - 3.0.0, 8.1.0+.
exo_fw_no = ctxt->pkg1_id->fuses;
else
exo_fw_no = ctxt->pkg1_id->fuses - 1; // 3.0.1 - 7.0.1, 8.0.0 - 8.0.1.
// New exosphere target versioning.
if (!memcmp(ctxt->pkg1_id->id, "20190314172056", 8)) // 8.0.0 - 8.0.1.
exo_fw_no++;
// Feed old exosphere target versioning to new.
if (exo_new)
{
// Feed old versioning.
switch (exoFwNo)
switch (exo_fw_no)
{
case 1:
case 2:
case 3:
case 4:
case 6:
exoFwNo = EXO_FW_VER(exoFwNo, 0, 0);
exo_fw_no = EXO_FW_VER(exo_fw_no, 0, 0);
break;
case 5:
if (!ctxt->exo_ctx.fs_is_510)
exoFwNo = EXO_FW_VER(5, 0, 0);
exo_fw_no = EXO_FW_VER(5, 0, 0);
else
exoFwNo = EXO_FW_VER(5, 1, 0);
exo_fw_no = EXO_FW_VER(5, 1, 0);
break;
case 7:
exoFwNo = EXO_FW_VER(6, 2, 0);
exo_fw_no = EXO_FW_VER(6, 2, 0);
break;
case 8:
exoFwNo = EXO_FW_VER(7, 0, 0);
exo_fw_no = EXO_FW_VER(7, 0, 0);
break;
case 9:
exoFwNo = EXO_FW_VER(8, 0, 0);
exo_fw_no = EXO_FW_VER(8, 0, 0);
break;
case 10:
exoFwNo = EXO_FW_VER(8, 1, 0);
exo_fw_no = EXO_FW_VER(8, 1, 0);
break;
case 11:
exoFwNo = EXO_FW_VER(9, 0, 0);
exo_fw_no = EXO_FW_VER(9, 0, 0);
break;
case 12:
exoFwNo = EXO_FW_VER(9, 1, 0);
exo_fw_no = EXO_FW_VER(9, 1, 0);
break;
case 13:
exoFwNo = EXO_FW_VER(10, 0, 0);
exo_fw_no = EXO_FW_VER(10, 0, 0);
break;
case 14:
exoFwNo = EXO_FW_VER(11, 0, 0);
exo_fw_no = EXO_FW_VER(11, 0, 0);
break;
}
}
@ -272,41 +256,41 @@ void config_exosphere(launch_ctxt_t *ctxt, u32 warmboot_base, bool exo_new)
// To avoid problems, make private debug mode always on if not semi-stock.
if (!ctxt->stock || (emu_cfg.enabled && !h_cfg.emummc_force_disable))
exoFlags |= EXO_FLAG_DBG_PRIV;
exo_flags |= EXO_FLAG_DBG_PRIV;
// Enable user debug.
if (user_debug)
exoFlags |= EXO_FLAG_DBG_USER;
exo_flags |= EXO_FLAG_DBG_USER;
// Disable proper failure handling.
if (ctxt->exo_ctx.no_user_exceptions)
exoFlags |= EXO_FLAG_NO_USER_EXC;
exo_flags |= EXO_FLAG_NO_USER_EXC;
// Enable user access to PMU.
if (ctxt->exo_ctx.user_pmu)
exoFlags |= EXO_FLAG_USER_PMU;
exo_flags |= EXO_FLAG_USER_PMU;
// Enable prodinfo blanking. Check if exo ini value is overridden. If not, check if enabled in exo ini.
if ((ctxt->exo_ctx.cal0_blank && *ctxt->exo_ctx.cal0_blank)
|| (!ctxt->exo_ctx.cal0_blank && cal0_blanking))
exoFlags |= EXO_FLAG_CAL0_BLANKING;
exo_flags |= EXO_FLAG_CAL0_BLANKING;
// Allow prodinfo writes. Check if exo ini value is overridden. If not, check if enabled in exo ini.
if ((ctxt->exo_ctx.cal0_allow_writes_sys && *ctxt->exo_ctx.cal0_allow_writes_sys)
|| (!ctxt->exo_ctx.cal0_allow_writes_sys && cal0_allow_writes_sys))
exoFlags |= EXO_FLAG_CAL0_WRITES_SYS;
exo_flags |= EXO_FLAG_CAL0_WRITES_SYS;
// Set mailbox values.
exo_cfg->magic = EXO_MAGIC_VAL;
exo_cfg->fwno = exoFwNo;
exo_cfg->flags[0] = exoFlags;
exo_cfg->fwno = exo_fw_no;
exo_cfg->flags[0] = exo_flags;
// If warmboot is lp0fw, add in RSA modulus.
volatile wb_cfg_t *wb_cfg = (wb_cfg_t *)(warmboot_base + ATM_WB_HEADER_OFF);
if (wb_cfg->magic == ATM_WB_MAGIC)
{
wb_cfg->fwno = exoFwNo;
wb_cfg->fwno = exo_fw_no;
// Set warmboot binary rsa modulus.
u8 *rsa_mod = (u8 *)malloc(512);

View file

@ -1186,6 +1186,8 @@ static lv_res_t _create_window_dump_pk12_tool(lv_obj_t *btn)
if (!pkg1_decrypt(pkg1_id, pkg1))
{
strcat(txt_buf, "#FFDD00 Pkg1 decryption failed!#\n");
if (h_cfg.t210b01)
strcat(txt_buf, "#FFDD00 Is BEK missing?#\n");
lv_label_set_text(lb_desc, txt_buf);
goto out_free;
}

View file

@ -42,22 +42,22 @@ static const u8 sec_map_2xx[3] = { PK11_SECTION_WB, PK11_SECTION_LD, PK11_SECTIO
static const u8 sec_map_4xx[3] = { PK11_SECTION_LD, PK11_SECTION_SM, PK11_SECTION_WB };
static const pkg1_id_t _pkg1_ids[] = {
{ "20161121183008", 0, 0x1900, 0x3FE0, 0x40014020, 0x8000D000 }, // 1.0.0.
{ "20170210155124", 0, 0x1900, 0x3FE0, 0x4002D000, 0x8000D000 }, // 2.0.0 - 2.3.0.
{ "20170519101410", 1, 0x1A00, 0x3FE0, 0x4002D000, 0x8000D000 }, // 3.0.0.
{ "20170710161758", 2, 0x1A00, 0x3FE0, 0x4002D000, 0x8000D000 }, // 3.0.1 - 3.0.2.
{ "20170921172629", 3, 0x1800, 0x3FE0, 0x4002B000, 0x4003B000 }, // 4.0.0 - 4.1.0.
{ "20180220163747", 4, 0x1900, 0x3FE0, 0x4002B000, 0x4003B000 }, // 5.0.0 - 5.1.0.
{ "20180802162753", 5, 0x1900, 0x3FE0, 0x4002B000, 0x4003D800 }, // 6.0.0 - 6.1.0.
{ "20181107105733", 6, 0x0E00, 0x6FE0, 0x4002B000, 0x4003D800 }, // 6.2.0.
{ "20181218175730", 7, 0x0F00, 0x6FE0, 0x40030000, 0x4003E000 }, // 7.0.0.
{ "20190208150037", 7, 0x0F00, 0x6FE0, 0x40030000, 0x4003E000 }, // 7.0.1.
{ "20190314172056", 7, 0x0E00, 0x6FE0, 0x40030000, 0x4003E000 }, // 8.0.0 - 8.0.1.
{ "20190531152432", 8, 0x0E00, 0x6FE0, 0x40030000, 0x4003E000 }, // 8.1.0.
{ "20190809135709", 9, 0x0E00, 0x6FE0, 0x40030000, 0x4003E000 }, // 9.0.0 - 9.0.1.
{ "20191021113848", 10, 0x0E00, 0x6FE0, 0x40030000, 0x4003E000 }, // 9.1.0.
{ "20200303104606", 10, 0x0E00, 0x6FE0, 0x40030000, 0x4003E000 }, // 10.0.0.
{ "20201030110855", 10, 0x0E00, 0x6FE0, 0x40030000, 0x4003E000 }, // 11.0.0.
{ "20161121183008", 0, 0x1900, 0x3FE0, 0x40014020, 0x8000D000 }, // 1.0.0.
{ "20170210155124", 0, 0x1900, 0x3FE0, 0x4002D000, 0x8000D000 }, // 2.0.0 - 2.3.0.
{ "20170519101410", 1, 0x1A00, 0x3FE0, 0x4002D000, 0x8000D000 }, // 3.0.0.
{ "20170710161758", 2, 0x1A00, 0x3FE0, 0x4002D000, 0x8000D000 }, // 3.0.1 - 3.0.2.
{ "20170921172629", 3, 0x1800, 0x3FE0, 0x4002B000, 0x4003B000 }, // 4.0.0 - 4.1.0.
{ "20180220163747", 4, 0x1900, 0x3FE0, 0x4002B000, 0x4003B000 }, // 5.0.0 - 5.1.0.
{ "20180802162753", 5, 0x1900, 0x3FE0, 0x4002B000, 0x4003D800 }, // 6.0.0 - 6.1.0.
{ "20181107105733", 6, 0x0E00, 0x6FE0, 0x4002B000, 0x4003D800 }, // 6.2.0.
{ "20181218175730", 7, 0x0F00, 0x6FE0, 0x40030000, 0x4003E000 }, // 7.0.0.
{ "20190208150037", 7, 0x0F00, 0x6FE0, 0x40030000, 0x4003E000 }, // 7.0.1.
{ "20190314172056", 7, 0x0E00, 0x6FE0, 0x40030000, 0x4003E000 }, // 8.0.0 - 8.0.1.
{ "20190531152432", 8, 0x0E00, 0x6FE0, 0x40030000, 0x4003E000 }, // 8.1.0 - 8.1.1.
{ "20190809135709", 9, 0x0E00, 0x6FE0, 0x40030000, 0x4003E000 }, // 9.0.0 - 9.0.1.
{ "20191021113848", 10, 0x0E00, 0x6FE0, 0x40030000, 0x4003E000 }, // 9.1.0 - 9.2.0.
{ "20200303104606", 10, 0x0E00, 0x6FE0, 0x40030000, 0x4003E000 }, // 10.0.0 - 10.2.0.
{ "20201030110855", 10, 0x0E00, 0x6FE0, 0x40030000, 0x4003E000 }, // 11.0.0+
{ NULL } //End.
};