/*
* Copyright (c) 1998 Robert Nordier
* All rights reserved.
*
* Redistribution and use in source and binary forms are freely
* permitted provided that the above copyright notice and this
* paragraph and the following disclaimer are duplicated in all
* such forms.
*
* This software is provided "AS IS" and without any express or
* implied warranties, including, without limitation, the implied
* warranties of merchantability and fitness for a particular
* purpose.
*
* $FreeBSD$
*/
#include <bootargs.h>
/*
* Memory layout.
*/
/*
* Paging control.
*/
/*
* Fields in %eflags.
*/
/*
* Segment selectors.
*/
/*
* Task state segment fields.
*/
/*
* System calls.
*/
/*
* Fields in V86 interface structure.
*/
/*
* V86 control flags.
*/
/*
* Dump format control bytes.
*/
/*
* Screen defaults and assumptions.
*/
/*
* BIOS Data Area locations.
*/
/*
* Derivations, for brevity.
*/
/*
* Code segment.
*/
/*
* BTX header.
*/
/*
* Initialization routine.
*/
/*
* Initialize memory.
*/
/*
* Update real mode IDT for reflecting hardware interrupts.
*/
/*
* Create IDT.
*/
/*
* Initialize TSS.
*/
/*
* Bring up the system.
*/
/*
* Launch user task.
*/
#ifdef BTX_SERIAL
#endif
/*
* Exit routine.
*/
/*
* Turn off paging.
*/
/*
* Restore the GDT in case we caught a kernel trap.
*/
/*
* To 16 bits.
*/
/*
* To real-address mode.
*/
/*
* Reboot or await reset.
*/
/*
* Set IRQ offsets by reprogramming 8259A PICs.
*/
/*
* Exception jump table.
*/
/*
* Save a zero error code.
*/
/*
* Handle exception.
*/
/*
* Reboot the machine by setting the reboot flag and exiting
*/
/*
* Protected Mode Hardware interrupt jump table.
*/
/*
*/
/*
*
* We place a trampoline on the user stack that will return to rret_tramp
* which will reenter protected mode and then finally return to the user
* client.
*
* Kernel frame %esi points to: Real mode stack frame at MEM_ESPR:
*
* -0x00 user %ss -0x04 kernel %esp (with full frame)
* -0x04 user %esp -0x08 btx_v86 pointer
* -0x08 user %eflags -0x0c flags (only used if interrupt)
* -0x0c user %cs -0x10 real mode CS:IP return trampoline
* -0x10 user %eip -0x12 real mode flags
* -0x14 int no -0x16 real mode CS:IP (target)
* -0x18 %eax
* -0x1c %ecx
* -0x20 %edx
* -0x24 %ebx
* -0x28 %esp
* -0x2c %ebp
* -0x30 %esi
* -0x34 %edi
* -0x38 %gs
* -0x3c %fs
* -0x40 %ds
* -0x44 %es
* -0x48 zero %eax (hardware int only)
* -0x4c zero %ecx (hardware int only)
* -0x50 zero %edx (hardware int only)
* -0x54 zero %ebx (hardware int only)
* -0x58 zero %esp (hardware int only)
* -0x5c zero %ebp (hardware int only)
* -0x60 zero %esi (hardware int only)
* -0x64 zero %edi (hardware int only)
* -0x68 zero %gs (hardware int only)
* -0x6c zero %fs (hardware int only)
* -0x70 zero %ds (hardware int only)
* -0x74 zero %es (hardware int only)
*/
/*
* v86 calls save the btx_v86 pointer on the real mode stack and read
* the address and flags from the btx_v86 structure. For interrupt
* handler invocations (VM86 INTx requests), disable interrupts,
* tracing, and alignment checking while the handler runs.
*/
/*
* Hardware interrupts store a NULL btx_v86 pointer and use the
* address (interrupt number) from the stack with empty flags. Also,
* push a dummy frame of zeros onto the stack for all the general
* purpose and segment registers and clear %eflags. This gives the
* hardware interrupt handler a clean slate.
*/
/*
* Look up real mode IDT entry for hardware interrupts and VM86 INTx
* requests.
*/
/*
* Panic if V86F_CALLF isn't set with V86F_ADDR.
*/
/*
* %eax now holds the segment:offset of the function.
* %ebx now holds the %eflags to pass to real mode.
* %edx now holds the V86F_* flags.
*/
# target
/*
* If this is a v86 call, copy the seg regs out of the btx_v86 structure.
*/
/*
* For the return to real mode we setup a stack frame like this on the real
* mode stack. Note that callf calls won't pop off the flags, but we just
* ignore that by repositioning %sp to be just above the btx_v86 pointer
* so it is aligned. The stack is relative to MEM_ESPR.
*
* -0x04 kernel %esp
* -0x08 btx_v86
* -0x0c %eax
* -0x10 %ecx
* -0x14 %edx
* -0x18 %ebx
* -0x1c %esp
* -0x20 %ebp
* -0x24 %esi
* -0x28 %edi
* -0x2c %gs
* -0x30 %fs
* -0x34 %ds
* -0x38 %es
* -0x3c %eflags
*/
/*
* Now we are back in protected mode. The kernel stack frame set up
* before entering real mode is still intact. For hardware interrupts,
* leave the frame unchanged.
*/
/*
* For V86 calls, copy the registers off of the real mode stack onto
* the kernel stack as we want their updated values. Also, initialize
* the segment registers on the kernel stack.
*
* Note that the %esp in the kernel stack after this is garbage, but popa
* ignores it, so we don't have to fix it up.
*/
/*
* For V86 calls, copy the saved seg regs on the real mode stack back
* over to the btx_v86 structure. Also, conditionally update the
* saved eflags on the kernel stack based on the flags from the user.
*/
/*
* Return to the user task
*/
/*
* System Call.
*/
popl %gs # invoking
movl $MEM_USR,%eax # User base address
addl 0xc(%esp,1),%eax # Change to user
leal 0x4(%eax),%esp # stack
popl %eax # Call
call *%eax # program
intx30.1: orb $0x1,%ss:btx_hdr+0x7 # Flag reboot
jmp exit # Exit
/*
* Dump structure [EBX] to [EDI], using format string [ESI].
*/
dump.0: stosb # Save char
dump: lodsb # Load char
testb %al,%al # End of string?
jz dump.10 # Yes
testb $0x80,%al # Control?
jz dump.0 # No
movb %al,%ch # Save control
movb $'=',%al # Append
stosb # '='
lodsb # Get offset
pushl %esi # Save
movsbl %al,%esi # To
addl %ebx,%esi # pointer
testb $DMP_X16,%ch # Dump word?
jz dump.1 # No
lodsw # Get and
call hex16 # dump it
dump.1: testb $DMP_X32,%ch # Dump long?
jz dump.2 # No
lodsl # Get and
call hex32 # dump it
dump.2: testb $DMP_MEM,%ch # Dump memory?
jz dump.8 # No
pushl %ds # Save
testl $PSL_VM,0x50(%ebx) # V86 mode?
jnz dump.3 # Yes
verr 0x4(%esi) # Readable selector?
jnz dump.3 # No
ldsl (%esi),%esi # Load pointer
jmp dump.4 # Join common code
dump.3: lodsl # Set offset
xchgl %eax,%edx # Save
lodsl # Get segment
shll $0x4,%eax # * 0x10
addl %edx,%eax # + offset
xchgl %eax,%esi # Set pointer
dump.4: movb $2,%dl # Num lines
dump.4a: movb $0x10,%cl # Bytes to dump
dump.5: lodsb # Get byte and
call hex8 # dump it
decb %cl # Keep count
jz dump.6a # If done
movb $'-',%al # Separator
cmpb $0x8,%cl # Half way?
je dump.6 # Yes
movb $' ',%al # Use space
dump.6: stosb # Save separator
jmp dump.5 # Continue
dump.6a: decb %dl # Keep count
jz dump.7 # If done
movb $0xa,%al # Line feed
stosb # Save one
movb $7,%cl # Leading
movb $' ',%al # spaces
dump.6b: stosb # Dump
decb %cl # spaces
jnz dump.6b
jmp dump.4a # Next line
dump.7: popl %ds # Restore
dump.8: popl %esi # Restore
movb $0xa,%al # Line feed
testb $DMP_EOL,%ch # End of line?
jnz dump.9 # Yes
movb $' ',%al # Use spaces
stosb # Save one
dump.9: jmp dump.0 # Continue
dump.10: stosb # Terminate string
ret # To caller
/*
* Convert EAX, AX, or AL to hex, saving the result to [EDI].
*/
hex32: pushl %eax # Save
shrl $0x10,%eax # Do upper
call hex16 # 16
popl %eax # Restore
hex16: call hex16.1 # Do upper 8
hex8: pushl %eax # Save
shrb $0x4,%al # Do upper
call hex8.1 # 4
popl %eax # Restore
hex8.1: andb $0xf,%al # Get lower 4
cmpb $0xa,%al # Convert
sbbb $0x69,%al # to hex
das # digit
orb $0x20,%al # To lower case
stosb # Save char
ret # (Recursive)
/*
* Output zero-terminated string [ESI] to the console.
*/
putstr.0: call putchr # Output char
putstr: lodsb # Load char
testb %al,%al # End of string?
jnz putstr.0 # No
ret # To caller
#ifdef BTX_SERIAL
.set SIO_PRT,SIOPRT # Base port
.set SIO_FMT,SIOFMT # 8N1
.set SIO_DIV,(115200/SIOSPD) # 115200 / SPD
/*
* int sio_init(void)
*/
sio_init: movw $SIO_PRT+0x3,%dx # Data format reg
movb $SIO_FMT|0x80,%al # Set format
outb %al,(%dx) # and DLAB
pushl %edx # Save
subb $0x3,%dl # Divisor latch reg
movw $SIO_DIV,%ax # Set
outw %ax,(%dx) # BPS
popl %edx # Restore
movb $SIO_FMT,%al # Clear
outb %al,(%dx) # DLAB
incl %edx # Modem control reg
movb $0x3,%al # Set RTS,
outb %al,(%dx) # DTR
incl %edx # Line status reg
call sio_getc.1 # Get character
/*
* int sio_flush(void)
*/
sio_flush: xorl %eax,%eax # Return value
xorl %ecx,%ecx # Timeout
movb $0x80,%ch # counter
sio_flush.1: call sio_ischar # Check for character
jz sio_flush.2 # Till none
loop sio_flush.1 # or counter is zero
movb $1, %al # Exhausted all tries
sio_flush.2: ret # To caller
/*
* void sio_putc(int c)
*/
sio_putc: movw $SIO_PRT+0x5,%dx # Line status reg
xor %ecx,%ecx # Timeout
movb $0x40,%ch # counter
sio_putc.1: inb (%dx),%al # Transmitter
testb $0x20,%al # buffer empty?
loopz sio_putc.1 # No
jz sio_putc.2 # If timeout
movb 0x4(%esp,1),%al # Get character
subb $0x5,%dl # Transmitter hold reg
outb %al,(%dx) # Write character
sio_putc.2: ret $0x4 # To caller
/*
* int sio_getc(void)
*/
sio_getc: call sio_ischar # Character available?
jz sio_getc # No
sio_getc.1: subb $0x5,%dl # Receiver buffer reg
inb (%dx),%al # Read character
ret # To caller
/*
* int sio_ischar(void)
*/
sio_ischar: movw $SIO_PRT+0x5,%dx # Line status register
xorl %eax,%eax # Zero
inb (%dx),%al # Received data
andb $0x1,%al # ready?
ret # To caller
/*
* Output character AL to the serial console.
*/
putchr: pusha # Save
cmpb $10, %al # is it a newline?
jne putchr.1 # no?, then leave
push $13 # output a carriage
call sio_putc # return first
movb $10, %al # restore %al
putchr.1: pushl %eax # Push the character
# onto the stack
call sio_putc # Output the character
popa # Restore
ret # To caller
#else
/*
* Output character AL to the console.
*/
putchr: pusha # Save
xorl %ecx,%ecx # Zero for loops
movl $BDA_POS,%ebx # BDA pointer
movw (%ebx),%dx # Cursor position
movl $0xb8000,%edi # Regen buffer (color)
cmpb %ah,BDA_SCR-BDA_POS(%ebx) # Mono mode?
jne putchr.1 # No
xorw %di,%di # Regen buffer (mono)
putchr.1: cmpb $0xa,%al # New line?
je putchr.2 # Yes
xchgl %eax,%ecx # Save char
movb $SCR_COL,%al # Columns per row
mulb %dh # * row position
addb %dl,%al # + column
adcb $0x0,%ah # position
shll %eax # * 2
xchgl %eax,%ecx # Swap char, offset
movw %ax,(%edi,%ecx,1) # Write attr:char
incl %edx # Bump cursor
cmpb $SCR_COL,%dl # Beyond row?
jb putchr.3 # No
putchr.2: xorb %dl,%dl # Zero column
incb %dh # Bump row
putchr.3: cmpb $SCR_ROW,%dh # Beyond screen?
jb putchr.4 # No
leal 2*SCR_COL(%edi),%esi # New top line
movw $(SCR_ROW-1)*SCR_COL/2,%cx # Words to move
rep # Scroll
movsl # screen
movb $0x20,%al # Space
movb $SCR_COL,%cl # Columns to clear
rep # Clear
stosw # line
movb $SCR_ROW-1,%dh # Bottom line
putchr.4: movw %dx,(%ebx) # Update position
popa # Restore
ret # To caller
#endif
.code16
/*
* Real Mode Hardware interrupt jump table.
*/
intr20: push $0x8 # Int 0x20: IRQ0
jmp int_hwr # V86 int 0x8
push $0x9 # Int 0x21: IRQ1
jmp int_hwr # V86 int 0x9
push $0xa # Int 0x22: IRQ2
jmp int_hwr # V86 int 0xa
push $0xb # Int 0x23: IRQ3
jmp int_hwr # V86 int 0xb
push $0xc # Int 0x24: IRQ4
jmp int_hwr # V86 int 0xc
push $0xd # Int 0x25: IRQ5
jmp int_hwr # V86 int 0xd
push $0xe # Int 0x26: IRQ6
jmp int_hwr # V86 int 0xe
push $0xf # Int 0x27: IRQ7
jmp int_hwr # V86 int 0xf
push $0x70 # Int 0x28: IRQ8
jmp int_hwr # V86 int 0x70
push $0x71 # Int 0x29: IRQ9
jmp int_hwr # V86 int 0x71
push $0x72 # Int 0x2a: IRQ10
jmp int_hwr # V86 int 0x72
push $0x73 # Int 0x2b: IRQ11
jmp int_hwr # V86 int 0x73
push $0x74 # Int 0x2c: IRQ12
jmp int_hwr # V86 int 0x74
push $0x75 # Int 0x2d: IRQ13
jmp int_hwr # V86 int 0x75
push $0x76 # Int 0x2e: IRQ14
jmp int_hwr # V86 int 0x76
push $0x77 # Int 0x2f: IRQ15
jmp int_hwr # V86 int 0x77
/*
* Reflect hardware interrupts in real mode.
*/
int_hwr: push %ax # Save
push %ds # Save
push %bp # Save
mov %sp,%bp # Address stack frame
xchg %bx,6(%bp) # Swap BX, int no
xor %ax,%ax # Set %ds:%bx to
shl $2,%bx # point to
mov %ax,%ds # IDT entry
mov (%bx),%ax # Load IP
mov 2(%bx),%bx # Load CS
xchg %ax,4(%bp) # Swap saved %ax,%bx with
xchg %bx,6(%bp) # CS:IP of handler
pop %bp # Restore
pop %ds # Restore
lret # Jump to handler
.p2align 4
/*
* Global descriptor table.
*/
gdt: .word 0x0,0x0,0x0,0x0 # Null entry
.word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE
.word 0xffff,0x0,0x9200,0xcf # SEL_SDATA
.word 0xffff,0x0,0x9a00,0x0 # SEL_RCODE
.word 0xffff,0x0,0x9200,0x0 # SEL_RDATA
.word 0xffff,MEM_USR,0xfa00,0xcf# SEL_UCODE
.word 0xffff,MEM_USR,0xf200,0xcf# SEL_UDATA
tss_desc: .word _TSSLM,MEM_TSS,0x8900,0x0 # SEL_TSS
gdt.1:
/*
* Pseudo-descriptors.
*/
gdtdesc: .word gdt.1-gdt-1,gdt,0x0 # GDT
idtdesc: .word _IDTLM,MEM_IDT,0x0 # IDT
ivtdesc: .word 0x400-0x0-1,0x0,0x0 # IVT
/*
* IDT construction control string.
*/
idtctl: .byte 0x10, 0x8e # Int 0x0-0xf
.word 0x7dfb,intx00 # (exceptions)
.byte 0x10, 0x8e # Int 0x10
.word 0x1, intx10 # (exception)
.byte 0x10, 0x8e # Int 0x20-0x2f
.word 0xffff,intx20 # (hardware)
.byte 0x1, 0xee # int 0x30
.word 0x1, intx30 # (system call)
.byte 0x2, 0xee # Int 0x31-0x32
.word 0x1, intx31 # (V86, null)
.byte 0x0 # End of string
/*
* Dump format string.
*/
dmpfmt: .byte '\n' # "\n"
.ascii "int" # "int="
.byte 0x80|DMP_X32, 0x40 # "00000000 "
.ascii "err" # "err="
.byte 0x80|DMP_X32, 0x44 # "00000000 "
.ascii "efl" # "efl="
.byte 0x80|DMP_X32, 0x50 # "00000000 "
.ascii "eip" # "eip="
.byte 0x80|DMP_X32|DMP_EOL,0x48 # "00000000\n"
.ascii "eax" # "eax="
.byte 0x80|DMP_X32, 0x34 # "00000000 "
.ascii "ebx" # "ebx="
.byte 0x80|DMP_X32, 0x28 # "00000000 "
.ascii "ecx" # "ecx="
.byte 0x80|DMP_X32, 0x30 # "00000000 "
.ascii "edx" # "edx="
.byte 0x80|DMP_X32|DMP_EOL,0x2c # "00000000\n"
.ascii "esi" # "esi="
.byte 0x80|DMP_X32, 0x1c # "00000000 "
.ascii "edi" # "edi="
.byte 0x80|DMP_X32, 0x18 # "00000000 "
.ascii "ebp" # "ebp="
.byte 0x80|DMP_X32, 0x20 # "00000000 "
.ascii "esp" # "esp="
.byte 0x80|DMP_X32|DMP_EOL,0x0 # "00000000\n"
.ascii "cs" # "cs="
.byte 0x80|DMP_X16, 0x4c # "0000 "
.ascii "ds" # "ds="
.byte 0x80|DMP_X16, 0xc # "0000 "
.ascii "es" # "es="
.byte 0x80|DMP_X16, 0x8 # "0000 "
.ascii " " # " "
.ascii "fs" # "fs="
.byte 0x80|DMP_X16, 0x10 # "0000 "
.ascii "gs" # "gs="
.byte 0x80|DMP_X16, 0x14 # "0000 "
.ascii "ss" # "ss="
.byte 0x80|DMP_X16|DMP_EOL,0x4 # "0000\n"
.ascii "cs:eip" # "cs:eip="
.byte 0x80|DMP_MEM|DMP_EOL,0x48 # "00 00 ... 00 00\n"
.ascii "ss:esp" # "ss:esp="
.byte 0x80|DMP_MEM|DMP_EOL,0x0 # "00 00 ... 00 00\n"
.asciz "BTX halted\n" # End
/*
* Bad VM86 call panic
*/
badvm86: .asciz "Invalid VM86 Request\n"
/*
* End of BTX memory.
*/
.p2align 4
break: