blob: db75d07c3645c0375090596ae29d1085f0f8a38a [file] [log] [blame]
H. Peter Anvin449f2ab2007-07-11 12:18:50 -07001/* -*- linux-c -*- ------------------------------------------------------- *
2 *
3 * Copyright (C) 1991, 1992 Linus Torvalds
4 * Copyright 2007 rPath, Inc. - All Rights Reserved
H. Peter Anvinc549e712009-03-28 13:53:26 -07005 * Copyright 2009 Intel Corporation; author H. Peter Anvin
H. Peter Anvin449f2ab2007-07-11 12:18:50 -07006 *
7 * This file is part of the Linux kernel, and is made available under
8 * the terms of the GNU General Public License version 2.
9 *
10 * ----------------------------------------------------------------------- */
11
12/*
H. Peter Anvin449f2ab2007-07-11 12:18:50 -070013 * Memory detection code
14 */
15
16#include "boot.h"
17
18#define SMAP 0x534d4150 /* ASCII "SMAP" */
19
20static int detect_memory_e820(void)
21{
H. Peter Anvin2efa33f2007-09-26 14:11:43 -070022 int count = 0;
H. Peter Anvindf7699c2009-04-01 18:13:46 -070023 struct biosregs ireg, oreg;
H. Peter Anvin449f2ab2007-07-11 12:18:50 -070024 struct e820entry *desc = boot_params.e820_map;
H. Peter Anvinbca23db2009-05-21 11:46:16 -070025 static struct e820entry buf; /* static so it is zeroed */
H. Peter Anvin449f2ab2007-07-11 12:18:50 -070026
H. Peter Anvindf7699c2009-04-01 18:13:46 -070027 initregs(&ireg);
28 ireg.ax = 0xe820;
29 ireg.cx = sizeof buf;
30 ireg.edx = SMAP;
31 ireg.di = (size_t)&buf;
32
H. Peter Anvincd670592009-04-01 11:35:00 -070033 /*
H. Peter Anvinbca23db2009-05-21 11:46:16 -070034 * Note: at least one BIOS is known which assumes that the
35 * buffer pointed to by one e820 call is the same one as
36 * the previous call, and only changes modified fields. Therefore,
37 * we use a temporary buffer and copy the results entry by entry.
38 *
39 * This routine deliberately does not try to account for
40 * ACPI 3+ extended attributes. This is because there are
41 * BIOSes in the field which report zero for the valid bit for
42 * all ranges, and we don't currently make any use of the
43 * other attribute bits. Revisit this if we see the extended
44 * attribute bits deployed in a meaningful way in the future.
H. Peter Anvincd670592009-04-01 11:35:00 -070045 */
H. Peter Anvincd670592009-04-01 11:35:00 -070046
H. Peter Anvin449f2ab2007-07-11 12:18:50 -070047 do {
H. Peter Anvindf7699c2009-04-01 18:13:46 -070048 intcall(0x15, &ireg, &oreg);
49 ireg.ebx = oreg.ebx; /* for next iteration... */
H. Peter Anvin449f2ab2007-07-11 12:18:50 -070050
H. Peter Anvin829157b2008-02-13 11:16:46 -080051 /* BIOSes which terminate the chain with CF = 1 as opposed
52 to %ebx = 0 don't always report the SMAP signature on
53 the final, failing, probe. */
H. Peter Anvindf7699c2009-04-01 18:13:46 -070054 if (oreg.eflags & X86_EFLAGS_CF)
H. Peter Anvin829157b2008-02-13 11:16:46 -080055 break;
56
H. Peter Anvin2efa33f2007-09-26 14:11:43 -070057 /* Some BIOSes stop returning SMAP in the middle of
58 the search loop. We don't know exactly how the BIOS
59 screwed up the map at that point, we might have a
60 partial map, the full map, or complete garbage, so
61 just return failure. */
H. Peter Anvindf7699c2009-04-01 18:13:46 -070062 if (oreg.eax != SMAP) {
H. Peter Anvin2efa33f2007-09-26 14:11:43 -070063 count = 0;
64 break;
65 }
66
H. Peter Anvinbca23db2009-05-21 11:46:16 -070067 *desc++ = buf;
H. Peter Anvin2efa33f2007-09-26 14:11:43 -070068 count++;
H. Peter Anvindf7699c2009-04-01 18:13:46 -070069 } while (ireg.ebx && count < ARRAY_SIZE(boot_params.e820_map));
H. Peter Anvin449f2ab2007-07-11 12:18:50 -070070
H. Peter Anvin2efa33f2007-09-26 14:11:43 -070071 return boot_params.e820_entries = count;
H. Peter Anvin449f2ab2007-07-11 12:18:50 -070072}
73
74static int detect_memory_e801(void)
75{
H. Peter Anvindf7699c2009-04-01 18:13:46 -070076 struct biosregs ireg, oreg;
H. Peter Anvin449f2ab2007-07-11 12:18:50 -070077
H. Peter Anvindf7699c2009-04-01 18:13:46 -070078 initregs(&ireg);
79 ireg.ax = 0xe801;
80 intcall(0x15, &ireg, &oreg);
H. Peter Anvin449f2ab2007-07-11 12:18:50 -070081
H. Peter Anvindf7699c2009-04-01 18:13:46 -070082 if (oreg.eflags & X86_EFLAGS_CF)
H. Peter Anvin449f2ab2007-07-11 12:18:50 -070083 return -1;
84
85 /* Do we really need to do this? */
H. Peter Anvindf7699c2009-04-01 18:13:46 -070086 if (oreg.cx || oreg.dx) {
87 oreg.ax = oreg.cx;
88 oreg.bx = oreg.dx;
H. Peter Anvin449f2ab2007-07-11 12:18:50 -070089 }
90
H. Peter Anvindf7699c2009-04-01 18:13:46 -070091 if (oreg.ax > 15*1024) {
H. Peter Anvin449f2ab2007-07-11 12:18:50 -070092 return -1; /* Bogus! */
H. Peter Anvindf7699c2009-04-01 18:13:46 -070093 } else if (oreg.ax == 15*1024) {
H. Peter Anvin39b68972011-04-25 14:52:37 -070094 boot_params.alt_mem_k = (oreg.bx << 6) + oreg.ax;
H. Peter Anvindf7699c2009-04-01 18:13:46 -070095 } else {
96 /*
97 * This ignores memory above 16MB if we have a memory
98 * hole there. If someone actually finds a machine
99 * with a memory hole at 16MB and no support for
100 * 0E820h they should probably generate a fake e820
101 * map.
102 */
103 boot_params.alt_mem_k = oreg.ax;
104 }
H. Peter Anvin449f2ab2007-07-11 12:18:50 -0700105
106 return 0;
107}
108
109static int detect_memory_88(void)
110{
H. Peter Anvindf7699c2009-04-01 18:13:46 -0700111 struct biosregs ireg, oreg;
H. Peter Anvin449f2ab2007-07-11 12:18:50 -0700112
H. Peter Anvindf7699c2009-04-01 18:13:46 -0700113 initregs(&ireg);
114 ireg.ah = 0x88;
115 intcall(0x15, &ireg, &oreg);
H. Peter Anvin449f2ab2007-07-11 12:18:50 -0700116
H. Peter Anvindf7699c2009-04-01 18:13:46 -0700117 boot_params.screen_info.ext_mem_k = oreg.ax;
H. Peter Anvin449f2ab2007-07-11 12:18:50 -0700118
H. Peter Anvindf7699c2009-04-01 18:13:46 -0700119 return -(oreg.eflags & X86_EFLAGS_CF); /* 0 or -1 */
H. Peter Anvin449f2ab2007-07-11 12:18:50 -0700120}
121
122int detect_memory(void)
123{
H. Peter Anvin2efa33f2007-09-26 14:11:43 -0700124 int err = -1;
125
H. Peter Anvin449f2ab2007-07-11 12:18:50 -0700126 if (detect_memory_e820() > 0)
H. Peter Anvin2efa33f2007-09-26 14:11:43 -0700127 err = 0;
H. Peter Anvin449f2ab2007-07-11 12:18:50 -0700128
129 if (!detect_memory_e801())
H. Peter Anvin2efa33f2007-09-26 14:11:43 -0700130 err = 0;
H. Peter Anvin449f2ab2007-07-11 12:18:50 -0700131
H. Peter Anvin2efa33f2007-09-26 14:11:43 -0700132 if (!detect_memory_88())
133 err = 0;
134
135 return err;
H. Peter Anvin449f2ab2007-07-11 12:18:50 -0700136}