summaryrefslogtreecommitdiff
path: root/Kernel/Prekernel
diff options
context:
space:
mode:
authorNico Weber <thakis@chromium.org>2021-09-18 21:41:39 -0400
committerLinus Groh <mail@linusgroh.de>2021-09-19 17:45:59 +0200
commit697e6ccb89f7885b83ae6fbe194990865ec32e24 (patch)
tree28b29e2c006a0cf4a3c923a5386a3e1dac063fb6 /Kernel/Prekernel
parentd0b9c7a20b2518f268c055c86427404bdae3a12c (diff)
downloadserenity-697e6ccb89f7885b83ae6fbe194990865ec32e24.zip
Kernel: Add a Mailbox class for aarch64
As a demo, query the firmware version. `Meta/serenity.sh gdb aarch64` can be used to observe that qemu puts 0x548E1 in x0 in response to this mailbox message.
Diffstat (limited to 'Kernel/Prekernel')
-rw-r--r--Kernel/Prekernel/Arch/aarch64/MMIO.h5
-rw-r--r--Kernel/Prekernel/Arch/aarch64/Mailbox.cpp99
-rw-r--r--Kernel/Prekernel/Arch/aarch64/Mailbox.h22
-rw-r--r--Kernel/Prekernel/Arch/aarch64/init.cpp4
-rw-r--r--Kernel/Prekernel/CMakeLists.txt1
5 files changed, 129 insertions, 2 deletions
diff --git a/Kernel/Prekernel/Arch/aarch64/MMIO.h b/Kernel/Prekernel/Arch/aarch64/MMIO.h
index 39f662641f..0eb00ea5d3 100644
--- a/Kernel/Prekernel/Arch/aarch64/MMIO.h
+++ b/Kernel/Prekernel/Arch/aarch64/MMIO.h
@@ -6,6 +6,8 @@
#pragma once
+#include <AK/Types.h>
+
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 0000000000..28779e92d9
--- /dev/null
+++ b/Kernel/Prekernel/Arch/aarch64/Mailbox.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2021, Nico Weber <thakis@chromium.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <Kernel/Prekernel/Arch/aarch64/MMIO.h>
+#include <Kernel/Prekernel/Arch/aarch64/Mailbox.h>
+
+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<u32>(reinterpret_cast<FlatPtr>(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 0000000000..006812d023
--- /dev/null
+++ b/Kernel/Prekernel/Arch/aarch64/Mailbox.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2021, Nico Weber <thakis@chromium.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Types.h>
+
+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 4a61464d60..d62e61701d 100644
--- a/Kernel/Prekernel/Arch/aarch64/init.cpp
+++ b/Kernel/Prekernel/Arch/aarch64/init.cpp
@@ -5,14 +5,14 @@
*/
#include <AK/Types.h>
-#include <Kernel/Prekernel/Arch/aarch64/MMIO.h>
+#include <Kernel/Prekernel/Arch/aarch64/Mailbox.h>
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 a13ee09f0d..66e88964b8 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