summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMichael Brown2006-05-01 23:26:44 +0200
committerMichael Brown2006-05-01 23:26:44 +0200
commitcc8821a4435139fe404dcfd1fd2cf90c3750c1c3 (patch)
treefbeb1649aafcceb14e40a79b8338d0bb988900ee /src
parentTypo (diff)
downloadipxe-cc8821a4435139fe404dcfd1fd2cf90c3750c1c3.tar.gz
ipxe-cc8821a4435139fe404dcfd1fd2cf90c3750c1c3.tar.xz
ipxe-cc8821a4435139fe404dcfd1fd2cf90c3750c1c3.zip
Checking in because I don't want to lose this rather neat code for
running the decompresser in 16:16 protected mode using the real-mode stack. However, there's an even simpler way to do it...
Diffstat (limited to 'src')
-rw-r--r--src/arch/i386/prefix/libprefix.S178
1 files changed, 178 insertions, 0 deletions
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
+
+