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