diff options
Diffstat (limited to 'arch/x86/boot/memory.c')
-rw-r--r-- | arch/x86/boot/memory.c | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/arch/x86/boot/memory.c b/arch/x86/boot/memory.c new file mode 100644 index 000000000000..378353956b5d --- /dev/null +++ b/arch/x86/boot/memory.c | |||
@@ -0,0 +1,118 @@ | |||
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 | /* | ||
12 | * arch/i386/boot/memory.c | ||
13 | * | ||
14 | * Memory detection code | ||
15 | */ | ||
16 | |||
17 | #include "boot.h" | ||
18 | |||
19 | #define SMAP 0x534d4150 /* ASCII "SMAP" */ | ||
20 | |||
21 | static int detect_memory_e820(void) | ||
22 | { | ||
23 | int count = 0; | ||
24 | u32 next = 0; | ||
25 | u32 size, id; | ||
26 | u8 err; | ||
27 | struct e820entry *desc = boot_params.e820_map; | ||
28 | |||
29 | do { | ||
30 | size = sizeof(struct e820entry); | ||
31 | |||
32 | /* Important: %edx is clobbered by some BIOSes, | ||
33 | so it must be either used for the error output | ||
34 | or explicitly marked clobbered. */ | ||
35 | asm("int $0x15; setc %0" | ||
36 | : "=d" (err), "+b" (next), "=a" (id), "+c" (size), | ||
37 | "=m" (*desc) | ||
38 | : "D" (desc), "d" (SMAP), "a" (0xe820)); | ||
39 | |||
40 | /* Some BIOSes stop returning SMAP in the middle of | ||
41 | the search loop. We don't know exactly how the BIOS | ||
42 | screwed up the map at that point, we might have a | ||
43 | partial map, the full map, or complete garbage, so | ||
44 | just return failure. */ | ||
45 | if (id != SMAP) { | ||
46 | count = 0; | ||
47 | break; | ||
48 | } | ||
49 | |||
50 | if (err) | ||
51 | break; | ||
52 | |||
53 | count++; | ||
54 | desc++; | ||
55 | } while (next && count < E820MAX); | ||
56 | |||
57 | return boot_params.e820_entries = count; | ||
58 | } | ||
59 | |||
60 | static int detect_memory_e801(void) | ||
61 | { | ||
62 | u16 ax, bx, cx, dx; | ||
63 | u8 err; | ||
64 | |||
65 | bx = cx = dx = 0; | ||
66 | ax = 0xe801; | ||
67 | asm("stc; int $0x15; setc %0" | ||
68 | : "=m" (err), "+a" (ax), "+b" (bx), "+c" (cx), "+d" (dx)); | ||
69 | |||
70 | if (err) | ||
71 | return -1; | ||
72 | |||
73 | /* Do we really need to do this? */ | ||
74 | if (cx || dx) { | ||
75 | ax = cx; | ||
76 | bx = dx; | ||
77 | } | ||
78 | |||
79 | if (ax > 15*1024) | ||
80 | return -1; /* Bogus! */ | ||
81 | |||
82 | /* This ignores memory above 16MB if we have a memory hole | ||
83 | there. If someone actually finds a machine with a memory | ||
84 | hole at 16MB and no support for 0E820h they should probably | ||
85 | generate a fake e820 map. */ | ||
86 | boot_params.alt_mem_k = (ax == 15*1024) ? (dx << 6)+ax : ax; | ||
87 | |||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | static int detect_memory_88(void) | ||
92 | { | ||
93 | u16 ax; | ||
94 | u8 err; | ||
95 | |||
96 | ax = 0x8800; | ||
97 | asm("stc; int $0x15; setc %0" : "=bcdm" (err), "+a" (ax)); | ||
98 | |||
99 | boot_params.screen_info.ext_mem_k = ax; | ||
100 | |||
101 | return -err; | ||
102 | } | ||
103 | |||
104 | int detect_memory(void) | ||
105 | { | ||
106 | int err = -1; | ||
107 | |||
108 | if (detect_memory_e820() > 0) | ||
109 | err = 0; | ||
110 | |||
111 | if (!detect_memory_e801()) | ||
112 | err = 0; | ||
113 | |||
114 | if (!detect_memory_88()) | ||
115 | err = 0; | ||
116 | |||
117 | return err; | ||
118 | } | ||