5663256d0a
to be the logarithm to base 2 of the alignment, in an ELF environment n is the actual alignment boundary; thus, adjust the directives accordingly. Albeit the wonderful i386 architecture doesn't mind the smaller alignment in an obvious way, it is likely to have resulted in some performance penalty during the a.out->ELF transition.
342 lines
8.3 KiB
ArmAsm
342 lines
8.3 KiB
ArmAsm
/* $NetBSD: biostramp.S,v 1.8 1999/08/23 08:24:36 kleink Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 1996 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by John Kohl.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the NetBSD
|
|
* Foundation, Inc. and its contributors.
|
|
* 4. Neither the name of The NetBSD Foundation nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* biostramp.S: provide a means for NetBSD to call BIOS interrupts
|
|
* by switching to real mode, calling it, and switching
|
|
* back to protected & paging mode.
|
|
*/
|
|
|
|
/*
|
|
* Micro$haft's book on i386/i486 programming says you should do the following
|
|
* to return to real mode from protected mode:
|
|
*
|
|
* 1) disable paging, by jumping to code with identical virtual and physical
|
|
* addresses, clearing PG in CR0, and zeroing CR3 (PDBR).
|
|
*
|
|
* 2) segment descriptors must be byte-granular with limit 64k-1, def32 = 0,
|
|
* (i.e. 16-bit data accesses and/or 80286 instructions)
|
|
* CS must be executable; DS,ES,FS,GS should be writable
|
|
*
|
|
* 3) disable interrupts, load IDTR with original value (base 0, limit 1023)
|
|
*
|
|
* 4) clear PE in CR0, execute FAR jump to load CS.
|
|
*
|
|
* 5) load SP, and off you go
|
|
*
|
|
*/
|
|
|
|
#include "assym.h"
|
|
|
|
#include <i386/include/param.h>
|
|
#include <i386/include/specialreg.h>
|
|
#include <i386/include/segments.h>
|
|
#include <i386/include/apmvar.h>
|
|
#include <i386/include/psl.h>
|
|
#include <i386/include/asm.h>
|
|
|
|
#define addr32 .byte 0x67
|
|
#define data32 .byte 0x66
|
|
|
|
.set MYBASE,NBPG
|
|
.set MYSCRATCH,NBPG+NBPG
|
|
.set CR3_ADDR,(MYSCRATCH-4)
|
|
.set IDTR_SAVE_ADDR,CR3_ADDR-6
|
|
.set GDTR_SAVE_ADDR,IDTR_SAVE_ADDR-6
|
|
.set GDTR_LOCAL_ADDR,GDTR_SAVE_ADDR-6
|
|
.set STACK_PTR_ADDR,GDTR_LOCAL_ADDR-4
|
|
.set BASE_PTR_ADDR,STACK_PTR_ADDR-4
|
|
.set FUNCTION_ADDR,(BASE_PTR_ADDR-2)
|
|
.set GDT_COPY_ADDR,(FUNCTION_ADDR-NGDT*8)
|
|
.set EAX_REGADDR,(GDT_COPY_ADDR-4)
|
|
.set EBX_REGADDR,(EAX_REGADDR-4)
|
|
.set ECX_REGADDR,(EBX_REGADDR-4)
|
|
.set EDX_REGADDR,(ECX_REGADDR-4)
|
|
.set ESI_REGADDR,(EDX_REGADDR-4)
|
|
.set EDI_REGADDR,(ESI_REGADDR-4)
|
|
.set EFLAGS_REGADDR,(EDI_REGADDR-4)
|
|
.set ENDREGADDR,(EFLAGS_REGADDR-4)
|
|
|
|
.set REALSTACK,ENDREGADDR-16 # leave a red zone?
|
|
|
|
#define COPY_FLAGS (PSL_C|PSL_PF|PSL_AF|PSL_Z|PSL_N|PSL_D|PSL_V)
|
|
|
|
/*
|
|
* do_bios_call(int function, struct bioscall *regs)
|
|
*/
|
|
|
|
ENTRY(do_bios_call)
|
|
pushl %ebp
|
|
movl %esp,%ebp /* set up frame ptr */
|
|
pushl %esi
|
|
pushl %edi
|
|
pushl %ebx
|
|
pushl %ds
|
|
pushl %es
|
|
pushl %fs
|
|
pushl %gs
|
|
|
|
# copy data to where the real-mode hook can handle it
|
|
movl 8(%ebp),%eax
|
|
movw %ax,FUNCTION_ADDR
|
|
movl 12(%ebp),%ebx
|
|
movl BIOSCALLREG_EAX(%ebx),%eax
|
|
movl %eax,EAX_REGADDR
|
|
movl BIOSCALLREG_EBX(%ebx),%eax
|
|
movl %eax,EBX_REGADDR
|
|
movl BIOSCALLREG_ECX(%ebx),%eax
|
|
movl %eax,ECX_REGADDR
|
|
movl BIOSCALLREG_EDX(%ebx),%eax
|
|
movl %eax,EDX_REGADDR
|
|
movl BIOSCALLREG_ESI(%ebx),%eax
|
|
movl %eax,ESI_REGADDR
|
|
movl BIOSCALLREG_EDI(%ebx),%eax
|
|
movl %eax,EDI_REGADDR
|
|
# merge current flags with certain provided flags
|
|
movl BIOSCALLREG_EFLAGS(%ebx),%ecx
|
|
pushfl
|
|
popl %eax
|
|
andl $~(COPY_FLAGS|PSL_I),%eax
|
|
andl $COPY_FLAGS,%ecx
|
|
orl %ecx,%eax
|
|
movl %eax,EFLAGS_REGADDR
|
|
|
|
# save flags, disable interrupts, do real mode stuff
|
|
pushfl
|
|
|
|
# save GDT
|
|
sgdt GDTR_SAVE_ADDR
|
|
|
|
# copy the GDT to local area
|
|
movl GDTR_SAVE_ADDR+2,%esi
|
|
movl $GDT_COPY_ADDR,%edi
|
|
movl $(NGDT*8),%ecx
|
|
cld
|
|
rep
|
|
movsb
|
|
movw $(NGDT*8)-1,GDTR_LOCAL_ADDR
|
|
movl $GDT_COPY_ADDR,GDTR_LOCAL_ADDR+2
|
|
|
|
# install GDT copy
|
|
lgdt GDTR_LOCAL_ADDR
|
|
|
|
cli
|
|
|
|
# save IDT
|
|
sidt IDTR_SAVE_ADDR
|
|
|
|
# set up new stack: save old ones, create new segs
|
|
movl %esp,STACK_PTR_ADDR
|
|
movl %ebp,BASE_PTR_ADDR
|
|
movl $REALSTACK,%esp
|
|
movl $0,%ebp # leave no trace, there is none.
|
|
|
|
# save CR3
|
|
movl %cr3,%eax
|
|
movl %eax,CR3_ADDR
|
|
|
|
# turn off paging
|
|
movl %cr0,%eax
|
|
andl $~(CR0_PG),%eax
|
|
movl %eax,%cr0
|
|
|
|
# flush TLB, drop PDBR
|
|
xorl %eax,%eax
|
|
movl %eax,%cr3
|
|
|
|
## load 16-bit segment descriptors
|
|
movw $GSEL(GBIOSDATA_SEL,SEL_KPL),%bx
|
|
movw %bx,%ds
|
|
movw %bx,%es
|
|
movw %bx,%fs
|
|
movw %bx,%gs
|
|
|
|
ljmp $GSEL(GBIOSCODE_SEL,SEL_KPL),$x16+MYBASE
|
|
|
|
x16:
|
|
# turn off protected mode--yikes!
|
|
mov %cr0,%eax
|
|
data32
|
|
and $~CR0_PE,%eax
|
|
mov %eax,%cr0
|
|
|
|
# need inter-segment jump to reload real-mode CS
|
|
data32
|
|
ljmp $(MYBASE>>4),$xreal
|
|
|
|
xreal: # really in real mode now
|
|
# set up segment selectors. Note: everything is now relative
|
|
# to zero-base in this file, except %ss.
|
|
# data items in our scratch area need to reflect MYADDR
|
|
xorl %ax,%ax
|
|
movw %ax,%ss
|
|
|
|
movw %cs,%ax
|
|
movw %ax,%es
|
|
movw %ax,%fs
|
|
movw %ax,%gs
|
|
movw %ax,%ds
|
|
|
|
## load IDT, now that we are here.
|
|
addr32
|
|
lidt IDT_bios
|
|
|
|
# Don't forget that we're in real mode, with 16-bit default data.
|
|
# all these movl's are really movw's, and movw's are movl's!
|
|
addr32
|
|
movw EDI_REGADDR-MYBASE,%edi
|
|
addr32
|
|
movw ESI_REGADDR-MYBASE,%esi
|
|
addr32
|
|
movw EDX_REGADDR-MYBASE,%edx
|
|
addr32
|
|
movw ECX_REGADDR-MYBASE,%ecx
|
|
addr32
|
|
movw EBX_REGADDR-MYBASE,%ebx
|
|
addr32
|
|
movb FUNCTION_ADDR-MYBASE,%al
|
|
addr32
|
|
movb %al,intaddr+1 # self modifying code, yuck. no indirect interrupt instruction!
|
|
# long jump to flush processor cache to reflect code modification
|
|
data32
|
|
ljmp $(MYBASE>>4),$flushit
|
|
flushit:
|
|
addr32
|
|
movw EFLAGS_REGADDR-MYBASE,%eax
|
|
pushl %eax
|
|
popfl
|
|
addr32
|
|
movw EAX_REGADDR-MYBASE,%eax
|
|
|
|
intaddr:
|
|
int $0xff
|
|
|
|
# save results
|
|
pushf
|
|
addr32
|
|
movw %eax,EAX_REGADDR-MYBASE
|
|
addr32
|
|
movw %ebx,EBX_REGADDR-MYBASE
|
|
addr32
|
|
movw %ecx,ECX_REGADDR-MYBASE
|
|
addr32
|
|
movw %edx,EDX_REGADDR-MYBASE
|
|
addr32
|
|
movw %esi,ESI_REGADDR-MYBASE
|
|
addr32
|
|
movw %edi,EDI_REGADDR-MYBASE
|
|
pop %eax
|
|
addr32
|
|
movw %eax,EFLAGS_REGADDR-MYBASE
|
|
|
|
# and return to protected mode
|
|
cli # just to be sure
|
|
|
|
mov %cr0,%eax
|
|
data32
|
|
or $CR0_PE,%eax
|
|
mov %eax,%cr0
|
|
|
|
# long jump to 32-bit code segment
|
|
data32
|
|
ljmp $GSEL(GCODE_SEL,SEL_KPL),$x32+MYBASE
|
|
x32:
|
|
#back in 32-bit mode/protected mode (but not paging yet).
|
|
# Reload the segment registers & IDT
|
|
|
|
movw $GSEL(GDATA_SEL,SEL_KPL),%bx
|
|
movw %bx,%ds
|
|
movw %bx,%ss
|
|
movw %bx,%es
|
|
|
|
# reload PDBR
|
|
movl CR3_ADDR,%eax
|
|
movl %eax,%cr3
|
|
movl %cr0,%eax
|
|
orl $CR0_PG,%eax
|
|
movl %eax,%cr0
|
|
|
|
# reload system copy of GDT
|
|
lgdt GDTR_SAVE_ADDR
|
|
|
|
# restore protected-mode stack
|
|
movl STACK_PTR_ADDR,%esp
|
|
movl BASE_PTR_ADDR,%ebp
|
|
|
|
#restore protected-mode IDT
|
|
lidt IDTR_SAVE_ADDR
|
|
|
|
# copy back arguments from holding pen
|
|
|
|
movl 12(%ebp),%ebx
|
|
movl EAX_REGADDR,%eax
|
|
movl %eax,BIOSCALLREG_EAX(%ebx)
|
|
movl EBX_REGADDR,%eax
|
|
movl %eax,BIOSCALLREG_EBX(%ebx)
|
|
movl ECX_REGADDR,%eax
|
|
movl %eax,BIOSCALLREG_ECX(%ebx)
|
|
movl EDX_REGADDR,%eax
|
|
movl %eax,BIOSCALLREG_EDX(%ebx)
|
|
movl ESI_REGADDR,%eax
|
|
movl %eax,BIOSCALLREG_ESI(%ebx)
|
|
movl EDI_REGADDR,%eax
|
|
movl %eax,BIOSCALLREG_EDI(%ebx)
|
|
movl EFLAGS_REGADDR,%eax
|
|
movl %eax,BIOSCALLREG_EFLAGS(%ebx)
|
|
|
|
# finish up, restore registers, and return
|
|
popfl
|
|
popl %gs
|
|
popl %fs
|
|
popl %es
|
|
popl %ds # see above
|
|
popl %ebx
|
|
popl %edi
|
|
popl %esi
|
|
leave
|
|
ret
|
|
|
|
#ifdef __ELF__
|
|
.align 16
|
|
#else
|
|
.align 4
|
|
#endif
|
|
IDT_bios: # BIOS IDT descriptor (real-mode)
|
|
.word 1023
|
|
.long 0
|