This commit was manufactured by cvs2svn to create tag
[opensuse:hwinfo.git] / src / hd / bios.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <byteswap.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #if defined(__i386__) || defined (__x86_64__) || defined(__ia64__)
10 #include <sys/io.h>
11 #endif
12 #include <sys/pci.h>
13
14 #include "hd.h"
15 #include "hd_int.h"
16 #include "bios.h"
17 #include "smbios.h"
18 #include "klog.h"
19
20 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
21  * bios info
22  *
23  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
24  */
25
26 #if defined(__i386__) || defined (__x86_64__)
27
28 #ifndef LIBHD_TINY
29 static struct {
30   int width;
31   int height;
32   char *vendor;
33   char *name;
34   char *version;
35 } panel_data[] = {
36   {  800,  600, "Fujitsu Siemens", "LiteLine", "LF6" },
37   { 1024,  768, "ASUSTEK", "L2000D", NULL },
38   { 1024,  768, "ASUSTeK Computer Inc.", "L8400C series Notebook PC", NULL },
39   { 1024,  768, "ASUSTeK Computer Inc.", "S5N", NULL },
40   { 1024,  768, "Acer", "TravelMate 720", NULL },
41   { 1024,  768, "COMPAL", "N30T5", NULL },
42   { 1024,  768, "Dell Computer Corporation", "Inspiron 5000", NULL },
43   { 1024,  768, "Dell Computer Corporation", "Latitude C400", NULL },
44   { 1024,  768, "Dell Computer Corporation", "Latitude CPt C400GT", NULL },
45   { 1024,  768, "Hewlett-Packard", "HP OmniBook PC", "HP OmniBook 4150 B" },
46   { 1024,  768, "IBM", "2373G1U", NULL },
47   { 1024,  768, "IBM", "26284UG", NULL },
48   { 1024,  768, "IBM", "26446AG", NULL },
49   { 1024,  768, "IBM", "26455BG", NULL },
50   { 1024,  768, "IBM", "264721G", NULL },
51   { 1024,  768, "KDST", "KDS6KSUMO", NULL  },
52   { 1024,  768, "Sony Corporation", "PCG-F370(UC)", NULL },
53   { 1024,  768, "Sony Corporation", "PCG-N505SN", NULL },
54   { 1024,  768, "TOSHIBA", "S2400-103", NULL },
55   { 1400, 1050, "Acer", "TravelMate 660", NULL },
56   { 1400, 1050, "Dell Computer Corporation", "Inspiron 8000", NULL },
57   { 1600, 1200, "Dell Computer Corporation", "Inspiron 8200", NULL },
58   { 1600, 1200, "Dell Computer Corporation", "Latitude C840", NULL }
59 };
60 #endif
61
62 #define BIOS_TEST
63
64 typedef struct {
65   unsigned eax, ebx, ecx, edx, esi, edi, eip, es, iret, cli;
66 } bios32_regs_t;
67
68 static void read_memory(memory_range_t *mem);
69 #ifndef LIBHD_TINY
70 static void dump_memory(hd_data_t *hd_data, memory_range_t *mem, int sparse, char *label);
71 static void get_pnp_support_status(memory_range_t *mem, bios_info_t *bt);
72 static void smbios_get_info(hd_data_t *hd_data, memory_range_t *mem, bios_info_t *bt);
73 static void get_fsc_info(hd_data_t *hd_data, memory_range_t *mem, bios_info_t *bt);
74 static void add_panel_info(hd_data_t *hd_data, bios_info_t *bt);
75 static void add_mouse_info(hd_data_t *hd_data, bios_info_t *bt);
76 static unsigned char crc(unsigned char *mem, unsigned len);
77 static int get_smp_info(hd_data_t *hd_data, memory_range_t *mem, smp_info_t *smp);
78 static void parse_mpconfig(hd_data_t *hd_data, memory_range_t *mem, smp_info_t *smp);
79 static int get_bios32_info(hd_data_t *hd_data, memory_range_t *mem, bios32_info_t *bios32);
80 #endif
81
82 int detect_smp_bios(hd_data_t *hd_data)
83 {
84   bios_info_t *bt;
85   hd_t *hd;
86
87   if(!hd_data->bios_ram.data) return -1;        /* hd_scan_bios() not called */
88
89   for(bt = NULL, hd = hd_data->hd; hd; hd = hd->next) {
90     if(
91       hd->base_class.id == bc_internal &&
92       hd->sub_class.id == sc_int_bios &&
93       hd->detail &&
94       hd->detail->type == hd_detail_bios &&
95       (bt = hd->detail->bios.data)
96     ) {
97       break;
98     }
99   }
100
101   if(!bt) return -1;
102
103 //  return bt->smp.ok ? bt->smp.cpus_en ? bt->smp.cpus_en : 1 : 0;
104 // Dell Dimension 8100 has a MP table with 0 cpus!
105
106   return bt->smp.ok ? bt->smp.cpus_en : 0;
107
108   return 0;
109 }
110
111
112 void hd_scan_bios(hd_data_t *hd_data)
113 {
114   hd_t *hd;
115   bios_info_t *bt;
116 #ifndef LIBHD_TINY
117   char *s;
118   unsigned char *bios_ram;
119   unsigned u, u1;
120   memory_range_t mem;
121   unsigned smp_ok;
122   vbe_info_t *vbe;
123   vbe_mode_info_t *mi;
124   hd_res_t *res;
125   str_list_t *sl;
126 #endif
127
128   if(!hd_probe_feature(hd_data, pr_bios)) return;
129
130   /* we better do nothing on a SGI Altix machine */
131   if(hd_is_sgi_altix(hd_data)) return;
132
133   hd_data->module = mod_bios;
134
135   /* some clean-up */
136   remove_hd_entries(hd_data);
137
138   PROGRESS(1, 0, "cmdline");
139
140   hd = add_hd_entry(hd_data, __LINE__, 0);
141   hd->base_class.id = bc_internal;
142   hd->sub_class.id = sc_int_bios;
143   hd->detail = new_mem(sizeof *hd->detail);
144   hd->detail->type = hd_detail_bios;
145   hd->detail->bios.data = bt = new_mem(sizeof *bt);
146
147 #ifndef LIBHD_TINY
148
149   /*
150    * first, look for APM support
151    */
152   if((s = get_cmd_param(hd_data, 1))) {
153     if(strlen(s) >= 10) {
154       bt->apm_supported = 1;
155       bt->apm_ver = hex(s, 1);
156       bt->apm_subver = hex(s + 1, 1);
157       bt->apm_bios_flags = hex(s + 2, 2);
158       /*
159        * Bitfields for APM flags (from Ralf Brown's list):
160        * Bit(s)  Description
161        *  0      16-bit protected mode interface supported
162        *  1      32-bit protected mode interface supported
163        *  2      CPU idle call reduces processor speed
164        *  3      BIOS power management disabled
165        *  4      BIOS power management disengaged (APM v1.1)
166        *  5-7    reserved
167        */
168       bt->apm_enabled = (bt->apm_bios_flags & 8) ? 0 : 1;
169
170       bt->vbe_ver = hex(s + 4, 2);
171       bt->vbe_ver = (((bt->vbe_ver >> 4) & 0xf) << 8) + (bt->vbe_ver & 0xf);
172       bt->vbe_video_mem = hex(s + 6, 4) << 16;
173     }
174
175     s = free_mem(s);
176   }
177
178   if((s = get_cmd_param(hd_data, 2))) {
179     if(strlen(s) > 8) {
180       if(s[8] == '.') bt->lba_support = 1;
181     }
182
183     s = free_mem(s);
184   }
185
186   PROGRESS(1, 1, "apm");
187
188   if(!bt->apm_ver) {
189     str_list_t *sl0, *sl;
190
191     sl0 = read_file(PROC_APM, 0, 0);
192     if(sl0) {
193       bt->apm_supported = 1;
194       bt->apm_enabled = 1;
195       ADD2LOG("----- %s -----\n", PROC_APM);
196       for(sl = sl0; sl; sl = sl->next) {
197         ADD2LOG("  %s", sl->str);
198       }
199       ADD2LOG("----- %s end -----\n", PROC_APM);
200     }
201     free_str_list(sl0);
202   }
203
204 #endif          /* !defined(LIBHD_TINY) */
205
206   /*
207    * get the i/o ports for the parallel & serial interfaces from the BIOS
208    * memory area starting at 0x40:0
209    */
210   PROGRESS(2, 0, "ram");
211
212   hd_data->bios_ram.start = BIOS_RAM_START;
213   hd_data->bios_ram.size = BIOS_RAM_SIZE;
214   read_memory(&hd_data->bios_ram);
215
216   hd_data->bios_rom.start = BIOS_ROM_START;
217   hd_data->bios_rom.size = BIOS_ROM_SIZE;
218   read_memory(&hd_data->bios_rom);
219
220 #ifndef LIBHD_TINY
221
222   if(hd_data->bios_ram.data) {
223     bios_ram = hd_data->bios_ram.data;
224
225     bt->ser_port0 = (bios_ram[1] << 8) + bios_ram[0];
226     bt->ser_port1 = (bios_ram[3] << 8) + bios_ram[2];
227     bt->ser_port2 = (bios_ram[5] << 8) + bios_ram[4];
228     bt->ser_port3 = (bios_ram[7] << 8) + bios_ram[6];
229
230     bt->par_port0 = (bios_ram[  9] << 8) + bios_ram[  8];
231     bt->par_port1 = (bios_ram[0xb] << 8) + bios_ram[0xa];
232     bt->par_port2 = (bios_ram[0xd] << 8) + bios_ram[0xc];
233
234     bt->led.scroll_lock = bios_ram[0x97] & 1;
235     bt->led.num_lock = (bios_ram[0x97] >> 1) & 1;
236     bt->led.caps_lock = (bios_ram[0x97] >> 2) & 1;
237     bt->led.ok = 1;
238
239     /*
240      * do some consistency checks:
241      *
242      * ports must be < 0x1000 and not appear twice
243      */
244     if(bt->ser_port0 >= 0x1000) bt->ser_port0 = 0;
245
246     if(
247       bt->ser_port1 >= 0x1000 ||
248       bt->ser_port1 == bt->ser_port0
249     ) bt->ser_port1 = 0;
250
251     if(
252       bt->ser_port2 >= 0x1000 ||
253       bt->ser_port2 == bt->ser_port0 ||
254       bt->ser_port2 == bt->ser_port1
255     ) bt->ser_port2 = 0;
256
257     if(
258       bt->ser_port3 >= 0x1000 ||
259       bt->ser_port3 == bt->ser_port0 ||
260       bt->ser_port3 == bt->ser_port1 ||
261       bt->ser_port3 == bt->ser_port2
262     ) bt->ser_port3 = 0;
263
264     if(bt->par_port0 >= 0x1000) bt->par_port0 = 0;
265
266     if(
267       bt->par_port1 >= 0x1000 ||
268       bt->par_port1 == bt->par_port0
269     ) bt->par_port1 = 0;
270
271     if(
272       bt->par_port2 >= 0x1000 ||
273       bt->par_port2 == bt->par_port0 ||
274       bt->par_port2 == bt->par_port1
275     ) bt->par_port2 = 0;
276
277     ADD2LOG("  bios: %u disks\n", bios_ram[0x75]);
278
279     bt->low_mem_size = ((bios_ram[0x14] << 8) + bios_ram[0x13]) << 10;
280
281     if(bt->low_mem_size >= 768 || bt->low_mem_size < 500) {
282       bt->low_mem_size = 0;
283     }
284
285     hd_data->bios_ebda.start = hd_data->bios_ebda.size = 0;
286     hd_data->bios_ebda.data = free_mem(hd_data->bios_ebda.data);
287     u = ((bios_ram[0x0f] << 8) + bios_ram[0x0e]) << 4;
288     if(u) {
289       hd_data->bios_ebda.start = u;
290       hd_data->bios_ebda.size = 1;      /* just one byte */
291       read_memory(&hd_data->bios_ebda);
292       if(hd_data->bios_ebda.data) {
293         u1 = hd_data->bios_ebda.data[0];
294         if(u1 > 0 && u1 <= 64) {        /* be sensible, typically only 1k */
295           u1 <<= 10;
296           if(u + u1 <= (1 << 20)) {
297             hd_data->bios_ebda.size = u1;
298             read_memory(&hd_data->bios_ebda);
299           }
300         }
301       }
302     }
303
304     if(hd_data->bios_ebda.data) {
305       ADD2LOG(
306         "  bios: EBDA 0x%05x bytes at 0x%05x\n",
307         hd_data->bios_ebda.size, hd_data->bios_ebda.start
308       );
309     }
310   }
311
312   /*
313    * read the bios rom and look for useful things there...
314    */
315   PROGRESS(2, 0, "rom");
316
317   if(hd_data->bios_rom.data) {
318     get_pnp_support_status(&hd_data->bios_rom, bt);
319     smbios_get_info(hd_data, &hd_data->bios_rom, bt);
320     get_fsc_info(hd_data, &hd_data->bios_rom, bt);
321     add_panel_info(hd_data, bt);
322     add_mouse_info(hd_data, bt);
323   }
324
325   PROGRESS(3, 0, "smp");
326
327   smp_ok = 0;
328
329   mem = hd_data->bios_ebda;
330   smp_ok = get_smp_info(hd_data, &mem, &bt->smp);
331
332   if(!smp_ok) {
333     mem = hd_data->bios_rom;
334     if(mem.data) {
335       mem.size -= 0xf0000 - mem.start;
336       mem.data += 0xf0000 - mem.start;
337       mem.start = 0xf0000;
338       if(mem.size < (1 << 20)) smp_ok = get_smp_info(hd_data, &mem, &bt->smp);
339     }
340   }
341
342   if(!smp_ok) {
343     mem.size = 1 << 10;
344     mem.start = 639 << 10;
345     mem.data = NULL;
346     read_memory(&mem);
347     if(mem.data) smp_ok = get_smp_info(hd_data, &mem, &bt->smp);
348     mem.data = free_mem(mem.data);
349   }
350
351   if(bt->smp.ok && bt->smp.mpconfig) {
352     mem.start = bt->smp.mpconfig;
353     mem.size = 1 << 16;
354     mem.data = NULL;
355     read_memory(&mem);
356     parse_mpconfig(hd_data, &mem, &bt->smp);
357     mem.data = free_mem(mem.data);
358   }
359   
360   if((hd_data->debug & HD_DEB_BIOS)) {
361     dump_memory(hd_data, &hd_data->bios_ram, 0, "BIOS data");
362     dump_memory(hd_data, &hd_data->bios_ebda, hd_data->bios_ebda.size <= (8 << 10) ? 0 : 1, "EBDA");
363     // dump_memory(hd_data, &hd_data->bios_rom, 1, "BIOS ROM");
364
365     if(bt->smp.ok && bt->smp.mpfp) {
366       mem.start = bt->smp.mpfp;
367       mem.size = 0x10;
368       mem.data = NULL;
369       read_memory(&mem);
370       dump_memory(hd_data, &mem, 0, "MP FP");
371       mem.data = free_mem(mem.data);
372     }
373
374     if(bt->smp.ok && bt->smp.mpconfig && bt->smp.mpconfig_size) {
375       mem.start = bt->smp.mpconfig;
376       mem.size = bt->smp.mpconfig_size;
377       mem.data = NULL;
378       read_memory(&mem);
379       dump_memory(hd_data, &mem, 0, "MP config table");
380       mem.data = free_mem(mem.data);
381     }
382   }
383
384   if(hd_probe_feature(hd_data, pr_bios_vesa)) {
385     PROGRESS(4, 0, "vbe");
386
387     vbe = &bt->vbe;
388     vbe->ok = 0;
389
390     if(!hd_data->klog) read_klog(hd_data);
391     for(sl = hd_data->klog; sl; sl = sl->next) {
392       if(sscanf(sl->str, "<6>PCI: Using configuration type %u", &u) == 1) {
393         hd_data->pci_config_type = u;
394         ADD2LOG("  klog: pci config type %u\n", hd_data->pci_config_type);
395       }
396     }
397
398     get_vbe_info(hd_data, vbe);
399
400     if(vbe->ok) {
401       bt->vbe_ver = vbe->version;
402     }
403
404     if(vbe->ok && vbe->fb_start) {
405       hd = add_hd_entry(hd_data, __LINE__, 0);
406       hd->base_class.id = bc_framebuffer;
407       hd->sub_class.id = sc_fb_vesa;
408
409       hd_set_hw_class(hd, hw_vbe);
410
411 #if 0
412       hd->detail = new_mem(sizeof *hd->detail);
413       hd->detail->type = hd_detail_bios;
414       hd->detail->bios.data = bt = new_mem(sizeof *bt);
415 #endif
416
417       hd->vendor.name = new_str(vbe->vendor_name);
418       hd->device.name = new_str(vbe->product_name);
419       hd->sub_vendor.name = new_str(vbe->oem_name);
420       hd->revision.name = new_str(vbe->product_revision);
421
422       res = add_res_entry(&hd->res, new_mem(sizeof *res));
423       res->phys_mem.type = res_phys_mem;
424       res->phys_mem.range = vbe->memory;
425
426       res = add_res_entry(&hd->res, new_mem(sizeof *res));
427       res->mem.type = res_mem;
428       res->mem.base = vbe->fb_start;
429       res->mem.range = vbe->memory;
430       res->mem.access = acc_rw;
431       res->mem.enabled = 1;
432
433       if(vbe->mode) {
434         for(u = 0; u < vbe->modes; u++) {
435           mi = vbe->mode + u;
436           if(
437             (mi->attributes & 1) &&     /* mode supported */
438             mi->fb_start &&
439             mi->pixel_size != -1u       /* text mode */
440           ) {
441             res = add_res_entry(&hd->res, new_mem(sizeof *res));
442             res->framebuffer.type = res_framebuffer;
443             res->framebuffer.width = mi->width;
444             res->framebuffer.bytes_p_line = mi->bytes_p_line;
445             res->framebuffer.height = mi->height;
446             res->framebuffer.colorbits = mi->pixel_size;
447             res->framebuffer.mode = mi->number + 0x200;
448           }
449         }
450       }
451
452 #if 0
453       if(
454         hd->vend_name &&
455         !strcmp(hd->vend_name, "Matrox") &&
456         hd->device.name &&
457         (
458           strstr(hd->dev_name, "G200") ||
459           strstr(hd->dev_name, "G400") ||
460           strstr(hd->dev_name, "G450")
461         )
462       ) {
463         hd->broken = 1;
464       }
465 #endif
466
467     }
468   }
469
470   PROGRESS(5, 0, "32");
471
472   mem = hd_data->bios_rom;
473   if(mem.data) {
474     mem.size -= 0xe0000 - mem.start;
475     mem.data += 0xe0000 - mem.start;
476     mem.start = 0xe0000;
477     if(mem.size < (1 << 20)) get_bios32_info(hd_data, &mem, &bt->bios32);
478   }
479
480   if(bt->bios32.ok) {
481     mem = hd_data->bios_rom;
482
483     if(
484       mem.start <= 0xfffea &&
485       mem.start + mem.size >= 0xfffea + 6 &&
486       !memcmp(mem.data + 0xfffea - mem.start, "COMPAQ", 6)
487     ) {
488       bt->bios32.compaq = 1;
489       ADD2LOG("  bios32: compaq machine\n");
490     }
491   }
492
493
494 #endif          /* !defined(LIBHD_TINY) */
495
496 }
497
498
499 void read_memory(memory_range_t *mem)
500 {
501   int fd;
502 #ifdef BIOS_TEST
503   char *s = getenv("LIBHD_MEM");
504 #endif
505
506 #ifdef LIBHD_MEMCHECK
507   {
508     if(libhd_log) fprintf(libhd_log, ">%p\n", CALLED_FROM(read_memory, mem));
509   }
510 #endif
511
512   if(mem->data) free_mem(mem->data);
513   mem->data = new_mem(mem->size);
514   fd = -1;
515   if(
516 #ifdef BIOS_TEST
517     (fd = open(s ? s : DEV_MEM, O_RDONLY)) >= 0 &&
518 #else
519     (fd = open(DEV_MEM, O_RDONLY)) >= 0 &&
520 #endif
521     lseek(fd, mem->start, SEEK_SET) >= 0
522   ) {
523     read(fd, mem->data, mem->size);
524   }
525
526   if(fd >= 0) close(fd);
527
528 #ifdef LIBHD_MEMCHECK
529   {
530     if(libhd_log) fprintf(libhd_log, "<%p\n", CALLED_FROM(read_memory, mem));
531   }
532 #endif
533 }
534
535
536 void dump_memory(hd_data_t *hd_data, memory_range_t *mem, int sparse, char *label)
537 {
538   unsigned u, step;
539
540   if(!mem->size || !mem->data) return;
541
542 #if 1
543   step = sparse ? 0x1000 : 0x10;
544 #else
545   step = 0x10;
546 #endif
547
548   ADD2LOG("----- %s 0x%05x - 0x%05x -----\n", label, mem->start, mem->start + mem->size - 1);
549   for(u = 0; u < mem->size; u += step) {
550     ADD2LOG("  %03x  ", u + mem->start);
551     hexdump(&hd_data->log, 1, 0x10, mem->data + u);
552     ADD2LOG("\n");
553   }
554   ADD2LOG("----- %s end -----\n", label);
555 }
556
557
558 #ifndef LIBHD_TINY
559 void get_pnp_support_status(memory_range_t *mem, bios_info_t *bt)
560 {
561   int i;
562   unsigned char pnp[4] = { '$', 'P', 'n', 'P' };
563   unsigned char *t;
564   unsigned l, cs;
565
566   if(!mem->data) return;
567
568   for(i = 0xf0000 - mem->start; (unsigned) i < mem->size; i += 0x10) {
569     t = mem->data + i;
570     if(t[0] == pnp[0] && t[1] == pnp[1] && t[2] == pnp[2] && t[3] == pnp[3]) {
571       for(l = cs = 0; l < t[5]; l++) { cs += t[l]; }
572       if((cs & 0xff) == 0) {            // checksum ok
573         bt->is_pnp_bios = 1;
574 //        printf("0x%x bytes at 0x%x, cs = 0x%x\n", t[5], i, cs);
575         bt->pnp_id = t[0x17] + (t[0x18] << 8) + (t[0x19] << 16) + (t[0x20] << 24);
576       }
577     }
578   }
579 }
580
581 unsigned char crc(unsigned char *mem, unsigned len)
582 {
583   unsigned char uc = 0;
584
585   while(len--) uc += *mem++;
586
587   return uc;
588 }
589
590
591 void smbios_get_info(hd_data_t *hd_data, memory_range_t *mem, bios_info_t *bt)
592 {
593   unsigned u, u1, u2, ok, hlen = 0, ofs;
594   unsigned addr = 0, len = 0, scnt;
595   unsigned structs = 0, type, slen;
596   char *s;
597   memory_range_t memory;
598   hd_smbios_t *sm;
599
600   if(!mem->data || mem->size < 0x100) return;
601
602   for(u = ok = 0; u <= mem->size - 0x100; u += 0x10) {
603     if(*(unsigned *) (mem->data + u) == 0x5f4d535f) {   /* "_SM_" */
604       hlen = mem->data[u + 5];
605       addr = *(unsigned *) (mem->data + u + 0x18);
606       len = *(unsigned short *) (mem->data + u + 0x16);
607       structs = *(unsigned short *) (mem->data + u + 0x1c);
608       if(hlen < 0x1e) continue;
609       ok = crc(mem->data + u, hlen) == 0 && addr < (1 << 20) && len;
610       if(ok) break;
611     }
612   }
613
614   if(!ok) return;
615
616   bt->smbios_ver = (mem->data[u + 6] << 8) + mem->data[u + 7];
617
618   hd_data->smbios = smbios_free(hd_data->smbios);
619
620   memory.start = mem->start + u;
621   memory.size = hlen;
622   memory.data = mem->data + u;
623   dump_memory(hd_data, &memory, 0, "SMBIOS Entry Point");
624
625   memory.start = addr;
626   memory.size = len;
627   memory.data = NULL;
628   read_memory(&memory);
629   if(len >= 0x4000) {
630     ADD2LOG(
631       "  SMBIOS Structure Table at 0x%05x (size 0x%x)\n",
632       addr, len
633     );
634   }
635   else {
636     dump_memory(hd_data, &memory, 0, "SMBIOS Structure Table");
637   }
638
639   for(type = 0, u = 0, ofs = 0; u < structs && ofs + 3 < len; u++) {
640     type = memory.data[ofs];
641     slen = memory.data[ofs + 1];
642     if(ofs + slen > len || slen < 4) break;
643     sm = smbios_add_entry(&hd_data->smbios, new_mem(sizeof *sm));
644     sm->any.type = type;
645     sm->any.data_len = slen;
646     sm->any.data = new_mem(slen);
647     memcpy(sm->any.data, memory.data + ofs, slen);
648     sm->any.handle = memory.data[ofs + 2] + (memory.data[ofs + 3] << 8);
649     ADD2LOG("  type 0x%02x [0x%04x]: ", type, sm->any.handle);
650     if(slen) hexdump(&hd_data->log, 0, slen, sm->any.data);
651     ADD2LOG("\n");
652     if(type == sm_end) break;
653     ofs += slen;
654     u1 = ofs;
655     u2 = 1;
656     scnt = 0;
657     while(ofs + 1 < len) {
658       if(!memory.data[ofs]) {
659         if(ofs > u1) {
660           s = canon_str(memory.data + u1, strlen(memory.data + u1));
661           add_str_list(&sm->any.strings, s);
662           scnt++;
663           if(*s) ADD2LOG("       str%d: \"%s\"\n", scnt, s);
664           free_mem(s);
665           u1 = ofs + 1;
666           u2++;
667         }
668         if(!memory.data[ofs + 1]) {
669           ofs += 2;
670           break;
671         }
672       }
673       ofs++;
674     }
675   }
676
677   if(u != structs) {
678     if(type == sm_end) {
679       ADD2LOG("  smbios: stopped at end tag\n");
680     }
681     else {
682       ADD2LOG("  smbios oops: only %d of %d structs found\n", u, structs);
683     }
684   }
685
686   memory.data = free_mem(memory.data);
687
688   smbios_parse(hd_data);
689 }
690
691
692 void get_fsc_info(hd_data_t *hd_data, memory_range_t *mem, bios_info_t *bt)
693 {
694   unsigned u, mtype, fsc_id;
695   unsigned x, y;
696   hd_smbios_t *sm;
697   char *vendor = NULL;
698
699   if(!mem->data || mem->size < 0x20) return;
700
701   for(sm = hd_data->smbios; sm; sm = sm->next) {
702     if(sm->any.type == sm_sysinfo) {
703       vendor = sm->sysinfo.manuf;
704       break;
705     }
706   }
707
708   vendor = vendor && !strcasecmp(vendor, "Fujitsu") ? "Fujitsu" : "Fujitsu Siemens";
709
710   for(u = 0; u <= mem->size - 0x20; u += 0x10) {
711     if(
712       *(unsigned *) (mem->data + u) == 0x696a7546 &&
713       *(unsigned *) (mem->data + u + 4) == 0x20757374
714     ) {
715       mtype = *(unsigned *) (mem->data + u + 0x14);
716       if(!crc(mem->data + u, 0x20) && !(mtype & 0xf0000000)) {
717         fsc_id = (mtype >> 12) & 0xf;
718
719         switch(fsc_id) {
720           case 1:
721             x = 640; y = 480;
722             break;
723
724           case 2:
725             x = 800; y = 600;
726             break;
727
728           case 3:
729             x = 1024; y = 768;
730             break;
731
732           case 4:
733             x = 1280; y = 1024;
734             break;
735
736           case 5:
737             x = 1400; y = 1050;
738             break;
739
740           case 6:
741             x = 1024; y = 512;
742             break;
743
744           case 7:
745             x = 1280; y = 600;
746             break;
747
748           case 8:
749             x = 1600; y = 1200;
750             break;
751
752           default:
753             x = 0; y = 0;
754         }
755
756         if(x) {
757           bt->lcd.vendor = new_str(vendor);
758           bt->lcd.name = new_str("Notebook LCD");
759           bt->lcd.width = x;
760           bt->lcd.height = y;
761         }
762
763         ADD2LOG("  found FSC LCD: %d (%ux%u)\n", fsc_id, x, y);
764         break;
765       }
766     }
767   }
768 }
769
770
771 void add_panel_info(hd_data_t *hd_data, bios_info_t *bt)
772 {
773   unsigned width, height;
774   char *vendor, *name, *version;
775   hd_smbios_t *sm;
776   unsigned u;
777
778   if(bt->lcd.width || !hd_data->smbios) return;
779
780   vendor = name = version = NULL;
781   width = height = 0;
782
783   for(sm = hd_data->smbios; sm; sm = sm->next) {
784     if(sm->any.type == sm_sysinfo) {
785       vendor = sm->sysinfo.manuf;
786       name = sm->sysinfo.product;
787       version = sm->sysinfo.version;
788       break;
789     }
790   }
791
792   if(!vendor || !name) return;
793
794   for(u = 0; u < sizeof panel_data / sizeof *panel_data; u++) {
795     if(
796       !strcmp(vendor, panel_data[u].vendor) &&
797       !strcmp(name, panel_data[u].name) &&
798       (version || !panel_data[u].version) &&
799       (!version || !panel_data[u].version || !strcmp(version, panel_data[u].version))
800     ) {
801       bt->lcd.vendor = new_str(vendor);
802       bt->lcd.name = new_str("Notebook LCD");
803       bt->lcd.width = panel_data[u].width;
804       bt->lcd.height = panel_data[u].height;
805       break;
806     }
807   }
808 }
809
810
811 void add_mouse_info(hd_data_t *hd_data, bios_info_t *bt)
812 {
813   unsigned compat_vend, compat_dev, bus;
814   char *vendor, *name, *type;
815   hd_smbios_t *sm;
816
817   if(bt->mouse.compat_vend || !hd_data->smbios) return;
818
819   vendor = name = type = NULL;
820   compat_vend = compat_dev = bus = 0;
821
822   for(sm = hd_data->smbios; sm; sm = sm->next) {
823     if(sm->any.type == sm_sysinfo) {
824       vendor = sm->sysinfo.manuf;
825       name = sm->sysinfo.product;
826     }
827     if(
828       sm->any.type == sm_mouse &&
829       !compat_vend      /* take the first entry */
830     ) {
831       compat_vend = compat_dev = bus = 0;
832       type = NULL;
833       
834       switch(sm->mouse.interface.id) {
835         case 4: /* ps/2 */
836         case 7: /* bus mouse (dell notebooks report this) */
837           bus = bus_ps2;
838           compat_vend = MAKE_ID(TAG_SPECIAL, 0x0200);
839           compat_dev = MAKE_ID(TAG_SPECIAL, sm->mouse.buttons == 3 ? 0x0007 : 0x0006);
840           break;
841       }
842       type = sm->mouse.mtype.name;
843       if(sm->mouse.mtype.id == 1) type = "Touch Pad";   /* Why??? */
844       if(sm->mouse.mtype.id == 2) type = NULL;          /* "Other" */
845     }
846   }
847
848   if(!vendor || !name) return;
849
850   if(!type) {
851     if(!strcmp(vendor, "Sony Corporation") && strstr(name, "PCG-") == name) {
852       bus = bus_ps2;
853       type = "Touch Pad";
854       compat_vend = MAKE_ID(TAG_SPECIAL, 0x0200);
855       compat_dev = MAKE_ID(TAG_SPECIAL, 0x0006);
856     }
857   }
858
859   if(!type) return;
860
861   bt->mouse.vendor = new_str(vendor);
862   bt->mouse.type = new_str(type);
863   bt->mouse.bus = bus;
864   bt->mouse.compat_vend = compat_vend;
865   bt->mouse.compat_dev = compat_dev;
866 }
867
868
869 int get_smp_info(hd_data_t *hd_data, memory_range_t *mem, smp_info_t *smp)
870 {
871 #ifndef __ia64__
872   unsigned u, ok;
873   unsigned addr = 0, len;
874
875   if(mem->size < 0x10) return 0;
876
877   for(u = ok = 0; u <= mem->size - 0x10; u++) {
878     if(*(unsigned *) (mem->data + u) == 0x5f504d5f) {   /* "_MP_" */
879       addr = *(unsigned *) (mem->data + u + 4);
880       len = mem->data[u + 8];
881       ok = len == 1 && crc(mem->data + u, 0x10) == 0 && addr < (1 << 20) ? 1 : 0;
882       ADD2LOG(
883         "  smp: %svalid MP FP at 0x%05x (size 0x%x, rev %u), MP config at 0x%05x\n",
884         ok ? "" : "in", u + mem->start, len << 4, mem->data[u + 9], addr
885       );
886       if(ok) break;
887     }
888   }
889
890   if(ok) {
891     smp->ok = 1;
892     smp->mpfp = mem->start + u;
893     smp->rev = mem->data[u + 9];
894     smp->mpconfig = addr;
895     memcpy(smp->feature, mem->data + u + 11, 5);
896   }
897
898   return ok;
899 #else
900   return 0;
901 #endif
902 }
903
904
905 void parse_mpconfig(hd_data_t *hd_data, memory_range_t *mem, smp_info_t *smp)
906 {
907   unsigned cfg_len, xcfg_len;
908   unsigned char u0, ux0;
909   unsigned u, type, len, entries, entry_cnt;
910   char *s;
911
912   cfg_len = xcfg_len = 0;
913
914   if(*(unsigned *) (mem->data) == 0x504d4350) {         /* "PCMP" */
915     cfg_len = mem->data[0x04] + (mem->data[0x05] << 8);
916     smp->mpconfig_size = cfg_len;
917     u0 = crc(mem->data, cfg_len);
918     if(u0) return;
919     smp->mpconfig_ok = 1;
920     smp->cpus = smp->cpus_en = 0;
921     xcfg_len = mem->data[0x28] + (mem->data[0x29] << 8);
922     ux0 = crc(mem->data + cfg_len, xcfg_len) + mem->data[0x2a];
923     if(!ux0) {
924       smp->mpconfig_size += xcfg_len;
925     }
926     else {
927       xcfg_len = 0;
928     }
929   }
930
931   if(cfg_len) {
932     s = canon_str(mem->data + 8, 8);
933     strcpy(smp->oem_id, s);
934     free_mem(s);
935     s = canon_str(mem->data + 0x10, 12);
936     strcpy(smp->prod_id, s);
937     s = free_mem(s);
938
939     entries = mem->data[0x22] + (mem->data[0x23] << 8);
940     ADD2LOG("  base MP config table (%u entries):\n", entries);
941     entry_cnt = 0;
942     for(u = 0x2c; u < cfg_len - 1; u += len, entry_cnt++) {
943       type = mem->data[u];
944       len = type == 0 ? 20 : type <= 4 ? 8 : 16;
945       ADD2LOG("  %stype %u, len %u\n    ", type > 4 ? "unknown ": "", type, len);
946       if(len + u > cfg_len) len = cfg_len - u;
947       hexdump(&hd_data->log, 1, len, mem->data + u);
948       ADD2LOG("\n");
949       if(type > 4) break;
950       if(type == 0) {
951         smp->cpus++;
952         if((mem->data[u + 3] & 1)) smp->cpus_en++;
953       }
954     }
955     if(entry_cnt != entries) {
956       ADD2LOG("  oops: %u entries instead of %u found\n", entry_cnt, entries);
957     }
958   }
959
960   if(xcfg_len) {
961     ADD2LOG("  extended MP config table:\n");
962     for(u = 0; u < xcfg_len - 2; u += len) {
963       type = mem->data[u + cfg_len];
964       len = mem->data[u + cfg_len + 1];
965       ADD2LOG("  type %u, len %u\n    ", type, len);
966       if(len + u > xcfg_len) len = xcfg_len - u;
967       hexdump(&hd_data->log, 1, len, mem->data + cfg_len + u);
968       ADD2LOG("\n");
969       if(len < 2) {
970         ADD2LOG("  oops: invalid record lenght\n");
971         break;
972       }
973     }
974   }
975 }
976
977
978 int get_bios32_info(hd_data_t *hd_data, memory_range_t *mem, bios32_info_t *bios32)
979 {
980   unsigned u, ok;
981   unsigned addr = 0, len;
982
983   if(mem->size < 0x10) return 0;
984
985   for(u = ok = 0; u <= mem->size - 0x10; u += 0x10) {
986     if(*(unsigned *) (mem->data + u) == 0x5f32335f) {   /* "_32_" */
987       addr = *(unsigned *) (mem->data + u + 4);
988       len = mem->data[u + 9];
989       ok = len == 1 && crc(mem->data + u, 0x10) == 0 && addr < (1 << 20) ? 1 : 0;
990       ADD2LOG(
991         "  bios32: %svalid SD header at 0x%05x (size 0x%x, rev %u), SD at 0x%05x\n",
992         ok ? "" : "in", u + mem->start, len << 4, mem->data[u + 8], addr
993       );
994       if(ok) break;
995     }
996   }
997
998   if(ok) {
999     bios32->ok = 1;
1000     bios32->entry = addr;
1001   }
1002
1003   return ok;
1004 }
1005
1006
1007 #endif          /* !defined(LIBHD_TINY) */
1008
1009
1010 #endif /* defined(__i386__) || defined (__x86_64__) */