#define ENTRY(x) .globl x ; x: #define CR0_PE_ON 0x1 #define CR0_PE_OFF 0xfffffffe #define PROT_MODE_CSEG 0x8 #define PROT_MODE_DSEG 0x10 #define PSEUDO_RM_CSEG 0x18 #define PSEUDO_RM_DSEG 0x20 #include "header.h" ENTRY(set_mode) movl 4(%esp),%ebx pusha call prot_to_real .code16 push %bp push %ds mov $0x4F02, %ax int $0x10 pop %ds pop %bp DATA32 call real_to_prot .code32 popa ret ENTRY(get_modeinfo) movl 4(%esp),%ecx pusha call prot_to_real .code16 push %bp push %ds push %es push %di mov $MODEINFO_SEG,%ax mov %ax,%es mov $MODEINFO_OFF, %di mov $0x4F01, %ax int $0x10 pop %di pop %es pop %ds pop %bp DATA32 call real_to_prot .code32 popa ret ENTRY(real_to_prot) .code16 cli /* load the GDT register */ DATA32 ADDR32 lgdt gdtdesc /* turn on protected mode */ movl %cr0, %eax orl $CR0_PE_ON, %eax movl %eax, %cr0 /* jump to relocation, flush prefetch queue, and reload %cs */ DATA32 ljmp $PROT_MODE_CSEG, $protcseg /* * The ".code32" directive only works in GAS, the GNU assembler! * This gets out of "16-bit" mode. */ .code32 protcseg: /* reload other segment registers */ movw $PROT_MODE_DSEG, %ax movw %ax, %ds movw %ax, %es movw %ax, %fs movw %ax, %gs movw %ax, %ss /* put the return address in a known safe location */ movl (%esp), %eax movl %eax, STACKOFF /* get protected mode stack */ movl protstack, %eax movl %eax, %esp movl %eax, %ebp /* get return address onto the right stack */ movl STACKOFF, %eax movl %eax, (%esp) /* zero %eax */ xorl %eax, %eax /* return on the old (or initialized) stack! */ ret ENTRY(prot_to_real) /* just in case, set GDT */ lgdt gdtdesc /* save the protected mode stack */ movl %esp, %eax movl %eax, protstack /* get the return address */ movl (%esp), %eax movl %eax, STACKOFF /* set up new stack */ movl $STACKOFF, %eax movl %eax, %esp movl %eax, %ebp /* set up segment limits */ movw $PSEUDO_RM_DSEG, %ax movw %ax, %ds movw %ax, %es movw %ax, %fs movw %ax, %gs movw %ax, %ss /* this might be an extra step */ ljmp $PSEUDO_RM_CSEG, $tmpcseg /* jump to a 16 bit segment */ tmpcseg: .code16 /* clear the PE bit of CR0 */ movl %cr0, %eax andl $CR0_PE_OFF, %eax movl %eax, %cr0 /* flush prefetch queue, reload %cs */ DATA32 ljmp $0, $realcseg realcseg: /* we are in real mode now * set up the real mode segment registers : DS, SS, ES */ /* zero %eax */ xorl %eax, %eax movw %ax, %ds movw %ax, %es movw %ax, %fs movw %ax, %gs movw %ax, %ss /* restore interrupts */ sti /* return on new stack! */ DATA32 ret .code32 protstack: .long PROTSTACKINIT /* * This is the Global Descriptor Table * * An entry, a "Segment Descriptor", looks like this: * * 31 24 19 16 7 0 * ------------------------------------------------------------ * | | |B| |A| | | |1|0|E|W|A| | * | BASE 31..24 |G|/|0|V| LIMIT |P|DPL| TYPE | BASE 23:16 | * | | |D| |L| 19..16| | |1|1|C|R|A| | * ------------------------------------------------------------ * | | | * | BASE 15..0 | LIMIT 15..0 | * | | | * ------------------------------------------------------------ * * Note the ordering of the data items is reversed from the above * description. */ .p2align 2 /* force 4-byte alignment */ gdt: .word 0, 0 .byte 0, 0, 0, 0 /* code segment */ .word 0xFFFF, 0 .byte 0, 0x9A, 0xCF, 0 /* data segment */ .word 0xFFFF, 0 .byte 0, 0x92, 0xCF, 0 /* 16 bit real mode CS */ .word 0xFFFF, 0 .byte 0, 0x9E, 0, 0 /* 16 bit real mode DS */ .word 0xFFFF, 0 .byte 0, 0x92, 0, 0 /* this is the GDT descriptor */ gdtdesc: .word 0x27 /* limit */ .long gdt /* addr */