diff --git a/.gitignore b/.gitignore index 7b8795b..02ed929 100644 --- a/.gitignore +++ b/.gitignore @@ -3,20 +3,9 @@ refs/* # Local backup files. *_old -# Build artifacts from lib/. -/lib/*.o -/lib/lib.a -/lib/lib-ps5.a -/lib/lib-elf.a -/lib/lib-elf-ps5.a + /lib/syscalls.asm /lib/syscalls-ps5.asm - -# Build artifacts from prosper0gdb/. -/prosper0gdb/*.o -/prosper0gdb/payload.elf -/prosper0gdb/payload.bin - # Build artifacts from ps5-kstuff-ldr/. /ps5-kstuff-ldr/kstuff.elf /ps5-kstuff-ldr/payload_bin.c @@ -33,3 +22,9 @@ refs/* /ps5-kstuff/uelf/uelf.bin /ps5-kstuff/debug-reader.bin /ps5-kstuff/debug-reader.elf +/ps5-kstuff/.build-config +logs/ +*.bin +*.elf +*.o +*.a diff --git a/ps5-kstuff/Makefile b/ps5-kstuff/Makefile index c0660ae..b1a297a 100644 --- a/ps5-kstuff/Makefile +++ b/ps5-kstuff/Makefile @@ -5,6 +5,7 @@ UELF_ARCH ?= -march=znver2 -mtune=znver2 KSTUFF_OBS ?= 0 ALL_TARGETS := payload.bin +BUILD_CONFIG := .build-config ifeq ($(KSTUFF_OBS),1) ifndef PS5_PAYLOAD_SDK @@ -16,7 +17,19 @@ endif all: $(ALL_TARGETS) clean: - rm -f payload.elf payload.bin debug-reader.elf debug-reader.bin freebsd-loader kelf kelf.o structs.h structs.inc uelf/uelf uelf/uelf.bin uelf/crt.o + rm -f payload.elf payload.bin debug-reader.elf debug-reader.bin freebsd-loader kelf kelf.o structs.h structs.inc uelf/uelf uelf/uelf.bin uelf/crt.o $(BUILD_CONFIG) + +.PHONY: FORCE +FORCE: + +$(BUILD_CONFIG): FORCE + @tmp="$@.tmp"; \ + printf 'KSTUFF_OBS=%s\n' "$(KSTUFF_OBS)" > "$$tmp"; \ + printf 'EXTRA_CFLAGS=%s\n' "$(EXTRA_CFLAGS)" >> "$$tmp"; \ + printf 'PAYLOAD_OPT=%s\n' "$(PAYLOAD_OPT)" >> "$$tmp"; \ + printf 'UELF_OPT=%s\n' "$(UELF_OPT)" >> "$$tmp"; \ + printf 'UELF_ARCH=%s\n' "$(UELF_ARCH)" >> "$$tmp"; \ + if test -f "$@" && cmp -s "$$tmp" "$@"; then rm -f "$$tmp"; else mv "$$tmp" "$@"; fi ../lib/lib-elf-ps5.a: cd ../lib; $(MAKE) @@ -27,7 +40,7 @@ clean: structs.inc: structs-ps5.inc cp structs-ps5.inc structs.inc -payload.elf: ../lib/lib-elf-ps5.a ../prosper0gdb/prosper0gdb.o main.c kelf uelf/uelf.bin +payload.elf: ../lib/lib-elf-ps5.a ../prosper0gdb/prosper0gdb.o main.c kelf uelf/uelf.bin $(BUILD_CONFIG) gcc $(PAYLOAD_OPT) -isystem ../freebsd-headers -nostdinc -nostdlib -fno-stack-protector -static ../lib/lib-elf-ps5.a ../prosper0gdb/prosper0gdb.o $(EXTRA_CFLAGS) -DKSTUFF_OBS=$(KSTUFF_OBS) main.c -DPS5KEK ../prosper0gdb/dbg.c -Wl,-gc-sections -o payload.elf -fPIE -ffreestanding -no-pie -Wl,-z,max-page-size=16384 -Wl,-z,common-page-size=16384 payloa%.bin: payloa%.elf @@ -46,7 +59,7 @@ structs.h: structs.inc uelf/crt.o: uelf/crt.asm yasm uelf/crt.asm -f elf64 -o uelf/crt.o -uelf/uelf: uelf/*.c uelf/*.h structs.h uelf/crt.o BearSSL/build/libbearssl.a isa-l_crypto/bin/isa-l_crypto.a +uelf/uelf: uelf/*.c uelf/*.h structs.h uelf/crt.o BearSSL/build/libbearssl.a isa-l_crypto/bin/isa-l_crypto.a $(BUILD_CONFIG) gcc -Wl,-Bsymbolic -Wl,-gc-sections -ffunction-sections -fdata-sections $(UELF_OPT) $(UELF_ARCH) -g -isystem ../freebsd-headers -isystem isa-l_crypto/include -nostdinc -nostdlib -mgeneral-regs-only -fno-stack-protector -fPIE -fPIC -shared -fvisibility=hidden -ffreestanding uelf/crt.o $(EXTRA_CFLAGS) -DKSTUFF_OBS=$(KSTUFF_OBS) uelf/*.c BearSSL/build/libbearssl.a isa-l_crypto/bin/isa-l_crypto.a -o uelf/uelf -Wl,-z,max-page-size=4096 uelf/uelf.bin: uelf/uelf diff --git a/ps5-kstuff/debug-reader.c b/ps5-kstuff/debug-reader.c index a7e6a8c..979a3da 100644 --- a/ps5-kstuff/debug-reader.c +++ b/ps5-kstuff/debug-reader.c @@ -247,7 +247,6 @@ static void print_metrics(const struct kstuff_metrics* metrics) PRINT_FIELD("emu", metrics->crypto_requests_emulated); PRINT_FIELD("fallback", metrics->crypto_requests_fallback); PRINT_FIELD("fail", metrics->crypto_requests_failed); - PRINT_FIELD("restart", metrics->crypto_requests_restarted); PRINT_FIELD("msg", metrics->crypto_messages_total); PRINT_FIELD("xts_msg", metrics->crypto_messages_xts); PRINT_FIELD("hmac_msg", metrics->crypto_messages_hmac); @@ -259,8 +258,6 @@ static void print_metrics(const struct kstuff_metrics* metrics) PRINT_FIELD("req", metrics->xts_requests); PRINT_FIELD("sectors", metrics->xts_sectors); PRINT_FIELD("run_msg", metrics->xts_run_messages_total); - PRINT_FIELD("coalesced", metrics->xts_run_coalesced_messages); - PRINT_FIELD("skipped", metrics->xts_run_skip_sectors); PRINT_FIELD("direct_runs", metrics->xts_full_direct_runs); PRINT_FIELD("direct_sectors", metrics->xts_full_direct_sectors); PRINT_FIELD("fallback", metrics->xts_full_fallback_sectors); @@ -291,7 +288,6 @@ static void print_metrics(const struct kstuff_metrics* metrics) PRINT_FIELD("hmac_shape", metrics->fpkg_reject_hmac_bad_shape); PRINT_FIELD("other", metrics->fpkg_reject_other_message); PRINT_FIELD("no_emu", metrics->fpkg_request_no_emulation); - PRINT_FIELD("partial", metrics->fpkg_request_partial_emulation); PRINT_FIELD("fpu_fail", metrics->fpkg_request_fpu_enter_fail); tee_putc('\n'); diff --git a/ps5-kstuff/uelf/fpkg.c b/ps5-kstuff/uelf/fpkg.c index 3206bf0..b10550d 100644 --- a/ps5-kstuff/uelf/fpkg.c +++ b/ps5-kstuff/uelf/fpkg.c @@ -128,8 +128,7 @@ static struct crypto_message_info inspect_crypto_message(const uint64_t msg_data } static struct crypto_message_result handle_hmac_message(uint64_t msg, const uint64_t msg_data[21], - int key_idx, uint64_t bytes_cap, - uint64_t* bytes_handled, + int key_idx, struct crypto_request_cache* cache) { struct crypto_message_result result = unhandled_crypto_message(msg); @@ -138,8 +137,7 @@ static struct crypto_message_result handle_hmac_message(uint64_t msg, const uint return result; uint8_t hash[32] = {0}; - *bytes_handled += msg_data[1]; - if(bytes_cap < *bytes_handled && pfs_hmac_virtual_fpu_held(cache, hash, key_idx, key, msg_data[2], msg_data[1])) + if(pfs_hmac_virtual_fpu_held(cache, hash, key_idx, key, msg_data[2], msg_data[1])) { result.emulated_messages = 1; result.status = -1; @@ -156,10 +154,9 @@ static struct crypto_message_result handle_hmac_message(uint64_t msg, const uint return result; } -static struct crypto_message_result handle_xts_message_run(uint64_t msg, const uint64_t first_msg_data[21], - int key_idx, - uint64_t bytes_cap, uint64_t* bytes_handled, - struct crypto_request_cache* cache) +static struct crypto_message_result handle_xts_message(uint64_t msg, const uint64_t msg_data[21], + int key_idx, + struct crypto_request_cache* cache) { struct crypto_message_result result = unhandled_crypto_message(msg); uint8_t key[32]; @@ -167,60 +164,20 @@ static struct crypto_message_result handle_xts_message_run(uint64_t msg, const u return result; METRIC_INC(xts_run_messages_total); - uint64_t src = first_msg_data[2]; - uint64_t dst = first_msg_data[3]; - uint64_t start_sector = first_msg_data[4]; - uint32_t total_sectors = (uint32_t)first_msg_data[1]; - int is_encrypt = (first_msg_data[0] & 0x800) >> 11; - uint64_t cursor = msg; + uint64_t src = msg_data[2]; + uint64_t dst = msg_data[3]; + uint64_t start_sector = msg_data[4]; + uint32_t total_sectors = (uint32_t)msg_data[1]; + int is_encrypt = (msg_data[0] & 0x800) >> 11; - while(result.next_msg) + if(pfs_xts_virtual_fpu_held(cache, dst, src, key_idx, key, start_sector, total_sectors, is_encrypt)) { - uint64_t next_msg_data[21]; - if(read_crypto_message(result.next_msg, next_msg_data)) - break; - if(!is_xts_message(next_msg_data)) - break; - uint32_t next_sectors = (uint32_t)next_msg_data[1]; - if(HANDLE_TO_IDX(next_msg_data[5]) != key_idx - || (int)((next_msg_data[0] & 0x800) >> 11) != is_encrypt - || next_msg_data[2] != src + ((uint64_t)total_sectors << 12) - || next_msg_data[3] != dst + ((uint64_t)total_sectors << 12) - || next_msg_data[4] != start_sector + total_sectors - || total_sectors > UINT32_MAX - next_sectors) - break; - - total_sectors += next_sectors; - cursor = result.next_msg; - result.next_msg = kpeek64(cursor + 320); - result.total_messages++; - } - - uint64_t run_bytes = (uint64_t)total_sectors << 12; - uint64_t skip_bytes = 0; - if(bytes_cap > *bytes_handled) - skip_bytes = bytes_cap - *bytes_handled; - if(skip_bytes >= run_bytes) - skip_bytes = run_bytes; - uint32_t skip_sectors = (uint32_t)(skip_bytes >> 12); - *bytes_handled += run_bytes; - METRIC_ADD(xts_run_skip_sectors, skip_sectors); - - if(skip_sectors < total_sectors) - { - if(pfs_xts_virtual_fpu_held(cache, dst + ((uint64_t)skip_sectors << 12), - src + ((uint64_t)skip_sectors << 12), key_idx, key, - start_sector + skip_sectors, total_sectors - skip_sectors, is_encrypt)) - { - result.emulated_messages = result.total_messages; - result.status = -1; - return result; - } + result.emulated_messages = 1; + result.status = -1; + return result; } - result.emulated_messages = result.total_messages; - if(result.total_messages > 1) - METRIC_ADD(xts_run_coalesced_messages, result.total_messages - 1); + result.emulated_messages = 1; result.status = 0; return result; } @@ -234,7 +191,7 @@ static inline uint64_t rdtsc(void) } */ -static int handle_crypto_request(uint64_t* regs, uint64_t bytes_handled) +static int handle_crypto_request(uint64_t* regs) { METRIC_TIME_START(start_cycles); // uint64_t start_time = rdtsc(); @@ -243,7 +200,6 @@ static int handle_crypto_request(uint64_t* regs, uint64_t bytes_handled) int total_status = 0; int handled = 0; int fpu_entered = 0; - uint64_t new_bytes_handled = 0; METRIC_INC(crypto_requests_total); uint64_t start = (FWVER >= 0x800) ? regs[RBX] : regs[R14]; @@ -277,30 +233,13 @@ static int handle_crypto_request(uint64_t* regs, uint64_t bytes_handled) struct crypto_message_result result; if(msg_info.kind == CRYPTO_MESSAGE_XTS) - result = handle_xts_message_run(msg, msg_data, msg_info.key_idx, bytes_handled, &new_bytes_handled, &s_crypto_request_cache); + result = handle_xts_message(msg, msg_data, msg_info.key_idx, &s_crypto_request_cache); else if(msg_info.kind == CRYPTO_MESSAGE_HMAC) - result = handle_hmac_message(msg, msg_data, msg_info.key_idx, bytes_handled, &new_bytes_handled, &s_crypto_request_cache); + result = handle_hmac_message(msg, msg_data, msg_info.key_idx, &s_crypto_request_cache); else result = unhandled_crypto_message(msg); int status = result.status; - if (status == EINTR) // partial decrypt, need to restart the syscall - { - METRIC_INC(crypto_requests_restarted); - uint64_t frame[6] = { - MKTRAP(TRAP_FPKG, 2), 0, 0, 0, 0, - new_bytes_handled, - }; - if(push_stack_checked(regs, frame, sizeof(frame))) - { - total_status = -1; - break; - } - regs[RIP] = (uint64_t)doreti_iret; - handled = 1; - goto exit; - } - total += result.total_messages; if (status != ENOSYS) @@ -319,7 +258,6 @@ static int handle_crypto_request(uint64_t* regs, uint64_t bytes_handled) { // not all requests successfully emulated // we can't run only part of the request, so just report failure - METRIC_INC(fpkg_request_partial_emulation); total_status = -1; } @@ -350,7 +288,7 @@ int try_handle_fpkg_trap(uint64_t* regs) { if(regs[RIP] == (uint64_t)sceSblServiceCryptAsync_deref_singleton) { - if(handle_crypto_request(regs, 0)) + if(handle_crypto_request(regs)) observe_current_syscall_emulated(); else { @@ -469,14 +407,6 @@ void handle_fpkg_trap(uint64_t* regs, uint32_t trapno) regs[RIP] = frame[11]; regs[RAX] = 0; } - else if(trapno == 2) - { - uint64_t frame[6]; - if(pop_stack_checked(regs, frame, sizeof(frame))) - return; - regs[RIP] = (uint64_t)sceSblServiceCryptAsync_deref_singleton; - handle_crypto_request(regs, frame[5]); - } } static const uint64_t dbgregs_for_nmount[6] = { diff --git a/ps5-kstuff/uelf/fself.c b/ps5-kstuff/uelf/fself.c index 295ec97..9d77cb7 100644 --- a/ps5-kstuff/uelf/fself.c +++ b/ps5-kstuff/uelf/fself.c @@ -16,11 +16,6 @@ static uint64_t s_auth_info_for_exec_ps4[17] = {0x3100000000000001, 0x2000038000 enum { SELF_BLOCK_SIZE = 16384 }; -static int fself_block_ranges_overlap(uint64_t src, uint64_t dst, size_t size) -{ - return src < dst ? dst - src < size : src - dst < size; -} - static void copy_decrypted_self_blocks(char* dmem, const uint64_t* src, const uint64_t* dst, uint32_t count) { for(uint32_t i = 0; i < count;) @@ -42,11 +37,9 @@ static void copy_decrypted_self_blocks(char* dmem, const uint64_t* src, const ui continue; } - // Overlapping runs rely on per-block copy order. while(i + 1 < count && src[i + 1] == run_src + run_size - && dst[i + 1] == run_dst + run_size - && !fself_block_ranges_overlap(run_src, run_dst, run_size + SELF_BLOCK_SIZE)) + && dst[i + 1] == run_dst + run_size) { i++; run_size += SELF_BLOCK_SIZE; @@ -324,6 +317,7 @@ void handle_fself_trap(uint64_t* regs, uint32_t trapno) int try_handle_fself_mailbox(uint64_t* regs, uint64_t lr) { METRIC_TIME_START(start_cycles); + int emulated = 0; #define RETURN_FSELF_MAILBOX(value) do { METRIC_TIME(fself_mailbox_cycles_total, fself_mailbox_cycles_max, start_cycles); return (value); } while(0) /* * Telemetry shows the mailbox hot order is decryptSelfBlock, decryptMultipleSelfBlocks, @@ -353,6 +347,7 @@ int try_handle_fself_mailbox(uint64_t* regs, uint64_t lr) memcpy(DMEM+request[1], DMEM+request[2], (uint32_t)request[6]); regs[RAX] = 0; METRIC_INC(fself_mailbox_decrypt_self_block_emulated); + emulated = 1; } } else if(lr == (uint64_t)sceSblServiceMailbox_lr_decryptMultipleSelfBlocks) @@ -383,6 +378,7 @@ int try_handle_fself_mailbox(uint64_t* regs, uint64_t lr) copy_decrypted_self_blocks(DMEM, src, dst, request[5]); regs[RAX] = 0; METRIC_INC(fself_mailbox_decrypt_multiple_self_blocks_emulated); + emulated = 1; } } else if(lr == (uint64_t)sceSblServiceMailbox_lr_verifyHeader) @@ -397,14 +393,14 @@ int try_handle_fself_mailbox(uint64_t* regs, uint64_t lr) if(kpeek64_checked(self_context + 56, &self_header)) RETURN_FSELF_MAILBOX(0); if(!is_header_fself(self_header, size, 0, 0, 0, 0)) - RETURN_FSELF_MAILBOX(1); + RETURN_FSELF_MAILBOX(FSELF_HANDLE_HANDLED); if(copy_from_kernel(ctx_data, self_context, sizeof(ctx_data))) RETURN_FSELF_MAILBOX(0); if(ctx_data[7] != self_header) { self_header = ctx_data[7]; if(!is_header_fself(self_header, size, 0, 0, 0, 0)) - RETURN_FSELF_MAILBOX(1); + RETURN_FSELF_MAILBOX(FSELF_HANDLE_HANDLED); } remember_context_fself_info(self_context, self_header, size, ctx_data); char fself_header_backup[(48 + mini_syscore_header_size + 15) & -16]; @@ -434,6 +430,7 @@ int try_handle_fself_mailbox(uint64_t* regs, uint64_t lr) RETURN_FSELF_MAILBOX(0); } METRIC_INC(fself_mailbox_verify_header_emulated); + emulated = 1; } else if(lr == (uint64_t)sceSblServiceMailbox_lr_loadSelfSegment) { @@ -459,18 +456,20 @@ int try_handle_fself_mailbox(uint64_t* regs, uint64_t lr) RETURN_FSELF_MAILBOX(0); regs[RAX] = 0; METRIC_INC(fself_mailbox_load_self_segment_emulated); + emulated = 1; } } else RETURN_FSELF_MAILBOX(0); - RETURN_FSELF_MAILBOX(1); + RETURN_FSELF_MAILBOX(FSELF_HANDLE_HANDLED | (emulated ? FSELF_HANDLE_EMULATED : 0)); #undef RETURN_FSELF_MAILBOX } int try_handle_fself_trap(uint64_t* regs) { METRIC_TIME_START(start_cycles); + int emulated = 0; #define RETURN_FSELF_TRAP(value) do { METRIC_TIME(fself_trap_cycles_total, fself_trap_cycles_max, start_cycles); return (value); } while(0) /* * The hot trap order is watchpoint, epilogue, then SmIsLoadable2. @@ -481,24 +480,31 @@ int try_handle_fself_trap(uint64_t* regs) METRIC_INC(fself_trap_watchpoint); uint64_t frame[4]; if(copy_from_kernel(frame, regs[RSP], sizeof(frame))) - RETURN_FSELF_TRAP(1); + RETURN_FSELF_TRAP(FSELF_HANDLE_HANDLED); regs[(FWVER >= 0x800) ? RAX : R10] |= 0xffffull << 48; if(frame[3] == (uint64_t)loadSelfSegment_watchpoint_lr) { - if(!set_dbgregs_for_watchpoint(regs, dbgregs_for_loadSelfSegment, sizeof(frame))) - RETURN_FSELF_TRAP(1); + if(set_dbgregs_for_watchpoint(regs, dbgregs_for_loadSelfSegment, sizeof(frame))) + emulated = 1; + else + RETURN_FSELF_TRAP(FSELF_HANDLE_HANDLED); } else if(frame[3] == (uint64_t)decryptSelfBlock_watchpoint_lr) { - if(!set_dbgregs_for_watchpoint(regs, dbgregs_for_decryptSelfBlock, sizeof(frame))) - RETURN_FSELF_TRAP(1); + if(set_dbgregs_for_watchpoint(regs, dbgregs_for_decryptSelfBlock, sizeof(frame))) + emulated = 1; + else + RETURN_FSELF_TRAP(FSELF_HANDLE_HANDLED); } else if(frame[3] == (uint64_t)decryptMultipleSelfBlocks_watchpoint_lr) { - if(!set_dbgregs_for_watchpoint(regs, dbgregs_for_decryptMultipleSelfBlocks, sizeof(frame))) - RETURN_FSELF_TRAP(1); + if(set_dbgregs_for_watchpoint(regs, dbgregs_for_decryptMultipleSelfBlocks, sizeof(frame))) + emulated = 1; + else + RETURN_FSELF_TRAP(FSELF_HANDLE_HANDLED); } - METRIC_INC(fself_trap_watchpoint_emulated); + if(emulated) + METRIC_INC(fself_trap_watchpoint_emulated); } else if(regs[RIP] == (uint64_t)loadSelfSegment_epilogue || regs[RIP] == (uint64_t)decryptSelfBlock_epilogue @@ -506,8 +512,9 @@ int try_handle_fself_trap(uint64_t* regs) { METRIC_INC(fself_trap_epilogue); if(!unset_dbgregs_for_watchpoint(regs)) - RETURN_FSELF_TRAP(1); + RETURN_FSELF_TRAP(FSELF_HANDLE_HANDLED); METRIC_INC(fself_trap_epilogue_emulated); + emulated = 1; } else if(regs[RIP] == (uint64_t)sceSblAuthMgrSmIsLoadable2) { @@ -537,20 +544,21 @@ int try_handle_fself_trap(uint64_t* regs) p_authinfo = s_auth_info_for_exec; } if(copy_u64_from_kernel(&ret_addr, regs[RSP])) - RETURN_FSELF_TRAP(1); + RETURN_FSELF_TRAP(FSELF_HANDLE_HANDLED); if(copy_to_kernel(regs[R8], p_authinfo, 0x88)) - RETURN_FSELF_TRAP(1); + RETURN_FSELF_TRAP(FSELF_HANDLE_HANDLED); if(copy_u16_to_kernel(regs[RDI] + 62, 0xdeb7)) - RETURN_FSELF_TRAP(1); + RETURN_FSELF_TRAP(FSELF_HANDLE_HANDLED); regs[RSP] += 8; regs[RIP] = ret_addr; regs[RAX] = 0; METRIC_INC(fself_trap_is_loadable2_emulated); + emulated = 1; } } else RETURN_FSELF_TRAP(0); - RETURN_FSELF_TRAP(1); + RETURN_FSELF_TRAP(FSELF_HANDLE_HANDLED | (emulated ? FSELF_HANDLE_EMULATED : 0)); #undef RETURN_FSELF_TRAP } diff --git a/ps5-kstuff/uelf/fself.h b/ps5-kstuff/uelf/fself.h index 62090ca..961fe2a 100644 --- a/ps5-kstuff/uelf/fself.h +++ b/ps5-kstuff/uelf/fself.h @@ -10,5 +10,13 @@ void handle_fself_syscall(uint64_t* regs); void handle_fself_trap(uint64_t* regs, uint32_t trapno); + +#define FSELF_HANDLE_HANDLED 1 +#if KSTUFF_OBS +#define FSELF_HANDLE_EMULATED 2 +#else +#define FSELF_HANDLE_EMULATED 0 +#endif + int try_handle_fself_trap(uint64_t* regs); -int try_handle_fself_mailbox(uint64_t* regs, uint64_t lr); \ No newline at end of file +int try_handle_fself_mailbox(uint64_t* regs, uint64_t lr); diff --git a/ps5-kstuff/uelf/log.c b/ps5-kstuff/uelf/log.c index 2dca0b2..08b0f32 100644 --- a/ps5-kstuff/uelf/log.c +++ b/ps5-kstuff/uelf/log.c @@ -101,25 +101,19 @@ static void observe_syscall_metric(enum kstuff_syscall_tag tag, int kind) } #undef OBS_CASE } -#endif void log_word(uint64_t word) { -#if KSTUFF_OBS uint64_t seq = __atomic_fetch_add(&shared_area.word_log.next_seq, 1, __ATOMIC_RELAXED); struct kstuff_word_log_entry* entry = &shared_area.word_log.entries[seq % SHARED_LOG_WORD_CAP]; __atomic_store_n(&entry->seq, 0, __ATOMIC_RELAXED); entry->word = word; __atomic_store_n(&entry->seq, seq + 1, __ATOMIC_RELEASE); METRIC_INC(log_word_writes); -#else - (void)word; -#endif } void log_msg(const char* msg) { -#if KSTUFF_OBS if(!msg) return; size_t len = strlen(msg); @@ -143,14 +137,10 @@ void log_msg(const char* msg) spin_unlock_u64(&shared_area.msg_log.write_lock); METRIC_INC(log_msg_writes); METRIC_ADD(log_msg_bytes, len); -#else - (void)msg; -#endif } void observe_ioctl_com_emulated(uint64_t com, uint32_t cmd) { -#if KSTUFF_OBS spin_lock_u64(&shared_area.ioctl_com_table.write_lock); struct kstuff_ioctl_com_entry* entry = find_or_add_ioctl_com_entry_locked(com); if(entry) @@ -164,69 +154,50 @@ void observe_ioctl_com_emulated(uint64_t com, uint32_t cmd) entry->cmd6_hits++; } spin_unlock_u64(&shared_area.ioctl_com_table.write_lock); -#else - (void)com; - (void)cmd; -#endif } void observe_ioctl_com_total(uint64_t com) { -#if KSTUFF_OBS struct kstuff_ioctl_com_entry* entry = find_tracked_ioctl_com_entry(com); if(entry) __atomic_fetch_add(&entry->total_hits, 1, __ATOMIC_RELAXED); -#else - (void)com; -#endif } void observe_syscall_armed(enum kstuff_syscall_tag tag) { -#if KSTUFF_OBS s_current_syscall_state.tag = tag; s_current_syscall_state.trap_reported = 0; s_current_syscall_state.emu_reported = 0; observe_syscall_metric(tag, 0); -#else - (void)tag; -#endif } void observe_current_syscall_trap(void) { -#if KSTUFF_OBS if(s_current_syscall_state.tag != KSTUFF_SYSCALL_NONE && !s_current_syscall_state.trap_reported) { s_current_syscall_state.trap_reported = 1; observe_syscall_metric(s_current_syscall_state.tag, 1); } -#endif } void observe_current_syscall_emulated(void) { -#if KSTUFF_OBS if(s_current_syscall_state.tag != KSTUFF_SYSCALL_NONE && !s_current_syscall_state.emu_reported) { s_current_syscall_state.emu_reported = 1; observe_syscall_metric(s_current_syscall_state.tag, 2); } -#endif } void observe_current_syscall_finish(void) { -#if KSTUFF_OBS s_current_syscall_state.tag = KSTUFF_SYSCALL_NONE; s_current_syscall_state.trap_reported = 0; s_current_syscall_state.emu_reported = 0; -#endif } -#if KSTUFF_OBS static int virt2phys_untracked(uint64_t addr, uint64_t* phys, uint64_t* phys_limit) { uint64_t pml = cr3_phys; @@ -357,11 +328,5 @@ int copy_shared_area_snapshot(uint64_t dst, uint64_t sz) return EFAULT; return 0; } -#else -int copy_shared_area_snapshot(uint64_t dst, uint64_t sz) -{ - (void)dst; - (void)sz; - return ENOSYS; -} + #endif diff --git a/ps5-kstuff/uelf/log.h b/ps5-kstuff/uelf/log.h index 6e54b50..744b5c3 100644 --- a/ps5-kstuff/uelf/log.h +++ b/ps5-kstuff/uelf/log.h @@ -19,6 +19,7 @@ enum kstuff_syscall_tag KSTUFF_SYSCALL_MDBG_CALL, }; +#if KSTUFF_OBS void log_word(uint64_t word); void log_msg(const char* msg); int copy_shared_area_snapshot(uint64_t dst, uint64_t sz); @@ -28,3 +29,23 @@ void observe_syscall_armed(enum kstuff_syscall_tag tag); void observe_current_syscall_trap(void); void observe_current_syscall_emulated(void); void observe_current_syscall_finish(void); +#else +static inline void log_word(uint64_t word) { (void)word; } +static inline void log_msg(const char* msg) { (void)msg; } +static inline int copy_shared_area_snapshot(uint64_t dst, uint64_t sz) +{ + (void)dst; + (void)sz; + return -1; +} +static inline void observe_ioctl_com_total(uint64_t com) { (void)com; } +static inline void observe_ioctl_com_emulated(uint64_t com, uint32_t cmd) +{ + (void)com; + (void)cmd; +} +static inline void observe_syscall_armed(enum kstuff_syscall_tag tag) { (void)tag; } +static inline void observe_current_syscall_trap(void) {} +static inline void observe_current_syscall_emulated(void) {} +static inline void observe_current_syscall_finish(void) {} +#endif diff --git a/ps5-kstuff/uelf/mailbox.c b/ps5-kstuff/uelf/mailbox.c index fb9afba..d58c499 100644 --- a/ps5-kstuff/uelf/mailbox.c +++ b/ps5-kstuff/uelf/mailbox.c @@ -14,11 +14,13 @@ int try_handle_mailbox_trap(uint64_t* regs) { METRIC_INC(mailbox_traps); uint64_t lr = kpeek64(regs[RSP]); - if(try_handle_fself_mailbox(regs, lr)) + int fself_result = try_handle_fself_mailbox(regs, lr); + if(fself_result & FSELF_HANDLE_HANDLED) { METRIC_INC(mailbox_fself); observe_current_syscall_trap(); - observe_current_syscall_emulated(); + if(fself_result & FSELF_HANDLE_EMULATED) + observe_current_syscall_emulated(); return 1; } if(try_handle_fpkg_mailbox(regs, lr)) diff --git a/ps5-kstuff/uelf/main.c b/ps5-kstuff/uelf/main.c index ac7fa75..0b491ac 100644 --- a/ps5-kstuff/uelf/main.c +++ b/ps5-kstuff/uelf/main.c @@ -300,11 +300,13 @@ static inline int handle_kernel_trap_fast(uint64_t* regs, uint64_t rip) } if(is_fself_trap_rip(rip)) { - if(try_handle_fself_trap(regs)) + int fself_result = try_handle_fself_trap(regs); + if(fself_result & FSELF_HANDLE_HANDLED) { METRIC_INC(fself_traps); observe_current_syscall_trap(); - observe_current_syscall_emulated(); + if(fself_result & FSELF_HANDLE_EMULATED) + observe_current_syscall_emulated(); return 1; } } diff --git a/ps5-kstuff/uelf/npdrm.c b/ps5-kstuff/uelf/npdrm.c index 83df39e..ebbce30 100644 --- a/ps5-kstuff/uelf/npdrm.c +++ b/ps5-kstuff/uelf/npdrm.c @@ -1,7 +1,6 @@ // https://github.com/PS5Dev/Byepervisor/blob/57204cbd7bd26ed4623634d52b0f60f40d630087/hen/src/fpkg.cpp#L82 #include -#include #include #include "utils.h" #include "npdrm.h" @@ -23,25 +22,21 @@ extern char sceSblServiceMailbox[]; static const uint8_t rif_debug_key[] = {0x96, 0xC2, 0x26, 0x8D, 0x69, 0x26, 0x1C, 0x8B, 0x1E, 0x3B, 0x6B, 0xFF, 0x2F, 0xE0, 0x4E, 0x12}; enum { AES128_EXPKEY_SIZE = 16 * 11 }; -enum { - NPDRM_EXPECT_CMD5 = 1u << 0, - NPDRM_EXPECT_CMD6 = 1u << 1, -}; +#if KSTUFF_OBS static struct { -#if KSTUFF_OBS uint64_t com; -#endif - uint8_t expected_cmd_mask; - uint8_t valid; + int valid; } s_current_ioctl_state; +#endif +#if KSTUFF_OBS void finish_npdrm_ioctl_state(void) { - s_current_ioctl_state.expected_cmd_mask = 0; s_current_ioctl_state.valid = 0; } +#endif static struct { @@ -113,26 +108,12 @@ int try_handle_npdrm_mailbox(uint64_t *regs, uint64_t lr) METRIC_INC(npdrm_reject_bad_lr); RETURN_NPDRM(0); } - cmd = (lr == (uint64_t)sceSblServiceMailbox_lr_npdrm_cmd_5) ? 5u : 6u; - if (s_current_ioctl_state.valid) - { - const uint8_t lr_cmd_mask = (cmd == 5u) ? NPDRM_EXPECT_CMD5 : NPDRM_EXPECT_CMD6; - if (!(s_current_ioctl_state.expected_cmd_mask & lr_cmd_mask)) - { - METRIC_INC(npdrm_reject_bad_cmd); - RETURN_NPDRM(1); - } - } if (copy_from_kernel(&request_hdr, regs[RDX], sizeof(request_hdr))) { METRIC_INC(npdrm_reject_copy_in_fail); RETURN_NPDRM(1); } - if (request_hdr.cmd != cmd) - { - METRIC_INC(npdrm_reject_bad_cmd); - RETURN_NPDRM(1); - } + cmd = request_hdr.cmd; #else if (copy_from_kernel(&request_hdr, regs[RDX], sizeof(request_hdr))) { @@ -211,7 +192,7 @@ int try_handle_npdrm_mailbox(uint64_t *regs, uint64_t lr) #ifdef NPDRM_PORTING if (cmd == 6) #else - if (cmd == 6) + if (lr == (uint64_t)sceSblServiceMailbox_lr_npdrm_cmd_6) #endif { uint8_t decrypted_secret[sizeof(rif->rifSecret)]; @@ -293,96 +274,21 @@ static const uint64_t dbgregs_for_ioctl[6] = { (uint64_t)sceSblServiceMailbox, 0, 0, 0, 0, 0x401}; -static uint8_t classify_npdrm_ioctl(uint64_t com) -{ - const uint32_t dir = (uint32_t)(com & IOC_DIRMASK); - const uint32_t len = IOCPARM_LEN(com); - const uint32_t group = IOCGROUP(com); - const uint32_t num = (uint32_t)(com & 0xff); - - /* - * Hot path from observed logs: - * IOC_INOUT + group 'N' (0x4e) - * with the useful commands clustered in len=0x20 and len=0x34. - * - * Keep the rest of the known-good commands in the rare exact tail below - * so coverage stays the same while the common case gets cheaper. - */ - if(__builtin_expect(dir == IOC_INOUT && group == 0x4e, 1)) - { - if(len == 0x20) - { - if(num == 0x0d || num == 0x0e) - return NPDRM_EXPECT_CMD5 | NPDRM_EXPECT_CMD6; - if(num == 0x16) - return NPDRM_EXPECT_CMD6; - } - else if(len == 0x34 && num == 0x18) - return NPDRM_EXPECT_CMD6; - } - - switch(com) - { - case 0x000000004004ae25ULL: - case 0x0000000040105305ULL: - case 0x0000000080048138ULL: - case 0x000000008008483eULL: - case 0x000000008010480fULL: - case 0x000000008018910bULL: - case 0x0000000080206216ULL: - case 0x0000000080208218ULL: - case 0x0000000080283208ULL: - case 0x0000000080308217ULL: - case 0x00000000c0044512ULL: - case 0x00000000c00c5a02ULL: - case 0x00000000c0108102ULL: - case 0x00000000c01c8f07ULL: - case 0x00000000c02066a5ULL: - case 0x00000000c0288907ULL: - case 0x00000000c0288908ULL: - case 0x00000000c028ab00ULL: - case 0x00000000c030ab00ULL: - case 0x00000000c0484851ULL: - return NPDRM_EXPECT_CMD5 | NPDRM_EXPECT_CMD6; - default: - return 0; - } -} - void handle_ioctl_syscall(uint64_t *regs) { +#if KSTUFF_OBS struct ioctl_args uap; - uint8_t expected_cmd_mask; + finish_npdrm_ioctl_state(); if(copy_from_kernel(&uap, regs[RSI], sizeof(uap))) { - finish_npdrm_ioctl_state(); -#if KSTUFF_OBS METRIC_INC(ioctl_prefilter_copy_in_fail_open); -#endif - start_syscall_with_dbgregs(regs, dbgregs_for_ioctl); - return; } - -#if KSTUFF_OBS - s_current_ioctl_state.com = (uint64_t)uap.com; -#endif - finish_npdrm_ioctl_state(); - - expected_cmd_mask = classify_npdrm_ioctl((uint64_t)uap.com); - if(!expected_cmd_mask) + else { -#if KSTUFF_OBS - METRIC_INC(ioctl_prefilter_skipped); -#endif - observe_current_syscall_finish(); - return; + s_current_ioctl_state.com = (uint64_t)uap.com; + s_current_ioctl_state.valid = 1; + observe_ioctl_com_total(s_current_ioctl_state.com); } - -#if KSTUFF_OBS - METRIC_INC(ioctl_prefilter_allowed); - observe_ioctl_com_total(s_current_ioctl_state.com); #endif - s_current_ioctl_state.expected_cmd_mask = expected_cmd_mask; - s_current_ioctl_state.valid = 1; start_syscall_with_dbgregs(regs, dbgregs_for_ioctl); } diff --git a/ps5-kstuff/uelf/npdrm.h b/ps5-kstuff/uelf/npdrm.h index 7f2da7a..5934a1e 100644 --- a/ps5-kstuff/uelf/npdrm.h +++ b/ps5-kstuff/uelf/npdrm.h @@ -84,4 +84,8 @@ struct RifCmd56MemoryLayout int try_handle_npdrm_mailbox(uint64_t *regs, uint64_t lr); void handle_ioctl_syscall(uint64_t *regs); +#if KSTUFF_OBS void finish_npdrm_ioctl_state(void); +#else +static inline void finish_npdrm_ioctl_state(void) {} +#endif diff --git a/ps5-kstuff/uelf/shared_area.h b/ps5-kstuff/uelf/shared_area.h index 32bc701..0c78048 100644 --- a/ps5-kstuff/uelf/shared_area.h +++ b/ps5-kstuff/uelf/shared_area.h @@ -7,13 +7,18 @@ #endif enum { - SHARED_AREA_SIZE = 8192, SHARED_FAKE_KEY_SLOTS = 63, SHARED_LOG_WORD_CAP = 16, SHARED_LOG_MSG_CAP = 488, SHARED_IOCTL_COM_TRACK_CAP = 128, }; +#if KSTUFF_OBS +enum { SHARED_AREA_SIZE = 8192 }; +#else +enum { SHARED_AREA_SIZE = 2048 }; +#endif + struct kstuff_metrics { uint64_t handle_entries; @@ -162,7 +167,6 @@ struct kstuff_metrics uint64_t crypto_requests_emulated; uint64_t crypto_requests_fallback; uint64_t crypto_requests_failed; - uint64_t crypto_requests_restarted; uint64_t crypto_messages_total; uint64_t crypto_messages_xts; uint64_t crypto_messages_hmac; @@ -173,8 +177,6 @@ struct kstuff_metrics uint64_t xts_requests; uint64_t xts_sectors; uint64_t xts_run_messages_total; - uint64_t xts_run_coalesced_messages; - uint64_t xts_run_skip_sectors; uint64_t xts_full_direct_runs; uint64_t xts_full_direct_sectors; @@ -213,7 +215,6 @@ struct kstuff_metrics uint64_t fpkg_reject_hmac_bad_shape; uint64_t fpkg_reject_other_message; uint64_t fpkg_request_no_emulation; - uint64_t fpkg_request_partial_emulation; uint64_t fpkg_request_fpu_enter_fail; uint64_t shared_area_snapshots; @@ -273,10 +274,12 @@ struct shared_area_layout uint64_t ready_mask; char pad[16]; uint8_t key_data[SHARED_FAKE_KEY_SLOTS][32]; +#if KSTUFF_OBS struct kstuff_metrics metrics; struct kstuff_word_log word_log; struct kstuff_ioctl_com_table ioctl_com_table; struct kstuff_msg_log msg_log; +#endif }; extern struct shared_area_layout shared_area; @@ -292,15 +295,19 @@ extern struct shared_area_layout shared_area; } while(0) #else #define METRIC_INC(field) do { } while(0) -#define METRIC_ADD(field, value) do { (void)(value); } while(0) -#define METRIC_MAX(field, value) do { (void)(value); } while(0) +#define METRIC_ADD(field, value) do { } while(0) +#define METRIC_MAX(field, value) do { } while(0) #endif -_Static_assert(sizeof(struct kstuff_metrics) == 1544, "unexpected metrics size"); +_Static_assert(sizeof(struct kstuff_metrics) == 1512, "unexpected metrics size"); _Static_assert(sizeof(struct kstuff_word_log) == 264, "unexpected word log size"); _Static_assert(sizeof(struct kstuff_ioctl_com_entry) == 24, "unexpected ioctl com entry size"); _Static_assert(sizeof(struct kstuff_ioctl_com_table) == 3088, "unexpected ioctl com table size"); _Static_assert(sizeof(struct kstuff_msg_log) == 504, "unexpected message log size"); -_Static_assert(sizeof(struct kstuff_snapshot) == 5416, "unexpected snapshot size"); -_Static_assert(sizeof(struct shared_area_layout) == 7448, "unexpected shared_area size"); +_Static_assert(sizeof(struct kstuff_snapshot) == 5384, "unexpected snapshot size"); +#if KSTUFF_OBS +_Static_assert(sizeof(struct shared_area_layout) == 7416, "unexpected shared_area size"); +#else +_Static_assert(sizeof(struct shared_area_layout) == 2048, "unexpected non-OBS shared_area size"); +#endif _Static_assert(sizeof(struct shared_area_layout) <= SHARED_AREA_SIZE, "shared_area must fit in configured mapping"); diff --git a/ps5-kstuff/uelf/utils.c b/ps5-kstuff/uelf/utils.c index fa55f48..07a8199 100644 --- a/ps5-kstuff/uelf/utils.c +++ b/ps5-kstuff/uelf/utils.c @@ -455,7 +455,7 @@ void handle_utils_trap(uint64_t* regs, uint32_t trapno) uint64_t stack_frame[12]; if(peek_stack_checked(regs, stack_frame, sizeof(stack_frame))) return; - if(restore_dbgregs_state_checked(stack_frame+5, stack_frame[4])) + if(restore_dbgregs_state_checked(stack_frame+5, stack_frame[3])) return; regs[RSP] += sizeof(stack_frame); regs[RIP] = stack_frame[11]; diff --git a/ps5-kstuff/uelf/utils.h b/ps5-kstuff/uelf/utils.h index a6df577..147c428 100644 --- a/ps5-kstuff/uelf/utils.h +++ b/ps5-kstuff/uelf/utils.h @@ -48,10 +48,8 @@ static inline uint64_t uelf_rdtsc(void) METRIC_MAX(max_field, _metric_elapsed); \ } while(0) #else -#define METRIC_TIME_START(var) uint64_t var __attribute__((unused)) = 0 -#define METRIC_TIME(total_field, max_field, start_cycles) do { \ - (void)(start_cycles); \ -} while(0) +#define METRIC_TIME_START(var) enum { var = 0 } +#define METRIC_TIME(total_field, max_field, start_cycles) do { } while(0) #endif static inline int kpeek64_checked(uintptr_t kptr, uint64_t* value)