Переглянути джерело

Kernel/aarch64: Support reading the command line via the RPi Mailbox

This reuses the existing `RPi::Mailbox` interface to read the command
line via a VideoCore-specific mailbox message. This will have to be
replaced if that interface starts being smarter, as this is needed very
early, and nothing guarantees that a smarter Mailbox interface wouldn't
need to allocate or log, which is a no-no during early boot.

As the response string can be arbitrarily long, it's the caller's job to
provide a long enough buffer for `Mailbox::query_kernel_command_line`.
This commit chose 512 bytes, as it provides a large enough headroom over
the 150-200 characters implicitly added by the VC firmware.

The portable way would be to parse the `/chosen/bootargs` property of
the device tree, but we currently lack the scaffolding for doing that.

Support for this in QEMU relies on a patch that has not yet been
accepted upstream, but is available via our `Toolchain/BuildQEMU.sh`
script. It should, however, work on bare metal.

Tested-By: Timon Kruiper <timonkruiper@gmail.com>
Daniel Bertalan 2 роки тому
батько
коміт
81dd29f713

+ 36 - 0
Kernel/Arch/aarch64/RPi/Mailbox.cpp

@@ -130,4 +130,40 @@ u32 Mailbox::query_firmware_version()
     return message_queue.query_firmware_version.version;
 }
 
+// https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface#get-command-line
+//
+// Note: This function is called very early in the boot process, before the heap or the console
+//       is initialized. Please ensure that it does the minimum amount of work possible.
+StringView Mailbox::query_kernel_command_line(Bytes buffer)
+{
+    // We want to use the user-provided buffer rather than a fixed-size one on the stack,
+    // so we need to construct the message manually.
+    auto aligned_buffer = buffer.align_to(16);
+    if (aligned_buffer.size() < 24)
+        return ""sv;
+
+    auto max_response_length = aligned_buffer.size() - 24;
+
+    auto* message = reinterpret_cast<u32*>(aligned_buffer.data());
+    message[0] = aligned_buffer.size();
+    message[1] = MBOX_REQUEST;
+
+    message[2] = 0x0005'0001; // Query command line
+    message[3] = max_response_length;
+    message[4] = max_response_length;
+
+    message[aligned_buffer.size() / sizeof(u32) - 1] = 0;
+
+    if (!the().send_queue(message, aligned_buffer.size()))
+        return ""sv;
+
+    // Bit 31 indicates that this is a response, the rest denote the length.
+    auto response_length = message[4] & 0x7fff'ffff;
+
+    if (response_length > max_response_length)
+        return ""sv; // The buffer was too small to hold the response.
+
+    return StringView { (char const*)&message[5], response_length };
+}
+
 }

+ 4 - 0
Kernel/Arch/aarch64/RPi/Mailbox.h

@@ -6,6 +6,7 @@
 
 #pragma once
 
+#include <AK/StringView.h>
 #include <AK/Types.h>
 
 namespace Kernel::RPi {
@@ -51,6 +52,9 @@ public:
     bool send_queue(void* queue, u32 queue_size) const;
 
     u32 query_firmware_version();
+
+    // Returns the kernel command line as a StringView into the given buffer.
+    StringView query_kernel_command_line(Bytes buffer);
 };
 
 }

+ 6 - 1
Kernel/Arch/init.cpp

@@ -149,6 +149,10 @@ READONLY_AFTER_INIT u8 multiboot_framebuffer_type;
 
 Atomic<Graphics::Console*> g_boot_console;
 
+#if ARCH(AARCH64)
+READONLY_AFTER_INIT static u8 s_command_line_buffer[512];
+#endif
+
 extern "C" [[noreturn]] UNMAP_AFTER_INIT void init([[maybe_unused]] BootInfo const& boot_info)
 {
     g_in_early_boot = true;
@@ -193,7 +197,8 @@ extern "C" [[noreturn]] UNMAP_AFTER_INIT void init([[maybe_unused]] BootInfo con
     multiboot_module_entry_t modules[] = {};
     multiboot_modules = modules;
     multiboot_modules_count = 0;
-    kernel_cmdline = ""sv;
+    // FIXME: Read the /chosen/bootargs property.
+    kernel_cmdline = RPi::Mailbox::the().query_kernel_command_line(s_command_line_buffer);
 #endif
 
     setup_serial_debug();