From b71145f79d6c00f5a852e305a8c66ba34c33a1b7 Mon Sep 17 00:00:00 2001 From: David Given Date: Thu, 14 Jun 2018 03:07:36 -0700 Subject: [PATCH] Implement a non-functional skeleton of the PowerPC emulator (with, hopefully, most of the instruction decode done). --- plat/linuxppc/emu/build.lua | 28 +++++ plat/linuxppc/emu/emu.c | 168 ++++++++++++++++++++++++++++ plat/linuxppc/emu/emu.h | 32 ++++++ plat/linuxppc/emu/instructions.dat | 149 +++++++++++++++++++++++++ plat/linuxppc/emu/main.c | 173 +++++++++++++++++++++++++++++ plat/linuxppc/emu/mkdispatcher.lua | 77 +++++++++++++ plat/linuxppc/tests/build.lua | 2 +- 7 files changed, 628 insertions(+), 1 deletion(-) create mode 100644 plat/linuxppc/emu/build.lua create mode 100644 plat/linuxppc/emu/emu.c create mode 100644 plat/linuxppc/emu/emu.h create mode 100644 plat/linuxppc/emu/instructions.dat create mode 100755 plat/linuxppc/emu/main.c create mode 100644 plat/linuxppc/emu/mkdispatcher.lua diff --git a/plat/linuxppc/emu/build.lua b/plat/linuxppc/emu/build.lua new file mode 100644 index 000000000..de5208513 --- /dev/null +++ b/plat/linuxppc/emu/build.lua @@ -0,0 +1,28 @@ +normalrule { + name = "dispatcher", + ins = { + "./mkdispatcher.lua", + "./instructions.dat" + }, + outleaves = { + "dispatcher.h" + }, + commands = { + "$(LUA) %{ins[1]} < %{ins[2]} > %{outs}" + } +} + +clibrary { + name = "dispatcher_lib", + srcs = {}, + hdrs = { "+dispatcher" } +} + +cprogram { + name = "emuppc", + srcs = { "./*.c" }, + deps = { + "+dispatcher_lib" + } +} + diff --git a/plat/linuxppc/emu/emu.c b/plat/linuxppc/emu/emu.c new file mode 100644 index 000000000..b2f5f2844 --- /dev/null +++ b/plat/linuxppc/emu/emu.c @@ -0,0 +1,168 @@ +#include +#include +#include +#include +#include +#include "emu.h" + +cpu_t cpu; + +static inline bool carry(void) +{ + fatal("carry() not supported yet"); +} + +#define swb16(x) bswap_16(x) +#define swb32(x) bswap_32(x) + +static inline uint32_t reg(uint8_t n) +{ + return cpu.gpr[n]; +} + +static inline uint32_t reg0(uint8_t n) +{ + if (n == 0) + return 0; + return cpu.gpr[n]; +} + +static inline uint32_t ext8(int8_t n) +{ + return (n << 24) >> 24; +} + +static inline uint32_t ext16(int16_t n) +{ + return (n << 16) >> 16; +} + +static bool getcr(uint8_t bit) +{ + bit = 31 - bit; /* note PowerPC bit numbering */ + return cpu.cr & (1<: a field occupying so many bits. +# T: a field occupying one bit. +# .: a bit we don't care about. + +# Branch processor instructions. + +<18-->AL branch(0x1f, 0x00, LI, A, L); +<16-->AL branch(BO, BI, BD, A, L); +<19--><16------>L branch(BO, BI, cpu.lr, 1, L); +<19--><528----->L branch(BO, BI, cpu.ctr, 1, L); +<17-->.................1. system_call(LEV); + +# Condition register instructions. + +<19--><257----->. setcr(BT, getcr(BA) & getcr(BB)); +<19--><449----->. setcr(BT, getcr(BA) | getcr(BB)); +<19--><193----->. setcr(BT, getcr(BA) ^ getcr(BB)); +<19--><225----->. setcr(BT, !(getcr(BA) & getcr(BB))); +<19--><33------>. setcr(BT, !(getcr(BA) | getcr(BB))); +<19--><289----->. setcr(BT, getcr(BA) == getcr(BB)); +<19--><129----->. setcr(BT, getcr(BA) & !getcr(BB)); +<19--><417----->. setcr(BT, getcr(BA) | !getcr(BB)); +<19-->.......<0------->. mcrf(BF, BA); + +# Fixed point loads + +<34--> cpu.gpr[RT] = read_byte(reg0(RA) + ext16(D)); +<31--><87------>. cpu.gpr[RT] = read_byte(reg0(RA) + reg(RB)); +<35--> uint32_t ea = reg(RA) + ext16(D); cpu.gpr[RT] = read_byte(ea); cpu.gpr[RA] = ea; +<31--><119----->. uint32_t ea = reg(RA) + reg(RB); cpu.gpr[RT] = read_byte(ea); cpu.gpr[RA] = ea; +<40--> cpu.gpr[RT] = read_word(reg0(RA) + ext16(D)); +<31--><279----->. cpu.gpr[RT] = read_word(reg0(RA) + reg(RB)); +<41--> uint32_t ea = reg(RA) + ext16(D); cpu.gpr[RT] = read_word(ea); cpu.gpr[RA] = ea; +<31--><311----->. uint32_t ea = reg(RA) + reg(RB); cpu.gpr[RT] = read_word(ea); cpu.gpr[RA] = ea; +<42--> cpu.gpr[RT] = ext16(read_word(reg0(RA) + ext16(D))); +<31--><343----->. cpu.gpr[RT] = ext16(read_word(reg0(RA) + reg(RB))); +<43--> uint32_t ea = reg(RA) + ext16(D); cpu.gpr[RT] = ext16(read_word(ea)); cpu.gpr[RA] = ea; +<31--><375----->. uint32_t ea = reg(RA) + reg(RB); cpu.gpr[RT] = ext16(read_word(ea)); cpu.gpr[RA] = ea; +<32--> cpu.gpr[RT] = read_long(reg0(RA) + ext16(D)); +<31--><23------>. cpu.gpr[RT] = read_long(reg0(RA) + reg(RB)); +<33--> uint32_t ea = reg(RA) + ext16(D); cpu.gpr[RT] = read_long(ea); cpu.gpr[RA] = ea; +<31--><55------>. uint32_t ea = reg(RA) + reg(RB); cpu.gpr[RT] = read_long(ea); cpu.gpr[RA] = ea; +<58-->10 cpu.gpr[RT] = read_long(reg0(RA) + ext16(DS<<2)); +<31--><341----->. cpu.gpr[RT] = read_long(reg0(RA) + reg(RB)); +<31--><373----->. uint32_t ea = reg(RA) + reg(RB); cpu.gpr[RT] = read_long(ea); cpu.gpr[RA] = ea; + +# Fixed point stores + +<38--> write_byte(reg0(RA) + ext16(D), RS); +<31--><215----->. write_byte(reg0(RA) + reg(RB), RS); +<39--> uint32_t ea = reg(RA) + ext16(D); write_byte(ea, reg(RS)); cpu.gpr[RA] = ea; +<31--><247----->. uint32_t ea = reg(RA) + reg(RB); write_byte(ea, reg(RS)); cpu.gpr[RA] = ea; +<44--> write_word(reg0(RA) + ext16(D), RS); +<31--><407----->. write_word(reg0(RA) + reg(RB), RS); +<45--> uint32_t ea = reg(RA) + ext16(D); write_word(ea, reg(RS)); cpu.gpr[RA] = ea; +<31--><439----->. uint32_t ea = reg(RA) + reg(RB); write_word(ea, reg(RS)); cpu.gpr[RA] = ea; +<36--> write_long(reg0(RA) + ext16(D), RS); +<31--><151----->. write_long(reg0(RA) + reg(RB), RS); +<37--> uint32_t ea = reg(RA) + ext16(D); write_long(ea, reg(RS)); cpu.gpr[RA] = ea; +<31--><183----->. uint32_t ea = reg(RA) + reg(RB); write_long(ea, reg(RS)); cpu.gpr[RA] = ea; + +# Fixed point load/stores with byte reversal + +<31--><790----->. cpu.gpr[RT] = swb16(read_word(reg0(RA) + reg(RB))); +<31--><534----->. cpu.gpr[RT] = swb32(read_long(reg0(RA) + reg(RB))); +<31--><918----->. write_word(reg0(RA) + reg(RB), swb16(reg(RS))); +<31--><662----->. write_long(reg0(RA) + reg(RB), swb32(reg(RS))); + +# Load/store multiple + +<46--> read_multiple(reg0(RA) + ext16(D), RT); +<47--> write_multiple(reg0(RA) + ext16(D), RS); +<31--><597----->. read_string(reg0(RA), RT, NB ? NB : 32); +<31--><533----->. read_string(reg0(RA) + reg(RB), RT, cpu.xer & 0x1f); +<31--><725----->. write_string(reg0(RA), RS, NB ? NB : 32); +<31--><661----->. write_string(reg0(RA) + reg(RB), RS, cpu.xer & 0x1f); + +# ALU instructions + +<14--> cpu.gpr[RT] = reg0(RA) + ext16(SI); +<15--> cpu.gpr[RT] = reg0(RA) + (SI<<16); +<31-->O<266---->R setcr0(R, cpu.gpr[RT] = addo(reg(RA), reg(RB), 0, O, 0)); +<31-->O<40----->R setcr0(R, cpu.gpr[RT] = addo(~reg(RA), reg(RB), 1, O, 0)); +<6-->R cpu.gpr[RT] = addo(reg(RA), ext16(SI), 0, 0, 1); +<8---> cpu.gpr[RT] = addo(~reg(RA), ext16(SI), 1, 0, 1); +<31-->O<10----->R setcr0(R, cpu.gpr[RT] = addo(reg(RA), reg(RB), 0, O, 1)); +<31-->O<8------>R setcr0(R, cpu.gpr[RT] = addo(~reg(RA), reg(RB), 1, O, 1)); +<31-->O<138---->R setcr0(R, cpu.gpr[RT] = addo(reg(RA), reg(RB), carry(), O, 1)); +<31-->O<136---->R setcr0(R, cpu.gpr[RT] = addo(~reg(RA), reg(RB), carry(), O, 1)); +<31-->.....O<234---->R setcr0(R, cpu.gpr[RT] = addo(reg(RA), -1, carry(), O, 1)); +<31-->.....O<232---->R setcr0(R, cpu.gpr[RT] = addo(~reg(RA), -1, carry(), O, 1)); +<31-->.....O<202---->R setcr0(R, cpu.gpr[RT] = addo(reg(RA), 0, carry(), O, 1)); +<31-->.....O<200---->R setcr0(R, cpu.gpr[RT] = addo(~reg(RA), 0, carry(), O, 1)); +<31-->.....O<104---->R setcr0(R, cpu.gpr[RT] = addo(~reg(RA), 0, 1, O, 0)); +<7---> cpu.gpr[RT] = reg(RA) * ext16(SI); +<31-->O<235---->R setcr0(R, cpu.gpr[RT] = mulo(reg(RA), reg(RB), O)); +<31-->O<491---->R setcr0(R, cpu.gpr[RT] = divo(reg(RA), reg(RB), O)); +<31-->O<459---->R setcr0(R, cpu.gpr[RT] = divuo(reg(RA), reg(RB), O)); + +# Comparison instructions + +<11-->.0 compares(reg(RA), ext16(SI), F); +<31-->.0<0------->. compares(reg(RA), reg(RB), F); +<10-->.0 compareu(reg(RA), UI, F); +<31-->.0<32------>. compareu(reg(RA), reg(RB), F); + +# Logical instructions + +<28--> cpu.gpr[RA] = reg(RS) & UI; +<29--> cpu.gpr[RA] = reg(RS) & (UI<<16); +<24--> cpu.gpr[RA] = reg(RS) | UI; +<25--> cpu.gpr[RA] = reg(RS) | (UI<<16); +<26--> cpu.gpr[RA] = reg(RS) ^ UI; +<27--> cpu.gpr[RA] = reg(RS) ^ (UI<<16); +<31--><28------>R setcr0(R, cpu.gpr[RA] = reg(RS) & reg(RB)); +<31--><444----->R setcr0(R, cpu.gpr[RA] = reg(RS) | reg(RB)); +<31--><316----->R setcr0(R, cpu.gpr[RA] = reg(RS) ^ reg(RB)); +<31--><476----->R setcr0(R, cpu.gpr[RA] = ~(reg(RS) & reg(RB))); +<31--><124----->R setcr0(R, cpu.gpr[RA] = ~(reg(RS) | reg(RB))); +<31--><284----->R setcr0(R, cpu.gpr[RA] = ~(reg(RS) ^ reg(RB))); +<31--><60------>R setcr0(R, cpu.gpr[RA] = reg(RS) & ~reg(RB)); +<31--><412----->R setcr0(R, cpu.gpr[RA] = reg(RS) | ~reg(RB)); +<31-->.....<954----->R setcr0(R, cpu.gpr[RA] = ext8(reg(RS))); +<31-->.....<922----->R setcr0(R, cpu.gpr[RA] = ext16(reg(RS))); +<31-->.....<26------>R setcr0(R, cpu.gpr[RA] = cntlzw(reg(RS))); +<31-->.....<122----->R setcr0(R, cpu.gpr[RA] = popcntb(reg(RS))); + +# Rotation/shift instructions + +<21-->R setcr0(R, cpu.gpr[RA] = rlwnm(reg(RS), SH, MB, ME)); +<23-->R setcr0(R, cpu.gpr[RA] = rlwnm(reg(RS), reg(RB), MB, ME)); +<20-->R setcr0(R, cpu.gpr[RA] = rlwmi(reg(RS), SH, MB, ME)); +<31--><24------>R setcr0(R, cpu.gpr[RA] = reg(RS) << (reg(RB) & 0x1f)); +<31--><536----->R setcr0(R, cpu.gpr[RA] = reg(RS) >> (reg(RB) & 0x1f)); +<31--><824----->R setcr0(R, cpu.gpr[RA] = ((int32_t)reg(RS)) >> SH); +<31--><792----->R setcr0(R, cpu.gpr[RA] = ((int32_t)reg(RS)) >> (reg(RB) & 0x1f)); + +# Move to/from special registers + +<31--><1-->00000<467----->. cpu.xer = reg(RS); +<31--><8-->00000<467----->. cpu.lr = reg(RS); +<31--><9-->00000<467----->. cpu.ctr = reg(RS); +<31--><1-->00000<339----->. cpu.gpr[RT] = cpu.xer; +<31--><8-->00000<339----->. cpu.gpr[RT] = cpu.lr; +<31--><9-->00000<339----->. cpu.gpr[RT] = cpu.ctr; +<31-->0.<144----->. mtcrf(FXM, reg(RS)); +<31-->0.........<19------>. cpu.gpr[RT] = cpu.cr; + diff --git a/plat/linuxppc/emu/main.c b/plat/linuxppc/emu/main.c new file mode 100755 index 000000000..3b2ef7676 --- /dev/null +++ b/plat/linuxppc/emu/main.c @@ -0,0 +1,173 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "emu.h" + +#define RAM_BASE 0x10000000 +#define RAM_TOP 0x10100000 + +#define BRK_TOP (RAM_TOP - 0x1000) + +#define INIT_SP RAM_TOP +#define INIT_PC 0x08000054 + +/* Read/write macros */ +#define READ_BYTE(BASE, ADDR) (BASE)[ADDR] +#define READ_WORD(BASE, ADDR) (((BASE)[ADDR]<<8) | \ + (BASE)[(ADDR)+1]) +#define READ_LONG(BASE, ADDR) (((BASE)[ADDR]<<24) | \ + ((BASE)[(ADDR)+1]<<16) | \ + ((BASE)[(ADDR)+2]<<8) | \ + (BASE)[(ADDR)+3]) + +#define WRITE_BYTE(BASE, ADDR, VAL) (BASE)[ADDR] = (VAL)&0xff +#define WRITE_WORD(BASE, ADDR, VAL) (BASE)[ADDR] = ((VAL)>>8) & 0xff; \ + (BASE)[(ADDR)+1] = (VAL)&0xff +#define WRITE_LONG(BASE, ADDR, VAL) (BASE)[ADDR] = ((VAL)>>24) & 0xff; \ + (BASE)[(ADDR)+1] = ((VAL)>>16)&0xff; \ + (BASE)[(ADDR)+2] = ((VAL)>>8)&0xff; \ + (BASE)[(ADDR)+3] = (VAL)&0xff + + +static void emulated_syscall(void); + +static unsigned char ram[RAM_TOP - RAM_BASE]; +uint32_t brkbase = RAM_BASE; +uint32_t brkpos = RAM_BASE; +uint32_t entrypoint = RAM_BASE; + +void fatal(char* fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fprintf(stderr, "fatal: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + + exit(EXIT_FAILURE); +} + +static uint32_t transform_address(uint32_t address) +{ + uint32_t a = address - RAM_BASE; + if (a >= RAM_TOP) + fatal("address 0x%x out of bounds", address); + return a; +} + +uint32_t read_long(uint32_t address) +{ + return READ_LONG(ram, transform_address(address)); +} + + +uint32_t read_word(uint32_t address) +{ + return READ_WORD(ram, transform_address(address)); +} + +uint32_t read_byte(uint32_t address) +{ + return READ_BYTE(ram, transform_address(address)); +} + +void write_byte(uint32_t address, uint32_t value) +{ + WRITE_BYTE(ram, transform_address(address), value); +} + +void write_word(uint32_t address, uint32_t value) +{ + WRITE_WORD(ram, transform_address(address), value); +} + +void write_long(uint32_t address, uint32_t value) +{ + WRITE_LONG(ram, transform_address(address), value); +} + +void system_call(uint8_t trapno) +{ + fatal("syscalls unimplemented"); +} + +static void load_program(FILE* fd) +{ + fseek(fd, 0, SEEK_SET); + if (fread(ram, 1, 0x34, fd) != 0x34) + fatal("couldn't read ELF header"); + + uint32_t phoff = READ_LONG(ram, 0x1c); + uint16_t phentsize = READ_WORD(ram, 0x2a); + uint16_t phnum = READ_WORD(ram, 0x2c); + entrypoint = READ_LONG(ram, 0x18); + if ((phentsize != 0x20) || (phnum != 1)) + fatal("unsupported ELF file"); + + fseek(fd, phoff, SEEK_SET); + if (fread(ram, 1, phentsize, fd) != phentsize) + fatal("couldn't read program header"); + + uint32_t offset = READ_LONG(ram, 0x04); + uint32_t vaddr = READ_LONG(ram, 0x08); + uint32_t filesz = READ_LONG(ram, 0x10); + uint32_t memsz = READ_LONG(ram, 0x14); + brkbase = brkpos = vaddr + memsz; + + uint32_t vaddroffset = transform_address(vaddr); + transform_address(vaddr + memsz); /* bounds check */ + memset(ram+vaddroffset, 0, memsz); + fseek(fd, offset, SEEK_SET); + if (fread(ram+vaddroffset, 1, filesz, fd) != filesz) + fatal("couldn't read program data"); +} + +/* The main loop */ +int main(int argc, char* argv[]) +{ + if(argc != 2) + fatal("syntax: emuppc "); + + FILE* fd = fopen(argv[1], "rb"); + if (!fd) + fatal("Unable to open %s", argv[1]); + load_program(fd); + fclose(fd); + + /* On entry, the Linux stack looks like this. + * + * sp+.. NULL + * sp+8+(4*argc) env (X quads) + * sp+4+(4*argc) NULL + * sp+4 argv (argc quads) + * sp argc + * + * We'll set it up with a bodgy stack frame with argc=0 just to keep the + * startup code happy. + */ + + { + uint32_t sp = INIT_SP; + write_long(sp -= 4, 0); + uint32_t envp = sp; + write_long(sp -= 4, envp); + write_long(sp -= 4, 0); + unsigned long argv = sp; + write_long(sp -= 4, argv); + write_long(sp -= 4, 0); + cpu.gpr[1] = sp; + cpu.cia = entrypoint; + } + + fatal("execution unimplemented"); + + return 0; +} + diff --git a/plat/linuxppc/emu/mkdispatcher.lua b/plat/linuxppc/emu/mkdispatcher.lua new file mode 100644 index 000000000..a660b8c34 --- /dev/null +++ b/plat/linuxppc/emu/mkdispatcher.lua @@ -0,0 +1,77 @@ +local function decode(line) + local _, _, bits = line:find("^([^ ]+) ") + if #bits ~= 32 then + error("'"..bits.."' isn't 32 bits long") + end + + local fields = {} + local i = 1 + while i ~= 33 do + local c = line:sub(i, i) + if c ~= "." then + local f = { pos=i } + if c:find("%w") then + f.size = 1 + f.value = c + elseif c == "<" then + local _, newi, name = line:find("^<%-*(%w+)%-*>", i) + f.size = 1 + newi - i + f.value = name + i = newi + else + error("bad field char '"..c.."' in '"..line.."'") + end + if f.value:find("[0-9]+") then + f.literal = true + f.variable = false + else + f.literal = false + f.variable = true + end + -- Convert from PowerPC numbering to sane numbering + f.pos = 33-(f.pos + f.size) + fields[#fields+1] = f + end + i = i + 1 + end + return fields +end + +local function emit(fields, code) + local mask = 0 + local value = 0 + for _, f in ipairs(fields) do + if f.literal then + local s = math.pow(2, f.pos) + local m = math.pow(2, f.size) - 1 + mask = mask + m*s + value = value + f.value*s + end + end + + print(string.format("if ((value & 0x%x) == 0x%x) {", mask, value)) + for _, f in ipairs(fields) do + if f.variable then + local m = math.pow(2, f.size) - 1 + print(string.format("uint32_t %s = (value >> %d) & 0x%x;", f.value, f.pos, m)) + end + end + + print(code) + print("return;") + print("}") +end + +while true do + local line = io.stdin:read("*l") + if not line then + break + end + line = line:gsub("#.*$", "") + line = line:gsub(" *$", "") + if line ~= "" then + local fields = decode(line) + emit(fields, line:sub(34, #line)) + end +end + diff --git a/plat/linuxppc/tests/build.lua b/plat/linuxppc/tests/build.lua index 7601ab0be..7ea6e5bb9 100644 --- a/plat/linuxppc/tests/build.lua +++ b/plat/linuxppc/tests/build.lua @@ -3,5 +3,5 @@ include("tests/plat/build.lua") plat_testsuite { name = "tests", plat = "linuxppc", - method = "qemu-ppc" + method = "plat/linuxppc/emu+emuppc" }