1/* 2 * Copyright 2004-2010, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7/*! This file contains code to call BIOS functions out of a protected 8 mode environment. It doesn't use the virtual86 mode - it switches 9 to real mode, make the BIOS call, and switch back to protected 10 mode again. It's meant to be used in a single-threaded boot loader, 11 not in a multi-tasking operating system. 12 It relies on the real mode segment descriptors found in shell.S. 13*/ 14 15 16#define FUNCTION(x) .globl x ; x ## : 17 18#define REAL_MODE_STACK 0x9000 19 // the location of the stack in real mode 20 21#define SAVED_ESP 0x10000 22#define SAVED_CR3 0x10004 23#define SAVED_EAX 0x10008 24#define SAVED_ES 0x1000c 25#define SAVED_FLAGS 0x10010 26#define SAVED_EBP 0x10014 27 // we're overwriting the start of our boot loader to hold some 28 // temporary values - the first 1024 bytes of it are used at 29 // startup only, and we avoid some linking issues this way 30 31 32.text 33.code32 34 35 36/*! This function brings you back to protected mode after you've 37 switched to it using switch_to_real_mode(). 38 Should restore the whole environment to what it looked like 39 before. Clobbers %eax. 40*/ 41FUNCTION(switch_to_protected_mode) 42 cli // turn off interrupts 43 44 .code16 45 movl %cr0, %eax // set the PE bit (0) to switch to protected mode 46 orb $0x1, %al 47 movl %eax, %cr0 48 49 .code32 50 .byte 0x66 // jump to the protected mode segment 51 ljmp $0x8, $_protected_code_segment 52_protected_code_segment: 53 movw $0x10, %ax // setup data and stack selectors 54 movw %ax, %ds 55 movw %ax, %es 56 movw %ax, %fs 57 movw %ax, %gs 58 movw %ax, %ss 59 60 // turn on paging again 61 movl SAVED_CR3, %eax // restore the saved page directory 62 orl %eax, %eax // is there a paging directory at all? 63 jz _no_paging; 64 65 movl %eax, %cr3 66 67 movl %cr0, %eax // set the PG bit (31) to enable paging 68 orl $0x80000000, %eax 69 movl %eax, %cr0 70 71_no_paging: 72 // save the return address so that we can pick it up again later 73 movl (%esp), %eax 74 movl %eax, REAL_MODE_STACK 75 76 // setup protected stack frame again 77 movl SAVED_ESP, %eax 78 movl %eax, %esp 79 movl SAVED_EBP, %ebp 80 81 // copy the return address to the current stack 82 movl REAL_MODE_STACK, %eax 83 movl %eax, (%esp) 84 85 ret 86 87//-------------------------------------------------------------- 88 89 90/*! Switches from protected mode back to real mode. 91 It will disable paging and set the real mode segment selectors to 0x1000, 92 except for the stack selector, which will be 0x0 (the stack is at 0x9000 93 which is where the BFS boot loader puts it as well). 94 Clobbers %eax. 95*/ 96FUNCTION(switch_to_real_mode) 97 // save the %esp register 98 movl %esp, %eax 99 movl %eax, SAVED_ESP 100 101 movl %ebp, SAVED_EBP 102 103 // put the return address on the real mode stack 104 movl (%esp), %eax 105 movl %eax, REAL_MODE_STACK 106 107 // disable paging 108 movl %cr3, %eax // save the page directory address 109 movl %eax, SAVED_CR3 110 111 movl %cr0, %eax 112 andl $0x7fffffff, %eax // clear PG bit (31) 113 movl %eax, %cr0 114 115 xor %eax, %eax // clear page directory to flush TLBs 116 movl %eax, %cr3 117 118 // setup real mode stack 119 movl $REAL_MODE_STACK, %eax 120 movl %eax, %esp 121 movl %eax, %ebp 122 123 // setup selectors to point to our 16 bit segments 124 movw $0x20, %ax 125 movw %ax, %ds 126 movw %ax, %es 127 movw %ax, %fs 128 movw %ax, %gs 129 movw $0x28, %ax 130 movw %ax, %ss 131 132 ljmp $0x18, $(_almost_real_code_segment - 0x10000) 133 134_almost_real_code_segment: 135 movl %cr0, %eax // switch to real mode 136 andb $0xfe, %al // clear PE bit (0) 137 movl %eax, %cr0 138 139 .byte 0x66 140 ljmp $0x1000, $(_real_code_segment - 0x10000) 141 142_real_code_segment: 143 .code16 144 movw $0x1000, %ax // setup data & stack segments 145 movw %ax, %ds // data in segment 0x1000, 146 movw %ax, %es 147 movw %ax, %fs 148 movw %ax, %gs 149 150 xor %ax, %ax // stack in segment 0x0 151 movw %ax, %ss 152 153 sti // turn on interrupts again 154 ret 155 156 .code32 157 158//-------------------------------------------------------------- 159 160 161/*! void call_bios_internal(uint8 num, struct bios_regs *regs) 162 Does a BIOS call by triggering a software interrupt in real 163 mode. 164*/ 165FUNCTION(call_bios_internal) 166 pushal 167 pushfl 168 169 // make sure the correct IDT is in place 170 lidt idt_descriptor 171 172 // get the interrupt vector and patch the instruction at the target address 173 movl 40(%esp), %eax 174 mov %al, int_number 175 176 // Fills registers from the passed in structure 177 // Since switch_to_real_mode() clobbers %eax, we have to handle 178 // it specially here (by temporarily storing it to an arbitrary 179 // memory location, SAVED_EAX) 180 movl 44(%esp), %ebp 181 182 movl (%ebp), %eax 183 movl %eax, SAVED_EAX 184 movl 4(%ebp), %ebx 185 movl 8(%ebp), %ecx 186 movl 12(%ebp), %edx 187 movl 16(%ebp), %esi 188 movl 20(%ebp), %edi 189 movw 24(%ebp), %ax 190 movw %ax, SAVED_ES 191 192 call switch_to_real_mode 193 194 .code16 195 196 // restore %eax and %es from saved location 197 movl (SAVED_EAX - 0x10000), %eax 198 movw (SAVED_ES - 0x10000), %es 199 200 // call the interrupt (will be dynamically changed above) 201 .byte 0xcd 202int_number: 203 .byte 0 204 205 // we're interested in the flags state as well 206 pushf 207 208 // save %eax from the call 209 movl %eax, (SAVED_EAX - 0x10000) 210 211 // save flags from the call 212 pop %ax 213 movw %ax, (SAVED_FLAGS - 0x10000) 214 215 // back to protected mode 216 call switch_to_protected_mode 217 .code32 218 219 // store the register state into the structure that has been passed in 220 movl 44(%esp), %eax 221 222 movl %ebx, 4(%eax) 223 movl %ecx, 8(%eax) 224 movl %edx, 12(%eax) 225 movl %esi, 16(%eax) 226 movl %edi, 20(%eax) 227 movl SAVED_EAX, %ecx // special handling for %eax and flags 228 movl %ecx, (%eax) 229 movw SAVED_FLAGS, %cx 230 movw %cx, 26(%eax) 231 232 popfl 233 popal 234 235 ret 236 237//-------------------------------------------------------------- 238 239 240/*! uint32 boot_key_in_keyboard_buffer() 241 Search keyboard buffer for the keycodes for space in the first run, and, 242 if not found - for the escape key at the last two positions of this buffer 243*/ 244FUNCTION(boot_key_in_keyboard_buffer) 245 pushal 246 pushfl 247 248 // make sure the correct IDT is in place 249 lidt idt_descriptor 250 251 call switch_to_real_mode 252 .code16 253 254 cld 255 push %ds 256 xorl %eax, %eax 257 mov %ax, %ds 258 mov $0x41E, %si // BIOS kbd buffer 259search_cycle1: 260 lodsw 261 cmp $0x3920, %ax // test space key 262 jz to_ret 263 cmp $0x440, %si 264 jnz search_cycle1 265 266 addw 0x41C, %si 267 movw -0x42(%si), %ax 268 cmp $0x011B, %ax // test ESC key 269 jz to_ret 270 movw -0x44(%si), %ax 271 cmp $0x011B, %ax // test ESC key 272to_ret: 273 pop %ds 274 275 // save %eax 276 movl %eax, (SAVED_EAX - 0x10000) 277 278 call switch_to_protected_mode 279 .code32 280 281 popfl 282 popal 283 284 // restore %eax 285 movl SAVED_EAX, %eax 286 287 ret 288 289//-------------------------------------------------------------- 290 291 292.globl idt_descriptor 293idt_descriptor: 294 .short 0x7ff // IDT at 0x0, default real mode location 295 .long 0x0 296