VirtualBox

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

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

BIOS: Fixed INT 15h/E820h reporting of memory above 1TiB, simplified code a little to save space (29 bytes). bugref:10093

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

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