segfault handler, op parser
[ginge.git] / loader / emu.c
CommitLineData
11913091 1// vim:shiftwidth=2:expandtab
2#include <stdio.h>
3#include <stdlib.h>
4#include <sys/types.h>
5#include <sys/stat.h>
6#include <fcntl.h>
7#include <sys/mman.h>
8#include <sys/types.h>
9#include <unistd.h>
10#include <signal.h>
11#include <asm/ucontext.h>
12
13#include "header.h"
14
15#define iolog printf
16//#define iolog(...)
17
18typedef unsigned int u32;
19typedef unsigned short u16;
20typedef unsigned char u8;
21
22static int memdev;
23static volatile u16 *memregs, *blitter;
24
25
26static volatile void *translate_addr(u32 a, u32 *mask)
27{
28 if ((a & 0xfff00000) == 0x7f000000) {
29 *mask = 0xffff;
30 return memregs;
31 }
32 if ((a & 0xfff00000) == 0x7f100000) {
33 *mask = 0xff;
34 return blitter;
35 }
36 fprintf(stderr, "bad IO @ %08x\n", a);
37 abort();
38}
39
40static u32 xread8(u32 a)
41{
42 volatile u8 *mem;
43 u32 mask;
44
45 iolog("r8 %08x\n", a);
46 mem = translate_addr(a, &mask);
47 return mem[a & mask];
48}
49
50static u32 xread16(u32 a)
51{
52 volatile u16 *mem;
53 u32 mask;
54
55 iolog("r16 %08x\n", a);
56 mem = translate_addr(a, &mask);
57 return mem[(a & mask) / 2];
58}
59
60static u32 xread32(u32 a)
61{
62 volatile u32 *mem;
63 u32 mask;
64
65 iolog("r32 %08x\n", a);
66 mem = translate_addr(a, &mask);
67 return mem[(a & mask) / 4];
68}
69
70static void xwrite8(u32 a, u32 d)
71{
72 volatile u8 *mem;
73 u32 mask;
74
75 iolog("w8 %08x %08x\n", a, d);
76 mem = translate_addr(a, &mask);
77 mem[a & mask] = d;
78}
79
80static void xwrite16(u32 a, u32 d)
81{
82 volatile u16 *mem;
83 u32 mask;
84
85 iolog("w16 %08x %08x\n", a, d);
86 mem = translate_addr(a, &mask);
87 mem[(a & mask) / 2] = d;
88}
89
90static void xwrite32(u32 a, u32 d)
91{
92 volatile u32 *mem;
93 u32 mask;
94
95 iolog("w32 %08x %08x\n", a, d);
96 mem = translate_addr(a, &mask);
97 mem[(a & mask) / 4] = d;
98}
99
100#define BIT_SET(v, b) (v & (1 << (b)))
101
102static void handle_op(u32 addr_pc, u32 op, u32 *regs, u32 addr_check)
103{
104 u32 t, shift, ret, addr;
105 int rn, rd;
106
107 rd = (op & 0x0000f000) >> 12;
108 rn = (op & 0x000f0000) >> 16;
109
110 if ((op & 0x0f200090) == 0x01000090) { // AM3: LDRH, STRH
111 if (BIT_SET(op, 6)) // S
112 goto unhandled;
113
114 if (BIT_SET(op, 22)) // imm offset
115 t = ((op & 0xf00) >> 4) | (op & 0x0f);
116 else // reg offset
117 t = regs[op & 0x000f];
118
119 if (!BIT_SET(op, 23))
120 t = -t;
121 addr = regs[rn] + t;
122
123 if (BIT_SET(op, 20)) { // Load
124 ret = xread16(addr);
125 regs[rd] = ret;
126 }
127 else
128 xwrite16(addr, regs[rd]);
129 }
130 else if ((op & 0x0d200000) == 0x05000000) { // AM2: LDR[B], STR[B]
131 if (BIT_SET(op, 25)) { // reg offs
132 if (BIT_SET(op, 4))
133 goto unhandled;
134
135 t = regs[op & 0x000f];
136 shift = (op & 0x0f80) >> 7;
137 switch ((op & 0x0060) >> 5) {
138 case 0: t = t << shift; break;
139 case 1: t = t >> (shift + 1); break;
140 case 2: t = (signed int)t >> (shift + 1); break;
141 case 3: goto unhandled; // I'm just lazy
142 }
143 }
144 else // imm offs
145 t = op & 0x0fff;
146
147 if (!BIT_SET(op, 23))
148 t = -t;
149 addr = regs[rn] + t;
150
151 if (BIT_SET(op, 20)) { // Load
152 if (BIT_SET(op, 22)) // Byte
153 ret = xread8(addr);
154 else
155 ret = xread32(addr);
156 regs[rd] = ret;
157 }
158 else {
159 if (BIT_SET(op, 22)) // Byte
160 xwrite8(addr, regs[rd]);
161 else
162 xwrite32(addr, regs[rd]);
163 }
164 }
165 else
166 goto unhandled;
167
168 if (addr != addr_check) {
169 fprintf(stderr, "bad calculated addr: %08x vs %08x\n", addr, addr_check);
170 abort();
171 }
172 return;
173
174unhandled:
175 fprintf(stderr, "unhandled IO op %08x @ %08x\n", op, addr_pc);
176}
177
178static void segv_sigaction(int num, siginfo_t *info, void *ctx)
179{
180 struct ucontext *context = ctx;
181 u32 *regs = (u32 *)&context->uc_mcontext.arm_r0;
182 u32 op = *(u32 *)regs[15];
183
184 //printf("segv %d %p @ %08x\n", info->si_code, info->si_addr, regs[15]);
185/*
186 static int thissec, sfps;
187 struct timeval tv;
188 gettimeofday(&tv, NULL);
189 sfps++;
190 if (tv.tv_sec != thissec) {
191 printf("%d\n", sfps);
192 sfps = 0;
193 thissec = tv.tv_sec;
194 }
195*/
196
197 handle_op(regs[15], op, regs, (u32)info->si_addr);
198 regs[15] += 4;
199 return;
200
201 //signal(num, SIG_DFL);
202 //raise(num);
203}
204
205#define LINKPAGE_SIZE 0x1000
206
207struct linkpage {
208 u32 (*xread8)(u32 a);
209 u32 (*xread16)(u32 a);
210 u32 (*xread32)(u32 a);
211 void (*xwrite8)(u32 a, u32 d);
212 void (*xwrite16)(u32 a, u32 d);
213 void (*xwrite32)(u32 a, u32 d);
214 u32 retval;
215 u32 *reg_ptr;
216 u32 saved_regs[6]; // r0-r3,r12,lr
217 u32 code[0];
218};
219
220static struct linkpage *g_linkpage;
221static u32 *g_code_ptr;
222
223void emu_init(void *map_bottom)
224{
225 struct sigaction segv_action = {
226 .sa_sigaction = segv_sigaction,
227 .sa_flags = SA_SIGINFO,
228 };
229 struct linkpage init_linkpage = {
230 .xread8 = xread8,
231 .xread16 = xread16,
232 .xread32 = xread32,
233 .xwrite8 = xwrite8,
234 .xwrite16 = xwrite16,
235 .xwrite32 = xwrite32,
236 };
237 void *ret;
238
239 sigemptyset(&segv_action.sa_mask);
240 sigaction(SIGSEGV, &segv_action, NULL);
241
242 g_linkpage = (void *)(((u32)map_bottom - LINKPAGE_SIZE) & ~0xfff);
243 ret = mmap(g_linkpage, LINKPAGE_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
244 if (ret != g_linkpage) {
245 perror("mmap linkpage");
246 exit(1);
247 }
248 printf("linkpage @ %p\n", g_linkpage);
249 memcpy(g_linkpage, &init_linkpage, sizeof(*g_linkpage));
250 g_linkpage->reg_ptr = g_linkpage->saved_regs;
251 g_code_ptr = g_linkpage->code;
252
253 memdev = open("/dev/mem", O_RDWR);
254 memregs = mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0xc0000000);
255 blitter = mmap(NULL, 0x100, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0xe0020000);
256 printf("mapped %d %p %p\n", memdev, memregs, blitter);
257}
258
259void *emu_mmap_dev(unsigned int length, int prot, int flags, unsigned int offset)
260{
261 char name[32];
262 int fd;
263
264 if ((offset & ~0xffff) == 0xc0000000) {
265 return mmap((void *)0x7f000000, length, PROT_NONE,
266 MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED|MAP_NORESERVE, -1, 0);
267 }
268 if ((offset & ~0xffff) == 0xe0020000) {
269 return mmap((void *)0x7f100000, length, PROT_NONE,
270 MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED|MAP_NORESERVE, -1, 0);
271 }
272 // pass through
273 if ((offset & 0xfe000000) == 0x02000000)
274 return mmap(NULL, length, prot, flags, memdev, offset);
275
276 sprintf(name, "m%08x", offset);
277 fd = open(name, O_CREAT|O_RDWR, 0644);
278 lseek(fd, length - 1, SEEK_SET);
279 name[0] = 0;
280 write(fd, name, 1);
281
282 return mmap(NULL, length, prot, MAP_SHARED, fd, 0);
283}
284