initial commit
[freebsd-arm:freebsd-arm.git] / compat / x86bios / x86bios.c
1 /*-
2  * Copyright (c) 2009 Alex Keda <admin@lissyara.su>
3  * Copyright (c) 2009 Jung-uk Kim <jkim@FreeBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include "opt_x86bios.h"
32
33 #include <sys/param.h>
34 #include <sys/bus.h>
35 #include <sys/kernel.h>
36 #include <sys/lock.h>
37 #include <sys/malloc.h>
38 #include <sys/module.h>
39 #include <sys/mutex.h>
40 #include <sys/proc.h>
41 #include <sys/sysctl.h>
42
43 #include <contrib/x86emu/x86emu.h>
44 #include <contrib/x86emu/x86emu_regs.h>
45 #include <compat/x86bios/x86bios.h>
46
47 #include <dev/pci/pcireg.h>
48 #include <dev/pci/pcivar.h>
49
50 #include <machine/cpufunc.h>
51
52 #include <vm/vm.h>
53 #include <vm/pmap.h>
54
55 #define X86BIOS_PAGE_SIZE       0x00001000      /* 4K */
56
57 #define X86BIOS_IVT_SIZE        0x00000500      /* 1K + 256 (BDA) */
58 #define X86BIOS_SEG_SIZE        0x00010000      /* 64K */
59 #define X86BIOS_MEM_SIZE        0x00100000      /* 1M */
60
61 #define X86BIOS_IVT_BASE        0x00000000
62 #define X86BIOS_RAM_BASE        0x00001000
63 #define X86BIOS_ROM_BASE        0x000a0000      /* XXX EBDA? */
64
65 #define X86BIOS_ROM_SIZE        (X86BIOS_MEM_SIZE - X86BIOS_ROM_BASE)
66
67 #define X86BIOS_PAGES           (X86BIOS_MEM_SIZE / X86BIOS_PAGE_SIZE)
68
69 #define X86BIOS_R_DS            _pad1
70 #define X86BIOS_R_SS            _pad2
71
72 static struct x86emu x86bios_emu;
73
74 static struct mtx x86bios_lock;
75
76 static void *x86bios_ivt;
77 static void *x86bios_rom;
78 static void *x86bios_seg;
79
80 static vm_offset_t *x86bios_map;
81
82 static vm_paddr_t x86bios_seg_phys;
83
84 SYSCTL_NODE(_debug, OID_AUTO, x86bios, CTLFLAG_RD, NULL, "x86bios debugging");
85 static int x86bios_trace_call;
86 TUNABLE_INT("debug.x86bios.call", &x86bios_trace_call);
87 SYSCTL_INT(_debug_x86bios, OID_AUTO, call, CTLFLAG_RW, &x86bios_trace_call, 0,
88     "Trace far function calls");
89 static int x86bios_trace_int;
90 TUNABLE_INT("debug.x86bios.int", &x86bios_trace_int);
91 SYSCTL_INT(_debug_x86bios, OID_AUTO, int, CTLFLAG_RW, &x86bios_trace_int, 0,
92     "Trace software interrupt handlers");
93
94 static void *
95 x86bios_get_pages(uint32_t offset, size_t size)
96 {
97         int i;
98
99         if (offset + size > X86BIOS_MEM_SIZE)
100                 return (NULL);
101
102         i = offset / X86BIOS_PAGE_SIZE;
103         if (x86bios_map[i] != 0)
104                 return ((void *)(x86bios_map[i] + offset -
105                     i * X86BIOS_PAGE_SIZE));
106
107         return (NULL);
108 }
109
110 static void
111 x86bios_set_pages(vm_offset_t va, vm_paddr_t pa, size_t size)
112 {
113         int i, j;
114
115         for (i = pa / X86BIOS_PAGE_SIZE, j = 0;
116             j < howmany(size, X86BIOS_PAGE_SIZE); i++, j++)
117                 x86bios_map[i] = va + j * X86BIOS_PAGE_SIZE;
118 }
119
120 static uint8_t
121 x86bios_emu_rdb(struct x86emu *emu, uint32_t addr)
122 {
123         uint8_t *va;
124
125         va = x86bios_get_pages(addr, sizeof(*va));
126         if (va == NULL)
127                 x86emu_halt_sys(emu);
128
129         return (*va);
130 }
131
132 static uint16_t
133 x86bios_emu_rdw(struct x86emu *emu, uint32_t addr)
134 {
135         uint16_t *va;
136
137         va = x86bios_get_pages(addr, sizeof(*va));
138         if (va == NULL)
139                 x86emu_halt_sys(emu);
140
141         return (le16toh(*va));
142 }
143
144 static uint32_t
145 x86bios_emu_rdl(struct x86emu *emu, uint32_t addr)
146 {
147         uint32_t *va;
148
149         va = x86bios_get_pages(addr, sizeof(*va));
150         if (va == NULL)
151                 x86emu_halt_sys(emu);
152
153         return (le32toh(*va));
154 }
155
156 static void
157 x86bios_emu_wrb(struct x86emu *emu, uint32_t addr, uint8_t val)
158 {
159         uint8_t *va;
160
161         va = x86bios_get_pages(addr, sizeof(*va));
162         if (va == NULL)
163                 x86emu_halt_sys(emu);
164
165         *va = val;
166 }
167
168 static void
169 x86bios_emu_wrw(struct x86emu *emu, uint32_t addr, uint16_t val)
170 {
171         uint16_t *va;
172
173         va = x86bios_get_pages(addr, sizeof(*va));
174         if (va == NULL)
175                 x86emu_halt_sys(emu);
176
177         *va = htole16(val);
178 }
179
180 static void
181 x86bios_emu_wrl(struct x86emu *emu, uint32_t addr, uint32_t val)
182 {
183         uint32_t *va;
184
185         va = x86bios_get_pages(addr, sizeof(*va));
186         if (va == NULL)
187                 x86emu_halt_sys(emu);
188
189         *va = htole32(val);
190 }
191
192 static uint8_t
193 x86bios_emu_inb(struct x86emu *emu, uint16_t port)
194 {
195
196         if (port == 0xb2) /* APM scratch register */
197                 return (0);
198         if (port >= 0x80 && port < 0x88) /* POST status register */
199                 return (0);
200
201         return (inb(port));
202 }
203
204 static uint16_t
205 x86bios_emu_inw(struct x86emu *emu, uint16_t port)
206 {
207
208         if (port >= 0x80 && port < 0x88) /* POST status register */
209                 return (0);
210
211         return (inw(port));
212 }
213
214 static uint32_t
215 x86bios_emu_inl(struct x86emu *emu, uint16_t port)
216 {
217
218         if (port >= 0x80 && port < 0x88) /* POST status register */
219                 return (0);
220
221         return (inl(port));
222 }
223
224 static void
225 x86bios_emu_outb(struct x86emu *emu, uint16_t port, uint8_t val)
226 {
227
228         if (port == 0xb2) /* APM scratch register */
229                 return;
230         if (port >= 0x80 && port < 0x88) /* POST status register */
231                 return;
232
233         outb(port, val);
234 }
235
236 static void
237 x86bios_emu_outw(struct x86emu *emu, uint16_t port, uint16_t val)
238 {
239
240         if (port >= 0x80 && port < 0x88) /* POST status register */
241                 return;
242
243         outw(port, val);
244 }
245
246 static void
247 x86bios_emu_outl(struct x86emu *emu, uint16_t port, uint32_t val)
248 {
249
250         if (port >= 0x80 && port < 0x88) /* POST status register */
251                 return;
252
253         outl(port, val);
254 }
255
256 static void
257 x86bios_emu_get_intr(struct x86emu *emu, int intno)
258 {
259         uint16_t *sp;
260         uint32_t iv;
261
262         emu->x86.R_SP -= 6;
263
264         sp = (uint16_t *)((vm_offset_t)x86bios_seg + emu->x86.R_SP);
265         sp[0] = htole16(emu->x86.R_IP);
266         sp[1] = htole16(emu->x86.R_CS);
267         sp[2] = htole16(emu->x86.R_FLG);
268
269         iv = x86bios_get_intr(intno);
270         emu->x86.R_IP = iv & 0x000f;
271         emu->x86.R_CS = (iv >> 12) & 0xffff;
272         emu->x86.R_FLG &= ~(F_IF | F_TF);
273 }
274
275 void *
276 x86bios_alloc(uint32_t *offset, size_t size)
277 {
278         void *vaddr;
279
280         if (offset == NULL || size == 0)
281                 return (NULL);
282
283         vaddr = contigmalloc(size, M_DEVBUF, M_NOWAIT, X86BIOS_RAM_BASE,
284             X86BIOS_ROM_BASE, X86BIOS_PAGE_SIZE, 0);
285         if (vaddr != NULL) {
286                 *offset = vtophys(vaddr);
287                 x86bios_set_pages((vm_offset_t)vaddr, *offset, size);
288         }
289
290         return (vaddr);
291 }
292
293 void
294 x86bios_free(void *addr, size_t size)
295 {
296         vm_paddr_t paddr;
297
298         if (addr == NULL || size == 0)
299                 return;
300
301         paddr = vtophys(addr);
302         if (paddr < X86BIOS_RAM_BASE || paddr >= X86BIOS_ROM_BASE ||
303             paddr % X86BIOS_PAGE_SIZE != 0)
304                 return;
305
306         bzero(x86bios_map + paddr / X86BIOS_PAGE_SIZE,
307             sizeof(*x86bios_map) * howmany(size, X86BIOS_PAGE_SIZE));
308         contigfree(addr, size, M_DEVBUF);
309 }
310
311 void
312 x86bios_init_regs(struct x86regs *regs)
313 {
314
315         bzero(regs, sizeof(*regs));
316         regs->X86BIOS_R_DS = regs->X86BIOS_R_SS = x86bios_seg_phys >> 4;
317 }
318
319 void
320 x86bios_call(struct x86regs *regs, uint16_t seg, uint16_t off)
321 {
322
323         if (x86bios_map == NULL)
324                 return;
325
326         if (x86bios_trace_call)
327                 printf("Calling 0x%05x (ax=0x%04x bx=0x%04x "
328                     "cx=0x%04x dx=0x%04x es=0x%04x di=0x%04x)\n",
329                     (seg << 4) + off, regs->R_AX, regs->R_BX, regs->R_CX,
330                     regs->R_DX, regs->R_ES, regs->R_DI);
331
332         mtx_lock_spin(&x86bios_lock);
333         memcpy(&x86bios_emu.x86, regs, sizeof(*regs));
334         x86emu_exec_call(&x86bios_emu, seg, off);
335         memcpy(regs, &x86bios_emu.x86, sizeof(*regs));
336         mtx_unlock_spin(&x86bios_lock);
337
338         if (x86bios_trace_call)
339                 printf("Exiting 0x%05x (ax=0x%04x bx=0x%04x "
340                     "cx=0x%04x dx=0x%04x es=0x%04x di=0x%04x)\n",
341                     (seg << 4) + off, regs->R_AX, regs->R_BX, regs->R_CX,
342                     regs->R_DX, regs->R_ES, regs->R_DI);
343 }
344
345 uint32_t
346 x86bios_get_intr(int intno)
347 {
348         uint32_t *iv;
349
350         iv = (uint32_t *)((vm_offset_t)x86bios_ivt + intno * 4);
351
352         return (le32toh(*iv));
353 }
354
355 void
356 x86bios_intr(struct x86regs *regs, int intno)
357 {
358
359         if (intno < 0 || intno > 255)
360                 return;
361
362         if (x86bios_map == NULL)
363                 return;
364
365         if (x86bios_trace_int)
366                 printf("Calling int 0x%x (ax=0x%04x bx=0x%04x "
367                     "cx=0x%04x dx=0x%04x es=0x%04x di=0x%04x)\n",
368                     intno, regs->R_AX, regs->R_BX, regs->R_CX,
369                     regs->R_DX, regs->R_ES, regs->R_DI);
370
371         mtx_lock_spin(&x86bios_lock);
372         memcpy(&x86bios_emu.x86, regs, sizeof(*regs));
373         x86emu_exec_intr(&x86bios_emu, intno);
374         memcpy(regs, &x86bios_emu.x86, sizeof(*regs));
375         mtx_unlock_spin(&x86bios_lock);
376
377         if (x86bios_trace_int)
378                 printf("Exiting int 0x%x (ax=0x%04x bx=0x%04x "
379                     "cx=0x%04x dx=0x%04x es=0x%04x di=0x%04x)\n",
380                     intno, regs->R_AX, regs->R_BX, regs->R_CX,
381                     regs->R_DX, regs->R_ES, regs->R_DI);
382 }
383
384 void *
385 x86bios_offset(uint32_t offset)
386 {
387
388         return (x86bios_get_pages(offset, 1));
389 }
390
391 void *
392 x86bios_get_orm(uint32_t offset)
393 {
394         uint8_t *p;
395
396         /* Does the shadow ROM contain BIOS POST code for x86? */
397         p = x86bios_offset(offset);
398         if (p == NULL || p[0] != 0x55 || p[1] != 0xaa || p[3] != 0xe9)
399                 return (NULL);
400
401         return (p);
402 }
403
404 int
405 x86bios_match_device(uint32_t offset, device_t dev)
406 {
407         uint8_t *p;
408         uint16_t device, vendor;
409         uint8_t class, progif, subclass;
410
411         /* Does the shadow ROM contain BIOS POST code for x86? */
412         p = x86bios_get_orm(offset);
413         if (p == NULL)
414                 return (0);
415
416         /* Does it contain PCI data structure? */
417         p += le16toh(*(uint16_t *)(p + 0x18));
418         if (bcmp(p, "PCIR", 4) != 0 ||
419             le16toh(*(uint16_t *)(p + 0x0a)) < 0x18 || *(p + 0x14) != 0)
420                 return (0);
421
422         /* Does it match the vendor, device, and classcode? */
423         vendor = le16toh(*(uint16_t *)(p + 0x04));
424         device = le16toh(*(uint16_t *)(p + 0x06));
425         progif = *(p + 0x0d);
426         subclass = *(p + 0x0e);
427         class = *(p + 0x0f);
428         if (vendor != pci_get_vendor(dev) || device != pci_get_device(dev) ||
429             class != pci_get_class(dev) || subclass != pci_get_subclass(dev) ||
430             progif != pci_get_progif(dev))
431                 return (0);
432
433         return (1);
434 }
435
436 static __inline int
437 x86bios_map_mem(void)
438 {
439
440         x86bios_ivt = pmap_mapbios(X86BIOS_IVT_BASE, X86BIOS_IVT_SIZE);
441         if (x86bios_ivt == NULL)
442                 return (1);
443         x86bios_rom = pmap_mapdev(X86BIOS_ROM_BASE, X86BIOS_ROM_SIZE);
444         if (x86bios_rom == NULL) {
445                 pmap_unmapdev((vm_offset_t)x86bios_ivt, X86BIOS_IVT_SIZE);
446                 return (1);
447         }
448         x86bios_seg = contigmalloc(X86BIOS_SEG_SIZE, M_DEVBUF, M_WAITOK,
449             X86BIOS_RAM_BASE, X86BIOS_ROM_BASE, X86BIOS_PAGE_SIZE, 0);
450         x86bios_seg_phys = vtophys(x86bios_seg);
451
452         return (0);
453 }
454
455 static __inline void
456 x86bios_unmap_mem(void)
457 {
458
459         pmap_unmapdev((vm_offset_t)x86bios_ivt, X86BIOS_IVT_SIZE);
460         pmap_unmapdev((vm_offset_t)x86bios_rom, X86BIOS_ROM_SIZE);
461         contigfree(x86bios_seg, X86BIOS_SEG_SIZE, M_DEVBUF);
462 }
463
464 static void
465 x86bios_init(void *arg __unused)
466 {
467         int i;
468
469         mtx_init(&x86bios_lock, "x86bios lock", NULL, MTX_SPIN);
470
471         if (x86bios_map_mem() != 0)
472                 return;
473
474         x86bios_map = malloc(sizeof(*x86bios_map) * X86BIOS_PAGES, M_DEVBUF,
475             M_WAITOK | M_ZERO);
476         x86bios_set_pages((vm_offset_t)x86bios_ivt, X86BIOS_IVT_BASE,
477             X86BIOS_IVT_SIZE);
478         x86bios_set_pages((vm_offset_t)x86bios_rom, X86BIOS_ROM_BASE,
479             X86BIOS_ROM_SIZE);
480         x86bios_set_pages((vm_offset_t)x86bios_seg, x86bios_seg_phys,
481             X86BIOS_SEG_SIZE);
482
483         bzero(&x86bios_emu, sizeof(x86bios_emu));
484
485         x86bios_emu.emu_rdb = x86bios_emu_rdb;
486         x86bios_emu.emu_rdw = x86bios_emu_rdw;
487         x86bios_emu.emu_rdl = x86bios_emu_rdl;
488         x86bios_emu.emu_wrb = x86bios_emu_wrb;
489         x86bios_emu.emu_wrw = x86bios_emu_wrw;
490         x86bios_emu.emu_wrl = x86bios_emu_wrl;
491
492         x86bios_emu.emu_inb = x86bios_emu_inb;
493         x86bios_emu.emu_inw = x86bios_emu_inw;
494         x86bios_emu.emu_inl = x86bios_emu_inl;
495         x86bios_emu.emu_outb = x86bios_emu_outb;
496         x86bios_emu.emu_outw = x86bios_emu_outw;
497         x86bios_emu.emu_outl = x86bios_emu_outl;
498
499         for (i = 0; i < 256; i++)
500                 x86bios_emu._x86emu_intrTab[i] = x86bios_emu_get_intr;
501 }
502
503 static void
504 x86bios_uninit(void *arg __unused)
505 {
506         vm_offset_t *map = x86bios_map;
507
508         mtx_lock_spin(&x86bios_lock);
509         if (x86bios_map != NULL) {
510                 free(x86bios_map, M_DEVBUF);
511                 x86bios_map = NULL;
512         }
513         mtx_unlock_spin(&x86bios_lock);
514
515         if (map != NULL)
516                 x86bios_unmap_mem();
517
518         mtx_destroy(&x86bios_lock);
519 }
520
521 static int
522 x86bios_modevent(module_t mod __unused, int type, void *data __unused)
523 {
524
525         switch (type) {
526         case MOD_LOAD:
527                 x86bios_init(NULL);
528                 break;
529         case MOD_UNLOAD:
530                 x86bios_uninit(NULL);
531                 break;
532         default:
533                 return (ENOTSUP);
534         }
535
536         return (0);
537 }
538
539 static moduledata_t x86bios_mod = {
540         "x86bios",
541         x86bios_modevent,
542         NULL,
543 };
544
545 DECLARE_MODULE(x86bios, x86bios_mod, SI_SUB_CPU, SI_ORDER_ANY);
546 MODULE_VERSION(x86bios, 1);