This commit is contained in:
Triss 2022-01-22 02:16:16 +01:00
parent 4cd397b564
commit 1b68909633
7 changed files with 328 additions and 119 deletions

1
.gitignore vendored
View File

@ -3,3 +3,4 @@
*.img
*.elf
*.map
notes

View File

@ -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

View File

@ -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)

View File

@ -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)
;
}

210
jazelle.c Normal file
View File

@ -0,0 +1,210 @@
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
// 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);
}

108
main.c
View File

@ -13,65 +13,7 @@
#include <stdio.h>
#include <string.h>
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(;;)
;
}

72
nl-glue.c Normal file
View File

@ -0,0 +1,72 @@
/* glue code for Newlib */
#include <bsp/uart.h>
#include <sys/types.h>
#include <sys/stat.h>
/* 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;
}