1 /* 2 * e820_bios.S: read e820 by int 15h call. 3 * 4 * The C language function exported by this file is: 5 * int get_e820_by_bios(void *e820_buf); 6 * @e820_buf: e820 mem map buffer, allocated by caller 7 * return: number of e820 entries 8 * 9 * Copyright (C) 2013 Intel Corporation. 10 * Author: Bin Gao <bin.gao (at) intel.com> 11 * 12 * This program is free software; you can redistribute it and/or modify it 13 * under the terms and conditions of the GNU General Public License, 14 * version 2, as published by the Free Software Foundation. 15 * 16 * This program is distributed in the hope it will be useful, but WITHOUT 17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 18 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 19 * more details. 20 * 21 * You should have received a copy of the GNU General Public License along with 22 * this program; if not, write to the Free Software Foundation, Inc., 23 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 24 * 25 */ 26 27 #include "bootstub.h" 28 29 /* Real mode low memory layout */ 30 #define IDT_START 0x0 31 #define RELOCATED_START 0xa000 32 #define STACK_START 0xb000 33 #define DATA_START 0xb200 34 35 #define SAVED_GDTR_ADDR 0xb100 36 #define SAVED_IDTR_ADDR 0xb110 37 #define COUNT_ADDR 0xb120 38 #define TOTAL_COUNT_ADDR 0xb130 39 #define MIN_BUF_LEN 20 40 #define BUF_LEN 2048 41 #define MAX_NR_ENTRIES 128 42 43 #define SMAP 0x534d4150 44 #define E820 0xe820 45 46 .text 47 .section ".text.head","ax",@progbits 48 49 .code32 50 .globl get_e820_by_bios 51 get_e820_by_bios: 52 jmp start_32bit 53 54 .balign 16 55 idtr: 56 .word 0xffff 57 .long IDT_START 58 59 .balign 16 60 gdt: 61 .quad 0 62 .quad GDT_ENTRY(0x009b, 0, 0xffff) 63 .quad GDT_ENTRY(0x0093, 0, 0xffff) 64 gdtr: 65 .word 3*8-1 66 .long gdt 67 68 saved_esp: 69 .long 0 70 71 start_32bit: 72 pushal 73 pushfl 74 75 /* Save ESP, GDTR and IDTR registers */ 76 movl $saved_esp, %eax 77 movl %esp, (%eax) 78 xorl %eax, %eax 79 sidtl SAVED_IDTR_ADDR(%eax) 80 sgdtl SAVED_GDTR_ADDR(%eax) 81 82 /* Relocate real mode codes to 64k segment */ 83 movl $relocated_end + 4, %ecx 84 subl $relocated_start, %ecx 85 shrl $2, %ecx 86 movl $relocated_start, %esi 87 movl $RELOCATED_START, %edi 88 rep movsl 89 90 /* Set up real mode IDT */ 91 lidtl %cs:idtr 92 93 /* Set up real mode GDT */ 94 lgdtl %cs:gdtr 95 movl $16, %ecx 96 movl %ecx, %ds 97 movl %ecx, %es 98 movl %ecx, %fs 99 movl %ecx, %gs 100 movl %ecx, %ss 101 102 /* Switch to 16bit segment */ 103 ljmpl $8, $RELOCATED_START 104 105 .code16 106 relocated_start: 107 reloc_base = . 108 109 /* Switch to real mode */ 110 andb $0x10, %al 111 movl %eax, %cr0 112 ljmpw $0, $realmode_entry - relocated_start + RELOCATED_START 113 114 realmode_entry = . 115 /* In real mode now, set up segment selectors */ 116 movl $0, %eax 117 movl %eax, %ds 118 movl %eax, %es 119 movl %eax, %ss 120 movl %eax, %gs 121 movl %eax, %fs 122 123 movl $STACK_START, %esp 124 125 /* Do int 15h call */ 126 movl $COUNT_ADDR, %eax 127 movl $0, (%eax) 128 movl $TOTAL_COUNT_ADDR, %eax 129 movl $0, (%eax) 130 xorl %ebx, %ebx 131 movw $DATA_START, %di 132 again: 133 movw $E820, %ax 134 movw $BUF_LEN, %cx 135 movl $SMAP, %edx 136 int $0x15 137 jc error /* EFLGAS.CF is set */ 138 cmpl $SMAP, %eax 139 jne error /* eax is not 'SMAP' */ 140 cmpw $MIN_BUF_LEN, %cx 141 jl error /* returned buffer len < 20 */ 142 cmpw $BUF_LEN, %cx 143 jg error /* returned buffer len > provided buffer len */ 144 movl $TOTAL_COUNT_ADDR, %eax 145 addw %cx, (%eax) 146 movl $COUNT_ADDR, %eax 147 incl (%eax) 148 movl (%eax), %eax 149 cmpl $MAX_NR_ENTRIES, %eax /* max supported entries: 128 */ 150 jge done 151 testl %ebx, %ebx /* ebx == 0: done, ebx != 0: continue */ 152 je done 153 addw %cx, %di 154 jmp again 155 done: 156 jmp 2f 157 error: 158 movl $COUNT_ADDR, %eax 159 movl $~0, (%eax) 160 2: 161 162 /* Switch back to protected mode */ 163 xorl %ebx, %ebx 164 lidtl SAVED_IDTR_ADDR(%ebx) 165 lgdtl SAVED_GDTR_ADDR(%ebx) 166 movl %cr0, %ebx 167 orb $1, %bl 168 movl %ebx, %cr0 169 .byte 0x66, 0xea /* opcode(JMP FAR) with operand size override */ 170 .long resumed_protected_mode /* offset */ 171 .word __BOOT_CS /* segment selector */ 172 relocated_end = . 173 174 .code32 175 resumed_protected_mode: 176 cli /* in case real mode codes turn on interrrupt! */ 177 /* Restore segment registers */ 178 movl $__BOOT_DS, %ebx 179 movl %ebx, %ds 180 movl %ebx, %es 181 movl %ebx, %gs 182 movl %ebx, %fs 183 movl %ebx, %ss 184 185 /* Restore stack pointer */ 186 movl $saved_esp, %eax 187 movl (%eax), %esp 188 189 /* Copy e820 data from our buffer to caller's buffer */ 190 xorl %eax, %eax 191 movl TOTAL_COUNT_ADDR(%eax), %ecx 192 movl $DATA_START, %esi 193 movl 40(%esp), %edi 194 rep movsb 195 196 popfl 197 popal 198 199 /* Return number of e820 entries */ 200 movl $COUNT_ADDR, %eax 201 movl (%eax), %eax 202 ret 203