x86, setup: ACPI 3, BIOS workaround for E820-probing code
[linux-2.6-block.git] / arch / x86 / boot / memory.c
CommitLineData
449f2ab9
PA
1/* -*- linux-c -*- ------------------------------------------------------- *
2 *
3 * Copyright (C) 1991, 1992 Linus Torvalds
4 * Copyright 2007 rPath, Inc. - All Rights Reserved
c549e71d 5 * Copyright 2009 Intel Corporation; author H. Peter Anvin
449f2ab9
PA
6 *
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/*
449f2ab9
PA
13 * Memory detection code
14 */
15
16#include "boot.h"
17
18#define SMAP 0x534d4150 /* ASCII "SMAP" */
19
c549e71d
PA
20struct e820_ext_entry {
21 struct e820entry std;
22 u32 ext_flags;
23} __attribute__((packed));
24
449f2ab9
PA
25static int detect_memory_e820(void)
26{
2efa33f8 27 int count = 0;
449f2ab9 28 u32 next = 0;
32ec7fd0 29 u32 size, id, edi;
449f2ab9
PA
30 u8 err;
31 struct e820entry *desc = boot_params.e820_map;
c549e71d 32 static struct e820_ext_entry buf; /* static so it is zeroed */
449f2ab9
PA
33
34 do {
c549e71d 35 size = sizeof buf;
4ee5b10a 36
01522df3
MJ
37 /* Important: %edx and %esi are clobbered by some BIOSes,
38 so they must be either used for the error output
32ec7fd0
PA
39 or explicitly marked clobbered. Given that, assume there
40 is something out there clobbering %ebp and %edi, too. */
41 asm("pushl %%ebp; int $0x15; popl %%ebp; setc %0"
4ee5b10a 42 : "=d" (err), "+b" (next), "=a" (id), "+c" (size),
c549e71d
PA
43 "=D" (edi), "+m" (buf)
44 : "D" (&buf), "d" (SMAP), "a" (0xe820)
01522df3 45 : "esi");
449f2ab9 46
829157be
PA
47 /* BIOSes which terminate the chain with CF = 1 as opposed
48 to %ebx = 0 don't always report the SMAP signature on
49 the final, failing, probe. */
50 if (err)
51 break;
52
2efa33f8
PA
53 /* Some BIOSes stop returning SMAP in the middle of
54 the search loop. We don't know exactly how the BIOS
55 screwed up the map at that point, we might have a
56 partial map, the full map, or complete garbage, so
57 just return failure. */
58 if (id != SMAP) {
59 count = 0;
449f2ab9 60 break;
2efa33f8 61 }
449f2ab9 62
c549e71d
PA
63 /* ACPI 3.0 added the extended flags support. If bit 0
64 in the extended flags is zero, we're supposed to simply
65 ignore the entry -- a backwards incompatible change! */
66 if (size > 20 && !(buf.ext_flags & 1))
67 continue;
68
69 *desc++ = buf.std;
2efa33f8 70 count++;
c3965bd1 71 } while (next && count < ARRAY_SIZE(boot_params.e820_map));
449f2ab9 72
2efa33f8 73 return boot_params.e820_entries = count;
449f2ab9
PA
74}
75
76static int detect_memory_e801(void)
77{
78 u16 ax, bx, cx, dx;
79 u8 err;
80
81 bx = cx = dx = 0;
82 ax = 0xe801;
83 asm("stc; int $0x15; setc %0"
84 : "=m" (err), "+a" (ax), "+b" (bx), "+c" (cx), "+d" (dx));
85
86 if (err)
87 return -1;
88
89 /* Do we really need to do this? */
90 if (cx || dx) {
91 ax = cx;
92 bx = dx;
93 }
94
95 if (ax > 15*1024)
96 return -1; /* Bogus! */
97
98 /* This ignores memory above 16MB if we have a memory hole
99 there. If someone actually finds a machine with a memory
100 hole at 16MB and no support for 0E820h they should probably
101 generate a fake e820 map. */
102 boot_params.alt_mem_k = (ax == 15*1024) ? (dx << 6)+ax : ax;
103
104 return 0;
105}
106
107static int detect_memory_88(void)
108{
109 u16 ax;
110 u8 err;
111
112 ax = 0x8800;
113 asm("stc; int $0x15; setc %0" : "=bcdm" (err), "+a" (ax));
114
115 boot_params.screen_info.ext_mem_k = ax;
116
117 return -err;
118}
119
120int detect_memory(void)
121{
2efa33f8
PA
122 int err = -1;
123
449f2ab9 124 if (detect_memory_e820() > 0)
2efa33f8 125 err = 0;
449f2ab9
PA
126
127 if (!detect_memory_e801())
2efa33f8
PA
128 err = 0;
129
130 if (!detect_memory_88())
131 err = 0;
449f2ab9 132
2efa33f8 133 return err;
449f2ab9 134}