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@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
51get_e820_by_bios:
52	jmp	start_32bit
53
54	.balign 16
55idtr:
56	.word	0xffff
57	.long	IDT_START
58
59	.balign 16
60gdt:
61	.quad	0
62	.quad	GDT_ENTRY(0x009b, 0, 0xffff)
63	.quad	GDT_ENTRY(0x0093, 0, 0xffff)
64gdtr:
65	.word	3*8-1
66	.long	gdt
67
68saved_esp:
69	.long	0
70
71start_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
106relocated_start:
107reloc_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
114realmode_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
132again:
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
155done:
156	jmp	2f
157error:
158	movl	$COUNT_ADDR, %eax
159	movl	$~0, (%eax)
1602:
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 */
172relocated_end = .
173
174	.code32
175resumed_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