Commit | Line | Data |
---|---|---|
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 |
20 | struct e820_ext_entry { |
21 | struct e820entry std; | |
22 | u32 ext_flags; | |
23 | } __attribute__((packed)); | |
24 | ||
449f2ab9 PA |
25 | static 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 | ||
76 | static 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 | ||
107 | static 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 | ||
120 | int 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 | } |