]>
Commit | Line | Data |
---|---|---|
93afd047 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 | |
12 | typedef 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 | ||
34 | static 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 | ||
69 | typedef struct { | |
70 | unsigned eax, ebx, ecx, edx, esi, edi, eip, es, iret, cli; | |
71 | } bios32_regs_t; | |
72 | ||
73 | static void read_memory(hd_data_t *hd_data, memory_range_t *mem); | |
74 | static void dump_memory(hd_data_t *hd_data, memory_range_t *mem, int sparse, char *label); | |
75 | static void get_pnp_support_status(memory_range_t *mem, bios_info_t *bt); | |
76 | static void smbios_get_info(hd_data_t *hd_data, memory_range_t *mem, bios_info_t *bt); | |
77 | static void get_fsc_info(hd_data_t *hd_data, memory_range_t *mem, bios_info_t *bt); | |
78 | static void add_panel_info(hd_data_t *hd_data, bios_info_t *bt); | |
79 | static void add_mouse_info(hd_data_t *hd_data, bios_info_t *bt); | |
80 | static unsigned char crc(unsigned char *mem, unsigned len); | |
81 | static int get_smp_info(hd_data_t *hd_data, memory_range_t *mem, smp_info_t *smp); | |
82 | static void parse_mpconfig(hd_data_t *hd_data, memory_range_t *mem, smp_info_t *smp); | |
83 | static int get_bios32_info(hd_data_t *hd_data, memory_range_t *mem, bios32_info_t *bios32); | |
84 | ||
85 | int 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 | ||
130 | void 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 | ||
515 | void 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 | ||
543 | void 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 | ||
565 | void 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 | ||
587 | unsigned 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 | ||
597 | void 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 | ||
698 | void 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 | ||
777 | void 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 | ||
817 | void 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 | ||
875 | int 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 | ||
911 | void 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 | ||
984 | int 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 |