Navigation Menu

Skip to content

Commit

Permalink
Merge pull request #5 from 5lipper/kaslr
Browse files Browse the repository at this point in the history
add support for kaslr on ps4 3.55, 4.00 and 4.01
  • Loading branch information
marcan committed Dec 18, 2016
2 parents cff8409 + 028f39d commit d081ecd
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 18 deletions.
9 changes: 3 additions & 6 deletions Makefile
@@ -1,11 +1,8 @@
ifdef DDO_NOT_REMAP_RWX
DO_NOT_REMAP_RWX := -DDO_NOT_REMAP_RWX
endif

CFLAGS := -march=btver2 -masm=intel -std=gnu11 -ffreestanding -fno-common \
CFLAGS=$(CFLAG)
CFLAGS += -march=btver2 -masm=intel -std=gnu11 -ffreestanding -fno-common \
-fPIC -fomit-frame-pointer -nostdlib -nostdinc \
-fno-asynchronous-unwind-tables \
-Os -Wall -Werror -Wl,--build-id=none,-T,kexec.ld,--nmagic $(DO_NOT_REMAP_RWX)
-Os -Wall -Werror -Wl,--build-id=none,-T,kexec.ld,--nmagic

SOURCES := kernel.c kexec.c linux_boot.c linux_thunk.S uart.c firmware.c

Expand Down
8 changes: 8 additions & 0 deletions README.md
Expand Up @@ -34,6 +34,14 @@ You may pass something other than NULL as `early_printf`. In that case, that
function will be used for debug output during early symbol resolution, before
printf is available.

Since PS4 3.55(?), KASLR(Kernel Address Space Layout Randomization) is
enabled by default, symtab also disappears in newer kernel, we have to
hardcode offsets for some symbols. Currently we use the `early_printf`
given by user to caculate the base address of kernel, then relocate all the
symbols from the kernel base. You could enable this feature like this:

make CFLAG='-DPS4_4_00 -DKASLR -DNO_SYMTAB'

If you do not want to call the syscall from userspace, you can pass the address
of a function pointer as `sys_kexec_ptr`. `kexec_init` will write to it the
address of `sys_kexec`, so you can invoke it manually (see kexec.h for
Expand Down
2 changes: 1 addition & 1 deletion firmware.c
Expand Up @@ -109,7 +109,7 @@ ssize_t firmware_extract(void *dest)
u8 *p = dest;

// Yeah, this calls it Starsha... Liverpool, Starsha, ThebeJ, whatever.
struct fw_info_t *info = kernel_resolve("Starsha_UcodeInfo");
struct fw_info_t *info = kern.Starsha_UcodeInfo;
if (!info) {
kern.printf("firmware_extract: Could not locate firmware table");
return -1;
Expand Down
48 changes: 44 additions & 4 deletions kernel.c
Expand Up @@ -12,15 +12,23 @@
#include "string.h"
#include "elf.h"
#include "x86.h"

#define KERNBASE 0xffffffff80000000ull
#define KERNSIZE 0x2000000
#include "magic.h"

struct ksym_t kern;
int (*early_printf)(const char *fmt, ...) = NULL;

#define eprintf(...) do { if (early_printf) early_printf(__VA_ARGS__); } while(0)

#ifdef NO_SYMTAB

#define RESOLVE(name) do {\
kern.name = (void *)(kern.kern_base + kern_off_ ## name); \
} while (0);

#else

#define KERNSIZE 0x2000000

static const u8 ELF_IDENT[9] = "\x7f" "ELF\x02\x01\x01\x09\x00";
static Elf64_Sym *symtab;
static char *strtab;
Expand All @@ -30,7 +38,7 @@ static Elf64_Ehdr *find_kern_ehdr(void)
{
// Search for the kernel copy embedded in ubios, then follow it to see
// where it was relocated to
for (uintptr_t p = KERNBASE; p < KERNBASE + KERNSIZE; p += PAGE_SIZE) {
for (uintptr_t p = kern.kern_base; p < kern.kern_base + KERNSIZE; p += PAGE_SIZE) {
Elf64_Ehdr *ehdr = (Elf64_Ehdr *)p;
if (!memcmp(ehdr->e_ident, ELF_IDENT, sizeof(ELF_IDENT))) {
for (size_t i = 0; i < ehdr->e_phnum; i++) {
Expand Down Expand Up @@ -87,6 +95,8 @@ void *kernel_resolve(const char *name)

#define RESOLVE(name) if (!(kern.name = kernel_resolve(#name))) return 0;

#endif

static int resolve_symbols(void)
{
RESOLVE(printf);
Expand All @@ -105,6 +115,8 @@ static int resolve_symbols(void)
RESOLVE(sched_unpin);
RESOLVE(smp_rendezvous);
RESOLVE(smp_no_rendevous_barrier);
RESOLVE(Starsha_UcodeInfo);
RESOLVE(icc_query_nowait);
return 1;
}

Expand Down Expand Up @@ -222,11 +234,38 @@ int kernel_init(void)
int rv = -1;
eprintf("kernel_init()\n");

#ifdef KASLR
// use `early_printf` to calculate kernel base
if (early_printf == NULL)
return 0;

kern.kern_base = (u64)(early_printf - kern_off_printf);
if ((kern.kern_base & PAGE_MASK) != 0) {
eprintf("Kernel base is not aligned\n");
return 0;
} else {
eprintf("Kernel base = %p\n", kern.kern_base);
}

u64 DMPML4I = *(u32 *)(kern.kern_base + kern_off_dmpml4i);
u64 DMPDPI = *(u32 *)(kern.kern_base + kern_off_dmpdpi);

#else
kern.kern_base = KVADDR(0x1ff, 0x1fe, 0, 0); // 0xffffffff80000000

u64 DMPML4I = 0x1fc;
u64 DMPDPI = 0;
#endif

kern.dmap_base = KVADDR(DMPML4I, DMPDPI, 0, 0);
eprintf("Direct map base = %p\n", kern.dmap_base);

// We may not be mapped writable yet, so to be able to write to globals
// we need WP disabled.
u64 flags = intr_disable();
u64 wp = write_protect_disable();

#ifndef NO_SYMTAB
Elf64_Ehdr *ehdr = find_kern_ehdr();
if (!ehdr) {
eprintf("Could not find kernel ELF header\n");
Expand All @@ -245,6 +284,7 @@ int kernel_init(void)
eprintf("Failed to parse ELF dynamic section\n");
goto err;
}
#endif

if (!resolve_symbols()) {
eprintf("Failed to resolve all symbols\n");
Expand Down
24 changes: 22 additions & 2 deletions kernel.h
Expand Up @@ -14,8 +14,22 @@
#include "types.h"

#define PAGE_SIZE 0x4000
#define PA_TO_DM(x) (((uintptr_t)x) | 0xfffffe0000000000ull)
#define DM_TO_ID(x) (((uintptr_t)x) & 0x000000ffffffffffull)
#define PAGE_MASK (PAGE_SIZE - 1)

#define PML4SHIFT 39
#define PDPSHIFT 30
#define PDRSHIFT 21
#define PAGE_SHIFT 12

#define KVADDR(l4, l3, l2, l1) ( \
((unsigned long)-1 << 47) | \
((unsigned long)(l4) << PML4SHIFT) | \
((unsigned long)(l3) << PDPSHIFT) | \
((unsigned long)(l2) << PDRSHIFT) | \
((unsigned long)(l1) << PAGE_SHIFT))

#define PA_TO_DM(x) (((uintptr_t)x) | kern.dmap_base)
#define DM_TO_ID(x) (((uintptr_t)x) & (~kern.dmap_base)) // XXX

typedef u64 vm_paddr_t;
typedef u64 vm_offset_t;
Expand All @@ -38,6 +52,10 @@ struct sysent_t {
};

struct ksym_t {
// two parameters related to kaslr (they are not symbols)
uintptr_t kern_base;
uintptr_t dmap_base;

int (*printf)(const char *fmt, ...);

int (*copyin)(const void *uaddr, void *kaddr, size_t len);
Expand All @@ -64,6 +82,8 @@ struct ksym_t {
smp_rendezvous_callback_t, void *);
// yes...it is misspelled :)
void (*smp_no_rendevous_barrier)(void *);
void *icc_query_nowait;
void *Starsha_UcodeInfo;
};

extern struct ksym_t kern;
Expand Down
3 changes: 2 additions & 1 deletion kexec.c
Expand Up @@ -25,9 +25,10 @@ int sys_kexec(void *td, struct sys_kexec_args *uap)
char *cmd_line = NULL;

kern.printf("sys_kexec invoked\n");
kern.printf("sys_kexec(%p, %ld, %p, %ld, \"%s\")\n", uap->image, uap->image_size, uap->initramfs, uap->initramfs_size, uap->cmd_line);

// Look up our shutdown hook point
void *icc_query_nowait = kernel_resolve("icc_query_nowait");
void *icc_query_nowait = kern.icc_query_nowait;
if (!icc_query_nowait) {
err = 2; // ENOENT
goto cleanup;
Expand Down
2 changes: 1 addition & 1 deletion linux_boot.c
Expand Up @@ -21,7 +21,7 @@ static u64 vram_base = 0x100000000;
// Current code assumes it's a power of two.
static u64 vram_size = 512 * 1024 * 1024;

#define DM_PML4_BASE 0x1fc
#define DM_PML4_BASE ((kern.dmap_base >> PML4SHIFT) & 0x1ff)

struct desc_ptr {
u16 limit;
Expand Down
5 changes: 4 additions & 1 deletion linux_thunk.S
Expand Up @@ -42,14 +42,17 @@ jmp_to_linux:
# TODO make sure we don't inadvertently overwrite (important) smap regions
# I think on ps4 we'll actually want to load to 0x700000
# since we have tons of free room there.
# on 4.00/4.01, bzImage might be allocated at 0x800000 accidently. We
# should choose a higher address as pref_address.

# save args
mov r12, rdi
mov r13, rsi

# memmove(pref_address, , (syssize * 0x10) / 8)
#mov rdi, [r12 + pref_address] # dst = [image_base + pref_address] ; where linux image wants to go
mov rdi, 0x700000 # where ps4 freebsd kernel is loaded (before relocating itself)
#mov rdi, 0x700000 # where ps4 freebsd kernel is loaded (before relocating itself)
mov rdi, 0x6000000 # should be far from bzImage and initramfs
mov r14, rdi # r14 = pref_address
xor edx, edx
mov dl, [r12 + setup_sects]
Expand Down
48 changes: 48 additions & 0 deletions magic.h
@@ -0,0 +1,48 @@
#ifdef PS4_3_55
#define kern_off_printf 0x1df550
#define kern_off_copyin 0x3b96e0
#define kern_off_copyout 0x3b9660
#define kern_off_copyinstr 0x3b9a50
#define kern_off_kmem_alloc_contig 0x337ea0
#define kern_off_kmem_free 0x33bca0
#define kern_off_pmap_extract 0x3afd70
#define kern_off_pmap_protect 0x3b1f50
#define kern_off_sched_pin 0x1ced60
#define kern_off_sched_unpin 0x1cedc0
#define kern_off_smp_rendezvous 0x1e7810
#define kern_off_smp_no_rendevous_barrier 0x1e75d0
#define kern_off_icc_query_nowait 0x3ed450
#define kern_off_kernel_map 0x196acc8
#define kern_off_sysent 0xeed880
#define kern_off_kernel_pmap_store 0x19bd628
#define kern_off_Starsha_UcodeInfo 0x1869fa0

#define kern_off_pml4pml4i 0x19bd618
#define kern_off_dmpml4i 0x19bd61c
#define kern_off_dmpdpi 0x19bd620

#elif defined PS4_4_00 || PS4_4_01

#define kern_off_printf 0x347450
#define kern_off_copyin 0x286cc0
#define kern_off_copyout 0x286c40
#define kern_off_copyinstr 0x287030
#define kern_off_kmem_alloc_contig 0x275da0
#define kern_off_kmem_free 0x369580
#define kern_off_pmap_extract 0x3eeed0
#define kern_off_pmap_protect 0x3f1120
#define kern_off_sched_pin 0x1d1120
#define kern_off_sched_unpin 0x1d1180
#define kern_off_smp_rendezvous 0x34a020
#define kern_off_smp_no_rendevous_barrier 0x349de0
#define kern_off_icc_query_nowait 0x46c5a0
#define kern_off_kernel_map 0x1fe71b8
#define kern_off_sysent 0xf17790
#define kern_off_kernel_pmap_store 0x200c310
#define kern_off_Starsha_UcodeInfo 0x18dafb0

#define kern_off_pml4pml4i 0x200c300
#define kern_off_dmpml4i 0x200c304
#define kern_off_dmpdpi 0x200c308

#endif
4 changes: 2 additions & 2 deletions uart.c
Expand Up @@ -9,8 +9,8 @@
*/

#include "uart.h"

#define PHYS_TO_DMAP(size, addr) ((volatile u##size *)(0xfffffe0000000000 | (uintptr_t)(addr)))
#include "kernel.h"
#define PHYS_TO_DMAP(size, addr) ((volatile u##size *)(kern.dmap_base | (uintptr_t)(addr)))

#define AEOLIA_UART_BASE 0xD0340000

Expand Down

0 comments on commit d081ecd

Please sign in to comment.