1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-22 18:26:39 +00:00

gamecard: cache LAFW blob during interface initialization.

Other changes include:

* Codestyle fixes.
* Remove references to GameCardKeyArea in the usb_gc_dumper PoC.
* Remove option to append key area to output XCI dumps in usb_gc_dumper PoC.
This commit is contained in:
Pablo Curiel 2022-02-03 04:39:54 +01:00
parent e79b03afeb
commit 8168a5ac84
3 changed files with 235 additions and 294 deletions

View file

@ -64,7 +64,7 @@ typedef struct
bool read_error;
bool write_error;
bool transfer_cancelled;
u32 xci_crc, full_xci_crc;
u32 xci_crc;
} ThreadSharedData;
/* Function prototypes. */
@ -73,13 +73,12 @@ static void consolePrint(const char *text, ...);
static u32 menuGetElementCount(const Menu *menu);
static bool sendGameCardKeyAreaViaUsb(void);
static bool sendGameCardSpecificDataViaUsb(void);
static bool sendGameCardCertificateViaUsb(void);
static bool sendGameCardInitialDataViaUsb(void);
static bool sendGameCardImageViaUsb(void);
static bool sendConsoleLafwViaUsb(void);
static bool sendConsoleLafwBlobViaUsb(void);
static void changeKeyAreaOption(u32 idx);
static void changeCertificateOption(u32 idx);
static void changeTrimOption(u32 idx);
static void changeCrcOption(u32 idx);
@ -89,7 +88,7 @@ static void write_thread_func(void *arg);
/* Global variables. */
static bool g_appendKeyArea = false, g_keepCertificate = false, g_trimDump = false, g_calcCrc = false;
static bool g_keepCertificate = false, g_trimDump = false, g_calcCrc = false;
static const char *g_xciOptions[] = { "no", "yes", NULL };
@ -100,16 +99,6 @@ static MenuElement *g_xciMenuElements[] = {
.task_func = &sendGameCardImageViaUsb,
.element_options = NULL
},
&(MenuElement){
.str = "append key area",
.child_menu = NULL,
.task_func = NULL,
.element_options = &(MenuElementOption){
.selected = 0,
.options_func = &changeKeyAreaOption,
.options = g_xciOptions
}
},
&(MenuElement){
.str = "keep certificate",
.child_menu = NULL,
@ -151,12 +140,6 @@ static Menu g_xciMenu = {
};
static MenuElement *g_rootMenuElements[] = {
&(MenuElement){
.str = "dump gamecard initial data",
.child_menu = NULL,
.task_func = &sendGameCardKeyAreaViaUsb,
.element_options = NULL
},
&(MenuElement){
.str = "dump gamecard specific data",
.child_menu = NULL,
@ -169,6 +152,12 @@ static MenuElement *g_rootMenuElements[] = {
.task_func = &sendGameCardCertificateViaUsb,
.element_options = NULL
},
&(MenuElement){
.str = "dump gamecard initial data",
.child_menu = NULL,
.task_func = &sendGameCardInitialDataViaUsb,
.element_options = NULL
},
&(MenuElement){
.str = "dump gamecard xci",
.child_menu = &g_xciMenu,
@ -176,9 +165,9 @@ static MenuElement *g_rootMenuElements[] = {
.element_options = NULL
},
&(MenuElement){
.str = "dump console LAFW",
.str = "dump console lafw blob",
.child_menu = NULL,
.task_func = &sendConsoleLafwViaUsb,
.task_func = &sendConsoleLafwBlobViaUsb,
.element_options = NULL
},
NULL
@ -450,24 +439,6 @@ static bool sendFileData(const char *path, void *data, size_t data_size)
return true;
}
static bool dumpGameCardKeyArea(GameCardKeyArea *out)
{
if (!out)
{
consolePrint("invalid parameters to dump key area!\n");
return false;
}
if (!gamecardGetKeyArea(out))
{
consolePrint("failed to get gamecard key area\n");
return false;
}
consolePrint("get gamecard key area ok\n");
return true;
}
static bool dumpGameCardSecurityInformation(GameCardSecurityInformation *out)
{
if (!out)
@ -475,81 +446,46 @@ static bool dumpGameCardSecurityInformation(GameCardSecurityInformation *out)
consolePrint("invalid parameters to dump gamecard security information!\n");
return false;
}
if (!gamecardGetSecurityInformation(out))
{
consolePrint("failed to get gamecard security information\n");
return false;
}
consolePrint("get gamecard security information ok\n");
return true;
}
static bool sendGameCardKeyAreaViaUsb(void)
{
if (!waitForGameCardAndUsb()) return false;
utilsSetLongRunningProcessState(true);
GameCardKeyArea gc_key_area = {0};
bool success = false;
u32 crc = 0;
char *filename = titleGenerateGameCardFileName(TitleNamingConvention_Full, TitleFileNameIllegalCharReplaceType_IllegalFsChars);
if (!dumpGameCardKeyArea(&gc_key_area) || !filename) goto end;
crc = crc32Calculate(&(gc_key_area.initial_data), sizeof(GameCardInitialData));
snprintf(path, MAX_ELEMENTS(path), "%s (Initial Data) (%08X).bin", filename, crc);
if (!sendFileData(path, &(gc_key_area.initial_data), sizeof(GameCardInitialData))) goto end;
printf("successfully sent key area as \"%s\"\n", path);
success = true;
end:
if (filename) free(filename);
utilsSetLongRunningProcessState(false);
FsGameCardIdSet id_set = {0};
if (gamecardGetIdSet(&id_set)) LOG_DATA(&id_set, sizeof(FsGameCardIdSet), "Gamecard ID set:");
consolePrint("press any button to continue");
utilsWaitForButtonPress(0);
return success;
}
static bool sendGameCardSpecificDataViaUsb(void)
{
if (!waitForGameCardAndUsb()) return false;
utilsSetLongRunningProcessState(true);
GameCardSecurityInformation gc_security_information = {0};
bool success = false;
u32 crc = 0;
char *filename = titleGenerateGameCardFileName(TitleNamingConvention_Full, TitleFileNameIllegalCharReplaceType_IllegalFsChars);
if (!dumpGameCardSecurityInformation(&gc_security_information) || !filename) goto end;
crc = crc32Calculate(&(gc_security_information.specific_data), sizeof(GameCardSpecificData));
snprintf(path, MAX_ELEMENTS(path), "%s (Specific Data) (%08X).bin", filename, crc);
if (!sendFileData(path, &(gc_security_information.specific_data), sizeof(GameCardSpecificData))) goto end;
printf("successfully sent specific data as \"%s\"\n", path);
success = true;
end:
if (filename) free(filename);
utilsSetLongRunningProcessState(false);
consolePrint("press any button to continue");
utilsWaitForButtonPress(0);
return success;
}
@ -591,44 +527,30 @@ end:
return success;
}
static bool sendConsoleLafwViaUsb(void)
static bool sendGameCardInitialDataViaUsb(void)
{
if (!waitForGameCardAndUsb()) return false;
utilsSetLongRunningProcessState(true);
LotusAsicFirmware lafw = {0};
GameCardSecurityInformation gc_security_information = {0};
bool success = false;
u32 crc = 0;
char *filename = titleGenerateGameCardFileName(TitleNamingConvention_Full, TitleFileNameIllegalCharReplaceType_IllegalFsChars);
if (!gamecardGetLotusAsicFirmware(&lafw))
{
consolePrint("failed to get console LAFW\n");
goto end;
}
if (!dumpGameCardSecurityInformation(&gc_security_information) || !filename) goto end;
u32 lafw_version = gamecardConvertLotusAsicFirmwareVersionBitmask(&lafw);
crc = crc32Calculate(&(gc_security_information.initial_data), sizeof(GameCardInitialData));
snprintf(path, MAX_ELEMENTS(path), "%s (Initial Data) (%08X).bin", filename, crc);
const char* filename = "";
switch(lafw.fw_type) {
case LotusAsicFirmwareType_ReadFw: filename = "ReadFw"; break;
case LotusAsicFirmwareType_ReadDevFw: filename = "ReadDevFw"; break;
case LotusAsicFirmwareType_WriterFw: filename = "WriterFw"; break;
case LotusAsicFirmwareType_RmaFw: filename = "RmaFw"; break;
default: filename = "Unknown"; break;
}
if (!sendFileData(path, &(gc_security_information.initial_data), sizeof(GameCardInitialData))) goto end;
consolePrint("get console LAFW ok\n");
crc = crc32Calculate(&lafw, sizeof(LotusAsicFirmware));
snprintf(path, MAX_ELEMENTS(path), "LAFW (%s) (v%d) (%08X).bin", filename, lafw_version, crc);
if (!sendFileData(path, &lafw, sizeof(LotusAsicFirmware))) goto end;
printf("successfully sent lafw as \"%s\"\n", path);
printf("successfully sent initial data as \"%s\"\n", path);
success = true;
end:
if (filename) free(filename);
utilsSetLongRunningProcessState(false);
consolePrint("press any button to continue");
@ -637,7 +559,6 @@ end:
return success;
}
static bool sendGameCardImageViaUsb(void)
{
if (!waitForGameCardAndUsb()) return false;
@ -645,8 +566,6 @@ static bool sendGameCardImageViaUsb(void)
utilsSetLongRunningProcessState(true);
u64 gc_size = 0;
u32 key_area_crc = 0;
GameCardKeyArea gc_key_area = {0};
ThreadSharedData shared_data = {0};
Thread read_thread = {0}, write_thread = {0};
@ -655,7 +574,7 @@ static bool sendGameCardImageViaUsb(void)
bool success = false;
consolePrint("gamecard image dump\nappend key area: %s | keep certificate: %s | trim dump: %s\n\n", g_appendKeyArea ? "yes" : "no", g_keepCertificate ? "yes" : "no", g_trimDump ? "yes" : "no");
consolePrint("gamecard image dump\nkeep certificate: %s | trim dump: %s\n\n", g_keepCertificate ? "yes" : "no", g_trimDump ? "yes" : "no");
filename = titleGenerateGameCardFileName(TitleNamingConvention_Full, TitleFileNameIllegalCharReplaceType_IllegalFsChars);
if (!filename)
@ -681,34 +600,13 @@ static bool sendGameCardImageViaUsb(void)
consolePrint("gamecard size: 0x%lX\n", gc_size);
if (g_appendKeyArea)
{
gc_size += sizeof(GameCardKeyArea);
if (!dumpGameCardKeyArea(&gc_key_area)) goto end;
if (g_calcCrc)
{
key_area_crc = crc32Calculate(&gc_key_area, sizeof(GameCardKeyArea));
if (g_appendKeyArea) shared_data.full_xci_crc = key_area_crc;
}
consolePrint("gamecard size (with key area): 0x%lX\n", gc_size);
}
snprintf(path, MAX_ELEMENTS(path), "%s (%s) (%s) (%s).xci", filename, g_appendKeyArea ? "keyarea" : "keyarealess", g_keepCertificate ? "cert" : "certless", g_trimDump ? "trimmed" : "untrimmed");
snprintf(path, MAX_ELEMENTS(path), "%s (%s) (%s).xci", filename, g_keepCertificate ? "cert" : "certless", g_trimDump ? "trimmed" : "untrimmed");
if (!usbSendFilePropertiesCommon(gc_size, path))
{
consolePrint("failed to send file properties for \"%s\"!\n", path);
goto end;
}
if (g_appendKeyArea && !usbSendFileData(&gc_key_area, sizeof(GameCardKeyArea)))
{
consolePrint("failed to send gamecard key area data!\n");
goto end;
}
consolePrint("creating threads\n");
utilsCreateThread(&read_thread, read_thread_func, &shared_data, 2);
utilsCreateThread(&write_thread, write_thread_func, &shared_data, 2);
@ -789,13 +687,7 @@ static bool sendGameCardImageViaUsb(void)
printf("process completed in %lu seconds\n", start);
success = true;
if (g_calcCrc)
{
if (g_appendKeyArea) printf("key area crc: %08X | ", key_area_crc);
printf("xci crc: %08X", shared_data.xci_crc);
if (g_appendKeyArea) printf(" | xci crc (with key area): %08X", shared_data.full_xci_crc);
printf("\n");
}
if (g_calcCrc) printf("xci crc: %08X\n", shared_data.xci_crc);
end:
if (shared_data.data) free(shared_data.data);
@ -810,9 +702,43 @@ end:
return success;
}
static void changeKeyAreaOption(u32 idx)
static bool sendConsoleLafwBlobViaUsb(void)
{
g_appendKeyArea = (idx > 0);
if (!waitForGameCardAndUsb()) return false;
utilsSetLongRunningProcessState(true);
u64 lafw_version = 0;
LotusAsicFirmwareBlob lafw_blob = {0};
bool success = false;
u32 crc = 0;
if (!gamecardGetLotusAsicFirmwareBlob(&lafw_blob, &lafw_version))
{
consolePrint("failed to get console lafw blob\n");
goto end;
}
const char *type_str = gamecardGetLafwTypeString(lafw_blob.fw_type);
if (!type_str) type_str = "Unknown";
consolePrint("get console lafw blob ok\n");
crc = crc32Calculate(&lafw_blob, sizeof(LotusAsicFirmwareBlob));
snprintf(path, MAX_ELEMENTS(path), "LAFW (%s) (v%lu) (%08X).bin", type_str, lafw_version, crc);
if (!sendFileData(path, &lafw_blob, sizeof(LotusAsicFirmwareBlob))) goto end;
printf("successfully sent lafw blob as \"%s\"\n", path);
success = true;
end:
utilsSetLongRunningProcessState(false);
consolePrint("press any button to continue");
utilsWaitForButtonPress(0);
return success;
}
static void changeCertificateOption(u32 idx)
@ -869,11 +795,7 @@ static void read_thread_func(void *arg)
if (!g_keepCertificate && offset == 0) memset(buf + GAMECARD_CERTIFICATE_OFFSET, 0xFF, sizeof(FsGameCardCertificate));
/* Update checksum */
if (g_calcCrc)
{
shared_data->xci_crc = crc32CalculateWithSeed(shared_data->xci_crc, buf, blksize);
if (g_appendKeyArea) shared_data->full_xci_crc = crc32CalculateWithSeed(shared_data->full_xci_crc, buf, blksize);
}
if (g_calcCrc) shared_data->xci_crc = crc32CalculateWithSeed(shared_data->xci_crc, buf, blksize);
/* Wait until the previous data chunk has been written */
mutexLock(&g_fileMutex);

View file

@ -40,6 +40,20 @@ extern "C" {
#define GAMECARD_CERTIFICATE_OFFSET 0x7000
/// Plaintext area. Dumped from FS program memory.
/// Overall structure may change with each new LAFW version.
typedef struct {
u32 asic_security_mode; ///< Determines how the Lotus ASIC initialised the gamecard security mode. Usually 0xFFFFFFF9.
u32 asic_status; ///< Bitmask of the internal gamecard interface status. Usually 0x20000000.
FsCardId1 card_id1;
FsCardId2 card_id2;
u8 card_uid[0x40];
u8 reserved[0x190];
u8 asic_session_hash[0x20]; ///< Changes with each gamecard (re)insertion.
} GameCardSpecificData;
NXDT_ASSERT(GameCardSpecificData, 0x200);
/// Encrypted using AES-128-ECB with the common titlekek generator key (stored in the .rodata segment from the Lotus firmware).
typedef struct {
u64 package_id; ///< Matches package_id from GameCardHeader.
@ -59,6 +73,18 @@ typedef struct {
NXDT_ASSERT(GameCardInitialData, 0x200);
/// Plaintext area. Dumped from FS program memory.
/// This struct is returned by Lotus command "ChangeToSecureMode" (0xF). This means it is only available *after* the gamecard secure area has been mounted.
/// A copy of the gamecard header without the RSA-2048 signature and a plaintext GameCardInfo precedes this struct in FS program memory.
typedef struct {
GameCardSpecificData specific_data;
FsGameCardCertificate certificate;
u8 reserved[0x200];
GameCardInitialData initial_data;
} GameCardSecurityInformation;
NXDT_ASSERT(GameCardSecurityInformation, 0x800);
/// Encrypted using AES-128-CTR with the key and IV/counter from the `GameCardTitleKeyAreaEncryption` section. Assumed to be all zeroes in retail gamecards.
typedef struct {
u8 titlekey[0x10]; ///< Decrypted titlekey from the `GameCardInitialData` section.
@ -77,7 +103,7 @@ typedef struct {
NXDT_ASSERT(GameCardTitleKeyAreaEncryption, 0x100);
/// Used to secure communications between the Lotus and the inserted gamecard.
/// Precedes the gamecard header.
/// Supposedly precedes the gamecard header.
typedef struct {
GameCardInitialData initial_data;
GameCardTitleKeyArea titlekey_area;
@ -86,30 +112,6 @@ typedef struct {
NXDT_ASSERT(GameCardKeyArea, 0x1000);
typedef struct {
u32 asic_security_mode;
u32 asic_status;
u32 cardid1;
u32 cardid2;
u8 card_uid[0x40];
u8 reserved[0x190];
u8 asic_session_hash[0x20];
} GameCardSpecificData;
NXDT_ASSERT(GameCardSpecificData, 0x200);
/// This struct is returned by Lotus command "ChangeToSecureMode" (0xF)
/// A copy of the gamecard header without the RSA-2048 signature and a plaintext GameCardInfo precedes this struct in FS program memory.
typedef struct {
GameCardSpecificData specific_data;
FsGameCardCertificate certificate;
u8 ffpadding[0x200];
GameCardInitialData initial_data;
} GameCardSecurityInformation;
NXDT_ASSERT(GameCardSecurityInformation, 0x800);
typedef enum {
GameCardKekIndex_Version0 = 0,
GameCardKekIndex_VersionForDev = 1
@ -250,6 +252,7 @@ typedef enum {
LotusAsicDeviceType_Prod2Dev = 3
} LotusAsicDeviceType;
/// Plaintext Lotus ASIC firmware (LAFW) blob. Dumped from FS program memory.
typedef struct {
u8 signature[0x100];
u32 magic; ///< "LAFW".
@ -265,10 +268,9 @@ typedef struct {
char placeholder_str[0x10]; ///< "IDIDIDIDIDIDIDID".
u8 reserved_3[0x40];
u8 data[0x7680];
} LotusAsicFirmware;
NXDT_ASSERT(LotusAsicFirmware, 0x7800);
} LotusAsicFirmwareBlob;
NXDT_ASSERT(LotusAsicFirmwareBlob, 0x7800);
/// Initializes data needed to access raw gamecard storage areas.
/// Also spans a background thread to automatically detect gamecard status changes and to cache data from the inserted gamecard.
@ -285,16 +287,7 @@ UEvent *gamecardGetStatusChangeUserEvent(void);
/// Returns the current GameCardStatus value.
u8 gamecardGetStatus(void);
/// Used to read raw data from the inserted gamecard. Supports unaligned reads.
/// All required handles, changes between normal <-> secure storage areas and proper offset calculations are managed internally.
/// 'offset' + 'read_size' must not exceed the value returned by gamecardGetTotalSize().
bool gamecardReadStorage(void *out, u64 read_size, u64 offset);
/// Fills the provided GameCardKeyArea pointer. Only GameCardInitialData data is retrieved at this moment.
/// This area can't be read using gamecardReadStorage().
bool gamecardGetKeyArea(GameCardKeyArea *out);
/// Fills the provided GameCardSecurityInformation pointer
/// Fills the provided GameCardSecurityInformation pointer.
/// This area can't be read using gamecardReadStorage().
bool gamecardGetSecurityInformation(GameCardSecurityInformation* out);
@ -302,6 +295,15 @@ bool gamecardGetSecurityInformation(GameCardSecurityInformation* out);
/// This area can't be read using gamecardReadStorage().
bool gamecardGetIdSet(FsGameCardIdSet *out);
/// Fills the provided pointers with LAFW blob data from FS program memory.
/// 'out_lafw_blob' or 'out_lafw_version' may be set to NULL, but at least one of them must be a valid pointer.
bool gamecardGetLotusAsicFirmwareBlob(LotusAsicFirmwareBlob *out_lafw_blob, u64 *out_lafw_version);
/// Used to read raw data from the inserted gamecard. Supports unaligned reads.
/// All required handles, changes between normal <-> secure storage areas and proper offset calculations are managed internally.
/// 'offset' + 'read_size' must not exceed the value returned by gamecardGetTotalSize().
bool gamecardReadStorage(void *out, u64 read_size, u64 offset);
/// Fills the provided GameCardHeader pointer.
/// This area can also be read using gamecardReadStorage(), starting at offset 0.
bool gamecardGetHeader(GameCardHeader *out);
@ -342,11 +344,9 @@ const char *gamecardGetRequiredHosVersionString(u64 fw_version);
/// Returns NULL if the provided value is out of range.
const char *gamecardGetCompatibilityTypeString(u8 compatibility_type);
/// Fills the provided LotusAsicFirmware pointer with FS's current LAFW blob.
bool gamecardGetLotusAsicFirmware(LotusAsicFirmware* out);
/// Convert LAFW version bitmask to an integer
u32 gamecardConvertLotusAsicFirmwareVersionBitmask(LotusAsicFirmware* lafw);
/// Takes a LotusAsicFirmwareType value. Returns a pointer to a string that represents the provided LAFW type.
/// Returns NULL if the provided value is invalid.
const char *gamecardGetLafwTypeString(u32 lafw_type);
#ifdef __cplusplus
}

View file

@ -66,6 +66,7 @@ static FsEventNotifier g_gameCardEventNotifier = {0};
static Event g_gameCardKernelEvent = {0};
static bool g_openDeviceOperator = false, g_openEventNotifier = false, g_loadKernelEvent = false;
static LotusAsicFirmwareBlob *g_lafwBlob = NULL;
static u64 g_lafwVersion = 0;
static Thread g_gameCardDetectionThread = {0};
@ -119,7 +120,7 @@ static const char *g_gameCardCompatibilityTypeStrings[GameCardCompatibilityType_
/* Function prototypes. */
static bool gamecardGetLotusAsicFirmwareVersion(void);
static bool gamecardReadLotusAsicFirmwareBlob(void);
static bool gamecardCreateDetectionThread(void);
static void gamecardDestroyDetectionThread(void);
@ -203,8 +204,8 @@ bool gamecardInitialize(void)
/* Create user-mode gamecard status change event. */
ueventCreate(&g_gameCardStatusChangeEvent, true);
/* Retrieve LAFW version. */
if (!gamecardGetLotusAsicFirmwareVersion()) break;
/* Retrieve LAFW blob. */
if (!gamecardReadLotusAsicFirmwareBlob()) break;
/* Create gamecard detection thread. */
if (!(g_gameCardDetectionThreadCreated = gamecardCreateDetectionThread())) break;
@ -227,6 +228,13 @@ void gamecardExit(void)
g_gameCardDetectionThreadCreated = false;
}
/* Free LAFW blob buffer. */
if (g_lafwBlob)
{
free(g_lafwBlob);
g_lafwBlob = NULL;
}
/* Close gamecard detection kernel event. */
if (g_loadKernelEvent)
{
@ -283,30 +291,6 @@ u8 gamecardGetStatus(void)
return status;
}
bool gamecardReadStorage(void *out, u64 read_size, u64 offset)
{
bool ret = false;
SCOPED_LOCK(&g_gameCardMutex) ret = gamecardReadStorageArea(out, read_size, offset);
return ret;
}
/* Read full FS program memory by calling gamecardGetSecurityInformation, and then extracts out the GameCardInitialData block. */
bool gamecardGetKeyArea(GameCardKeyArea *out)
{
GameCardSecurityInformation securityInformation;
/* Clear output. */
memset(out, 0, sizeof(GameCardKeyArea));
if (!gamecardGetSecurityInformation(&securityInformation)) {
return false;
}
memcpy(&out->initial_data, &securityInformation.initial_data, sizeof(GameCardInitialData));
return true;
}
/* Read full FS program memory to retrieve the GameCardSecurityInformation block. */
/* In FS program memory, this is returned by Lotus command "ChangeToSecureMode" (0xF). */
/* This means it is only available *after* the gamecard secure area has been mounted, which is taken care of in gamecardReadSecurityInformation(). */
@ -334,6 +318,33 @@ bool gamecardGetIdSet(FsGameCardIdSet *out)
return ret;
}
bool gamecardGetLotusAsicFirmwareBlob(LotusAsicFirmwareBlob *out_lafw_blob, u64 *out_lafw_version)
{
bool ret = false;
SCOPED_LOCK(&g_gameCardMutex)
{
if (!g_gameCardInterfaceInit || !g_lafwBlob || (!out_lafw_blob && !out_lafw_version)) break;
/* Copy LAFW blob data. */
if (out_lafw_blob) memcpy(out_lafw_blob, g_lafwBlob, sizeof(LotusAsicFirmwareBlob));
/* Copy LAFW version. */
if (out_lafw_version) *out_lafw_version = g_lafwVersion;
ret = true;
}
return ret;
}
bool gamecardReadStorage(void *out, u64 read_size, u64 offset)
{
bool ret = false;
SCOPED_LOCK(&g_gameCardMutex) ret = gamecardReadStorageArea(out, read_size, offset);
return ret;
}
bool gamecardGetHeader(GameCardHeader *out)
{
bool ret = false;
@ -529,93 +540,100 @@ const char *gamecardGetCompatibilityTypeString(u8 compatibility_type)
return (compatibility_type < GameCardCompatibilityType_Count ? g_gameCardCompatibilityTypeStrings[compatibility_type] : NULL);
}
bool gamecardGetLotusAsicFirmware(LotusAsicFirmware* out)
const char *gamecardGetLafwTypeString(u32 lafw_type)
{
bool ret = false;
SCOPED_LOCK(&g_gameCardMutex)
const char *type = NULL;
switch(lafw_type)
{
bool found = false, dev_unit = utilsIsDevelopmentUnit();
/* Temporarily set the segment mask to .data. */
g_fsProgramMemory.mask = MemoryProgramSegmentType_Data;
/* Retrieve full FS program memory dump. */
ret = memRetrieveProgramMemorySegment(&g_fsProgramMemory);
/* Clear segment mask. */
g_fsProgramMemory.mask = 0;
if (!ret)
{
LOG_MSG("Failed to retrieve FS .data segment dump!");
goto end;
}
/* Look for the LAFW ReadFw blob in the FS .data memory dump. */
for(u64 offset = 0; offset < g_fsProgramMemory.data_size; offset++)
{
if ((g_fsProgramMemory.data_size - offset) < sizeof(LotusAsicFirmware)) break;
LotusAsicFirmware *lafw_blob = (LotusAsicFirmware*)(g_fsProgramMemory.data + offset);
u32 magic = __builtin_bswap32(lafw_blob->magic), fw_type = lafw_blob->fw_type;
if (magic == LAFW_MAGIC && ((!dev_unit && fw_type == LotusAsicFirmwareType_ReadFw) || (dev_unit && fw_type == LotusAsicFirmwareType_ReadDevFw)))
{
/* Jackpot. */
memcpy(out, lafw_blob, sizeof(LotusAsicFirmware));
found = true;
break;
}
}
if (!found)
{
LOG_MSG("Unable to locate Lotus %s blob in FS .data segment!", dev_unit ? "ReadDevFw" : "ReadFw");
goto end;
}
/* Update flag. */
ret = true;
end:
memFreeMemoryLocation(&g_fsProgramMemory);
case LotusAsicFirmwareType_ReadFw:
type = "ReadFw";
break;
case LotusAsicFirmwareType_ReadDevFw:
type = "ReadDevFw";
break;
case LotusAsicFirmwareType_WriterFw:
type = "WriterFw";
break;
case LotusAsicFirmwareType_RmaFw:
type = "RmaFw";
break;
default:
break;
}
return ret;
return type;
}
u32 gamecardConvertLotusAsicFirmwareVersionBitmask(LotusAsicFirmware* lafw) {
u64 fw_version_bitmask = lafw->fw_version;
u32 fw_version = 0;
while(fw_version_bitmask)
{
fw_version += (fw_version_bitmask & 1);
fw_version_bitmask >>= 1;
}
return fw_version;
}
static bool gamecardGetLotusAsicFirmwareVersion(void)
static bool gamecardReadLotusAsicFirmwareBlob(void)
{
bool ret = false;
LotusAsicFirmware lafw;
u64 fw_version = 0;
bool ret = false, found = false, dev_unit = utilsIsDevelopmentUnit();
/* Allocate memory for the LAFW blob. */
g_lafwBlob = calloc(1, sizeof(LotusAsicFirmwareBlob));
if (!g_lafwBlob)
{
LOG_MSG("Failed to allocate memory for LAFW blob!");
goto end;
}
/* Temporarily set the segment mask to .data. */
g_fsProgramMemory.mask = MemoryProgramSegmentType_Data;
/* Retrieve full FS program memory dump. */
ret = memRetrieveProgramMemorySegment(&g_fsProgramMemory);
/* Clear segment mask. */
g_fsProgramMemory.mask = 0;
/* Retrieve LAFW. */
ret = gamecardGetLotusAsicFirmware(&lafw);
if (!ret)
{
LOG_MSG("Failed to retrieve Lotus Asic Firmware!");
LOG_MSG("Failed to retrieve FS .data segment dump!");
goto end;
}
/* Look for the LAFW ReadFw blob in the FS .data memory dump. */
for(u64 offset = 0; offset < g_fsProgramMemory.data_size; offset++)
{
if ((g_fsProgramMemory.data_size - offset) < sizeof(LotusAsicFirmwareBlob)) break;
LotusAsicFirmwareBlob *lafw_blob = (LotusAsicFirmwareBlob*)(g_fsProgramMemory.data + offset);
u32 magic = __builtin_bswap32(lafw_blob->magic), fw_type = lafw_blob->fw_type;
if (magic == LAFW_MAGIC && ((!dev_unit && fw_type == LotusAsicFirmwareType_ReadFw) || (dev_unit && fw_type == LotusAsicFirmwareType_ReadDevFw)))
{
/* Jackpot. */
memcpy(g_lafwBlob, lafw_blob, sizeof(LotusAsicFirmwareBlob));
fw_version = lafw_blob->fw_version;
found = true;
break;
}
}
if (!found)
{
LOG_MSG("Unable to locate Lotus %s blob in FS .data segment!", dev_unit ? "ReadDevFw" : "ReadFw");
goto end;
}
/* Convert LAFW version bitmask to an integer. */
g_lafwVersion = gamecardConvertLotusAsicFirmwareVersionBitmask(&lafw);
g_lafwVersion = 0;
while(fw_version)
{
g_lafwVersion += (fw_version & 1);
fw_version >>= 1;
}
LOG_MSG("LAFW version: %lu.", g_lafwVersion);
/* Update flag. */
ret = true;
end:
memFreeMemoryLocation(&g_fsProgramMemory);
return ret;
}
@ -938,11 +956,12 @@ static bool gamecardReadSecurityInformation(GameCardSecurityInformation *out)
if (!memcmp(tmp_hash, g_gameCardHeader.initial_data_hash, SHA256_HASH_SIZE))
{
/* Jackpot. */
memcpy(out, g_fsProgramMemory.data + offset - 0x600, sizeof(GameCardSecurityInformation));
memcpy(out, g_fsProgramMemory.data + offset + sizeof(GameCardInitialData) - sizeof(GameCardSecurityInformation), sizeof(GameCardSecurityInformation));
/* Clear out the current ASIC session hash. */
/* It's not actually part of the gamecard data, and this changes every time a gamecard (re)insertion takes place. */
memset(out->specific_data.asic_session_hash, 0xFF, sizeof(out->specific_data.asic_session_hash));
// Clear out the asic session hash of the current Lotus session with the console.
// It's not actually part of the gamecard data, and this changes every time the gamecard is reinserted.
memset(out->specific_data.asic_session_hash, 0xFF, 32);
found = true;
break;
}