Commit | Line | Data |
---|---|---|
449f2ab9 PA |
1 | /* -*- linux-c -*- ------------------------------------------------------- * |
2 | * | |
3 | * Copyright (C) 1991, 1992 Linus Torvalds | |
4 | * Copyright 2007 rPath, Inc. - All Rights Reserved | |
5 | * | |
6 | * This file is part of the Linux kernel, and is made available under | |
7 | * the terms of the GNU General Public License version 2. | |
8 | * | |
9 | * ----------------------------------------------------------------------- */ | |
10 | ||
11 | /* | |
449f2ab9 PA |
12 | * Memory detection code |
13 | */ | |
14 | ||
15 | #include "boot.h" | |
16 | ||
17 | #define SMAP 0x534d4150 /* ASCII "SMAP" */ | |
18 | ||
19 | static int detect_memory_e820(void) | |
20 | { | |
2efa33f8 | 21 | int count = 0; |
449f2ab9 PA |
22 | u32 next = 0; |
23 | u32 size, id; | |
24 | u8 err; | |
25 | struct e820entry *desc = boot_params.e820_map; | |
26 | ||
27 | do { | |
28 | size = sizeof(struct e820entry); | |
4ee5b10a PA |
29 | |
30 | /* Important: %edx is clobbered by some BIOSes, | |
31 | so it must be either used for the error output | |
32 | or explicitly marked clobbered. */ | |
449f2ab9 | 33 | asm("int $0x15; setc %0" |
4ee5b10a | 34 | : "=d" (err), "+b" (next), "=a" (id), "+c" (size), |
449f2ab9 | 35 | "=m" (*desc) |
4ee5b10a | 36 | : "D" (desc), "d" (SMAP), "a" (0xe820)); |
449f2ab9 | 37 | |
829157be PA |
38 | /* BIOSes which terminate the chain with CF = 1 as opposed |
39 | to %ebx = 0 don't always report the SMAP signature on | |
40 | the final, failing, probe. */ | |
41 | if (err) | |
42 | break; | |
43 | ||
2efa33f8 PA |
44 | /* Some BIOSes stop returning SMAP in the middle of |
45 | the search loop. We don't know exactly how the BIOS | |
46 | screwed up the map at that point, we might have a | |
47 | partial map, the full map, or complete garbage, so | |
48 | just return failure. */ | |
49 | if (id != SMAP) { | |
50 | count = 0; | |
449f2ab9 | 51 | break; |
2efa33f8 | 52 | } |
449f2ab9 | 53 | |
2efa33f8 | 54 | count++; |
449f2ab9 | 55 | desc++; |
2efa33f8 | 56 | } while (next && count < E820MAX); |
449f2ab9 | 57 | |
2efa33f8 | 58 | return boot_params.e820_entries = count; |
449f2ab9 PA |
59 | } |
60 | ||
61 | static int detect_memory_e801(void) | |
62 | { | |
63 | u16 ax, bx, cx, dx; | |
64 | u8 err; | |
65 | ||
66 | bx = cx = dx = 0; | |
67 | ax = 0xe801; | |
68 | asm("stc; int $0x15; setc %0" | |
69 | : "=m" (err), "+a" (ax), "+b" (bx), "+c" (cx), "+d" (dx)); | |
70 | ||
71 | if (err) | |
72 | return -1; | |
73 | ||
74 | /* Do we really need to do this? */ | |
75 | if (cx || dx) { | |
76 | ax = cx; | |
77 | bx = dx; | |
78 | } | |
79 | ||
80 | if (ax > 15*1024) | |
81 | return -1; /* Bogus! */ | |
82 | ||
83 | /* This ignores memory above 16MB if we have a memory hole | |
84 | there. If someone actually finds a machine with a memory | |
85 | hole at 16MB and no support for 0E820h they should probably | |
86 | generate a fake e820 map. */ | |
87 | boot_params.alt_mem_k = (ax == 15*1024) ? (dx << 6)+ax : ax; | |
88 | ||
89 | return 0; | |
90 | } | |
91 | ||
92 | static int detect_memory_88(void) | |
93 | { | |
94 | u16 ax; | |
95 | u8 err; | |
96 | ||
97 | ax = 0x8800; | |
98 | asm("stc; int $0x15; setc %0" : "=bcdm" (err), "+a" (ax)); | |
99 | ||
100 | boot_params.screen_info.ext_mem_k = ax; | |
101 | ||
102 | return -err; | |
103 | } | |
104 | ||
105 | int detect_memory(void) | |
106 | { | |
2efa33f8 PA |
107 | int err = -1; |
108 | ||
449f2ab9 | 109 | if (detect_memory_e820() > 0) |
2efa33f8 | 110 | err = 0; |
449f2ab9 PA |
111 | |
112 | if (!detect_memory_e801()) | |
2efa33f8 PA |
113 | err = 0; |
114 | ||
115 | if (!detect_memory_88()) | |
116 | err = 0; | |
449f2ab9 | 117 | |
2efa33f8 | 118 | return err; |
449f2ab9 | 119 | } |