Last active
July 31, 2021 14:36
-
-
Save xerpi/0e682d594c5def602750c523ee491098 to your computer and use it in GitHub Desktop.
PSVita Reverse Engineering
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* SceKermit (MIPS side): | |
* - https://gist.github.com/TheOfficialFloW/4fdec09e53a7c93a0d07d9e3e982dff4#file-kermit-c-L145 | |
* - https://github.com/Total-Noob/kermit_reverse/blob/master/main.c | |
*/ | |
/* | |
* Initial state: | |
* MIPS ARM | |
* Consumer ready = true Producer ready = true | |
* Response ready = false Command ready = false | |
* | |
* Sequence diagram: | |
* | |
* MIPS ARM | |
* | | compat_wait_and_get_request() | |
* | |<----------------------------------------- | |
* | | - Wait command ready | |
* | | (blocked) | |
* Send command | | | |
* --------------------------------->| | | |
* - Wait consumer ready | | | |
* - Write command to SRAM | | | |
* - Send interrupt [68-70] |-------Interrupt-------->|************************ | |
* - Wait response ready (blocked) | | Push command to CQ * | |
* *************************|<------Interrupt---------| Send interrupt [4-6] * | |
* * Signal consumer ready | | Signal command ready * | |
* *************************| |************************ | |
* | | - Wake from command ready | |
* | | - Pop command from CQ and return | |
* | | ----------------------------------------> | |
* | | | |
* | | compat_wait_intr() | |
* | |<----------------------------------------- | |
* | | - Wait producer ready | |
* | |-----------------------------------------> | |
* | | | |
* | | compat_return_value_ex() | |
* | |<----------------------------------------- | |
* | | - Write response to SRAM | |
* **************************|<------Interrupt---------| - Send interrupt [7-9] | |
* * Read response value | |-----------------------------------------> | |
* * Signal response ready | | | |
* * Send interrupt [71-73] |-------Interrupt-------->|************************ | |
* **************************| | Signal producer ready * | |
* - Wake from response ready | |************************ | |
* - Return response | | | |
* <---------------------------------| | | |
* | | | |
* | | | |
*/ | |
typedef enum SceKermitMode { | |
SCE_KERMIT_MODE_NONE, | |
SCE_KERMIT_MODE_UNK_1, | |
SCE_KERMIT_MODE_UNK_2, | |
SCE_KERMIT_MODE_MSFS, | |
SCE_KERMIT_MODE_FLASHFS, | |
SCE_KERMIT_MODE_AUDIOOUT, | |
SCE_KERMIT_MODE_ME, | |
SCE_KERMIT_MODE_LOWIO, | |
SCE_KERMIT_MODE_POCS_USBPSPCM, | |
SCE_KERMIT_MODE_PERIPHERAL, | |
SCE_KERMIT_MODE_WLAN, | |
SCE_KERMIT_MODE_AUDIOIN, | |
SCE_KERMIT_MODE_USB, | |
SCE_KERMIT_MODE_UTILITY, | |
SCE_KERMIT_MODE_EXTRA_1, | |
SCE_KERMIT_MODE_EXTRA_2 | |
} SceKermitMode; | |
typedef enum SceKermitVirtualInterrupt { | |
SCE_KERMIT_VIRTUAL_INTR_NONE, | |
SCE_KERMIT_VIRTUAL_INTR_AUDIO_CH1, | |
SCE_KERMIT_VIRTUAL_INTR_AUDIO_CH2, | |
SCE_KERMIT_VIRTUAL_INTR_AUDIO_CH3, | |
SCE_KERMIT_VIRTUAL_INTR_ME_DMA_CH1, | |
SCE_KERMIT_VIRTUAL_INTR_ME_DMA_CH2, | |
SCE_KERMIT_VIRTUAL_INTR_ME_DMA_CH3, | |
SCE_KERMIT_VIRTUAL_INTR_WLAN_CH1, | |
SCE_KERMIT_VIRTUAL_INTR_WLAN_CH2, | |
SCE_KERMIT_VIRTUAL_INTR_IMPOSE_CH1, | |
SCE_KERMIT_VIRTUAL_INTR_POWER_CH1, | |
SCE_KERMIT_VIRTUAL_INTR_UNKNOWN_CH1, // <- used after settings | |
SCE_KERMIT_VIRTUAL_INTR_USBGPS_CH1, | |
SCE_KERMIT_VIRTUAL_INTR_USBPSPCM_CH1 | |
} KermitVirtualInterrupt; | |
typedef enum SceKermitArgumentMode { | |
SCE_KERMIT_INPUT_MODE = 0x1, | |
SCE_KERMIT_OUTPUT_MODE = 0x2 | |
} SceKermitArgumentMode; | |
typedef enum SceCompatCacheMode { | |
SCE_COMPAT_CACHE_NONE = 0, | |
SCE_COMPAT_CACHE_INVALIDATE = 1, | |
SCE_COMPAT_CACHE_WRITEBACK = 2 | |
} SceCompatCacheMode; | |
typedef enum SceCompatPeripheralMode { | |
SCE_COMPAT_PERIPHERAL_POWER_IS_SUSPEND_REQUIRED = 1, | |
SCE_COMPAT_PERIPHERAL_POWER_GET_BATTERY_LIFETIME = 2, | |
SCE_COMPAT_PERIPHERAL_POWER_GET_BATTERY_PERCENT = 3, | |
SCE_COMPAT_PERIPHERAL_HPREMOTE_IS_HEADPHONE_EXIST = 4 | |
} SceCompatPeripheralMode; | |
/* API structures */ | |
typedef struct SceCompatCdram { | |
void *cached_cdram; | |
void *uncached_cdram; | |
} SceCompatCdram; | |
typedef struct SceCtrlDataPsp { | |
unsigned int timestamp; | |
unsigned int buttons; | |
unsigned char lx; | |
unsigned char ly; | |
unsigned char rx; | |
unsigned char ry; | |
unsigned char reserved[4]; | |
} SceCtrlDataPsp; // 0x10 | |
typedef struct SceKermitRequest { | |
uint32_t cmd; //0x0 | |
SceUID sema_id; //0x4 | |
uint64_t *response; //0x8 | |
uint32_t padding; //0xC | |
uint64_t args[14]; // 0x10 | |
} SceKermitRequest; //0x80 | |
typedef struct SceKermitReturnParam { | |
SceUID sema_id; //0x0 | |
uint64_t *response; //0x4 | |
} SceKermitReturnParam; //0x8 | |
/* Internal structures */ | |
typedef struct SceCompatCircularQueue { | |
SceKermitRequest *request[16]; | |
uint32_t head; | |
uint32_t tail; | |
uint32_t size; | |
uint32_t full; | |
} SceCompatCircularQueue; /* size = 0x50 */ | |
/* Shared structures */ | |
// MIPS -> ARM. 0xBFC00800 | |
typedef struct SceKermitCommand { | |
uint32_t cmd; //0x00 | |
SceKermitRequest *request; //0x04 | |
} SceKermitCommand; //0x8 | |
// ARM -> MIPS. 0xBFC00840 | |
typedef struct SceKermitResponse { | |
uint64_t result; //0x0 | |
SceUID sema_id; //0x8 | |
int32_t unk_C; //0xC | |
uint64_t *response; //0x10 | |
uint32_t padding; //0x14 | |
uint64_t unk_18; //0x18 | |
} SceKermitResponse; //0x20 | |
// ARM -> MIPS. 0xBFC008C0 | |
typedef struct SceKermitInterrupt { | |
int32_t flag; //0x0 | |
int32_t unused; //0x4 | |
} SceKermitInterrupt; //0x8 | |
// 0xE5070000 | |
typedef struct SceCompatMailbox { | |
uint32_t unk00[32]; // 0x000, for Intr IDs 64 - 74 | |
uint32_t unk80[32]; // 0x080, for Intr IDs 64 - 74 | |
} SceCompatMailbox; /* size = 0x1000 */ | |
// 0xE8100000 on ARM, 0xBFC00000 on MIPS | |
typedef struct SceCompatSharedSram { | |
uint8_t unk000[0x100]; // 0x000 | |
uint32_t shared32_0_80; // 0x100 | |
uint32_t shared32_81; // 0x104 | |
uint32_t shared32_82; // 0x108 | |
uint32_t shared32_4; // 0x10c | |
uint32_t shared32_5; // 0x110 | |
uint32_t shared32_6; // 0x114 | |
uint32_t unk118; // 0x118 | |
uint32_t unk11c; // 0x11c | |
SceCtrlDataPsp pad_data; // 0x120 | |
uint8_t unk130[0x10]; // 0x130 | |
uint32_t pad_data_timestamp; // 0x140 | |
uint8_t unk144[0x2c]; // 0x144 | |
uint32_t shared32_1_83; // 0x170 | |
uint32_t shared32_84; // 0x174 | |
uint32_t shared32_85; // 0x178 | |
uint32_t shared32_2_86; // 0x17c | |
uint32_t shared32_87; // 0x180 | |
uint32_t shared32_88; // 0x184 | |
uint32_t shared32_7; // 0x188 | |
uint32_t unk18c; // 0x18c | |
uint8_t unk190[0x650]; // 0x190 | |
SceKermitCommand kermit_command[10]; // 0x7E0, Each for Intr IDs 64 - 74 | |
uint32_t unk830; // 0x830 | |
uint32_t unk834; // 0x834 | |
uint32_t unk838; // 0x838 | |
uint32_t unk83c; // 0x83c | |
SceKermitResponse kermit_response[3]; // 0x840 | |
uint8_t unk8a0[0x20]; // 0x8a0 | |
SceKermitInterrupt kermit_intr[16]; // 0x8c0 | |
uint8_t unk940[0x6B8]; // 0x940 | |
uint32_t unkff8; // 0xff8 | |
uint32_t unkffc; // 0xffc | |
} SceCompatSharedSram; /* size = 0x1000 */ | |
static SceUID g_compat_mutex; // Default value = 0 | |
// Command ready semaphore | |
static SceUID g_compat_virtual_handler_sema[16]; // Default value: [0] = 1, rest = 0 | |
// Producer ready semaphore | |
static SceUID g_compat_r_sema[3]; // Default value = 1 | |
static SceCompatCircularQueue g_circular_queue[16]; | |
static SceCompatSharedSram *g_shared_sram_addr; | |
static SceCompatMailbox *g_mailbox_addr; | |
#if COMPILE | |
// Triggered when MIPS is ready to receive a Response (wakes compat_wait_intr()) | |
int SceCompatNr_intr_handler(int intr_id, SceUID sema) | |
{ | |
// Same as g_compat_r_sema[intr_id - 71 (SceCompat0r)] | |
ksceKernelSignalSema(sema, 1); | |
return -1; | |
} | |
// Triggered when MIPS sends a Command (wakes compat_wait_and_get_request()) | |
int SceCompatNc_intr_handler(int intr_id) | |
{ | |
SceKermitCommand *cmd; | |
uint32_t tail; | |
uint32_t mode; | |
uint32_t cur_size; | |
command = &g_shared_sram_addr.kermit_command[intr_id - 64]; | |
mode = command->cmd >> 16; | |
if (mode < 16) { | |
tail = g_circular_queue[mode].tail; | |
cur_size = g_circular_queue[mode].size; | |
if (cur_size < 16) { | |
g_circular_queue[mode].request[tail] = command->request; | |
g_circular_queue[mode].tail = (tail + 1) % 16; | |
g_circular_queue[mode].size = cur_size + 1; | |
g_mailbox_addr->unk80[intr_id - 64] = 0xffffffff; /* MIPS Intr IDs 4-6 */ | |
dsb(0xf); | |
g_mailbox_addr->unk00[intr_id - 64] = 0xffffffff; | |
dsb(0xf); | |
} else { | |
g_circular_queue[mode].full = 1; | |
} | |
ksceKernelSignalSema(g_compat_virtual_handler_sema[mode], 1); | |
} | |
return -1; | |
} | |
// Waits for MIPS to send an Interrupt | |
int compat_wait_intr(uint32_t intr_index) | |
{ | |
if (intr_index >= 3) | |
return 0x80020005; | |
return ksceKernelWaitSema(g_compat_r_sema[intr_index], 1, NULL); | |
} | |
// Waits for MIPS to send a Command | |
int compat_wait_and_get_request(SceKermitMode mode, uint32_t intr_index) | |
{ | |
SceCompatMailbox *mailbox; | |
int ret; | |
int flags; | |
uint32_t head; | |
uint32_t cur_size; | |
uint32_t tail; | |
SceKermitRequest *request; | |
uint32_t timeout; | |
if ((mode >= 16) || (intr_index >= 3)) | |
return 0x80020005; | |
ret = ksceKernelWaitSema(g_compat_virtual_handler_sema[mode], 1, NULL); | |
if (ret < 0) | |
return ret; | |
timeout = 5000000; | |
ret = ksceKernelLockMutex(g_compat_mutex, 1, &timeout); | |
if (ret < 0) | |
return 0x80028005; | |
flags = ksceKernelCpuDisableInterrupts(); | |
head = g_circular_queue[mode].head; | |
cur_size = g_circular_queue[mode].size; | |
request = g_circular_queue[mode].request[head]; | |
g_circular_queue[mode].head = (head + 1) % 16; | |
if (cur_size == 0) { | |
ksceKernelCpuEnableInterrupts(flags); | |
ksceKernelUnlockMutex(g_compat_mutex, 1); | |
return 0x80010016; | |
} | |
g_circular_queue[mode].size = cur_size - 1; | |
if (g_circular_queue[mode].full) { | |
tail = g_circular_queue[mode].tail; | |
g_circular_queue[mode].request[tail] = | |
g_shared_sram_addr->kermit_command[intr_index + 4].request; | |
g_circular_queue[mode].tail = (tail + 1) % 16; | |
g_mailbox_addr->unk80[intr_index + 4] = 0xffffffff; /* MIPS Intr IDs 4-6 */ | |
dsb(0xf); | |
g_mailbox_addr->unk00[intr_index + 4] = 0xffffffff; | |
dsb(0xf); | |
g_circular_queue[mode].full = 0; | |
} | |
ksceKernelCpuEnableInterrupts(flags); | |
ksceKernelUnlockMutex(g_compat_mutex, 1); | |
ksceKernelCpuDcacheInvalidateRange | |
((void *)(((uint32_t) request & 0x3ffffff) + SceCompatCdram_memblock_addr), | |
0x80); | |
dsb(0xf); | |
return (int)((uint32_t)request >> 1); | |
} | |
// Returns the Response to a Command that was initiated by MIPS | |
int compat_return_value_ex(uint32_t intr_index, const SceKermitReturnParam *param, uint64_t result) | |
{ | |
int ret; | |
uint32_t *kermit_response; | |
uint32_t lr; | |
uint32_t timeout; | |
if (intr_index >= 3) | |
return 0x80020005; | |
timeout = 5000000; | |
ret = ksceKernelLockMutex(g_compat_mutex, 1, &timeout); | |
if (ret < 0) { | |
return 0x80028005; | |
} | |
if (DAT_8100606c == 0) { | |
ksceKernelUnlockMutex(g_compat_mutex, 1); | |
return 0x80027205; | |
} | |
kermit_response = &g_shared_sram_addr->kermit_response[intr_index]; | |
if ((kermit_response < g_shared_sram_addr) || (&g_shared_sram_addr->unkff8 < kermit_response)) { | |
ksceKernelUnlockMutex(g_compat_mutex, 1); | |
return 0x8002000b; | |
} | |
g_shared_sram_addr->kermit_response[intr_index].result = result; | |
g_shared_sram_addr->kermit_response[intr_index].sema_id = param->sema_id; | |
g_shared_sram_addr->kermit_response[intr_index].unk_C = 0; | |
g_shared_sram_addr->kermit_response[intr_index].response = param->response; | |
g_shared_sram_addr->kermit_response[intr_index].padding = 0; | |
dsb(0xf); | |
dsb(0xf); | |
g_mailbox_addr->unk80[intr_index + 7] = 0xffffffff; /* MIPS Intr IDs 7-9 */ | |
dsb(0xf); | |
g_mailbox_addr->unk00[intr_index + 7] = 0xffffffff; | |
dsb(0xf); | |
ksceKernelUnlockMutex(g_compat_mutex, 1); | |
return 0; | |
} | |
// Sends a Virtual Interrupt to MIPS | |
int compat_interrupt(SceKermitVirtualInterrupt intr_code) | |
{ | |
int ret; | |
uint64_t start_time; | |
uint32_t timeout; | |
if (intr_code >= 16) | |
return 0x80010016; | |
timeout = 5000000; | |
ret = ksceKernelLockMutex(g_compat_mutex, 1, &timeout); | |
if (ret < 0) | |
return 0x80028005; | |
if (DAT_8100606c == 0) { | |
ksceKernelUnlockMutex(g_compat_mutex, 1); | |
return 0x80010086; | |
} | |
start_time = ksceKernelGetProcessTimeWideCore(); | |
do { | |
if (g_shared_sram_addr->kermit_intr[intr_code].flag == 0) | |
break; | |
} while (ksceKernelGetProcessTimeWideCore() < (start_time + 1000000)); | |
g_shared_sram_addr->kermit_intr[intr_code].flag = 1; | |
dsb(0xf); | |
g_mailbox_addr->unk80[intr_code + 16] = 0xffffffff; /* MIPS Intr IDs 16-31 */ | |
dsb(0xf); | |
g_mailbox_addr->unk00[intr_code + 16] = 0xffffffff; | |
dsb(0xf); | |
ksceKernelUnlockMutex(g_compat_mutex, 1); | |
return 0; | |
} | |
int compat_write_shared_ctrl(SceCtrlDataPsp *pad_data) | |
{ | |
int ret; | |
uint32_t timestamp; | |
timeout = 5000000; | |
ret = ksceKernelLockMutex(g_compat_mutex, 1, &timeout); | |
if (ret < 0) | |
return 0x80028005; | |
if (DAT_8100606c == 0) { | |
ksceKernelUnlockMutex(g_compat_mutex, 1); | |
return 0x80027205; | |
} | |
timestamp = pad_data->timestamp; | |
g_shared_sram_addr->pad_data.timestamp = timestamp; | |
dsb(0xf); | |
g_shared_sram_addr->pad_data.buttons = pad_data->buttons; | |
*(uint32_t *)&g_shared_sram_addr.pad_data.lx = *(uint32_t *)&pad_data->lx; | |
dsb(0xf); | |
g_shared_sram_addr->pad_data_timestamp = timestamp; | |
dsb(0xf); | |
dsb(0xf); | |
ksceKernelUnlockMutex(g_compat_mutex, 1); | |
return 0; | |
} | |
int compat_read_shared32(uint32_t location, uint32_t *value) | |
{ | |
int ret; | |
uint32_t timeout; | |
timeout = 5000000; | |
ret = ksceKernelLockMutex(g_compat_mutex, 1, &timeout); | |
if (ret < 0) | |
return 0x80028005; | |
if (DAT_8100606c != 0) { | |
ksceKernelUnlockMutex(g_compat_mutex, 1); | |
return 0x80027205; | |
} | |
ret = 0; | |
switch (location) { | |
case 0: | |
*value = g_shared_sram_addr->shared32_0; | |
break; | |
case 1: | |
*value = g_shared_sram_addr->shared32_1; | |
break; | |
case 2: | |
*value = g_shared_sram_addr->shared32_2; | |
break; | |
default: | |
ret = 0x80020005; | |
break; | |
case 4: | |
*value = g_shared_sram_addr->shared32_4; | |
break; | |
case 5: | |
*value = g_shared_sram_addr->shared32_5; | |
break; | |
case 6: | |
*value = g_shared_sram_addr->shared32_6; | |
break; | |
case 7: | |
*value = g_shared_sram_addr->shared32_7; | |
} | |
ksceKernelUnlockMutex(g_compat_mutex, 1); | |
return ret; | |
} | |
int compat_write_shared32(uint32_t location, uint32_t value) | |
{ | |
int ret; | |
uint32_t timeout; | |
timeout = 5000000; | |
ret = ksceKernelLockMutex(g_compat_mutex,1 , &timeout); | |
if (ret < 0) | |
return 0x80028005; | |
if (DAT_8100606c != 0) { | |
ksceKernelUnlockMutex(g_compat_mutex, 1); | |
return 0x80027205; | |
} | |
ret = 0; | |
switch (location) { | |
case 0x80: | |
g_shared_sram_addr->shared32_0_80 = value; | |
dsb(0xf); | |
break; | |
case 0x81: | |
g_shared_sram_addr->shared32_81 = value; | |
dsb(0xf); | |
break; | |
case 0x82: | |
g_shared_sram_addr->shared32_82 = value; | |
dsb(0xf); | |
break; | |
case 0x83: | |
g_shared_sram_addr->shared32_1_83 = value; | |
dsb(0xf); | |
break; | |
case 0x84: | |
g_shared_sram_addr->shared32_84 = value; | |
dsb(0xf); | |
break; | |
case 0x85: | |
g_shared_sram_addr->shared32_85 = value; | |
dsb(0xf); | |
break; | |
case 0x86: | |
g_shared_sram_addr->shared32_2_86 = value; | |
dsb(0xf); | |
break; | |
case 0x87: | |
g_shared_sram_addr->shared32_87 = value; | |
dsb(0xf); | |
break; | |
case 0x88: | |
g_shared_sram_addr->shared32_88 = value; | |
dsb(0xf); | |
break; | |
default: | |
ret = 0x80020005; | |
} | |
ksceKernelUnlockMutex(g_compat_mutex, 1); | |
return ret; | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment