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);