Implemented call_bios() function that is called from protected mode.

It switches to real mode for execution of the BIOS interrupt; it's really
only intended for the boot loader.


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@7233 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2004-04-18 23:58:26 +00:00
parent 1e56fb4aeb
commit 46df41d103
2 changed files with 261 additions and 0 deletions

View File

@ -0,0 +1,229 @@
/*
** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
** Distributed under the terms of the OpenBeOS License.
*/
/** This file contains code to call BIOS functions out of a protected
* mode environment. It doesn't use the virtual86 mode - it switches
* to real mode, make the BIOS call, and switch back to protected
* mode again. It's meant to be used in a single-threaded boot loader,
* not in a multi-tasking operating system.
* It relies on the real mode segment descriptors found in shell.S.
*/
#define FUNCTION(x) .globl x ; x ## :
#define REAL_MODE_STACK 0x9000
// the location of the stack in real mode
#define SAVED_ESP 0x10000
#define SAVED_CR3 0x10004
#define SAVED_EAX 0x10008
#define SAVED_FLAGS 0x1000c
// we're overwriting the start of our boot loader to hold some
// temporary values - the first 1024 bytes of it are used at
// startup only, and we avoid some linking issues this way
.text
.code32
/** This function brings you back to protected mode after you've
* switched to it using switch_to_real_mode().
* Should restore the whole environment to what it looked like
* before. Clobbers %eax.
*/
FUNCTION(switch_to_protected_mode)
cli // turn off interrupts
.code16
movl %cr0, %eax // set the PE bit (0) to switch to protected mode
orb $0x1, %al
movl %eax, %cr0
.code32
.byte 0x66 // jump to the protected mode segment
ljmp $0x8, $_protected_code_segment
_protected_code_segment:
movw $0x10, %ax // setup data and stack selectors
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movw %ax, %ss
// turn on paging again
movl SAVED_CR3, %eax // restore the saved page directory
movl %eax, %cr3
movl %cr0, %eax // set the PG bit (31) to enable paging
orl $0x80000000, %eax
movl %eax, %cr0
// save the return address so that we can pick it up again later
movl (%esp), %eax
movl %eax, REAL_MODE_STACK
// setup protected stack frame again
movl SAVED_ESP, %eax
movl %eax, %esp
movl %eax, %ebp
// copy the return address to the current stack
movl REAL_MODE_STACK, %eax
movl %eax, (%esp)
ret
//--------------------------------------------------------------
/** Switches from protected mode back to real mode.
* It will disable paging and set the real mode segment selectors to 0x1000,
* except for the stack selector, which will be 0x0 (the stack is at 0x9000
* which is where the BFS boot loader puts it as well).
* Clobbers %eax.
*/
FUNCTION(switch_to_real_mode)
// save the %esp register
movl %esp, %eax
movl %eax, SAVED_ESP
// put the return address on the real mode stack
movl (%esp), %eax
movl %eax, REAL_MODE_STACK
// disable paging
movl %cr3, %eax // save the page directory address
movl %eax, SAVED_CR3
movl %cr0, %eax
andl $0x7fffffff, %eax // clear PG bit (31)
movl %eax, %cr0
xor %eax, %eax // clear page directory to flush TLBs
movl %eax, %cr3
// setup real mode stack
movl $REAL_MODE_STACK, %eax
movl %eax, %esp
movl %eax, %ebp
// setup selectors to point to our 16 bit segments
movw $0x20, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movw $0x28, %ax
movw %ax, %ss
ljmp $0x18, $(_almost_real_code_segment - 0x10000)
_almost_real_code_segment:
movl %cr0, %eax // switch to real mode
andb $0xfe, %al // clear PE bit (0)
movl %eax, %cr0
.byte 0x66
ljmp $0x1000, $(_real_code_segment - 0x10000)
_real_code_segment:
.code16
movw $0x1000, %ax // setup data & stack segments
movw %ax, %ds // data in segment 0x1000,
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
xor %ax, %ax // stack in segment 0x0
movw %ax, %ss
sti // turn on interrupts again
ret
.code32
//--------------------------------------------------------------
/** void call_bios(uint8 num, struct bios_regs *regs)
* Does a BIOS call by triggering a software interrupt in real
* mode.
*/
FUNCTION(call_bios)
pushal
pushfl
// make sure the correct IDT is in place
lidt idt_descriptor
// get the interrupt vector and patch the instruction at the target address
movl 40(%esp), %eax
mov %al, int_number
// Fills registers from the passed in structure
// Since switch_to_real_mode() clobbers %eax, we have to handle
// it specially here (by temporarily storing it to an arbitrary
// memory location, SAVED_EAX)
movl 44(%esp), %ebp
movl (%ebp), %eax
movl %eax, SAVED_EAX
movl 4(%ebp), %ebx
movl 8(%ebp), %ecx
movl 12(%ebp), %edx
movl 16(%ebp), %esi
movl 20(%ebp), %edi
mov 24(%ebp), %es
call switch_to_real_mode
.code16
// restore %eax from saved location
movl (SAVED_EAX - 0x10000), %eax
// call the interrupt (will be dynamically changed above)
.byte 0xcd
int_number:
.byte 0
// we're interested in the flags state as well
pushf
// save %eax from the call
movl %eax, (SAVED_EAX - 0x10000)
// save flags from the call
pop %ax
movw %ax, (SAVED_FLAGS - 0x10000)
// back to protected mode
call switch_to_protected_mode
.code32
// store the register state into the structure that has been passed in
movl 44(%esp), %eax
movl %ebx, 4(%eax)
movl %ecx, 8(%eax)
movl %edx, 12(%eax)
movl %esi, 16(%eax)
movl %edi, 20(%eax)
movl SAVED_EAX, %ecx // special handling for %eax and flags
movl %ecx, (%eax)
movw SAVED_FLAGS, %cx
movw %cx, 26(%eax)
popfl
popal
ret
//--------------------------------------------------------------
idt_descriptor:
.short 0x7ff // IDT at 0x0, default real mode location
.long 0x0

View File

@ -0,0 +1,32 @@
/*
** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
** Distributed under the terms of the OpenBeOS License.
*/
#ifndef BIOS_H
#define BIOS_H
#include <SupportDefs.h>
// The values in this structure are passed to the BIOS call, and
// are updated to the register contents after that call.
struct bios_regs {
uint32 eax;
uint32 ebx;
uint32 ecx;
uint32 edx;
uint32 esi;
uint32 edi;
uint16 es;
uint16 flags;
};
extern
#ifdef __cplusplus
"C"
#endif
void call_bios(uint8 num, struct bios_regs *regs);
#endif /* BIOS_H */