769bad9f8a
Implement simple chain loader in loader; this update does add chain command, taking device or file as argument to load and start new boot loader. In case of BIOS, the chain will read the boot block to address 0000:7c00 and jumps on it. In case of UEFI, the chain command is to be used with efi application, typically stored in EFI System Partition. The update also does add simple menu entry, if the variable chain_disk is set. The value of the variable chain_disk is used as argument for chain loading. Relnotes: yes Differential Revision: https://reviews.freebsd.org/D5992
359 lines
8.3 KiB
ArmAsm
359 lines
8.3 KiB
ArmAsm
/*-
|
|
* Copyright 2015 Toomas Soome <tsoome@me.com>
|
|
* All rights reserved.
|
|
*
|
|
* 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.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
|
|
/*
|
|
* relocate is needed to support loading code which has to be located
|
|
* below 1MB, as both BTX and loader are using low memory area.
|
|
*
|
|
* relocate and start loaded code. Since loaded code may need to be
|
|
* placed to already occupied memory area, this code is moved to safe
|
|
* memory area and then btx __exec will be called with physical pointer
|
|
* to this area. __exec will set pointer to %eax and use call *%eax,
|
|
* so on entry, we have new "base" address in %eax.
|
|
*
|
|
* Relocate will first set up and load new safe GDT to shut down BTX,
|
|
* then loaded code will be relocated to final memory location,
|
|
* then machine will be switched from 32bit protected mode to 16bit
|
|
* protected mode following by switch to real mode with A20 enabled or
|
|
* disabled. Finally the loaded code will be started and it will take
|
|
* over the whole system.
|
|
*
|
|
* For now, the known "safe" memory area for relocate is 0x600,
|
|
* the actual "free" memory is supposed to start from 0x500, leaving
|
|
* first 0x100 bytes in reserve. As relocate code+data is very small,
|
|
* it will leave enough space to set up boot blocks to 0:7c00 or load
|
|
* linux kernel below 1MB space.
|
|
*/
|
|
/*
|
|
* segment selectors
|
|
*/
|
|
.set SEL_SCODE,0x8
|
|
.set SEL_SDATA,0x10
|
|
.set SEL_RCODE,0x18
|
|
.set SEL_RDATA,0x20
|
|
|
|
.p2align 4
|
|
.globl relocater
|
|
relocater:
|
|
cli
|
|
/*
|
|
* set up GDT from new location
|
|
*/
|
|
movl %eax, %esi /* our base address */
|
|
add $(relocater.1-relocater), %eax
|
|
jmp *%eax
|
|
relocater.1:
|
|
/* set up jump */
|
|
lea (relocater.2-relocater)(%esi), %eax
|
|
movl %eax, (jump_vector-relocater) (%esi)
|
|
|
|
/* set up gdt */
|
|
lea (gdt-relocater) (%esi), %eax
|
|
movl %eax, (gdtaddr-relocater) (%esi)
|
|
|
|
/* load gdt */
|
|
lgdt (gdtdesc - relocater) (%esi)
|
|
lidt (idt-relocater) (%esi)
|
|
|
|
/* update cs */
|
|
ljmp *(jump_vector-relocater) (%esi)
|
|
|
|
.code32
|
|
relocater.2:
|
|
xorl %eax, %eax
|
|
movb $SEL_SDATA, %al
|
|
movw %ax, %ss
|
|
movw %ax, %ds
|
|
movw %ax, %es
|
|
movw %ax, %fs
|
|
movw %ax, %gs
|
|
movl %cr0, %eax /* disable paging */
|
|
andl $~0x80000000,%eax
|
|
movl %eax, %cr0
|
|
xorl %ecx, %ecx /* flush TLB */
|
|
movl %ecx, %cr3
|
|
cld
|
|
/*
|
|
* relocate data loop. load source, dest and size from
|
|
* relocater_data[i], 0 value will stop the loop.
|
|
* registers used for move: %esi, %edi, %ecx.
|
|
* %ebx to keep base
|
|
* %edx for relocater_data offset
|
|
*/
|
|
movl %esi, %ebx /* base address */
|
|
xorl %edx, %edx
|
|
loop.1:
|
|
movl (relocater_data-relocater)(%ebx, %edx, 4), %eax
|
|
testl %eax, %eax
|
|
jz loop.2
|
|
movl (relocater_data-relocater)(%ebx, %edx, 4), %esi
|
|
inc %edx
|
|
movl (relocater_data-relocater)(%ebx, %edx, 4), %edi
|
|
inc %edx
|
|
movl (relocater_data-relocater)(%ebx, %edx, 4), %ecx
|
|
inc %edx
|
|
rep
|
|
movsb
|
|
jmp loop.1
|
|
loop.2:
|
|
movl %ebx, %esi /* restore esi */
|
|
/*
|
|
* data is relocated, switch to 16bit mode
|
|
*/
|
|
lea (relocater.3-relocater)(%esi), %eax
|
|
movl %eax, (jump_vector-relocater) (%esi)
|
|
movl $SEL_RCODE, %eax
|
|
movl %eax, (jump_vector-relocater+4) (%esi)
|
|
|
|
ljmp *(jump_vector-relocater) (%esi)
|
|
relocater.3:
|
|
.code16
|
|
|
|
movw $SEL_RDATA, %ax
|
|
movw %ax, %ds
|
|
movw %ax, %es
|
|
movw %ax, %fs
|
|
movw %ax, %gs
|
|
movw %ax, %ss
|
|
lidt (idt-relocater) (%esi)
|
|
lea (relocater.4-relocater)(%esi), %eax
|
|
movl %eax, (jump_vector-relocater) (%esi)
|
|
xorl %eax, %eax
|
|
movl %eax, (jump_vector-relocater+4) (%esi)
|
|
/* clear PE */
|
|
movl %cr0, %eax
|
|
dec %al
|
|
movl %eax, %cr0
|
|
ljmp *(jump_vector-relocater) (%esi)
|
|
relocater.4:
|
|
xorw %ax, %ax
|
|
movw %ax, %ds
|
|
movw %ax, %es
|
|
movw %ax, %fs
|
|
movw %ax, %gs
|
|
movw %ax, %ss
|
|
/*
|
|
* set real mode irq offsets
|
|
*/
|
|
movw $0x7008,%bx
|
|
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
|
|
# done
|
|
/*
|
|
* Should A20 be left enabled?
|
|
*/
|
|
/* movw imm16, %ax */
|
|
.byte 0xb8
|
|
.globl relocator_a20_enabled
|
|
relocator_a20_enabled:
|
|
.word 0
|
|
test %ax, %ax
|
|
jnz a20_done
|
|
|
|
movw $0xa00, %ax
|
|
movw %ax, %sp
|
|
movw %ax, %bp
|
|
|
|
/* Disable A20 */
|
|
movw $0x2400, %ax
|
|
int $0x15
|
|
# jnc a20_done
|
|
|
|
call a20_check_state
|
|
testb %al, %al
|
|
jz a20_done
|
|
|
|
inb $0x92
|
|
andb $(~0x03), %al
|
|
outb $0x92
|
|
jmp a20_done
|
|
|
|
a20_check_state:
|
|
movw $100, %cx
|
|
1:
|
|
xorw %ax, %ax
|
|
movw %ax, %ds
|
|
decw %ax
|
|
movw %ax, %es
|
|
xorw %ax, %ax
|
|
movw $0x8000, %ax
|
|
movw %ax, %si
|
|
addw $0x10, %ax
|
|
movw %ax, %di
|
|
movb %ds:(%si), %dl
|
|
movb %es:(%di), %al
|
|
movb %al, %dh
|
|
decb %dh
|
|
movb %dh, %ds:(%si)
|
|
outb %al, $0x80
|
|
outb %al, $0x80
|
|
movb %es:(%di), %dh
|
|
subb %dh, %al
|
|
xorb $1, %al
|
|
movb %dl, %ds:(%si)
|
|
testb %al, %al
|
|
jz a20_done
|
|
loop 1b
|
|
ret
|
|
a20_done:
|
|
/*
|
|
* set up registers
|
|
*/
|
|
/* movw imm16, %ax. */
|
|
.byte 0xb8
|
|
.globl relocator_ds
|
|
relocator_ds: .word 0
|
|
movw %ax, %ds
|
|
|
|
/* movw imm16, %ax. */
|
|
.byte 0xb8
|
|
.globl relocator_es
|
|
relocator_es: .word 0
|
|
movw %ax, %es
|
|
|
|
/* movw imm16, %ax. */
|
|
.byte 0xb8
|
|
.globl relocator_fs
|
|
relocator_fs: .word 0
|
|
movw %ax, %fs
|
|
|
|
/* movw imm16, %ax. */
|
|
.byte 0xb8
|
|
.globl relocator_gs
|
|
relocator_gs: .word 0
|
|
movw %ax, %gs
|
|
|
|
/* movw imm16, %ax. */
|
|
.byte 0xb8
|
|
.globl relocator_ss
|
|
relocator_ss: .word 0
|
|
movw %ax, %ss
|
|
|
|
/* movw imm16, %ax. */
|
|
.byte 0xb8
|
|
.globl relocator_sp
|
|
relocator_sp: .word 0
|
|
movzwl %ax, %esp
|
|
|
|
/* movw imm32, %eax. */
|
|
.byte 0x66, 0xb8
|
|
.globl relocator_esi
|
|
relocator_esi: .long 0
|
|
movl %eax, %esi
|
|
|
|
/* movw imm32, %edx. */
|
|
.byte 0x66, 0xba
|
|
.globl relocator_edx
|
|
relocator_edx: .long 0
|
|
|
|
/* movw imm32, %ebx. */
|
|
.byte 0x66, 0xbb
|
|
.globl relocator_ebx
|
|
relocator_ebx: .long 0
|
|
|
|
/* movw imm32, %eax. */
|
|
.byte 0x66, 0xb8
|
|
.globl relocator_eax
|
|
relocator_eax: .long 0
|
|
|
|
/* movw imm32, %ebp. */
|
|
.byte 0x66, 0xbd
|
|
.globl relocator_ebp
|
|
relocator_ebp: .long 0
|
|
|
|
sti
|
|
.byte 0xea /* ljmp */
|
|
.globl relocator_ip
|
|
relocator_ip:
|
|
.word 0
|
|
.globl relocator_cs
|
|
relocator_cs:
|
|
.word 0
|
|
|
|
/* GDT to reset BTX */
|
|
.code32
|
|
.p2align 4
|
|
jump_vector: .long 0
|
|
.long SEL_SCODE
|
|
|
|
gdt: .word 0x0, 0x0 /* null entry */
|
|
.byte 0x0, 0x0, 0x0, 0x0
|
|
.word 0xffff, 0x0 /* SEL_SCODE */
|
|
.byte 0x0, 0x9a, 0xcf, 0x0
|
|
.word 0xffff, 0x0 /* SEL_SDATA */
|
|
.byte 0x0, 0x92, 0xcf, 0x0
|
|
.word 0xffff, 0x0 /* SEL_RCODE */
|
|
.byte 0x0, 0x9a, 0x0f, 0x0
|
|
.word 0xffff, 0x0 /* SEL_RDATA */
|
|
.byte 0x0, 0x92, 0x0f, 0x0
|
|
gdt.1:
|
|
|
|
gdtdesc: .word gdt.1 - gdt - 1 /* limit */
|
|
gdtaddr: .long 0 /* base */
|
|
|
|
idt: .word 0x3ff
|
|
.long 0
|
|
|
|
.globl relocater_data
|
|
relocater_data:
|
|
.long 0 /* src */
|
|
.long 0 /* dest */
|
|
.long 0 /* size */
|
|
.long 0 /* src */
|
|
.long 0 /* dest */
|
|
.long 0 /* size */
|
|
.long 0 /* src */
|
|
.long 0 /* dest */
|
|
.long 0 /* size */
|
|
.long 0
|
|
|
|
.globl relocater_size
|
|
relocater_size:
|
|
.long relocater_size-relocater
|