1 /* ----------------------------------------------------------------------- *
3 * Copyright 2007-2009 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009 H. Peter Anvin - All Rights Reserved
6 * Permission is hereby granted, free of charge, to any person
7 * obtaining a copy of this software and associated documentation
8 * files (the "Software"), to deal in the Software without
9 * restriction, including without limitation the rights to use,
10 * copy, modify, merge, publish, distribute, sublicense, and/or
11 * sell copies of the Software, and to permit persons to whom
12 * the Software is furnished to do so, subject to the following
15 * The above copyright notice and this permission notice shall
16 * be included in all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 * OTHER DEALINGS IN THE SOFTWARE.
27 * ----------------------------------------------------------------------- */
32 * Obtain a memory map for a Multiboot OS
34 * This differs from the libcom32 memory map functions in that it doesn't
35 * attempt to filter out memory regions...
47 #define RANGE_ALLOC_BLOCK 128
49 static int mboot_scan_memory(struct AddrRangeDesc
**ardp
, uint32_t * dosmem
)
51 com32sys_t ireg
, oreg
;
52 struct e820_entry
*e820buf
;
53 struct AddrRangeDesc
*ard
;
54 size_t ard_count
, ard_space
;
57 /* Use INT 12h to get DOS memory */
58 __intcall(0x12, &__com32_zero_regs
, &oreg
);
59 *dosmem
= oreg
.eax
.w
[0] << 10;
60 if (*dosmem
< 32 * 1024 || *dosmem
> 640 * 1024) {
61 /* INT 12h reports nonsense... now what? */
62 uint16_t ebda_seg
= *(uint16_t *) 0x40e;
63 if (ebda_seg
>= 0x8000 && ebda_seg
< 0xa000)
64 *dosmem
= ebda_seg
<< 4;
66 *dosmem
= 640 * 1024; /* Hope for the best... */
69 e820buf
= lmalloc(sizeof(*e820buf
));
73 /* Allocate initial space */
74 *ardp
= ard
= malloc(RANGE_ALLOC_BLOCK
* sizeof *ard
);
79 ard_space
= RANGE_ALLOC_BLOCK
;
81 /* First try INT 15h AX=E820h */
82 memset(&ireg
, 0, sizeof ireg
);
84 ireg
.edx
.l
= 0x534d4150;
86 ireg
.ecx
.l
= sizeof(*e820buf
);
87 ireg
.es
= SEG(e820buf
);
88 ireg
.edi
.w
[0] = OFFS(e820buf
);
89 memset(e820buf
, 0, sizeof *e820buf
);
92 __intcall(0x15, &ireg
, &oreg
);
94 if ((oreg
.eflags
.l
& EFLAGS_CF
) ||
95 (oreg
.eax
.l
!= 0x534d4150) || (oreg
.ecx
.l
< 20))
98 if (ard_count
>= ard_space
) {
99 ard_space
+= RANGE_ALLOC_BLOCK
;
100 *ardp
= ard
= realloc(ard
, ard_space
* sizeof *ard
);
107 ard
[ard_count
].size
= 20;
108 ard
[ard_count
].BaseAddr
= e820buf
->start
;
109 ard
[ard_count
].Length
= e820buf
->len
;
110 ard
[ard_count
].Type
= e820buf
->type
;
113 ireg
.ebx
.l
= oreg
.ebx
.l
;
114 } while (oreg
.ebx
.l
);
123 ard
[0].Length
= *dosmem
<< 10;
126 /* Next try INT 15h AX=E801h */
127 memset(&ireg
, 0, sizeof ireg
);
128 ireg
.eax
.w
[0] = 0xe801;
129 __intcall(0x15, &ireg
, &oreg
);
131 if (!(oreg
.eflags
.l
& EFLAGS_CF
) && oreg
.ecx
.w
[0]) {
133 ard
[1].BaseAddr
= 1 << 20;
134 ard
[1].Length
= oreg
.ecx
.w
[0] << 10;
139 ard
[2].BaseAddr
= 16 << 20;
140 ard
[2].Length
= oreg
.edx
.w
[0] << 16;
150 /* Finally try INT 15h AH=88h */
151 memset(&ireg
, 0, sizeof ireg
);
152 ireg
.eax
.w
[0] = 0x8800;
153 __intcall(0x15, &ireg
, &oreg
);
154 if (!(oreg
.eflags
.l
& EFLAGS_CF
) && oreg
.eax
.w
[0]) {
156 ard
[1].BaseAddr
= 1 << 20;
157 ard
[1].Length
= oreg
.ecx
.w
[0] << 10;
163 rv
= 1; /* ... problematic ... */
169 void mboot_make_memmap(void)
172 struct AddrRangeDesc
*ard
;
173 uint32_t lowmem
, highmem
;
176 /* Always report DOS memory as "lowmem", this may be overly conservative
177 (e.g. if we're dropping PXE), but it should be *safe*... */
179 nmap
= mboot_scan_memory(&ard
, &lowmem
);
182 highrsvd
= 0xfff00000;
185 for (i
= 0; i
< nmap
; i
++) {
188 start
= ard
[i
].BaseAddr
;
189 end
= start
+ ard
[i
].Length
;
194 if (start
& 0xffffffff00000000ULL
)
195 continue; /* Not interested in 64-bit memory */
203 if (ard
[i
].Type
== 1 && start
== highmem
) {
206 } else if (ard
[i
].Type
!= 1 && start
< highrsvd
)
210 if (highmem
> highrsvd
)
213 mbinfo
.mem_lower
= lowmem
>> 10;
214 mbinfo
.mem_upper
= (highmem
- 0x100000) >> 10;
215 mbinfo
.flags
|= MB_INFO_MEMORY
;
217 /* The spec says this address should be +4, but Grub disagrees */
218 mbinfo
.mmap_addr
= map_data(ard
, nmap
* sizeof *ard
, 4, false);
219 if (mbinfo
.mmap_addr
) {
220 mbinfo
.mmap_length
= nmap
* sizeof *ard
;
221 mbinfo
.flags
|= MB_INFO_MEM_MAP
;