/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*/
/*
* Copyright 2016 Toomas Soome <tsoome@me.com>
*/
/*
* 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 in an already occupied memory area, the code is moved to a safe
* memory area and then btx __exec will be called with physical pointer
* to this area. __exec will set the pointer to %eax and call *%eax,
* so that on entry, we have the 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 32-bit protected mode to 16-bit
* 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 16-bit 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
/* reserve space for 3 entries */
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