VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/BIOS/system.c@ 71423

Last change on this file since 71423 was 71423, checked in by vboxsync, 7 years ago

BIOS: Reduce harmless warnings for unsupported INT 15h functions.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.0 KB
Line 
1/* $Id: system.c 71423 2018-03-21 11:24:32Z vboxsync $ */
2/** @file
3 * PC BIOS - ???
4 */
5
6/*
7 * Copyright (C) 2006-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 * --------------------------------------------------------------------
17 *
18 * This code is based on:
19 *
20 * ROM BIOS for use with Bochs/Plex86/QEMU emulation environment
21 *
22 * Copyright (C) 2002 MandrakeSoft S.A.
23 *
24 * MandrakeSoft S.A.
25 * 43, rue d'Aboukir
26 * 75002 Paris - France
27 * http://www.linux-mandrake.com/
28 * http://www.mandrakesoft.com/
29 *
30 * This library is free software; you can redistribute it and/or
31 * modify it under the terms of the GNU Lesser General Public
32 * License as published by the Free Software Foundation; either
33 * version 2 of the License, or (at your option) any later version.
34 *
35 * This library is distributed in the hope that it will be useful,
36 * but WITHOUT ANY WARRANTY; without even the implied warranty of
37 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
38 * Lesser General Public License for more details.
39 *
40 * You should have received a copy of the GNU Lesser General Public
41 * License along with this library; if not, write to the Free Software
42 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
43 *
44 */
45
46/*
47 * Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice
48 * other than GPL or LGPL is available it will apply instead, Oracle elects to use only
49 * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where
50 * a choice of LGPL license versions is made available with the language indicating
51 * that LGPLv2 or any later version may be used, or where a choice of which version
52 * of the LGPL is applied is otherwise unspecified.
53 */
54
55
56#include <stdint.h>
57#include "biosint.h"
58#include "inlines.h"
59
60#if DEBUG_INT15
61# define BX_DEBUG_INT15(...) BX_DEBUG(__VA_ARGS__)
62#else
63# define BX_DEBUG_INT15(...)
64#endif
65
66
67#define UNSUPPORTED_FUNCTION 0x86 /* Specific to INT 15h. */
68
69#define BIOS_CONFIG_TABLE 0xe6f5 /** @todo configurable? put elsewhere? */
70
71#define ACPI_DATA_SIZE 0x00010000L /** @todo configurable? put elsewhere? */
72
73#define BX_CPU 3
74
75extern int pmode_IDT;
76extern int rmode_IDT;
77
78uint16_t read_ss(void);
79#pragma aux read_ss = "mov ax, ss" modify exact [ax] nomemory;
80
81#if VBOX_BIOS_CPU >= 80386
82
83/* The 386+ code uses CR0 to switch to/from protected mode.
84 * Quite straightforward.
85 */
86
87void pm_stack_save(uint16_t cx, uint16_t es, uint16_t si, uint16_t frame);
88#pragma aux pm_stack_save = \
89 ".386" \
90 "push ds" \
91 "push eax" \
92 "xor eax, eax" \
93 "mov ds, ax" \
94 "mov ds:[467h], sp" \
95 "mov ds:[469h], ss" \
96 parm [cx] [es] [si] [ax] modify nomemory;
97
98/* Uses position independent code... because it was too hard to figure
99 * out how to code the far call in inline assembler.
100 */
101void pm_enter(void);
102#pragma aux pm_enter = \
103 ".386p" \
104 "call pentry" \
105 "pentry:" \
106 "pop ax" \
107 "add ax, 1Bh" \
108 "push 20h" \
109 "push ax" \
110 "lgdt fword ptr es:[si+8]" \
111 "lidt fword ptr cs:pmode_IDT" \
112 "mov eax, cr0" \
113 "or al, 1" \
114 "mov cr0, eax" \
115 "retf" \
116 "pm_pm:" \
117 "mov ax, 28h" \
118 "mov ss, ax" \
119 "mov ax, 10h" \
120 "mov ds, ax" \
121 "mov ax, 18h" \
122 "mov es, ax" \
123 modify nomemory;
124
125/* Restore segment limits to real mode compatible values and
126 * return to real mode.
127 */
128void pm_exit(void);
129#pragma aux pm_exit = \
130 ".386p" \
131 "call pexit" \
132 "pexit:" \
133 "pop ax" \
134 "push 0F000h" \
135 "add ax, 18h" \
136 "push ax" \
137 "mov ax, 28h" \
138 "mov ds, ax" \
139 "mov es, ax" \
140 "mov eax, cr0" \
141 "and al, 0FEh" \
142 "mov cr0, eax" \
143 "retf" \
144 "real_mode:" \
145 "lidt fword ptr cs:rmode_IDT" \
146 modify nomemory;
147
148/* Restore stack and reload segment registers in real mode to ensure
149 * real mode compatible selector+base.
150 */
151void pm_stack_restore(void);
152#pragma aux pm_stack_restore = \
153 ".386" \
154 "xor ax, ax" \
155 "mov ds, ax" \
156 "mov es, ax" \
157 "lss sp, ds:[467h]" \
158 "pop eax" \
159 "pop ds" \
160 modify nomemory;
161
162#elif VBOX_BIOS_CPU >= 80286
163
164/* The 286 code uses LMSW to switch to protected mode but it has to reset
165 * the CPU to get back to real mode. Ugly! See return_blkmove in orgs.asm
166 * for the other matching half.
167 */
168void pm_stack_save(uint16_t cx, uint16_t es, uint16_t si, uint16_t frame);
169#pragma aux pm_stack_save = \
170 "xor ax, ax" \
171 "mov ds, ax" \
172 "mov ds:[467h], bx" \
173 "mov ds:[469h], ss" \
174 parm [cx] [es] [si] [bx] modify nomemory;
175
176/* Uses position independent code... because it was too hard to figure
177 * out how to code the far call in inline assembler.
178 * NB: Trashes MSW bits but the CPU will be reset anyway.
179 */
180void pm_enter(void);
181#pragma aux pm_enter = \
182 ".286p" \
183 "call pentry" \
184 "pentry:" \
185 "pop di" \
186 "add di, 18h" \
187 "push 20h" \
188 "push di" \
189 "lgdt fword ptr es:[si+8]" \
190 "lidt fword ptr cs:pmode_IDT" \
191 "or al, 1" \
192 "lmsw ax" \
193 "retf" \
194 "pm_pm:" \
195 "mov ax, 28h" \
196 "mov ss, ax" \
197 "mov ax, 10h" \
198 "mov ds, ax" \
199 "mov ax, 18h" \
200 "mov es, ax" \
201 modify nomemory;
202
203/* Set up shutdown status and reset the CPU. The POST code
204 * will regain control. Port 80h is written with status.
205 * Code 9 is written to CMOS shutdown status byte (0Fh).
206 * CPU is triple faulted. .
207 */
208void pm_exit(void);
209#pragma aux pm_exit = \
210 "xor ax, ax" \
211 "out 80h, al" \
212 "mov al, 0Fh" \
213 "out 70h, al" \
214 "mov al, 09h" \
215 "out 71h, al" \
216 ".286p" \
217 "lidt fword ptr cs:pmode_IDT" \
218 "int 3" \
219 modify nomemory;
220
221/* Dummy. Actually done in return_blkmove. */
222void pm_stack_restore(void);
223#pragma aux pm_stack_restore = \
224 "rm_return:" \
225 modify nomemory;
226
227#endif
228
229void pm_copy(void);
230#pragma aux pm_copy = \
231 "xor si, si" \
232 "xor di, di" \
233 "cld" \
234 "rep movsw" \
235 modify [di] nomemory;
236
237/* The pm_switch has a few crucial differences from pm_enter, hence
238 * it is replicated here. Uses LMSW to avoid trashing high word of eax.
239 */
240void pm_switch(uint16_t reg_si);
241#pragma aux pm_switch = \
242 ".286p" \
243 "call pentry" \
244 "pentry:" \
245 "pop di" \
246 "add di, 18h" \
247 "push 38h" \
248 "push di" \
249 "lgdt fword ptr es:[si+08h]" \
250 "lidt fword ptr es:[si+10h]" \
251 "mov ax, 1" \
252 "lmsw ax" \
253 "retf" \
254 "pm_pm:" \
255 "mov ax, 28h" \
256 "mov ss, ax" \
257 "mov ax, 18h" \
258 "mov ds, ax" \
259 "mov ax, 20h" \
260 "mov es, ax" \
261 parm [si] modify nomemory;
262
263/* Return to caller - we do not use IRET because we should not enable
264 * interrupts. Note that AH must be zero on exit.
265 * WARNING: Needs to be adapted if calling sequence is modified!
266 */
267void pm_unwind(uint16_t args);
268#pragma aux pm_unwind = \
269 ".286" \
270 "mov sp, ax" \
271 "popa" \
272 "add sp, 6" \
273 "pop cx" \
274 "pop ax" \
275 "pop ax" \
276 "mov ax, 30h" \
277 "push ax" \
278 "push cx" \
279 "retf" \
280 parm [ax] modify nomemory aborts;
281
282/// @todo This method is silly. The RTC should be programmed to fire an interrupt
283// instead of hogging the CPU with inaccurate code.
284void timer_wait(uint32_t usec_wait)
285{
286 uint32_t cycles;
287 uint8_t old_val;
288 uint8_t cur_val;
289
290 /* We wait in 15 usec increments. */
291 cycles = usec_wait / 15;
292
293 old_val = inp(0x61) & 0x10;
294 while (cycles--) {
295 /* Wait 15us. */
296 do {
297 cur_val = inp(0x61) & 0x10;
298 } while (cur_val != old_val);
299 old_val = cur_val;
300 }
301}
302
303bx_bool set_enable_a20(bx_bool val)
304{
305 uint8_t oldval;
306
307 // Use PS/2 System Control port A to set A20 enable
308
309 // get current setting first
310 oldval = inb(0x92);
311
312 // change A20 status
313 if (val)
314 outb(0x92, oldval | 0x02);
315 else
316 outb(0x92, oldval & 0xfd);
317
318 return((oldval & 0x02) != 0);
319}
320
321typedef struct {
322 uint32_t start;
323 uint32_t xstart;
324 uint32_t len;
325 uint32_t xlen;
326 uint32_t type;
327} mem_range_t;
328
329void set_e820_range(uint16_t ES, uint16_t DI, uint32_t start, uint32_t end,
330 uint8_t extra_start, uint8_t extra_end, uint16_t type)
331{
332 mem_range_t __far *range;
333
334 range = ES :> (mem_range_t *)DI;
335 range->start = start;
336 range->xstart = extra_start;
337 end -= start;
338 extra_end -= extra_start;
339 range->len = end;
340 range->xlen = extra_end;
341 range->type = type;
342}
343
344/// @todo move elsewhere?
345#define AX r.gr.u.r16.ax
346#define BX r.gr.u.r16.bx
347#define CX r.gr.u.r16.cx
348#define DX r.gr.u.r16.dx
349#define SI r.gr.u.r16.si
350#define DI r.gr.u.r16.di
351#define BP r.gr.u.r16.bp
352#define SP r.gr.u.r16.sp
353#define FLAGS r.fl.u.r16.flags
354#define EAX r.gr.u.r32.eax
355#define EBX r.gr.u.r32.ebx
356#define ECX r.gr.u.r32.ecx
357#define EDX r.gr.u.r32.edx
358#define ESI r.gr.u.r32.esi
359#define EDI r.gr.u.r32.edi
360#define ES r.es
361
362
363void BIOSCALL int15_function(sys_regs_t r)
364{
365 uint16_t bRegister;
366 uint8_t irqDisable;
367
368 BX_DEBUG_INT15("int15 AX=%04x\n",AX);
369
370 switch (GET_AH()) {
371 case 0x00: /* assorted functions */
372 if (GET_AL() != 0xc0)
373 goto undecoded;
374 /* GRUB calls int15 with ax=0x00c0 to get the ROM configuration table,
375 * which we don't support, but logging that event is annoying. In fact
376 * it is likely that they just misread some specs, because there is a
377 * int15 BIOS function AH=0xc0 which sounds quite similar to what GRUB
378 * wants to achieve. */
379 SET_CF();
380 SET_AH(UNSUPPORTED_FUNCTION);
381 break;
382 case 0x24: /* A20 Control */
383 switch (GET_AL()) {
384 case 0x00:
385 set_enable_a20(0);
386 CLEAR_CF();
387 SET_AH(0);
388 break;
389 case 0x01:
390 set_enable_a20(1);
391 CLEAR_CF();
392 SET_AH(0);
393 break;
394 case 0x02:
395 SET_AL( (inb(0x92) >> 1) & 0x01 );
396 CLEAR_CF();
397 SET_AH(0);
398 break;
399 case 0x03:
400 CLEAR_CF();
401 SET_AH(0);
402 BX = 3;
403 break;
404 default:
405 BX_INFO("int15: Func 24h, subfunc %02xh, A20 gate control not supported\n", (unsigned) GET_AL());
406 SET_CF();
407 SET_AH(UNSUPPORTED_FUNCTION);
408 }
409 break;
410
411 /* These are here just to avoid warnings being logged. */
412 case 0x22: /* Locate ROM BASIC (tough when we don't have any.) */
413 case 0x41: /* PC Convertible, wait for external events. */
414 case 0xC7: /* PS/2, get memory map. */
415 SET_CF();
416 SET_AH(UNSUPPORTED_FUNCTION);
417 break;
418
419 /// @todo Why does this need special handling? All we need is to set CF
420 // but not handle this as an unknown function (regardless of CPU type).
421 case 0x4f:
422 /* keyboard intercept */
423#if BX_CPU < 2
424 SET_AH(UNSUPPORTED_FUNCTION);
425#else
426 // nop
427#endif
428 SET_CF();
429 break;
430
431 case 0x52: // removable media eject
432 CLEAR_CF();
433 SET_AH(0); // "ok ejection may proceed"
434 break;
435
436 case 0x83: {
437 if( GET_AL() == 0 ) {
438 // Set Interval requested.
439 if( ( read_byte( 0x40, 0xA0 ) & 1 ) == 0 ) {
440 // Interval not already set.
441 write_byte( 0x40, 0xA0, 1 ); // Set status byte.
442 write_word( 0x40, 0x98, ES ); // Byte location, segment
443 write_word( 0x40, 0x9A, BX ); // Byte location, offset
444 write_word( 0x40, 0x9C, DX ); // Low word, delay
445 write_word( 0x40, 0x9E, CX ); // High word, delay.
446 CLEAR_CF( );
447 irqDisable = inb( 0xA1 );
448 outb( 0xA1, irqDisable & 0xFE );
449 bRegister = inb_cmos( 0xB ); // Unmask IRQ8 so INT70 will get through.
450 outb_cmos( 0xB, bRegister | 0x40 ); // Turn on the Periodic Interrupt timer
451 } else {
452 // Interval already set.
453 BX_DEBUG_INT15("int15: Func 83h, failed, already waiting.\n" );
454 SET_CF();
455 SET_AH(UNSUPPORTED_FUNCTION);
456 }
457 } else if( GET_AL() == 1 ) {
458 // Clear Interval requested
459 write_byte( 0x40, 0xA0, 0 ); // Clear status byte
460 CLEAR_CF( );
461 bRegister = inb_cmos( 0xB );
462 outb_cmos( 0xB, bRegister & ~0x40 ); // Turn off the Periodic Interrupt timer
463 } else {
464 BX_DEBUG_INT15("int15: Func 83h, failed.\n" );
465 SET_CF();
466 SET_AH(UNSUPPORTED_FUNCTION);
467 SET_AL(GET_AL() - 1);
468 }
469
470 break;
471 }
472
473 case 0x88:
474 // Get the amount of extended memory (above 1M)
475#if BX_CPU < 2
476 SET_AH(UNSUPPORTED_FUNCTION);
477 SET_CF();
478#else
479 AX = (inb_cmos(0x31) << 8) | inb_cmos(0x30);
480
481 // According to Ralf Brown's interrupt the limit should be 15M,
482 // but real machines mostly return max. 63M.
483 if(AX > 0xffc0)
484 AX = 0xffc0;
485
486 CLEAR_CF();
487#endif
488 break;
489
490 case 0x89:
491 // Switch to Protected Mode.
492 // ES:DI points to user-supplied GDT
493 // BH/BL contains starting interrupt numbers for PIC0/PIC1
494 // This subfunction does not return!
495
496 // turn off interrupts
497 int_disable(); /// @todo aren't they off already?
498
499 set_enable_a20(1); // enable A20 line; we're supposed to fail if that fails
500
501 // Initialize CS descriptor for BIOS
502 write_word(ES, SI+0x38+0, 0xffff);// limit 15:00 = normal 64K limit
503 write_word(ES, SI+0x38+2, 0x0000);// base 15:00
504 write_byte(ES, SI+0x38+4, 0x000f);// base 23:16 (hardcoded to f000:0000)
505 write_byte(ES, SI+0x38+5, 0x9b); // access
506 write_word(ES, SI+0x38+6, 0x0000);// base 31:24/reserved/limit 19:16
507
508 /* Reprogram the PICs. */
509 outb(PIC_MASTER, PIC_CMD_INIT);
510 outb(PIC_SLAVE, PIC_CMD_INIT);
511 outb(PIC_MASTER + 1, GET_BH());
512 outb(PIC_SLAVE + 1, GET_BL());
513 outb(PIC_MASTER + 1, 4);
514 outb(PIC_SLAVE + 1, 2);
515 outb(PIC_MASTER + 1, 1);
516 outb(PIC_SLAVE + 1, 1);
517 /* Mask all IRQs, user must re-enable. */
518 outb(PIC_MASTER_MASK, 0xff);
519 outb(PIC_SLAVE_MASK, 0xff);
520
521 pm_switch(SI);
522 pm_unwind((uint16_t)&r);
523
524 break;
525
526 case 0x90:
527 /* Device busy interrupt. Called by Int 16h when no key available */
528 break;
529
530 case 0x91:
531 /* Interrupt complete. Called by Int 16h when key becomes available */
532 break;
533
534 case 0xbf:
535 BX_INFO("*** int 15h function AH=bf not yet supported!\n");
536 SET_CF();
537 SET_AH(UNSUPPORTED_FUNCTION);
538 break;
539
540 case 0xC0:
541 CLEAR_CF();
542 SET_AH(0);
543 BX = BIOS_CONFIG_TABLE;
544 ES = 0xF000;
545 break;
546
547 case 0xc1:
548 ES = read_word(0x0040, 0x000E);
549 CLEAR_CF();
550 break;
551
552 case 0xd8:
553 bios_printf(BIOS_PRINTF_DEBUG, "EISA BIOS not present\n");
554 SET_CF();
555 SET_AH(UNSUPPORTED_FUNCTION);
556 break;
557
558 /* Make the BIOS warning for pretty much every Linux kernel start
559 * disappear - it calls with ax=0xe980 to figure out SMI info. */
560 case 0xe9: /* SMI functions (SpeedStep and similar things) */
561 SET_CF();
562 SET_AH(UNSUPPORTED_FUNCTION);
563 break;
564 case 0xec: /* AMD64 target operating mode callback */
565 if (GET_AL() != 0)
566 goto undecoded;
567 SET_AH(0);
568 if (GET_BL() >= 1 && GET_BL() <= 3)
569 CLEAR_CF(); /* Accepted value. */
570 else
571 SET_CF(); /* Reserved, error. */
572 break;
573undecoded:
574 default:
575 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
576 (unsigned) AX, (unsigned) BX);
577 SET_CF();
578 SET_AH(UNSUPPORTED_FUNCTION);
579 break;
580 }
581}
582
583void BIOSCALL int15_function32(sys32_regs_t r)
584{
585 uint32_t extended_memory_size=0; // 64bits long
586 uint32_t extra_lowbits_memory_size=0;
587 uint8_t extra_highbits_memory_size=0;
588 uint32_t mcfgStart, mcfgSize;
589
590 BX_DEBUG_INT15("int15 AX=%04x\n",AX);
591
592 switch (GET_AH()) {
593 case 0x86:
594 // Wait for CX:DX microseconds. currently using the
595 // refresh request port 0x61 bit4, toggling every 15usec
596 int_enable();
597 timer_wait(((uint32_t)CX << 16) | DX);
598 break;
599
600 case 0xd0:
601 if (GET_AL() != 0x4f)
602 goto int15_unimplemented;
603 if (EBX == 0x50524f43 && ECX == 0x4d4f4445 && ESI == 0 && EDI == 0)
604 {
605 CLEAR_CF();
606 ESI = EBX;
607 EDI = ECX;
608 EAX = 0x49413332;
609 }
610 else
611 goto int15_unimplemented;
612 break;
613
614 case 0xe8:
615 switch(GET_AL()) {
616 case 0x20: // coded by osmaker aka K.J.
617 if(EDX == 0x534D4150) {
618 extended_memory_size = inb_cmos(0x35);
619 extended_memory_size <<= 8;
620 extended_memory_size |= inb_cmos(0x34);
621 extended_memory_size *= 64;
622#ifndef VBOX /* The following excludes 0xf0000000 thru 0xffffffff. Trust DevPcBios.cpp to get this right. */
623 // greater than EFF00000???
624 if(extended_memory_size > 0x3bc000) {
625 extended_memory_size = 0x3bc000; // everything after this is reserved memory until we get to 0x100000000
626 }
627#endif /* !VBOX */
628 extended_memory_size *= 1024;
629 extended_memory_size += (16L * 1024 * 1024);
630
631 if(extended_memory_size <= (16L * 1024 * 1024)) {
632 extended_memory_size = inb_cmos(0x31);
633 extended_memory_size <<= 8;
634 extended_memory_size |= inb_cmos(0x30);
635 extended_memory_size *= 1024;
636 extended_memory_size += (1L * 1024 * 1024);
637 }
638
639#ifdef VBOX /* We've already used the CMOS entries for SATA.
640 BTW. This is the amount of memory above 4GB measured in 64KB units. */
641 extra_lowbits_memory_size = inb_cmos(0x62);
642 extra_lowbits_memory_size <<= 8;
643 extra_lowbits_memory_size |= inb_cmos(0x61);
644 extra_lowbits_memory_size <<= 16;
645 extra_highbits_memory_size = inb_cmos(0x63);
646 /* 0x64 and 0x65 can be used if we need to dig 1 TB or more at a later point. */
647#else
648 extra_lowbits_memory_size = inb_cmos(0x5c);
649 extra_lowbits_memory_size <<= 8;
650 extra_lowbits_memory_size |= inb_cmos(0x5b);
651 extra_lowbits_memory_size *= 64;
652 extra_lowbits_memory_size *= 1024;
653 extra_highbits_memory_size = inb_cmos(0x5d);
654#endif /* !VBOX */
655
656 mcfgStart = 0;
657 mcfgSize = 0;
658
659 switch(BX)
660 {
661 case 0:
662 set_e820_range(ES, DI,
663#ifndef VBOX /** @todo Upstream suggests the following, needs checking. (see next as well) */
664 0x0000000L, 0x0009f000L, 0, 0, 1);
665#else
666 0x0000000L, 0x0009fc00L, 0, 0, 1);
667#endif
668 EBX = 1;
669 break;
670 case 1:
671 set_e820_range(ES, DI,
672#ifndef VBOX /** @todo Upstream suggests the following, needs checking. (see next as well) */
673 0x0009f000L, 0x000a0000L, 0, 0, 2);
674#else
675 0x0009fc00L, 0x000a0000L, 0, 0, 2);
676#endif
677 EBX = 2;
678 break;
679 case 2:
680#ifdef VBOX
681 /* Mark the BIOS as reserved. VBox doesn't currently
682 * use the 0xe0000-0xeffff area. It does use the
683 * 0xd0000-0xdffff area for the BIOS logo, but it's
684 * not worth marking it as reserved. (this is not
685 * true anymore because the VGA adapter handles the logo stuff)
686 * The whole 0xe0000-0xfffff can be used for the BIOS.
687 * Note that various
688 * Windows versions don't accept (read: in debug builds
689 * they trigger the "Too many similar traps" assertion)
690 * a single reserved range from 0xd0000 to 0xffffff.
691 * A 128K area starting from 0xd0000 works. */
692 set_e820_range(ES, DI,
693 0x000f0000L, 0x00100000L, 0, 0, 2);
694#else /* !VBOX */
695 set_e820_range(ES, DI,
696 0x000e8000L, 0x00100000L, 0, 0, 2);
697#endif /* !VBOX */
698 EBX = 3;
699 break;
700 case 3:
701#if BX_ROMBIOS32 || defined(VBOX)
702 set_e820_range(ES, DI,
703 0x00100000L,
704 extended_memory_size - ACPI_DATA_SIZE, 0, 0, 1);
705 EBX = 4;
706#else
707 set_e820_range(ES, DI,
708 0x00100000L,
709 extended_memory_size, 1);
710 EBX = 5;
711#endif
712 break;
713 case 4:
714 set_e820_range(ES, DI,
715 extended_memory_size - ACPI_DATA_SIZE,
716 extended_memory_size, 0, 0, 3); // ACPI RAM
717 EBX = 5;
718 break;
719 case 5:
720 set_e820_range(ES, DI,
721 0xfec00000,
722 0xfec00000 + 0x1000, 0, 0, 2); // I/O APIC
723 EBX = 6;
724 break;
725 case 6:
726 set_e820_range(ES, DI,
727 0xfee00000,
728 0xfee00000 + 0x1000, 0, 0, 2); // Local APIC
729 EBX = 7;
730 break;
731 case 7:
732 /* 256KB BIOS area at the end of 4 GB */
733#ifdef VBOX
734 /* We don't set the end to 1GB here and rely on the 32-bit
735 unsigned wrap around effect (0-0xfffc0000L). */
736#endif
737 set_e820_range(ES, DI,
738 0xfffc0000L, 0x00000000L, 0, 0, 2);
739 if (mcfgStart != 0)
740 EBX = 8;
741 else
742 {
743 if (extra_highbits_memory_size || extra_lowbits_memory_size)
744 EBX = 9;
745 else
746 EBX = 0;
747 }
748 break;
749 case 8:
750 /* PCI MMIO config space (MCFG) */
751 set_e820_range(ES, DI,
752 mcfgStart, mcfgStart + mcfgSize, 0, 0, 2);
753
754 if (extra_highbits_memory_size || extra_lowbits_memory_size)
755 EBX = 9;
756 else
757 EBX = 0;
758 break;
759 case 9:
760#ifdef VBOX /* Don't succeeded if no memory above 4 GB. */
761 /* Mapping of memory above 4 GB if present.
762 Note1: set_e820_range needs do no borrowing in the
763 subtraction because of the nice numbers.
764 Note2* works only up to 1TB because of uint8_t for
765 the upper bits!*/
766 if (extra_highbits_memory_size || extra_lowbits_memory_size)
767 {
768 set_e820_range(ES, DI,
769 0x00000000L, extra_lowbits_memory_size,
770 1 /*x4GB*/, extra_highbits_memory_size + 1 /*x4GB*/, 1);
771 EBX = 0;
772 }
773 break;
774 /* fall thru */
775#else /* !VBOX */
776 /* Mapping of memory above 4 GB */
777 set_e820_range(ES, DI, 0x00000000L,
778 extra_lowbits_memory_size, 1, extra_highbits_memory_size
779 + 1, 1);
780 EBX = 0;
781 break;
782#endif /* !VBOX */
783 default: /* AX=E820, DX=534D4150, BX unrecognized */
784 goto int15_unimplemented;
785 break;
786 }
787 EAX = 0x534D4150;
788 ECX = 0x14;
789 CLEAR_CF();
790 } else {
791 // if DX != 0x534D4150)
792 goto int15_unimplemented;
793 }
794 break;
795
796 case 0x01:
797 // do we have any reason to fail here ?
798 CLEAR_CF();
799
800 // my real system sets ax and bx to 0
801 // this is confirmed by Ralph Brown list
802 // but syslinux v1.48 is known to behave
803 // strangely if ax is set to 0
804 // regs.u.r16.ax = 0;
805 // regs.u.r16.bx = 0;
806
807 // Get the amount of extended memory (above 1M)
808 CX = (inb_cmos(0x31) << 8) | inb_cmos(0x30);
809
810 // limit to 15M
811 if(CX > 0x3c00)
812 CX = 0x3c00;
813
814 // Get the amount of extended memory above 16M in 64k blocks
815 DX = (inb_cmos(0x35) << 8) | inb_cmos(0x34);
816
817 // Set configured memory equal to extended memory
818 AX = CX;
819 BX = DX;
820 break;
821 default: /* AH=0xE8?? but not implemented */
822 goto int15_unimplemented;
823 }
824 break;
825 int15_unimplemented:
826 // fall into the default case
827 default:
828 BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
829 (unsigned) AX, (unsigned) BX);
830 SET_CF();
831 SET_AL(UNSUPPORTED_FUNCTION);
832 break;
833 }
834}
835
836#if VBOX_BIOS_CPU >= 80286
837
838#undef FLAGS
839#define FLAGS r.ra.flags.u.r16.flags
840
841/* Function 0x87 handled separately due to specific stack layout requirements. */
842void BIOSCALL int15_blkmove(disk_regs_t r)
843{
844 uint16_t base15_00;
845 uint8_t base23_16;
846 uint16_t ss;
847
848 // +++ should probably have descriptor checks
849 // +++ should have exception handlers
850
851 // turn off interrupts
852 int_disable(); /// @todo aren't they disabled already?
853
854 set_enable_a20(1); // enable A20 line
855
856 // 128K max of transfer on 386+ ???
857 // source == destination ???
858
859 // ES:SI points to descriptor table
860 // offset use initially comments
861 // ==============================================
862 // 00..07 Unused zeros Null descriptor
863 // 08..0f GDT zeros filled in by BIOS
864 // 10..17 source ssssssss source of data
865 // 18..1f dest dddddddd destination of data
866 // 20..27 CS zeros filled in by BIOS
867 // 28..2f SS zeros filled in by BIOS
868
869 //es:si
870 //eeee0
871 //0ssss
872 //-----
873
874 // check for access rights of source & dest here
875
876 // Initialize GDT descriptor
877 base15_00 = (ES << 4) + SI;
878 base23_16 = ES >> 12;
879 if (base15_00 < (ES<<4))
880 base23_16++;
881 write_word(ES, SI+0x08+0, 47); // limit 15:00 = 6 * 8bytes/descriptor
882 write_word(ES, SI+0x08+2, base15_00);// base 15:00
883 write_byte(ES, SI+0x08+4, base23_16);// base 23:16
884 write_byte(ES, SI+0x08+5, 0x93); // access
885 write_word(ES, SI+0x08+6, 0x0000); // base 31:24/reserved/limit 19:16
886
887 // Initialize CS descriptor
888 write_word(ES, SI+0x20+0, 0xffff);// limit 15:00 = normal 64K limit
889 write_word(ES, SI+0x20+2, 0x0000);// base 15:00
890 write_byte(ES, SI+0x20+4, 0x000f);// base 23:16
891 write_byte(ES, SI+0x20+5, 0x9b); // access
892 write_word(ES, SI+0x20+6, 0x0000);// base 31:24/reserved/limit 19:16
893
894 // Initialize SS descriptor
895 ss = read_ss();
896 base15_00 = ss << 4;
897 base23_16 = ss >> 12;
898 write_word(ES, SI+0x28+0, 0xffff); // limit 15:00 = normal 64K limit
899 write_word(ES, SI+0x28+2, base15_00);// base 15:00
900 write_byte(ES, SI+0x28+4, base23_16);// base 23:16
901 write_byte(ES, SI+0x28+5, 0x93); // access
902 write_word(ES, SI+0x28+6, 0x0000); // base 31:24/reserved/limit 19:16
903
904 pm_stack_save(CX, ES, SI, FP_OFF(&r));
905 pm_enter();
906 pm_copy();
907 pm_exit();
908 pm_stack_restore();
909
910 set_enable_a20(0); // unconditionally disable A20 line
911
912 // turn interrupts back on
913 int_enable();
914
915 SET_AH(0);
916 CLEAR_CF();
917}
918#endif
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette