mirror of
https://github.com/SerenityOS/serenity
synced 2024-10-15 12:23:15 +00:00
Kernel/riscv64: Take the memory map from the FDT and dump it
For this the BootInfo struct was made architecture specific
This commit is contained in:
parent
21a21c6a11
commit
d3f6b03733
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/Arch/CPU.h>
|
||||
#include <Kernel/Arch/InterruptManagement.h>
|
||||
#include <Kernel/Arch/Processor.h>
|
||||
#include <Kernel/Boot/BootInfo.h>
|
||||
|
@ -217,29 +218,7 @@ extern "C" [[noreturn]] UNMAP_AFTER_INIT void init([[maybe_unused]] BootInfo con
|
|||
// FIXME: Read the /chosen/bootargs property.
|
||||
kernel_cmdline = RPi::Mailbox::the().query_kernel_command_line(s_command_line_buffer);
|
||||
#elif ARCH(RISCV64)
|
||||
// FIXME: Get the actual memory map from the device tree.
|
||||
static multiboot_memory_map_t mmap[] = {
|
||||
{
|
||||
// We currently can't get the actual size of firmware-reserved memory, so mark the first 0x20'0000 bytes as reserved.
|
||||
// This reserved memory region should be large enough for now.
|
||||
sizeof(multiboot_mmap_entry) - sizeof(u32),
|
||||
0x8000'0000,
|
||||
0x20'0000,
|
||||
MULTIBOOT_MEMORY_RESERVED,
|
||||
},
|
||||
{
|
||||
sizeof(multiboot_mmap_entry) - sizeof(u32),
|
||||
0x8020'0000,
|
||||
1 * GiB - 0x20'0000,
|
||||
MULTIBOOT_MEMORY_AVAILABLE,
|
||||
},
|
||||
};
|
||||
|
||||
multiboot_memory_map = mmap;
|
||||
multiboot_memory_map_count = array_size(mmap);
|
||||
multiboot_modules = nullptr;
|
||||
multiboot_modules_count = 0;
|
||||
|
||||
// FIXME: Take this from the flattened device tree (/chosen/bootargs)
|
||||
kernel_cmdline = "serial_debug"sv;
|
||||
#endif
|
||||
|
||||
|
@ -308,6 +287,11 @@ extern "C" [[noreturn]] UNMAP_AFTER_INIT void init([[maybe_unused]] BootInfo con
|
|||
InterruptManagement::initialize();
|
||||
ACPI::initialize();
|
||||
|
||||
#if ARCH(RISCV64)
|
||||
// FIXME: Unflatten the device tree and use it for device discovery
|
||||
dump_fdt();
|
||||
#endif
|
||||
|
||||
// Initialize TimeManagement before using randomness!
|
||||
TimeManagement::initialize(0);
|
||||
|
||||
|
|
25
Kernel/Arch/riscv64/CPU.cpp
Normal file
25
Kernel/Arch/riscv64/CPU.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Leon Albrecht <leon.a@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Kernel/Arch/riscv64/CPU.h>
|
||||
#include <Kernel/Memory/MemoryManager.h>
|
||||
#include <Userland/Libraries/LibDeviceTree/FlattenedDeviceTree.h>
|
||||
#include <Userland/Libraries/LibDeviceTree/Validation.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
BootInfo s_boot_info;
|
||||
|
||||
alignas(PAGE_SIZE) __attribute__((section(".bss.fdt"))) u8 s_fdt_storage[fdt_storage_size];
|
||||
|
||||
void dump_fdt()
|
||||
{
|
||||
auto& header = *bit_cast<DeviceTree::FlattenedDeviceTreeHeader*>(&s_fdt_storage[0]);
|
||||
auto fdt = ReadonlyBytes(s_fdt_storage, header.totalsize);
|
||||
MUST(DeviceTree::dump(header, fdt));
|
||||
}
|
||||
|
||||
}
|
|
@ -6,5 +6,18 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <Kernel/Memory/PhysicalAddress.h>
|
||||
#include <Kernel/Prekernel/Prekernel.h>
|
||||
|
||||
#include <AK/Platform.h>
|
||||
VALIDATE_IS_RISCV64()
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
constexpr size_t fdt_storage_size = 2 * MiB;
|
||||
extern u8 s_fdt_storage[fdt_storage_size];
|
||||
|
||||
extern BootInfo s_boot_info;
|
||||
|
||||
void dump_fdt();
|
||||
}
|
||||
|
|
|
@ -6,12 +6,14 @@
|
|||
|
||||
#include <AK/Types.h>
|
||||
|
||||
#include <Kernel/Arch/riscv64/CPU.h>
|
||||
#include <Kernel/Arch/riscv64/MMU.h>
|
||||
#include <Kernel/Arch/riscv64/PageDirectory.h>
|
||||
#include <Kernel/Arch/riscv64/SBI.h>
|
||||
#include <Kernel/Arch/riscv64/pre_init.h>
|
||||
#include <Kernel/Memory/MemoryManager.h>
|
||||
#include <Kernel/Sections.h>
|
||||
#include <LibDeviceTree/FlattenedDeviceTree.h>
|
||||
|
||||
// These come from the linker script
|
||||
extern u8 page_tables_phys_start[];
|
||||
|
@ -173,10 +175,11 @@ static UNMAP_AFTER_INIT void setup_kernel_page_directory(u64* root_table)
|
|||
}
|
||||
|
||||
// This function has to fit into one page as it will be identity mapped.
|
||||
[[gnu::aligned(PAGE_SIZE)]] [[noreturn]] UNMAP_AFTER_INIT static void enable_paging(FlatPtr satp, u64* enable_paging_pte)
|
||||
[[gnu::aligned(PAGE_SIZE)]] [[noreturn]] UNMAP_AFTER_INIT static void enable_paging(BootInfo const& info, FlatPtr satp, u64* enable_paging_pte)
|
||||
{
|
||||
// Switch current root page table to argument 0. This will immediately take effect, but we won't not crash as this function is identity mapped.
|
||||
// Also, set up a temporary trap handler to catch any traps while switching page tables.
|
||||
register FlatPtr a0 asm("a0") = bit_cast<FlatPtr>(&info);
|
||||
asm volatile(
|
||||
" lla t0, 1f \n"
|
||||
" csrw stvec, t0 \n"
|
||||
|
@ -210,21 +213,33 @@ static UNMAP_AFTER_INIT void setup_kernel_page_directory(u64* root_table)
|
|||
" wfi \n"
|
||||
" j 1b \n"
|
||||
:
|
||||
: [satp] "r"(satp), [offset] "r"(calculate_physical_to_link_time_address_offset()), [enable_paging_pte] "r"(enable_paging_pte)
|
||||
: "r"(a0), [satp] "r"(satp), [offset] "r"(calculate_physical_to_link_time_address_offset()), [enable_paging_pte] "r"(enable_paging_pte)
|
||||
: "t0");
|
||||
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
[[noreturn]] UNMAP_AFTER_INIT void init_page_tables_and_jump_to_init()
|
||||
[[noreturn]] UNMAP_AFTER_INIT void init_page_tables_and_jump_to_init(FlatPtr mhartid, PhysicalPtr fdt_phys_addr)
|
||||
{
|
||||
if (RISCV64::CSR::SATP::read().MODE != RISCV64::CSR::SATP::Mode::Bare)
|
||||
panic_without_mmu("Kernel booted with MMU enabled"sv);
|
||||
|
||||
// Copy the FDT to a known location
|
||||
DeviceTree::FlattenedDeviceTreeHeader* fdt_header = bit_cast<DeviceTree::FlattenedDeviceTreeHeader*>(fdt_phys_addr);
|
||||
u8* fdt_storage = bit_cast<u8*>(fdt_phys_addr);
|
||||
if (fdt_header->totalsize > fdt_storage_size)
|
||||
panic_without_mmu("Passed FDT is bigger than the internal storage"sv);
|
||||
for (size_t o = 0; o < fdt_header->totalsize; o += 1) {
|
||||
// FIXME: Maybe increase the IO size here
|
||||
adjust_by_mapping_base(s_fdt_storage)[o] = fdt_storage[o];
|
||||
}
|
||||
|
||||
*adjust_by_mapping_base(&physical_to_virtual_offset) = calculate_physical_to_link_time_address_offset();
|
||||
*adjust_by_mapping_base(&kernel_mapping_base) = KERNEL_MAPPING_BASE;
|
||||
*adjust_by_mapping_base(&kernel_load_base) = KERNEL_MAPPING_BASE;
|
||||
|
||||
*adjust_by_mapping_base(&s_boot_info) = { .mhartid = mhartid, .fdt_phys_addr = fdt_phys_addr };
|
||||
|
||||
PageBumpAllocator allocator(adjust_by_mapping_base(reinterpret_cast<u64*>(page_tables_phys_start)), adjust_by_mapping_base(reinterpret_cast<u64*>(page_tables_phys_end)));
|
||||
auto* root_table = allocator.take_page();
|
||||
build_mappings(allocator, root_table);
|
||||
|
@ -247,7 +262,7 @@ static UNMAP_AFTER_INIT void setup_kernel_page_directory(u64* root_table)
|
|||
.MODE = RISCV64::CSR::SATP::Mode::Sv39,
|
||||
};
|
||||
|
||||
enable_paging(bit_cast<FlatPtr>(satp), &enable_paging_level0_table[enable_paging_vpn_0]);
|
||||
enable_paging(s_boot_info, bit_cast<FlatPtr>(satp), &enable_paging_level0_table[enable_paging_vpn_0]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,12 +7,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Forward.h>
|
||||
#include <Kernel/Prekernel/Prekernel.h>
|
||||
|
||||
#include <AK/Platform.h>
|
||||
VALIDATE_IS_RISCV64()
|
||||
|
||||
namespace Kernel::Memory {
|
||||
|
||||
[[noreturn]] void init_page_tables_and_jump_to_init();
|
||||
[[noreturn]] void init_page_tables_and_jump_to_init(FlatPtr mhartid, PhysicalPtr fdt_phys_addr);
|
||||
|
||||
}
|
||||
|
|
|
@ -45,13 +45,11 @@ UNMAP_AFTER_INIT void dbgln_without_mmu(StringView message)
|
|||
|
||||
extern "C" [[noreturn]] UNMAP_AFTER_INIT void pre_init(FlatPtr mhartid, PhysicalPtr fdt_phys_addr)
|
||||
{
|
||||
(void)mhartid;
|
||||
(void)fdt_phys_addr;
|
||||
|
||||
// Catch traps in pre_init
|
||||
RISCV64::CSR::write(RISCV64::CSR::Address::STVEC, bit_cast<FlatPtr>(&early_trap_handler));
|
||||
|
||||
Memory::init_page_tables_and_jump_to_init();
|
||||
Memory::init_page_tables_and_jump_to_init(mhartid, fdt_phys_addr);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -529,6 +529,7 @@ elseif("${SERENITY_ARCH}" STREQUAL "riscv64")
|
|||
Arch/riscv64/Firmware/ACPI/StaticParsing.cpp
|
||||
|
||||
Arch/riscv64/boot.S
|
||||
Arch/riscv64/CPU.cpp
|
||||
Arch/riscv64/CurrentTime.cpp
|
||||
Arch/riscv64/DebugOutput.cpp
|
||||
Arch/riscv64/Delay.cpp
|
||||
|
@ -575,6 +576,12 @@ set(AK_SOURCES
|
|||
../AK/UUID.cpp
|
||||
)
|
||||
|
||||
set(DT_SOURCES
|
||||
../Userland/Libraries/LibDeviceTree/DeviceTree.cpp
|
||||
../Userland/Libraries/LibDeviceTree/FlattenedDeviceTree.cpp
|
||||
../Userland/Libraries/LibDeviceTree/Validation.cpp
|
||||
)
|
||||
|
||||
set(EDID_SOURCES
|
||||
../Userland/Libraries/LibEDID/DMT.cpp
|
||||
../Userland/Libraries/LibEDID/EDID.cpp
|
||||
|
@ -624,6 +631,7 @@ set(SOURCES
|
|||
${KERNEL_SOURCES}
|
||||
${GENERATED_SOURCES}
|
||||
${AK_SOURCES}
|
||||
${DT_SOURCES}
|
||||
${EDID_SOURCES}
|
||||
${ELF_SOURCES}
|
||||
${VT_SOURCES}
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
*/
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/QuickSort.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <Kernel/Arch/CPU.h>
|
||||
#include <Kernel/Arch/PageDirectory.h>
|
||||
|
@ -26,6 +28,7 @@
|
|||
#include <Kernel/Sections.h>
|
||||
#include <Kernel/Security/AddressSanitizer.h>
|
||||
#include <Kernel/Tasks/Process.h>
|
||||
#include <Userland/Libraries/LibDeviceTree/FlattenedDeviceTree.h>
|
||||
|
||||
extern u8 start_of_kernel_image[];
|
||||
extern u8 end_of_kernel_image[];
|
||||
|
@ -249,7 +252,7 @@ bool MemoryManager::is_allowed_to_read_physical_memory_for_userspace(PhysicalAdd
|
|||
UNMAP_AFTER_INIT void MemoryManager::parse_memory_map()
|
||||
{
|
||||
// Register used memory regions that we know of.
|
||||
m_global_data.with([&](auto& global_data) {
|
||||
m_global_data.with([this](auto& global_data) {
|
||||
global_data.used_memory_ranges.ensure_capacity(4);
|
||||
#if ARCH(X86_64)
|
||||
// NOTE: We don't touch the first 1 MiB of RAM on x86-64 even if it's usable as indicated
|
||||
|
@ -278,6 +281,279 @@ UNMAP_AFTER_INIT void MemoryManager::parse_memory_map()
|
|||
#endif
|
||||
global_data.used_memory_ranges.append(UsedMemoryRange { UsedMemoryRangeType::Kernel, PhysicalAddress(virtual_to_low_physical((FlatPtr)start_of_kernel_image)), PhysicalAddress(page_round_up(virtual_to_low_physical((FlatPtr)end_of_kernel_image)).release_value_but_fixme_should_propagate_errors()) });
|
||||
|
||||
#if ARCH(RISCV64)
|
||||
// FIXME: AARCH64 might be able to make use of this code path
|
||||
// Some x86 platforms also provide flattened device trees
|
||||
parse_memory_map_fdt(global_data, s_fdt_storage);
|
||||
#else
|
||||
parse_memory_map_multiboot(global_data);
|
||||
#endif
|
||||
|
||||
// Now we need to setup the physical regions we will use later
|
||||
struct ContiguousPhysicalVirtualRange {
|
||||
PhysicalAddress lower;
|
||||
PhysicalAddress upper;
|
||||
};
|
||||
Optional<ContiguousPhysicalVirtualRange> last_contiguous_physical_range;
|
||||
for (auto range : global_data.physical_memory_ranges) {
|
||||
if (range.type != PhysicalMemoryRangeType::Usable)
|
||||
continue;
|
||||
auto address = range.start.get();
|
||||
auto length = range.length;
|
||||
|
||||
// Fix up unaligned memory regions.
|
||||
auto diff = (FlatPtr)address % PAGE_SIZE;
|
||||
if (diff != 0) {
|
||||
dmesgln("MM: Got an unaligned usable physical_region from the bootloader; correcting {:p} by {} bytes", address, diff);
|
||||
diff = PAGE_SIZE - diff;
|
||||
address += diff;
|
||||
length -= diff;
|
||||
}
|
||||
if ((length % PAGE_SIZE) != 0) {
|
||||
dmesgln("MM: Got an unaligned usable physical_region from the bootloader; correcting length {} by {} bytes", length, length % PAGE_SIZE);
|
||||
length -= length % PAGE_SIZE;
|
||||
}
|
||||
if (length < PAGE_SIZE) {
|
||||
dmesgln("MM: Memory usable physical_region from bootloader is too small; we want >= {} bytes, but got {} bytes", PAGE_SIZE, length);
|
||||
continue;
|
||||
}
|
||||
|
||||
// FIXME: This might have a nicer solution than slicing the ranges apart,
|
||||
// to just put them back together when we dont find a used range in them
|
||||
for (PhysicalSize page_base = address; page_base <= (address + length); page_base += PAGE_SIZE) {
|
||||
auto addr = PhysicalAddress(page_base);
|
||||
|
||||
// Skip used memory ranges.
|
||||
bool should_skip = false;
|
||||
for (auto& used_range : global_data.used_memory_ranges) {
|
||||
if (addr.get() >= used_range.start.get() && addr.get() <= used_range.end.get()) {
|
||||
should_skip = true;
|
||||
page_base = used_range.end.get();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (should_skip)
|
||||
continue;
|
||||
|
||||
if (!last_contiguous_physical_range.has_value() || last_contiguous_physical_range->upper.offset(PAGE_SIZE) != addr) {
|
||||
if (last_contiguous_physical_range.has_value()) {
|
||||
auto range = last_contiguous_physical_range.release_value();
|
||||
// FIXME: OOM?
|
||||
global_data.physical_regions.append(PhysicalRegion::try_create(range.lower, range.upper).release_nonnull());
|
||||
}
|
||||
last_contiguous_physical_range = ContiguousPhysicalVirtualRange { .lower = addr, .upper = addr };
|
||||
} else {
|
||||
last_contiguous_physical_range->upper = addr;
|
||||
}
|
||||
}
|
||||
// FIXME: If this is ever false, theres a good chance that all physical memory is already spent
|
||||
if (last_contiguous_physical_range.has_value()) {
|
||||
auto range = last_contiguous_physical_range.release_value();
|
||||
// FIXME: OOM?
|
||||
global_data.physical_regions.append(PhysicalRegion::try_create(range.lower, range.upper).release_nonnull());
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& region : global_data.physical_regions)
|
||||
global_data.system_memory_info.physical_pages += region->size();
|
||||
|
||||
register_reserved_ranges();
|
||||
for (auto& range : global_data.reserved_memory_ranges) {
|
||||
dmesgln("MM: Contiguous reserved range from {}, length is {}", range.start, range.length);
|
||||
}
|
||||
|
||||
initialize_physical_pages();
|
||||
|
||||
VERIFY(global_data.system_memory_info.physical_pages > 0);
|
||||
|
||||
// We start out with no committed pages
|
||||
global_data.system_memory_info.physical_pages_uncommitted = global_data.system_memory_info.physical_pages;
|
||||
|
||||
for (auto& used_range : global_data.used_memory_ranges) {
|
||||
dmesgln("MM: {} range @ {} - {} (size {:#x})", UserMemoryRangeTypeNames[to_underlying(used_range.type)], used_range.start, used_range.end.offset(-1), used_range.end.as_ptr() - used_range.start.as_ptr());
|
||||
}
|
||||
|
||||
for (auto& region : global_data.physical_regions) {
|
||||
dmesgln("MM: User physical region: {} - {} (size {:#x})", region->lower(), region->upper().offset(-1), PAGE_SIZE * region->size());
|
||||
region->initialize_zones();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT void MemoryManager::parse_memory_map_fdt(MemoryManager::GlobalData& global_data, u8 const* fdt_addr)
|
||||
{
|
||||
auto const& fdt_header = *reinterpret_cast<DeviceTree::FlattenedDeviceTreeHeader const*>(fdt_addr);
|
||||
auto fdt_buffer = ReadonlyBytes(fdt_addr, fdt_header.totalsize);
|
||||
|
||||
// FIXME: Parse the MemoryReservationBlock
|
||||
|
||||
// Schema:
|
||||
// https://github.com/devicetree-org/dt-schema/blob/main/dtschema/schemas/root-node.yaml
|
||||
// -> /#address-cells ∈ [1,2], /#size-cells ∈ [1,2]
|
||||
// Reserved Memory:
|
||||
// https://android.googlesource.com/kernel/msm/+/android-7.1.0_r0.2/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
|
||||
// -> #address-cells === /#address-cells, #size-cells === /#size-cells
|
||||
// https://github.com/devicetree-org/dt-schema/blob/main/dtschema/schemas/reserved-memory/reserved-memory.yaml
|
||||
// Memory:
|
||||
// https://github.com/devicetree-org/dt-schema/blob/main/dtschema/schemas/memory.yaml
|
||||
// -> #address-cells: /#address-cells , #size-cells: /size-cells #
|
||||
|
||||
// FIXME: When booting from UEFI, the /memory node may not be relied upon
|
||||
enum class State {
|
||||
Root,
|
||||
InReservedMemory,
|
||||
InReservedMemoryChild,
|
||||
|
||||
InMemory
|
||||
};
|
||||
|
||||
struct {
|
||||
u32 depth = 0;
|
||||
State state = State::Root;
|
||||
Optional<u64> start {};
|
||||
Optional<u64> size {};
|
||||
u32 address_cells = 0;
|
||||
u32 size_cells = 0;
|
||||
} state;
|
||||
|
||||
MUST(DeviceTree::walk_device_tree(
|
||||
fdt_header, fdt_buffer,
|
||||
DeviceTree::DeviceTreeCallbacks {
|
||||
.on_node_begin = [&state](StringView node_name) -> ErrorOr<IterationDecision> {
|
||||
switch (state.state) {
|
||||
case State::Root:
|
||||
if (state.depth != 1)
|
||||
break;
|
||||
if (node_name == "reserved-memory")
|
||||
state.state = State::InReservedMemory;
|
||||
else if (node_name.starts_with("memory"sv))
|
||||
state.state = State::InMemory;
|
||||
break;
|
||||
case State::InReservedMemory:
|
||||
// FIXME: The node names may hint to the purpose
|
||||
state.state = State::InReservedMemoryChild;
|
||||
state.start = {};
|
||||
state.size = {};
|
||||
break;
|
||||
case State::InReservedMemoryChild:
|
||||
case State::InMemory:
|
||||
// We should never be here
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
state.depth++;
|
||||
return IterationDecision::Continue;
|
||||
},
|
||||
.on_node_end = [&global_data, &state](StringView node_name) -> ErrorOr<IterationDecision> {
|
||||
switch (state.state) {
|
||||
case State::Root:
|
||||
break;
|
||||
case State::InReservedMemory:
|
||||
state.state = State::Root;
|
||||
break;
|
||||
case State::InMemory:
|
||||
VERIFY(state.start.has_value() && state.size.has_value());
|
||||
dbgln("MM: Memory Range {}: address: {} size {:#x}", node_name, PhysicalAddress { state.start.value() }, state.size.value());
|
||||
global_data.physical_memory_ranges.append(PhysicalMemoryRange { PhysicalMemoryRangeType::Usable, PhysicalAddress { state.start.value() }, state.size.value() });
|
||||
state.state = State::Root;
|
||||
break;
|
||||
case State::InReservedMemoryChild:
|
||||
// FIXME: Handle non static allocations,
|
||||
VERIFY(state.start.has_value() && state.size.has_value());
|
||||
dbgln("MM: Reserved Range {}: address: {} size {:#x}", node_name, PhysicalAddress { state.start.value() }, state.size.value());
|
||||
global_data.physical_memory_ranges.append(PhysicalMemoryRange { PhysicalMemoryRangeType::Reserved, PhysicalAddress { state.start.value() }, state.size.value() });
|
||||
// FIXME: Not all of these are "used", only those in "memory" are actually "used"
|
||||
// There might be for example debug DMA control registers, which are marked as reserved
|
||||
global_data.used_memory_ranges.append(UsedMemoryRange { UsedMemoryRangeType::BootModule, PhysicalAddress { state.start.value() }, PhysicalAddress { state.start.value() + state.size.value() } });
|
||||
state.state = State::InReservedMemory;
|
||||
break;
|
||||
}
|
||||
state.depth--;
|
||||
return IterationDecision::Continue;
|
||||
},
|
||||
.on_property = [&state](StringView property_name, ReadonlyBytes data) -> ErrorOr<IterationDecision> {
|
||||
switch (state.state) {
|
||||
case State::Root:
|
||||
if (state.depth != 1)
|
||||
break;
|
||||
if (property_name == "#address-cells"sv) {
|
||||
BigEndian<u32> data_as_int;
|
||||
__builtin_memcpy(&data_as_int, data.data(), sizeof(u32));
|
||||
state.address_cells = data_as_int;
|
||||
VERIFY(state.address_cells != 0);
|
||||
VERIFY(state.address_cells <= 2);
|
||||
} else if (property_name == "#size-cells"sv) {
|
||||
BigEndian<u32> data_as_int;
|
||||
__builtin_memcpy(&data_as_int, data.data(), sizeof(u32));
|
||||
state.size_cells = data_as_int;
|
||||
VERIFY(state.size_cells != 0);
|
||||
VERIFY(state.size_cells <= 2);
|
||||
}
|
||||
break;
|
||||
case State::InReservedMemory:
|
||||
// FIXME: We could check and verify that the address and size cells
|
||||
// are the same as in the root node
|
||||
// FIXME: Handle the ranges attribute if not empty
|
||||
if (property_name == "ranges"sv && data.size() != 0)
|
||||
TODO();
|
||||
break;
|
||||
case State::InReservedMemoryChild:
|
||||
case State::InMemory:
|
||||
if (property_name == "reg"sv) {
|
||||
VERIFY(state.address_cells);
|
||||
VERIFY(state.size_cells);
|
||||
// FIXME: We may get more than one range here
|
||||
if (data.size() > (state.address_cells + state.size_cells) * sizeof(u32))
|
||||
TODO();
|
||||
if (state.address_cells == 1) {
|
||||
BigEndian<u32> data_as_int;
|
||||
__builtin_memcpy(&data_as_int, data.data(), sizeof(u32));
|
||||
state.start = data_as_int;
|
||||
data = data.slice(sizeof(u32));
|
||||
} else {
|
||||
BigEndian<u64> data_as_int;
|
||||
__builtin_memcpy(&data_as_int, data.data(), sizeof(u64));
|
||||
state.start = data_as_int;
|
||||
data = data.slice(sizeof(u64));
|
||||
}
|
||||
|
||||
if (state.size_cells == 1) {
|
||||
BigEndian<u32> data_as_int;
|
||||
__builtin_memcpy(&data_as_int, data.data(), sizeof(u32));
|
||||
state.size = data_as_int;
|
||||
data = data.slice(sizeof(u32));
|
||||
} else {
|
||||
BigEndian<u64> data_as_int;
|
||||
__builtin_memcpy(&data_as_int, data.data(), sizeof(u64));
|
||||
state.size = data_as_int;
|
||||
data = data.slice(sizeof(u64));
|
||||
}
|
||||
} else {
|
||||
// Reserved Memory:
|
||||
// FIXME: Handle `compatible: "framebuffer";`
|
||||
// FIMXE: Handle `compatible: "shared-dma-pool";`, `compatible: "restricted-dma-pool";`
|
||||
// FIXME: Handle "iommu-addresses" property
|
||||
// FIXME: Support "size" and "align" property
|
||||
// Also "alloc-ranges"
|
||||
// FIXME: Support no-map
|
||||
// FIXME: Support no-map-fixup
|
||||
// FIXME: Support reusable
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return IterationDecision::Continue;
|
||||
},
|
||||
.on_noop = []() -> ErrorOr<IterationDecision> { return IterationDecision::Continue; },
|
||||
.on_end = []() -> ErrorOr<void> { return {}; },
|
||||
}));
|
||||
|
||||
// FDTs do not seem to be fully sort memory ranges, especially as we get them from at least two structures
|
||||
quick_sort(global_data.physical_memory_ranges, [](auto& a, auto& b) -> bool { return a.start > b.start; });
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT void MemoryManager::parse_memory_map_multiboot(MemoryManager::GlobalData& global_data)
|
||||
{
|
||||
// Register used memory regions that we know of.
|
||||
if (multiboot_flags & 0x4) {
|
||||
auto* bootmods_start = multiboot_copy_boot_modules_array;
|
||||
auto* bootmods_end = bootmods_start + multiboot_copy_boot_modules_count;
|
||||
|
@ -342,88 +618,7 @@ UNMAP_AFTER_INIT void MemoryManager::parse_memory_map()
|
|||
global_data.physical_memory_ranges.append(PhysicalMemoryRange { PhysicalMemoryRangeType::Unknown, start_address, length });
|
||||
break;
|
||||
}
|
||||
|
||||
if (mmap->type != MULTIBOOT_MEMORY_AVAILABLE)
|
||||
continue;
|
||||
|
||||
// Fix up unaligned memory regions.
|
||||
auto diff = (FlatPtr)address % PAGE_SIZE;
|
||||
if (diff != 0) {
|
||||
dmesgln("MM: Got an unaligned physical_region from the bootloader; correcting {:p} by {} bytes", address, diff);
|
||||
diff = PAGE_SIZE - diff;
|
||||
address += diff;
|
||||
length -= diff;
|
||||
}
|
||||
if ((length % PAGE_SIZE) != 0) {
|
||||
dmesgln("MM: Got an unaligned physical_region from the bootloader; correcting length {} by {} bytes", length, length % PAGE_SIZE);
|
||||
length -= length % PAGE_SIZE;
|
||||
}
|
||||
if (length < PAGE_SIZE) {
|
||||
dmesgln("MM: Memory physical_region from bootloader is too small; we want >= {} bytes, but got {} bytes", PAGE_SIZE, length);
|
||||
continue;
|
||||
}
|
||||
|
||||
// FIXME: This might have a nicer solution than slicing the ranges apart,
|
||||
// to just put them back together when we dont find a used range in them
|
||||
for (PhysicalSize page_base = address; page_base <= (address + length); page_base += PAGE_SIZE) {
|
||||
auto addr = PhysicalAddress(page_base);
|
||||
|
||||
// Skip used memory ranges.
|
||||
bool should_skip = false;
|
||||
for (auto& used_range : global_data.used_memory_ranges) {
|
||||
if (addr.get() >= used_range.start.get() && addr.get() <= used_range.end.get()) {
|
||||
should_skip = true;
|
||||
page_base = used_range.end.get();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (should_skip)
|
||||
continue;
|
||||
|
||||
if (!last_contiguous_physical_range.has_value() || last_contiguous_physical_range->upper.offset(PAGE_SIZE) != addr) {
|
||||
if (last_contiguous_physical_range.has_value()) {
|
||||
auto range = last_contiguous_physical_range.release_value();
|
||||
// FIXME: OOM?
|
||||
global_data.physical_regions.append(PhysicalRegion::try_create(range.lower, range.upper).release_nonnull());
|
||||
}
|
||||
last_contiguous_physical_range = ContiguousPhysicalVirtualRange { .lower = addr, .upper = addr };
|
||||
} else {
|
||||
last_contiguous_physical_range->upper = addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: If this is ever false, theres a good chance that all physical memory is already spent
|
||||
if (last_contiguous_physical_range.has_value()) {
|
||||
auto range = last_contiguous_physical_range.release_value();
|
||||
// FIXME: OOM?
|
||||
global_data.physical_regions.append(PhysicalRegion::try_create(range.lower, range.upper).release_nonnull());
|
||||
}
|
||||
|
||||
for (auto& region : global_data.physical_regions)
|
||||
global_data.system_memory_info.physical_pages += region->size();
|
||||
|
||||
register_reserved_ranges();
|
||||
for (auto& range : global_data.reserved_memory_ranges) {
|
||||
dmesgln("MM: Contiguous reserved range from {}, length is {}", range.start, range.length);
|
||||
}
|
||||
|
||||
initialize_physical_pages();
|
||||
|
||||
VERIFY(global_data.system_memory_info.physical_pages > 0);
|
||||
|
||||
// We start out with no committed pages
|
||||
global_data.system_memory_info.physical_pages_uncommitted = global_data.system_memory_info.physical_pages;
|
||||
|
||||
for (auto& used_range : global_data.used_memory_ranges) {
|
||||
dmesgln("MM: {} range @ {} - {} (size {:#x})", UserMemoryRangeTypeNames[to_underlying(used_range.type)], used_range.start, used_range.end.offset(-1), used_range.end.as_ptr() - used_range.start.as_ptr());
|
||||
}
|
||||
|
||||
for (auto& region : global_data.physical_regions) {
|
||||
dmesgln("MM: User physical region: {} - {} (size {:#x})", region->lower(), region->upper().offset(-1), PAGE_SIZE * region->size());
|
||||
region->initialize_zones();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT void MemoryManager::initialize_physical_pages()
|
||||
|
|
|
@ -240,6 +240,21 @@ private:
|
|||
MemoryManager();
|
||||
~MemoryManager();
|
||||
|
||||
struct GlobalData {
|
||||
GlobalData();
|
||||
|
||||
SystemMemoryInfo system_memory_info;
|
||||
|
||||
Vector<NonnullOwnPtr<PhysicalRegion>> physical_regions;
|
||||
OwnPtr<PhysicalRegion> physical_pages_region;
|
||||
|
||||
RegionTree region_tree;
|
||||
|
||||
Vector<UsedMemoryRange> used_memory_ranges;
|
||||
Vector<PhysicalMemoryRange> physical_memory_ranges;
|
||||
Vector<ContiguousReservedMemoryRange> reserved_memory_ranges;
|
||||
};
|
||||
|
||||
void initialize_physical_pages();
|
||||
void register_reserved_ranges();
|
||||
|
||||
|
@ -251,6 +266,8 @@ private:
|
|||
|
||||
void protect_kernel_image();
|
||||
void parse_memory_map();
|
||||
void parse_memory_map_fdt(GlobalData&, u8 const* fdt_addr);
|
||||
void parse_memory_map_multiboot(GlobalData&);
|
||||
static void flush_tlb_local(VirtualAddress, size_t page_count = 1);
|
||||
static void flush_tlb(PageDirectory const*, VirtualAddress, size_t page_count = 1);
|
||||
|
||||
|
@ -286,21 +303,6 @@ private:
|
|||
PhysicalPageEntry* m_physical_page_entries { nullptr };
|
||||
size_t m_physical_page_entries_count { 0 };
|
||||
|
||||
struct GlobalData {
|
||||
GlobalData();
|
||||
|
||||
SystemMemoryInfo system_memory_info;
|
||||
|
||||
Vector<NonnullOwnPtr<PhysicalRegion>> physical_regions;
|
||||
OwnPtr<PhysicalRegion> physical_pages_region;
|
||||
|
||||
RegionTree region_tree;
|
||||
|
||||
Vector<UsedMemoryRange> used_memory_ranges;
|
||||
Vector<PhysicalMemoryRange> physical_memory_ranges;
|
||||
Vector<ContiguousReservedMemoryRange> reserved_memory_ranges;
|
||||
};
|
||||
|
||||
SpinlockProtected<GlobalData, LockRank::None> m_global_data;
|
||||
};
|
||||
|
||||
|
|
|
@ -22,17 +22,16 @@
|
|||
#ifdef __cplusplus
|
||||
namespace Kernel {
|
||||
|
||||
# if ARCH(X86_64)
|
||||
struct [[gnu::packed]] BootInfo {
|
||||
u32 start_of_prekernel_image;
|
||||
u32 end_of_prekernel_image;
|
||||
u64 physical_to_virtual_offset;
|
||||
u64 kernel_mapping_base;
|
||||
u64 kernel_load_base;
|
||||
# if ARCH(X86_64)
|
||||
u32 gdt64ptr;
|
||||
u16 code64_sel;
|
||||
u32 boot_pml4t;
|
||||
# endif
|
||||
u32 boot_pdpt;
|
||||
u32 boot_pd0;
|
||||
u32 boot_pd_kernel;
|
||||
|
@ -50,5 +49,14 @@ struct [[gnu::packed]] BootInfo {
|
|||
u8 multiboot_framebuffer_bpp;
|
||||
u8 multiboot_framebuffer_type;
|
||||
};
|
||||
# elif ARCH(AARCH64)
|
||||
struct BootInfo { };
|
||||
# elif ARCH(RISCV64)
|
||||
struct BootInfo {
|
||||
FlatPtr mhartid;
|
||||
PhysicalPtr fdt_phys_addr;
|
||||
};
|
||||
# endif
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue