| /* |
| * 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. |
| */ |
| .set MEM_BTX,0x1000 # Start of BTX memory |
| .set MEM_ESP0,0x1800 # Supervisor stack |
| .set MEM_BUF,0x1800 # Scratch buffer |
| .set MEM_ESPR,0x5e00 # Real mode stack |
| .set MEM_IDT,0x5e00 # IDT |
| .set MEM_TSS,0x5f98 # TSS |
| .set MEM_MAP,0x6000 # I/O bit map |
| .set MEM_TSS_END,0x7fff # End of TSS |
| .set MEM_ORG,0x9000 # BTX code |
| .set MEM_USR,0xa000 # Start of user memory |
| /* |
| * Paging control. |
| */ |
| .set PAG_SIZ,0x1000 # Page size |
| .set PAG_CNT,0x1000 # Pages to map |
| /* |
| * Fields in %eflags. |
| */ |
| .set PSL_RESERVED_DEFAULT,0x00000002 |
| .set PSL_T,0x00000100 # Trap flag |
| .set PSL_I,0x00000200 # Interrupt enable flag |
| .set PSL_D,0x00000400 # String instruction direction |
| .set PSL_NT,0x00004000 # Nested task flag |
| .set PSL_VM,0x00020000 # Virtual 8086 mode flag |
| .set PSL_AC,0x00040000 # Alignment check flag |
| /* |
| * Segment selectors. |
| */ |
| .set SEL_SCODE,0x8 # Supervisor code |
| .set SEL_SDATA,0x10 # Supervisor data |
| .set SEL_RCODE,0x18 # Real mode code |
| .set SEL_RDATA,0x20 # Real mode data |
| .set SEL_UCODE,0x28|3 # User code |
| .set SEL_UDATA,0x30|3 # User data |
| .set SEL_TSS,0x38 # TSS |
| /* |
| * Task state segment fields. |
| */ |
| .set TSS_ESP0,0x4 # PL 0 ESP |
| .set TSS_SS0,0x8 # PL 0 SS |
| .set TSS_MAP,0x66 # I/O bit map base |
| /* |
| * System calls. |
| */ |
| .set SYS_EXIT,0x0 # Exit |
| .set SYS_EXEC,0x1 # Exec |
| /* |
| * Fields in V86 interface structure. |
| */ |
| .set V86_CTL,0x0 # Control flags |
| .set V86_ADDR,0x4 # Int number/address |
| .set V86_ES,0x8 # V86 ES |
| .set V86_DS,0xc # V86 DS |
| .set V86_FS,0x10 # V86 FS |
| .set V86_GS,0x14 # V86 GS |
| /* |
| * V86 control flags. |
| */ |
| .set V86F_ADDR,0x10000 # Segment:offset address |
| .set V86F_CALLF,0x20000 # Emulate far call |
| .set V86F_FLAGS,0x40000 # Return flags |
| /* |
| * Dump format control bytes. |
| */ |
| .set DMP_X16,0x1 # Word |
| .set DMP_X32,0x2 # Long |
| .set DMP_MEM,0x4 # Memory |
| .set DMP_EOL,0x8 # End of line |
| /* |
| * Screen defaults and assumptions. |
| */ |
| .set SCR_MAT,0x7 # Mode/attribute |
| .set SCR_COL,0x50 # Columns per row |
| .set SCR_ROW,0x19 # Rows per screen |
| /* |
| * BIOS Data Area locations. |
| */ |
| .set BDA_MEM,0x413 # Free memory |
| .set BDA_SCR,0x449 # Video mode |
| .set BDA_POS,0x450 # Cursor position |
| .set BDA_BOOT,0x472 # Boot howto flag |
| /* |
| * Derivations, for brevity. |
| */ |
| .set _ESP0H,MEM_ESP0>>0x8 # Byte 1 of ESP0 |
| .set _TSSIO,MEM_MAP-MEM_TSS # TSS I/O base |
| .set _TSSLM,MEM_TSS_END-MEM_TSS # TSS limit |
| .set _IDTLM,MEM_TSS-MEM_IDT-1 # IDT limit |
| /* |
| * Code segment. |
| */ |
| .globl start |
| .code16 |
| start: # Start of code |
| /* |
| * BTX header. |
| */ |
| btx_hdr: .byte 0xeb # Machine ID |
| .byte 0xe # Header size |
| .ascii "BTX" # Magic |
| .byte 0x1 # Major version |
| .byte 0x2 # Minor version |
| .byte BTX_FLAGS # Flags |
| .word PAG_CNT-MEM_ORG>>0xc # Paging control |
| .word break-start # Text size |
| .long 0x0 # Entry address |
| /* |
| * Initialization routine. |
| */ |
| init: cli # Disable interrupts |
| xor %ax,%ax # Zero/segment |
| mov %ax,%ss # Set up |
| mov $MEM_ESP0,%sp # stack |
| mov %ax,%es # Address |
| mov %ax,%ds # data |
| pushl $0x2 # Clear |
| popfl # flags |
| /* |
| * Initialize memory. |
| */ |
| mov $MEM_IDT,%di # Memory to initialize |
| mov $(MEM_ORG-MEM_IDT)/2,%cx # Words to zero |
| rep # Zero-fill |
| stosw # memory |
| /* |
| * Update real mode IDT for reflecting hardware interrupts. |
| */ |
| mov $intr20,%bx # Address first handler |
| mov $0x10,%cx # Number of handlers |
| mov $0x20*4,%di # First real mode IDT entry |
| init.0: mov %bx,(%di) # Store IP |
| inc %di # Address next |
| inc %di # entry |
| stosw # Store CS |
| add $4,%bx # Next handler |
| loop init.0 # Next IRQ |
| /* |
| * Create IDT. |
| */ |
| mov $MEM_IDT,%di |
| mov $idtctl,%si # Control string |
| init.1: lodsb # Get entry |
| cbw # count |
| xchg %ax,%cx # as word |
| jcxz init.4 # If done |
| lodsb # Get segment |
| xchg %ax,%dx # P:DPL:type |
| lodsw # Get control |
| xchg %ax,%bx # set |
| lodsw # Get handler offset |
| mov $SEL_SCODE,%dh # Segment selector |
| init.2: shr %bx # Handle this int? |
| jnc init.3 # No |
| mov %ax,(%di) # Set handler offset |
| mov %dh,0x2(%di) # and selector |
| mov %dl,0x5(%di) # Set P:DPL:type |
| add $0x4,%ax # Next handler |
| init.3: lea 0x8(%di),%di # Next entry |
| loop init.2 # Till set done |
| jmp init.1 # Continue |
| /* |
| * Initialize TSS. |
| */ |
| init.4: movb $_ESP0H,TSS_ESP0+1(%di) # Set ESP0 |
| movb $SEL_SDATA,TSS_SS0(%di) # Set SS0 |
| movb $_TSSIO,TSS_MAP(%di) # Set I/O bit map base |
| /* |
| * Bring up the system. |
| */ |
| mov $0x2820,%bx # Set protected mode |
| callw setpic # IRQ offsets |
| lidt idtdesc # Set IDT |
| lgdt gdtdesc # Set GDT |
| mov %cr0,%eax # Switch to protected |
| inc %ax # mode |
| mov %eax,%cr0 # |
| ljmp $SEL_SCODE,$init.8 # To 32-bit code |
| .code32 |
| init.8: xorl %ecx,%ecx # Zero |
| movb $SEL_SDATA,%cl # To 32-bit |
| movw %cx,%ss # stack |
| /* |
| * Launch user task. |
| */ |
| movb $SEL_TSS,%cl # Set task |
| ltr %cx # register |
| movl $MEM_USR,%edx # User base address |
| movzwl %ss:BDA_MEM,%eax # Get free memory |
| shll $0xa,%eax # To bytes |
| subl $ARGSPACE,%eax # Less arg space |
| subl %edx,%eax # Less base |
| movb $SEL_UDATA,%cl # User data selector |
| pushl %ecx # Set SS |
| pushl %eax # Set ESP |
| push $0x202 # Set flags (IF set) |
| push $SEL_UCODE # Set CS |
| pushl btx_hdr+0xc # Set EIP |
| pushl %ecx # Set GS |
| pushl %ecx # Set FS |
| pushl %ecx # Set DS |
| pushl %ecx # Set ES |
| pushl %edx # Set EAX |
| movb $0x7,%cl # Set remaining |
| init.9: push $0x0 # general |
| loop init.9 # registers |
| #ifdef BTX_SERIAL |
| call sio_init # setup the serial console |
| #endif |
| popa # and initialize |
| popl %es # Initialize |
| popl %ds # user |
| popl %fs # segment |
| popl %gs # registers |
| iret # To user mode |
| /* |
| * Exit routine. |
| */ |
| exit: cli # Disable interrupts |
| movl $MEM_ESP0,%esp # Clear stack |
| /* |
| * Turn off paging. |
| */ |
| movl %cr0,%eax # Get CR0 |
| andl $~0x80000000,%eax # Disable |
| movl %eax,%cr0 # paging |
| xorl %ecx,%ecx # Zero |
| movl %ecx,%cr3 # Flush TLB |
| /* |
| * Restore the GDT in case we caught a kernel trap. |
| */ |
| lgdt %cs:gdtdesc # Set GDT |
| /* |
| * To 16 bits. |
| */ |
| ljmpw $SEL_RCODE,$exit.1 # Reload CS |
| .code16 |
| exit.1: mov $SEL_RDATA,%cl # 16-bit selector |
| mov %cx,%ss # Reload SS |
| mov %cx,%ds # Load |
| mov %cx,%es # remaining |
| mov %cx,%fs # segment |
| mov %cx,%gs # registers |
| /* |
| * To real-address mode. |
| */ |
| dec %ax # Switch to |
| mov %eax,%cr0 # real mode |
| ljmp $0x0,$exit.2 # Reload CS |
| exit.2: xor %ax,%ax # Real mode segment |
| mov %ax,%ss # Reload SS |
| mov %ax,%ds # Address data |
| mov $0x7008,%bx # Set real mode |
| callw setpic # IRQ offsets |
| lidt ivtdesc # Set IVT |
| /* |
| * Reboot or await reset. |
| */ |
| sti # Enable interrupts |
| testb $0x1,btx_hdr+0x7 # Reboot? |
| exit.3: jz exit.3 # No |
| movw $0x1234, BDA_BOOT # Do a warm boot |
| ljmp $0xf000,$0xfff0 # reboot the machine |
| /* |
| * Set IRQ offsets by reprogramming 8259A PICs. |
| */ |
| setpic: in $0x21,%al # Save master |
| push %ax # IMR |
| in $0xa1,%al # Save slave |
| push %ax # IMR |
| movb $0x11,%al # ICW1 to |
| outb %al,$0x20 # master, |
| outb %al,$0xa0 # slave |
| movb %bl,%al # ICW2 to |
| outb %al,$0x21 # master |
| movb %bh,%al # ICW2 to |
| outb %al,$0xa1 # slave |
| movb $0x4,%al # ICW3 to |
| outb %al,$0x21 # master |
| movb $0x2,%al # ICW3 to |
| outb %al,$0xa1 # slave |
| movb $0x1,%al # ICW4 to |
| outb %al,$0x21 # master, |
| outb %al,$0xa1 # slave |
| pop %ax # Restore slave |
| outb %al,$0xa1 # IMR |
| pop %ax # Restore master |
| outb %al,$0x21 # IMR |
| retw # To caller |
| .code32 |
| /* |
| * Exception jump table. |
| */ |
| intx00: push $0x0 # Int 0x0: #DE |
| jmp ex_noc # Divide error |
| push $0x1 # Int 0x1: #DB |
| jmp ex_noc # Debug |
| push $0x3 # Int 0x3: #BP |
| jmp ex_noc # Breakpoint |
| push $0x4 # Int 0x4: #OF |
| jmp ex_noc # Overflow |
| push $0x5 # Int 0x5: #BR |
| jmp ex_noc # BOUND range exceeded |
| push $0x6 # Int 0x6: #UD |
| jmp ex_noc # Invalid opcode |
| push $0x7 # Int 0x7: #NM |
| jmp ex_noc # Device not available |
| push $0x8 # Int 0x8: #DF |
| jmp except # Double fault |
| push $0xa # Int 0xa: #TS |
| jmp except # Invalid TSS |
| push $0xb # Int 0xb: #NP |
| jmp except # Segment not present |
| push $0xc # Int 0xc: #SS |
| jmp except # Stack segment fault |
| push $0xd # Int 0xd: #GP |
| jmp except # General protection |
| push $0xe # Int 0xe: #PF |
| jmp except # Page fault |
| intx10: push $0x10 # Int 0x10: #MF |
| jmp ex_noc # Floating-point error |
| /* |
| * Save a zero error code. |
| */ |
| ex_noc: pushl (%esp,1) # Duplicate int no |
| movb $0x0,0x4(%esp,1) # Fake error code |
| /* |
| * Handle exception. |
| */ |
| except: cld # String ops inc |
| pushl %ds # Save |
| pushl %es # most |
| pusha # registers |
| pushl %gs # Set GS |
| pushl %fs # Set FS |
| pushl %ds # Set DS |
| pushl %es # Set ES |
| cmpw $SEL_SCODE,0x44(%esp,1) # Supervisor mode? |
| jne except.1 # No |
| pushl %ss # Set SS |
| jmp except.2 # Join common code |
| except.1: pushl 0x50(%esp,1) # Set SS |
| except.2: pushl 0x50(%esp,1) # Set ESP |
| push $SEL_SDATA # Set up |
| popl %ds # to |
| pushl %ds # address |
| popl %es # data |
| movl %esp,%ebx # Stack frame |
| movl $dmpfmt,%esi # Dump format string |
| movl $MEM_BUF,%edi # Buffer |
| pushl %edi # Dump to |
| call dump # buffer |
| popl %esi # and |
| call putstr # display |
| leal 0x18(%esp,1),%esp # Discard frame |
| popa # Restore |
| popl %es # registers |
| popl %ds # saved |
| cmpb $0x3,(%esp,1) # Breakpoint? |
| je except.3 # Yes |
| cmpb $0x1,(%esp,1) # Debug? |
| jne except.2a # No |
| testl $PSL_T,0x10(%esp,1) # Trap flag set? |
| jnz except.3 # Yes |
| except.2a: jmp exit # Exit |
| except.3: leal 0x8(%esp,1),%esp # Discard err, int no |
| iret # From interrupt |
| |
| /* |
| * Reboot the machine by setting the reboot flag and exiting |
| */ |
| reboot: orb $0x1,btx_hdr+0x7 # Set the reboot flag |
| jmp exit # Terminate BTX and reboot |
| |
| /* |
| * Protected Mode Hardware interrupt jump table. |
| */ |
| intx20: push $0x8 # Int 0x20: IRQ0 |
| jmp int_hw # V86 int 0x8 |
| push $0x9 # Int 0x21: IRQ1 |
| jmp int_hw # V86 int 0x9 |
| push $0xa # Int 0x22: IRQ2 |
| jmp int_hw # V86 int 0xa |
| push $0xb # Int 0x23: IRQ3 |
| jmp int_hw # V86 int 0xb |
| push $0xc # Int 0x24: IRQ4 |
| jmp int_hw # V86 int 0xc |
| push $0xd # Int 0x25: IRQ5 |
| jmp int_hw # V86 int 0xd |
| push $0xe # Int 0x26: IRQ6 |
| jmp int_hw # V86 int 0xe |
| push $0xf # Int 0x27: IRQ7 |
| jmp int_hw # V86 int 0xf |
| push $0x70 # Int 0x28: IRQ8 |
| jmp int_hw # V86 int 0x70 |
| push $0x71 # Int 0x29: IRQ9 |
| jmp int_hw # V86 int 0x71 |
| push $0x72 # Int 0x2a: IRQ10 |
| jmp int_hw # V86 int 0x72 |
| push $0x73 # Int 0x2b: IRQ11 |
| jmp int_hw # V86 int 0x73 |
| push $0x74 # Int 0x2c: IRQ12 |
| jmp int_hw # V86 int 0x74 |
| push $0x75 # Int 0x2d: IRQ13 |
| jmp int_hw # V86 int 0x75 |
| push $0x76 # Int 0x2e: IRQ14 |
| jmp int_hw # V86 int 0x76 |
| push $0x77 # Int 0x2f: IRQ15 |
| jmp int_hw # V86 int 0x77 |
| |
| /* |
| * Invoke real mode interrupt/function call from user mode with arguments. |
| */ |
| intx31: pushl $-1 # Dummy int no for btx_v86 |
| /* |
| * Invoke real mode interrupt/function call from protected mode. |
| * |
| * 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) |
| */ |
| int_hw: cld # String ops inc |
| pusha # Save gp regs |
| pushl %gs # Save |
| pushl %fs # seg |
| pushl %ds # regs |
| pushl %es |
| push $SEL_SDATA # Set up |
| popl %ds # to |
| pushl %ds # address |
| popl %es # data |
| leal 0x44(%esp,1),%esi # Base of frame |
| movl %esp,MEM_ESPR-0x04 # Save kernel stack pointer |
| movl -0x14(%esi),%eax # Get Int no |
| cmpl $-1,%eax # Hardware interrupt? |
| jne intusr.1 # Yes |
| /* |
| * 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. |
| */ |
| movl $MEM_USR,%ebx # User base |
| movl %ebx,%edx # address |
| addl -0x4(%esi),%ebx # User ESP |
| movl (%ebx),%ebp # btx_v86 pointer |
| addl %ebp,%edx # Flatten btx_v86 ptr |
| movl %edx,MEM_ESPR-0x08 # Save btx_v86 ptr |
| movl V86_ADDR(%edx),%eax # Get int no/address |
| movl V86_CTL(%edx),%edx # Get control flags |
| movl -0x08(%esi),%ebx # Save user flags in %ebx |
| testl $V86F_ADDR,%edx # Segment:offset? |
| jnz intusr.4 # Yes |
| andl $~(PSL_I|PSL_T|PSL_AC),%ebx # Disable interrupts, tracing, |
| # and alignment checking for |
| # interrupt handler |
| jmp intusr.3 # Skip hardware interrupt |
| /* |
| * 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. |
| */ |
| intusr.1: xorl %edx,%edx # Control flags |
| movl %edx,MEM_ESPR-0x08 # NULL btx_v86 ptr |
| movl $12,%ecx # Frame is 12 dwords |
| intusr.2: pushl $0x0 # Fill frame |
| loop intusr.2 # with zeros |
| movl $PSL_RESERVED_DEFAULT,%ebx # Set clean %eflags |
| /* |
| * Look up real mode IDT entry for hardware interrupts and VM86 INTx |
| * requests. |
| */ |
| intusr.3: shll $0x2,%eax # Scale |
| movl (%eax),%eax # Load int vector |
| jmp intusr.5 # Skip CALLF test |
| /* |
| * Panic if V86F_CALLF isn't set with V86F_ADDR. |
| */ |
| intusr.4: testl $V86F_CALLF,%edx # Far call? |
| jnz intusr.5 # Ok |
| movl %edx,0x30(%esp,1) # Place VM86 flags in int no |
| movl $badvm86,%esi # Display bad |
| call putstr # VM86 call |
| popl %es # Restore |
| popl %ds # seg |
| popl %fs # regs |
| popl %gs |
| popal # Restore gp regs |
| jmp ex_noc # Panic |
| /* |
| * %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. |
| */ |
| intusr.5: movw %bx,MEM_ESPR-0x12 # Pass user flags to real mode |
| # target |
| /* |
| * If this is a v86 call, copy the seg regs out of the btx_v86 structure. |
| */ |
| movl MEM_ESPR-0x08,%ecx # Get btx_v86 ptr |
| jecxz intusr.6 # Skip for hardware ints |
| leal -0x44(%esi),%edi # %edi => kernel stack seg regs |
| pushl %esi # Save |
| leal V86_ES(%ecx),%esi # %esi => btx_v86 seg regs |
| movl $4,%ecx # Copy seg regs |
| rep # from btx_v86 |
| movsl # to kernel stack |
| popl %esi # Restore |
| intusr.6: movl -0x08(%esi),%ebx # Copy user flags to real |
| movl %ebx,MEM_ESPR-0x0c # mode return trampoline |
| movl $rret_tramp,%ebx # Set return trampoline |
| movl %ebx,MEM_ESPR-0x10 # CS:IP |
| movl %eax,MEM_ESPR-0x16 # Real mode target CS:IP |
| ljmpw $SEL_RCODE,$intusr.7 # Change to 16-bit segment |
| .code16 |
| intusr.7: movl %cr0,%eax # Leave |
| dec %al # protected |
| movl %eax,%cr0 # mode |
| ljmpw $0x0,$intusr.8 |
| intusr.8: xorw %ax,%ax # Reset %ds |
| movw %ax,%ds # and |
| movw %ax,%ss # %ss |
| lidt ivtdesc # Set IVT |
| popl %es # Restore |
| popl %ds # seg |
| popl %fs # regs |
| popl %gs |
| popal # Restore gp regs |
| movw $MEM_ESPR-0x16,%sp # Switch to real mode stack |
| iret # Call target routine |
| /* |
| * 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 |
| */ |
| rret_tramp: movw $MEM_ESPR-0x08,%sp # Reset stack pointer |
| pushal # Save gp regs |
| pushl %gs # Save |
| pushl %fs # seg |
| pushl %ds # regs |
| pushl %es |
| pushfl # Save %eflags |
| pushl $PSL_RESERVED_DEFAULT|PSL_D # Use clean %eflags with |
| popfl # string ops dec |
| xorw %ax,%ax # Reset seg |
| movw %ax,%ds # regs |
| movw %ax,%es # (%ss is already 0) |
| lidt idtdesc # Set IDT |
| lgdt gdtdesc # Set GDT |
| mov %cr0,%eax # Switch to protected |
| inc %ax # mode |
| mov %eax,%cr0 # |
| ljmp $SEL_SCODE,$rret_tramp.1 # To 32-bit code |
| .code32 |
| rret_tramp.1: xorl %ecx,%ecx # Zero |
| movb $SEL_SDATA,%cl # Setup |
| movw %cx,%ss # 32-bit |
| movw %cx,%ds # seg |
| movw %cx,%es # regs |
| movl MEM_ESPR-0x04,%esp # Switch to kernel stack |
| leal 0x44(%esp,1),%esi # Base of frame |
| andb $~0x2,tss_desc+0x5 # Clear TSS busy |
| movb $SEL_TSS,%cl # Set task |
| ltr %cx # register |
| /* |
| * 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. |
| */ |
| cmpl $0,MEM_ESPR-0x08 # Leave saved regs unchanged |
| jz rret_tramp.3 # for hardware ints |
| /* |
| * 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. |
| */ |
| leal -0x18(%esi),%edi # Kernel stack GP regs |
| pushl %esi # Save |
| movl $MEM_ESPR-0x0c,%esi # Real mode stack GP regs |
| movl $8,%ecx # Copy GP regs from |
| rep # real mode stack |
| movsl # to kernel stack |
| movl $SEL_UDATA,%eax # Selector for data seg regs |
| movl $4,%ecx # Initialize %ds, |
| rep # %es, %fs, and |
| stosl # %gs |
| /* |
| * 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. |
| */ |
| movl MEM_ESPR-0x08,%ecx # Get btx_v86 ptr |
| leal V86_GS(%ecx),%edi # %edi => btx_v86 seg regs |
| leal MEM_ESPR-0x2c,%esi # %esi => real mode seg regs |
| xchgl %ecx,%edx # Save btx_v86 ptr |
| movl $4,%ecx # Copy seg regs |
| rep # from real mode stack |
| movsl # to btx_v86 |
| popl %esi # Restore |
| movl V86_CTL(%edx),%edx # Read V86 control flags |
| testl $V86F_FLAGS,%edx # User wants flags? |
| jz rret_tramp.3 # No |
| movl MEM_ESPR-0x3c,%eax # Read real mode flags |
| andl $~(PSL_T|PSL_NT),%eax # Clear unsafe flags |
| movw %ax,-0x08(%esi) # Update user flags (low 16) |
| /* |
| * Return to the user task |
| */ |
| rret_tramp.3: popl %es # Restore |
| popl %ds # seg |
| popl %fs # regs |
| popl %gs |
| popal # Restore gp regs |
| addl $4,%esp # Discard int no |
| iret # Return to user mode |
| |
| /* |
| * System Call. |
| */ |
| intx30: cmpl $SYS_EXEC,%eax # Exec system call? |
| jne intx30.1 # No |
| pushl %ss # Set up |
| popl %es # all |
| pushl %es # segment |
| popl %ds # registers |
| pushl %ds # for the |
| popl %fs # program |
| pushl %fs # we're |
| 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 |
| hex16.1: xchgb %ah,%al # Save/restore |
| 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 |
| movb $SCR_MAT,%ah # Mode/attribute |
| 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: |