xref: /openbmc/linux/arch/x86/boot/memory.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
1*97873a3dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
296ae6ea0SThomas Gleixner /* -*- linux-c -*- ------------------------------------------------------- *
396ae6ea0SThomas Gleixner  *
496ae6ea0SThomas Gleixner  *   Copyright (C) 1991, 1992 Linus Torvalds
596ae6ea0SThomas Gleixner  *   Copyright 2007 rPath, Inc. - All Rights Reserved
6c549e71dSH. Peter Anvin  *   Copyright 2009 Intel Corporation; author H. Peter Anvin
796ae6ea0SThomas Gleixner  *
896ae6ea0SThomas Gleixner  * ----------------------------------------------------------------------- */
996ae6ea0SThomas Gleixner 
1096ae6ea0SThomas Gleixner /*
1196ae6ea0SThomas Gleixner  * Memory detection code
1296ae6ea0SThomas Gleixner  */
1396ae6ea0SThomas Gleixner 
1496ae6ea0SThomas Gleixner #include "boot.h"
1596ae6ea0SThomas Gleixner 
1696ae6ea0SThomas Gleixner #define SMAP	0x534d4150	/* ASCII "SMAP" */
1796ae6ea0SThomas Gleixner 
detect_memory_e820(void)18e8eeb3c8SJordan Borgner static void detect_memory_e820(void)
1996ae6ea0SThomas Gleixner {
2096ae6ea0SThomas Gleixner 	int count = 0;
21df7699c5SH. Peter Anvin 	struct biosregs ireg, oreg;
227410aa1cSIngo Molnar 	struct boot_e820_entry *desc = boot_params.e820_table;
237410aa1cSIngo Molnar 	static struct boot_e820_entry buf; /* static so it is zeroed */
2496ae6ea0SThomas Gleixner 
25df7699c5SH. Peter Anvin 	initregs(&ireg);
26df7699c5SH. Peter Anvin 	ireg.ax  = 0xe820;
270e96f31eSJordan Borgner 	ireg.cx  = sizeof(buf);
28df7699c5SH. Peter Anvin 	ireg.edx = SMAP;
29df7699c5SH. Peter Anvin 	ireg.di  = (size_t)&buf;
30df7699c5SH. Peter Anvin 
31cd670599SH. Peter Anvin 	/*
32bca23dbaSH. Peter Anvin 	 * Note: at least one BIOS is known which assumes that the
33bca23dbaSH. Peter Anvin 	 * buffer pointed to by one e820 call is the same one as
34bca23dbaSH. Peter Anvin 	 * the previous call, and only changes modified fields.  Therefore,
35bca23dbaSH. Peter Anvin 	 * we use a temporary buffer and copy the results entry by entry.
36bca23dbaSH. Peter Anvin 	 *
37bca23dbaSH. Peter Anvin 	 * This routine deliberately does not try to account for
38bca23dbaSH. Peter Anvin 	 * ACPI 3+ extended attributes.  This is because there are
39bca23dbaSH. Peter Anvin 	 * BIOSes in the field which report zero for the valid bit for
40bca23dbaSH. Peter Anvin 	 * all ranges, and we don't currently make any use of the
41bca23dbaSH. Peter Anvin 	 * other attribute bits.  Revisit this if we see the extended
42bca23dbaSH. Peter Anvin 	 * attribute bits deployed in a meaningful way in the future.
43cd670599SH. Peter Anvin 	 */
44cd670599SH. Peter Anvin 
4596ae6ea0SThomas Gleixner 	do {
46df7699c5SH. Peter Anvin 		intcall(0x15, &ireg, &oreg);
47df7699c5SH. Peter Anvin 		ireg.ebx = oreg.ebx; /* for next iteration... */
4896ae6ea0SThomas Gleixner 
49829157beSH. Peter Anvin 		/* BIOSes which terminate the chain with CF = 1 as opposed
50829157beSH. Peter Anvin 		   to %ebx = 0 don't always report the SMAP signature on
51829157beSH. Peter Anvin 		   the final, failing, probe. */
52df7699c5SH. Peter Anvin 		if (oreg.eflags & X86_EFLAGS_CF)
53829157beSH. Peter Anvin 			break;
54829157beSH. Peter Anvin 
5596ae6ea0SThomas Gleixner 		/* Some BIOSes stop returning SMAP in the middle of
5696ae6ea0SThomas Gleixner 		   the search loop.  We don't know exactly how the BIOS
5796ae6ea0SThomas Gleixner 		   screwed up the map at that point, we might have a
5896ae6ea0SThomas Gleixner 		   partial map, the full map, or complete garbage, so
5996ae6ea0SThomas Gleixner 		   just return failure. */
60df7699c5SH. Peter Anvin 		if (oreg.eax != SMAP) {
6196ae6ea0SThomas Gleixner 			count = 0;
6296ae6ea0SThomas Gleixner 			break;
6396ae6ea0SThomas Gleixner 		}
6496ae6ea0SThomas Gleixner 
65bca23dbaSH. Peter Anvin 		*desc++ = buf;
6696ae6ea0SThomas Gleixner 		count++;
6761a50101SIngo Molnar 	} while (ireg.ebx && count < ARRAY_SIZE(boot_params.e820_table));
6896ae6ea0SThomas Gleixner 
69e8eeb3c8SJordan Borgner 	boot_params.e820_entries = count;
7096ae6ea0SThomas Gleixner }
7196ae6ea0SThomas Gleixner 
detect_memory_e801(void)72e8eeb3c8SJordan Borgner static void detect_memory_e801(void)
7396ae6ea0SThomas Gleixner {
74df7699c5SH. Peter Anvin 	struct biosregs ireg, oreg;
7596ae6ea0SThomas Gleixner 
76df7699c5SH. Peter Anvin 	initregs(&ireg);
77df7699c5SH. Peter Anvin 	ireg.ax = 0xe801;
78df7699c5SH. Peter Anvin 	intcall(0x15, &ireg, &oreg);
7996ae6ea0SThomas Gleixner 
80df7699c5SH. Peter Anvin 	if (oreg.eflags & X86_EFLAGS_CF)
81e8eeb3c8SJordan Borgner 		return;
8296ae6ea0SThomas Gleixner 
8396ae6ea0SThomas Gleixner 	/* Do we really need to do this? */
84df7699c5SH. Peter Anvin 	if (oreg.cx || oreg.dx) {
85df7699c5SH. Peter Anvin 		oreg.ax = oreg.cx;
86df7699c5SH. Peter Anvin 		oreg.bx = oreg.dx;
8796ae6ea0SThomas Gleixner 	}
8896ae6ea0SThomas Gleixner 
89df7699c5SH. Peter Anvin 	if (oreg.ax > 15*1024) {
90e8eeb3c8SJordan Borgner 		return;	/* Bogus! */
91df7699c5SH. Peter Anvin 	} else if (oreg.ax == 15*1024) {
9239b68976SH. Peter Anvin 		boot_params.alt_mem_k = (oreg.bx << 6) + oreg.ax;
93df7699c5SH. Peter Anvin 	} else {
94df7699c5SH. Peter Anvin 		/*
95df7699c5SH. Peter Anvin 		 * This ignores memory above 16MB if we have a memory
96df7699c5SH. Peter Anvin 		 * hole there.  If someone actually finds a machine
97df7699c5SH. Peter Anvin 		 * with a memory hole at 16MB and no support for
98df7699c5SH. Peter Anvin 		 * 0E820h they should probably generate a fake e820
99df7699c5SH. Peter Anvin 		 * map.
100df7699c5SH. Peter Anvin 		 */
101df7699c5SH. Peter Anvin 		boot_params.alt_mem_k = oreg.ax;
102df7699c5SH. Peter Anvin 	}
10396ae6ea0SThomas Gleixner }
10496ae6ea0SThomas Gleixner 
detect_memory_88(void)105e8eeb3c8SJordan Borgner static void detect_memory_88(void)
10696ae6ea0SThomas Gleixner {
107df7699c5SH. Peter Anvin 	struct biosregs ireg, oreg;
10896ae6ea0SThomas Gleixner 
109df7699c5SH. Peter Anvin 	initregs(&ireg);
110df7699c5SH. Peter Anvin 	ireg.ah = 0x88;
111df7699c5SH. Peter Anvin 	intcall(0x15, &ireg, &oreg);
11296ae6ea0SThomas Gleixner 
113df7699c5SH. Peter Anvin 	boot_params.screen_info.ext_mem_k = oreg.ax;
11496ae6ea0SThomas Gleixner }
11596ae6ea0SThomas Gleixner 
detect_memory(void)116e8eeb3c8SJordan Borgner void detect_memory(void)
11796ae6ea0SThomas Gleixner {
118e8eeb3c8SJordan Borgner 	detect_memory_e820();
11996ae6ea0SThomas Gleixner 
120e8eeb3c8SJordan Borgner 	detect_memory_e801();
12196ae6ea0SThomas Gleixner 
122e8eeb3c8SJordan Borgner 	detect_memory_88();
12396ae6ea0SThomas Gleixner }
124