From 1d00a1f96f648d4c1a6a0aa1a8cbed520067bb57 Mon Sep 17 00:00:00 2001 From: Li Date: Sat, 24 Feb 2024 21:16:04 +1300 Subject: [PATCH] add cart identify feature --- app/device.c | 43 +++++++ app/device.h | 1 + app/f00dbridge.h | 3 + app/log.h | 14 +++ app/main.c | 4 + app/menu.c | 55 ++++++++- app/menu.h | 5 +- kern/CMakeLists.txt | 2 + kern/gc.c | 28 +++++ kern/gc.h | 275 ++++++++++++++++++++++++++++++++++++++++++++ kern/kplugin.yml | 4 +- kern/otg.c | 8 +- 12 files changed, 431 insertions(+), 11 deletions(-) create mode 100644 kern/gc.c create mode 100644 kern/gc.h diff --git a/app/device.c b/app/device.c index b989871..cae7588 100644 --- a/app/device.c +++ b/app/device.c @@ -62,6 +62,49 @@ // exfatfs does each 0x20000 reading internally - Princess of Sleeping static uint8_t DEVICE_DUMP_BUFFER[0x20000]__attribute__((aligned(0x40))); +char* mmc_vendor_id_to_manufacturer(uint8_t vendorId) { + char* vendor = "Unknown"; + switch(vendorId) { + case 0x00: + vendor = "Sandisk"; + break; + case 0x02: + vendor = "Kingston or SanDisk"; + break; + case 0x03: + case 0x11: + vendor = "Toshiba"; + break; + case 0x13: + vendor = "Micron"; + break; + case 0x15: + vendor = "Samsung or SanDisk or LG"; + break; + case 0x37: + vendor = "KingMax"; + break; + case 0x44: + vendor = "ATP"; + break; + case 0x45: + vendor = "SanDisk Corporation"; + break; + case 0x2c: + case 0x70: + vendor = "Kingston"; + break; + case 0x90: + vendor = "Hynix"; + break; + case 0xfe: + vendor = "Micron"; + break; + } + + return vendor; +} + uint8_t device_exist(char* block_device) { int dfd = OpenDevice(block_device, SCE_O_RDONLY); diff --git a/app/device.h b/app/device.h index b3155c8..4aee974 100644 --- a/app/device.h +++ b/app/device.h @@ -5,6 +5,7 @@ #define BLOCK_DEVICE_GRW0 "sdstor0:gcd-lp-ign-gamerw" #define BLOCK_DEVICE_GRO0 "sdstor0:gcd-lp-ign-gamero" +char* mmc_vendor_id_to_manufacturer(uint8_t vendorId); uint64_t device_size(char* block_device); int dump_device_network(char* ip_address, unsigned short port, char* block_device, char* output_path, GcKEYS* keys, void (*progress_callback)(char*, char*, uint64_t, uint64_t)); int dump_device(char* block_device, char* output_path, GcKEYS* keys, void (*progress_callback)(char*, char*, uint64_t, uint64_t)); diff --git a/app/f00dbridge.h b/app/f00dbridge.h index fbfd609..3a0e88d 100644 --- a/app/f00dbridge.h +++ b/app/f00dbridge.h @@ -16,6 +16,9 @@ void GetDeviceSize(int device_handle, uint64_t* device_size); int FormatDevice(char* device); +int GetCardId(int deviceIndex, void* cardId); +int GetCardCsd(int deviceIndex, void* cardCsd); + typedef struct SceSblSmCommGcData { int always1; int command; diff --git a/app/log.h b/app/log.h index 4018958..1b64793 100644 --- a/app/log.h +++ b/app/log.h @@ -12,3 +12,17 @@ #define PRINT_BUFFER(buffer) /**/ #endif +#define TO_HEX(in, insz, out, outsz) \ +{ \ + unsigned char * pin = in; \ + const char * hex = "0123456789ABCDEF"; \ + char * pout = out; \ + for(; pin < in+insz; pout +=2, pin++){ \ + pout[0] = hex[(*pin>>4) & 0xF]; \ + pout[1] = hex[ *pin & 0xF]; \ + if (pout + 2 - out > outsz){ \ + break; \ + } \ + } \ + pout[-1] = 0; \ +} \ No newline at end of file diff --git a/app/main.c b/app/main.c index bd10d4f..6f1d787 100644 --- a/app/main.c +++ b/app/main.c @@ -319,6 +319,8 @@ void handle_menu_select_option() { break; case RESET_GRW0: break; + case GET_GC_INFO: + break; default: break; }; @@ -331,6 +333,8 @@ void handle_menu_select_option() { handle_wipe_option(selected); if(selected == WRITE_MEDIAID || selected == WRITE_GRW0) handle_select_input_device(selected); + if(selected == GET_GC_INFO) + do_device_info(); } int main() { diff --git a/app/menu.c b/app/menu.c index e6a9a46..5687cb2 100644 --- a/app/menu.c +++ b/app/menu.c @@ -113,9 +113,6 @@ void term_menus() { free_texture(insertgc_tex); } - - - void draw_wipe_progress(char* device, char* unused, uint64_t progress, uint64_t total) { RESTORE_MENU("Formatting", "Writing", device, device); } @@ -150,6 +147,8 @@ int draw_gc_options(int* selected, int* window, char* title, uint8_t has_grw0, u ADDOPT(has_grw0, "Restore Writable Section (.IMG)"); ADDOPT(has_grw0, "Format Writable Section"); + ADDOPT(1, "Get GC Information"); + end_draw(); RETURNOPT(); @@ -291,12 +290,60 @@ void draw_confirmation_message(char* title, char* msg) { draw_title(title); draw_text_center(200, msg); - draw_text_center(250, "Press any button to continue ..."); end_draw(); } +void draw_device_info(char* cardId, char* cardCsd, uint8_t vendorId) { + start_draw(); + draw_background(); + + draw_title("GC Information"); + + char msg[0x100]; + snprintf(msg, sizeof(msg), "CID: %s", cardId); + draw_text_center(200, msg); + + snprintf(msg, sizeof(msg), "CSD: %s", cardCsd); + draw_text_center(220, msg); + + snprintf(msg, sizeof(msg), "Vendor: %s (0x%02X)", mmc_vendor_id_to_manufacturer(vendorId), vendorId); + draw_text_center(240, msg); + + draw_text_center(280, "Press any button to continue ..."); + + end_draw(); +} + + +void do_device_info() { + char cardId[0xF]; + char cardCsd[0xF]; + + // cmd56 parms + uint16_t cardKeyId = 0x1; + char cardRandom[0x10]; + + char cardIdHex[0x100]; + char cardCsdHex[0x100]; + char cardRandomHex[0x100]; + + int cidRes = GetCardId(1, cardId); + int csdRes = GetCardCsd(1, cardCsd); + + if(cidRes >= 0 && csdRes >= 0){ + TO_HEX(cardId, sizeof(cardId), cardIdHex, sizeof(cardIdHex)); + TO_HEX(cardCsd, sizeof(cardCsd), cardCsdHex, sizeof(cardCsdHex)); + + uint8_t vendorId = cardId[0xF]; + + draw_device_info(cardIdHex, cardCsdHex, vendorId); + } + get_key(); + +} + int do_network_options(char* ip_address, unsigned short port) { PROCESS_MENU(draw_network_settings, ip_address, port); return selected; diff --git a/app/menu.h b/app/menu.h index 07cda3f..2ae12f0 100644 --- a/app/menu.h +++ b/app/menu.h @@ -12,6 +12,7 @@ void do_confirm_message(char* title, char* msg); int do_network_options(char* ip_address, unsigned short port); void do_ime(); int do_error(int error); +void do_device_info(); void init_menus(); void term_menus(); @@ -24,8 +25,8 @@ enum insert_menu_options { RESET_MEDIAID, DUMP_GRW0, WRITE_GRW0, - RESET_GRW0 - + RESET_GRW0, + GET_GC_INFO }; enum select_network_options { diff --git a/kern/CMakeLists.txt b/kern/CMakeLists.txt index 3fba058..68db410 100644 --- a/kern/CMakeLists.txt +++ b/kern/CMakeLists.txt @@ -27,6 +27,7 @@ add_executable(${PROJECT_NAME} otg.c format.c cmd56.c + gc.c ) target_link_libraries(${PROJECT_NAME} @@ -43,6 +44,7 @@ target_link_libraries(${PROJECT_NAME} SceSysmemForDriver_stub SceDebugForDriver_stub ScePowerForDriver_stub + SceSdifForDriver_stub SceSysconForDriver_stub SceSblGcAuthMgrGcAuthForDriver_stub SceSblGcAuthMgrDrmBBForDriver_stub diff --git a/kern/gc.c b/kern/gc.c new file mode 100644 index 0000000..8b11a2d --- /dev/null +++ b/kern/gc.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include "gc.h" + +int GetCardId(int deviceIndex, void* cardId) { + sd_context_part_mmc* k_deviceInfo = ksceSdifGetSdContextPartValidateMmc(deviceIndex); + if(k_deviceInfo == NULL) return -1; + + char k_cardId[sizeof(k_deviceInfo->ctxb.CID)]; + + memcpy(k_cardId, k_deviceInfo->ctxb.CID, sizeof(k_deviceInfo->ctxb.CID)); + ksceKernelMemcpyKernelToUser(cardId, (const void*)k_cardId, sizeof(k_cardId)); + + return 0; +} + +int GetCardCsd(int deviceIndex, void* cardCsd) { + sd_context_part_mmc* k_deviceInfo = ksceSdifGetSdContextPartValidateMmc(deviceIndex); + if(k_deviceInfo == NULL) return -1; + + char k_cardCsd[sizeof(k_deviceInfo->ctxb.CSD)]; + + memcpy(k_cardCsd, k_deviceInfo->ctxb.CSD, sizeof(k_deviceInfo->ctxb.CSD)); + ksceKernelMemcpyKernelToUser(cardCsd, (const void*)k_cardCsd, sizeof(k_cardCsd)); + + return 0; +} \ No newline at end of file diff --git a/kern/gc.h b/kern/gc.h new file mode 100644 index 0000000..3cea290 --- /dev/null +++ b/kern/gc.h @@ -0,0 +1,275 @@ +#ifndef GC_H +#define GC_H 1 + +// shamelessly stolen from the henkaku wiki. +typedef struct sd_context_part_base { + struct sd_context_global* gctx_ptr; + uint32_t unk_4; + uint32_t def_sector_size_mmc; // looks like default sector size - used in mmc read/write commands for resp_block_size_24 + uint32_t def_sector_size_sd; // looks like default sector size - used in sd read/write commands for resp_block_size_24 + + uint8_t unk_10; // can be padding + uint8_t CID[15]; // this is CID data but in reverse + + uint8_t unk_20; // can be padding + uint8_t CSD[15]; // this is CSD data but in reverse +} sd_context_part_base; + +typedef enum SceSdifDevice { + SCE_SDIF_DEVICE_EMMC = 0, + SCE_SDIF_DEVICE_GC = 1, + SCE_SDIF_DEVICE_SDIO = 2, + SCE_SDIF_DEVICE_USD = 3, +} SceSdifDevice; + +typedef enum SceSdifDeviceType { + SCE_SDIF_DEVICE_TYPE_INVALID = 0, + SCE_SDIF_DEVICE_TYPE_MMC = 1, + SCE_SDIF_DEVICE_TYPE_SD = 2, + SCE_SDIF_DEVICE_TYPE_SDIO = 3, +} SceSdifDeviceType; + +typedef struct cmd_info { + uint32_t state_flags; + uint32_t command; + uint32_t argument; + void* buffer; + uint16_t resp_block_size; + uint16_t resp_n_blocks; + + union { + struct { + char data[0x10]; + } db; + struct { + uint32_t dw0; + uint32_t dw1; + uint32_t dw2; + uint32_t dw3; + } dw; + } response; + + uint32_t error_code; +} cmd_info; + +typedef struct host_info { + void** host_registers; + uint32_t unk_4; + uint32_t base_clock; // = 48000000 dec + uint32_t bus_width; // = 1 / 4 / 8 (bits) + uint32_t clock_frequency; // = base_clock >> (SDCLK Frequency Select) + uint8_t timeout_control_register; + uint8_t specification_version_number; // = 1 / 2 / 3 + uint8_t vendor_version_number; + uint8_t unk_17; + uint32_t unk_18; + uint32_t unk_1C; + uint32_t unk_20; + uint32_t unk_24; +} host_info; + +typedef struct device_info { + uint32_t dev_type_idx; // (1,2,3) + uint32_t unk_4; + uint16_t unk_8; +} device_info; + +typedef struct sdif_context_general { //size is 0x40 + SceUID suspend_callback_id; + uint32_t max_array_index; // typically 3 + uint32_t unk_8; + uint32_t unk_C; + + uint32_t unk_10; + uint32_t unk_14; + uint32_t unk_18; + uint32_t unk_1C; + + uint32_t unk_20; + uint32_t unk_24; + uint32_t unk_28; + uint32_t unk_2C; + + uint32_t unk_30; + uint32_t unk_34; + uint32_t unk_38; + uint32_t unk_3C; +} sdif_context_general; + +typedef struct cmd_input { // size is 0x240 + uint32_t size; // 0x240 + + // bit 10 (shift left 0x15) - request invalidate flag - invalidate vaddr_1C0 and vaddr_200 + // this flag is used for CMD56 and CMD17 + // bit 20 (shift left 0xB) - request mem_188 free - free memblock with uid mem_188 + + // bit 20 or bit 9 cancels invalidation (both must be clear) + uint32_t state_flags; // interrupt handler completion flag + + uint32_t command; + uint32_t argument; + + // stores normal response without command index and crc-7 + // can also store CID or CSD. crr-7 will be cleared + // storage order is reversed + union { + struct { + char data[0x10]; + } db; + struct { + uint32_t dw0; + uint32_t dw1; + uint32_t dw2; + uint32_t dw3; + } dw; + } response; + + void* buffer; // cmd data buffer ptr - dest for vaddr_1C0 + uint16_t resp_block_size_24; // block size of response. typically 0x200 which is default sector size + uint16_t resp_n_blocks_26; // number of blocks in response. typically number of sectors to read/write + uint32_t error_code; // error code from interrupt handler (confirmed) + uint32_t unk_2C; + + uint8_t data0[0x30]; + + struct cmd_input* next_cmd; + uint32_t unk_64; // some flag. must be 3 for invalidation to happen + uint32_t array_index; + int(*set_event_flag_callback)(void* ctx); + + SceUID evid; // event id SceSdif0, SceSdif1, SceSdif2 (SceSdif3 ?) + struct cmd_input* secondary_cmd; // (when multiple commands are sent) + struct sd_context_global* gctx_ptr; + uint32_t unk_7C; + + char vaddr_80[0x80]; // 3 - mapped to paddr_184 (invalidate 0x80) + + void* vaddr_100; + uint8_t data_104[0x7C]; + + uint32_t unk_180; + void* paddr_184; // 3 - phys address of vaddr_80 + SceUID mem_188; // SceSdif memblock + uint32_t unk_18C; + + uint32_t unk_190; + uint32_t unk_194; + void* base_198; // dest base for vaddr_200 (also ptr for invalidate) + // data at base contains CMD17 data + // data at base also contains fragments of CMD56 response + // data at offset is unknown (zeroes) + uint32_t offset_19C; //dest offset for vaddr_200 (also size for invalidate) + + uint32_t size_1A0; // size of vaddr_1C0 - only valid if request invalidate flag is set + uint32_t size_1A4; // size of vaddr_200 - only valid if request invalidate flag is set + void* paddr_1A8; // 1 - phys address of vaddr_1C0 + void* paddr_1AC; // 2 - phys address of vaddr_200 + + SceInt64 wide_time1; // 0x1B0 + SceInt64 wide_time2; // 0x1B8 - relevant for commands that need to wait for data on DAT lines + + char vaddr_1C0[0x40]; // 1 - mapped to paddr_1A8 (invalidate 0x40) + // - only valid if request invalidate flag is set + // - contains fragments of CMD56 request/response + // - does not contain CMD17 data + + char vaddr_200[0x40]; // 2 - mapped to paddr_1AC (invalidate 0x40) + // - only valid if request invalidate flag is set + // - contains unknown data (zeroes) +} cmd_input; + + +typedef struct sd_context_data { // size is 0xC0 + struct cmd_input* cmd_ptr; + struct cmd_input* cmd_ptr_next; + uint32_t unk_8; + uint32_t unk_C; + + uint32_t dev_type_idx; // (1, 2, 3) + sd_context_part_base* ctx; // pointer to custom context (sd_context_part_mmc*, sd_context_part_sd*, sd_context_part_wlanbt*) + uint32_t voltages; // MMC_VDD_165_195, MMC_VDD_32_33, etc. Values seen: SDIF0 and SDIF2: 0x80, SDIF1 and SDIF3: 0x300000 + uint32_t unk_1C; + + uint32_t array_idx; // (0,1,2) + uint8_t unk_24; + uint8_t unk_25; + uint8_t unk_26; + uint8_t unk_27; + cmd_input* cmd_28; + cmd_input* cmd_2C; + + void** host_registers; // membase of SceSdif (0,1,2) memblock of size 0x1000 + uint32_t unk_34; + uint8_t unk_38; + uint8_t slow_mode; // relevant only for 2 read and 2 write functions + uint8_t unk_3A; + uint8_t unk_3B; + + SceUID host_registers_uid; // UID of SceSdif (0,1,2) memblock of size 0x1000 + + SceUID evid; // event id SceSdif0, SceSdif1, SceSdif2 (SceSdif3 ?) + void* sdif_fast_mutex; //size is 0x40 - SceSdif0, SceSdif1, SceSdif2 (SceSdif3 ?) + + //it looks like this chunk is separate structure since offset 0x2480 is used too often + + //offset 0x2484 + SceUID uid_10000; // UID of SceSdif (0,1,2) memblock of size 0x10000 + void* membase_10000; // membase of SceSdif (0,1,2) memblock of size 0x10000 + uint32_t unk_8C; + + uint32_t unk_90; + int lockable_int; + uint32_t unk_98; + uint32_t unk_9C; + + uint32_t unk_A0; + uint32_t unk_A4; + uint32_t unk_A8; + uint32_t unk_AC; + + uint32_t unk_B0; + uint32_t unk_B4; + uint32_t unk_B8; + uint32_t unk_BC; +} sd_context_data; + +typedef struct sd_context_part_mmc { // size is 0x398 + sd_context_part_base ctxb; + + uint8_t EXT_CSD[0x200]; // 0x30 + + uint8_t data_230[0x160]; + + void* unk_390; + uint32_t unk_394; +} sd_context_part_mmc; + +typedef struct sd_context_part_sd { // size is 0xC0 + sd_context_part_base ctxb; + + uint8_t data[0x90]; +} sd_context_part_sd; + +typedef struct sd_context_part_wlanbt { // size is 0x398 + struct sd_context_global* gctx_ptr; + + uint8_t data[0x394]; +} sd_context_part_wlanbt; + +typedef struct sd_context_global { // size is 0x24C0 + struct cmd_input commands[16]; + struct sd_context_data ctx_data; +} sd_context_global; + +typedef struct bulk_transfer { + uint32_t unk0; + uint32_t unk1; + uint32_t count; + uint32_t unk2; + uint32_t type; // 1: register access, 2: memory access + uint32_t unk3; + void * (*get_next)(void *); // callback to get next buffer + uint32_t unk4; +} bulk_transfer; + +#endif \ No newline at end of file diff --git a/kern/kplugin.yml b/kern/kplugin.yml index a294083..bf5e1e3 100644 --- a/kern/kplugin.yml +++ b/kern/kplugin.yml @@ -23,4 +23,6 @@ f00dbridge: - WriteDevice - CloseDevice - GetDeviceSize - - FormatDevice \ No newline at end of file + - FormatDevice + - GetCardCsd + - GetCardId \ No newline at end of file diff --git a/kern/otg.c b/kern/otg.c index 3393bdd..a2b6ca7 100644 --- a/kern/otg.c +++ b/kern/otg.c @@ -7,6 +7,7 @@ SceUID sceSysconSetOtgPowerLevelHook = -1; static tai_hook_ref_t sceSysconSetOtgPowerLevelHookRef; + static int return_1() { return 1; } @@ -31,7 +32,7 @@ int load_umass() { if(ksceKernelSearchModuleByName("SceUsbMass") < 0) { tai_hook_ref_t ksceSysrootIsSafeModeHookRef; tai_hook_ref_t ksceSblAimgrIsDolceHookRef; - + // temporarily patch isSafeMode and isDolce SceUID ksceSysrootIsSafeModeHook = taiHookFunctionExportForKernel(KERNEL_PID, &ksceSysrootIsSafeModeHookRef, @@ -54,11 +55,10 @@ int load_umass() { PRINT_STR("ksceSblAimgrIsDolceHook 0x%04X\n", ksceSblAimgrIsDolceHook); PRINT_STR("ksceSblAimgrIsDolceHookRef 0x%04X\n", ksceSblAimgrIsDolceHookRef); - // start from here, technically the module is actually in os0 bootimage - // but cannot start after system already started, unfortunately. + // load from the bootimage SceUID umass_modid = ksceKernelLoadStartModule("ux0:VitaShell/module/umass.skprx", 0, NULL, 0, NULL, NULL); PRINT_STR("Load umass.skprx 0x%04X\n", umass_modid); - + // release hooks if(ksceSysrootIsSafeModeHook > 0) taiHookReleaseForKernel(ksceSysrootIsSafeModeHook, ksceSysrootIsSafeModeHookRef); if(ksceSblAimgrIsDolceHook > 0) taiHookReleaseForKernel(ksceSblAimgrIsDolceHook, ksceSblAimgrIsDolceHookRef);