VirtualBox

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

Last change on this file since 92288 was 92288, checked in by vboxsync, 3 years ago

BIOS: More INT 15 / E820h cleanup, reducing code size by 33 bytes. bugref:6549

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