1*479ab7f0SSascha Wildner/* 2*479ab7f0SSascha Wildner * Copyright (c) 2000 John Baldwin 3*479ab7f0SSascha Wildner * All rights reserved. 4*479ab7f0SSascha Wildner * 5*479ab7f0SSascha Wildner * Redistribution and use in source and binary forms are freely 6*479ab7f0SSascha Wildner * permitted provided that the above copyright notice and this 7*479ab7f0SSascha Wildner * paragraph and the following disclaimer are duplicated in all 8*479ab7f0SSascha Wildner * such forms. 9*479ab7f0SSascha Wildner * 10*479ab7f0SSascha Wildner * This software is provided "AS IS" and without any express or 11*479ab7f0SSascha Wildner * implied warranties, including, without limitation, the implied 12*479ab7f0SSascha Wildner * warranties of merchantability and fitness for a particular 13*479ab7f0SSascha Wildner * purpose. 14*479ab7f0SSascha Wildner * 15*479ab7f0SSascha Wildner * 16*479ab7f0SSascha Wildner * $FreeBSD: src/sys/boot/i386/pxeldr/pxeldr.s,v 1.9 2003/09/03 08:12:20 phk Exp $ 17*479ab7f0SSascha Wildner * $DragonFly: src/sys/boot/pc32/pxeldr/pxeldr.S,v 1.5 2007/05/18 07:41:43 dillon Exp $ 18*479ab7f0SSascha Wildner */ 19*479ab7f0SSascha Wildner 20*479ab7f0SSascha Wildner/* 21*479ab7f0SSascha Wildner * This simple program is a preloader for the normal boot3 loader. It is 22*479ab7f0SSascha Wildner * simply prepended to the beginning of a fully built and btxld'd loader. 23*479ab7f0SSascha Wildner * It then copies the loader to the address boot2 normally loads it, 24*479ab7f0SSascha Wildner * emulates the boot[12] environment (protected mode, a bootinfo struct, 25*479ab7f0SSascha Wildner * etc.), and then jumps to the start of btxldr to start the boot process. 26*479ab7f0SSascha Wildner * This method allows a stock /boot/loader to be booted over the network 27*479ab7f0SSascha Wildner * via PXE w/o having to write a separate PXE-aware client just to load 28*479ab7f0SSascha Wildner * the loader. 29*479ab7f0SSascha Wildner */ 30*479ab7f0SSascha Wildner#include "../bootasm.h" 31*479ab7f0SSascha Wildner 32*479ab7f0SSascha Wildner /* 33*479ab7f0SSascha Wildner * a.out header fields 34*479ab7f0SSascha Wildner */ 35*479ab7f0SSascha Wildner .set AOUT_TEXT,0x04 # text segment size 36*479ab7f0SSascha Wildner .set AOUT_DATA,0x08 # data segment size 37*479ab7f0SSascha Wildner .set AOUT_BSS,0x0c # zerod BSS size 38*479ab7f0SSascha Wildner .set AOUT_SYMBOLS,0x10 # symbol table 39*479ab7f0SSascha Wildner .set AOUT_ENTRY,0x14 # entry point 40*479ab7f0SSascha Wildner .set AOUT_HEADER,MEM_PAGE_SIZE # size of the a.out header 41*479ab7f0SSascha Wildner 42*479ab7f0SSascha Wildner /* 43*479ab7f0SSascha Wildner * Flags for kargs->bootflags 44*479ab7f0SSascha Wildner */ 45*479ab7f0SSascha Wildner .set KARGS_FLAGS_PXE,0x2 # flag to indicate booting from 46*479ab7f0SSascha Wildner # PXE loader 47*479ab7f0SSascha Wildner /* 48*479ab7f0SSascha Wildner * Boot howto bits 49*479ab7f0SSascha Wildner */ 50*479ab7f0SSascha Wildner .set RB_SERIAL,0x1000 # serial console 51*479ab7f0SSascha Wildner 52*479ab7f0SSascha Wildner /* 53*479ab7f0SSascha Wildner * Segment selectors. 54*479ab7f0SSascha Wildner */ 55*479ab7f0SSascha Wildner .set SEL_SDATA,0x8 # Supervisor data 56*479ab7f0SSascha Wildner .set SEL_RDATA,0x10 # Real mode data 57*479ab7f0SSascha Wildner .set SEL_SCODE,0x18 # PM-32 code 58*479ab7f0SSascha Wildner .set SEL_SCODE16,0x20 # PM-16 code 59*479ab7f0SSascha Wildner 60*479ab7f0SSascha Wildner /* 61*479ab7f0SSascha Wildner * BTX constants 62*479ab7f0SSascha Wildner */ 63*479ab7f0SSascha Wildner .set INT_SYS,0x30 # BTX syscall interrupt 64*479ab7f0SSascha Wildner 65*479ab7f0SSascha Wildner /* 66*479ab7f0SSascha Wildner * Bit in BDA_KEYBOARD that is set if an enhanced 67*479ab7f0SSascha Wildner * keyboard is present. 68*479ab7f0SSascha Wildner */ 69*479ab7f0SSascha Wildner .set KEYBOARD_BIT,0x10 70*479ab7f0SSascha Wildner 71*479ab7f0SSascha Wildner /* 72*479ab7f0SSascha Wildner * We expect to be loaded by the BIOS at LOAD (0x7c00), 73*479ab7f0SSascha Wildner * which is the standard boot loader entry point. 74*479ab7f0SSascha Wildner */ 75*479ab7f0SSascha Wildner .code16 76*479ab7f0SSascha Wildner .globl start 77*479ab7f0SSascha Wildner .org 0x0, 0x0 78*479ab7f0SSascha Wildner 79*479ab7f0SSascha Wildner /* 80*479ab7f0SSascha Wildner * BTX program loader for PXE network booting 81*479ab7f0SSascha Wildner */ 82*479ab7f0SSascha Wildnerstart: cld # string ops inc 83*479ab7f0SSascha Wildner xorw %ax, %ax # zero %ax 84*479ab7f0SSascha Wildner movw %ax, %ss # setup the 85*479ab7f0SSascha Wildner movw $start, %sp # stack 86*479ab7f0SSascha Wildner movw %es, %cx # save PXENV+ segment 87*479ab7f0SSascha Wildner movw %ax, %ds # setup the 88*479ab7f0SSascha Wildner movw %ax, %es # data segments 89*479ab7f0SSascha Wildner andl $0xffff, %ecx # clear upper words 90*479ab7f0SSascha Wildner andl $0xffff, %ebx # of %ebx and %ecx 91*479ab7f0SSascha Wildner shll $4, %ecx # calculate the offset of 92*479ab7f0SSascha Wildner addl %ebx, %ecx # the PXENV+ struct and 93*479ab7f0SSascha Wildner pushl %ecx # save it on the stack 94*479ab7f0SSascha Wildner movw $welcome_msg, %si # %ds:(%si) -> welcome message 95*479ab7f0SSascha Wildner callw putstr # display the welcome message 96*479ab7f0SSascha Wildner 97*479ab7f0SSascha Wildner /* 98*479ab7f0SSascha Wildner * Setup the arguments that the loader is expecting 99*479ab7f0SSascha Wildner * from boot[12] 100*479ab7f0SSascha Wildner */ 101*479ab7f0SSascha Wildner movw $bootinfo_msg, %si # %ds:(%si) -> boot args message 102*479ab7f0SSascha Wildner callw putstr # display the message 103*479ab7f0SSascha Wildner movw $MEM_ARG, %bx # %ds:(%bx) -> boot args 104*479ab7f0SSascha Wildner movw %bx, %di # %es:(%di) -> boot args 105*479ab7f0SSascha Wildner xorl %eax, %eax # zero %eax 106*479ab7f0SSascha Wildner movw $(MEM_ARG_SIZE/4), %cx # Size of arguments in 32-bit 107*479ab7f0SSascha Wildner # dwords 108*479ab7f0SSascha Wildner rep # Clear the arguments 109*479ab7f0SSascha Wildner stosl # to zero 110*479ab7f0SSascha Wildner orb $KARGS_FLAGS_PXE, 0x8(%bx) # kargs->bootflags |= 111*479ab7f0SSascha Wildner # KARGS_FLAGS_PXE 112*479ab7f0SSascha Wildner popl 0xc(%bx) # kargs->pxeinfo = *PXENV+ 113*479ab7f0SSascha Wildner#ifdef ALWAYS_SERIAL 114*479ab7f0SSascha Wildner /* 115*479ab7f0SSascha Wildner * set the RBX_SERIAL bit in the howto byte. 116*479ab7f0SSascha Wildner */ 117*479ab7f0SSascha Wildner orl $RB_SERIAL, (%bx) # enable serial console 118*479ab7f0SSascha Wildner#endif 119*479ab7f0SSascha Wildner#ifdef PROBE_KEYBOARD 120*479ab7f0SSascha Wildner /* 121*479ab7f0SSascha Wildner * Look at the BIOS data area to see if we have an enhanced 122*479ab7f0SSascha Wildner * keyboard. If not, set the RBX_SERIAL bit in the howto 123*479ab7f0SSascha Wildner * byte. 124*479ab7f0SSascha Wildner */ 125*479ab7f0SSascha Wildner testb $KEYBOARD_BIT, BDA_KEYBOARD # keyboard present? 126*479ab7f0SSascha Wildner jnz keyb # yes, so skip 127*479ab7f0SSascha Wildner orl $RB_SERIAL, (%bx) # enable serial console 128*479ab7f0SSascha Wildnerkeyb: 129*479ab7f0SSascha Wildner#endif 130*479ab7f0SSascha Wildner /* 131*479ab7f0SSascha Wildner * Turn on the A20 address line 132*479ab7f0SSascha Wildner */ 133*479ab7f0SSascha Wildner callw seta20 # Turn A20 on 134*479ab7f0SSascha Wildner 135*479ab7f0SSascha Wildner /* 136*479ab7f0SSascha Wildner * Relocate the loader and BTX using a very lazy protected 137*479ab7f0SSascha Wildner * mode 138*479ab7f0SSascha Wildner */ 139*479ab7f0SSascha Wildner movw $relocate_msg, %si # Display the 140*479ab7f0SSascha Wildner callw putstr # relocation message 141*479ab7f0SSascha Wildner movl end+AOUT_ENTRY, %edi # %edi is the destination 142*479ab7f0SSascha Wildner movl $(end+AOUT_HEADER), %esi # %esi is 143*479ab7f0SSascha Wildner # the start of the text 144*479ab7f0SSascha Wildner # segment 145*479ab7f0SSascha Wildner movl end+AOUT_TEXT, %ecx # %ecx = length of the text 146*479ab7f0SSascha Wildner # segment 147*479ab7f0SSascha Wildner lgdt gdtdesc # setup our own gdt 148*479ab7f0SSascha Wildner cli # turn off interrupts 149*479ab7f0SSascha Wildner movl %cr0, %eax # Turn on 150*479ab7f0SSascha Wildner orb $0x1, %al # protected 151*479ab7f0SSascha Wildner movl %eax, %cr0 # mode 152*479ab7f0SSascha Wildner ljmp $SEL_SCODE,$pm_start # long jump to clear the 153*479ab7f0SSascha Wildner # instruction pre-fetch queue 154*479ab7f0SSascha Wildner .code32 155*479ab7f0SSascha Wildnerpm_start: movw $SEL_SDATA, %ax # Initialize 156*479ab7f0SSascha Wildner movw %ax, %ds # %ds and 157*479ab7f0SSascha Wildner movw %ax, %es # %es to a flat selector 158*479ab7f0SSascha Wildner rep # Relocate the 159*479ab7f0SSascha Wildner movsb # text segment 160*479ab7f0SSascha Wildner addl $(MEM_PAGE_SIZE - 1), %edi # pad %edi out to a new page 161*479ab7f0SSascha Wildner andl $~(MEM_PAGE_SIZE - 1), %edi # for the data segment 162*479ab7f0SSascha Wildner movl end+AOUT_DATA, %ecx # size of the data segment 163*479ab7f0SSascha Wildner rep # Relocate the 164*479ab7f0SSascha Wildner movsb # data segment 165*479ab7f0SSascha Wildner movl end+AOUT_BSS, %ecx # size of the bss 166*479ab7f0SSascha Wildner xorl %eax, %eax # zero %eax 167*479ab7f0SSascha Wildner addb $3, %cl # round %ecx up to 168*479ab7f0SSascha Wildner shrl $2, %ecx # a multiple of 4 169*479ab7f0SSascha Wildner rep # zero the 170*479ab7f0SSascha Wildner stosl # bss 171*479ab7f0SSascha Wildner movl end+AOUT_ENTRY, %esi # %esi -> relocated loader 172*479ab7f0SSascha Wildner addl $MEM_BTX_LDR_OFF, %esi # %esi -> BTX in the loader 173*479ab7f0SSascha Wildner movl $MEM_BTX_ORG, %edi # %edi -> where BTX needs to go 174*479ab7f0SSascha Wildner movzwl 0xa(%esi), %ecx # %ecx -> length of BTX 175*479ab7f0SSascha Wildner rep # Relocate 176*479ab7f0SSascha Wildner movsb # BTX 177*479ab7f0SSascha Wildner ljmp $SEL_SCODE16,$pm_16 # Jump to 16-bit PM 178*479ab7f0SSascha Wildner .code16 179*479ab7f0SSascha Wildnerpm_16: movw $SEL_RDATA, %ax # Initialize 180*479ab7f0SSascha Wildner movw %ax, %ds # %ds and 181*479ab7f0SSascha Wildner movw %ax, %es # %es to a real mode selector 182*479ab7f0SSascha Wildner movl %cr0, %eax # Turn off 183*479ab7f0SSascha Wildner andb $~0x1, %al # protected 184*479ab7f0SSascha Wildner movl %eax, %cr0 # mode 185*479ab7f0SSascha Wildner ljmp $0,$pm_end # Long jump to clear the 186*479ab7f0SSascha Wildner # instruction pre-fetch queue 187*479ab7f0SSascha Wildnerpm_end: sti # Turn interrupts back on now 188*479ab7f0SSascha Wildner 189*479ab7f0SSascha Wildner /* 190*479ab7f0SSascha Wildner * Copy the BTX client to MEM_BTX_USR 191*479ab7f0SSascha Wildner */ 192*479ab7f0SSascha Wildner xorw %ax, %ax # zero %ax and set 193*479ab7f0SSascha Wildner movw %ax, %ds # %ds and %es 194*479ab7f0SSascha Wildner movw %ax, %es # to segment 0 195*479ab7f0SSascha Wildner movw $MEM_BTX_USR, %di # Prepare to relocate 196*479ab7f0SSascha Wildner movw $btx_client, %si # the simple btx client 197*479ab7f0SSascha Wildner movw $(btx_client_end-btx_client), %cx # length of btx client 198*479ab7f0SSascha Wildner rep # Relocate the 199*479ab7f0SSascha Wildner movsb # simple BTX client 200*479ab7f0SSascha Wildner 201*479ab7f0SSascha Wildner /* 202*479ab7f0SSascha Wildner * Copy the boot[12] args to where the BTX client can 203*479ab7f0SSascha Wildner * see them 204*479ab7f0SSascha Wildner */ 205*479ab7f0SSascha Wildner movw $MEM_ARG, %si # where the args are at now 206*479ab7f0SSascha Wildner movw $MEM_BTX_USR_ARG, %di # where the args are moving to 207*479ab7f0SSascha Wildner movw $(MEM_ARG_SIZE/4), %cx # size of the arguments in longs 208*479ab7f0SSascha Wildner rep # Relocate 209*479ab7f0SSascha Wildner movsl # the words 210*479ab7f0SSascha Wildner 211*479ab7f0SSascha Wildner /* 212*479ab7f0SSascha Wildner * Save the entry point so the client can get to it later on 213*479ab7f0SSascha Wildner */ 214*479ab7f0SSascha Wildner movl end+AOUT_ENTRY, %eax # load the entry point 215*479ab7f0SSascha Wildner stosl # add it to the end of the 216*479ab7f0SSascha Wildner # arguments 217*479ab7f0SSascha Wildner /* 218*479ab7f0SSascha Wildner * Now we just start up BTX and let it do the rest 219*479ab7f0SSascha Wildner */ 220*479ab7f0SSascha Wildner movw $jump_message, %si # Display the 221*479ab7f0SSascha Wildner callw putstr # jump message 222*479ab7f0SSascha Wildner ljmp $0,$MEM_BTX_ENTRY # Jump to the BTX entry point 223*479ab7f0SSascha Wildner 224*479ab7f0SSascha Wildner /* 225*479ab7f0SSascha Wildner * Display a null-terminated string 226*479ab7f0SSascha Wildner */ 227*479ab7f0SSascha Wildnerputstr: lodsb # load %al from %ds:(%si) 228*479ab7f0SSascha Wildner testb %al,%al # stop at null 229*479ab7f0SSascha Wildner jnz putc # if the char != null, output it 230*479ab7f0SSascha Wildner retw # return when null is hit 231*479ab7f0SSascha Wildnerputc: movw $0x7,%bx # attribute for output 232*479ab7f0SSascha Wildner movb $0xe,%ah # BIOS: put_char 233*479ab7f0SSascha Wildner int $0x10 # call BIOS, print char in %al 234*479ab7f0SSascha Wildner jmp putstr # keep looping 235*479ab7f0SSascha Wildner 236*479ab7f0SSascha Wildner /* 237*479ab7f0SSascha Wildner * Enable A20. Put upper limit on amount of time we wait for the 238*479ab7f0SSascha Wildner * keyboard controller to get ready (65K x ISA access time). If 239*479ab7f0SSascha Wildner * we wait more than that amount it's likely that the hardware 240*479ab7f0SSascha Wildner * is legacy-free and simply doesn't have keyboard controller 241*479ab7f0SSascha Wildner * and don't need enabling A20 at all. 242*479ab7f0SSascha Wildner */ 243*479ab7f0SSascha Wildnerseta20: cli # Disable interrupts 244*479ab7f0SSascha Wildner xor %cx,%cx # Clear 245*479ab7f0SSascha Wildnerseta20.1: inc %cx # Increment, overflow? 246*479ab7f0SSascha Wildner jz seta20.3 # Yes 247*479ab7f0SSascha Wildner inb $0x64,%al # Get status 248*479ab7f0SSascha Wildner testb $0x2,%al # Busy? 249*479ab7f0SSascha Wildner jnz seta20.1 # Yes 250*479ab7f0SSascha Wildner movb $0xd1,%al # Command: Write 251*479ab7f0SSascha Wildner outb %al,$0x64 # output port 252*479ab7f0SSascha Wildnerseta20.2: inb $0x64,%al # Get status 253*479ab7f0SSascha Wildner testb $0x2,%al # Busy? 254*479ab7f0SSascha Wildner jnz seta20.2 # Yes 255*479ab7f0SSascha Wildner movb $0xdf,%al # Enable 256*479ab7f0SSascha Wildner outb %al,$0x60 # A20 257*479ab7f0SSascha Wildnerseta20.3: sti # Enable interrupts 258*479ab7f0SSascha Wildner retw # To caller 259*479ab7f0SSascha Wildner 260*479ab7f0SSascha Wildner /* 261*479ab7f0SSascha Wildner * BTX client to start btxldr 262*479ab7f0SSascha Wildner */ 263*479ab7f0SSascha Wildner .code32 264*479ab7f0SSascha Wildnerbtx_client: movl $(MEM_BTX_USR_ARG-MEM_BTX_USR+MEM_ARG_SIZE-4), %esi 265*479ab7f0SSascha Wildner # %ds:(%esi) -> end 266*479ab7f0SSascha Wildner # of boot[12] args 267*479ab7f0SSascha Wildner movl $(MEM_ARG_SIZE/4), %ecx # Number of words to push 268*479ab7f0SSascha Wildner std # Go backwards 269*479ab7f0SSascha Wildnerpush_arg: lodsl # Read argument 270*479ab7f0SSascha Wildner pushl %eax # Push it onto the stack 271*479ab7f0SSascha Wildner loop push_arg # Push all of the arguments 272*479ab7f0SSascha Wildner cld # In case anyone depends on this 273*479ab7f0SSascha Wildner pushl MEM_BTX_USR_ARG-MEM_BTX_USR+MEM_ARG_SIZE # Entry point of 274*479ab7f0SSascha Wildner # the loader 275*479ab7f0SSascha Wildner pushl %eax # Emulate a near call 276*479ab7f0SSascha Wildner movl $0x1, %eax # "exec" system call 277*479ab7f0SSascha Wildner int $INT_SYS # BTX system call 278*479ab7f0SSascha Wildnerbtx_client_end: 279*479ab7f0SSascha Wildner .code16 280*479ab7f0SSascha Wildner 281*479ab7f0SSascha Wildner .p2align 4 282*479ab7f0SSascha Wildner 283*479ab7f0SSascha Wildner /* 284*479ab7f0SSascha Wildner * Global descriptor table. 285*479ab7f0SSascha Wildner */ 286*479ab7f0SSascha Wildnergdt: .word 0x0,0x0,0x0,0x0 # Null entry 287*479ab7f0SSascha Wildner .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA 288*479ab7f0SSascha Wildner .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA 289*479ab7f0SSascha Wildner .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE (32-bit) 290*479ab7f0SSascha Wildner .word 0xffff,0x0,0x9a00,0x8f # SEL_SCODE16 (16-bit) 291*479ab7f0SSascha Wildnergdt.1: 292*479ab7f0SSascha Wildner 293*479ab7f0SSascha Wildner /* 294*479ab7f0SSascha Wildner * Pseudo-descriptors. 295*479ab7f0SSascha Wildner */ 296*479ab7f0SSascha Wildnergdtdesc: .word gdt.1-gdt-1 # Limit 297*479ab7f0SSascha Wildner .long gdt # Base 298*479ab7f0SSascha Wildner 299*479ab7f0SSascha Wildnerwelcome_msg: .asciz "PXE Loader 1.00\r\n\n" 300*479ab7f0SSascha Wildnerbootinfo_msg: .asciz "Building the boot loader arguments\r\n" 301*479ab7f0SSascha Wildnerrelocate_msg: .asciz "Relocating the loader and the BTX\r\n" 302*479ab7f0SSascha Wildnerjump_message: .asciz "Starting the BTX loader\r\n" 303*479ab7f0SSascha Wildner 304*479ab7f0SSascha Wildner .p2align 4 305*479ab7f0SSascha Wildnerend: 306