- remove some obsolete code
[opensuse:hwinfo.git] / src / hd / mdt.c
1 #define _GNU_SOURCE
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <unistd.h>
6 #include <string.h>
7 #include <stdarg.h>
8 #include <getopt.h>
9 #include <inttypes.h>
10 #include <fcntl.h>
11 #include <errno.h>
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <sys/ioctl.h>
15 #include <sys/mman.h>
16 #include <sys/io.h>
17 #include <sys/time.h>
18 #include <x86emu.h>
19
20 #include "hd.h"
21 #include "hd_int.h"
22
23 #define STR_SIZE 128
24
25 #define VBIOS_ROM       0xc0000
26 #define VBIOS_ROM_SIZE  0x10000
27
28 #define VBIOS_MEM       0xa0000
29 #define VBIOS_MEM_SIZE  0x10000
30
31 #define VBE_BUF         0x8000
32
33 #define ADD_RES(w, h, f, i) \
34   res[res_cnt].width = w, \
35   res[res_cnt].height = h, \
36   res[res_cnt].vfreq = f, \
37   res[res_cnt++].il = i;
38
39 #define LPRINTF(a...) hd_log_printf(vm->hd_data, a)
40
41 typedef struct vm_s {
42   x86emu_t *emu;
43   unsigned char *video_mem;
44
45   unsigned ports;
46   unsigned verbose;
47   unsigned timeout;
48
49   unsigned all_modes:1;
50   unsigned mode;
51   unsigned mode_set:1;
52
53   unsigned trace_flags;
54   unsigned dump_flags;
55
56   hd_data_t *hd_data;
57 } vm_t;
58
59
60 static void flush_log(x86emu_t *emu, char *buf, unsigned size);
61
62 static void vm_write_byte(x86emu_t *emu, unsigned addr, unsigned val, unsigned perm);
63 static void vm_write_word(x86emu_t *emu, unsigned addr, unsigned val, unsigned perm);
64 // static void vm_write_dword(x86emu_t *emu, unsigned addr, unsigned val, unsigned perm);
65 static void copy_to_vm(x86emu_t *emu, unsigned dst, unsigned char *src, unsigned size, unsigned perm);
66 static void copy_from_vm(x86emu_t *emu, void *dst, unsigned src, unsigned len);
67
68 static int do_int(x86emu_t *emu, u8 num, unsigned type);
69 static vm_t *vm_new(void);
70 static void vm_free(vm_t *vm);
71 static unsigned vm_run(x86emu_t *emu, double *t);
72 static int vm_prepare(vm_t *vm);
73
74 static double get_time(void);
75 static void *map_mem(vm_t *vm, unsigned start, unsigned size);
76
77 void print_vbe_info(vm_t *vm, x86emu_t *emu, unsigned mode);
78
79 void probe_all(vm_t *vm, vbe_info_t *vbe);
80 void get_video_mode(vm_t *vm, vbe_info_t *vbe);
81 void list_modes(vm_t *vm, vbe_info_t *vbe);
82
83 void print_edid(int port, unsigned char *edid);
84 int chk_edid_info(unsigned char *edid);
85
86
87 void get_vbe_info(hd_data_t *hd_data, vbe_info_t *vbe)
88 {
89   int i, err;
90   char *t;
91   unsigned u, tbits, dbits;
92   str_list_t *sl;
93   vm_t *vm;
94
95   PROGRESS(4, 1, "vbe info");
96
97   vm = vm_new();
98   vm->hd_data = hd_data;
99   hd_data->vm = vm;
100
101   for(sl = get_probe_val_list(hd_data, pr_x86emu); sl && (t = sl->str); sl = sl->next) {
102     err = 0;
103     u = 1;
104     tbits = dbits = 0;
105     while(*t == '+' || *t == '-') u = *t++ == '+' ? 1 : 0;
106          if(!strcmp(t, "trace")) tbits = X86EMU_TRACE_DEFAULT;
107     else if(!strcmp(t, "code"))  tbits = X86EMU_TRACE_CODE;
108     else if(!strcmp(t, "regs"))  tbits = X86EMU_TRACE_REGS;
109     else if(!strcmp(t, "data"))  tbits = X86EMU_TRACE_DATA;
110     else if(!strcmp(t, "acc"))   tbits = X86EMU_TRACE_ACC;
111     else if(!strcmp(t, "io"))    tbits = X86EMU_TRACE_IO;
112     else if(!strcmp(t, "ints"))  tbits = X86EMU_TRACE_INTS;
113     else if(!strcmp(t, "time"))  tbits = X86EMU_TRACE_TIME;
114     else if(!strcmp(t, "dump"))         dbits = X86EMU_DUMP_DEFAULT;
115     else if(!strcmp(t, "dump.regs"))    dbits = X86EMU_DUMP_REGS;
116     else if(!strcmp(t, "dump.mem"))     dbits = X86EMU_DUMP_MEM;
117     else if(!strcmp(t, "dump.mem.acc")) dbits = X86EMU_DUMP_ACC_MEM;
118     else if(!strcmp(t, "dump.mem.inv")) dbits = X86EMU_DUMP_INV_MEM;
119     else if(!strcmp(t, "dump.attr"))    dbits = X86EMU_DUMP_ATTR;
120     else if(!strcmp(t, "dump.io"))      dbits = X86EMU_DUMP_IO;
121     else if(!strcmp(t, "dump.ints"))    dbits = X86EMU_DUMP_INTS;
122     else if(!strcmp(t, "dump.time"))    dbits = X86EMU_DUMP_TIME;
123     else err = 5;
124     if(err) {
125       ADD2LOG("x86emu: invalid flag '%s'\n", t);
126     }
127     else {
128       if(tbits) {
129         if(u) {
130           vm->trace_flags |= tbits;
131         }
132         else {
133           vm->trace_flags &= ~tbits;
134         }
135       }
136       if(dbits) {
137         if(u) {
138           vm->dump_flags |= dbits;
139         }
140         else {
141           vm->dump_flags &= ~dbits;
142         }
143       }
144     }
145   }
146
147   if(!vm_prepare(vm)) {
148     ADD2LOG("x86emu: could not init vm\n");
149
150     return;
151   }
152
153   vm->ports = 3;
154
155   i = get_probe_val_int(hd_data, pr_bios_ddc_ports);
156   if(i > sizeof vbe->ddc_port / sizeof *vbe->ddc_port) i = sizeof vbe->ddc_port / sizeof *vbe->ddc_port;
157   if(i) vm->ports = i;
158
159   if(hd_probe_feature(hd_data, pr_bios_fb)) {
160     PROGRESS(4, 2, "mode info");
161
162     list_modes(vm, vbe);
163   }
164
165   if(hd_probe_feature(hd_data, pr_bios_ddc)) {
166     PROGRESS(4, 3, "ddc info");
167
168     ADD2LOG("vbe: probing %d ports\n", vm->ports);
169
170     probe_all(vm, vbe);
171   }
172
173   if(hd_probe_feature(hd_data, pr_bios_mode)) {
174     PROGRESS(4, 4, "gfx mode");
175
176     get_video_mode(vm, vbe);
177   }
178
179   vm_free(vm);
180 }
181
182
183 void flush_log(x86emu_t *emu, char *buf, unsigned size)
184 {
185   vm_t *vm = emu->private;
186   hd_data_t *hd_data = vm->hd_data;
187
188   if(!buf || !size || !hd_data) return;
189
190   hd_log(hd_data, buf, size);
191 }
192
193
194 unsigned vm_read_segofs16(x86emu_t *emu, unsigned addr)
195 {
196   return x86emu_read_word(emu, addr) + (x86emu_read_word(emu, addr + 2) << 4);
197 }
198
199
200 void vm_write_byte(x86emu_t *emu, unsigned addr, unsigned val, unsigned perm)
201 {
202   x86emu_write_byte_noperm(emu, addr, val);
203   x86emu_set_perm(emu, addr, addr, perm | X86EMU_PERM_VALID);
204 }
205
206
207 void vm_write_word(x86emu_t *emu, unsigned addr, unsigned val, unsigned perm)
208 {
209   x86emu_write_byte_noperm(emu, addr, val);
210   x86emu_write_byte_noperm(emu, addr + 1, val >> 8);
211   x86emu_set_perm(emu, addr, addr + 1, perm | X86EMU_PERM_VALID);
212 }
213
214
215 #if 0
216 void vm_write_dword(x86emu_t *emu, unsigned addr, unsigned val, unsigned perm)
217 {
218   x86emu_write_byte_noperm(emu, addr, val);
219   x86emu_write_byte_noperm(emu, addr + 1, val >> 8);
220   x86emu_write_byte_noperm(emu, addr + 2, val >> 16);
221   x86emu_write_byte_noperm(emu, addr + 3, val >> 24);
222   x86emu_set_perm(emu, addr, addr + 3, perm | X86EMU_PERM_VALID);
223 }
224 #endif
225
226 void copy_to_vm(x86emu_t *emu, unsigned dst, unsigned char *src, unsigned size, unsigned perm)
227 {
228   if(!size) return;
229
230   while(size--) vm_write_byte(emu, dst++, *src++, perm);
231 }
232
233
234 void copy_from_vm(x86emu_t *emu, void *dst, unsigned src, unsigned len)
235 {
236   unsigned char *p = dst;
237   unsigned u;
238
239   for(u = 0; u < len; u++) {
240     p[u] = x86emu_read_byte_noperm(emu, src + u);
241   }
242 }
243
244
245 int do_int(x86emu_t *emu, u8 num, unsigned type)
246 {
247   if((type & 0xff) == INTR_TYPE_FAULT) {
248     x86emu_stop(emu);
249
250     return 0;
251   }
252
253   // ignore ints != 0x10
254   if(num != 0x10) return 1;
255
256   return 0;
257 }
258
259
260 vm_t *vm_new()
261 {
262   vm_t *vm;
263
264   vm = calloc(1, sizeof *vm);
265
266   vm->emu = x86emu_new(0, X86EMU_PERM_RW);
267   vm->emu->private = vm;
268
269   x86emu_set_log(vm->emu, 200000000, flush_log);
270   x86emu_set_intr_handler(vm->emu, do_int);
271
272   return vm;
273 }
274
275
276 void vm_free(vm_t *vm)
277 {
278   if(!vm) return;
279
280   x86emu_done(vm->emu);
281
282   free(vm);
283 }
284
285
286 unsigned vm_run(x86emu_t *emu, double *t)
287 {
288   vm_t *vm = emu->private;
289   unsigned err;
290
291   x86emu_log(emu, "=== emulation log ===\n");
292
293   *t = get_time();
294
295   x86emu_reset_access_stats(emu);
296
297   iopl(3);
298   err = x86emu_run(emu, X86EMU_RUN_LOOP | X86EMU_RUN_NO_CODE | X86EMU_RUN_TIMEOUT);
299   iopl(0);
300
301   *t = get_time() - *t;
302
303   if(vm->dump_flags) {
304     x86emu_log(emu, "\n; - - - final state\n");
305     x86emu_dump(emu, vm->dump_flags);
306     x86emu_log(emu, "; - - -\n");
307   }
308
309   x86emu_log(emu, "=== emulation log end ===\n");
310
311   x86emu_clear_log(emu, 1);
312
313   return err;
314 }
315
316
317 int vm_prepare(vm_t *vm)
318 {
319   int ok = 0;
320   unsigned u;
321   unsigned char *p1, *p2;
322
323   LPRINTF("=== bios setup ===\n");
324
325   p1 = map_mem(vm, 0, 0x1000);
326   if(!p1) {
327     LPRINTF("failed to read /dev/mem\n");
328     return ok;
329   }
330
331   copy_to_vm(vm->emu, 0x10*4, p1 + 0x10*4, 4, X86EMU_PERM_RW);
332   copy_to_vm(vm->emu, 0x400, p1 + 0x400, 0x100, X86EMU_PERM_RW);
333
334   munmap(p1, 0x1000);
335
336   p2 = map_mem(vm, VBIOS_ROM, VBIOS_ROM_SIZE);
337   if(!p2 || p2[0] != 0x55 || p2[1] != 0xaa || p2[2] == 0) {
338     if(p2) munmap(p2, VBIOS_ROM_SIZE);
339     LPRINTF("error: no video bios\n");
340     return ok;
341   }
342
343   copy_to_vm(vm->emu, VBIOS_ROM, p2, p2[2] * 0x200, X86EMU_PERM_RX);
344
345   munmap(p2, VBIOS_ROM_SIZE);
346
347   LPRINTF("video bios: size 0x%04x\n", x86emu_read_byte(vm->emu, VBIOS_ROM + 2) * 0x200);
348   LPRINTF("video bios: entry 0x%04x:0x%04x\n",
349     x86emu_read_word(vm->emu, 0x10*4 +  2),
350     x86emu_read_word(vm->emu, 0x10*4)
351   );
352
353   // video memory
354   vm->video_mem = map_mem(vm, VBIOS_MEM, VBIOS_MEM_SIZE);
355
356   if(vm->video_mem) {
357     x86emu_set_perm(vm->emu, VBIOS_MEM, VBIOS_MEM + VBIOS_MEM_SIZE - 1, X86EMU_PERM_RW);
358     for(u = 0; u < VBIOS_MEM_SIZE; u += X86EMU_PAGE_SIZE) {
359       x86emu_set_page(vm->emu, VBIOS_MEM + u, vm->video_mem + u);
360     }
361   }
362
363   // start address 0:0x7c00
364   x86emu_set_seg_register(vm->emu, vm->emu->x86.R_CS_SEL, 0);
365   vm->emu->x86.R_EIP = 0x7c00;
366
367   // int 0x10 ; hlt
368   vm_write_word(vm->emu, 0x7c00, 0x10cd, X86EMU_PERM_RX);
369   vm_write_byte(vm->emu, 0x7c02, 0xf4, X86EMU_PERM_RX);
370
371   // stack & buffer space
372   x86emu_set_perm(vm->emu, VBE_BUF, 0xffff, X86EMU_PERM_RW);
373
374   vm->emu->log.trace = vm->trace_flags;
375
376   if(vm->timeout) vm->emu->timeout = vm->timeout ?: 20;
377
378   ok = 1;
379
380   return ok;
381 }
382
383
384 double get_time()
385 {
386   static struct timeval t0 = { };
387   struct timeval t1 = { };
388
389   gettimeofday(&t1, NULL);
390
391   if(!timerisset(&t0)) t0 = t1;
392
393   timersub(&t1, &t0, &t1);
394
395   return t1.tv_sec + t1.tv_usec / 1e6;
396 }
397
398
399 void *map_mem(vm_t *vm, unsigned start, unsigned size)
400 {
401   int fd;
402   void *p;
403
404   if(!size) return NULL;
405
406   fd = open("/dev/mem", O_RDONLY);
407
408   if(fd == -1) return NULL;
409
410   p = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, start);
411
412   if(p == MAP_FAILED) {
413     LPRINTF("error: [0x%x, %u]: mmap failed: %s\n", start, size, strerror(errno));
414     close(fd);
415
416     return NULL;
417   }
418
419   LPRINTF("[0x%x, %u]: mmap ok\n", start, size);
420
421   close(fd);
422
423   return p;
424 }
425
426
427 void list_modes(vm_t *vm, vbe_info_t *vbe)
428 {
429   x86emu_t *emu = NULL;
430   int err = 0, i;
431   double d1, d2, timeout;
432   unsigned char buf2[0x100], tmp[0x100];
433   unsigned u, ml;
434   unsigned modelist[0x100];
435   unsigned bpp, fb, clock;
436   int res_bpp;
437   vbe_mode_info_t *mi;
438   char s[64];
439
440   LPRINTF("=== running bios\n");
441
442   timeout = get_time() + (vm->timeout ?: 20);
443
444   emu = x86emu_clone(vm->emu);
445
446   emu->x86.R_EAX = 0x4f00;
447   emu->x86.R_EBX = 0;
448   emu->x86.R_ECX = 0;
449   emu->x86.R_EDX = 0;
450   emu->x86.R_EDI = VBE_BUF;
451
452   x86emu_write_dword(emu, VBE_BUF, 0x32454256);         // "VBE2"
453
454   err = vm_run(emu, &d1);
455
456   LPRINTF("=== vbe get info: %s (time %.3fs, eax 0x%x, err = 0x%x)\n",
457     emu->x86.R_AX == 0x4f ? "ok" : "failed",
458     d1,
459     emu->x86.R_EAX,
460     err
461   );
462
463   if(!err && emu->x86.R_AX == 0x4f) {
464     LPRINTF("=== vbe info\n");
465
466     vbe->ok = 1;
467
468     vbe->version = x86emu_read_word(emu, VBE_BUF + 0x04);
469     vbe->oem_version = x86emu_read_word(emu, VBE_BUF + 0x14);
470     vbe->memory = x86emu_read_word(emu, VBE_BUF + 0x12) << 16;
471
472     LPRINTF(
473       "version = %u.%u, oem version = %u.%u\n",
474       vbe->version >> 8, vbe->version & 0xff, vbe->oem_version >> 8, vbe->oem_version & 0xff
475     );
476
477     LPRINTF("memory = %uk\n", vbe->memory >> 10);
478
479     buf2[sizeof buf2 - 1] = 0;
480
481     u = vm_read_segofs16(emu, VBE_BUF + 0x06);
482     copy_from_vm(emu, buf2, u, sizeof buf2 - 1);
483     vbe->oem_name = canon_str(buf2, strlen(buf2));
484     LPRINTF("oem name [0x%05x] = \"%s\"\n", u, vbe->oem_name);
485
486     u = vm_read_segofs16(emu, VBE_BUF + 0x16);
487     copy_from_vm(emu, buf2, u, sizeof buf2 - 1);
488     vbe->vendor_name = canon_str(buf2, strlen(buf2));
489     LPRINTF("vendor name [0x%05x] = \"%s\"\n", u, vbe->vendor_name);
490
491     u = vm_read_segofs16(emu, VBE_BUF + 0x1a);
492     copy_from_vm(emu, buf2, u, sizeof buf2 - 1);
493     vbe->product_name = canon_str(buf2, strlen(buf2));
494     LPRINTF("product name [0x%05x] = \"%s\"\n", u, vbe->product_name);
495
496     u = vm_read_segofs16(emu, VBE_BUF + 0x1e);
497     copy_from_vm(emu, buf2, u, sizeof buf2 - 1);
498     vbe->product_revision = canon_str(buf2, strlen(buf2));
499     LPRINTF("product revision [0x%05x] = \"%s\"\n", u, vbe->product_revision);
500
501     ml = vm_read_segofs16(emu, VBE_BUF + 0x0e);
502
503     for(vbe->modes = 0; vbe->modes < sizeof modelist / sizeof *modelist; ) {
504       u = x86emu_read_word(emu, ml + 2 * vbe->modes);
505       if(u == 0xffff) break;
506       modelist[vbe->modes++] = u;
507     }
508
509     LPRINTF("%u video modes:\n", vbe->modes);
510
511     vbe->mode = new_mem(vbe->modes * sizeof *vbe->mode);
512
513     for(i = 0; i < vbe->modes; i++) {
514
515       mi = vbe->mode + i;
516
517       mi->number = modelist[i];
518       
519       emu = x86emu_done(emu);
520       emu = x86emu_clone(vm->emu);
521
522       emu->x86.R_EAX = 0x4f01;
523       emu->x86.R_EBX = 0;
524       emu->x86.R_ECX = modelist[i];
525       emu->x86.R_EDI = VBE_BUF;
526
527       err = vm_run(emu, &d1);
528       d2 += d1;
529
530       LPRINTF("=== vbe mode info [0x%04x]: %s (time %.3fs, eax 0x%x, err = 0x%x)\n",
531         modelist[i],
532         emu->x86.R_AX == 0x4f ? "ok" : "failed",
533         d1,
534         emu->x86.R_EAX,
535         err
536       );
537
538       if(err || emu->x86.R_AX != 0x4f) continue;
539
540       copy_from_vm(emu, tmp, VBE_BUF, sizeof tmp);
541
542       mi->attributes = tmp[0x00] + (tmp[0x01] << 8);
543
544       mi->width = tmp[0x12] + (tmp[0x13] << 8);
545       mi->height = tmp[0x14] + (tmp[0x15] << 8);
546       mi->bytes_p_line = tmp[0x10] + (tmp[0x11] << 8);
547
548       mi->win_A_start = (tmp[0x08] + (tmp[0x09] << 8)) << 4;
549       mi->win_B_start = (tmp[0x0a] + (tmp[0x0b] << 8)) << 4;
550
551       mi->win_A_attr = tmp[0x02];
552       mi->win_B_attr = tmp[0x03];
553
554       mi->win_gran = (tmp[0x04] + (tmp[0x05] << 8)) << 10;
555       mi->win_size = (tmp[0x06] + (tmp[0x07] << 8)) << 10;
556
557       bpp = res_bpp = 0;
558       switch(tmp[0x1b]) {
559         case 0:
560           bpp = -1;
561           break;
562
563         case 1:
564           bpp = 2;
565           break;
566
567         case 2:
568           bpp = 1;
569           break;
570
571         case 3:
572           bpp = 4;
573           break;
574
575         case 4:
576           bpp = 8;
577           break;
578
579         case 6:
580           bpp = tmp[0x1f] + tmp[0x21] + tmp[0x23];
581           res_bpp = tmp[0x19] - bpp;
582           if(res_bpp < 0) res_bpp = 0;
583       }
584
585       fb = 0;
586       if(vbe->version >= 0x0200) {
587         mi->fb_start = tmp[0x28] + (tmp[0x29] << 8) + (tmp[0x2a] << 16) + (tmp[0x2b] << 24);
588       }
589
590       clock = 0;
591       if(vbe->version >= 0x0300) {
592         mi->pixel_clock = tmp[0x3e] + (tmp[0x3f] << 8) + (tmp[0x40] << 16) + (tmp[0x41] << 24);
593       }
594
595       mi->pixel_size = bpp;
596
597       if(bpp == -1u) {
598         LPRINTF("  0x%04x[%02x]: %ux%u, text\n", mi->number, mi->attributes, mi->width, mi->height);
599       }
600       else {
601         if(
602           (mi->attributes & 1) &&               /* mode is supported */
603           mi->fb_start
604         ) {
605           if(!vbe->fb_start) vbe->fb_start = mi->fb_start;
606         }
607         *s = 0;
608         if(res_bpp) sprintf(s, "+%d", res_bpp);
609         LPRINTF(
610           "  0x%04x[%02x]: %ux%u+%u, %u%s bpp",
611           mi->number, mi->attributes, mi->width, mi->height, mi->bytes_p_line, mi->pixel_size, s
612         );
613
614         if(mi->pixel_clock) LPRINTF(", max. %u MHz", mi->pixel_clock/1000000);
615
616         if(mi->fb_start) LPRINTF(", fb: 0x%08x", mi->fb_start);
617
618         LPRINTF(", %04x.%x", mi->win_A_start, mi->win_A_attr);
619
620         if(mi->win_B_start || mi->win_B_attr) LPRINTF("/%04x.%x", mi->win_B_start, mi->win_B_attr);
621
622         LPRINTF(": %uk", mi->win_size >> 10);
623
624         if(mi->win_gran != mi->win_size) LPRINTF("/%uk", mi->win_gran >> 10);
625
626         LPRINTF("\n");
627       }
628     }
629   }
630   else {
631     LPRINTF("=== no vbe info\n");
632   }
633
634   x86emu_done(emu);
635 }
636
637
638
639 void probe_all(vm_t *vm, vbe_info_t *vbe)
640 {
641   x86emu_t *emu = NULL;
642   int err = 0, i;
643   unsigned port, cnt;
644   double d1, d2, timeout;
645   unsigned char edid[0x80];
646
647   LPRINTF("=== running bios\n");
648
649   timeout = get_time() + (vm->timeout ?: 20);
650
651   for(port = 0; port < vm->ports; port++) {
652     d1 = d2 = 0;
653
654     for(cnt = 0; cnt < 2 && get_time() <= timeout; cnt++) {
655       emu = x86emu_done(emu);
656       emu = x86emu_clone(vm->emu);
657
658       emu->x86.R_EAX = 0x4f15;
659       emu->x86.R_EBX = 1;
660       emu->x86.R_ECX = port;
661       emu->x86.R_EDX = 0;
662       emu->x86.R_EDI = VBE_BUF;
663
664       err = vm_run(emu, &d1);
665       d2 += d1;
666
667       LPRINTF("=== port %u, try %u: %s (time %.3fs, eax 0x%x, err = 0x%x)\n",
668         port,
669         cnt,
670         emu->x86.R_AX == 0x4f ? "ok" : "failed",
671         d1,
672         emu->x86.R_EAX,
673         err
674       );
675
676       if(err || emu->x86.R_AX == 0x4f) break;
677     }
678
679     if(!emu) {
680       LPRINTF("=== timeout\n");
681       break;
682     }
683
684     LPRINTF("=== port %u: %s (time %.3fs, eax 0x%x, err = 0x%x)\n",
685       port,
686       emu->x86.R_AX == 0x4f ? "ok" : "failed",
687       d2,
688       emu->x86.R_EAX,
689       err
690     );
691
692     copy_from_vm(emu, edid, VBE_BUF, sizeof edid);
693
694     LPRINTF("=== port %u: ddc data ===\n", port);
695     for(i = 0; i < 0x80; i++) {
696       LPRINTF("%02x", edid[i]);
697       LPRINTF((i & 15) == 15 ? "\n" : " ");
698     }
699     LPRINTF("=== port %u: ddc data end ===\n", port);
700
701     if(!err && emu->x86.R_AX == 0x4f) {
702       LPRINTF("=== port %u: monitor info ok\n", port);
703
704       vbe->ok = 1;
705       vbe->ddc_ports = port + 1;
706
707       memcpy(vbe->ddc_port[port], edid, sizeof *vbe->ddc_port);
708     }
709     else {
710       if(!err) err = -1;
711       LPRINTF("=== port %u: no monitor info\n", port);
712     }
713
714     emu = x86emu_done(emu);
715   }
716 }
717
718
719 void get_video_mode(vm_t *vm, vbe_info_t *vbe)
720 {
721   x86emu_t *emu = NULL;
722   int err = 0;
723   double d, timeout;
724
725   LPRINTF("=== running bios\n");
726
727   timeout = get_time() + (vm->timeout ?: 20);
728
729   emu = x86emu_clone(vm->emu);
730
731   emu->x86.R_EAX = 0x4f03;
732   emu->x86.R_EBX = 0;
733
734   err = vm_run(emu, &d);
735
736   LPRINTF("=== vbe get current video mode: %s (time %.3fs, eax 0x%x, err = 0x%x)\n",
737     emu->x86.R_AX == 0x4f ? "ok" : "failed",
738     d,
739     emu->x86.R_EAX,
740     err
741   );
742
743   if(!err && emu->x86.R_AX == 0x4f) {
744     vbe->ok = 1;
745     vbe->current_mode = emu->x86.R_BX;
746
747     LPRINTF("=== current mode: 0x%04x\n", vbe->current_mode);
748   }
749   else {
750     LPRINTF("=== current mode: no info\n");
751   }
752
753   x86emu_done(emu);
754 }
755
756