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
= __com32
.cs_bounce
;
53 struct AddrRangeDesc
*ard
;
54 size_t ard_count
, ard_space
;
56 /* Use INT 12h to get DOS memory */
57 __intcall(0x12, &__com32_zero_regs
, &oreg
);
58 *dosmem
= oreg
.eax
.w
[0] << 10;
59 if (*dosmem
< 32 * 1024 || *dosmem
> 640 * 1024) {
60 /* INT 12h reports nonsense... now what? */
61 uint16_t ebda_seg
= *(uint16_t *) 0x40e;
62 if (ebda_seg
>= 0x8000 && ebda_seg
< 0xa000)
63 *dosmem
= ebda_seg
<< 4;
65 *dosmem
= 640 * 1024; /* Hope for the best... */
68 /* Allocate initial space */
69 *ardp
= ard
= malloc(RANGE_ALLOC_BLOCK
* sizeof *ard
);
74 ard_space
= RANGE_ALLOC_BLOCK
;
76 /* First try INT 15h AX=E820h */
77 memset(&ireg
, 0, sizeof ireg
);
79 ireg
.edx
.l
= 0x534d4150;
81 ireg
.ecx
.l
= sizeof(*e820buf
);
82 ireg
.es
= SEG(e820buf
);
83 ireg
.edi
.w
[0] = OFFS(e820buf
);
84 memset(e820buf
, 0, sizeof *e820buf
);
87 __intcall(0x15, &ireg
, &oreg
);
89 if ((oreg
.eflags
.l
& EFLAGS_CF
) ||
90 (oreg
.eax
.l
!= 0x534d4150) || (oreg
.ecx
.l
< 20))
93 if (ard_count
>= ard_space
) {
94 ard_space
+= RANGE_ALLOC_BLOCK
;
95 *ardp
= ard
= realloc(ard
, ard_space
* sizeof *ard
);
100 ard
[ard_count
].size
= 20;
101 ard
[ard_count
].BaseAddr
= e820buf
->start
;
102 ard
[ard_count
].Length
= e820buf
->len
;
103 ard
[ard_count
].Type
= e820buf
->type
;
106 ireg
.ebx
.l
= oreg
.ebx
.l
;
107 } while (oreg
.ebx
.l
);
114 ard
[0].Length
= *dosmem
<< 10;
117 /* Next try INT 15h AX=E801h */
118 ireg
.eax
.w
[0] = 0xe801;
119 __intcall(0x15, &ireg
, &oreg
);
121 if (!(oreg
.eflags
.l
& EFLAGS_CF
) && oreg
.ecx
.w
[0]) {
123 ard
[1].BaseAddr
= 1 << 20;
124 ard
[1].Length
= oreg
.ecx
.w
[0] << 10;
129 ard
[2].BaseAddr
= 16 << 20;
130 ard
[2].Length
= oreg
.edx
.w
[0] << 16;
138 /* Finally try INT 15h AH=88h */
139 ireg
.eax
.w
[0] = 0x8800;
140 if (!(oreg
.eflags
.l
& EFLAGS_CF
) && oreg
.eax
.w
[0]) {
142 ard
[1].BaseAddr
= 1 << 20;
143 ard
[1].Length
= oreg
.ecx
.w
[0] << 10;
148 return 1; /* ... problematic ... */
151 void mboot_make_memmap(void)
154 struct AddrRangeDesc
*ard
;
155 uint32_t lowmem
, highmem
;
158 /* Always report DOS memory as "lowmem", this may be overly conservative
159 (e.g. if we're dropping PXE), but it should be *safe*... */
161 nmap
= mboot_scan_memory(&ard
, &lowmem
);
164 highrsvd
= 0xfff00000;
167 for (i
= 0; i
< nmap
; i
++) {
170 start
= ard
[i
].BaseAddr
;
171 end
= start
+ ard
[i
].Length
;
176 if (start
& 0xffffffff00000000ULL
)
177 continue; /* Not interested in 64-bit memory */
185 if (ard
[i
].Type
== 1 && start
== highmem
) {
188 } else if (ard
[i
].Type
!= 1 && start
< highrsvd
)
192 if (highmem
> highrsvd
)
195 mbinfo
.mem_lower
= lowmem
>> 10;
196 mbinfo
.mem_upper
= (highmem
- 0x100000) >> 10;
197 mbinfo
.flags
|= MB_INFO_MEMORY
;
199 /* The spec says this address should be +4, but Grub disagrees */
200 mbinfo
.mmap_addr
= map_data(ard
, nmap
* sizeof *ard
, 4, false);
201 if (mbinfo
.mmap_addr
) {
202 mbinfo
.mmap_length
= nmap
* sizeof *ard
;
203 mbinfo
.flags
|= MB_INFO_MEM_MAP
;