Checking in because I don't want to lose this rather neat code for
authorMichael Brown <mcb30@etherboot.org>
Mon, 1 May 2006 21:26:44 +0000 (21:26 +0000)
committerMichael Brown <mcb30@etherboot.org>
Mon, 1 May 2006 21:26:44 +0000 (21:26 +0000)
running the decompresser in 16:16 protected mode using the real-mode
stack.  However, there's an even simpler way to do it...

src/arch/i386/prefix/libprefix.S [new file with mode: 0644]

diff --git a/src/arch/i386/prefix/libprefix.S b/src/arch/i386/prefix/libprefix.S
new file mode 100644 (file)
index 0000000..4dcb50d
--- /dev/null
@@ -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
+       
+