VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 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
RevLine 
[69501]1/* $Id: system.c 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * PC BIOS - ???
4 */
5
[38699]6/*
[98103]7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
[38699]8 *
[96407]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
[69498]26 * --------------------------------------------------------------------
[38699]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
[69501]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 */
[38699]64
[69501]65
[92288]66#include <iprt/cdefs.h>
[38699]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
[63562]80#define BIOS_CONFIG_TABLE 0xe6f5 /** @todo configurable? put elsewhere? */
[38699]81
[63562]82#define ACPI_DATA_SIZE 0x00010000L /** @todo configurable? put elsewhere? */
[38699]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
[60610]90#if VBOX_BIOS_CPU >= 80386
91
92/* The 386+ code uses CR0 to switch to/from protected mode.
93 * Quite straightforward.
94 */
95
[74612]96void pm_stack_save(uint16_t cx, uint16_t es, uint16_t si);
[38699]97#pragma aux pm_stack_save = \
98 ".386" \
99 "push ds" \
100 "push eax" \
[74612]101 "xor ax, ax" \
[38699]102 "mov ds, ax" \
103 "mov ds:[467h], sp" \
104 "mov ds:[469h], ss" \
[74612]105 parm [cx] [es] [si] modify nomemory;
[38699]106
[74612]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.
[38699]114 */
115void pm_enter(void);
116#pragma aux pm_enter = \
117 ".386p" \
[74612]118 "lgdt fword ptr es:[si+8]" \
119 "lidt fword ptr cs:pmode_IDT" \
120 "push 20h" \
[38699]121 "call pentry" \
122 "pentry:" \
[71422]123 "pop ax" \
[74612]124 "add ax, 0Eh" \
[71422]125 "push ax" \
[38699]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" \
[74612]133 "add al, 08h" \
[38699]134 "mov es, ax" \
[74612]135 "add al, 10h" \
136 "mov ss, ax" \
[38699]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" \
[74612]145 "mov ax, 28h" \
146 "mov ds, ax" \
147 "mov es, ax" \
148 "push 0F000h" \
[38699]149 "call pexit" \
150 "pexit:" \
151 "pop ax" \
[74612]152 "add ax, 0Eh" \
[38699]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
[41759]162/* Restore stack and reload segment registers in real mode to ensure
163 * real mode compatible selector+base.
164 */
[38699]165void pm_stack_restore(void);
166#pragma aux pm_stack_restore = \
167 ".386" \
168 "xor ax, ax" \
169 "mov ds, ax" \
[41759]170 "mov es, ax" \
[38699]171 "lss sp, ds:[467h]" \
172 "pop eax" \
173 "pop ds" \
174 modify nomemory;
175
[60610]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" \
[74612]196 "lgdt fword ptr es:[si+8]" \
197 "lidt fword ptr cs:pmode_IDT" \
198 "push 20h" \
[60610]199 "call pentry" \
200 "pentry:" \
[74612]201 "pop ax" \
202 "add ax, 0Eh" \
203 "push ax" \
204 "smsw ax" \
[60610]205 "or al, 1" \
206 "lmsw ax" \
207 "retf" \
208 "pm_pm:" \
209 "mov ax, 10h" \
210 "mov ds, ax" \
[74612]211 "add al, 08h" \
[60610]212 "mov es, ax" \
[74612]213 "add al, 10h" \
214 "mov ss, ax" \
[60610]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
[74612]243/* NB: CX is set earlier in pm_stack_save */
[60610]244void pm_copy(void);
245#pragma aux pm_copy = \
246 "xor si, si" \
247 "xor di, di" \
248 "cld" \
249 "rep movsw" \
[74612]250 modify [si di cx] nomemory;
[60610]251
[38699]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" \
[74612]258 "lgdt fword ptr es:[si+08h]" \
259 "lidt fword ptr es:[si+10h]" \
260 "push 38h" \
[38699]261 "call pentry" \
262 "pentry:" \
[74612]263 "pop ax" \
264 "add ax, 0Eh" \
265 "push ax" \
266 "smsw ax" \
267 "or al, 1" \
[38699]268 "lmsw ax" \
269 "retf" \
270 "pm_pm:" \
271 "mov ax, 18h" \
272 "mov ds, ax" \
[74612]273 "add al, 08h" \
[38699]274 "mov es, ax" \
[74612]275 "add al, 08h" \
276 "mov ss, ax" \
[38699]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;
[45669]297
[38699]298bx_bool set_enable_a20(bx_bool val)
299{
300 uint8_t oldval;
[45669]301
[38699]302 // Use PS/2 System Control port A to set A20 enable
[45669]303
[38699]304 // get current setting first
305 oldval = inb(0x92);
[45669]306
[38699]307 // change A20 status
308 if (val)
309 outb(0x92, oldval | 0x02);
310 else
311 outb(0x92, oldval & 0xfd);
[45669]312
[38699]313 return((oldval & 0x02) != 0);
314}
315
[63562]316/// @todo move elsewhere?
[38699]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
[45669]330#define ESI r.gr.u.r32.esi
331#define EDI r.gr.u.r32.edi
[38699]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
[71423]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. */
[38699]387 SET_CF();
388 SET_AH(UNSUPPORTED_FUNCTION);
389 break;
390
[63562]391 /// @todo Why does this need special handling? All we need is to set CF
[38699]392 // but not handle this as an unknown function (regardless of CPU type).
393 case 0x4f:
394 /* keyboard intercept */
[75229]395#if VBOX_BIOS_CPU >= 80286
396 // nop
397#else
[38699]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: {
[59495]409 if( GET_AL() == 0 ) {
[38699]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( );
[87784]419 // Unmask IRQ8 so INT70 will get through.
[38699]420 irqDisable = inb( 0xA1 );
421 outb( 0xA1, irqDisable & 0xFE );
[87784]422 bRegister = inb_cmos( 0xB );
423 // Turn on the Periodic Interrupt timer
424 outb_cmos( 0xB, bRegister | 0x40 );
[38699]425 } else {
426 // Interval already set.
427 BX_DEBUG_INT15("int15: Func 83h, failed, already waiting.\n" );
[87784]428 SET_CF(); // AH is left unmodified
[38699]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 }
[45669]442
[38699]443 break;
444 }
445
[75229]446 case 0x86:
[87784]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 }
[75229]475 break;
476
[38699]477 case 0x88:
478 // Get the amount of extended memory (above 1M)
[75229]479#if VBOX_BIOS_CPU >= 80286
[92290]480 AX = get_cmos_word(0x30 /*, 0x31*/);
[45669]481
[75232]482#if VBOX_BIOS_CPU >= 80386
[38699]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;
[75232]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
[45669]494
[38699]495 CLEAR_CF();
[75229]496#else
497 SET_AH(UNSUPPORTED_FUNCTION);
498 SET_CF();
[38699]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!
[45669]507
[38699]508 // turn off interrupts
[63562]509 int_disable(); /// @todo aren't they off already?
[45669]510
[38699]511 set_enable_a20(1); // enable A20 line; we're supposed to fail if that fails
[45669]512
[38699]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:
[39366]560 ES = read_word(0x0040, 0x000E);
[38699]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",
[41594]588 (unsigned) AX, (unsigned) BX);
[38699]589 SET_CF();
590 SET_AH(UNSUPPORTED_FUNCTION);
591 break;
592 }
593}
594
[75229]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
[92280]605static void set_e820_range_len(uint16_t reg_ES, uint16_t reg_DI, uint32_t start, uint32_t len, uint8_t type)
[75229]606{
[92279]607 mem_range_t __far *fpRange = reg_ES :> (mem_range_t *)reg_DI;
608 fpRange->start = start;
[92280]609 fpRange->len = len;
[92279]610 fpRange->type = type;
611 fpRange->xlen = 0;
612 fpRange->xstart = 0;
613}
[75229]614
[92280]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)
[92279]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 */
[75229]625}
626
[38699]627void BIOSCALL int15_function32(sys32_regs_t r)
628{
629 BX_DEBUG_INT15("int15 AX=%04x\n",AX);
630
631 switch (GET_AH()) {
[45669]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
[38699]646 case 0xe8:
647 switch(GET_AL()) {
648 case 0x20: // coded by osmaker aka K.J.
649 if(EDX == 0x534D4150) {
[92279]650 uint32_t extended_memory_size; // 64bits long
651 uint16_t c64k_above_4G_low;
652 uint16_t c64k_above_4G_high;
[92280]653#ifdef BIOS_WITH_MCFG_E820
[92279]654 uint32_t mcfgStart, mcfgSize;
[92280]655#endif
[92279]656
[92288]657 /* Go for the amount of memory above 16MB first. */
[92290]658 extended_memory_size = get_cmos_word(0x34 /*, 0x35*/);
[92288]659 if (extended_memory_size > 0)
660 {
661 extended_memory_size += _16M / _64K;
662 extended_memory_size <<= 16;
[38699]663 }
[92288]664 else
665 {
666 /* No memory above 16MB, query memory above 1MB ASSUMING we have at least 1MB. */
[92290]667 extended_memory_size = get_cmos_word(0x30 /*, 0x31*/);
[92288]668 extended_memory_size += _1M / _1K;
669 extended_memory_size *= _1K;
[38699]670 }
671
[92288]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 */
[92290]674 c64k_above_4G_low = get_cmos_word(0x61 /*, 0x62*/);
675 c64k_above_4G_high = get_cmos_word(0x63 /*, 0x64*/);
[38699]676
[92280]677#ifdef BIOS_WITH_MCFG_E820 /** @todo Actually implement the mcfg reporting. */
678 mcfgStart = 0;
[38699]679 mcfgSize = 0;
[92280]680#endif
681 switch (BX)
[38699]682 {
683 case 0:
[92280]684 set_e820_range_end(ES, DI, 0x0000000L, 0x0009fc00L, 1);
[38699]685 EBX = 1;
686 break;
687 case 1:
[92280]688 set_e820_range_end(ES, DI, 0x0009fc00L, 0x000a0000L, 2);
[38699]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. */
[92280]703 set_e820_range_end(ES, DI, 0x000f0000L, 0x00100000L, 2);
[38699]704 EBX = 3;
705 break;
706 case 3:
[92280]707 set_e820_range_end(ES, DI, 0x00100000L, extended_memory_size - ACPI_DATA_SIZE, 1);
[38699]708 EBX = 4;
709 break;
710 case 4:
[92280]711 set_e820_range_len(ES, DI, extended_memory_size - ACPI_DATA_SIZE, ACPI_DATA_SIZE, 3); // ACPI RAM
[38699]712 EBX = 5;
713 break;
714 case 5:
[92280]715 set_e820_range_len(ES, DI, 0xfec00000, 0x1000, 2); // I/O APIC
[59354]716 EBX = 6;
717 break;
718 case 6:
[92280]719 set_e820_range_len(ES, DI, 0xfee00000, 0x1000, 2); // Local APIC
[59354]720 EBX = 7;
721 break;
722 case 7:
[38699]723 /* 256KB BIOS area at the end of 4 GB */
[92280]724 set_e820_range_len(ES, DI, 0xfffc0000L, 0x40000, 2);
725#ifdef BIOS_WITH_MCFG_E820
[38699]726 if (mcfgStart != 0)
[59354]727 EBX = 8;
[92280]728 else
729#endif
730 if (c64k_above_4G_low || c64k_above_4G_high)
[92279]731 EBX = 9;
[38699]732 else
[92279]733 EBX = 0;
[38699]734 break;
[92280]735#ifdef BIOS_WITH_MCFG_E820
[59354]736 case 8:
[38699]737 /* PCI MMIO config space (MCFG) */
[92280]738 set_e820_range_len(ES, DI, mcfgStart, mcfgSize, 2);
[92279]739 if (c64k_above_4G_low || c64k_above_4G_high)
[59354]740 EBX = 9;
[38699]741 else
742 EBX = 0;
743 break;
[92280]744#endif
[59354]745 case 9:
[92280]746 /* Mapping of memory above 4 GB if present. */
[92279]747 if (c64k_above_4G_low || c64k_above_4G_high)
[38699]748 {
[92279]749 set_e820_range_above_4g(ES, DI, c64k_above_4G_low, c64k_above_4G_high);
[38699]750 EBX = 0;
[81941]751 break;
[38699]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();
[45669]770
[38699]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;
[45669]777
[38699]778 // Get the amount of extended memory (above 1M)
[92290]779 CX = get_cmos_word(0x30 /*, 0x31*/);
[45669]780
[38699]781 // limit to 15M
782 if(CX > 0x3c00)
783 CX = 0x3c00;
[45669]784
[38699]785 // Get the amount of extended memory above 16M in 64k blocks
[92290]786 DX = get_cmos_word(0x34 /*, 0x35*/);
[45669]787
[38699]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}
[75229]806#endif /* VBOX_BIOS_CPU >= 80386 */
[60610]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
[63562]824 int_disable(); /// @todo aren't they disabled already?
[60610]825
[71422]826 set_enable_a20(1); // enable A20 line
[60610]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
[74612]835 // 08..0f scratch zeros work area used by BIOS
[60610]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
[74612]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
[60614]882 pm_stack_save(CX, ES, SI, FP_OFF(&r));
[74612]883#endif
[60610]884 pm_enter();
885 pm_copy();
886 pm_exit();
887 pm_stack_restore();
888
[71422]889 set_enable_a20(0); // unconditionally disable A20 line
[60610]890
891 // turn interrupts back on
892 int_enable();
893
894 SET_AH(0);
895 CLEAR_CF();
896}
[75229]897#endif /* VBOX_BIOS_CPU >= 80286 */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use