From 8168a5ac8454998a2359446b77b9039290c4b3af Mon Sep 17 00:00:00 2001 From: Pablo Curiel Date: Thu, 3 Feb 2022 04:39:54 +0100 Subject: [PATCH] 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. --- code_templates/usb_gc_dumper.c | 226 +++++++++++---------------------- include/core/gamecard.h | 86 ++++++------- source/core/gamecard.c | 217 ++++++++++++++++--------------- 3 files changed, 235 insertions(+), 294 deletions(-) diff --git a/code_templates/usb_gc_dumper.c b/code_templates/usb_gc_dumper.c index 4020ea3..bc50eac 100644 --- a/code_templates/usb_gc_dumper.c +++ b/code_templates/usb_gc_dumper.c @@ -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); diff --git a/include/core/gamecard.h b/include/core/gamecard.h index f90a114..e6117eb 100644 --- a/include/core/gamecard.h +++ b/include/core/gamecard.h @@ -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 } diff --git a/source/core/gamecard.c b/source/core/gamecard.c index 6d94566..59d9be1 100644 --- a/source/core/gamecard.c +++ b/source/core/gamecard.c @@ -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; }