1*ce099b40Smartin/* $NetBSD: biostramp.S,v 1.14 2008/04/28 20:23:23 martin Exp $ */ 24620e806Sjtc 3fda7345cSjtk/*- 44620e806Sjtc * Copyright (c) 1996 The NetBSD Foundation, Inc. 54620e806Sjtc * All rights reserved. 64620e806Sjtc * 74620e806Sjtc * This code is derived from software contributed to The NetBSD Foundation 84620e806Sjtc * by John Kohl. 9fda7345cSjtk * 10fda7345cSjtk * Redistribution and use in source and binary forms, with or without 11fda7345cSjtk * modification, are permitted provided that the following conditions 12fda7345cSjtk * are met: 13fda7345cSjtk * 1. Redistributions of source code must retain the above copyright 14fda7345cSjtk * notice, this list of conditions and the following disclaimer. 15fda7345cSjtk * 2. Redistributions in binary form must reproduce the above copyright 16fda7345cSjtk * notice, this list of conditions and the following disclaimer in the 17fda7345cSjtk * documentation and/or other materials provided with the distribution. 18fda7345cSjtk * 194620e806Sjtc * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 204620e806Sjtc * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 214620e806Sjtc * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 221bcecdd6Sjtc * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 231bcecdd6Sjtc * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 244620e806Sjtc * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 254620e806Sjtc * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 264620e806Sjtc * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 274620e806Sjtc * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 284620e806Sjtc * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29fda7345cSjtk * POSSIBILITY OF SUCH DAMAGE. 30fda7345cSjtk */ 31fda7345cSjtk 32fda7345cSjtk/* 33fda7345cSjtk * biostramp.S: provide a means for NetBSD to call BIOS interrupts 34fda7345cSjtk * by switching to real mode, calling it, and switching 35fda7345cSjtk * back to protected & paging mode. 36fda7345cSjtk */ 37fda7345cSjtk 38fda7345cSjtk/* 39fda7345cSjtk * Micro$haft's book on i386/i486 programming says you should do the following 40fda7345cSjtk * to return to real mode from protected mode: 41fda7345cSjtk * 42fda7345cSjtk * 1) disable paging, by jumping to code with identical virtual and physical 43fda7345cSjtk * addresses, clearing PG in CR0, and zeroing CR3 (PDBR). 44fda7345cSjtk * 45fda7345cSjtk * 2) segment descriptors must be byte-granular with limit 64k-1, def32 = 0, 46fda7345cSjtk * (i.e. 16-bit data accesses and/or 80286 instructions) 47fda7345cSjtk * CS must be executable; DS,ES,FS,GS should be writable 48fda7345cSjtk * 49fda7345cSjtk * 3) disable interrupts, load IDTR with original value (base 0, limit 1023) 50fda7345cSjtk * 51fda7345cSjtk * 4) clear PE in CR0, execute FAR jump to load CS. 52fda7345cSjtk * 53fda7345cSjtk * 5) load SP, and off you go 54fda7345cSjtk * 55fda7345cSjtk */ 56fda7345cSjtk 57fda7345cSjtk#include "assym.h" 5804f1e23fSjtk 5904f1e23fSjtk#include <i386/include/param.h> 6004f1e23fSjtk#include <i386/include/specialreg.h> 6104f1e23fSjtk#include <i386/include/segments.h> 6204f1e23fSjtk#include <i386/include/apmvar.h> 6304f1e23fSjtk#include <i386/include/psl.h> 6404f1e23fSjtk#include <i386/include/asm.h> 6504f1e23fSjtk 66dbacbd36Smycroft#define addr32 .byte 0x67 67dbacbd36Smycroft#define data32 .byte 0x66 68fda7345cSjtk 6915eb4971Smycroft .set MYBASE,NBPG 7015eb4971Smycroft .set MYSCRATCH,NBPG*2 71fda7345cSjtk .set CR3_ADDR,(MYSCRATCH-4) 72fda7345cSjtk .set IDTR_SAVE_ADDR,CR3_ADDR-6 73fda7345cSjtk .set GDTR_SAVE_ADDR,IDTR_SAVE_ADDR-6 74fda7345cSjtk .set GDTR_LOCAL_ADDR,GDTR_SAVE_ADDR-6 75fda7345cSjtk .set STACK_PTR_ADDR,GDTR_LOCAL_ADDR-4 76fda7345cSjtk .set BASE_PTR_ADDR,STACK_PTR_ADDR-4 77fda7345cSjtk .set FUNCTION_ADDR,(BASE_PTR_ADDR-2) 78fda7345cSjtk .set GDT_COPY_ADDR,(FUNCTION_ADDR-NGDT*8) 7910ec6359Sjtk .set EAX_REGADDR,(GDT_COPY_ADDR-4) 8010ec6359Sjtk .set EBX_REGADDR,(EAX_REGADDR-4) 8110ec6359Sjtk .set ECX_REGADDR,(EBX_REGADDR-4) 8210ec6359Sjtk .set EDX_REGADDR,(ECX_REGADDR-4) 8310ec6359Sjtk .set ESI_REGADDR,(EDX_REGADDR-4) 8410ec6359Sjtk .set EDI_REGADDR,(ESI_REGADDR-4) 8510ec6359Sjtk .set EFLAGS_REGADDR,(EDI_REGADDR-4) 860b263476Sjdolecek .set ES_REGADDR, (EFLAGS_REGADDR-4) 870b263476Sjdolecek .set ENDREGADDR,(ES_REGADDR-4) 88fda7345cSjtk 890b263476Sjdolecek .set REALSTACK,ENDREGADDR-20 # leave a red zone? 90fda7345cSjtk 91fda7345cSjtk#define COPY_FLAGS (PSL_C|PSL_PF|PSL_AF|PSL_Z|PSL_N|PSL_D|PSL_V) 92fda7345cSjtk 93fda7345cSjtk/* 9410ec6359Sjtk * do_bios_call(int function, struct bioscall *regs) 95fda7345cSjtk */ 96fda7345cSjtk 97fda7345cSjtkENTRY(do_bios_call) 98fda7345cSjtk pushl %ebp 99fda7345cSjtk movl %esp,%ebp /* set up frame ptr */ 100fda7345cSjtk pushl %esi 101fda7345cSjtk pushl %edi 102fda7345cSjtk pushl %ebx 103fda7345cSjtk pushl %ds 104fda7345cSjtk pushl %es 105fda7345cSjtk pushl %fs 106fda7345cSjtk pushl %gs 107fda7345cSjtk 108fda7345cSjtk # copy data to where the real-mode hook can handle it 109fda7345cSjtk movl 8(%ebp),%eax 110fda7345cSjtk movw %ax,FUNCTION_ADDR 111fda7345cSjtk movl 12(%ebp),%ebx 11210ec6359Sjtk movl BIOSCALLREG_EAX(%ebx),%eax 11310ec6359Sjtk movl %eax,EAX_REGADDR 11410ec6359Sjtk movl BIOSCALLREG_EBX(%ebx),%eax 11510ec6359Sjtk movl %eax,EBX_REGADDR 11610ec6359Sjtk movl BIOSCALLREG_ECX(%ebx),%eax 11710ec6359Sjtk movl %eax,ECX_REGADDR 11810ec6359Sjtk movl BIOSCALLREG_EDX(%ebx),%eax 11910ec6359Sjtk movl %eax,EDX_REGADDR 12010ec6359Sjtk movl BIOSCALLREG_ESI(%ebx),%eax 12110ec6359Sjtk movl %eax,ESI_REGADDR 12210ec6359Sjtk movl BIOSCALLREG_EDI(%ebx),%eax 12310ec6359Sjtk movl %eax,EDI_REGADDR 124fda7345cSjtk # merge current flags with certain provided flags 12510ec6359Sjtk movl BIOSCALLREG_EFLAGS(%ebx),%ecx 126fda7345cSjtk pushfl 127fda7345cSjtk popl %eax 128fda7345cSjtk andl $~(COPY_FLAGS|PSL_I),%eax 129fda7345cSjtk andl $COPY_FLAGS,%ecx 130fda7345cSjtk orl %ecx,%eax 13110ec6359Sjtk movl %eax,EFLAGS_REGADDR 1320b263476Sjdolecek movl $0, ES_REGADDR 133fda7345cSjtk 134fda7345cSjtk # save flags, disable interrupts, do real mode stuff 135fda7345cSjtk pushfl 136fda7345cSjtk 137fda7345cSjtk # save GDT 138fda7345cSjtk sgdt GDTR_SAVE_ADDR 139fda7345cSjtk 140fda7345cSjtk # copy the GDT to local area 141fda7345cSjtk movl GDTR_SAVE_ADDR+2,%esi 142fda7345cSjtk movl $GDT_COPY_ADDR,%edi 143fda7345cSjtk movl $(NGDT*8),%ecx 144fda7345cSjtk cld 145fda7345cSjtk rep 146fda7345cSjtk movsb 147fda7345cSjtk movw $(NGDT*8)-1,GDTR_LOCAL_ADDR 148fda7345cSjtk movl $GDT_COPY_ADDR,GDTR_LOCAL_ADDR+2 149fda7345cSjtk 150fda7345cSjtk # install GDT copy 151fda7345cSjtk lgdt GDTR_LOCAL_ADDR 152fda7345cSjtk 153fda7345cSjtk cli 154fda7345cSjtk 155fda7345cSjtk # save IDT 156fda7345cSjtk sidt IDTR_SAVE_ADDR 157fda7345cSjtk 158fda7345cSjtk # set up new stack: save old ones, create new segs 159fda7345cSjtk movl %esp,STACK_PTR_ADDR 160fda7345cSjtk movl %ebp,BASE_PTR_ADDR 161fda7345cSjtk movl $REALSTACK,%esp 162fda7345cSjtk movl $0,%ebp # leave no trace, there is none. 163fda7345cSjtk 164fda7345cSjtk # save CR3 165fda7345cSjtk movl %cr3,%eax 166fda7345cSjtk movl %eax,CR3_ADDR 167fda7345cSjtk 168fda7345cSjtk # turn off paging 169fda7345cSjtk movl %cr0,%eax 170fda7345cSjtk andl $~(CR0_PG),%eax 171fda7345cSjtk movl %eax,%cr0 172fda7345cSjtk 173fda7345cSjtk # flush TLB, drop PDBR 174fda7345cSjtk xorl %eax,%eax 175fda7345cSjtk movl %eax,%cr3 176fda7345cSjtk 177fda7345cSjtk ## load 16-bit segment descriptors 178fda7345cSjtk movw $GSEL(GBIOSDATA_SEL,SEL_KPL),%bx 179fda7345cSjtk movw %bx,%ds 180fda7345cSjtk movw %bx,%es 181fda7345cSjtk movw %bx,%fs 182fda7345cSjtk movw %bx,%gs 183fda7345cSjtk 184fda7345cSjtk ljmp $GSEL(GBIOSCODE_SEL,SEL_KPL),$x16+MYBASE 185fda7345cSjtk 186fda7345cSjtkx16: 187fda7345cSjtk # turn off protected mode--yikes! 188fda7345cSjtk mov %cr0,%eax 189fda7345cSjtk data32 190fda7345cSjtk and $~CR0_PE,%eax 191fda7345cSjtk mov %eax,%cr0 192fda7345cSjtk 193fda7345cSjtk # need inter-segment jump to reload real-mode CS 194fda7345cSjtk data32 195fda7345cSjtk ljmp $(MYBASE>>4),$xreal 196fda7345cSjtk 197fda7345cSjtkxreal: # really in real mode now 198fda7345cSjtk # set up segment selectors. Note: everything is now relative 199fda7345cSjtk # to zero-base in this file, except %ss. 200fda7345cSjtk # data items in our scratch area need to reflect MYADDR 201d259e610Schristos xorl %eax,%eax 202fda7345cSjtk movw %ax,%ss 203fda7345cSjtk 204fda7345cSjtk movw %cs,%ax 205fda7345cSjtk movw %ax,%es 206fda7345cSjtk movw %ax,%fs 207fda7345cSjtk movw %ax,%gs 208fda7345cSjtk movw %ax,%ds 209fda7345cSjtk 210fda7345cSjtk ## load IDT, now that we are here. 211fda7345cSjtk addr32 212fda7345cSjtk lidt IDT_bios 213fda7345cSjtk 214fda7345cSjtk # Don't forget that we're in real mode, with 16-bit default data. 21510ec6359Sjtk # all these movl's are really movw's, and movw's are movl's! 216fda7345cSjtk addr32 217d259e610Schristos movw EDI_REGADDR-MYBASE,%di 218fda7345cSjtk addr32 219d259e610Schristos movw ESI_REGADDR-MYBASE,%si 220fda7345cSjtk addr32 221d259e610Schristos movw EDX_REGADDR-MYBASE,%dx 222fda7345cSjtk addr32 223d259e610Schristos movw ECX_REGADDR-MYBASE,%cx 224fda7345cSjtk addr32 225d259e610Schristos movw EBX_REGADDR-MYBASE,%bx 226fda7345cSjtk addr32 227fda7345cSjtk movb FUNCTION_ADDR-MYBASE,%al 228fda7345cSjtk addr32 229fda7345cSjtk movb %al,intaddr+1 # self modifying code, yuck. no indirect interrupt instruction! 230fda7345cSjtk # long jump to flush processor cache to reflect code modification 231fda7345cSjtk data32 232fda7345cSjtk ljmp $(MYBASE>>4),$flushit 233fda7345cSjtkflushit: 234fda7345cSjtk addr32 235d259e610Schristos movw EFLAGS_REGADDR-MYBASE,%ax 236fda7345cSjtk pushl %eax 237fda7345cSjtk popfl 238fda7345cSjtk addr32 239d259e610Schristos movw EAX_REGADDR-MYBASE,%ax 240fda7345cSjtk 241fda7345cSjtkintaddr: 242fda7345cSjtk int $0xff 243fda7345cSjtk 244fda7345cSjtk # save results 245fda7345cSjtk pushf 246fda7345cSjtk addr32 247d259e610Schristos movw %ax,EAX_REGADDR-MYBASE 248fda7345cSjtk addr32 249d259e610Schristos movw %bx,EBX_REGADDR-MYBASE 250fda7345cSjtk addr32 251d259e610Schristos movw %cx,ECX_REGADDR-MYBASE 252fda7345cSjtk addr32 253d259e610Schristos movw %dx,EDX_REGADDR-MYBASE 254fda7345cSjtk addr32 255d259e610Schristos movw %si,ESI_REGADDR-MYBASE 256fda7345cSjtk addr32 257d259e610Schristos movw %di,EDI_REGADDR-MYBASE 258d259e610Schristos pop %ax 259fda7345cSjtk addr32 260d259e610Schristos movw %ax,EFLAGS_REGADDR-MYBASE 2610b263476Sjdolecek addr32 2620b263476Sjdolecek movw %es,ES_REGADDR-MYBASE 263fda7345cSjtk 264fda7345cSjtk # and return to protected mode 265fda7345cSjtk cli # just to be sure 266fda7345cSjtk 267fda7345cSjtk mov %cr0,%eax 268fda7345cSjtk data32 269fda7345cSjtk or $CR0_PE,%eax 270fda7345cSjtk mov %eax,%cr0 271fda7345cSjtk 272fda7345cSjtk # long jump to 32-bit code segment 273fda7345cSjtk data32 274fda7345cSjtk ljmp $GSEL(GCODE_SEL,SEL_KPL),$x32+MYBASE 275fda7345cSjtkx32: 276fda7345cSjtk #back in 32-bit mode/protected mode (but not paging yet). 277fda7345cSjtk # Reload the segment registers & IDT 278fda7345cSjtk 279fda7345cSjtk movw $GSEL(GDATA_SEL,SEL_KPL),%bx 280fda7345cSjtk movw %bx,%ds 281fda7345cSjtk movw %bx,%ss 282fda7345cSjtk movw %bx,%es 283fda7345cSjtk 284fda7345cSjtk # reload PDBR 285fda7345cSjtk movl CR3_ADDR,%eax 286fda7345cSjtk movl %eax,%cr3 287fda7345cSjtk movl %cr0,%eax 288fda7345cSjtk orl $CR0_PG,%eax 289fda7345cSjtk movl %eax,%cr0 290fda7345cSjtk 291fda7345cSjtk # reload system copy of GDT 292fda7345cSjtk lgdt GDTR_SAVE_ADDR 293fda7345cSjtk 294fda7345cSjtk # restore protected-mode stack 295fda7345cSjtk movl STACK_PTR_ADDR,%esp 296fda7345cSjtk movl BASE_PTR_ADDR,%ebp 297fda7345cSjtk 298fda7345cSjtk #restore protected-mode IDT 299fda7345cSjtk lidt IDTR_SAVE_ADDR 300fda7345cSjtk 301fda7345cSjtk # copy back arguments from holding pen 302fda7345cSjtk 303fda7345cSjtk movl 12(%ebp),%ebx 30410ec6359Sjtk movl EAX_REGADDR,%eax 30510ec6359Sjtk movl %eax,BIOSCALLREG_EAX(%ebx) 30610ec6359Sjtk movl EBX_REGADDR,%eax 30710ec6359Sjtk movl %eax,BIOSCALLREG_EBX(%ebx) 30810ec6359Sjtk movl ECX_REGADDR,%eax 30910ec6359Sjtk movl %eax,BIOSCALLREG_ECX(%ebx) 31010ec6359Sjtk movl EDX_REGADDR,%eax 31110ec6359Sjtk movl %eax,BIOSCALLREG_EDX(%ebx) 31210ec6359Sjtk movl ESI_REGADDR,%eax 31310ec6359Sjtk movl %eax,BIOSCALLREG_ESI(%ebx) 31410ec6359Sjtk movl EDI_REGADDR,%eax 31510ec6359Sjtk movl %eax,BIOSCALLREG_EDI(%ebx) 31610ec6359Sjtk movl EFLAGS_REGADDR,%eax 31710ec6359Sjtk movl %eax,BIOSCALLREG_EFLAGS(%ebx) 3180b263476Sjdolecek movl ES_REGADDR, %eax 3190b263476Sjdolecek movl %eax,BIOSCALLREG_ES(%ebx) 320fda7345cSjtk 321fda7345cSjtk # finish up, restore registers, and return 322fda7345cSjtk popfl 323fda7345cSjtk popl %gs 324fda7345cSjtk popl %fs 325fda7345cSjtk popl %es 326fda7345cSjtk popl %ds # see above 327fda7345cSjtk popl %ebx 328fda7345cSjtk popl %edi 329fda7345cSjtk popl %esi 330fda7345cSjtk leave 331fda7345cSjtk ret 332fda7345cSjtk 3335663256dSkleink#ifdef __ELF__ 3345663256dSkleink .align 16 3355663256dSkleink#else 336fda7345cSjtk .align 4 3375663256dSkleink#endif 338fda7345cSjtkIDT_bios: # BIOS IDT descriptor (real-mode) 339fda7345cSjtk .word 1023 340fda7345cSjtk .long 0 341