diff --git a/src/arch/i386/prefix/libprefix.S b/src/arch/i386/prefix/libprefix.S new file mode 100644 index 00000000..4dcb50d9 --- /dev/null +++ b/src/arch/i386/prefix/libprefix.S @@ -0,0 +1,178 @@ + +#define CR0_PE 1 + + + .arch i386 + .section ".prefix", "awx", @progbits + +/**************************************************************************** + * alloc_basemem (real-mode near call) + * + * Allocate space from base memory via the BIOS free base memory + * counter at 40: 13 + * + * Parameters: + * %cx : Number of bytes to allocate + * Returns: + * %es : Segment address of newly allocated memory + **************************************************************************** + */ + .section ".prefix" + .code16 +alloc_basemem: + /* Preserve registers */ + pushw %cx + pushw %ax + + /* %fs = 0x40, %ax = fbms */ + movw $40, %ax + movw %ax, %fs + + /* Round up %cx to nearest kB, subtract from FBMS */ + addw $0x03ff, %cx + andw $0xfc00, %cx + shrw $10, %cx + movw %fs:0x13, %ax + subw %cx, %ax + movw %ax, %fs:0x13 + + /* Convert to segment address in %es */ + shlw $6, %ax + movw %ax, %es + + /* Restore registers and return */ + popw %ax + popw %cx + ret + + + .section ".prefix" + .align 16 +gdt: +gdt_limit: .word gdt_length - 1 +gdt_base: .long gdt + .word 0 /* padding */ + +cs16: /* 16 bit code segment, base at real-mode %cs:0000 */ + .equ CS16, cs16 - gdt + .word 0xffff, 0 + .byte 0, 0x9b, 0, 0 + +ss16: /* 16 bit stack segment, base at real-mode %ss:0000 */ + .equ SS16, ss16 - gdt + .word 0xffff, 0 + .byte 0, 0x93, 0, 0 + +flat_ds: /* 16 bit data segment, zero base, 4GB limit */ + .equ FLAT_DS, flat_ds - gdt + .word 0xffff, 0 + .byte 0, 0x9f, 0xcf, 0 + +gdt_end: + .equ gdt_length, gdt_end - gdt + + + + + .section ".prefix" + .code16 +prot16_call: + + + /* Install .data16 to top of base memory */ + movw %cs, %ax + addw $_data16_load_offset_pgh, %ax + movw %ax, %ds + movw $_data16_size, %cx + call alloc_basemem + xorw %si, %si + xorw %di, %di + movw $_data16_progbits_size, %cx + rep movsb /* or "call decompress16" */ + + /* Install .code16 to top of base memory */ + movw %cs, %ax + addw $_code16_load_offset_pgh, %ax + movw %ax, %ds + movw $_code16_size, %cx + call alloc_basemem + xorw %si, %si + xorw %di, %di + rep movsb /* or "call decompress16" */ + + /* Push flags and real-mode segment registers */ + pushfl + push %gs + push %fs + push %es + push %ds + push %ss + push %cs + + /* Physical address of %cs:0000 to %ebx, of %ss:0000 to %eax */ + xorl %ebx, %ebx + movw %cs, %bx + shll $4, %ebx + xorl %eax, %eax + movw %ss, %ax + shll $4, %eax + + /* Set up GDT and switch to protected mode */ + addl %ebx, %cs:gdt_base + orl %ebx, %cs:(cs16+2) + orl %eax, %cs:(ss16+2) + cli + data32 lgdt %cs:gdt + movl %cr0, %eax + orb $CR0_PE, %al + movl %eax, %cr0 + data32 ljmp $CS16, $1f +1: movw $SS16, %ax + movw %ax, %ss + movw $FLAT_DS, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + + /* Install .text and .data to 2MB mark. Use 2MB to avoid + * having to deal with A20. + */ + leal _text_load_offset(%ebx), %esi + movl $( 2 * 1024 * 1024 ), %edi + movl $_text_and_data_progbits_size, %ecx + addr32 rep movsb /* or "call decompress16" */ + + /* Restore real-mode segment limits */ + movw %ss, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + + /* Return to real mode, restore segment registers and flags */ + pushw $1f + movl %cr0, %eax + andb $0!CR0_PE, %al + movl %eax, %cr0 + lret /* used as equivalent of pop %cs */ +1: pop %ss + pop %ds + pop %es + pop %fs + pop %gs + popfl + + /* Call init_gdt */ + pushw %cs + pushw $1f + pushw %es + pushw $init_gdt + lret /* lcall %es:init_gdt */ +1: + + + + ret + +