/* * 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: head/sys/boot/i386/btx/btxldr/btxldr.S 235154 2012-05-09 08:04:29Z avg $ */ #include #define RBX_MUTE 0x10 /* -m */ #define OPT_SET(opt) (1 << (opt)) /* * Prototype BTX loader program, written in a couple of hours. The * real thing should probably be more flexible, and in C. */ /* * Memory locations. */ .set MEM_STUB,0x600 # Real mode stub .set MEM_ESP,0x1000 # New stack pointer .set MEM_TBL,0x5000 # BTX page tables .set MEM_ENTRY,0x9010 # BTX entry point .set MEM_DATA,start+0x1000 # Data segment /* * Segment selectors. */ .set SEL_SCODE,0x8 # 4GB code .set SEL_SDATA,0x10 # 4GB data .set SEL_RCODE,0x18 # 64K code .set SEL_RDATA,0x20 # 64K data /* * Paging constants. */ .set PAG_SIZ,0x1000 # Page size .set PAG_ENT,0x4 # Page entry size /* * Screen constants. */ .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 /* * Required by aout gas inadequacy. */ .set SIZ_STUB,0x1a # Size of stub /* * We expect to be loaded by boot2 at the origin defined in ./Makefile. */ .globl start /* * BTX program loader for ELF clients. */ start: cld # String ops inc testl $OPT_SET(RBX_MUTE), 4(%esp) # Check first argument setnz muted # for RBX_MUTE, set flag movl $m_logo,%esi # Identify call putstr # ourselves movzwl BDA_MEM,%eax # Get base memory shll $0xa,%eax # in bytes movl %eax,%ebp # Base of user stack #ifdef BTXLDR_VERBOSE movl $m_mem,%esi # Display call hexout # amount of call putstr # base memory #endif lgdt gdtdesc # Load new GDT /* * Relocate caller's arguments. */ #ifdef BTXLDR_VERBOSE movl $m_esp,%esi # Display movl %esp,%eax # caller call hexout # stack call putstr # pointer movl $m_args,%esi # Format string leal 0x4(%esp),%ebx # First argument movl $0x6,%ecx # Count start.1: movl (%ebx),%eax # Get argument and addl $0x4,%ebx # bump pointer call hexout # Display it loop start.1 # Till done call putstr # End message #endif movl BA_BOOTINFO+4(%esp),%esi # Source: bootinfo cmpl $0x0, %esi # If the bootinfo pointer je start_null_bi # is null, don't copy it movl BI_SIZE(%esi),%ecx # Allocate space subl %ecx,%ebp # for bootinfo movl %ebp,%edi # Destination rep # Copy movsb # it movl %ebp,BA_BOOTINFO+4(%esp) # Update pointer movl %edi,%ebp # Restore base pointer #ifdef BTXLDR_VERBOSE movl $m_rel_bi,%esi # Display movl %ebp,%eax # bootinfo call hexout # relocation call putstr # message #endif start_null_bi: movl $BOOTARGS_SIZE,%ecx # Fixed size of arguments testl $KARGS_FLAGS_EXTARG, BA_BOOTFLAGS+4(%esp) # Check for extra data jz start_fixed # Skip if the flag is not set addl BOOTARGS_SIZE+4(%esp),%ecx # Add size of variable args start_fixed: subl $ARGOFF,%ebp # Place args at fixed offset leal 0x4(%esp),%esi # Source movl %ebp,%edi # Destination rep # Copy movsb # them #ifdef BTXLDR_VERBOSE movl $m_rel_args,%esi # Display movl %ebp,%eax # argument call hexout # relocation call putstr # message #endif /* * Set up BTX kernel. */ movl $MEM_ESP,%esp # Set up new stack movl $MEM_DATA,%ebx # Data segment movl $m_vers,%esi # Display BTX call putstr # version message movb 0x5(%ebx),%al # Get major version addb $'0',%al # Display call putchr # it movb $'.',%al # And a call putchr # dot movb 0x6(%ebx),%al # Get minor xorb %ah,%ah # version movb $0xa,%dl # Divide divb %dl,%al # by 10 addb $'0',%al # Display call putchr # tens movb %ah,%al # Get units addb $'0',%al # Display call putchr # units call putstr # End message movl %ebx,%esi # BTX image movzwl 0x8(%ebx),%edi # Compute orl $PAG_SIZ/PAG_ENT-1,%edi # the incl %edi # BTX shll $0x2,%edi # load addl $MEM_TBL,%edi # address pushl %edi # Save load address movzwl 0xa(%ebx),%ecx # Image size #ifdef BTXLDR_VERBOSE pushl %ecx # Save image size #endif rep # Relocate movsb # BTX movl %esi,%ebx # Keep place #ifdef BTXLDR_VERBOSE movl $m_rel_btx,%esi # Restore popl %eax # parameters call hexout # and #endif popl %ebp # display #ifdef BTXLDR_VERBOSE movl %ebp,%eax # the call hexout # relocation call putstr # message #endif addl $PAG_SIZ,%ebp # Display #ifdef BTXLDR_VERBOSE movl $m_base,%esi # the movl %ebp,%eax # user call hexout # base call putstr # address #endif /* * Set up ELF-format client program. */ cmpl $0x464c457f,(%ebx) # ELF magic number? je start.3 # Yes movl $e_fmt,%esi # Display error call putstr # message start.2: jmp start.2 # Hang start.3: #ifdef BTXLDR_VERBOSE movl $m_elf,%esi # Display ELF call putstr # message movl $m_segs,%esi # Format string #endif movl $0x2,%edi # Segment count movl 0x1c(%ebx),%edx # Get e_phoff addl %ebx,%edx # To pointer movzwl 0x2c(%ebx),%ecx # Get e_phnum start.4: cmpl $0x1,(%edx) # Is p_type PT_LOAD? jne start.6 # No #ifdef BTXLDR_VERBOSE movl 0x4(%edx),%eax # Display call hexout # p_offset movl 0x8(%edx),%eax # Display call hexout # p_vaddr movl 0x10(%edx),%eax # Display call hexout # p_filesz movl 0x14(%edx),%eax # Display call hexout # p_memsz call putstr # End message #endif pushl %esi # Save pushl %edi # working pushl %ecx # registers movl 0x4(%edx),%esi # Get p_offset addl %ebx,%esi # as pointer movl 0x8(%edx),%edi # Get p_vaddr addl %ebp,%edi # as pointer movl 0x10(%edx),%ecx # Get p_filesz rep # Set up movsb # segment movl 0x14(%edx),%ecx # Any bytes subl 0x10(%edx),%ecx # to zero? jz start.5 # No xorb %al,%al # Then rep # zero stosb # them start.5: popl %ecx # Restore popl %edi # working popl %esi # registers decl %edi # Segments to do je start.7 # If none start.6: addl $0x20,%edx # To next entry loop start.4 # Till done start.7: #ifdef BTXLDR_VERBOSE movl $m_done,%esi # Display done call putstr # message #endif movl $start.8,%esi # Real mode stub movl $MEM_STUB,%edi # Destination movl $start.9-start.8,%ecx # Size rep # Relocate movsb # it ljmp $SEL_RCODE,$MEM_STUB # To 16-bit code .code16 start.8: xorw %ax,%ax # Data movb $SEL_RDATA,%al # selector movw %ax,%ss # Reload SS movw %ax,%ds # Reset movw %ax,%es # other movw %ax,%fs # segment movw %ax,%gs # limits movl %cr0,%eax # Switch to decw %ax # real movl %eax,%cr0 # mode ljmp $0,$MEM_ENTRY # Jump to BTX entry point start.9: .code32 /* * Output message [ESI] followed by EAX in hex. */ hexout: pushl %eax # Save call putstr # Display message popl %eax # Restore pushl %esi # Save pushl %edi # caller's movl $buf,%edi # Buffer pushl %edi # Save call hex32 # To hex xorb %al,%al # Terminate stosb # string popl %esi # Restore hexout.1: lodsb # Get a char cmpb $'0',%al # Leading zero? je hexout.1 # Yes testb %al,%al # End of string? jne hexout.2 # No decl %esi # Undo hexout.2: decl %esi # Adjust for inc call putstr # Display hex popl %edi # Restore popl %esi # caller's ret # To caller /* * Output zero-terminated string [ESI] to the console. */ putstr.0: call putchr # Output char putstr: lodsb # Load char testb %al,%al # End of string? jne putstr.0 # No ret # To caller /* * Output character AL to the console. */ putchr: testb $1,muted # Check muted jnz putchr.5 # do a nop 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 $' ',%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 putchr.5: 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) .data .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 gdt.1: gdtdesc: .word gdt.1-gdt-1 # Limit .long gdt # Base /* * Messages. */ m_logo: .asciz " \nBTX loader 1.00 " m_vers: .asciz "BTX version is \0\n" e_fmt: .asciz "Error: Client format not supported\n" #ifdef BTXLDR_VERBOSE m_mem: .asciz "Starting in protected mode (base mem=\0)\n" m_esp: .asciz "Arguments passed (esp=\0):\n" m_args: .asciz"\n" m_rel_bi: .asciz "Relocated bootinfo (size=48) to \0\n" m_rel_args: .asciz "Relocated arguments (size=18) to \0\n" m_rel_btx: .asciz "Relocated kernel (size=\0) to \0\n" m_base: .asciz "Client base address is \0\n" m_elf: .asciz "Client format is ELF\n" m_segs: .asciz "text segment: offset=" .asciz " vaddr=" .asciz " filesz=" .asciz " memsz=\0\n" .asciz "data segment: offset=" .asciz " vaddr=" .asciz " filesz=" .asciz " memsz=\0\n" m_done: .asciz "Loading complete\n" #endif /* * Flags */ muted: .byte 0x0 /* * Uninitialized data area. */ buf: # Scratch buffer