diff --git a/Kernel/Prekernel/Arch/aarch64/MMIO.h b/Kernel/Prekernel/Arch/aarch64/MMIO.h index 39f662641f1..0eb00ea5d32 100644 --- a/Kernel/Prekernel/Arch/aarch64/MMIO.h +++ b/Kernel/Prekernel/Arch/aarch64/MMIO.h @@ -6,6 +6,8 @@ #pragma once +#include + namespace Prekernel { // Knows about memory-mapped IO addresses on the Broadcom family of SOCs used in Raspberry Pis. @@ -16,6 +18,9 @@ class MMIO { public: static MMIO& the(); + u32 read(FlatPtr offset) { return *(u32 volatile*)(m_base_address + offset); } + void write(FlatPtr offset, u32 value) { *(u32 volatile*)(m_base_address + offset) = value; } + private: MMIO(); diff --git a/Kernel/Prekernel/Arch/aarch64/Mailbox.cpp b/Kernel/Prekernel/Arch/aarch64/Mailbox.cpp new file mode 100644 index 00000000000..28779e92d95 --- /dev/null +++ b/Kernel/Prekernel/Arch/aarch64/Mailbox.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021, Nico Weber + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Prekernel { + +// 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; + +constexpr u32 MBOX_RESPONSE_SUCCESS = 0x8000'0000; +constexpr u32 MBOX_RESPONSE_PARTIAL = 0x8000'0001; +constexpr u32 MBOX_REQUEST = 0; +constexpr u32 MBOX_FULL = 0x8000'0000; +constexpr u32 MBOX_EMPTY = 0x4000'0000; + +constexpr int ARM_TO_VIDEOCORE_CHANNEL = 8; + +static void wait_until_we_can_write(MMIO& mmio) +{ + // 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) + ; +} + +static void wait_for_reply(MMIO& mmio) +{ + while (mmio.read(MBOX_READ_STATUS) & MBOX_EMPTY) + ; +} + +bool Mailbox::call(u8 channel, u32 volatile* __attribute__((aligned(16))) message) +{ + auto& mmio = MMIO::the(); + + // The mailbox interface has a FIFO for message deliverly 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); + + // The mailbox message is 32-bit based, so this assumes that message is in the first 4 GiB. + u32 request = static_cast(reinterpret_cast(message) & ~0xF) | (channel & 0xF); + mmio.write(MBOX_WRITE_DATA, request); + + for (;;) { + wait_for_reply(mmio); + + u32 response = mmio.read(MBOX_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[1] == MBOX_RESPONSE_SUCCESS; + } + + return true; +} + +constexpr u32 MBOX_TAG_GET_FIRMWARE_VERSION = 0x0000'0001; + +u32 Mailbox::query_firmware_version() +{ + // See https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface for data format. + u32 __attribute__((aligned(16))) message[7]; + message[0] = sizeof(message); + message[1] = MBOX_REQUEST; + + message[2] = MBOX_TAG_GET_FIRMWARE_VERSION; + message[3] = 0; // Tag data size. MBOX_TAG_GET_FIRMWARE_VERSION needs no arguments. + message[4] = MBOX_REQUEST; + message[5] = 0; // Trailing zero for request, room for data in response. + + message[6] = 0; // Room for trailing zero in response. + + if (call(ARM_TO_VIDEOCORE_CHANNEL, message) && message[2] == MBOX_TAG_GET_FIRMWARE_VERSION) + return message[5]; + + return 0xffff'ffff; +} + +} diff --git a/Kernel/Prekernel/Arch/aarch64/Mailbox.h b/Kernel/Prekernel/Arch/aarch64/Mailbox.h new file mode 100644 index 00000000000..006812d0237 --- /dev/null +++ b/Kernel/Prekernel/Arch/aarch64/Mailbox.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2021, Nico Weber + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Prekernel { + +// Can exchange mailbox messages with the Raspberry Pi's VideoCore chip. +// https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface +class Mailbox { +public: + static bool call(u8 channel, u32 volatile* __attribute__((aligned(16))) data); + + static u32 query_firmware_version(); +}; + +} diff --git a/Kernel/Prekernel/Arch/aarch64/init.cpp b/Kernel/Prekernel/Arch/aarch64/init.cpp index 4a61464d608..d62e61701dc 100644 --- a/Kernel/Prekernel/Arch/aarch64/init.cpp +++ b/Kernel/Prekernel/Arch/aarch64/init.cpp @@ -5,14 +5,14 @@ */ #include -#include +#include extern "C" [[noreturn]] void halt(); extern "C" [[noreturn]] void init(); extern "C" [[noreturn]] void init() { - [[maybe_unused]] auto& MMIO = Prekernel::MMIO::the(); + [[maybe_unused]] u32 firmware_version = Prekernel::Mailbox::query_firmware_version(); halt(); } diff --git a/Kernel/Prekernel/CMakeLists.txt b/Kernel/Prekernel/CMakeLists.txt index a13ee09f0d3..66e88964b87 100644 --- a/Kernel/Prekernel/CMakeLists.txt +++ b/Kernel/Prekernel/CMakeLists.txt @@ -12,6 +12,7 @@ if ("${SERENITY_ARCH}" STREQUAL "aarch64") Arch/aarch64/boot.S ${SOURCES} + Arch/aarch64/Mailbox.cpp Arch/aarch64/MainIdRegister.cpp Arch/aarch64/MMIO.cpp Arch/aarch64/init.cpp