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