VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 15 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use