]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/x86emu/lib/int10/int10.cc
2a482adbd6accb5483a992e2e60b8cea8f101f73
[l4.git] / l4 / pkg / x86emu / lib / int10 / int10.cc
1 /**
2  * \file        x86emu/lib/int10/int10.c(c)
3  * \brief       Call VESA BIOS functions using the real mode interface
4  *
5  * \date        2005
6  * \author      Frank Mehnert <fm3@os.inf.tu-dresden.de>
7  *              Adam Lackorzynski <adam@os.inf.tu-dresden.de>
8  *              Alexander Warg <warg@os.inf.tu-dresden.de>
9  */
10 /*
11  * (c) 2005-2009 Author(s)
12  *     economic rights: Technische Universität Dresden (Germany)
13  *
14  * This file is part of TUD:OS and distributed under the terms of the
15  * GNU General Public License 2.
16  * Please see the COPYING-GPL-2 file for details.
17  */
18
19 #include <l4/sys/types.h>
20 #include <l4/sys/ipc.h>
21 #include <l4/sys/kdebug.h>
22 #include <l4/x86emu/x86emu.h>
23 #include <l4/re/env>
24 #include <l4/re/mem_alloc>
25 #include <l4/re/rm>
26 #include <l4/re/util/cap_alloc>
27 #include <l4/re/namespace>
28 #include <l4/re/dataspace>
29 #include <l4/util/mb_info.h>
30 #include <l4/util/macros.h>
31 #include <l4/sys/err.h>
32 #include <l4/io/io.h>
33 #include <l4/vbus/vbus.h>
34 #include <l4/vbus/vbus_types.h>
35 #include <l4/vbus/vbus_pci.h>
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <stdarg.h>
40
41 #include <l4/x86emu/int10.h>
42
43 enum {
44   Dbg_io  = 0,
45   Dbg_mem = 0,
46   Dbg_pci = 0,
47 };
48
49
50 static l4_addr_t   v_page[1024*1024/(L4_PAGESIZE)];
51 static l4_addr_t v_area;
52 static l4_umword_t initialized;
53 static L4::Cap<void> vbus;
54 static l4vbus_device_handle_t root_bridge;
55
56 static void
57 warn(u32 addr, const char *func)
58 {
59   printf("\033[31mWARNING: Function %s access %08lx\033[m\n", func,
60          (unsigned long)addr);
61 }
62
63 #define PCI_SLOT(devfn)         (((devfn) >> 3) & 0x1f)
64 #define PCI_FUNC(devfn)         ((devfn) & 0x07)
65
66 static int do_pcicfg(unsigned long addr, u32 *val32, int size, int out)
67 {
68   static l4_uint32_t config_reg;
69   /* check for PCI addr and data ports */
70   if (addr < 0xcf8 || addr >= 0xd00)
71     return 0;
72
73   if (addr == 0xcf8)
74     {
75       config_reg = *val32;
76       return 1;
77     }
78
79   if (addr >= 0xcfc)
80     {
81       l4_uint32_t reg;
82       l4_uint32_t v;
83       unsigned df = (config_reg >> 8) & 0xff;
84       unsigned bus = (config_reg >> 16) & 0xff;
85
86       df = (PCI_SLOT(df) << 16) | PCI_FUNC(df);
87
88       reg = (addr & 3) | (config_reg & 0xfc) | ((config_reg >> 16) & 0xf00);
89       if (Dbg_pci)
90         printf("CFG ACCESS(%d-%d): %02x:%03x.%02x reg=%x val=%p (*val=%lx)\n",
91                out, size, bus, (df >> 16) & 0xffff, df & 0xffff,
92                reg, val32, (unsigned long)*val32);
93
94       if (out)
95         l4vbus_pci_cfg_write(vbus.cap(), root_bridge, bus, df, reg,
96                              *val32, size);
97       else
98         {
99           l4vbus_pci_cfg_read(vbus.cap(), root_bridge, bus, df, reg, &v, size);
100           *val32 = v;
101         }
102     }
103   else
104     printf("Uups: %lx\n", addr);
105
106   if (Dbg_pci)
107     printf("access to PCI %lx %s %lx\n", addr, out ? "WRITE" : "READ",
108            (unsigned long)*val32);
109
110   return 1;
111 }
112
113 template< typename W >
114 static W X86API
115 port_in(X86EMU_pioAddr addr)
116 {
117   u32 r;
118   if (do_pcicfg(addr, &r, sizeof(W) * 8, 0))
119     return r;
120
121   switch (sizeof(W))
122     {
123     case 1: asm volatile ("in %w1, %b0" : "=a" (r) : "d" (addr)); break;
124     case 2: asm volatile ("in %w1, %w0" : "=a" (r) : "d" (addr)); break;
125     case 4: asm volatile ("in %w1, %0" : "=a" (r) : "d" (addr)); break;
126     default: r = 0;
127     }
128
129   if (Dbg_io)
130     {
131       static char const *const ws[4] =  { "b", "w", "X", "l" };
132       printf("%04x:%04x in%s %x -> %lx\n", M.x86.R_CS, M.x86.R_IP,
133              ws[sizeof(W)-1], addr, (unsigned long)r);
134       //l4_sleep(10);
135     }
136   return r;
137 }
138
139
140 template< typename W >
141 static void X86API
142 port_out(X86EMU_pioAddr addr, W val)
143 {
144   u32 r = val;
145   if (do_pcicfg(addr, &r, sizeof(W) * 8, 1))
146     return;
147
148   if (Dbg_io)
149     {
150       static char const *const ws[4] =  { "b", "w", "X", "l" };
151       printf("%04x:%04x out%s %x -> %lx\n", M.x86.R_CS, M.x86.R_IP,
152              ws[sizeof(W)-1], addr, (unsigned long)r);
153       //l4_sleep(10);
154     }
155
156   switch (sizeof(W))
157     {
158     case 1: asm volatile ("out %b0, %w1" : : "a" (r), "d" (addr)); break;
159     case 2: asm volatile ("out %w0, %w1" : : "a" (r), "d" (addr)); break;
160     case 4: asm volatile ("out %0, %w1"  : : "a" (r), "d" (addr)); break;
161     default: break;
162     }
163 }
164
165 template< typename W >
166 static W X86API
167 mem_read(u32 addr)
168 {
169   if (addr > (1 << 20))
170     warn(addr, __FUNCTION__);
171
172   if (Dbg_mem)
173     printf("read(%d) %08x => ", (int)sizeof(W), (unsigned)addr);
174
175   W const *a = (W const *)(v_page[addr/L4_PAGESIZE] + (addr % L4_PAGESIZE));
176
177   if (Dbg_mem)
178     printf("%0*x\n", (int)sizeof(W)*2, (unsigned)*a);
179
180   return *a;
181 }
182
183 template< typename W >
184 static void X86API
185 mem_write(u32 addr, W val)
186 {
187   if (addr > (1 << 20))
188     warn(addr, __FUNCTION__);
189
190   if (Dbg_mem)
191     printf("write(%d) %0*x => %08x\n", (int)sizeof(W), (int)sizeof(W)*2, (unsigned)val, (unsigned)addr);
192
193   W *a = (W *)(v_page[addr/L4_PAGESIZE] + (addr % L4_PAGESIZE));
194   *a = val;
195 }
196
197
198 X86EMU_pioFuncs my_pioFuncs =
199 {
200   port_in<u8>,  port_in<u16>,  port_in<u32>,
201   port_out<u8>, port_out<u16>, port_out<u32>
202 };
203
204 X86EMU_memFuncs my_memFuncs =
205 {
206   mem_read<u8>,  mem_read<u16>,  mem_read<u32>,
207   mem_write<u8>, mem_write<u16>, mem_write<u32>
208 };
209
210 void
211 printk(const char *format, ...)
212 {
213   va_list list;
214   va_start(list, format);
215   vprintf(format, list);
216   va_end(list);
217 }
218
219
220 static int
221 x86emu_int10_init(void)
222 {
223   int error;
224   l4_addr_t addr;
225   l4_uint32_t idx;
226   L4::Cap<L4Re::Dataspace> ds;
227
228   if (initialized)
229     return 0;
230
231   int err;
232   vbus = L4Re::Env::env()->get_cap<void>("vbus");
233   if (!vbus.is_valid())
234     {
235       printf("no 'vbus' found in namespace\n");
236       return -L4_ENOENT;
237     }
238
239
240   err = l4vbus_get_device_by_hid(vbus.cap(), 0, &root_bridge, "PNP0A03", 0, 0);
241   if (err < 0)
242     {
243       printf("No PCI root bridge found\n");
244       L4Re::Util::cap_alloc.free(vbus);
245       return -L4_ENOENT;
246     }
247
248
249   X86EMU_setupPioFuncs(&my_pioFuncs);
250   X86EMU_setupMemFuncs(&my_memFuncs);
251   M.x86.debug = 0 /*| DEBUG_DECODE_F*/;
252
253   l4io_request_all_ioports();
254
255   /* Reserve region for physical memory 0x00000...0xfffff. Make sure that we
256    * can unmap it using one single l4_fpage_unmap (alignment). */
257   v_page[0] = 0;
258   if ((error = L4Re::Env::env()->rm()->reserve_area(&v_page[0], 1<<20,
259                                                     L4Re::Rm::Search_addr, 20)))
260     {
261       printf("Error %d reserving area for x86emu\n", error);
262       return error;
263     }
264   v_area = v_page[0];
265
266   /* Map physical page 0x00000 */
267   if (l4io_request_iomem_region(0, v_page[0], L4_PAGESIZE, L4IO_MEM_CACHED | L4IO_MEM_USE_RESERVED_AREA) < 0)
268     {
269       printf("Error %d allocating physical page 0 for x86emu\n", error);
270       return error;
271     }
272
273   ds = L4Re::Util::cap_alloc.alloc<L4Re::Dataspace>();
274   if (!ds.is_valid())
275     return -L4_ENOMEM;
276
277   if ((error = L4Re::Env::env()->mem_alloc()->alloc(L4_PAGESIZE, ds, 0)))
278     {
279       printf("Error %d allocating page for x86emu\n", error);
280       return error;
281     }
282
283   /* Map dummy as physical page 0x01000 */
284   v_page[1] = v_page[0] + L4_PAGESIZE;
285   if ((error = L4Re::Env::env()->rm()->attach((void**)&v_page[1], L4_PAGESIZE,
286                                               L4Re::Rm::In_area, ds,
287                                               0, L4_PAGESHIFT)))
288     {
289       printf("Error %d attaching page for x86emu\n", error);
290       return error;
291     }
292
293   for (idx=0x09F000/L4_PAGESIZE, addr=0x09F000;
294                                  addr<0x100000;
295        idx++,                    addr+=L4_PAGESIZE)
296     v_page[idx] = v_page[0] + addr;
297
298   if ((error = l4io_request_iomem_region(0x09F000, v_page[0x9f],
299                                          0x100000 - 0x09F000,
300                                          L4IO_MEM_CACHED |
301                                            L4IO_MEM_USE_RESERVED_AREA)) < 0)
302     {
303       printf("BIOS area mapping failed: %x\n", error);
304       return error;
305     }
306
307   /* int 10 ; ret */
308   *(l4_uint8_t*)(v_page[1]+0) = 0xcd;
309   *(l4_uint8_t*)(v_page[1]+1) = 0x10;
310   *(l4_uint8_t*)(v_page[1]+2) = 0xf4;
311
312   initialized = 1;
313
314   return 0;
315 }
316
317 int
318 x86emu_int10_done(void)
319 {
320   int ret;
321   unsigned i = 0;
322
323   if (!initialized)
324     return 0;
325
326   for (; i < sizeof(v_page) / sizeof(v_page[0]); ++i)
327     if (v_page[i])
328       L4Re::Env::env()->rm()->detach((void *)v_page[i], 0);
329
330   /* Free the area */
331   ret = L4Re::Env::env()->rm()->free_area(v_area);
332
333   initialized = 0;
334
335   return ret;
336 }
337
338 static l4_addr_t far_to_addr(l4_uint32_t farp)
339 {
340   l4_addr_t p = (farp & 0x0FFFF) + ((farp >> 12) & 0xFFFF0);
341   return (l4_addr_t)(v_page[p / L4_PAGESIZE] + (p % L4_PAGESIZE));
342 }
343
344 static void dump_mode(int mode, l4util_mb_vbe_mode_t *m)
345 {
346   char s[13];
347
348   if (!m)
349     {
350       printf("Mode: 0x%x: No information available\n", mode);
351       return;
352     }
353
354   snprintf(s, sizeof(s), "%dx%d@%d",
355            m->x_resolution, m->y_resolution, m->bits_per_pixel);
356   s[sizeof(s) - 1] = 0;
357
358   printf("Mode: 0x%x %13s, RGB: %d(%d):%d(%d):%d(%d) mode: %x\n",
359          mode, s,
360          m->red_field_position, m->red_mask_size,
361          m->green_field_position, m->green_mask_size,
362          m->blue_field_position, m->blue_mask_size,
363          m->mode_attributes);
364 }
365
366 static
367 l4util_mb_vbe_mode_t *get_mode_info(int mode)
368 {
369   M.x86.R_EAX  = 0x4F01;        /* int10 function number */
370   M.x86.R_ECX  = mode;          /* VESA mode */
371   M.x86.R_EDI  = 0x800;         /* ES:DI pointer to at least 256 bytes */
372   M.x86.R_IP   = 0;             /* address of "int10; hlt" */
373   M.x86.R_SP   = L4_PAGESIZE;   /* SS:SP pointer to stack */
374   M.x86.R_CS   =
375   M.x86.R_DS   =
376   M.x86.R_ES   =
377   M.x86.R_SS   = L4_PAGESIZE >> 4;
378   X86EMU_exec();
379
380   if (M.x86.R_AX != 0x004F)
381     return 0;
382
383   l4util_mb_vbe_mode_t *m = (l4util_mb_vbe_mode_t*)(v_page[1] + 0x800);
384   if ((m->mode_attributes & 0x91) == 0x91)
385     return m;
386
387   return 0;
388 }
389
390
391 int
392 x86emu_int10_set_vbemode(int mode, l4util_mb_vbe_ctrl_t **ctrl_info,
393                          l4util_mb_vbe_mode_t **mode_info)
394 {
395   int error;
396
397   if ((error = x86emu_int10_init()))
398     return error;
399
400   printf("Trying execution of ``set VBE mode'' using x86emu\n");
401
402   /* Get VESA BIOS controller information. */
403   M.x86.R_EAX  = 0x4F00;        /* int10 function number */
404   M.x86.R_EDI  = 0x100;         /* ES:DI pointer to at least 512 bytes */
405   M.x86.R_IP   = 0;             /* address of "int10; hlt" */
406   M.x86.R_SP   = L4_PAGESIZE;   /* SS:SP pointer to stack */
407   M.x86.R_CS   =
408   M.x86.R_DS   =
409   M.x86.R_ES   =
410   M.x86.R_SS   = L4_PAGESIZE >> 4;
411   X86EMU_exec();
412   *ctrl_info = (l4util_mb_vbe_ctrl_t*)(v_page[1] + 0x100);
413   if (M.x86.R_AX != 0x4F)
414     {
415       printf("VBE BIOS not present.\n");
416       return -L4_EINVAL;
417     }
418
419   const char *oem_string = (const char *)far_to_addr((*ctrl_info)->oem_string);
420   printf("Found VESA BIOS version %d.%d\n"
421          "OEM %s\n",
422          (int)((*ctrl_info)->version >> 8),
423          (int)((*ctrl_info)->version &  0xFF),
424          (*ctrl_info)->oem_string ? oem_string : "[unknown]");
425   if ((*ctrl_info)->version < 0x0200)
426     {
427       printf("VESA BIOS 2.0 or later required.\n");
428       return -L4_EINVAL;
429     }
430
431   if (mode == ~0)
432     {
433       // mode == ~0 means to look for the 'best' mode available, we'll look
434       // for the highest 16bit mode
435       int max_val = 0;
436       printf("Scanning for 'best' possible mode:\n");
437       l4_uint16_t *mode_list = (l4_uint16_t *)far_to_addr((*ctrl_info)->video_mode);
438       for (; *mode_list != 0xffff; ++mode_list)
439         {
440           l4util_mb_vbe_mode_t *m = get_mode_info(*mode_list);
441           dump_mode(*mode_list, m);
442           if (m)
443             {
444               // our 'best' expression...
445               if (m->x_resolution > max_val && m->bits_per_pixel == 16)
446                 {
447                   max_val = m->x_resolution;
448                   mode = *mode_list;
449                   *mode_info = m;
450                 }
451             }
452         }
453       if (mode == ~0)
454         {
455           printf("Could not find suitable mode\n");
456           return -L4_EINVAL;
457         }
458       printf("Choosen mode:\n");
459       dump_mode(mode, get_mode_info(mode));
460       printf("To force a specific setting use a '-m <mode>' option.\n");
461     }
462   else if (!(*mode_info = get_mode_info(mode)))
463     {
464       printf("Mode %x not supported\n", mode);
465       printf("List of supported graphics modes:\n");
466       l4_uint16_t *mode_list = (l4_uint16_t *)far_to_addr((*ctrl_info)->video_mode);
467       for (; *mode_list != 0xffff; ++mode_list)
468         dump_mode(*mode_list, get_mode_info(*mode_list));
469
470       return -L4_EINVAL;
471     }
472
473   /* Switch mode. */
474   M.x86.R_EAX  = 0x4F02;        /* int10 function number */
475   M.x86.R_EBX  = mode & 0xf7ff; /* VESA mode; use current refresh rate */
476   M.x86.R_EBX |= (1<<14);       /* use flat buffer model */
477   if (0)
478     M.x86.R_EBX |= (1<<15);     /* no screen clearing */
479   M.x86.R_IP   = 0;             /* address of "int10; hlt" */
480   M.x86.R_SP   = L4_PAGESIZE;   /* SS:SP pointer to stack */
481   M.x86.R_CS   =
482   M.x86.R_DS   =
483   M.x86.R_ES   =
484   M.x86.R_SS   = L4_PAGESIZE >> 4;
485   X86EMU_exec();
486
487   printf("VBE mode 0x%x successfully set.\n", mode);
488   return 0;
489 }
490
491 int
492 x86emu_int10_pan(unsigned *x, unsigned *y)
493 {
494   int error;
495
496   if ((error = x86emu_int10_init()))
497     return error;
498
499   printf("Trying execution of ``set display start'' using x86emu\n");
500
501   /* Get VESA BIOS controller information. */
502   M.x86.R_EAX  = 0x4F07;        /* int10 function number */
503   M.x86.R_BL   = 0x00;          /* set display start */
504   M.x86.R_CX   = *x;            /* first displayed pixel in scanline */
505   M.x86.R_DX   = *y;            /* first displayed scanline */
506   M.x86.R_IP   = 0;             /* address of "int10; hlt" */
507   M.x86.R_SP   = L4_PAGESIZE;   /* SS:SP pointer to stack */
508   M.x86.R_CS   =
509   M.x86.R_DS   =
510   M.x86.R_ES   =
511   M.x86.R_SS   = L4_PAGESIZE >> 4;
512   X86EMU_exec();
513
514   if (M.x86.R_AX != 0x004F)
515     {
516       printf("Panning to %d,%d not supported\n", *x, *y);
517       return -L4_EINVAL;
518     }
519
520   printf("Successful.\n");
521   return 0;
522 }