From 46df41d103f319d154279f084f89ab983d57a2a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Sun, 18 Apr 2004 23:58:26 +0000 Subject: [PATCH] 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 --- src/kernel/boot/platform/bios_ia32/bios.S | 229 ++++++++++++++++++++++ src/kernel/boot/platform/bios_ia32/bios.h | 32 +++ 2 files changed, 261 insertions(+) create mode 100644 src/kernel/boot/platform/bios_ia32/bios.S create mode 100644 src/kernel/boot/platform/bios_ia32/bios.h diff --git a/src/kernel/boot/platform/bios_ia32/bios.S b/src/kernel/boot/platform/bios_ia32/bios.S new file mode 100644 index 0000000000..354c93c9e0 --- /dev/null +++ b/src/kernel/boot/platform/bios_ia32/bios.S @@ -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 diff --git a/src/kernel/boot/platform/bios_ia32/bios.h b/src/kernel/boot/platform/bios_ia32/bios.h new file mode 100644 index 0000000000..80a168c8d2 --- /dev/null +++ b/src/kernel/boot/platform/bios_ia32/bios.h @@ -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 + + +// 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 */