From 25db504c228965281f558b96a02efc84665f255a Mon Sep 17 00:00:00 2001 From: Lephe Date: Fri, 3 May 2019 11:19:36 +0200 Subject: [PATCH] fxos: bring disassembling to a new level Almost-complete implementation of fxos, the disassembler in particular is now able to detect syscalls and register addresses on the fly, plus support for SH4-only instructions. --- Makefile | 37 +-- configure | 4 +- fxos/analysis.c | 22 ++ fxos/asm-sh3.txt | 220 +++++++++++++++ fxos/asm-sh4.txt | 26 ++ fxos/asm.c | 74 +++++ fxos/disassembly.c | 268 ++++++++++++++++++ fxos/errors.c | 84 ++++++ fxos/errors.h | 63 +++++ fxos/fxos.h | 446 +++++++++++++++++++++++------- fxos/info.c | 120 ++++++++ fxos/lexer-asm.l | 187 +++++++++++++ fxos/lexer-reg.l | 117 ++++++++ fxos/lexer-sys.l | 123 +++++++++ fxos/main.c | 354 ++++++++++++++++-------- fxos/memory.c | 38 +++ fxos/os.c | 91 ++++++ fxos/reg-sh7305.txt | 114 ++++++++ fxos/reg-simlo.txt | 141 ++++++++++ fxos/reg.c | 58 ++++ fxos/sys-lephe.txt | 7 + fxos/sys-simlo.txt | 656 ++++++++++++++++++++++++++++++++++++++++++++ fxos/sys.c | 59 ++++ fxos/tables.c | 115 ++++---- fxos/util.c | 72 ++++- fxos/util.h | 44 +++ 26 files changed, 3259 insertions(+), 281 deletions(-) create mode 100644 fxos/analysis.c create mode 100644 fxos/asm-sh3.txt create mode 100644 fxos/asm-sh4.txt create mode 100644 fxos/asm.c create mode 100644 fxos/disassembly.c create mode 100644 fxos/errors.c create mode 100644 fxos/errors.h create mode 100644 fxos/info.c create mode 100644 fxos/lexer-asm.l create mode 100644 fxos/lexer-reg.l create mode 100644 fxos/lexer-sys.l create mode 100644 fxos/memory.c create mode 100644 fxos/os.c create mode 100644 fxos/reg-sh7305.txt create mode 100644 fxos/reg-simlo.txt create mode 100644 fxos/reg.c create mode 100644 fxos/sys-lephe.txt create mode 100644 fxos/sys-simlo.txt create mode 100644 fxos/sys.c create mode 100644 fxos/util.h diff --git a/Makefile b/Makefile index b0a6bcb..7c09afb 100755 --- a/Makefile +++ b/Makefile @@ -6,12 +6,10 @@ include Makefile.cfg endif # Compiler flags -cflags = -Wall -Wextra -std=c11 -O2 -I $(dir $<) -D_GNU_SOURCE \ +cflags = -Wall -Wextra -std=c11 -g -I $(dir $<) -D_GNU_SOURCE \ -DFXSDK_PREFIX='"$(PREFIX)"' $(CFLAGS) # Linker flags lflags = -lpng -# Bison generation flags -# bflags = -L C --defines=$(@:.c=.h) --verbose # Dependency generation flags dflags = -MT $@ -MMD -MP -MF $(@:%.o=%.d) @@ -28,15 +26,18 @@ src = $(wildcard $1/*.c) src-fxsdk := $(call src,fxsdk) src-fxg1a := $(call src,fxg1a) src-fxos := $(call src,fxos) +lex-fxos := $(wildcard fxos/*.l) -obj = $(src-$1:%=build/%.o) -obj-fxsdk := $(call obj,fxsdk) -obj-fxg1a := $(call obj,fxg1a) -obj-fxos := $(call obj,fxos) +obj = $($1:%=build/%.o) +lex = $($1:%.l=build/%.yy.c.o) +obj-fxsdk := $(call obj,src-fxsdk) +obj-fxg1a := $(call obj,src-fxg1a) +obj-fxos := $(call obj,src-fxos) $(call lex,lex-fxos) # Symbolic targets all: $(bin) + @echo $(lex-fxos) all-fxsdk: bin/fxsdk all-fxg1a: bin/fxg1a @@ -62,17 +63,11 @@ build/%.c.o: %.c @mkdir -p $(dir $@) gcc -c $< -o $@ $(cflags) $(dflags) -# Flex lexers (unused since fxconv is written in Python) -# build/%/lexer.yy.c: %/lexer.l build/%/parser.tab.c -# flex -o $@ -s $< -# build/%/lexer.yy.c.o: build/%/lexer.yy.c -# gcc -c $< -o $@ $(cflags) -Wno-unused-function $(dflags) -I $* - -# Bison parsers (unused since fxconv is written in Python) -# build/%/parser.tab.c: %/parser.y -# bison $< -o $@ $(bflags) -# build/%/parser.tab.c.o: build/%/parser.tab.c -# gcc -c $< -o $@ $(cflags) $(dflags) -I $* +# Flex lexers for the fxos database +build/fxos/lexer-%.yy.c: fxos/lexer-%.l + flex -o $@ -s $< +build/fxos/lexer-%.yy.c.o: build/fxos/lexer-%.yy.c + gcc -c $< -o $@ $(cflags) -Wno-unused-function $(dflags) -I fxos # # Dependency system, misc. @@ -95,10 +90,16 @@ Makefile.cfg: install: $(bin) install -d $(PREFIX)/bin + install -d $(PREFIX)/share/fxsdk install $(bin) -m 755 $(PREFIX)/bin + install fxos/*.txt -m 644 $(PREFIX)/share/fxsdk install fxconv/fxconv-main.py -m 755 $(PREFIX)/bin/fxconv install fxconv/fxconv.py -m 644 $(PREFIX)/bin +uninstall: + rm -f $(PREFIX)/bin/{fxsdk,fxg1a,fxos,fxconv,fxconv.py} + rm -rf $(PREFIX)/share/fxsdk + # # Cleaning # diff --git a/configure b/configure index 8c01f87..0f99a06 100755 --- a/configure +++ b/configure @@ -5,7 +5,7 @@ # # Path parameters -PREFIX="/usr" +PREFIX="$HOME/.local" # Individual component selection BUILD_fxsdk=1 BUILD_fxconv=1 @@ -47,7 +47,7 @@ Install folders: Executables will be installed in /bin and runtime data in /share/fxsdk. - --prefix= Base install folder [default /usr] + --prefix= Base install folder [default $HOME/.local] EOF exit 0 } diff --git a/fxos/analysis.c b/fxos/analysis.c new file mode 100644 index 0000000..99585e8 --- /dev/null +++ b/fxos/analysis.c @@ -0,0 +1,22 @@ +#include +#include + +/* analysis_short(): Print a one-line summary for an address */ +void analysis_short(struct os const *os, uint32_t value) +{ + /* Find out whether it is a syscall address */ + int syscall = os_syscall_find(os, value); + + if(syscall != -1) + { + printf(" %%%03x", syscall); + + /* Also find out the syscall's name! */ + struct sys_call const *call = sys_find(syscall); + if(call) printf(" %s", call->name); + } + + /* Find out any peripheral register address */ + struct reg_address const *reg = reg_find(value); + if(reg) printf(" %s", reg->name); +} diff --git a/fxos/asm-sh3.txt b/fxos/asm-sh3.txt new file mode 100644 index 0000000..239968e --- /dev/null +++ b/fxos/asm-sh3.txt @@ -0,0 +1,220 @@ +# Instruction decoding table: 'sh3' +# Format: [01nmdi]{16}, followed by the mnemonic and the list of arguments. +# There should be at most one series of each argument letter in the opcode. +# +# Possible argument strings are predefined and include: +# rn rm #imm +# jump8 jump12 disp pcdisp +# @rn @rm @rn+ @rm+ @-rn +# @(disp,rn) @(disp,rm) @(r0,rn) @(r0,rm) @(disp,gbr) +# +# The disassembler substitutes some elements as follows: +# rn -> value of the "n"-sequence +# rm -> value of the "m"-sequence +# #imm -> value of the "i"-sequence +# disp -> value of the "d"-sequence +# jump8 -> value of the 8-bit "d"-sequence plus value of PC +# jump12 -> value of the 12-bit "d"-sequence plus value of PC +# pcdisp -> same as "jump8", but also prints pointed value (for data) + +0000000001001000 clrs +0000000000001000 clrt +0000000000101000 clrmac +0000000000011001 div0u +0000000000111000 ldtlb +0000000000001001 nop +0000000000101011 rte +0000000000001011 rts +0000000001011000 sets +0000000000011000 sett +0000000000011011 sleep + +0100nnnn00010101 cmp/pl rn +0100nnnn00010001 cmp/pz rn +0100nnnn00010000 dt rn +0000nnnn00101001 movt rn +0100nnnn00000100 rotl rn +0100nnnn00000101 rotr rn +0100nnnn00100100 rotcl rn +0100nnnn00100101 rotcr rn +0100nnnn00100000 shal rn +0100nnnn00100001 shar rn +0100nnnn00000000 shll rn +0100nnnn00000001 shlr rn +0100nnnn00001000 shll2 rn +0100nnnn00001001 shlr2 rn +0100nnnn00011000 shll8 rn +0100nnnn00011001 shlr8 rn +0100nnnn00101000 shll16 rn +0100nnnn00101001 shlr16 rn + +0011nnnnmmmm1100 add rm, rn +0011nnnnmmmm1110 addc rm, rn +0011nnnnmmmm1111 addv rm, rn +0010nnnnmmmm1001 and rm, rn +0011nnnnmmmm0000 cmp/eq rm, rn +0011nnnnmmmm0010 cmp/hs rm, rn +0011nnnnmmmm0011 cmp/ge rm, rn +0011nnnnmmmm0110 cmp/hi rm, rn +0011nnnnmmmm0111 cmp/gt rm, rn +0010nnnnmmmm1100 cmp/str rm, rn +0011nnnnmmmm0100 div1 rm, rn +0010nnnnmmmm0111 div0s rm, rn +0011nnnnmmmm1101 dmuls.l rm, rn +0011nnnnmmmm0101 dmulu.l rm, rn +0110nnnnmmmm1110 exts.b rm, rn +0110nnnnmmmm1111 exts.w rm, rn +0110nnnnmmmm1100 extu.b rm, rn +0110nnnnmmmm1101 extu.w rm, rn +0110nnnnmmmm0011 mov rm, rn +0000nnnnmmmm0111 mul.l rm, rn +0010nnnnmmmm1111 muls.w rm, rn +0010nnnnmmmm1110 mulu.w rm, rn +0110nnnnmmmm1011 neg rm, rn +0110nnnnmmmm1010 negc rm, rn +0110nnnnmmmm0111 not rm, rn +0010nnnnmmmm1011 or rm, rn +0100nnnnmmmm1100 shad rm, rn +0100nnnnmmmm1101 shld rm, rn +0011nnnnmmmm1000 sub rm, rn +0011nnnnmmmm1010 subc rm, rn +0011nnnnmmmm1011 subv rm, rn +0110nnnnmmmm1000 swap.b rm, rn +0110nnnnmmmm1001 swap.w rm, rn +0010nnnnmmmm1000 tst rm, rn +0010nnnnmmmm1010 xor rm, rn +0010nnnnmmmm1101 xtrct rm, rn + +0100mmmm00001110 ldc rm, sr +0100mmmm00011110 ldc rm, gbr +0100mmmm00101110 ldc rm, vbr +0100mmmm00111110 ldc rm, ssr +0100mmmm01001110 ldc rm, spc +0100mmmm10001110 ldc rm, r0_bank +0100mmmm10011110 ldc rm, r1_bank +0100mmmm10101110 ldc rm, r2_bank +0100mmmm10111110 ldc rm, r3_bank +0100mmmm11001110 ldc rm, r4_bank +0100mmmm11011110 ldc rm, r5_bank +0100mmmm11101110 ldc rm, r6_bank +0100mmmm11111110 ldc rm, r7_bank +0100mmmm00001010 lds rm, mach +0100mmmm00011010 lds rm, macl +0100mmmm00101010 lds rm, pr +0000nnnn00000010 stc sr, rn +0000nnnn00010010 stc gbr, rn +0000nnnn00100010 stc vbr, rn +0000nnnn00110010 stc ssr, rn +0000nnnn01000010 stc spc, rn +0000nnnn10000010 stc r0_bank, rn +0000nnnn10010010 stc r1_bank, rn +0000nnnn10100010 stc r2_bank, rn +0000nnnn10110010 stc r3_bank, rn +0000nnnn11000010 stc r4_bank, rn +0000nnnn11010010 stc r5_bank, rn +0000nnnn11100010 stc r6_bank, rn +0000nnnn11110010 stc r7_bank, rn +0000nnnn00001010 sts mach, rn +0000nnnn00011010 sts macl, rn +0000nnnn00101010 sts pr, rn + +0100nnnn00101011 jmp @rn +0100nnnn00001011 jsr @rn +0000nnnn10000011 pref @rn +0100nnnn00011011 tas.b @rn +0010nnnnmmmm0000 mov.b rm, @rn +0010nnnnmmmm0001 mov.w rm, @rn +0010nnnnmmmm0010 mov.l rm, @rn +0110nnnnmmmm0000 mov.b @rm, rn +0110nnnnmmmm0001 mov.w @rm, rn +0110nnnnmmmm0010 mov.l @rm, rn +0000nnnnmmmm1111 mac.l @rm+, @rn+ +0100nnnnmmmm1111 mac.w @rm+, @rn+ + +0110nnnnmmmm0100 mov.b @rm+, rn +0110nnnnmmmm0101 mov.w @rm+, rn +0110nnnnmmmm0110 mov.l @rm+, rn + +0100mmmm00000111 ldc.l @rm+, sr +0100mmmm00010111 ldc.l @rm+, gbr +0100mmmm00100111 ldc.l @rm+, vbr +0100mmmm00110111 ldc.l @rm+, ssr +0100mmmm01000111 ldc.l @rm+, spc +0100mmmm10000111 ldc.l @rm+, r0_bank +0100mmmm10010111 ldc.l @rm+, r1_bank +0100mmmm10100111 ldc.l @rm+, r2_bank +0100mmmm10110111 ldc.l @rm+, r3_bank +0100mmmm11000111 ldc.l @rm+, r4_bank +0100mmmm11010111 ldc.l @rm+, r5_bank +0100mmmm11100111 ldc.l @rm+, r6_bank +0100mmmm11110111 ldc.l @rm+, r7_bank +0100mmmm00000110 lds.l @rm+, mach +0100mmmm00010110 lds.l @rm+, macl +0100mmmm00100110 lds.l @rm+, pr + +0010nnnnmmmm0100 mov.b rm, @-rn +0010nnnnmmmm0101 mov.w rm, @-rn +0010nnnnmmmm0110 mov.l rm, @-rn + +0100nnnn00000011 stc.l sr, @-rn +0100nnnn00010011 stc.l gbr, @-rn +0100nnnn00100011 stc.l vbr, @-rn +0100nnnn00110011 stc.l ssr, @-rn +0100nnnn01000011 stc.l spc, @-rn +0100nnnn10000011 stc.l r0_bank, @-rn +0100nnnn10010011 stc.l r1_bank, @-rn +0100nnnn10100011 stc.l r2_bank, @-rn +0100nnnn10110011 stc.l r3_bank, @-rn +0100nnnn11000011 stc.l r4_bank, @-rn +0100nnnn11010011 stc.l r5_bank, @-rn +0100nnnn11100011 stc.l r6_bank, @-rn +0100nnnn11110011 stc.l r7_bank, @-rn +0100nnnn00000010 sts.l mach, @-rn +0100nnnn00010010 sts.l macl, @-rn +0100nnnn00100010 sts.l pr, @-rn + +10000000nnnndddd mov.b r0, @(disp, rn) +10000001nnnndddd mov.w r0, @(disp, rn) +0001nnnnmmmmdddd mov.l rm, @(disp, rn) +10000100mmmmdddd mov.b @(disp, rm), r0 +10000101mmmmdddd mov.w @(disp, rm), r0 +0101nnnnmmmmdddd mov.l @(disp, rm), rn +0000nnnnmmmm0100 mov.b rm, @(r0, rn) +0000nnnnmmmm0101 mov.w rm, @(r0, rn) +0000nnnnmmmm0110 mov.l rm, @(r0, rn) +0000nnnnmmmm1100 mov.b @(r0, rm), rn +0000nnnnmmmm1101 mov.w @(r0, rm), rn +0000nnnnmmmm1110 mov.l @(r0, rm), rn +11000000dddddddd mov.b r0, @(disp, gbr) +11000001dddddddd mov.w r0, @(disp, gbr) +11000010dddddddd mov.l r0, @(disp, gbr) +11000100dddddddd mov.b @(disp, gbr), r0 +11000101dddddddd mov.w @(disp, gbr), r0 +11000110dddddddd mov.l @(disp, gbr), r0 + +11001101iiiiiiii and.b #imm, @(r0, gbr) +11001111iiiiiiii or.b #imm, @(r0, gbr) +11001100iiiiiiii tst.b #imm, @(r0, gbr) +11001110iiiiiiii xor.b #imm, @(r0, gbr) + +1001nnnndddddddd mov.w pcdisp, rn +1101nnnndddddddd mov.l pcdisp, rn +11000111dddddddd mova pcdisp, r0 + +0000mmmm00100011 braf rm +0000mmmm00000011 bsrf rm +10001011dddddddd bf jump8 +10001111dddddddd bf/s jump8 +10001001dddddddd bt jump8 +10001101dddddddd bt/s jump8 +1010dddddddddddd bra jump12 +1011dddddddddddd bsr jump12 + +0111nnnniiiiiiii add #imm, rn +11001001iiiiiiii and #imm, r0 +10001000iiiiiiii cmp/eq #imm, r0 +1110nnnniiiiiiii mov #imm, rn +11001011iiiiiiii or #imm, r0 +11001000iiiiiiii tst #imm, r0 +11001010iiiiiiii xor #imm, r0 +11000011iiiiiiii trapa #imm diff --git a/fxos/asm-sh4.txt b/fxos/asm-sh4.txt new file mode 100644 index 0000000..45cad9c --- /dev/null +++ b/fxos/asm-sh4.txt @@ -0,0 +1,26 @@ +# Instruction decoding table: 'sh4' +# This table is an extension for SH4 processors. See 'asm-sh3.txt' for details +# on the format. + +0000nnnn01110011 movco.l r0, @rn +0000mmmm01100011 movli.l @rm, r0 +0100mmmm10101001 movua.l @rm, r0 +0100mmmm11101001 movua.l @rm+, r0 +0000nnnn11000011 movca.l r0, @rn + +0000nnnn11100011 icbi @rn +0000nnnn10010011 ocbi @rn +0000nnnn10100011 ocbp @rn +0000nnnn10110011 ocbwb @rn + +0000nnnn11010011 prefi @rn +0000000010101011 synco + +0100mmmm00111010 ldc rm, sgr +0100mmmm11111010 ldc rm, dbr +0100mmmm00110110 ldc.l @rm+, sgr +0100mmmm11110110 ldc.l @rm+, dbr +0000nnnn00111010 stc sgr, rn +0000nnnn11111010 stc dbr, rn +0100nnnn00110010 stc.l sgr, @-rn +0100nnnn11110010 stc.l dbr, @-rn diff --git a/fxos/asm.c b/fxos/asm.c new file mode 100644 index 0000000..8c7b9c7 --- /dev/null +++ b/fxos/asm.c @@ -0,0 +1,74 @@ +#include +#include +#include +#include + +/* asm_free_item(): Free an assembly instruction details */ +static void asm_free_item(void *item) +{ + struct asm_insn *insn = item; + free(insn->mnemonic); + free(insn->literal1); + free(insn->literal2); +} + +/* asm_load(): Load an assembly table */ +void asm_load(char const *file) +{ + err_context("assembly", file, NULL); + if(!table_available()) + { + err("too many tables, skipping"); + err_pop(); + return; + } + + /* Map the file to memory */ + int fd; + size_t size; + void *data = map(file, &fd, &size); + if(!data) { err_pop(); return; } + + /* If the file is named "asm-x.txt", use "x" as the table name */ + char *name = match_table_name(file, "asm", ".txt"); + + /* Parse the contents; the lexer in is [lexer-asm.l] and the parser is + implemented as an automaton with basic error correction. */ + int count; + struct asm_insn *ins = lex_asm(data, size, &count); + + table_create("asm", name, asm_free_item, count, sizeof *ins, ins); + + unmap(data, fd, size); + err_pop(); +} + +/* Global storage for asm_match() */ +static uint16_t asm_match_opcode; + +/* asm_match(): Check if an instruction matches a pattern */ +int asm_match(void *item) +{ + struct asm_insn *insn = item; + return ((insn->bits ^ asm_match_opcode) & insn->arg_mask) == 0; +} + +/* asm_decode(): Match a 16-bit opcode against the assembly database */ +int asm_decode(uint16_t opcode, struct asm_match *match, int next) +{ + asm_match_opcode = opcode; + char const *table; + struct asm_insn *insn = table_find("asm", asm_match, &table, next); + if(!insn) return 1; + + /* Set match details */ + match->insn = insn; + match->table = table; + + match->n = (opcode >> insn->n_sh) & insn->n_mask; + match->m = (opcode >> insn->m_sh) & insn->m_mask; + match->d = (opcode >> insn->d_sh) & insn->d_mask; + match->i = (opcode >> insn->i_sh) & insn->i_mask; + + return 0; +} diff --git a/fxos/disassembly.c b/fxos/disassembly.c new file mode 100644 index 0000000..025984b --- /dev/null +++ b/fxos/disassembly.c @@ -0,0 +1,268 @@ +#include +#include + +#include +#include + +/* Architecture we are disassembling for */ +static enum mpu mpu = MPU_GUESS; +/* Current program counter */ +static uint32_t pc = 0; +/* Data chunk under disassembly (whole file) */ +static uint8_t *data; +static size_t len; +/* Non-NULL when disassembling an OS */ +static struct os const *os = NULL; + +/* pcdisp(): Compute the address of a PC-relative displacement + @pc Current PC value + @disp Displacement value (from opcode) + @unit Number of bytes per step (1, 2 or 4), depends on instruction + Returns the pointed location. */ +static uint32_t pcdisp(uint32_t pc, uint32_t disp, int unit) +{ + pc += 4; + if(unit == 4) pc &= ~3; + + return pc + disp * unit; +} + +/* pcread(): Read data pointed through a PC-relative displacement + @pc Current PC value + @disp Displacement value (from opcode) + @unit Number of bytes per set (also number of bytes read) + @out If non-NULL, set to 1 if pointed address is out of the file + Returns the value pointed at, an undefined value if out of bounds. */ +static uint32_t pcread(uint32_t pc, uint32_t disp, int unit, int *out) +{ + /* Take physical addresses */ + uint32_t addr = pcdisp(pc, disp, unit) & 0x1fffffff; + if(out) *out = (addr + unit > len); + + uint32_t value = 0; + while(unit--) value = (value << 8) | data[addr++]; + return value; +} + +/* matches(): Count number of matches for a single instruction + @opcode 16-bit opcode value + Returns the number of matching instructions in the database. */ +static int matches(uint16_t opcode) +{ + struct asm_match match; + int count = 0; + + while(!asm_decode(opcode, &match, count)) + { + count++; + } + return count; +} + +static void arg_output(int type, char const *literal, + struct asm_match const *match, int opsize) +{ + int n = match->n; + int m = match->m; + int d = match->d; + int i = match->i; + + /* Sign extensions of d to 8 and 12 bits */ + int32_t d8 = (int8_t)d; + int32_t d12 = (d & 0x800) ? (int32_t)(d | 0xfffff000) : (d); + /* Sign extension of i to 8 bits */ + int32_t i8 = (int8_t)i; + + int out_of_bounds; + uint32_t addr; + uint32_t value; + + switch(type) + { + case LITERAL: + printf("%s", literal); + break; + case IMM: + printf("#%d", i8); + break; + case RN: + printf("r%d", n); + break; + case RM: + printf("r%d", m); + break; + case JUMP8: + value = pcdisp(pc, d8, 2); + printf("<%x", value); + if(os) analysis_short(os, value | 0x80000000); + printf(">"); + break; + case JUMP12: + value = pcdisp(pc, d12, 2); + printf("<%x", value); + if(os) analysis_short(os, value | 0x80000000); + printf(">"); + break; + case PCDISP: + addr = pcdisp(pc, d, opsize); + value = pcread(pc, d, opsize, &out_of_bounds); + + printf("<%x>", addr); + if(out_of_bounds) printf("(out of bounds)"); + else + { + printf("(#0x%0*x", opsize * 2, value); + if(os) analysis_short(os, value); + printf(")"); + } + + break; + case AT_RN: + printf("@r%d", n); + break; + case AT_RM: + printf("@r%d", m); + break; + case AT_RMP: + printf("@r%d+", m); + break; + case AT_RNP: + printf("@r%d+", n); + break; + case AT_MRN: + printf("@-r%d", n); + break; + case AT_DRN: + printf("@(%d, r%d)", d * opsize, n); + break; + case AT_DRM: + printf("@(%d, r%d)", d * opsize, m); + break; + case AT_R0RN: + printf("@(r0, r%d)", n); + break; + case AT_R0RM: + printf("@(r0, r%d)", m); + break; + case AT_DGBR: + printf("@(%d, gbr)", d * opsize); + break; + } +} + +static void instruction_output(uint16_t opcode, struct asm_match const *match) +{ + char const *mnemonic = match->insn->mnemonic; + printf("%6x: %04x %s", pc, opcode, mnemonic); + + /* Find out operation size */ + + size_t n = strlen(mnemonic); + int opsize = 0; + + if(n >= 3 && mnemonic[n-2] == '.') + { + int c = mnemonic[n-1]; + opsize = (c == 'b') + 2 * (c == 'w') + 4 * (c == 'l'); + } + + /* Output arguments */ + + if(!match->insn->arg1) return; + printf("%*s", (int)(8-n), ""); + arg_output(match->insn->arg1, match->insn->literal1, match, opsize); + + if(!match->insn->arg2) return; + printf(", "); + arg_output(match->insn->arg2, match->insn->literal2, match, opsize); +} + +static void instruction_single(uint16_t opcode) +{ + struct asm_match match; + asm_decode(opcode, &match, 0); + instruction_output(opcode, &match); + printf("\n"); +} + +static void instruction_conflicts(uint16_t opcode, int count) +{ + struct asm_match match; + printf("\n # conflicts[%d] on <%x>(%04x)\n", count, pc, opcode); + + for(int i = 0; i < count; i++) + { + asm_decode(opcode, &match, i); + instruction_output(opcode, &match); + printf(" # table '%s'\n", match.table); + } + + printf("\n"); +} + +/* disassembly_os(): Disassemble an address or a syscall */ +void disassembly_os(struct os const *src, struct disassembly const *opt) +{ + /* Initialize this file's global state */ + mpu = src->mpu; + pc = opt->start; + data = src->data; + len = src->len; + os = src; + + /* Override MPU guesses if user requested a specific platform */ + if(opt->mpu != MPU_GUESS) mpu = opt->mpu; + + /* Handle situations where the start address is a syscall */ + if(opt->syscall) + { + pc = os_syscall(os, opt->start); + if(pc == (uint32_t)-1) + { + err("syscall 0x%04x does not exist", opt->start); + return; + } + } + + /* Take physical addresses */ + pc &= 0x1fffffff; + + /* Cut the length if it reaches past the end of the file */ + uint32_t limit = (pc + opt->len) & ~1; + if(limit > os->len) limit = os->len; + + /* Enforce alignment of PC */ + if(pc & 1) + { + err("address is not 2-aligned, skipping 1 byte"); + pc += 1; + } + + uint8_t *data = os->data; + + while(pc < limit) + { + if(os) + { + int syscall = os_syscall_find(os, pc | 0x80000000); + if(syscall == -1) + syscall = os_syscall_find(os, pc | 0xa0000000); + if(syscall != -1) + { + struct sys_call const *s = sys_find(syscall); + printf("\n<%x %%%03x", pc, syscall); + if(s) printf(" %s", s->name); + printf(">\n"); + } + } + + uint16_t opcode = (data[pc] << 8) | data[pc + 1]; + int count = matches(opcode); + + if(count == 0) printf("%6x: %04x\n", pc, opcode); + else if(count == 1) instruction_single(opcode); + else instruction_conflicts(opcode, count); + + pc += 2; + } +} diff --git a/fxos/errors.c b/fxos/errors.c new file mode 100644 index 0000000..f236bd9 --- /dev/null +++ b/fxos/errors.c @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include + +#define MAX 32 + +/* Array of context strings. */ +static char const *context[MAX] = { NULL }; +/* Number of active elements. */ +static int elements = 0; + +/* push(): Push an element in the context buffer + @el New element + Returns non-zero if the buffer is full. */ +static int push(char const *el) +{ + if(elements >= MAX - 2) return 1; + context[elements++] = el; + return 0; +} + +/* err_context(): Push one or more context elements */ +int err_context(char const *context, ...) +{ + va_list args; + va_start(args, context); + int x = 0; + + /* Delimiter between calls to err_context(), for err_pop() */ + x |= push(NULL); + x |= push(context); + + while(1) + { + char const *el = va_arg(args, char const *); + if(!el) break; + x |= push(el); + } + + va_end(args); + return x; +} + +/* err_pop(): Pop back context elements */ +void err_pop(void) +{ + /* Clear everything until the last NULL delimiter */ + while(--elements >= 0 && context[elements]) + { + context[elements] = NULL; + } +} + +void err_debug(void) +{ + for(int i = 0; i < MAX; i++) fprintf(stderr, "%s: ", context[i]); + fprintf(stderr, "\n"); +} + +/* errf(): Emit an error message */ +void errf(int flags, char const *str, ...) +{ + int saved_errno = errno; + + va_list args; + va_start(args, str); + + for(int i = 0; i <= MAX-2 && (context[i] || context[i+1]); i++) + { + if(context[i]) fprintf(stderr, "%s: ", context[i]); + } + + vfprintf(stderr, str, args); + + if(flags & ERR_ERRNO) + { + fprintf(stderr, ": %s", strerror(saved_errno)); + } + + fprintf(stderr, "\n"); + va_end(args); +} diff --git a/fxos/errors.h b/fxos/errors.h new file mode 100644 index 0000000..a8ab0bd --- /dev/null +++ b/fxos/errors.h @@ -0,0 +1,63 @@ +//--- +// errors: Error message management +// +// An attempt at producing elaborate error messages providing context from +// multiple function calls, with a small amount of code. +// +// Basically, caller functions define context and message parameters +// before doing anything risky. When an error occurs in a sub-function, +// the context is used to prepend relevant information and fill in the +// error message where relevant. +// +// This means that the caller functions must know it advance what kind of +// messages can be emitted to accurately set the context. Although it +// would work through library calls, it is not very suitable. It's rather +// meant for the logic of applications. +//--- + +#ifndef FXOS_ERRORS +#define FXOS_ERRORS + +/* Error levels */ +#define ERR_CRIT 0x01 /* Aborts part or whole of the program's task */ +#define ERR_ERR 0x02 /* Unexpected behavior */ +#define ERR_WARN 0x03 /* Internal consistency issues */ +#define ERR_LOG 0x04 /* Internal logging */ +/* Error flags */ +#define ERR_ERRNO 0x10 + +/* err_context(): Push one or more context elements + @context Context string, must live until it is popped by err_pop() + @... More contexts, same requirements as the first (NULL-terminated) + Returns non-zero if the deepest context level (at least 15) is reached. */ +int err_context(char const *context, ...); + +/* err_pop(): Pop back context elements + Pops back all context elements that were added by the previous call to + err_context(), no matter their number. */ +void err_pop(void); + +/* errf(): Emit an error message + Error message is printed via fprintf() and supports the same format. All the + arguments are passed directly. + + Available flags are any combination of at most one error level: + ERR_CRIT -- Unrecoverable error + ERR_ERR -- Normal error + ERR_WARN -- Warning + ERR_LOG -- Logging + and any of these boolean flags: + ERR_ERRNO -- Also print strerror(errno) as a suffix (as perror()). + + Error levels might be used later on for filtering. Messages without level + will always be displayed. + + @flags An OR-combination of the previous flags, or 0 + @str Error messages + @... Arguments to format [str] through fprintf() */ +void errf(int flags, char const *str, ...); + +/* err(): Short error function */ +#define err(str, ...) errf(0, str __VA_OPT__(,) __VA_ARGS__) + +#endif /* FXOS_ERRORS */ diff --git a/fxos/fxos.h b/fxos/fxos.h index 5c5f2a6..c22441a 100644 --- a/fxos/fxos.h +++ b/fxos/fxos.h @@ -6,149 +6,405 @@ #define FX_FXOS #include -#include +#include /* Microprocessor platforms */ -enum mpu -{ - mpu_unknown = 0, - mpu_sh7705 = 1, - mpu_sh7305 = 2, +enum mpu { + MPU_GUESS = 0, + MPU_SH7705 = 1, + MPU_SH7305 = 2, }; /* -** Data tables (tables.c) +** Memory (memory.c) */ -/* tables_add_asm(): Append an instruction table to the table list - This function adds a new instruction table to fetch instructions from; it - will be consulted if searching any of the previously-declared tables fails. - - @filename Name of instruction table file - Returns non-zero on error (and prints a message on stderr) */ -int tables_add_asm(const char *filename); - -/* tables_add_syscall(): Append a syscall table to the table list - This function adds a new syscall table to fetch syscalls information from; - if will be consulted if searching any of the previously-declared tables - fails. - - @filename Name of instruction table file - Returns non-zero on error (and prints a message on stderr) */ -int tables_add_syscall(const char *filename); - - -/* -** RAM dumps (ram.c) -*/ - -/* Region for a single RAM dump */ +/* struct region: A valid memory region for at least one platform */ struct region { - uint32_t start; - uint32_t length; - void *data; + uint32_t start; /* Start address */ + uint32_t end; /* End address */ + char const *name; /* Name, used to identify RAM dump files */ + enum mpu platform; /* Platform hint or MPU_GUESS */ }; -/* RAM dump */ -struct ram_sh7705 -{ - struct region RAM; /* Usual RAM (256k) */ +/* memory_region(): Find the region where an address is located + Returns NULL if the address points to no valid memory. This function rejects + addresses of peripheral registers in P3 or P4 space and only heeds for + contiguous RAM or ROM areas. + + @address 32-bit virtual memory address + Returns a region description matching the address, NULL if none is known.*/ +struct region const *memory_region(uint32_t address); + +/* +** General table storage (tables.c) +*/ + +/* struct table: Parametric data table */ +struct table { + char const *type; /* Table type, set by user */ + char *name; /* Table name, also set by user */ + void (*free_item)(void *); /* Function to free individual items */ + + int count; /* Number of items in the table */ + size_t size; /* Size of each item */ + void *items; /* Table data */ }; -struct ram_sh7305 -{ - struct region RAM; /* Usual RAM (512k) */ - struct region IL; /* On-chip instruction storage (16k) */ - struct region RS; /* On-chip generic storage (2k) */ + +/* table_available(): Whether a table can be allocated + Returns non-zero if there is space left for a new table, zero if not. */ +int table_available(void); + +/* table_create(): Create a new table + Allocates a new table inside the global storage. The created table can be + searched immediately with table_find(). + + @type Table type, expectedly a string constant + @name Name string, this module takes ownership and will free() it + @free_item Function to use to destroy items in the future + @count Number of items + @size Size of each item + @items Full data array */ +void table_create(char const *type, char *name, void (*free_item)(void *), + int count, size_t size, void *items); + +/* table_find(): Find matching entries in the database tables + + This function traverses all the tables of type @type and returns all the + elements [e] such that [match(e)] is non-zero. + + The search starts with [next=0] and returns the first match; further calls + with [next!=0] will return more matching elements until no more are found + (in which case this function returns NULL) or this function is called again + with [next=0] to start a new search. + + @type Table filter by type + @match Match function + @name Set to matching table name, if not NULL + @next Set it to 0 on the first call, and non-zero after that + Returns a match if one is found, NULL otherwise. */ +void *table_find(char const *type, int (*match)(void *), char const **name, + int next); + + +/* +** Assembly tables (asm.c, lexer-asm.l) +** These tables reference all assembler instructions used by fxos to +** disassemble code. In case of conflict, fxos will disassemble the same +** opcode several times. +*/ + +/* struct asm_insn: Entry of an instruction table */ +struct asm_insn { + uint16_t bits; /* Opcode; arbitrary values for arguments */ + uint16_t arg_mask; /* 1 for constant bits, 0 for arguments */ + + /* Position of the arguments */ + uint8_t n_sh, m_sh, d_sh, i_sh; + /* Masks indicating the length of arguments */ + uint16_t n_mask, m_mask, d_mask, i_mask; + + char *mnemonic; /* NUL-terminated mnemonic */ + + int arg1; /* asm_arg member */ + int arg2; /* asm_arg member */ + + char *literal1; /* When arg1 == LITERAL, argument string */ + char *literal2; /* When arg2 == LITERAL, argument string */ }; +/* enum asm_arg: Argument variants */ +enum asm_arg { + LITERAL=1, /* Literal string, eg. "vbr" or "@(r0, gbr)" */ + IMM, RN, RM, /* "#imm", "rn", "rm" */ + JUMP8, JUMP12, /* Jump from displacement (PC + disp) */ + PCDISP, /* PC-displacement with data */ + AT_RN, AT_RM, /* "@rn", "@rm" */ + AT_RMP, AT_RNP, AT_MRN, /* Post-increment and pre-decrement */ + AT_DRN, AT_DRM, /* Displacement structure addressing */ + AT_R0RN, AT_R0RM, /* r0 structure addressing */ + AT_DGBR, /* GBR addressing */ +}; + +/* struct asm_match: Matching of a 16-bit code against an instruction + Specifies the source instruction and the value of the parameters. The value + of [m], [n], [d] or [i] is unspecified for parameters that are not used in + the matched instruction's opcode. */ +struct asm_match { + /* Matching instruction */ + struct asm_insn const *insn; + + char const *table; /* Table name */ + int m, n, d, i; /* Parameter assignment */ +}; + +/* asm_load(): Load an assembly table + Loads all instructions described by @file into a table named "x" is @file's + basename is on the form "asm-x.txt" and the whole basename otherwise. The + resulting table is available immediately to use with asm_match(). + + Skips every row that does not conform to the file's syntax after printing a + message to stderr. + + @file Input file path */ +void asm_load(char const *file); + +/* asm_decode(): Match a 16-bit opcode against the assembly database + This function searches matches of a 16-bit instruction code inside the + instruction database. Depending on the database files currently loaded, + there can be several matches; this function uses static variables to + maintain state information through several calls. + + First call this function with [next] set to 0. If there is no match, the + call will return non-zero and [*match] will be left unchanged. Otherwise, + the first matching instruction will be described in [*match], the call will + return 0 and internal static variables will be reset. + + Repeatedly call this function with [next != 0] to get further matches. The + search ends when there are no more matches, in which case this function + returns non-zero and [*match] is left unchanged. Any non-zero value for + [next] is suitable. + + The name of the table providing the match is set in [match->table]. Please + bear in mind, though, that table names do not uniquely identify tables. + + @opcode 16-bit opcode to be matched against the database + @match Set to a description of the matching instruction (must not be NULL) + @next Set it to 0 on the first call, and non-zero after that + Returns 0 if a match is found, non-zero otherwise. */ +int asm_decode(uint16_t opcode, struct asm_match *match, int next); + +/* asm_quit(): Unload all assembly tables + Releases all memory held by the assembly table database. */ +void asm_quit(void); + +/* lex_asm(): Assembly table lexer and parser + Lexes and parses string @data of length @length, allocating and filling an + instruction array whose size is stored in [*count] if @count is non-NULL. + Prints messages to stderr for every syntax error in the file. + + @data Input memory, not NUL-terminated (typically a memory-mapped file) + @length Length of input string + @count Set to number of successfully decoded instructions if non-NULL + Returns a free()able array of decoded instructions. */ +struct asm_insn *lex_asm(void *data, size_t length, int *count); + + +/* +** Syscall tables (sys.c) +*/ + +/* struct sys_call: Entry of a syscall table */ +struct sys_call { + uint32_t number; /* Syscall number */ + char *name; /* Symbol or function name */ + char *descr; /* Prototype or description */ +}; + +/* sys_load(): Load a syscall table + Loads a syscall description table. If @file is named "sys-x.txt" then the + table name is set to "x", otherwise @file's basename. + Prints syntax errors and skips invalid lines. + + @file Input file path */ +void sys_load(char const *path); + +/* sys_find(): Find information on a given syscall number + Traverse the syscall tables currently loaded and returns the first entry + matching the provided syscall number, if any is found. + + @number Syscall number + Returns a description of the syscall, NULL if none was found. */ +struct sys_call const *sys_find(uint32_t number); + +/* sys_quit(): Release memory held by the syscall tables */ +void sys_quit(void); + +/* lex_sys(): Syscall table lexer and parser + Lexes and parses string @data of length @len, allocating and filling a + syscall array whose size is stored in [*count] if @count is not NULL. + Prints syntax errors on stderr. + + @data Input memory (typically memory-mapped file) + @len Length of input + @count Set to number of decoded entries if not NULL + Returns a free()able table of decoded syscall entries. */ +struct sys_call *lex_sys(void *data, size_t len, int *count); + + +/* +** Peripheral register tables (reg.c) +*/ + +/* struct reg_address: Entry of a peripheral register table */ +struct reg_address { + uint32_t address; /* Register address */ + char *name; /* Typically an upper-case dotted specifier */ +}; + + +/* reg_load(): Load a peripheral register table + Loads a peripheral register listing. If @file is named "reg-x.txt" then the + table name is set to "x", otherwise @file's basename. + Prints syntax errors and skips invalid lines. Loaded data is available + immediately through reg_find(). + + @file Input file path */ +void reg_load(char const *path); + +/* reg_find(): Find information on a given peripheral register address + Looks up the loaded tables and returns the first entry matching the given + address (if any). + + @address Any input address + Returns a pointer to the matching register, NULL if none was found. */ +struct reg_address const *reg_find(uint32_t address); + +/* reg_quit(): Release memory held by the peripheral register tables */ +void reg_quit(void); + +/* lex_reg(): Peripheral register table lexer and parser + Lexes and parses @data (of length @length). Allocates and fills a register + description array and stores its size in [*count] if @count is not NULL. + Prints messages on stderr if there are syntax errors. + + @data Input string (needs not be NUL-terminated) + @len Length of input + @count Set to the number of decoded register addresses, if not NULL + Returns a free()able table with the decoded data. */ +struct reg_address *lex_reg(void *data, size_t len, int *count); + + +/* +** General OS operations (os.c) +*/ + +/* struct os: Basic OS information */ +struct os { + void *data; /* Operating system dump */ + size_t len; /* File length */ + int fd; /* Underlying file descriptor */ + + char version[15]; /* NUL-terminated OS version string */ + enum mpu mpu; /* User-provided or guessed MPU type */ + + uint32_t syscall_table; /* Syscall table address */ + int syscalls; /* Number of valid syscalls found */ + + uint32_t footer; /* Footer address (-1 if not found) */ +}; + +/* os_load(): Load an OS file and find out basic properties + Guesses the MPU type, finds the syscall table and its size, finds the footer + address. + + @path File path + @os Will be filled with loaded data and information + Returns non-zero in case of loading error or file format error. */ +int os_load(char const *path, struct os *os); + +/* os_syscall(): Get the address of a syscall entry + Does not check bounds, only returns (uint32_t)-1 if the requested entry of + the table is past the end of the file. + + @os Source OS + @syscall Syscall entry number + Returns the syscall address. */ +uint32_t os_syscall(struct os const *os, int syscall); + +/* os_syscall_find(): Find a syscall which points to an address + This function looks for a syscall entry (among the ones that point to valid + memory) whose value is @entry. + + @os Loaded OS structure + @entry Researched value + Returns a syscall ID if some is found, -1 otherwise. */ +int os_syscall_find(struct os const *os, uint32_t entry); + +/* os_free(): Free an OS file opened with os_load() + @os Loaded OS structure */ +void os_free(struct os const *os); + /* ** File identification (info.c) */ -/* Info options */ -struct info -{ - /* OS file (0) or binary file (1) */ - int binary; - /* Force underlying architecture */ - enum mpu mpu; +/* info_os(): Print general information on an OS file - /* RAM dumps, if any */ - union { - struct ram_sh7705 ram3; - struct ram_sh7305 ram4; - }; -}; + This function prints the OS metadata, traverses the syscall table, and + shows a few details of known binary regions such as the footer. + + @os Input OS file */ +void info_os(struct os const *os); + +/* info_binary(): Print general information on a binary file + + This function tries to determine the platform by looking for SH4-only + instructions or SH7705 and SH7305-specific registers addresses. The analysis + results are printed on stdout. + + @data Input file data (memory-mapped) + @len Length of input */ +void info_binary(void *data, size_t len); /* ** Disassembling (disassembly.c) */ -/* Disassembly options */ +/* struct disassembly: Disassembly options */ struct disassembly { - /* OS file (0) or binary file (1) */ - int binary; - /* Force underlying architecture */ - enum mpu mpu; + int binary; /* OS file (0) or binary file (1) */ + enum mpu mpu; /* Force architecture (or MPU_GUESS) */ - /* RAM dumps, if any */ - union { - struct ram_sh7705 ram3; - struct ram_sh7305 ram4; - }; - - /* Start address */ - uint32_t start; - /* Length of disassembled region */ - uint32_t length; + uint32_t start; /* Start address or syscall ID */ + int syscall; /* Non-zero if [start] is a syscall ID */ + uint32_t len; /* Length of disassembled region */ }; +/* disassembly_os(): Disassemble an address or a syscall + Produces a disassembly listing of the program on stdout, annotated with + every piece of information that can be extracted from the OS. + + @os Operating system image to disassemble + @opt Disassembly region and options */ +void disassembly_os(struct os const *os, struct disassembly const *opt); + /* ** Blind analysis (analysis.c) */ -/* Analysis options */ +/* analysis_short(): Print a one-line summary for an address + Prints a list of space-separated elements summarizing the information that + can be found about the provided value (typically an address). This summary + is often inserted in disassembled code as annotation. + + @os Source OS + @value Analyzed value, often an address */ +void analysis_short(struct os const *os, uint32_t value); + +/* struct analysis: In-depth analysis options */ struct analysis { /* Force underlying architecture */ enum mpu mpu; - /* RAM dumps, if any */ - union { - struct ram_sh7705 ram3; - struct ram_sh7305 ram4; - }; - /* Analysis mode */ enum { - ANALYSIS_FULL = 0, - ANALYSIS_SYSCALL = 1, - ANALYSIS_ADDRESS = 2, - ANALYSIS_REGISTER = 3, + ANALYSIS_SYSCALL = 0x01, + ANALYSIS_ADDRESS = 0x02, + ANALYSIS_REGISTER = 0x04, + + ANALYSIS_FULL = 0x07, } type; /* Max number of printed occurrences */ int occurrences; }; - -/* -** Utility functions (util.c) -*/ - -/* integer(): Convert base 8, 10 or 16 into integers - Prints an error message and sets *error to 1 in case of conversion error or - overflow. - - @str Original integer representation ("10", "0x1f", "07") - @error Set to 1 on error, otherwise unchanged (can be NULL) - Returns result of conversion (valid if *error is not 1) */ -long long integer(const char *str, int *error); - #endif /* FX_FXOS */ diff --git a/fxos/info.c b/fxos/info.c new file mode 100644 index 0000000..37bfde7 --- /dev/null +++ b/fxos/info.c @@ -0,0 +1,120 @@ +#include +#include + +#include +#include +#include + +char const *info_str = +"Header information:\n" +" Bootcode timestamp (DateA) (0x8000ffb0) : %s\n" +" Serial number (0x8000ffd0) : %s\n" +" Bootcode checksum (0x8000fffc) : 0x%s\n" +" OS version (0x80010020) : %s\n"; + +char const *footer_str = +"\nFooter information:\n" +" Detected footer address : 0x8%07x\n" +" Langdata entries found : %d\n" +" OS date (DateO) (0x8%07x)" " : %s\n" +" OS checksum (0x8%07x)" " : 0x%s\n"; + +char const *syscall_str = +"\nSyscall information:\n" +" Syscall table address (0x8001007c) : 0x%08x\n" +" Entries that point to valid memory : 0x%x\n" +" First seemingly invalid entry : 0x%08x\n" +" Syscall entries outside ROM:\n"; + +char const *syscall_nonrom_str = +" %%%04x -> 0x%08x (%s memory)\n"; + +/* extract_str(): Extract a string from the OS file + This function checks offsets and bounds and writes "(eof)" if the provided + file is too small for the tested offset. */ +static void extract_str(struct os const * os, uint32_t address, size_t size, + char *dst) +{ + if(address + size > os->len) + { + strcpy(dst, "(eof)"); + return; + } + + memcpy(dst, os->data + address, size); + dst[size] = 0; +} + +/* extract_int(): Extract a string from the OS file */ +static void extract_int(struct os const *os, uint32_t address, char *dst) +{ + if(address + 4 > os->len) + { + strcpy(dst, "(eof)"); + return; + } + + uint32_t x; + memcpy(&x, os->data + address, 4); + x = be32toh(x); + sprintf(dst, "%08x", x); +} + +/* info_os(): Print general information on an OS file */ +void info_os(struct os const *os) +{ + void *data = os->data; + size_t len = os->len; + + char bios_timestamp[15]; + extract_str(os, 0xffb0, 14, bios_timestamp); + + char serial[9]; + extract_str(os, 0xffd0, 8, serial); + + char bios_checksum[9]; + extract_int(os, 0xfffc, bios_checksum); + + printf(info_str, bios_timestamp, serial, bios_checksum, os->version); + + if(os->footer == (uint32_t)-1) + { + printf("\nFooter could not be found.\n"); + } + else + { + uint32_t addr = os->footer + 8; + int langdata = 0; + + while(addr + 8 < len && !memcmp(data + addr, "Langdata", 8)) + { + langdata++; + addr += 0x30; + } + + char os_timestamp[15]; + extract_str(os, addr, 14, os_timestamp); + + char os_checksum[9]; + extract_int(os, addr + 0x18, os_checksum); + + printf(footer_str, os->footer, langdata, addr, os_timestamp, + addr + 0x18, os_checksum); + } + + printf(syscall_str, os->syscall_table, os->syscalls, + os_syscall(os, os->syscalls)); + + int total = 0; + for(int i = 0; i < os->syscalls; i++) + { + uint32_t e = os_syscall(os, i); + struct region const *r = memory_region(e); + if(!r || !strcmp(r->name, "ROM")) continue; + + printf(syscall_nonrom_str, i, e, r->name); + total++; + } + + if(!total) printf(" (none)\n"); +} diff --git a/fxos/lexer-asm.l b/fxos/lexer-asm.l new file mode 100644 index 0000000..09aac26 --- /dev/null +++ b/fxos/lexer-asm.l @@ -0,0 +1,187 @@ +%{ +#include +#include +#include + +/* Text value for parser */ +static char *yylval; + +%} + +%option prefix="asm" +%option noyywrap +%option nounput + +code ^[01nmdi]{16} +literal [^ ,\t\n]+|[^ ,\t\n(]*"("[^")"\n]*")"[^ ,\t\n]* + +space [ \t]+ + +%% + +^#[^\n]* ; +{space} ; +, ; +[\n] yylineno++; + +{code} { yylval = strdup(yytext); return 0; } +^.{0,16} { err("%d: invalid opcode at start of line", yylineno); } + +"#imm" { return IMM; } +"rn" { return RN; } +"rm" { return RM; } +"jump8" { return JUMP8; } +"jump12" { return JUMP12; } +"pcdisp" { return PCDISP; } +"@rn" { return AT_RN; } +"@rm" { return AT_RM; } +"@rm+" { return AT_RMP; } +"@rn+" { return AT_RNP; } +"@-rn" { return AT_MRN; } +"@(disp,"[ ]*"rn)" { return AT_DRN; } +"@(disp,"[ ]*"rm)" { return AT_DRM; } +"@(r0,"[ ]*"rn)" { return AT_R0RN; } +"@(r0,"[ ]*"rm)" { return AT_R0RM; } +"@(disp",[ ]*"gbr)" { return AT_DGBR; } +{literal} { yylval = strdup(yytext); return LITERAL; } + +<> { return -1; } + +%% + +#include + +/* set_code(): Build an efficient representation of an opcode + Takes a 16-byte string as argument, representing the parameterized opcode, + and computes a bit-based representation inside the assembly structure. + + @code 16-bit opcode made of '0', '1', 'm', 'n', 'd' and 'i' + @insn Instruction object */ +void set_code(char const *code, struct asm_insn *insn) +{ + insn->bits = insn->arg_mask = 0; + insn->n_sh = insn->n_mask = 0; + insn->m_sh = insn->m_mask = 0; + insn->d_sh = insn->d_mask = 0; + insn->i_sh = insn->i_mask = 0; + + for(int i = 0; i < 16; i++) + { + int c = code[i]; + + /* Constant bits */ + if(c == '0' || c == '1') + { + insn->bits = (insn->bits << 1) | (c - '0'); + insn->arg_mask <<= 1; + continue; + } + + /* Argument bits */ + insn->bits <<= 1; + insn->arg_mask = (insn->arg_mask << 1) | 1; + + if(c == 'n') + { + insn->n_sh = 15 - i; + insn->n_mask = (insn->n_mask << 1) | 1; + } + if(c == 'm') + { + insn->m_sh = 15 - i; + insn->m_mask = (insn->m_mask << 1) | 1; + } + if(c == 'd') + { + insn->d_sh = 15 - i; + insn->d_mask = (insn->d_mask << 1) | 1; + } + if(c == 'i') + { + insn->i_sh = 15 - i; + insn->i_mask = (insn->i_mask << 1) | 1; + } + } + + insn->arg_mask = ~insn->arg_mask; +} + +/* lex_asm(): Assembly table lexer and parser */ +struct asm_insn *lex_asm(void *data, size_t length, int *count) +{ + /* First count the number of instruction codes */ + + YY_BUFFER_STATE buf = yy_scan_bytes(data, length); + yylineno = 1; + + int total = 0, t; + while((t = yylex()) != -1) + { + total += (t == 0); + if(t == 0 || t == LITERAL) free(yylval); + } + + yy_delete_buffer(buf); + + /* Allocate a large enough instruction array */ + + struct asm_insn *table = calloc(total, sizeof *table); + if(!table) + { + errf(ERR_ERRNO, "cannot allocate memory for database"); + return 0; + } + + /* Lex all instructions and fill in the array */ + + buf = yy_scan_bytes(data, length); + yylineno = 1; + + struct asm_insn *insn = table - 1; + int line = -1; + int named = 1; + + while(1) + { + t = yylex(); + + if(yylineno != line || t == 0 || t == -1) + { + /* Finalize current instruction */ + if(!named) err("%d: unnamed instruction", line); + insn++; + } + if(t == -1) break; + + if(t == 0) + { + set_code(yylval, insn); + free(yylval); + line = yylineno; + named = 0; + } + else if(t == LITERAL && !named) + { + insn->mnemonic = yylval; + named = 1; + } + else if(!named) + { + err("%d: missing mnemonic", line); + } + else if(!insn->arg1) + { + insn->arg1 = t; + if(t == LITERAL) insn->literal1 = yylval; + } + else if(!insn->arg2) + { + insn->arg2 = t; + if(t == LITERAL) insn->literal2 = yylval; + } + } + + yy_delete_buffer(buf); + if(count) *count = insn - table; + return table; +} diff --git a/fxos/lexer-reg.l b/fxos/lexer-reg.l new file mode 100644 index 0000000..c46e0cb --- /dev/null +++ b/fxos/lexer-reg.l @@ -0,0 +1,117 @@ +%{ +#include +#include +#include + +/* Values for tokens generated by the lexer */ +static union { + char *text; + long long integer; +} yylval; + +/* Token list */ +#define NUMBER 0 +#define SYMBOL 1 + +%} + +%option prefix="reg" +%option noyywrap +%option nounput + +decimal 0|[1-9][0-9]* +octal 0[0-7]+ +hexa 0x[0-9a-fA-F]+ +number {decimal}|{octal}|{hexa} + +symbol [^0-9 \t\n][^\n]* + +space [ \t]+ + +%% + +^#[^\n]* ; +{space} ; +[\n] yylineno++; + +{number} { yylval.integer = integer(yytext, NULL); return NUMBER; } +{symbol} { yylval.text = strdup(yytext); return SYMBOL; } + +<> return -1; + +%% + +#include + +/* lex_reg(): Peripheral register table lexer and parser */ +struct reg_address *lex_reg(void *data, size_t len, int *count) +{ + /* Count number of register descriptions (upper bound) */ + + YY_BUFFER_STATE buf = yy_scan_bytes(data, len); + yylineno = 1; + + int total = 0, t; + while((t = yylex()) != -1) + { + total += (t == NUMBER); + if(t == SYMBOL) free(yylval.text); + } + + yy_delete_buffer(buf); + + /* Allocate a large enough register array */ + + struct reg_address *table = calloc(total, sizeof *table); + if(!table) + { + errf(ERR_ERRNO, "cannot allocate memory for database"); + return 0; + } + + /* Lex all instructions and fill in the array */ + + buf = yy_scan_bytes(data, len); + yylineno = 1; + + struct reg_address *reg = table - 1; + int line = -1; + + while(1) + { + t = yylex(); + + if(t == NUMBER || t == -1) + { + /* Finalize current instruction */ + if(reg >= table && !reg->name) + err("%d: unnamed register", line); + else reg++; + } + if(t == -1) break; + + if(t == NUMBER) + { + reg->address = yylval.integer; + line = yylineno; + } + else if(t == SYMBOL && reg < table) + { + err("%d: expected register address", yylineno); + free(yylval.text); + } + else if(t == SYMBOL && !reg->name) + { + reg->name = yylval.text; + } + else if(t == SYMBOL) + { + err("%d: excess names for 0x%08x\n", reg->address); + free(yylval.text); + } + } + + yy_delete_buffer(buf); + if(count) *count = reg - table; + return table; +} diff --git a/fxos/lexer-sys.l b/fxos/lexer-sys.l new file mode 100644 index 0000000..fca11e2 --- /dev/null +++ b/fxos/lexer-sys.l @@ -0,0 +1,123 @@ +%{ +#include +#include +#include + +/* Values for tokens generated by the lexer */ +static union { + char *text; + long long integer; +} yylval; + +/* Token list */ +#define NUMBER 0 +#define TEXT 1 + +%} + +%option prefix="sys" +%option noyywrap +%option nounput + +decimal 0|[1-9][0-9]* +octal 0[0-7]+ +hexa 0x[0-9a-fA-F]+ +number {decimal}|{octal}|{hexa} + +space [ \t]+ + +%% + +^#[^\n]* ; +{space} ; +[\n] yylineno++; + +{number} { yylval.integer = integer(yytext, NULL); return NUMBER; } +[^ \t\n0-9].* { yylval.text = strdup(yytext); return TEXT; } + +<> return -1; + +%% + +#include + +/* lex_sys(): Syscall table lexer and parser */ +struct sys_call *lex_sys(void *data, size_t len, int *count) +{ + /* Count the number of syscalls */ + + YY_BUFFER_STATE buf = yy_scan_bytes(data, len); + yylineno = 1; + + int total = 0, t; + while((t = yylex()) != -1) + { + total += (t == NUMBER); + if(t == TEXT) free(yylval.text); + } + + yy_delete_buffer(buf); + + /* Allocate a large enough syscall array */ + + struct sys_call *table = calloc(total, sizeof *table); + if(!table) + { + errf(ERR_ERRNO, "cannot allocate memory for database"); + return 0; + } + + /* Lex all instructions and fill in the array */ + + buf = yy_scan_bytes(data, len); + yylineno = 1; + + struct sys_call *call = table - 1; + int line = -1; + int named = 1; + + while(1) + { + t = yylex(); + + if(t == NUMBER || t == -1) + { + /* Finalize current instruction */ + if(!named) err("%d: unnamed syscall", line); + else call++; + } + if(t == -1) break; + + if(t == NUMBER) + { + call->number = yylval.integer; + line = yylineno; + named = 0; + } + else if(t == TEXT && call < table) + { + err("%d: expected syscall id", yylineno); + free(yylval.text); + } + else if(t == TEXT && named == 0) + { + call->name = yylval.text; + named = 1; + } + else if(t == TEXT && named == 1) + { + call->descr = yylval.text; + named = 2; + } + else if(t == TEXT) + { + err("%d: excess qualifiers for syscall 0x%03x", line, + call->number); + free(yylval.text); + } + } + + yy_delete_buffer(buf); + if(count) *count = call - table; + return table; +} diff --git a/fxos/main.c b/fxos/main.c index cc364fd..a88b14c 100644 --- a/fxos/main.c +++ b/fxos/main.c @@ -4,144 +4,253 @@ #include #include +#include +#include -static const char *help_string = +static char const *help_string = "usage: %1$s info ( | -b )\n" " %1$s disasm (-a
| -s ) [options...]\n" " %1$s disasm -b [options...]\n" -" %1$s analyze [-f|-s|-a|-r] [options...]\n" +" %1$s analyze [-f] [-s] [-a] [-r] [options...]\n" "\n" -"fxos disassembles or analyzes binary and OS files for efficient reverse-\n" -"engineering. It currently only supports fx9860g binaries.\n" +"fxos is a reverse-engineering tool to disassemble and analyze fx9860g-like\n" +"OS dumps, providing efficient annotations through an editable database.\n" "\n" "Commands:\n" " info Identify an OS image: version, platform, date, checksums...\n" " Identify the architecture of a binary file.\n" " disasm Disassemble and annotate code with relative address targets,\n" " syscall invocations and hints about memory structure.\n" -" analyze Dig an address or syscall number, finding syscall call graph,\n" +" analyze Dig an address or syscall number, finding syscall references,\n" " 4-aligned occurrences, memory region and probable role.\n" "\n" "General options:\n" -" -b Disassemble any binary file, not an OS file\n" -" -3, --sh3 Assume SH3 OS and platform (default: guess)\n" -" -4, --sh4 Assume SH4 OS and platform (default: guess)\n" -" --ram Read RAM dumps from \n" -" --table-asm Read more instruction patterns in \n" -" --table-call Read more syscall prototypes in \n" +" -b Work with an arbitrary binary file, not an OS\n" "\n" -"Disassembly region options:\n" +"Table extensions:\n" +" --table-asm Use instruction codes and mnemonics from \n" +" --table-sys Use syscall prototypes and descriptions from \n" +" --table-reg Use peripheral register addresses from \n" +"\n" +"Disassembly options:\n" " -a
Start disassembling at this address\n" " -s Start disassembling at this syscall's address\n" " -l Length of region\n" +" -3, --sh3 Assume SH3 OS and platform (default: guess)\n" +" -4, --sh4 Assume SH4 OS and platform (default: guess)\n" "\n" "Analysis modes:\n" -" -f, --full Find everything that can be known about \n" -" -s, --syscall is a syscall ID\n" -" -a, --address is a code/data address in ROM or RAM\n" -" -r, --register is a register or peripheral address\n" +" -f, --full Run all analysis passes on (same as -sar)\n" +" -s, --syscall Run syscall ID analysis\n" +" -a, --address Run code/data address analyis\n" +" -r, --register Run peripheral register analysis\n" "\n" "Analysis options:\n" -" --occurrences Show at most occurrences (integer or \"all\")\n"; +" --occurrences Show at most occurrences (integer or \"all\")\n" +"\n" +"All numbers support base prefixes '0' (octal) and '0x' (hexadecimal).\n"; -/* "Low-level" command-line option set */ -struct options +#define OPT_ASM 1 +#define OPT_SYS 2 +#define OPT_REG 3 +#define OPT_OCC 4 + +/* +** "info" command +*/ + +int main_info(int argc, char **argv) { - const char *input; + int error=0, option=0, binary=0; - int target; - const char *ram; + struct option const longs[] = { + { "help", no_argument, NULL, 'h' }, + }; - const char *a; - const char *s; - size_t l; - - int f; - int r; - const char *occ; -}; - -int main(int argc, char **argv) -{ - int command = 0, error = 0; - struct options opt = { 0 }; - /* For string -> int conversions, first non-int character */ - - - /* Get command name */ - if(argc >= 2) + while(option >= 0 && option != '?') + switch((option = getopt_long(argc, argv, "hb", longs, NULL))) { - if(!strcmp(argv[1], "info")) command = 'i'; - if(!strcmp(argv[1], "disasm")) command = 'd'; - if(!strcmp(argv[1], "analyze")) command = 'a'; - - if(!command && argv[1][0] != '-') - { - fprintf(stderr, "invalid operation: '%s'\n", argv[1]); - fprintf(stderr, "Try '%s --help'.\n", argv[0]); - return 1; - } - - if(command) argv[1] = ""; + case 'h': + err(help_string, argv[0]); + break; + case 'b': + binary = 1; + break; + case '?': + error = 1; } - enum { - OPT_RAM = 1, - OPT_ASM = 2, - OPT_CALL = 3, - OPT_OCC = 4, - }; - const struct option longs[] = { - { "help", no_argument, NULL, 'h' }, - { "sh3", no_argument, NULL, '3' }, - { "sh4", no_argument, NULL, '4' }, - { "ram", required_argument, NULL, OPT_RAM }, - { "table-asm", required_argument, NULL, OPT_ASM }, - { "table-call", required_argument, NULL, OPT_CALL }, - { "full", no_argument, NULL, 'f' }, - { "syscall", no_argument, NULL, 's' }, - { "address", no_argument, NULL, 'a' }, - { "register", no_argument, NULL, 'r' }, - { "occurrences", required_argument, NULL, OPT_OCC }, + if(error) return 1; + char const *path = argv[optind + 1]; + + if(!path) + { + err(help_string, argv[0]); + return 1; + } + + err_context(path, NULL); + + struct os os; + if(os_load(path, &os)) { err_pop(); return 1; } + + if(binary) + { + err("TODO: Binary file info x_x"); + } + else + { + info_os(&os); + } + + os_free(&os); + err_pop(); + return 0; +} + +/* +** "disasm" command +*/ + +int main_disassembly(int argc, char **argv) +{ + int error=0, option=0; + + /* First load some of fxos' default resources */ + asm_load(FXSDK_PREFIX "/share/fxsdk/asm-sh3.txt"); + asm_load(FXSDK_PREFIX "/share/fxsdk/asm-sh4.txt"); + sys_load(FXSDK_PREFIX "/share/fxsdk/sys-simlo.txt"); + sys_load(FXSDK_PREFIX "/share/fxsdk/sys-lephe.txt"); + reg_load(FXSDK_PREFIX "/share/fxsdk/reg-sh7305.txt"); + reg_load(FXSDK_PREFIX "/share/fxsdk/reg-simlo.txt"); + + struct disassembly opt = { 0 }; + opt.mpu = MPU_GUESS; + opt.len = 0x20; + + struct option const longs[] = { + { "help", no_argument, NULL, 'h' }, + { "sh3", no_argument, NULL, '3' }, + { "sh4", no_argument, NULL, '4' }, + { "table-asm", required_argument, NULL, OPT_ASM }, + { "table-sys", required_argument, NULL, OPT_SYS }, + { "table-reg", required_argument, NULL, OPT_REG }, }; - int option = 0; while(option >= 0 && option != '?') - switch((option = getopt_long(argc, argv, "hb34a::s::l:fr",longs,NULL))) + switch((option = getopt_long(argc, argv, "hb34a:s:l:", longs, NULL))) + { + case 'h': + err(help_string, argv[0]); + break; + case 'b': + opt.binary = 1; + break; + case '3': + opt.mpu = MPU_SH7705; + break; + case '4': + opt.mpu = MPU_SH7305; + break; + case OPT_ASM: + asm_load(optarg); + break; + case OPT_SYS: + printf("TODO: Load additional syscall tables x_x\n"); + break; + case OPT_REG: + printf("TODO: Load additional register tables x_x\n"); + break; + case 'a': + case 's': + opt.start = integer(optarg, &error); + opt.syscall = (option == 's'); + break; + case 'l': + opt.len = integer(optarg, &error); + break; + case '?': + error = 1; + } + + if(error) return 1; + char const *path = argv[optind + 1]; + + if(!path) + { + err(help_string, argv[0]); + return 1; + } + + err_context(path, NULL); + + if(opt.binary) + { + printf("TODO: Disassembly binary x_x"); + err_pop(); + return 1; + } + + struct os os; + if(os_load(path, &os)) { err_pop(); return 1; } + + disassembly_os(&os, &opt); + + os_free(&os); + err_pop(); + return 0; +} + +/* +** "analyze" command +*/ + +int main_analyze(int argc, char **argv) +{ + int error=0, option=0; + struct analysis opt = { 0 }; + + struct option const longs[] = { + { "help", no_argument, NULL, 'h' }, + { "table-asm", required_argument, NULL, OPT_ASM }, + { "table-sys", required_argument, NULL, OPT_SYS }, + { "table-reg", required_argument, NULL, OPT_REG }, + { "full", no_argument, NULL, 'f' }, + { "syscall", no_argument, NULL, 's' }, + { "address", no_argument, NULL, 'a' }, + { "register", no_argument, NULL, 'r' }, + { "occurrences", required_argument, NULL, OPT_OCC }, + }; + + while(option >= 0 && option != '?') + switch((option = getopt_long(argc,argv,"hfsar", longs, NULL))) { case 'h': fprintf(stderr, help_string, argv[0]); return 0; - case '3': - case '4': - opt.target = option; - break; - case OPT_RAM: - opt.ram = optarg; - break; case OPT_ASM: - tables_add_asm(optarg); + asm_load(optarg); break; - case OPT_CALL: - tables_add_syscall(optarg); + case OPT_SYS: +// sys_load(optarg); + break; + case OPT_REG: +// reg_load(optarg); break; case 'f': - opt.f = 1; + opt.type = ANALYSIS_FULL; break; case 's': - opt.s = optarg; + opt.type |= ANALYSIS_SYSCALL; break; case 'a': - opt.a = optarg; - break; - case 'l': - opt.l = integer(optarg, &error); + opt.type |= ANALYSIS_ADDRESS; break; case 'r': - opt.r = 1; + opt.type |= ANALYSIS_REGISTER; break; case OPT_OCC: - opt.occ = optarg; + opt.occurrences = integer(optarg, &error); break; case '?': error = 1; @@ -149,35 +258,44 @@ int main(int argc, char **argv) } if(error) return 1; - opt.input = argv[optind]; - if(!opt.input) - { - fprintf(stderr, help_string, argv[0]); - return 1; - } - - /* Load default tables (user-specified tables will override them) */ - tables_add_asm (FXSDK_PREFIX "/share/assembly-sh3.txt"); - tables_add_syscall(FXSDK_PREFIX "/share/syscalls.txt"); - - /* Change interpretation of arguments depending on mode */ - - printf("Operation is '%c'\n", command); - printf(" input=%s\n", opt.input); - printf(" target='%c'\n", opt.target); - printf(" ram=%s\n", opt.ram); - printf(" a=%s\n", opt.a); - printf(" s=%s\n", opt.s); - printf(" l=%zu\n", opt.l); - printf(" f=%d\n", opt.f); - printf(" r=%d\n", opt.r); - printf(" occ=%s\n", opt.occ); - - /* TODO: Execution procedure: - TODO: 1. Identify architecture - TODO: 2. Load RAM data, syscall tables, peripheral modules, etc - TODO: 3. Execute command */ + /* If no analysis mode was specified, do everything */ + if(!opt.type) opt.type = ANALYSIS_FULL; return 0; } + +/* +** Program entry +*/ + +int main(int argc, char **argv) +{ + if(argc < 2) + { + err(help_string, argv[0]); + return 1; + } + + char *cmd = argv[1]; + argv[1] = ""; + + /* Switch on command name */ + if(!strcmp(cmd, "info")) + return main_info(argc, argv); + else if(!strcmp(cmd, "disasm")) + return main_disassembly(argc, argv); + else if(!strcmp(cmd, "analyze")) + return main_analyze(argc, argv); + + else if(!strcmp(cmd, "-?") || !strcmp(cmd, "-h") || + !strcmp(cmd, "--help")) + { + err(help_string, argv[0]); + return 0; + } + + err("invalid operation '%s'", cmd); + err("Try '%s --help'.", argv[0]); + return 1; +} diff --git a/fxos/memory.c b/fxos/memory.c new file mode 100644 index 0000000..8a97e72 --- /dev/null +++ b/fxos/memory.c @@ -0,0 +1,38 @@ +#include + +/* Shared by all platforms (though ROM is 4M on most) */ + +static struct region ROM = { 0x80000000, 0x807fffff, "ROM", MPU_GUESS }; +static struct region RAM = { 0x88000000, 0x88040000, "RAM", MPU_GUESS }; + +static struct region P2_ROM = { 0xa0000000, 0xa07fffff, "P2 ROM", MPU_GUESS }; +static struct region P2_RAM = { 0xa8000000, 0xa8040000, "P2 RAM", MPU_GUESS }; + +/* SH7305 only */ + +static struct region RS = { 0xfd800000, 0xfd8007ff, "RS", MPU_SH7305 }; +static struct region IL = { 0xe5200000, 0xe5203fff, "IL", MPU_SH7305 }; +static struct region XRAM = { 0xe5007000, 0xe5008fff, "XRAM", MPU_SH7305 }; +static struct region YRAM = { 0xe5017000, 0xe5018fff, "YRAM", MPU_SH7305 }; + +/* A summary */ +static struct region *regions[] = { + &ROM, &RAM, &P2_ROM, &P2_RAM, &IL, &RS, NULL, +}; + +/* memory_in(): Check if an address if inside a given region */ +static int memory_in(uint32_t address, struct region const *reg) +{ + return reg && address >= reg->start && address <= reg->end; +} + +/* memory_region(): Find the region where an address is located */ +struct region const *memory_region(uint32_t address) +{ + for(int i = 0; regions[i]; i++) + { + if(memory_in(address, regions[i])) return regions[i]; + } + + return NULL; +} diff --git a/fxos/os.c b/fxos/os.c new file mode 100644 index 0000000..cf374b2 --- /dev/null +++ b/fxos/os.c @@ -0,0 +1,91 @@ +#include +#include + +#include +#include +#include + +/* os_load(): Load an OS file and find out basic properties */ +int os_load(char const *path, struct os *os) +{ + if(!path || !os) return 1; + + os->data = map(path, &os->fd, &os->len); + if(!os->data) return 1; + if(os->len < 0x10080) + { + err("too small for the OS format I know (min. 0x10080 bytes)", + os->len); + return 1; + } + + /* Get the OS version */ + + memcpy(os->version, os->data + 0x10020, 14); + os->version[14] = 0; + + /* Guess the MPU type (or assume SH7305). Until version 03.00, the last + digit of the version number indicated the presence of an SH7305 */ + + os->mpu = MPU_SH7305; + if(!strncmp(os->version, "02.", 3) && os->version[13] == '0') + os->mpu = MPU_SH7705; + + /* Find the syscall table address */ + + memcpy(&os->syscall_table, os->data + 0x1007c, 4); + os->syscall_table = be32toh(os->syscall_table); + + /* Count the number of valid syscall entries */ + + os->syscalls = 0; + while(memory_region(os_syscall(os, os->syscalls))) os->syscalls++; + + /* Find the footer address (last occurrence of "CASIOABSLangdata") */ + + char const *signature = "CASIOABSLangdata"; + void *occ = NULL, *next = memmem(os->data, os->len, signature, 16); + void *end = os->data + os->len; + + while(next) + { + occ = next; + next = memmem(next + 1, end - (next + 1), signature, 16); + } + + os->footer = (occ) ? (occ - os->data) : (uint32_t)-1; + + return 0; +} + +/* os_syscall(): Get the address of a syscall entry */ +uint32_t os_syscall(struct os const *os, int syscall) +{ + uint32_t table_entry = (os->syscall_table & 0x1fffffff) + 4 * syscall; + if(table_entry + 4 > os->len) return (uint32_t)-1; + + uint32_t address; + memcpy(&address, os->data + table_entry, 4); + address = be32toh(address); + + return address; +} + +/* os_syscall_find(): Find a syscall which points to an address */ +int os_syscall_find(struct os const *os, uint32_t entry) +{ + /* Try to make this slightly quicker */ + uint32_t table_entry = (os->syscall_table & 0x1fffffff); + entry = htobe32(entry); + + uint32_t *entries = os->data + table_entry; + for(int i = 0; i < os->syscalls; i++) if(entries[i] == entry) return i; + + return -1; +} + +/* os_free(): Free an OS file opened with os_load() */ +void os_free(struct os const *os) +{ + if(os && os->data) unmap(os->data, os->fd, os->len); +} diff --git a/fxos/reg-sh7305.txt b/fxos/reg-sh7305.txt new file mode 100644 index 0000000..3d33caf --- /dev/null +++ b/fxos/reg-sh7305.txt @@ -0,0 +1,114 @@ +# T6K11 interface +0xb4000000 T6K11.REG +0xb4010000 T6K11.DATA + +# Exception handling +0xff000020 TRA +0xff000024 EXPEVT +0xff000028 INTEVT +0xff2f0004 EXPMASK + +# Memory Management Unit +0xff000000 MMU.PTEH +0xff000004 MMU.PTEL +0xff00000c MMU.TEA +0xff000010 MMU.MMUCR +0xff000034 MMU.PTEA +0xff000070 MMU.PASCR +0xff000078 MMU.IRMCR + +# Interrupt controller +0xa4140000 INTC.ICR0 +0xa414001c INTC.ICR1 +0xa4140010 INTC.INTPRI00 +0xa4140024 INTC.INTREQ00 +0xa4140044 INTC.INTMSK00 +0xa4140064 INTC.INTMSKCLR00 +0xa41400c0 INTC.NMIFCR +0xa4700000 INTC.USERIMSK +0xa4080000 INTC.IPRA +0xa4080004 INTC.IPRB +0xa4080008 INTC.IPRC +0xa408000c INTC.IPRD +0xa4080010 INTC.IPRE +0xa4080014 INTC.IPRF +0xa4080018 INTC.IPRG +0xa408001c INTC.IPRH +0xa4080020 INTC.IPRI +0xa4080024 INTC.IPRJ +0xa4080028 INTC.IPRK +0xa408002c INTC.IPRL +0xa4080080 INTC.IMR0 +0xa4080084 INTC.IMR1 +0xa4080088 INTC.IMR2 +0xa408008c INTC.IMR3 +0xa4080090 INTC.IMR4 +0xa4080094 INTC.IMR5 +0xa4080098 INTC.IMR6 +0xa408009c INTC.IMR7 +0xa40800a0 INTC.IMR8 +0xa40800a4 INTC.IMR9 +0xa40800a8 INTC.IMR10 +0xa40800ac INTC.IMR11 +0xa40800b0 INTC.IMR12 +0xa40800c0 INTC.IMCR0 +0xa40800c4 INTC.IMCR1 +0xa40800c8 INTC.IMCR2 +0xa40800cc INTC.IMCR3 +0xa40800d0 INTC.IMCR4 +0xa40800d4 INTC.IMCR5 +0xa40800d8 INTC.IMCR6 +0xa40800dc INTC.IMCR7 +0xa40800e0 INTC.IMCR8 +0xa40800e4 INTC.IMCR9 +0xa40800e8 INTC.IMCR10 +0xa40800ec INTC.IMCR11 +0xa40800f0 INTC.IMCR12 + +# Direct Memory Access Controller: TODO + +# Reset and power-down modes +0xa4150020 POWER.STBCR +0xa4150030 POWER.MSTPCR0 +0xa4150034 POWER.MSTPCR1 +0xa4150038 POWER.MSTPCR2 +0xa4150040 POWER.BAR + +# Real-Time Clock +0xa465fec0 RTC.R64CNT +0xa465fec2 RTC.RSECCNT +0xa465fec4 RTC.RMINCNT +0xa465fec6 RTC.RHRCNT +0xa465fec8 RTC.RWKCNT +0xa465feca RTC.RDAYCNT +0xa465fecc RTC.RMONCNT +0xa465fece RTC.RYRCNT +0xa465fed0 RTC.RSECAR +0xa465fed2 RTC.RMINAR +0xa465fed4 RTC.RHRAR +0xa465fed6 RTC.RWKAR +0xa465fed8 RTC.RDAYAR +0xa465feda RTC.RMONAR +0xa465fedc RTC.RCR1 +0xa465fede RTC.RCR2 +0xa465fee0 RTC.RYRAR +0xa465fee4 RTC.RCR3 + +# User Break Controller +0xff200000 UBC.CBR0 +0xff200004 UBC.CRR0 +0xff200008 UBC.CAR0 +0xff20000c UBC.CAMR0 +0xff200020 UBC.CBR1 +0xff200024 UBC.CRR1 +0xff200028 UBC.CAR1 +0xff20002c UBC.CAMR1 +0xff200030 UBC.CDR1 +0xff200034 UBC.CDMR1 +0xff200038 UBC.CETR1 +0xff200600 UBC.CCMFR +0xff200620 UBC.CBCR + +# RCLK Watchdog Timer +0xa4520000 RWDT.RWTCNT +0xA4520004 RWDT.RWTCSR diff --git a/fxos/reg-simlo.txt b/fxos/reg-simlo.txt new file mode 100644 index 0000000..dc474fd --- /dev/null +++ b/fxos/reg-simlo.txt @@ -0,0 +1,141 @@ +# Processor version +0xff2f0000 CPUOPM +0xff000030 PVR +0xff000040 CVR +0xff000044 PRR + +# Key Scan Interface +0xa44b0000 KEYSC.DATA +0xa44b000c KEYSC.UCNTREG +0xa44b000e KEYSC.AUTOFIXREG +0xa44b0010 KEYSC.UMODEREG +0xa44b0012 KEYSC.USTATEREG +0xa44b0014 KEYSC.UINTREG +0xa44b0016 KEYSC.UWSETREG +0xa44b0018 KEYSC.UINTERVALREG +0xa44b001a KEYSC.OUTPINSET +0xa44b001c KEYSC.INPINSET + +# Timer Unit +0xa4490004 TMU.TSTR +0xa4490008 TMU0.TCOR +0xa449000c TMU0.TCNT +0xa4490010 TMU0.TCR +0xa4490014 TMU1.TCOR +0xa4490018 TMU1.TCNT +0xa449001c TMU1.TCR +0xa4490020 TMU2.TCOR +0xa4490024 TMU2.TCNT +0xa4490028 TMU2.TCR + +# Serial Communication Interface +0xa4410000 SCIF.SCSMR +0xa4410004 SCIF.SCBRR +0xa4410008 SCIF.SCSCR +0xa441000c SCIF.SCFTDR +0xa4410010 SCIF.SCFSR +0xa4410014 SCIF.SCFRDR +0xa4410018 SCIF.SCFCR +0xa441001c SCIF.SCFDR +0xa4410024 SCIF.SCLSR + +# Pin Function Controller +0xa4050100 PFC.PACR +0xa4050102 PFC.PBCR +0xa4050104 PFC.PCCR +0xa4050106 PFC.PDCR +0xa4050108 PFC.PECR +0xa405010a PFC.PFCR +0xa405010c PFC.PGCR +0xa405010e PFC.PHCR +0xa4050110 PFC.PJCR +0xa4050112 PFC.PKCR +0xa4050114 PFC.PLCR +0xa4050116 PFC.PMCR +0xa4050118 PFC.PNCR +0xa405014c PFC.PPCR +0xa405011a PFC.PQCR +0xa405011c PFC.PRCR +0xa405011e PFC.PSCR +0xa4050140 PFC.PTCR +0xa4050142 PFC.PUCR +0xa4050144 PFC.PVCR +0xa405014e PFC.PSELA +0xa4050150 PFC.PSELB +0xa4050152 PFC.PSELC +0xa4050154 PFC.PSELD +0xa4050156 PFC.PSELE +0xa405015e PFC.PSELF +0xa40501c8 PFC.PSELG +0xa40501d6 PFC.PSELH +0xa4050158 PFC.HIZCRA +0xa405015a PFC.HIZCRB +0xa405015c PFC.HIZCRC +0xa4050180 PFC.MSELCRA +0xa4050182 PFC.MSELCRB +0xa4050184 PFC.DRVCRD +0xa4050186 PFC.DRVCRA +0xa4050188 PFC.DRVCRB +0xa405018a PFC.DRVCRC +0xa40501c3 PFC.PULCRBSC +0xa40501c5 PFC.PULCRTRST +0xa4050190 PFC.PULCRA +0xa4050191 PFC.PULCRB +0xa4050192 PFC.PULCRC +0xa4050193 PFC.PULCRD +0xa4050194 PFC.PULCRE +0xa4050195 PFC.PULCRF +0xa4050196 PFC.PULCRG +0xa4050197 PFC.PULCRH +0xa4050198 PFC.PULCRJ +0xa4050199 PFC.PULCRK +0xa405019a PFC.PULCRL +0xa405019b PFC.PULCRM +0xa405019c PFC.PULCRN +0xa40501c6 PFC.PULCRP +0xa405019d PFC.PULCRQ +0xa405019e PFC.PULCRR +0xa405019f PFC.PULCRS +0xa40501c0 PFC.PULCRT +0xa40501c1 PFC.PULCRU +0xa40501c2 PFC.PULCRV + +# Bus State Controller +0xfec10000 BSC.CMNCR +0xfec10004 BSC.CS0BCR +0xfec10008 BSC.CS2BCR +0xfec1000c BSC.CS3BCR +0xfec10010 BSC.CS4BCR +0xfec10014 BSC.CS5ABCR +0xfec10018 BSC.CS5BBCR +0xfec1001c BSC.CS6ABCR +0xfec10020 BSC.CS6BBCR +0xfec10024 BSC.CS0WCR +0xfec10028 BSC.CS2WCR +0xfec1002c BSC.CS3WCR +0xfec10030 BSC.CS4WCR +0xfec10034 BSC.CS5AWCR +0xfec10038 BSC.CS5BWCR +0xfec1003c BSC.CS6AWCR +0xfec10040 BSC.CS6BWCR +0xfec10044 BSC.SDCR +0xfec10048 BSC.RTCSR +0xfec1004c BSC.RTCNT +0xfec10050 BSC.RTCOR +0xfec14000 BSC.SDMR2 +0xfec15000 BSC.SDMR3 + +# Clock Pulse Generator +0xa4150000 CPG.FRQCR +0xa4150008 CPG.FCLKCR +0xa4150010 CPG.DDCLKCR +0xa4150014 CPG.USBCLKCR +0xa4150024 CPG.PLLCR +0xa4150028 CPG.PLL2CR +0xa415003c CPG.SPUCLKCR +0xa4150044 CPG.SSCGCR +0xa4150050 CPG.FLLFRQ +0xa4150060 CPG.LSTATS + +# More addresses are around on Casiopeia. +# See http://www.casiopeia.net/forum/viewtopic.php?f=11&t=1756#p14588. diff --git a/fxos/reg.c b/fxos/reg.c new file mode 100644 index 0000000..d190986 --- /dev/null +++ b/fxos/reg.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include + +/* reg_free_item(): Free a peripheral register item */ +static void reg_free_item(void *item) +{ + struct reg_address *reg = item; + free(reg->name); +} + +/* reg_load(): Load a peripheral register table */ +void reg_load(char const *file) +{ + err_context("register", file, NULL); + if(!table_available()) + { + err("too many tables, skipping"); + err_pop(); + return; + } + + /* Map the file to memory */ + int fd; + size_t size; + void *data = map(file, &fd, &size); + if(!data) { err_pop(); return; } + + /* If the file is named "reg-x.txt", use "x" as the table name */ + char *name = match_table_name(file, "reg", ".txt"); + + /* Parse the contents */ + int count; + struct reg_address *regs = lex_reg(data, size, &count); + + table_create("reg", name, reg_free_item, count, sizeof *regs, regs); + + unmap(data, fd, size); + err_pop(); +} + +/* Global storage for reg_match() */ +static uint32_t reg_match_address; + +/* reg_match(): Search routine for reg_find() */ +static int reg_match(void *item) +{ + struct reg_address *reg = item; + return reg->address == reg_match_address; +} + +/* reg_find(): Find information on a given peripheral register address */ +struct reg_address const *reg_find(uint32_t address) +{ + reg_match_address = address; + return table_find("reg", reg_match, NULL, 0); +} diff --git a/fxos/sys-lephe.txt b/fxos/sys-lephe.txt new file mode 100644 index 0000000..f748330 --- /dev/null +++ b/fxos/sys-lephe.txt @@ -0,0 +1,7 @@ +0x000 sys_init +0x004 tlb_init + void tlb_init(void) +0x025 t6k11_read_datareg + uint8_t t6k11_read_datareg(void) +0x3fc tlb_map + void tlb_map(int shifted_vpn, int way, uint32_t data_field) diff --git a/fxos/sys-simlo.txt b/fxos/sys-simlo.txt new file mode 100644 index 0000000..efcfc99 --- /dev/null +++ b/fxos/sys-simlo.txt @@ -0,0 +1,656 @@ +0x001 vbr_tlb_error +0x002 vbr_cpu_address +0x003 vbr_tlb_exception +0x005 App_RegisterAddins + int App_RegisterAddins(void) +0x009 App_FindFreeAddinSlot + int FindFreeAddinSlot(void) +0x00a App_GetAddinHeaderAddr + int GetAddindHeaderAddr(int addin_no, int offset, void *result) +0x00e App_GetAddindEstrip + int App_GetAddindEstrip(int addin_no, int estrip_no, void *result) +0x013 GlibAddinAplExecutionCheck +0x014 GlibGetAddinLibInfo +0x015 GlibGetOSVersionInfo + int GlibGetOSVersionInfo(char *a, char *b, short *c, short *d) +0x018 MMU_FlushCache +0x01b DD_Clear +0x01c Bdisp_WriteGraph_VRAM +0x01d Bdisp_WriteGraph_DD +0x01e Bdisp_WriteGraph_DDVRAM +0x022 Bdisp_ReadArea_VRAM +0x023 Bdisp_ReadArea_DD +0x024 Bdisp_GetDisp_DD +0x025 DD_Read +0x026 DD_ReadFromPage +0x027 DD_WriteToPage +0x028 Bdisp_PutDisp_DD +0x02a Bdisp_DrawShapeToVRAM +0x02f Bdisp_DrawShapeToVRAM +0x030 Bdisp_DrawLineVRAM +0x031 Bdisp_ClearLineVRAM +0x032 Bdisp_DrawShapeToDD +0x033 Bdisp_DrawShapeToVRAM_DD +0x034 Bdisp_DrawShapeToDD +0x035 Bdisp_DrawShapeToVRAM_DD +0x039 RTC_Reset + void RTC_Reset(unsigned int mode) +0x03a RTC_GetTime + void RTC_GetTime(uint *hour, uint *minute, uint *second, uint *millisecond) +0x03b RTC_GetTicks + int RTC_GetTicks(void) +0x03c RTC_Elapsed_ms + int RTC_Elapsed_ms(int start_value, int duration_ms) +0x05c Num_UIntToBCD +0x05d Num_BCDToUInt + +0x118 Timer_Install + int Timer_Install(int timerID, void *handler, int elapse), also known as Bcre_cychdr +0x119 Timer_Deinstall + int Timer_Deinstall(int timerID), also known as Bdel_cychdr +0x11a Timer_Start + int Timer_Start(int timerID) +0x11b Timer_Stop + int Timer_Stop(int timerID) +0x11f Bdisp_PutDispArea_DD +0x12d DD_Poweroff +0x130 Wait_ms +0x132 DD_SetContrast +0x133 DD_SetFRS +0x134 DD_SetBias +0x135 GetVRAMAddress +0x136 GetCharacterGlyph +0x137 GetCharacterMiniGlyph +0x138 Cursor_SetPosition + int Cursor_SetPosition(char column, char row) +0x139 Cursor_SetFlashStyle + int Cursor_SetFlashStyle(short flashStyle) +0x13a Cursor_SetFlashMode + void Cursor_SetFlashMode(long flashMode) +0x13b Cursor_GetSettings + uint Cursor_GetSettings(uint *settingsArray) +0x13c Print_OS + void Print_OS(unsigned char *msg, int type) +0x142 Bdisp_AllClr_DD +0x143 Bdisp_AllClr_VRAM +0x144 Bdisp_AllClr_DDVRAM +0x145 Bdisp_GetDisp_VRAM +0x146 Bdisp_SetPoint_VRAM +0x147 Bdisp_SetPoint_DD +0x148 Bdisp_SetPoint_DDVRAM +0x149 Bdisp_GetPoint_VRAM +0x14a Bdisp_AreaClr_DD +0x14b Bdisp_AreaClr_VRAM +0x14c Bdisp_AreaClr_DDVRAM +0x14d Bdisp_AreaReverseVRAM +0x150 PrintXY + void PrintXY(int x, int y, unsigned char *msg, int type) +0x153 Disp_Save +0x154 Disp_Restore +0x155 Disp_GetPtr +0x156 PopUpWin +0x158 Disp_Manage +0x159 System_UpdateOS +0x15d PrintCR +0x15f atoi +0x160 LongToAsc + int LongToAsc(int value, unsigned char *dest, int digits) +0x161 LongToAscHex + int LongToAscHex(int value, unsigned char *dest, int digits) +0x162 pc_toupper + void pc_toupper(char *str) +0x163 pc_tolower + void pc_tolower(char *str) +0x172 strcmp +0x173 strcmp +0x175 some_datatable +0x176 DiagnosticMode +0x18a InvertMem + void InvertMem(void *memoryrange, int count) +0x19f SMEM_Optimization + void SMEM_Optimization(void) +0x1a9 GUI_ProgressBar +0x1b7 Get8x8BitmapPointer_1 +0x1b8 Get8x8BitmapPointer_2 +0x1b9 Get8x8BitmapPointer_3 +0x1ba Get8x8BitmapPointer_4 +0x1bb Get8x8BitmapPointer_5 +0x1bc Get8x8BitmapPointer_6 +0x1bd Get8x8BitmapPointer_7 + +0x20e StorageMemory_GetFilePos + int StorageMemory_GetFilePos(int handle, int *pos) +0x236 RebootOS +0x23d RTC_TriggerAlarm + void RTC_TriggerAlarm(void) +0x23e RTC_SetDateTime + void RTC_SetDateTime(unsigned char *[7] time) +0x241 Keyboard_ClrBuffer +0x242 Bkey_Set_RepeatTime +0x243 Bkey_Get_RepeatTime +0x244 Bkey_Set_RepeatTime_Default +0x245 Keyboard_EnableAutoRepeat +0x246 Keyboard_DisableAutoRepeat +0x247 Keyboard_GetKeyWait +0x248 Keyboard_PutKeycode +0x249 Keyboard_GetKeyDownTime + short Keyboard_GetKeyDownTime(void) +0x24a Keyboard_IsAnyKeyDown + int Keyboard_IsAnyKeyDown(short *matrixcode) +0x24b Keyboard_IsSpecialKeyDown + int Keyboard_IsSpecialKeyDown(short *matrixcode) +0x24c Keyboard_IsSpecialKeyDown + int Keyboard_IsSpecialKeyDown(short *matrixcode) +0x24d Keyboard_KeyDown + int Keyboard_KeyDown(void) +0x24e Keyboard_SecondaryInterruptHandler +0x24f Keyboard_PutKeymatrixCode + int Keyboard_PutKeymatrixCode(short *matrixcode) +0x251 Keyboard_TimerHandler +0x25e Keyboard_PrimaryInterruptHandler +0x268 GetFKeyIconPointer + void GetFKeyIconPointer(int fkeyno, unsigned char *bitmap) +0x284 BCD_GetNaN + int BCD_GetNaN(unsigned char *value, unsigned char *result) +0x285 Serial_Open_57600 + int Serial_Open_57600(void) +0x286 BCD_AnsToSerial +0x28d Comm_Open + int Comm_Open(unsigned short parameters) +0x28e Comm_Close +0x28f Comm_WaitForAnyBuffer + int Comm_WaitForAnyBuffer(int timeout_ms, int P2, int *P3) +0x290 Comm_ReadOneByte + int Comm_ReadOneByte(unsigned char *result) +0x291 Comm_TransmitOneByte +0x292 Comm_WaitForAndReadNBytes + int Comm_WaitForAndReadNBytes(char *buffer, int bytes) +0x293 Comm_TransmitNBytes +0x294 Comm_ClearReceiveBuffer +0x295 Comm_ClearTransmitBuffer +0x296 Comm_IsValidPacketAvailable + int Comm_IsValidPacketAvailable(unsigned char *result) +0x298 Comm_IsOpen +0x299 Comm_GetCurrentSelector + int Comm_GetCurrentSelector(void) +0x2a1 HexToByte + int HexToByte(short *hex, char *result) +0x2a2 HexToWord + int HexToWord(int *hex, short *result) +0x2a3 ByteToHex +0x2a4 WordToHex +0x2a5 Comm_Padding_5C +0x2a6 Comm_ReversePadding_5C +0x2a7 AscHexToNibble + void AscHexToNibble(char hex, char *result) +0x2a8 NibbleToAscHex +0x2a9 strlen +0x2aa slow_memcpy +0x2ab Serial_Open2 + int Serial_Open2(unsigned short parameters) +0x2af Comm_Spy0thByte + int Comm_Spy0thByte(unsigned char *result) +0x2db Comm_ProcessInPacket + int Comm_ProcessInPacket(TReceivePacketDesc *rpd) +0x2e1 Comm_PrepareAckPacket + int Comm_PrepareAckPacket(TReceivePacketDesc *rpd, unsigned char subtype, void *datapointer, unsigned short datasize) +0x2e2 Comm_PrepareErrorPacket + void Comm_PrepareErrorPacket(TReceivePacketDesc *rpd, unsigned char subtype) +0x2e3 Comm_PrepareTerminatePacket + void Comm_PrepareTerminatePacket(TReceivePacketDesc *rpd, unsigned char subtype) +0x2e4 Comm_PrepareRoleswapPacket + void Comm_PrepareRoleswapPacket(TReceivePacketDesc *rpd, unsigned char subtype) +0x2e5 Comm_PrepareCheckPacket + void Comm_PrepareCheckPacket(TReceivePacketDesc *rpd, unsigned char subtype) +0x2e6 Comm_PrepareCommandPacket + int Comm_PrepareCommandPacket(TReceivePacketDesc *rpd, unsigned char subtype, void *datapointer, unsigned short datasize) +0x2e7 Comm_PrepareDataPacket + int Comm_PrepareDataPacket(TReceivePacketDesc *rpd, unsigned char subtype, void *datapointer, unsigned short datasize) +0x2ee System_GetOSVersion + void System_GetOSVersion(unsigned char *version) + +0x35e memset_range +0x35f memset +0x363 MCS_CreateDirectory + int MCS_CreateDirectory(unsigned char *dir, char *dirno) +0x364 MCS_WriteItem + int MCS_WriteItem(unsigned char *dir, unsigned char *item, short itemType, int data_length, int buffer) +0x366 MCS_DeleteDirectory + int MCS_DeleteDirectory(unsigned char *dir) +0x367 MCS_DeleteItem + int MCS_DeleteItem(unsigned char *dir, unsigned char *item) +0x368 MCS_GetState + int MCS_GetState(int *maxspace, int *currentload, int *remainingspace) +0x369 MCS_GetSystemDirectoryInfo + int MCS_GetSystemDirectoryInfo(unsigned char *dir, unsigned int *pdir, char *dirno) +0x370 MCS_RenameItem + int MCS_RenameItem(unsigned char *srcdir, unsigned char *srcitem, unsigned char *tgtdir, int tgtitem) +0x371 MCS_OverwriteData + int MCS_OverwriteData(unsigned char *dir, unsigned char *item, int write_offset, int bytes_to_write, void *buffer) +0x372 MCS_GetItemData + int MCS_GetItemData(unsigned char *dir, unsigned char *item, int offset, int bytes_to_read, void *buffer) +0x373 MCS_RenameDirectory + int MCS_RenameDirectory(unsigned char *oldname, unsigned char *newname) +0x374 BMCSRenameVariable +0x375 MCS_SearchDirectory + int MCS_SearchDirectory(unsigned char *dir, TMainMemoryDirectoryEntry *dir, char *dirno) +0x376 MCS_SearchDirectoryItem + int MCS_SearchDirectoryItem(unsigned char *dir, unsigned char *item, char *flags_0, TDirectoryItem *item, int *data, int *data_length) +0x37c MCS_GetFirstDataPointerByDirno + int MCS_GetFirstDataPointerByDirno(char *dirno, void *pdata) +0x37d MCS_GetDirectoryEntryByNumber + int MCS_GetDirectoryEntryByNumber(char dirno, TMainMemoryDirectoryEntry *pdir) +0x37e MCS_SearchItem + int MCS_SearchItem(unsigned char *item, TMainMemoryDirectoryEntry *pdir, unsigned short *itemno) +0x37f MCS_str8cpy + int MCS_str8cpy(unsigned char *source, unsigned char *target, int mode) +0x380 MCS_GetDirectoryEntryAddress + void MCS_GetDirectoryEntryAddress(void *directory_entry_address) +0x381 MCS_GetCurrentBottomAddress + void MCS_GetCurrentBottomAddress(void *current_bottom_address) +0x383 MCS_GetCapa + int MCS_GetCapa(int *current_bottom) +0x392 MCS_GetMainMemoryStart + int MCS_GetMainMemoryStart(void) +0x3dc Setup_GetInfo + int Setup_GetInfo(unsigned char *, int, TSetupInfo *info), where the first arguments are used only in fx-CG 10/20 +0x3ea the start of a datatable *** +0x3ed Interrupt_SetOrClrStatusFlags + int Interrupt_SetOrClrStatusFlags(int flagmask, int set) +0x3ee Interrupt_QueryStatusFlags + int Interrupt_QueryStatusFlags(int flagmask, int clear) +0x3f4 PowerOff +0x3f5 ClearMainMemory +0x3f6 SH7337_TMU_Stop +0x3f7 SH7337_TMU_int_handler +0x3fa Hmem_SetMMU + void Hmem_SetMMU(int virtualPageNumber, int physicalPageNumber, int pageManagement) +0x3fb MMU_ConfigureAndFlush + void MMU_ConfigureAndFlush(void) +0x3fc TLB_SetAddressValue + void TLB_SetAddressValue(int entryAddress, int way, int value) +0x3fe GetStackPtr +0x3ff MMU_FlushCache + +0x400 MMU_ConfigureAndEnable + void MMU_ConfigureAndEnable(void) +0x404 GetPhysicalROMstart +0x405 GetPhysicalRAMstart +0x409 Serial_ResetAndDisable + void *Serial_ResetAndDisable(void) +0x40a Serial_GetInterruptHandler + void *Serial_GetInterruptHandler(void) +0x40b Serial_SetInterruptHandler + int Serial_SetInterruptHandler(int type, void *handler) +0x40c Serial_ReadOneByte + int Serial_ReadOneByte(unsigned char *result) +0x40d Serial_ReadNBytes + int Serial_ReadNBytes(unsigned char *result, int max_size, short *actually_transferred) +0x40e Serial_BufferedTransmitOneByte + int Serial_BufferedTransmitOneByte(unsigned char byte) +0x40f Serial_BufferedTransmitNBytes + int Serial_BufferedTransmitNBytes(unsigned char *bytes, int requested_count) +0x410 Serial_DirectTransmitOneByte + int Serial_DirectTransmitOneByte(unsigned char byte) +0x411 Serial_GetReceivedBytesAvailable + int Serial_GetReceivedBytesAvailable(void) +0x412 Serial_GetFreeTransmitSpace + int Serial_GetFreeTransmitSpace(void) +0x413 Serial_ClearReceiveBuffer + int Serial_ClearReceiveBuffer(void) +0x414 Serial_ClearTransmitBuffer + int Serial_ClearTransmitBuffer(void) +0x418 Serial_Open + int Serial_Open(unsigned char *mode) +0x419 Serial_Close(int mode) +0x41b Serial_CallReceiveIntErrorResetHandler + void *Serial_CallReceiveIntErrorResetHandler(void) +0x41c Serial_CallReceiveIntHandler + void *Serial_CallReceiveIntHandler(void) +0x41d Serial_CallTransmitIntErrorResetHandler + void *Serial_CallTransmitIntErrorResetHandler(void) +0x41e Serial_CallTransmitIntHandler + void *Serial_CallTransmitIntHandler(void) +0x420 OS_inner_Sleep +0x422 Serial_SpyNthByte + int Serial_SpyNthByte(int byteno_to_spy, unsigned char *result) +0x423 Serial_GetStatus + void Serial_GetStatus(unsigned int *serial_status) +0x425 Serial_IsOpen + int Serial_IsOpen(void) +0x429 Bfile_identify_device_OS + int Bfile_identify_device_OS(const FONTCHARACTER *filename) +0x42c Bfile_OpenFile_OS +0x42d Bfile_CloseFile_OS +0x42e Bfile_GetMediaFree_OS +0x42f Bfile_GetFileSize_OS +0x431 Bfile_SeekFile_OS +0x432 Bfile_ReadFile_OS +0x434 Bfile_CreateEntry_OS +0x435 Bfile_WriteFile_OS +0x438 Bfile_RenameEntry +0x439 Bfile_DeleteEntry +0x43b Bfile_FindFirst +0x43c Bfile_FindNext +0x43d Bfile_FindClose +0x44e memcpy +0x44f memcmp +0x450 Bfile_GetFilenameLength + int Bfile_GetFilenameLength(const FONTCHARACTER *filename) +0x451 Bfile_Name_cmp +0x452 Bfile_Name_cpy +0x453 Bfile_Name_ncpy +0x456 Bfile_NameToStr_ncpy + void Bfile_NameToStr_ncpy(char *dest, FONTCHARACTER *src, int n) +0x457 Bfile_StrToName_ncpy + void Bfile_StrToName_ncpy(FONTCHARACTER *dest, char *src, int n) +0x462 GetAppName + char *GetAppName(char *dest) +0x463 SetAppName + int SetAppName(char *src) +0x464 CmpAppName + int CmpAppName(char *name) +0x465 GetIntPtrContent + int GetIntPtrContent(unsigned char *ptr) +0x467 LongToAscHex + int LongToAscHex(int value, char *dest, int digits) +0x468 hasSDOption +0x469 Battery_DisplayLowStatus + int Battery_DisplayLowStatus(int delayflag) +0x46b App_BuiltInCount + int App_BuiltInCount(void) +0x476 Battery_IsLow + int Battery_IsLow(void) +0x477 EnableGetkeyToMainFunctionReturn + void EnableGetkeyToMainFunctionReturn(void) +0x478 DisableGetkeyToMainFunctionReturn + void DisableGetkeyToMainFunctionReturn(void) +0x47f SetAutoPowerOffTime +0x480 GetAutoPowerOffTime +0x486 GetdatatablePtr +0x48d SetAutoPowerOffFlag +0x48e GetAutoPowerOffFlag +0x492 Battery_IsLow + int Battery_IsLow(int delayflag) +0x494 CallbackAtQuitMainFunction +0x495 Battery_DisplayLowStatus + int Battery_DisplayLowStatus(int delayflag) +0x499 Heap_SetTopChunk + int Heap_SetTopChunk(int size) +0x49a App_Start + int App_Start(int r4, int r5, int index, int force) +0x49c Battery_GetStatus + int Battery_GetStatus(int delayflag) +0x49e RebootOS +0x4a0 AUX_DisplayErrorMessage + void AUX_DisplayErrorMessage(void) +0x4ad USB_InterruptHandler +0x4ae USB_TimerHandler + void USB_TimerHandler(void) +0x4b0 AUX_DisplayFKeyIcons + void AUX_DisplayFKeyIcons(void) +0x4cb Keyboard_RemapFKeyCode + void Keyboard_RemapFKeyCode(int mode, TFKeyDef *source) +0x4d1 AUX_DisplayFKeyIcon + void AUX_DisplayFKeyIcon(int pos, unsigned char *bitmap) +0x4dc char Setup_GetEntry(unsigned int index) +0x4dd Setup_SetEntry + char *Setup_SetEntry(unsigned int index, char setting) +0x4de Setup_GetEntryPtr + char *Setup_GetEntryPtr(unsigned int index) +0x4df Alpha_GetData + char *Alpha_GetData(char var, char *dest) +0x4e0 Alpha_SetData + char *Alpha_SetData(char var, char *src) +0x4e1 Alpha_ClearAll + void Alpha_ClearAll(void) +0x4e6 HourGlass + void HourGlass(void) +0x4e9 LocalizeStringID +0x4f5 BCD_ToStrAsNumber1 + int BCD_ToStrAsNumber1(char *data, char *string) +0x4f6 BCD_ToStrAsNumber2 + int BCD_ToStrAsNumber2(char *data, char *string) + +0x500 BCDToInternal + int BCDToInternal(TBCDinternal *target, TBCDvalue *source) +0x518 Setup_GetEntry_3E +0x519 Setup_GetEntry_40 +0x51a Setup_SetEntry_3E +0x51b Setup_SetEntry_40 +0x531 MB_IsLead + int MB_IsLead(char c) +0x533 MB_ElementCount + int MB_ElementCount(char *str) +0x534 MB_ByteCount + int MB_ByteCount(char *str) +0x536 MB_strcat + char *MB_strcat(char *dest, char *src) +0x537 MB_strncat + char *MB_strncat(char *dest, char *src, int bytes) +0x538 MB_strcpy + char *MB_strcpy(char *dest, char *src) +0x53c MB_GetSecondElemPtr + char *MB_GetSecondElemPtr(char *str) +0x53d MB_GetElement + short MB_GetElement(char *str) +0x53e MB_CopyToHeap + char *MB_CopyToHeap(char *src) +0x53f memcmp +0x541 itoa +0x542 to_uppercase +0x543 to_lowercase +0x544 BCD_0 +0x545 BCD_1 +0x546 BCD_2 +0x547 BCD_10 +0x548 BCD_1/3 +0x549 BCD_0.5 +0x54a BCD_32767 +0x54b BCD_-32768 +0x54c BCD_65536 +0x54d BCD_0x7fffffff +0x54e BCD_-2Gi +0x54f BCD_4Gi +0x550 BCD_pi +0x551 BCD_2pi +0x552 BCD_pi/2 +0x553 BCD_e +0x554 BCD_5 +0x5a6 BCD_SetAsInt + int BCD_SetAsInt(int input, TBCDValue *result) +0x5af BCD_pi/4 +0x5b0 BCD_ln(10) +0x5b1 BCD_ln(2) +0x5b2 BCD_9.99e+99 +0x5b3 BCD_-9.99e+99 +0x5b4 BCD_9.99999999999999e+99 +0x5b5 BCD_227.85 +0x5b6 BCD_sqrt(2) +0x5b7 BCD_sqrt(2)/2 +0x5b8 BCD_506.6282746310 + +0x645 CalculateExpression + void CalculateExpression(char **formula, char opcode[2], TBCDValue *result, int p4) +0x64a CalculateExpression0 +0x652 PRGM_NextOpcode + void PRGM_NextOpcode(char opcode[2], char **program) +0x6a6 PRGM_IsEndOfLine + int PRGM_IsEndOfLine(char *opcode) +0x6c4 Keyboard_PRGM_GetKey + int Keyboard_PRGM_GetKey(TBCDValue *result) +0x6d4 Alpha_GetData2 + int Alpha_GetData2(char var, char *dest) + +0x713 Print_ClearLine +0x763 Bdisp_DrawRectangle + void Bdisp_DrawRectangle(int x1, int y1, int x2, int y2) +0x7fc OpcodeToStr + int OpcodeToStr(unsigned short opcode, unsigned char *string) + +0x804 CLIP_Store + int CLIP_Store(unsigned char *buffer, int length) +0x807 locate +0x808 Print +0x809 PrintRev +0x80a PrintC +0x80b PrintRevC +0x80c PrintLine +0x80d PrintRLine +0x80e Cursor_GetFlashStyle + int Cursor_GetFlashStyle(void) +0x80f Cursor_GetSettings + int Cursor_GetSettings(struct CursorSettings *settings) +0x811 Cursor_SetFlashOn + void Cursor_SetFlashOn(char flash_style) +0x812 Cursor_SetFlashOff + void Cursor_SetFlashOff(void) +0x813 SaveDisp +0x814 RestoreDisp +0x829 MCS_CreateDirectory + unsigned char *MCS_CreateDirectory(unsigned char *dir) +0x82a MCS_PutInternalItem + int MCS_PutInternalItem(char dirtype, unsigned char *item, int data_len, void *buffer) +0x82b MCSPutVar2 + int MCSPutVar2(unsigned char *dir, unsigned char *item, int data_len, void *buffer) +0x830 MCSOvwDat2 + int MCSOvwDat2(unsigned char *dir, unsigned char *item, int bytes_to_write, void *buffer, int write_offset) +0x832 MCS_OverwriteOpenItem + int MCS_OverwriteOpenItem(int bytes_to_write, void *buffer, int write_offset) +0x833 MCS_ClearInternalDirectory + int MCS_ClearInternalDirectory(char dirtype) +0x834 MCS_ClearDirectory + int MCS_ClearDirectory(unsigned char *dir) +0x835 MCS_DeleteInternalItem + int MCS_DeleteInternalItem(char dirtype, unsigned char *dir) +0x836 MCSDelVar2 + int MCSDelVar2(unsigned char *dir, unsigned char *item) +0x83a MCS_GotoInternalItem + int MCS_GotoInternalItem(char dirtype, unsigned char *item, int direction) +0x83b MCS_OpenMainMemoryItem + int MCS_OpenMainMemoryItem(unsigned char *dir, unsigned char *item, char direction) +0x83c MCS_GotoHandleNeighbour + int MCS_GotoHandleNeighbour(char direction) +0x83d MCS_CheckOpenedItem + int MCS_CheckOpenedItem(char *dirtype, int *data_len) +0x83e MCS_GetOpenItem + int MCS_GetOpenItem(unsigned char *item) +0x83f MCS_OpenInternalDirectoryItem + int MCS_OpenInternalDirectoryItem(char dirtype, unsigned char *item, int *data_len) +0x840 MCSGetDlen2 + int MCSGetDlen2(unsigned char *dir, unsigned char *item, int *data_len) +0x841 MCSGetData1 + int MCSGetData1(int offset, int len_to_copy, void *buffer) +0x843 MCS_MapMCS_Result + int MCS_MapMCS_Result(void) +0x844 MCSGetCapa +0x84d MCS_OpenAlphaMemItem + int MCS_OpenAlphaMemItem(char variablename, int *data_len, void *data_ptr) +0x852 MCS_DirtypeToItemtype + int MCS_DirtypeToItemtype(char dirtype) +0x853 MCS_ItemtypeToDirtype + int MCS_ItemtypeToDirtype(char itemtype) +0x863 MCS_DirtypeToName + unsigned char *MCS_DirtypeToName(char dirtype) +0x866 MCS_MapError + int MCS_MapError(int error_in) +0x869 Alpha_ClearAllAndAns + void Alpha_ClearAllAndAns(void) +0x86f MCS_DeleteDirectoryItems + void MCS_DeleteDirectoryItems(unsigned char *dir) +0x8db EditExpression + int EditExpression(int mode, short key, int row, TBCDValue *value, char *editbuffer, short editbufferlen, char *pretext, int mode2) +0x8dc EditValue + int EditValue(int mode, short key, int row, TBCDValue *value, char *pretext) +0x8e6 EditMBStringCtrl + void EditMBStringCtrl(unsigned char *, int xposmax, void *, void *, void *, int, int) +0x8ea DisplayMBString + void DisplayMBString(unsigned char *, int, int xpos, int x, int y) +0x8ec EditMBStringChar + void EditMBStringChar(unsigned char *, int xposmax, int xpos, int char) +0x8f7 DisplayMBString2 + void DisplayMBString2(int, unsigned char *, int, int xpos, int zero, int x, int y, int, int, int) +0x8fe PopupWin + void PopupWin(int nlines) + +0x901 DisplayMessageBox + void DisplayMessageBox(int height, unsigned char *message) +0x905 DisplayErrorMessage + void DisplayErrorMessage(int id) +0x90b SetShiftAlphaState +0x90c GetInsOverwriteState +0x90d SetInsOverwriteState +0x90e ClrShiftAlphaState +0x90f GetKey + int GetKey(unsigned int *keycode) +0x910 PutKey + int PutKey(int keycode, int mode) +0x91b GetShiftAlphaState +0x924 TestMode + void TestMode(void) +0x954 DisplayErrorMessage +0x985 App_CONICS +0x998 App_DYNA +0x9ad PrintXY +0x9df App_EACT +0x9e1 App_Equation +0x9e2 App_EQUA +0x9f5 App_Program + +0xa00 App_FINANCE +0xa1f Keyboard_RemapFKeyCode + void Keyboard_RemapFKeyCode(TKeyDef *workspace, TKeyDef *source, int mode) +0xa35 AUX_DisplayMessage +0xa48 App_GRAPH_TABLE +0xa4a App_LINK +0xa6a App_Optimization +0xa6b App_Memory +0xa75 App_RECUR +0xa97 App_RUN_MAT_EXE +0xaae App_RUN_MAT +0xac6 App_STAT +0xac8 App_SYSTEM +0xacc free +0xacd malloc +0xace memcmp +0xacf smart_memcpy +0xad0 memset +0xad4 strcat +0xad5 smart_strcmp +0xad6 strlen +0xad7 strncat +0xad8 strncmp +0xad9 strncpy +0xada strrchr +0xae8 CatalogDialog + +0xc4f PrintMiniSd +0xca7 OpcodeType + int OpcodeType(char *); +0xcb0 Basic_Send_Send38k +0xcb1 Basic_Receive_Receive38k +0xcb2 Basic_OpenComPort38k_CloseComPort38k +0xcc4 InputNumber + int InputNumber(unsigned char *heading, int maxlen, int mode) +0xcc5 InputString + int InputString(unsigned char *buffer, unsigned char *heading, int maxlen) +0xccb GetRAMSize + int GetRAMSize(void) +0xcd0 another_diagnostic_dialog + +0xd64 InputDateDialog + int InputDateDialog(int *day, int *month, int *year, int *p4) +0xd65 InputMonthDialog + int InputMonthDialog(int *month, int *p2) +0xd66 InputDayDialog + int InputDayDialog(int *day, int *p2) +0xd67 InputYearDialog + int InputYearDialog(int *year, int *p2) +0xdab StoreExpressionToGraphFuncMemory + void StoreExpressionToGraphFuncMemory(int fun_no, int, char *expression) + +0xe6b calloc +0xe6c memmove +0xe6d realloc +0xe6e strchr +0xe6f strstr diff --git a/fxos/sys.c b/fxos/sys.c new file mode 100644 index 0000000..e08d372 --- /dev/null +++ b/fxos/sys.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include + +/* sys_free_item(): Free a syscall description item */ +static void sys_free_item(void *item) +{ + struct sys_call *call = item; + free(call->name); + free(call->descr); +} + +/* sys_load(): Load a syscall table */ +void sys_load(char const *file) +{ + err_context("syscall", file, NULL); + if(!table_available()) + { + err("too many tables, skipping"); + err_pop(); + return; + } + + /* Map the file to memory */ + int fd; + size_t size; + void *data = map(file, &fd, &size); + if(!data) { err_pop(); return; } + + /* If the file is named "sys-x.txt", use "x" as the table name */ + char *name = match_table_name(file, "sys", ".txt"); + + /* Parse the contents */ + int count; + struct sys_call *calls = lex_sys(data, size, &count); + + table_create("sys", name, sys_free_item, count, sizeof *calls, calls); + + unmap(data, fd, size); + err_pop(); +} + +/* Global storage for the sys_match() function */ +static uint32_t sys_match_number; + +/* sys_match(): Search routine for sys_find() */ +static int sys_match(void *item) +{ + struct sys_call *call = item; + return call->number == sys_match_number; +} + +/* sys_find(): Find information on a given syscall number */ +struct sys_call const *sys_find(uint32_t number) +{ + sys_match_number = number; + return table_find("sys", sys_match, NULL, 0); +} diff --git a/fxos/tables.c b/fxos/tables.c index 032b05c..97a07d6 100644 --- a/fxos/tables.c +++ b/fxos/tables.c @@ -1,59 +1,82 @@ -#include #include +#include +#include -/* A linked list of pointers (either file names or tables) */ -struct element +/* Up to 128 tables can be defined */ +#define MAX 128 +static struct table db[MAX] = { 0 }; +/* Number of tables currently used */ +static int db_size = 0; + +/* table_free(): Free a table and its contents + @t Table pointer */ +static void table_free(struct table *t) { - void *data; - struct element *next; -}; + if(!t) return; + free(t->name); -/* list_append(): Append a pointer to a linked list - Returns a pointer to the new node, NULL on error. */ -struct element *list_append(struct element **head, void *data) -{ - struct element *el = malloc(sizeof *el); - if(!el) return NULL; - el->data = data; - el->next = NULL; - - while(*head) head = &((*head)->next); - *head = el; - - return el; -} - -/* list_free(): Free a linked list */ -void list_free(struct element *head, void (*destructor)(void *)) -{ - struct element *next; - while(head) + if(t->items) for(int i = 0; i < t->count; i++) { - destructor(head->data); - next = head->next; - free(head); - head = next; + t->free_item(t->items + i * t->size); } + free(t->items); } -/* Table of assembly instruction listings */ -static struct element *tasm = NULL; -/* Table of system call information listings */ -static struct element *tcall = NULL; - - -/* -** Public API -*/ - -/* tables_add_asm(): Append an instruction table to the table list */ -int tables_add_asm(const char *filename) +/* table_quit(): Destroy all allocated tables and release memory */ +__attribute__((destructor)) +static void table_quit(void) { - return !list_append(&tasm, (void *)filename); + for(int i = 0; i < db_size; i++) table_free(&db[i]); } -/* tables_add_syscall(): Append a syscall table to the table list */ -int tables_add_syscall(const char *filename) +/* table_available(): Whether a table can be allocated */ +int table_available(void) { - return !list_append(&tcall, (void *)filename); + return db_size < MAX; +} + +/* table_create(): Create a new table */ +void table_create(char const *type, char *name, void (*free_item)(void *), + int count, size_t size, void *items) +{ + if(!table_available()) return; + struct table *t = &db[db_size++]; + + t->type = type; + t->name = name; + t->free_item = free_item; + t->count = count; + t->size = size; + t->items = items; +} + +/* table_find(): Find matching entries in the database tables */ +void *table_find(char const *type, int (*match)(void *), char const **name, + int next) +{ + static int table = 0; + static int index = 0; + if(!next) table = index = 0; + + while(table < db_size) + { + struct table const *t = &db[table]; + + if(!strcmp(t->type, type)) + while(index < t->count) + { + void *entry = t->items + index * t->size; + index++; + if(match(entry)) + { + if(name) *name = t->name; + return entry; + } + } + + table++; + index = 0; + } + + return NULL; } diff --git a/fxos/util.c b/fxos/util.c index 2c380c9..73328c6 100644 --- a/fxos/util.c +++ b/fxos/util.c @@ -1,10 +1,18 @@ -#include #include #include +#include #include +#include +#include +#include +#include + +#include +#include + /* integer(): Convert base 8, 10 or 16 into integers */ -long long integer(const char *str, int *error) +long long integer(char const *str, int *error) { char *end; errno = 0; @@ -28,3 +36,63 @@ long long integer(const char *str, int *error) return ll; } + +/* match_table_name(): Some specific matching on filenames */ +char *match_table_name(char const *path, char const *type, char const *suffix) +{ + char const *base = strrchr(path, '/'); + base = (base) ? (base + 1) : (path); + + size_t len = strlen(base); + size_t len_t = strlen(type); + size_t len_s = strlen(suffix); + + if(len > len_t + len_s + 1 + && !strncmp(type, base, len_t) + && base[len_t] == '-' + && !strcmp(suffix, base + len - len_s)) + { + return strndup(base + len_t + 1, len - len_t - len_s - 1); + } + + return strdup(base); +} + +/* map(): Map a file to memory */ +void *map(char const *path, int *fd, size_t *size) +{ + int x; + struct stat statbuf; + + *fd = open(path, O_RDONLY); + if(*fd < 0) + { + errf(ERR_ERRNO, "cannot open file"); + return NULL; + } + + x = fstat(*fd, &statbuf); + if(x < 0) + { + errf(ERR_ERRNO, "cannot stat file"); + close(*fd); + return NULL; + } + + *size = statbuf.st_size; + + void *data = mmap(NULL, *size, PROT_READ, MAP_PRIVATE, *fd, 0); + if(data == MAP_FAILED) + { + errf(ERR_ERRNO, "cannot map file"); + close(*fd); + } + return data; +} + +/* unmap(): Unmap a file loaded with map() */ +void unmap(void *data, int fd, size_t size) +{ + munmap(data, size); + close(fd); +} diff --git a/fxos/util.h b/fxos/util.h new file mode 100644 index 0000000..6df3ae3 --- /dev/null +++ b/fxos/util.h @@ -0,0 +1,44 @@ +//--- +// util: Utility functions +//--- + +#ifndef FXOS_UTIL +#define FXOS_UTIL + +/* integer(): Convert base 8, 10 or 16 into integers + Prints an error message and sets *error to 1 in case of conversion error or + overflow. + + @str Original integer representation ("10", "0x1f", "07") + @error Set to 1 on error, otherwise unchanged (can be NULL) + Returns result of conversion (valid if *error is not 1) */ +long long integer(const char *str, int *error); + +/* match_table_name(): Some specific matching on filenames + Returns the table name to associate with the file located at @path for a + table of type @type, with file suffix @suffix. + + Essentially if the basename of the file at @path is on the form + {@type}-{x}{@suffix} + then an malloc'ed copy of x is returned. Otherwise an malloc'ed copy of the + basename is returned. */ +char *match_table_name(char const *path, char const *type, char const *suffix); + +/* map(): Map a file to memory + Maps a file given by its path to memory, and return the associated region, + a file descriptor and the size. + + @path File path + @fd Will be set to fd of open file + @size Will be set to file size + Returns NULL no error, in which case the file is closed and [*fd] and + [*size] are undefined; otherwise, returns a pointer to mapped data. */ +void *map(char const *path, int *fd, size_t *size); + +/* unmap(): Unmap a file loaded with map() + @data Region returned by map() + @fd File descriptor set by map() + @size Size set by map() */ +void unmap(void *data, int fd, size_t size); + +#endif /* FXOS_UTIL */