diff options
Diffstat (limited to 'e820_bios.S')
-rw-r--r-- | e820_bios.S | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/e820_bios.S b/e820_bios.S new file mode 100644 index 0000000..833cd31 --- /dev/null +++ b/e820_bios.S @@ -0,0 +1,202 @@ +/* + * e820_bios.S: read e820 by int 15h call. + * + * The C language function exported by this file is: + * int get_e820_by_bios(void *e820_buf); + * @e820_buf: e820 mem map buffer, allocated by caller + * return: number of e820 entries + * + * Copyright (C) 2013 Intel Corporation. + * Author: Bin Gao <bin.gao@intel.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include "bootstub.h" + +/* Real mode low memory layout */ +#define IDT_START 0x0 +#define RELOCATED_START 0xa000 +#define STACK_START 0xb000 +#define DATA_START 0xb200 + +#define SAVED_GDTR_ADDR 0xb100 +#define SAVED_IDTR_ADDR 0xb110 +#define COUNT_ADDR 0xb120 +#define TOTAL_COUNT_ADDR 0xb130 +#define MIN_BUF_LEN 20 +#define BUF_LEN 2048 +#define MAX_NR_ENTRIES 128 + +#define SMAP 0x534d4150 +#define E820 0xe820 + +.text +.section ".text.head","ax",@progbits + + .code32 + .globl get_e820_by_bios +get_e820_by_bios: + jmp start_32bit + + .balign 16 +idtr: + .word 0xffff + .long IDT_START + + .balign 16 +gdt: + .quad 0 + .quad GDT_ENTRY(0x009b, 0, 0xffff) + .quad GDT_ENTRY(0x0093, 0, 0xffff) +gdtr: + .word 3*8-1 + .long gdt + +saved_esp: + .long 0 + +start_32bit: + pushal + pushfl + + /* Save ESP, GDTR and IDTR registers */ + movl $saved_esp, %eax + movl %esp, (%eax) + xorl %eax, %eax + sidtl SAVED_IDTR_ADDR(%eax) + sgdtl SAVED_GDTR_ADDR(%eax) + + /* Relocate real mode codes to 64k segment */ + movl $relocated_end + 4, %ecx + subl $relocated_start, %ecx + shrl $2, %ecx + movl $relocated_start, %esi + movl $RELOCATED_START, %edi + rep movsl + + /* Set up real mode IDT */ + lidtl %cs:idtr + + /* Set up real mode GDT */ + lgdtl %cs:gdtr + movl $16, %ecx + movl %ecx, %ds + movl %ecx, %es + movl %ecx, %fs + movl %ecx, %gs + movl %ecx, %ss + + /* Switch to 16bit segment */ + ljmpl $8, $RELOCATED_START + + .code16 +relocated_start: +reloc_base = . + + /* Switch to real mode */ + andb $0x10, %al + movl %eax, %cr0 + ljmpw $0, $realmode_entry - relocated_start + RELOCATED_START + +realmode_entry = . + /* In real mode now, set up segment selectors */ + movl $0, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + movl %eax, %gs + movl %eax, %fs + + movl $STACK_START, %esp + + /* Do int 15h call */ + movl $COUNT_ADDR, %eax + movl $0, (%eax) + movl $TOTAL_COUNT_ADDR, %eax + movl $0, (%eax) + xorl %ebx, %ebx + movw $DATA_START, %di +again: + movw $E820, %ax + movw $BUF_LEN, %cx + movl $SMAP, %edx + int $0x15 + jc error /* EFLGAS.CF is set */ + cmpl $SMAP, %eax + jne error /* eax is not 'SMAP' */ + cmpw $MIN_BUF_LEN, %cx + jl error /* returned buffer len < 20 */ + cmpw $BUF_LEN, %cx + jg error /* returned buffer len > provided buffer len */ + movl $TOTAL_COUNT_ADDR, %eax + addw %cx, (%eax) + movl $COUNT_ADDR, %eax + incl (%eax) + movl (%eax), %eax + cmpl $MAX_NR_ENTRIES, %eax /* max supported entries: 128 */ + jge done + testl %ebx, %ebx /* ebx == 0: done, ebx != 0: continue */ + je done + addw %cx, %di + jmp again +done: + jmp 2f +error: + movl $COUNT_ADDR, %eax + movl $~0, (%eax) +2: + + /* Switch back to protected mode */ + xorl %ebx, %ebx + lidtl SAVED_IDTR_ADDR(%ebx) + lgdtl SAVED_GDTR_ADDR(%ebx) + movl %cr0, %ebx + orb $1, %bl + movl %ebx, %cr0 + .byte 0x66, 0xea /* opcode(JMP FAR) with operand size override */ + .long resumed_protected_mode /* offset */ + .word __BOOT_CS /* segment selector */ +relocated_end = . + + .code32 +resumed_protected_mode: + cli /* in case real mode codes turn on interrrupt! */ + /* Restore segment registers */ + movl $__BOOT_DS, %ebx + movl %ebx, %ds + movl %ebx, %es + movl %ebx, %gs + movl %ebx, %fs + movl %ebx, %ss + + /* Restore stack pointer */ + movl $saved_esp, %eax + movl (%eax), %esp + + /* Copy e820 data from our buffer to caller's buffer */ + xorl %eax, %eax + movl TOTAL_COUNT_ADDR(%eax), %ecx + movl $DATA_START, %esi + movl 40(%esp), %edi + rep movsb + + popfl + popal + + /* Return number of e820 entries */ + movl $COUNT_ADDR, %eax + movl (%eax), %eax + ret |