From 1b68909633d814e45fed17b48b8bca529afbf35e Mon Sep 17 00:00:00 2001 From: sys64738 Date: Sat, 22 Jan 2022 02:16:16 +0100 Subject: [PATCH] stuff --- .gitignore | 1 + Makefile | 22 +++++- README.md | 30 ++++---- bsp/uart.c | 4 +- jazelle.c | 210 +++++++++++++++++++++++++++++++++++++++++++++++++++++ main.c | 108 ++------------------------- nl-glue.c | 72 ++++++++++++++++++ 7 files changed, 328 insertions(+), 119 deletions(-) create mode 100644 jazelle.c create mode 100644 nl-glue.c diff --git a/.gitignore b/.gitignore index 49cd5e9..c97444d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ *.img *.elf *.map +notes diff --git a/Makefile b/Makefile index dc1f7db..8da3761 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,11 @@ +OPENOCD_HOST ?= localhost +OPENOCD_PORT_GDB ?= 3333 +OPENOCD_PORT_TELNET ?= 4444 +PREFIX ?= arm-none-eabi- -CC = arm-none-eabi-gcc +CC = $(PREFIX)gcc +GDB ?= $(PREFIX)gdb CGENFLAGS = -mcpu=arm926ej-s -mthumb-interwork -fno-pie WARN = -Wall -Wextra -Werror @@ -14,7 +19,7 @@ LDFLAGS = -static -nostartfiles -T bsp/fx3.ld -Wl,-z,max-page-size=4096,-Map,$(b VPATH = bsp -OBJS = main.o usb.o gpif.o gctl.o gpio.o uart.o util.o dma.o irq.o cache.o vectors.o +OBJS = main.o usb.o gpif.o gctl.o gpio.o uart.o util.o dma.o irq.o cache.o vectors.o jazelle.o nl-glue.o all : jazelle.img @@ -27,4 +32,17 @@ clean : jazelle.elf : $(OBJS) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ +openocd-launch: + openocd -c "bindto $(OPENOCD_HOST)" -f ./arm926ejs_fx3.cfg -c init # TODO: ports + +openocd-load: jazelle.elf + printf 'reset halt\nload_image jazelle.elf\nexit\n' \ + | nc $(OPENOCD_HOST) $(OPENOCD_PORT_TELNET) + +gdb: + $(GDB) -ex 'target extended-remote $(OPENOCD_HOST):$(OPENOCD_PORT_GDB)' \ + -ex 'set $$pc=_start' -ex 'b jazelle_exec' -ex c jazelle.elf + -include $(OBJS:.o=.d) + +.PHONY: all clean openocd-launch openocd-load gdb diff --git a/README.md b/README.md index 8031e97..370ac49 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ $ openocd -f ./arm926ejs_fx3.cfg -c "transport select jtag" -c "adapter speed 10 ``` $ printf 'reset halt\nload_image jazelle.elf\nexit\n' | nc localhost 4444 -gdb -ex 'target extended-remote localhost:3333' -ex 'set $pc=_start' -ex 'b jazelle_exec' -ex c jazelle.elf +$ arm-none-eabi-gdb -ex 'target extended-remote localhost:3333' -ex 'set $pc=_start' -ex 'b jazelle_exec' -ex c jazelle.elf ``` ## Credits @@ -42,17 +42,17 @@ Jazelle info this project is based on: ## TODO * Figure out Jazelle stuff: - * Which bytecode instructions are supported on which Jazelle versions? - * How exactly does the stack work? (When a handler function is being called) - * How exactly does the Jazelle status register work? - * What control registers are there that influence the execution? - * Is it possible to force execute a certain instruction using the handler - instead of the default in-hardware execution? - * ... - * How does one call regular ARM/Thumb code from inside Jazelle? - * ... - * Verify what Hackspire and libjz have, to check if it is correct - * Look at what Hackspire and libjz don't have and try to complete it -* Port this code to the ARM11 using either Raspberry Pi v1 baremetal, or 3DS - homebrew with kernel privileges (and do tests on these to check for different - Jazelle versions) + * [ ] Which bytecode instructions are supported on which Jazelle versions? + * [x] How exactly does the stack work? (When a handler function is being called) + * [ ] How exactly does the Jazelle status register work? + * [ ] What control registers are there that influence the execution? + * [ ] Is it possible to force execute a certain instruction using the handler + instead of the default in-hardware execution? + * [ ] ... + * [ ] How does one call regular ARM/Thumb code from inside Jazelle? + * [ ] ... + * [ ] Verify what Hackspire and libjz have, to check if it is correct + * [ ] Look at what Hackspire and libjz don't have and try to complete it +* [ ] Port this code to the ARM11 using either Raspberry Pi v1 baremetal, or + 3DS homebrew with kernel privileges (and do tests on these to check for + different Jazelle versions) diff --git a/bsp/uart.c b/bsp/uart.c index c842c46..459424d 100644 --- a/bsp/uart.c +++ b/bsp/uart.c @@ -38,7 +38,7 @@ void Fx3UartInit(uint32_t baud_rate, Fx3UartParity_t parity, Fx3UartStopBits_t s Fx3WriteReg32(FX3_UART_POWER, 0); Fx3UtilDelayUs(10); Fx3WriteReg32(FX3_UART_POWER, FX3_UART_POWER_RESETN); - while(!(Fx3ReadReg32(FX3_UART_POWER) & FX3_UART_POWER_ACTIVE)) + for (int i = 0; !(Fx3ReadReg32(FX3_UART_POWER) & FX3_UART_POWER_ACTIVE) && i < 1000*1000; ++i) ; /* Configure and enable UART */ @@ -77,6 +77,6 @@ extern void Fx3UartTxString(const char *str) void Fx3UartTxFlush(void) { - while(!(Fx3ReadReg32(FX3_UART_STATUS) & FX3_UART_STATUS_TX_DONE)) + for (int i = 0; !(Fx3ReadReg32(FX3_UART_STATUS) & FX3_UART_STATUS_TX_DONE) && i < 1000*1000; ++i) ; } diff --git a/jazelle.c b/jazelle.c new file mode 100644 index 0000000..d5623a4 --- /dev/null +++ b/jazelle.c @@ -0,0 +1,210 @@ + +#include +#include +#include +#include + +// known ID table: +// Chip name | ARM core | Jazelle ID +// ------------+------------+----------- +// ??? | ARM7EJ-S | ??? +// Cypress FX3 | ARM926EJ-S | 0x64100004 +// TI Nspire | ARM926EJ-S?| 0x64100004? +// RPi v1.?? | ARM11??? | ??? +// Ninty ?3DS | ARM11MPCore| 0x74100064? + +// TODO: immediate next steps: +// * check if we can override jazelle insn execution with custom handlers +// * check what the control registers actually do +// * enumerate what all insns do + +__attribute__((__naked__)) +static uint32_t jazelle_get_id(void) { + asm volatile( + "mrc p14, 7, r0, c0, c0, 0\n" + "bx lr\n" + ); +} + +/* + * c0: Jazelle Identity register (read-only) + Bits 0-11: Subarchitecture-defined bits (reads as 4, meaning unknown) + Bits 12-19: Subarchitecture (reads as 0, Jazelle V1 according to documentation) + Bits 20-27: Implementor (reads as 0x41, ARM Limited according to documentation) + Bits 28-31: Architecture (reads as 6, ARMv5TEJ according to documentation) + c1: Operating System Control register + Bit 0: Configuration Disabled (CD) (documented) + Bit 1: Configuration Valid (CV) (documented) + c2: Main Configuration register + Bit 0: Jazelle Enable (JE) (documented) + Bits 26-28: Unknown + Bit 29: If set, array object contains its elements directly, otherwise it contains a pointer to its elements + Bit 31: Disable array instructions if set? + c3: Array object layout register + Bits 0-7: Unknown + Bits 8-11: Offset (in words) within array object of first element or of pointer to first element + Bits 12-15: Offset (in words) within array object of length + Bit 16: If set, offset to length is subtracted, otherwise added + Bits 17-19: Array length shift value (number of elements = stored length >> this) + Bits 20-21: Disable array instructions if set? + */ + +// https://github.com/SonoSooS/libjz/wiki/Java-instruction-set +static uint8_t bytecode[] = { + 0x06, // iconst_3 + 0x07, // iconst_4 + 0x05, // iconst_2 + 0x6C, // idiv + 0x04, // iconst_1 + 0x60, // iadd + 0x60, // iadd + 0xAC, // ireturn +}; + +__attribute__((__aligned__(1024))) +static struct { + void* handlers[512]; + uint8_t stack[256]; + uint8_t locals[256]; +} jazelle_block; + +__attribute__((__naked__)) +static void handler_idiv(void) { + // r0 = 3 + // r1 = 4 + // r2 = 2 + // editing the above has no effect + // NOTE: these depend on the stack content, i.e. it's not a moving register + // window. pushes happen in the following order: r0,r1,r2,r3 + // TODO: when the 'register stack cache' is full, what happens? does + // it loop or does it act in a FIFO way, moving r0<-r1<-r2<-r3? + // how can the fillrate be known??? (libjz says r5 & 3 but i + // dont see anything like that, maybe its an ARM11 thing?) + // "r4: Copy of local variable 0. Only valid when local variable 0 is a + // single word (i.e. int, float, or Object; not long or double)" + // -Hackspire + // + // [r6-4] is stack top (2) + // [r6-8] is 4 + // etc + // use the above to manipulate the stack, eg. "iadd" is implemented as: + // - add r1, r2 // or equivalently, read from stack i guess + // - str r1, [r6, #-8] // store to the place where it will be read + // - sub r6, #4 // pop off & discard stack top element + // NOTE: this input usage (with r1 and r2) is NOT robust at all, use memory + // reads instead! + + asm volatile( + // FIXME: read out stack contents in a better way + "add r1, r2\n" + "str r1, [r6,#-8]\n" + "sub r6, #4\n" + + // return to jazelle (yes lr has to be incremented otherwise the + // current instruction keeps getting executed in a loop) + "add lr, #1\n" + "bxj r12\n" // FIXME: r12 can be modified by jazelle so it should be restored to something + ); +} + +static uint32_t jazelle_exit_save; + +__attribute__((__naked__)) +static void handler_ireturn(void) { + int result; + asm volatile( + "ldr %[res], [r6, #-4]!\n" + :[res]"=r"(result) + ); + printf("result=%d\r\n", result); // FIXME: save & restore r0-r3 if ret implemented properly + + // get back to original code + // TODO: later stage: get back to previous bytecode stuff + asm volatile( + "ldr r12, %[exsav]\n" + "bx r12\n" + : + :[exsav]"m"(jazelle_exit_save) + :"r12" + ); + __builtin_unreachable(); +} + +__attribute__((__naked__)) +static int jazelle_exec_native(const void* bytecode, const void* block) { + // inline asm parameters seems to be borking in GCC sooooo lets do it this way + (void)bytecode; (void)block; (void)&jazelle_exit_save; + asm volatile( + "push {r5-r7,lr}\n" + + "mov lr, r0\n" + + // init handler table pointer and stack pointer + "mov r5, r1\n" + "add r6, r5, #0x800\n" + "add r7, r5, #0x900\n" + // "r8: Pointer to constant pool? (haven't checked this yet)" -Hackspire + + // set configuration valid & jazelle enable bits + "mov r0, #2\n" + "mcr p14, 7, r0, c1, c0, 0\n" + "mov r0, #1\n" + "mcr p14, 7, r0, c2, c0, 0\n" + + // apparently there's no good way to find the exit point from jazelle, + // so we're going to hack that into the stuff now + "ldr r12, =jazelle_exit_save\n" + "adr r0, .Ljazend\n" + "str r0, [r12]\n" + + // switch to jazelle mode + "adr r12, .Lno_jazelle\n" + "bxj r12\n" + + ".Ljazend:\n" + "mov r0, #0\n" + "b .Lend\n" + + ".Lno_jazelle:\n" + "mov r0, #1\n" + + ".Lend:\n" + "mov r5, #0\n" + "mcr p14, 7, %r5, c1, c0, 0\n" + "mcr p14, 7, %r5, c2, c0, 0\n" + + "pop {r5-r7,lr}\n" + "bx lr\n" + + ".pool\n" + ); +} + +static int jazelle_exec(const uint8_t* bytecode) { + jazelle_block.handlers[0x6C] = handler_idiv; + jazelle_block.handlers[0xAC] = handler_ireturn; +/* + * +000-3FF: Unhandled bytecodes + The stack is flushed to memory before calling any of these handlers, so they may modify r0-r3 freely + +400: Null pointer exception + +404: Array index out of bounds exception + +40C: Jazelle mode entered with JE = 0 + +410: Configuration invalid (Jazelle mode entered with CV = 0) + CV is automatically set to 1 on entering this handler + +414: Prefetch abort occurred in middle of instruction + -Hackspire + */ + const void* block = &jazelle_block; + + return jazelle_exec_native(bytecode, block); +} + +void jazelle_main(void) { + uint32_t id = jazelle_get_id(); + printf("hello world! jazelle ID=0x%lx\r\n", id); + + int r = jazelle_exec(bytecode); + + printf("retcode=%d\r\n", r); +} + diff --git a/main.c b/main.c index 4f2ecef..87f3346 100644 --- a/main.c +++ b/main.c @@ -13,65 +13,7 @@ #include #include -static uint8_t bytecode[] = { - 0x05, // iconst_2 - 0x05, // iconst_2 - 0x60, // iadd - 0xAC, // ireturn -}; - -__attribute__((__aligned__(1024))) -static struct { - void* handlers[512]; - uint8_t stack[256]; - uint8_t locals[256]; -} jazelle_block; - -static void handler_ireturn(void) { - int32_t result; - asm volatile( - "ldr %[res], [r6, #-4]!\n" - "bkpt #1\n" - :[res]"=r"(result) - ); - while (true); // a -} - -static void jazelle_exec(const uint8_t* bytecode) { - jazelle_block.handlers[0xAC] = handler_ireturn; - const void* block = &jazelle_block; - - asm volatile( - "push {lr}\n" - // set configuration valid & jazelle enable bits - "mov r0, #2\n" - "mcr p14, 7, r0, c1, c0, 0\n" - "mov r0, #1\n" - "mcr p14, 7, r0, c2, c0, 0\n" - - "mov lr, %[bc]\n" - - // init handler table pointer and stack pointer - "mov r5, %[blk]\n" - "add r6, r5, #0x800\n" - "add r7, r5, #0x900\n" - - // switch to jazelle mode - "adr r12, .Lno_jazelle\n" - "bxj r12\n" - "bkpt #0\n" - "b .Lend\n" - ".Lno_jazelle:\n" - "mov r0, #0\n" - "mcr p14, 7, r0, c1, c0, 0\n" - "mcr p14, 7, r0, c2, c0, 0\n" - "bkpt #2\n" - ".Lend:\n" - "pop {lr}\n" - : - :[bc]"r"(bytecode),[blk]"r"(block) - :"r0","r12","r5","r6","r7","memory"); -} +extern void jazelle_main(void); int main(void) { @@ -81,12 +23,14 @@ int main(void) Fx3GctlInitClock(); *(volatile uint32_t *)(void *)0x400020e8 = 0; Fx3GctlInitIoMatrix(FX3_GCTL_ALTFUNC_GPIF32BIT_UART_I2S); - /*Fx3UartInit(115200, FX3_UART_NO_PARITY, FX3_UART_1_STOP_BIT); - Fx3UartTxString("\nhello world\n"); - Fx3UartTxFlush(); + Fx3UartInit(115200, FX3_UART_NO_PARITY, FX3_UART_1_STOP_BIT); + /*while (true) { + Fx3UartTxString("\nhello world\n"); + Fx3UartTxFlush(); + }*/ Fx3GpioInitClock(); - Fx3GpioSetupSimple(45, + /*Fx3GpioSetupSimple(45, FX3_GPIO_SIMPLE_ENABLE | FX3_GPIO_SIMPLE_INPUT_EN); Fx3GpioSetupSimple(54, @@ -103,44 +47,8 @@ int main(void) Fx3IrqEnableInterrupts();*/ - jazelle_exec(bytecode); + jazelle_main(); while (true) ; // a } -/* Newlib's assert() calls this function if the assertion fails */ -void -__assert_func (const char *file, - int line, - const char *func, - const char *failedexpr) -{ - if (file != NULL) { - char linestrbuf[16], *linestr = &linestrbuf[sizeof(linestrbuf)]; - Fx3UartTxString(file); - Fx3UartTxChar(':'); - /* Avoid using newlib functions like itoa so as not to trigger - a recursive assert... */ - *--linestr = '\0'; - while (line >= 10 && linestr != &linestrbuf[1]) { - *--linestr = '0' + (line % 10); - line /= 10; - } - *--linestr = '0' + line; - Fx3UartTxString(linestr); - Fx3UartTxString(": "); - } - if (func != NULL) { - Fx3UartTxString(func); - Fx3UartTxString(": "); - } - Fx3UartTxString("Assertion "); - if (failedexpr != NULL) { - Fx3UartTxChar('`'); - Fx3UartTxString(failedexpr); - Fx3UartTxString("' "); - } - Fx3UartTxString("failed.\n"); - for(;;) - ; -} diff --git a/nl-glue.c b/nl-glue.c new file mode 100644 index 0000000..1b721a3 --- /dev/null +++ b/nl-glue.c @@ -0,0 +1,72 @@ + +/* glue code for Newlib */ + +#include + +#include +#include + +/* Newlib's assert() calls this function if the assertion fails */ +void __assert_func (const char *file, int line, const char *func, const char *failedexpr) +{ + if (file != NULL) { + char linestrbuf[16], *linestr = &linestrbuf[sizeof(linestrbuf)]; + Fx3UartTxString(file); + Fx3UartTxChar(':'); + /* Avoid using newlib functions like itoa so as not to trigger + a recursive assert... */ + *--linestr = '\0'; + while (line >= 10 && linestr != &linestrbuf[1]) { + *--linestr = '0' + (line % 10); + line /= 10; + } + *--linestr = '0' + line; + Fx3UartTxString(linestr); + Fx3UartTxString(": "); + } + if (func != NULL) { + Fx3UartTxString(func); + Fx3UartTxString(": "); + } + Fx3UartTxString("Assertion "); + if (failedexpr != NULL) { + Fx3UartTxChar('`'); + Fx3UartTxString(failedexpr); + Fx3UartTxString("' "); + } + Fx3UartTxString("failed.\n"); + Fx3UartTxFlush(); + for(;;) ; +} + +int _write(int fd, char* data, int size) { + if (fd >= 0 && fd < 3) { + if (size > 0) + Fx3UartTxBytes((const uint8_t*)data, (size_t)size); + Fx3UartTxFlush(); + return size; + } + + return -1; +} + +int _close(int fd) { (void)fd; return 0; } + +int _stat(char *file, struct stat *st) { + (void)file; + st->st_mode = S_IFCHR; + return 0; +} + +int _read(int fd, char *ptr, int len) { (void)fd; (void)ptr; (void)len; return 0; } + +int _lseek(int fd, int ptr, int dir) { (void)fd; (void)ptr; (void)dir; return 0; } + +int _isatty(int fd) { (void)fd; return 1; } + +int _fstat(int fd, struct stat *st) { + (void)fd; + st->st_mode = S_IFCHR; + return 0; +} +