Skip to content

Commit 3b0b641

Browse files
committed
support memory above 4G
The multiboot specification explicitly states that `mem_upper` is not guaranteed to be the correct value for mem_upper. Additionally, placing the heap in-between `mem_lower` and `mem_upper` only really makes sense on 32-bit systems. By using memory mapped pages we can pick the largest consecutive region of memory available. Selecting 1_000_000 as the minimum address is a temporary hack since it's not immediately obvious how to get the correct address for virtual mappings that are still not initialized (such as LiveUpdate and SystemLog). The correct solution here would be to make these regions dynamic in size, initialize them earlier so the regions are already claimed, or re-initialize the heap. This is only necessary for systems that have less than 4G of memory. See-Also: https://www.gnu.org/software/grub/manual/multiboot/multiboot.html See-Also: https://wiki.osdev.org/Multiboot
1 parent 08c9539 commit 3b0b641

4 files changed

Lines changed: 108 additions & 39 deletions

File tree

api/boot/multiboot.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,12 +195,19 @@ typedef struct multiboot_info multiboot_info_t;
195195

196196
struct multiboot_mmap_entry
197197
{
198-
multiboot_uint32_t size;
198+
multiboot_uint32_t size; // size of struct
199199
multiboot_uint64_t addr;
200-
multiboot_uint64_t len;
200+
multiboot_uint64_t len; // bytes available
201201
#define MULTIBOOT_MEMORY_AVAILABLE 1
202202
#define MULTIBOOT_MEMORY_RESERVED 2
203+
#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3
204+
#define MULTIBOOT_MEMORY_NVS 4
205+
#define MULTIBOOT_MEMORY_BADRAM 5
203206
multiboot_uint32_t type;
207+
208+
[[nodiscard]] constexpr bool is_available() const noexcept {
209+
return type == MULTIBOOT_MEMORY_AVAILABLE;
210+
}
204211
} __attribute__((packed));
205212
typedef struct multiboot_mmap_entry multiboot_memory_map_t;
206213

src/arch/x86_64/paging.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,8 @@ uintptr_t mem::active_page_size(uintptr_t addr){
376376

377377
void allow_executable()
378378
{
379+
// FIXME: this seems weird. is this only used for tests?
380+
379381
INFO2("* Allowing execute on %p -> %p",
380382
(void*) __exec_begin, (void*)__exec_end);
381383

@@ -391,7 +393,7 @@ void allow_executable()
391393
m.page_sizes = os::mem::Map::any_size;
392394
m.flags = os::mem::Access::execute | os::mem::Access::read;
393395

394-
os::mem::map(m, "ELF .text");
396+
os::mem::map(m, "Executable");
395397
}
396398

397399
/* TODO: Compiler warning unused

src/kernel/multiboot.cpp

Lines changed: 72 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <boot/multiboot.h>
2323
#include <kernel/memory.hpp>
2424
#include <fmt/format.h>
25+
#include <ranges>
2526

2627
template<class... Args>
2728
static inline void _kfmt(fmt::string_view prefix, fmt::format_string<Args...> fmtstr, Args&&... args) {
@@ -122,9 +123,41 @@ uintptr_t _multiboot_free_begin(uintptr_t boot_addr)
122123
return multi_end;
123124
}
124125

126+
constexpr static inline const char* multiboot_memory_type_str(uint32_t type) noexcept {
127+
// TODO: convert multiboot types to enum class
128+
switch (type) {
129+
case MULTIBOOT_MEMORY_AVAILABLE:
130+
return "Available";
131+
case MULTIBOOT_MEMORY_ACPI_RECLAIMABLE:
132+
return "ACPI Reclaimable";
133+
case MULTIBOOT_MEMORY_NVS:
134+
return "ACPI Non-volatile Storage";
135+
case MULTIBOOT_MEMORY_BADRAM:
136+
return "Bad RAM";
137+
case MULTIBOOT_MEMORY_RESERVED:
138+
return "Reserved";
139+
default:
140+
return "UNKNOWN";
141+
}
142+
}
143+
144+
145+
std::span<multiboot_memory_map_t> _multiboot_memory_maps() {
146+
auto* info = kernel::bootinfo();
147+
148+
auto* hardware_map = reinterpret_cast<multiboot_memory_map_t*>(info->mmap_addr);
149+
const size_t entry_count = static_cast<size_t>(info->mmap_length / sizeof(multiboot_memory_map_t));
150+
151+
return std::span<multiboot_memory_map_t> { hardware_map, entry_count };
152+
}
153+
125154
void kernel::multiboot(uint32_t boot_addr)
126155
{
127-
MYINFO("Booted with multiboot");
156+
#if defined(__x86_64)
157+
MYINFO("Booted with multiboot x86_64");
158+
#else
159+
MYINFO("Booted with multiboot x86");
160+
#endif
128161
auto* info = ::bootinfo(boot_addr);
129162
INFO2("* Boot flags: {:#x}", info->flags);
130163

@@ -152,36 +185,50 @@ void kernel::multiboot(uint32_t boot_addr)
152185
}
153186

154187
if (info->flags & MULTIBOOT_INFO_MEM_MAP) {
155-
INFO2("* Multiboot provided memory map ({} entries @ {})",
156-
info->mmap_length / sizeof(multiboot_memory_map_t),
157-
(const void*)(uintptr_t)info->mmap_addr);
158-
std::span<multiboot_memory_map_t> mmap {
159-
reinterpret_cast<multiboot_memory_map_t*>(info->mmap_addr),
160-
static_cast<size_t>(info->mmap_length / sizeof(multiboot_memory_map_t))
161-
};
162-
163-
for (auto map : mmap)
188+
auto* hardware_map = reinterpret_cast<multiboot_memory_map_t*>(info->mmap_addr);
189+
const size_t entry_count = static_cast<size_t>(info->mmap_length / sizeof(multiboot_memory_map_t));
190+
191+
INFO2("* Multiboot provided memory map ({} entries @ {})\n", entry_count, reinterpret_cast<const void*>(hardware_map));
192+
193+
for (auto map : std::span<multiboot_memory_map_t>{ hardware_map, entry_count })
164194
{
165-
const char* str_type = map.type & MULTIBOOT_MEMORY_AVAILABLE ? "FREE" : "RESERVED";
166-
const uintptr_t addr = map.addr;
195+
const uintptr_t start = map.addr;
167196
const uintptr_t size = map.len;
168-
INFO2(" {:#x} - {:#x} {} ({} KiB)", addr, addr + size - 1, str_type, size / 1024);
169-
170-
if (not (map.type & MULTIBOOT_MEMORY_AVAILABLE)) {
197+
const uintptr_t end = start + size - 1;
171198

172-
if (util::bits::is_aligned<4_KiB>(map.addr)) {
173-
os::mem::map({addr, addr, os::mem::Access::read | os::mem::Access::write, size},
174-
"Reserved (Multiboot)");
175-
continue;
176-
}
199+
INFO2(" {:#16x} - {:#16x} ({} KiB): {}", start, end, size / 1024, multiboot_memory_type_str(map.type));
177200

178-
// For non-aligned addresses, assign
179-
os::mem::vmmap().assign_range({addr, addr + size - 1, "Reserved (Multiboot)"});
201+
// os::mem::map() does not accept non-aligned page addresses
202+
if (not util::bits::is_aligned<4_KiB>(map.addr)) {
203+
os::mem::vmmap().assign_range({start, start + size - 1, "UNALIGNED"});
204+
continue;
180205
}
181-
else
206+
207+
os::mem::Map rw_map = { /*.linear=*/start, /*.physical=*/start, /*.fl=*/os::mem::Access::read | os::mem::Access::write, /*.sz=*/size };
208+
switch (map.type)
182209
{
183-
// Map as free memory
184-
//os::mem::map_avail({map.addr, map.addr, {os::mem::Access::read | os::mem::Access::write}, map.len}, "Reserved (Multiboot)");
210+
case MULTIBOOT_MEMORY_ACPI_RECLAIMABLE:
211+
os::mem::map(rw_map, "Multiboot (ACPI Reclaimable)");
212+
break;
213+
case MULTIBOOT_MEMORY_NVS:
214+
os::mem::map(rw_map, "Multiboot (ACPI Non-volatile Storage)");
215+
break;
216+
case MULTIBOOT_MEMORY_BADRAM:
217+
os::mem::map(rw_map, "Multiboot (Bad RAM)");
218+
break;
219+
case MULTIBOOT_MEMORY_RESERVED:
220+
os::mem::map(rw_map, "Multiboot (Reserved)");
221+
break;
222+
223+
case MULTIBOOT_MEMORY_AVAILABLE: {
224+
// these are mapped in src/platform/${platform}/os.cpp
225+
break;
226+
}
227+
default: {
228+
char buf[32]; // libc is not entirely initialized at this point
229+
std::snprintf(buf, sizeof(buf), "Unknown memory map type: %d", map.type);
230+
os::panic(buf);
231+
}
185232
}
186233
}
187234
INFO2("");

src/platform/x86_pc/os.cpp

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ extern uintptr_t _ELF_END_;
4848
// in kernel/os.cpp
4949
extern bool os_default_stdout;
5050

51+
extern std::span<multiboot_memory_map_t> _multiboot_memory_maps();
52+
5153
struct alignas(SMP_ALIGN) OS_CPU {
5254
uint64_t cycles_hlt = 0;
5355
};
@@ -125,10 +127,6 @@ void kernel::start(uint32_t boot_magic, uint32_t boot_addr)
125127

126128
MYINFO("Total memory detected as %s ", util::Byte_r(kernel::memory_end()).to_string().c_str());
127129

128-
// Give the rest of physical memory to heap
129-
kernel::state().heap_max = kernel::memory_end() - 1;
130-
assert(kernel::heap_begin() != 0x0 and kernel::heap_max() != 0x0);
131-
132130
PROFILE("Memory map");
133131
// Assign memory ranges used by the kernel
134132
auto& memmap = os::mem::vmmap();
@@ -145,13 +143,28 @@ void kernel::start(uint32_t boot_magic, uint32_t boot_addr)
145143
memmap.assign_range({0x10000, 0x9d3ff, "Stack"});
146144
#endif
147145

148-
// heap (physical) area
149-
uintptr_t span_max = std::numeric_limits<std::ptrdiff_t>::max();
150-
uintptr_t heap_range_max_ = std::min(span_max, kernel::heap_max());
146+
multiboot_memory_map_t heap_map = {0,0,0,0};
147+
for (auto entry : _multiboot_memory_maps())
148+
{
149+
if (not entry.is_available()) continue;
150+
151+
if (entry.len > heap_map.len) {
152+
heap_map = entry;
153+
}
154+
}
155+
uintptr_t end = heap_map.addr + heap_map.len - 1;
156+
157+
if (heap_map.addr < 0x1'000'000) {
158+
kernel::state().heap_begin = std::max((uintptr_t)0x1'000'000, (uintptr_t)heap_map.addr);
159+
kernel::state().heap_max = std::min(kernel::heap_max(), end);
160+
} else {
161+
kernel::state().heap_begin = heap_map.addr;
162+
kernel::state().heap_max = end;
163+
}
151164

152-
INFO2("* Assigning heap 0x%zx -> 0x%zx", kernel::heap_begin(), heap_range_max_);
153-
memmap.assign_range({kernel::heap_begin(), heap_range_max_,
154-
"Dynamic memory", kernel::heap_usage });
165+
166+
INFO2("* Assigning heap 0x%lx -> 0x%lx", kernel::heap_begin(), kernel::heap_max());
167+
memmap.assign_range({kernel::heap_begin(), kernel::heap_max(), "Heap", kernel::heap_usage });
155168

156169
MYINFO("Virtual memory map");
157170
for (const auto& entry : memmap)
@@ -182,7 +195,7 @@ void os::event_loop()
182195
__arch_poweroff();
183196
}
184197

185-
198+
/* legacy boot is used when MULTIBOOT_BOOTLOADER_MAGIC is unset, see x86_pc/kernel_start.cpp */
186199
void kernel::legacy_boot()
187200
{
188201
// Fetch CMOS memory info (unfortunately this is maximally 10^16 kb)

0 commit comments

Comments
 (0)