Skip to content

Commit

Permalink
Kernel/aarch64: Use MM APIs for MMIO
Browse files Browse the repository at this point in the history
This causes serial output to stop working before MM is completely
initialized, as MMIO is not usable before that.
  • Loading branch information
spholz committed Oct 11, 2024
1 parent 68d1a8e commit 9902da9
Show file tree
Hide file tree
Showing 22 changed files with 169 additions and 80 deletions.
10 changes: 0 additions & 10 deletions Kernel/Arch/aarch64/MMU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,33 +171,23 @@ static void build_mappings(PageBumpAllocator& allocator, u64* root_table)
u64 normal_memory_flags = ACCESS_FLAG | PAGE_DESCRIPTOR | INNER_SHAREABLE | NORMAL_MEMORY;
u64 device_memory_flags = ACCESS_FLAG | PAGE_DESCRIPTOR | OUTER_SHAREABLE | DEVICE_MEMORY;

// TODO: We should change the RPi drivers to use the MemoryManager to map physical memory,
// instead of mapping the complete MMIO region beforehand.
auto mmio_base = RPi::MMIO::the().peripheral_base_address().get();
auto mmio_end = RPi::MMIO::the().peripheral_end_address().get();

// Align the identity mapping of the kernel image to 2 MiB, the rest of the memory is initially not mapped.
auto start_of_kernel_range = VirtualAddress((FlatPtr)start_of_kernel_image & ~(FlatPtr)0x1fffff);
auto end_of_kernel_range = VirtualAddress(((FlatPtr)end_of_kernel_image & ~(FlatPtr)0x1fffff) + 0x200000 - 1);
auto start_of_mmio_range = VirtualAddress(mmio_base + KERNEL_MAPPING_BASE);
auto end_of_mmio_range = VirtualAddress(mmio_end + KERNEL_MAPPING_BASE);

// FIXME: don't use the memory before `start` as the initial stack
auto start_of_stack_range = start_of_kernel_range.offset(-128 * KiB);
auto end_of_stack_range = start_of_kernel_range;

auto start_of_physical_kernel_range = PhysicalAddress(start_of_kernel_range.get()).offset(-calculate_physical_to_link_time_address_offset());
auto start_of_physical_mmio_range = PhysicalAddress(mmio_base);
auto start_of_physical_stack_range = PhysicalAddress { start_of_stack_range.get() }.offset(-calculate_physical_to_link_time_address_offset());

// Insert identity mappings
insert_entries_for_memory_range(allocator, root_table, start_of_kernel_range.offset(-calculate_physical_to_link_time_address_offset()), end_of_kernel_range.offset(-calculate_physical_to_link_time_address_offset()), start_of_physical_kernel_range, normal_memory_flags);
insert_entries_for_memory_range(allocator, root_table, VirtualAddress(mmio_base), VirtualAddress(mmio_end), start_of_physical_mmio_range, device_memory_flags);
insert_entries_for_memory_range(allocator, root_table, start_of_stack_range.offset(-calculate_physical_to_link_time_address_offset()), end_of_stack_range.offset(-calculate_physical_to_link_time_address_offset()), start_of_physical_stack_range, device_memory_flags);

// Map kernel and MMIO into high virtual memory
insert_entries_for_memory_range(allocator, root_table, start_of_kernel_range, end_of_kernel_range, start_of_physical_kernel_range, normal_memory_flags);
insert_entries_for_memory_range(allocator, root_table, start_of_mmio_range, end_of_mmio_range, start_of_physical_mmio_range, device_memory_flags);

// Map the initial stack
insert_entries_for_memory_range(allocator, root_table, start_of_stack_range, end_of_stack_range, start_of_physical_stack_range, normal_memory_flags);
Expand Down
51 changes: 51 additions & 0 deletions Kernel/Arch/aarch64/RPi/AUX.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2024, Sönke Holz <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#include <AK/Singleton.h>
#include <Kernel/Arch/aarch64/RPi/AUX.h>
#include <Kernel/Arch/aarch64/RPi/MMIO.h>

namespace Kernel::RPi {

// bcm2711-peripherals.pdf "2. Auxiliaries: UART1, SPI1 & SPI2"
struct AUXRegisters {
u32 IRQ;
struct {
u32 mini_uart_enable : 1;
u32 spi1_enable : 1;
u32 spi2_enable : 1;
u32 : 29;
} ENABLES;
};
static_assert(AssertSize<AUXRegisters, 8>());

AUX::AUX()
: m_registers(MMIO::the().peripheral<AUXRegisters>(0x21'5000).release_value_but_fixme_should_propagate_errors())
{
}

AUX& AUX::the()
{
static Singleton<AUX> instance;
return instance;
}

void AUX::set_peripheral_enabled(Peripheral peripheral, bool enabled)
{
switch (peripheral) {
case Peripheral::MiniUART:
m_registers->ENABLES.mini_uart_enable = enabled;
break;
case Peripheral::SPI1:
m_registers->ENABLES.spi1_enable = enabled;
break;
case Peripheral::SPI2:
m_registers->ENABLES.spi2_enable = enabled;
break;
}
}

}
32 changes: 32 additions & 0 deletions Kernel/Arch/aarch64/RPi/AUX.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (c) 2024, Sönke Holz <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#pragma once

#include <Kernel/Memory/TypedMapping.h>

namespace Kernel::RPi {

struct AUXRegisters;

class AUX {
public:
AUX();
static AUX& the();

enum class Peripheral {
MiniUART,
SPI1,
SPI2,
};

void set_peripheral_enabled(Peripheral, bool);

private:
Memory::TypedMapping<AUXRegisters volatile> m_registers;
};

}
3 changes: 3 additions & 0 deletions Kernel/Arch/aarch64/RPi/DebugOutput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ namespace Kernel {

void debug_output(char ch)
{
if (!Memory::MemoryManager::is_initialized())
return;

RPi::UART::the().send(ch);
}

Expand Down
5 changes: 3 additions & 2 deletions Kernel/Arch/aarch64/RPi/GPIO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/

#include <AK/Singleton.h>
#include <Kernel/Arch/aarch64/ASM_wrapper.h>
#include <Kernel/Arch/aarch64/RPi/GPIO.h>
#include <Kernel/Arch/aarch64/RPi/MMIO.h>
Expand Down Expand Up @@ -38,13 +39,13 @@ struct GPIOControlRegisters {
};

GPIO::GPIO()
: m_registers(MMIO::the().peripheral<GPIOControlRegisters>(0x20'0000))
: m_registers(MMIO::the().peripheral<GPIOControlRegisters>(0x20'0000).release_value_but_fixme_should_propagate_errors())
{
}

GPIO& GPIO::the()
{
static GPIO instance;
static Singleton<GPIO> instance;
return instance;
}

Expand Down
6 changes: 4 additions & 2 deletions Kernel/Arch/aarch64/RPi/GPIO.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <AK/Array.h>
#include <AK/Types.h>
#include <Kernel/Memory/TypedMapping.h>

namespace Kernel::RPi {

Expand All @@ -16,6 +17,8 @@ struct GPIOControlRegisters;
// Can configure the general-purpose I/O registers on a Raspberry Pi.
class GPIO {
public:
GPIO();

enum class PinFunction {
Input = 0,
Output = 1,
Expand Down Expand Up @@ -53,10 +56,9 @@ class GPIO {
void set_pin_high_detect_enable(unsigned pin_number, bool enable);

private:
GPIO();
void internal_enable_pins(u32 enable[2], PullUpDownState state);

GPIOControlRegisters volatile* m_registers;
Memory::TypedMapping<GPIOControlRegisters volatile> m_registers;
};

}
2 changes: 1 addition & 1 deletion Kernel/Arch/aarch64/RPi/InterruptController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ struct InterruptControllerRegisters {
};

InterruptController::InterruptController()
: m_registers(MMIO::the().peripheral<InterruptControllerRegisters>(0xB200))
: m_registers(MMIO::the().peripheral<InterruptControllerRegisters>(0xB200).release_value_but_fixme_should_propagate_errors())
{
}

Expand Down
3 changes: 2 additions & 1 deletion Kernel/Arch/aarch64/RPi/InterruptController.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <AK/StringView.h>
#include <AK/Types.h>
#include <Kernel/Arch/aarch64/IRQController.h>
#include <Kernel/Memory/TypedMapping.h>

namespace Kernel::RPi {

Expand All @@ -34,7 +35,7 @@ class InterruptController : public IRQController {
return "Raspberry Pi Interrupt Controller"sv;
}

InterruptControllerRegisters volatile* m_registers;
Memory::TypedMapping<InterruptControllerRegisters volatile> m_registers;
};

}
18 changes: 6 additions & 12 deletions Kernel/Arch/aarch64/RPi/MMIO.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <AK/Types.h>
#include <Kernel/Memory/PhysicalAddress.h>
#include <Kernel/Memory/TypedMapping.h>
#include <Kernel/Sections.h>

namespace Kernel::RPi {
Expand All @@ -20,18 +21,11 @@ class MMIO {
public:
static MMIO& the();

u32 read(FlatPtr offset) { return *peripheral_address(offset); }
void write(FlatPtr offset, u32 value) { *peripheral_address(offset) = value; }

// FIXME: The MMIO region is currently mapped at kernel_mapping_base + peripheral_base_address(),
// but the code should be changed to use the MemoryManager to map the physical memory instead
// of pre-mapping the whole MMIO region.
u32 volatile* peripheral_address(FlatPtr offset) { return (u32 volatile*)(kernel_mapping_base + m_base_address.get() + offset); }
template<class T>
T volatile* peripheral(FlatPtr offset) { return (T volatile*)peripheral_address(offset); }

PhysicalAddress peripheral_base_address() const { return m_base_address; }
PhysicalAddress peripheral_end_address() const { return m_base_address.offset(0x00FFFFFF); }
template<typename T>
ErrorOr<Memory::TypedMapping<T volatile>> peripheral(FlatPtr offset)
{
return Memory::map_typed_writable<T volatile>(PhysicalAddress { m_base_address.offset(offset) });
}

private:
MMIO();
Expand Down
53 changes: 29 additions & 24 deletions Kernel/Arch/aarch64/RPi/Mailbox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/

#include <AK/Singleton.h>
#include <Kernel/Arch/aarch64/ASM_wrapper.h>
#include <Kernel/Arch/aarch64/RPi/MMIO.h>
#include <Kernel/Arch/aarch64/RPi/Mailbox.h>
Expand All @@ -15,18 +16,19 @@ namespace Kernel::RPi {
// There's one mailbox at MBOX_BASE_OFFSET for reading responses from VideoCore, and one at MBOX_BASE_OFFSET + 0x20 for sending requests.
// Each has its own status word.

constexpr u32 MBOX_BASE_OFFSET = 0xB880;
constexpr u32 MBOX_0 = MBOX_BASE_OFFSET;
constexpr u32 MBOX_1 = MBOX_BASE_OFFSET + 0x20;

constexpr u32 MBOX_READ_DATA = MBOX_0;
constexpr u32 MBOX_READ_POLL = MBOX_0 + 0x10;
constexpr u32 MBOX_READ_SENDER = MBOX_0 + 0x14;
constexpr u32 MBOX_READ_STATUS = MBOX_0 + 0x18;
constexpr u32 MBOX_READ_CONFIG = MBOX_0 + 0x1C;

constexpr u32 MBOX_WRITE_DATA = MBOX_1;
constexpr u32 MBOX_WRITE_STATUS = MBOX_1 + 0x18;
struct MailboxRegisters {
u32 read_data;
u32 reserved0[3];
u32 read_poll;
u32 read_sender;
u32 read_status;
u32 read_config;

u32 write_data;
u32 reserved1[5];
u32 write_status;
};
static_assert(AssertSize<MailboxRegisters, 60>());

constexpr u32 MBOX_RESPONSE_SUCCESS = 0x8000'0000;
constexpr u32 MBOX_RESPONSE_PARTIAL = 0x8000'0001;
Expand All @@ -36,6 +38,11 @@ constexpr u32 MBOX_EMPTY = 0x4000'0000;

constexpr int ARM_TO_VIDEOCORE_CHANNEL = 8;

Mailbox::Mailbox()
: m_registers(MMIO::the().peripheral<MailboxRegisters>(0xb880).release_value_but_fixme_should_propagate_errors())
{
}

Mailbox::Message::Message(u32 tag, u32 arguments_size)
{
m_tag = tag;
Expand All @@ -56,40 +63,38 @@ bool Mailbox::MessageHeader::success() const

Mailbox& Mailbox::the()
{
static Mailbox instance;
static Singleton<Mailbox> instance;
return instance;
}

static void wait_until_we_can_write(MMIO& mmio)
void Mailbox::wait_until_we_can_write() const
{
// Since nothing else writes to the mailbox, this wait is mostly cargo-culted.
// Most baremetal tutorials on the internet query MBOX_READ_STATUS here, which I think is incorrect and only works because this wait really isn't needed.
while (mmio.read(MBOX_WRITE_STATUS) & MBOX_FULL)
while ((m_registers->write_status & MBOX_FULL) != 0)
Processor::wait_check();
}

static void wait_for_reply(MMIO& mmio)
void Mailbox::wait_for_reply() const
{
while (mmio.read(MBOX_READ_STATUS) & MBOX_EMPTY)
while ((m_registers->read_status & MBOX_EMPTY) != 0)
Processor::wait_check();
}

bool Mailbox::send_queue(void* queue, u32 queue_size) const
bool Mailbox::send_queue(void* queue, u32 queue_size)
{
// According to Raspberry Pi specs this is the only channel implemented.
u32 const channel = ARM_TO_VIDEOCORE_CHANNEL;

auto message_header = reinterpret_cast<MessageHeader*>(queue);
message_header->set_queue_size(queue_size);

auto& mmio = MMIO::the();

// The mailbox interface has a FIFO for message delivery in both directions.
// Responses can be delivered out of order to requests, but we currently ever only send on request at once.
// It'd be nice to have an async interface here where we send a message, then return immediately, and read the response when an interrupt arrives.
// But for now, this is synchronous.

wait_until_we_can_write(mmio);
wait_until_we_can_write();

// The mailbox message is 32-bit based, so this assumes that message is in the first 4 GiB.
// FIXME: Memory::virtual_to_low_physical only works for the initial kernel mappings (including the stack).
Expand All @@ -101,12 +106,12 @@ bool Mailbox::send_queue(void* queue, u32 queue_size) const
// The queue buffer might point to normal cached memory, so flush any writes that are in cache and not visible to VideoCore.
Aarch64::Asm::flush_data_cache((FlatPtr)queue, queue_size);

mmio.write(MBOX_WRITE_DATA, request);
m_registers->write_data = request;

for (;;) {
wait_for_reply(mmio);
wait_for_reply();

u32 response = mmio.read(MBOX_READ_DATA);
u32 response = m_registers->read_data;
// We keep at most one message in flight and do synchronous communication, so response will always be == request for us.
if (response == request)
return message_header->success();
Expand Down
13 changes: 12 additions & 1 deletion Kernel/Arch/aarch64/RPi/Mailbox.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,18 @@

#include <AK/StringView.h>
#include <AK/Types.h>
#include <Kernel/Memory/TypedMapping.h>

namespace Kernel::RPi {

struct MailboxRegisters;

// Can exchange mailbox messages with the Raspberry Pi's VideoCore chip.
// https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface
class Mailbox {
public:
Mailbox();

// Base class for Mailbox messages. Implemented in subsystems that use Mailbox.
class Message {
protected:
Expand Down Expand Up @@ -49,9 +54,15 @@ class Mailbox {
static Mailbox& the();

// Sends message queue to VideoCore
bool send_queue(void* queue, u32 queue_size) const;
bool send_queue(void* queue, u32 queue_size);

u32 query_firmware_version();

Memory::TypedMapping<MailboxRegisters volatile> m_registers;

private:
void wait_until_we_can_write() const;
void wait_for_reply() const;
};

}
Loading

0 comments on commit 9902da9

Please sign in to comment.