/* * 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 * * 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