From d94c7fa417f4fff749ffea5f6fbaa30a145fac88 Mon Sep 17 00:00:00 2001 From: James Mintram Date: Sun, 3 Apr 2022 21:42:10 +0100 Subject: Kernel: Improve the aarch64 kernel source files disk layout --- Kernel/Arch/aarch64/Aarch64_asm_utils.S | 48 ----- Kernel/Arch/aarch64/Aarch64_asm_utils.h | 14 -- Kernel/Arch/aarch64/Framebuffer.cpp | 114 ------------ Kernel/Arch/aarch64/Framebuffer.h | 43 ----- Kernel/Arch/aarch64/FramebufferMailboxMessages.h | 113 ------------ Kernel/Arch/aarch64/GPIO.cpp | 98 ---------- Kernel/Arch/aarch64/GPIO.h | 60 ------- Kernel/Arch/aarch64/MMIO.cpp | 26 --- Kernel/Arch/aarch64/MMIO.h | 37 ---- Kernel/Arch/aarch64/Mailbox.cpp | 107 ----------- Kernel/Arch/aarch64/Mailbox.h | 54 ------ Kernel/Arch/aarch64/Prekernel.h | 18 -- Kernel/Arch/aarch64/Prekernel/Aarch64_asm_utils.S | 48 +++++ Kernel/Arch/aarch64/Prekernel/Aarch64_asm_utils.h | 14 ++ Kernel/Arch/aarch64/Prekernel/Prekernel.h | 18 ++ Kernel/Arch/aarch64/Prekernel/PrekernelCommon.cpp | 32 ++++ .../Arch/aarch64/Prekernel/PrekernelExceptions.cpp | 101 +++++++++++ Kernel/Arch/aarch64/Prekernel/PrekernelMMU.cpp | 198 +++++++++++++++++++++ Kernel/Arch/aarch64/Prekernel/boot.S | 33 ++++ Kernel/Arch/aarch64/PrekernelCommon.cpp | 32 ---- Kernel/Arch/aarch64/PrekernelExceptions.cpp | 101 ----------- Kernel/Arch/aarch64/PrekernelMMU.cpp | 198 --------------------- Kernel/Arch/aarch64/RPi/Framebuffer.cpp | 114 ++++++++++++ Kernel/Arch/aarch64/RPi/Framebuffer.h | 43 +++++ .../Arch/aarch64/RPi/FramebufferMailboxMessages.h | 113 ++++++++++++ Kernel/Arch/aarch64/RPi/GPIO.cpp | 98 ++++++++++ Kernel/Arch/aarch64/RPi/GPIO.h | 60 +++++++ Kernel/Arch/aarch64/RPi/MMIO.cpp | 26 +++ Kernel/Arch/aarch64/RPi/MMIO.h | 37 ++++ Kernel/Arch/aarch64/RPi/Mailbox.cpp | 107 +++++++++++ Kernel/Arch/aarch64/RPi/Mailbox.h | 54 ++++++ Kernel/Arch/aarch64/RPi/Timer.cpp | 88 +++++++++ Kernel/Arch/aarch64/RPi/Timer.h | 46 +++++ Kernel/Arch/aarch64/RPi/UART.cpp | 162 +++++++++++++++++ Kernel/Arch/aarch64/RPi/UART.h | 69 +++++++ Kernel/Arch/aarch64/Timer.cpp | 88 --------- Kernel/Arch/aarch64/Timer.h | 46 ----- Kernel/Arch/aarch64/UART.cpp | 162 ----------------- Kernel/Arch/aarch64/UART.h | 69 ------- Kernel/Arch/aarch64/boot.S | 33 ---- Kernel/CMakeLists.txt | 35 ++-- 41 files changed, 1484 insertions(+), 1473 deletions(-) delete mode 100644 Kernel/Arch/aarch64/Aarch64_asm_utils.S delete mode 100644 Kernel/Arch/aarch64/Aarch64_asm_utils.h delete mode 100644 Kernel/Arch/aarch64/Framebuffer.cpp delete mode 100644 Kernel/Arch/aarch64/Framebuffer.h delete mode 100644 Kernel/Arch/aarch64/FramebufferMailboxMessages.h delete mode 100644 Kernel/Arch/aarch64/GPIO.cpp delete mode 100644 Kernel/Arch/aarch64/GPIO.h delete mode 100644 Kernel/Arch/aarch64/MMIO.cpp delete mode 100644 Kernel/Arch/aarch64/MMIO.h delete mode 100644 Kernel/Arch/aarch64/Mailbox.cpp delete mode 100644 Kernel/Arch/aarch64/Mailbox.h delete mode 100644 Kernel/Arch/aarch64/Prekernel.h create mode 100644 Kernel/Arch/aarch64/Prekernel/Aarch64_asm_utils.S create mode 100644 Kernel/Arch/aarch64/Prekernel/Aarch64_asm_utils.h create mode 100644 Kernel/Arch/aarch64/Prekernel/Prekernel.h create mode 100644 Kernel/Arch/aarch64/Prekernel/PrekernelCommon.cpp create mode 100644 Kernel/Arch/aarch64/Prekernel/PrekernelExceptions.cpp create mode 100644 Kernel/Arch/aarch64/Prekernel/PrekernelMMU.cpp create mode 100644 Kernel/Arch/aarch64/Prekernel/boot.S delete mode 100644 Kernel/Arch/aarch64/PrekernelCommon.cpp delete mode 100644 Kernel/Arch/aarch64/PrekernelExceptions.cpp delete mode 100644 Kernel/Arch/aarch64/PrekernelMMU.cpp create mode 100644 Kernel/Arch/aarch64/RPi/Framebuffer.cpp create mode 100644 Kernel/Arch/aarch64/RPi/Framebuffer.h create mode 100644 Kernel/Arch/aarch64/RPi/FramebufferMailboxMessages.h create mode 100644 Kernel/Arch/aarch64/RPi/GPIO.cpp create mode 100644 Kernel/Arch/aarch64/RPi/GPIO.h create mode 100644 Kernel/Arch/aarch64/RPi/MMIO.cpp create mode 100644 Kernel/Arch/aarch64/RPi/MMIO.h create mode 100644 Kernel/Arch/aarch64/RPi/Mailbox.cpp create mode 100644 Kernel/Arch/aarch64/RPi/Mailbox.h create mode 100644 Kernel/Arch/aarch64/RPi/Timer.cpp create mode 100644 Kernel/Arch/aarch64/RPi/Timer.h create mode 100644 Kernel/Arch/aarch64/RPi/UART.cpp create mode 100644 Kernel/Arch/aarch64/RPi/UART.h delete mode 100644 Kernel/Arch/aarch64/Timer.cpp delete mode 100644 Kernel/Arch/aarch64/Timer.h delete mode 100644 Kernel/Arch/aarch64/UART.cpp delete mode 100644 Kernel/Arch/aarch64/UART.h delete mode 100644 Kernel/Arch/aarch64/boot.S diff --git a/Kernel/Arch/aarch64/Aarch64_asm_utils.S b/Kernel/Arch/aarch64/Aarch64_asm_utils.S deleted file mode 100644 index 33f2d4de7f..0000000000 --- a/Kernel/Arch/aarch64/Aarch64_asm_utils.S +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * Copyright (c) 2021, Marcin Undak - * Copyright (c) 2021, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -.global wait_cycles -.type wait_cycles, @function -wait_cycles: -Lstart: - // This is probably too fast when caching and branch prediction is turned on. - // FIXME: Make timer-based. - subs x0, x0, #1 - bne Lstart - ret - -.global enter_el2_from_el3 -.type enter_el2_from_el3, @function -enter_el2_from_el3: - adr x0, entered_el2 - msr elr_el3, x0 - eret -entered_el2: - ret - -.global enter_el1_from_el2 -.type enter_el1_from_el2, @function -enter_el1_from_el2: - adr x0, entered_el1 - msr elr_el2, x0 - eret -entered_el1: - ret - -// -// Installs the EL1 vector table -// Args: -// x0 - Address of vector table -// -// This function doesn't return a value -// -.global el1_vector_table_install -.type el1_vector_table_install, @function -el1_vector_table_install: - msr VBAR_EL1, x0 - ret diff --git a/Kernel/Arch/aarch64/Aarch64_asm_utils.h b/Kernel/Arch/aarch64/Aarch64_asm_utils.h deleted file mode 100644 index 5536904b2b..0000000000 --- a/Kernel/Arch/aarch64/Aarch64_asm_utils.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2021, Marcin Undak - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -extern "C" void wait_cycles(int n); -extern "C" void el1_vector_table_install(void* vector_table); - -// CPU initialization functions -extern "C" [[noreturn]] void return_from_el2(); -extern "C" [[noreturn]] void return_from_el3(); diff --git a/Kernel/Arch/aarch64/Framebuffer.cpp b/Kernel/Arch/aarch64/Framebuffer.cpp deleted file mode 100644 index fb99de9adf..0000000000 --- a/Kernel/Arch/aarch64/Framebuffer.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2021, Marcin Undak - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Prekernel { - -Framebuffer::Framebuffer() -{ - // FIXME: query HDMI for best mode - // https://github.com/raspberrypi/userland/blob/master/host_applications/linux/apps/tvservice/tvservice.c - m_width = 1280; - m_height = 720; - m_depth = 32; - m_initialized = false; - - struct __attribute__((aligned(16))) { - Mailbox::MessageHeader header; - FramebufferSetPhysicalSizeMboxMessage set_physical_size; - FramebufferSetVirtualSizeMboxMessage set_virtual_size; - FramebufferSetVirtualOffsetMboxMessage set_virtual_offset; - FramebufferSetDepthMboxMessage set_depth; - FramebufferSetPixelOrderMboxMessage set_pixel_order; - FramebufferAllocateBufferMboxMessage allocate_buffer; - FramebufferGetPithMboxMessage get_pitch; - Mailbox::MessageTail tail; - } message_queue; - - message_queue.header.set_queue_size(sizeof(message_queue)); - message_queue.set_physical_size.width = m_width; - message_queue.set_physical_size.height = m_height; - message_queue.set_virtual_size.width = m_width; - message_queue.set_virtual_size.height = m_height; - - // FIXME! those next 2 lines crash... - // message_queue.set_virtual_offset.x = 0; - // message_queue.set_virtual_offset.y = 0; - - message_queue.set_depth.depth_bits = 32; - message_queue.set_pixel_order.pixel_order = FramebufferSetPixelOrderMboxMessage::PixelOrder::RGB; - message_queue.allocate_buffer.alignment = 4096; - - if (!Mailbox::the().send_queue(&message_queue, sizeof(message_queue))) { - warnln("Framebuffer(): Mailbox send failed."); - return; - } - - // Now message queue contains responses. Process them. - - if (message_queue.set_physical_size.width != m_width || message_queue.set_physical_size.height != m_height) { - warnln("Framebuffer(): Setting physical dimension failed."); - return; - } - - if (message_queue.set_virtual_size.width != m_width || message_queue.set_virtual_size.height != m_height) { - warnln("Framebuffer(): Setting virtual dimension failed."); - return; - } - - if (message_queue.set_virtual_offset.x != 0 || message_queue.set_virtual_offset.y != 0) { - warnln("Framebuffer(): Setting virtual offset failed."); - return; - } - - if (message_queue.set_depth.depth_bits != m_depth) { - warnln("Framebuffer(): Setting depth failed."); - return; - } - - if (message_queue.allocate_buffer.size == 0 || message_queue.allocate_buffer.address == 0) { - warnln("Framebuffer(): Allocating buffer failed."); - return; - } - - if (message_queue.get_pitch.pitch == 0) { - warnln("Framebuffer(): Retrieving pitch failed."); - return; - } - - // Convert GPU address space to RAM - // GPU maps memory from 0x80000000 instead of 0x00000000 - m_gpu_buffer = reinterpret_cast(message_queue.allocate_buffer.address & 0x3FFFFFFF); - - m_buffer_size = message_queue.allocate_buffer.size; - m_pitch = message_queue.get_pitch.pitch; - - switch (message_queue.set_pixel_order.pixel_order) { - case FramebufferSetPixelOrderMboxMessage::PixelOrder::RGB: - m_pixel_order = PixelOrder::RGB; - break; - case FramebufferSetPixelOrderMboxMessage::PixelOrder::BGR: - m_pixel_order = PixelOrder::BGR; - break; - default: - warnln("Framebuffer(): Unsupported pixel order reported by GPU."); - m_pixel_order = PixelOrder::RGB; - break; - } - - dbgln("Initialized framebuffer: 1280 x 720 @ 32 bits"); - m_initialized = true; -} - -Framebuffer& Framebuffer::the() -{ - static Framebuffer instance; - return instance; -} -} diff --git a/Kernel/Arch/aarch64/Framebuffer.h b/Kernel/Arch/aarch64/Framebuffer.h deleted file mode 100644 index 2cde37dfdb..0000000000 --- a/Kernel/Arch/aarch64/Framebuffer.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2021, Marcin Undak - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Prekernel { - -class Framebuffer { -public: - enum class PixelOrder { - RGB, - BGR, - }; - - static Framebuffer& the(); - - bool initialized() const { return m_initialized; } - u16 width() const { return m_width; } - u16 height() const { return m_height; } - u8 depth() const { return m_depth; } - u8* gpu_buffer() const { return m_gpu_buffer; } - u32 buffer_size() const { return m_buffer_size; } - u32 pitch() const { return m_pitch; } - PixelOrder pixel_order() { return m_pixel_order; } - -private: - u16 m_width; - u16 m_height; - u8 m_depth; - u8* m_gpu_buffer; - u32 m_buffer_size; - u32 m_pitch; - bool m_initialized; - PixelOrder m_pixel_order; - - Framebuffer(); -}; -} diff --git a/Kernel/Arch/aarch64/FramebufferMailboxMessages.h b/Kernel/Arch/aarch64/FramebufferMailboxMessages.h deleted file mode 100644 index 7dab801b73..0000000000 --- a/Kernel/Arch/aarch64/FramebufferMailboxMessages.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (c) 2021, Marcin Undak - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Prekernel { - -class FramebufferSetPhysicalSizeMboxMessage : public Mailbox::Message { -public: - u32 width; - u32 height; - - FramebufferSetPhysicalSizeMboxMessage() - : Mailbox::Message(0x48003, 8) - { - width = 0; - height = 0; - } -}; -static_assert(sizeof(FramebufferSetPhysicalSizeMboxMessage) == 20); - -class FramebufferSetVirtualSizeMboxMessage : public Mailbox::Message { -public: - u32 width; - u32 height; - - FramebufferSetVirtualSizeMboxMessage() - : Mailbox::Message(0x48004, 8) - { - width = 0; - height = 0; - } -}; -static_assert(sizeof(FramebufferSetVirtualSizeMboxMessage) == 20); - -class FramebufferSetVirtualOffsetMboxMessage : public Mailbox::Message { -public: - u32 x; - u32 y; - - FramebufferSetVirtualOffsetMboxMessage() - : Mailbox::Message(0x48009, 8) - { - x = 0; - y = 0; - } -}; -static_assert(sizeof(FramebufferSetVirtualOffsetMboxMessage) == 20); - -class FramebufferSetDepthMboxMessage : public Mailbox::Message { -public: - u32 depth_bits; - - FramebufferSetDepthMboxMessage() - : Mailbox::Message(0x48005, 4) - { - depth_bits = 0; - } -}; -static_assert(sizeof(FramebufferSetDepthMboxMessage) == 16); - -class FramebufferSetPixelOrderMboxMessage : public Mailbox::Message { -public: - enum PixelOrder : u32 { - BGR = 0, - RGB = 1 - }; - - PixelOrder pixel_order; - - FramebufferSetPixelOrderMboxMessage() - : Mailbox::Message(0x48006, 4) - { - pixel_order = PixelOrder::BGR; - } -}; -static_assert(sizeof(FramebufferSetPixelOrderMboxMessage) == 16); - -class FramebufferAllocateBufferMboxMessage : public Mailbox::Message { -public: - union { - u32 alignment; - u32 address; - }; - u32 size = 0; - - FramebufferAllocateBufferMboxMessage() - : Mailbox::Message(0x40001, 8) - { - alignment = 0; - size = 0; - } -}; -static_assert(sizeof(FramebufferAllocateBufferMboxMessage) == 20); - -class FramebufferGetPithMboxMessage : public Mailbox::Message { -public: - u32 pitch; - - FramebufferGetPithMboxMessage() - : Mailbox::Message(0x40008, 4) - { - pitch = 0; - } -}; -static_assert(sizeof(FramebufferGetPithMboxMessage) == 16); - -} diff --git a/Kernel/Arch/aarch64/GPIO.cpp b/Kernel/Arch/aarch64/GPIO.cpp deleted file mode 100644 index bffba6bade..0000000000 --- a/Kernel/Arch/aarch64/GPIO.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -extern "C" void wait_cycles(int n); - -namespace Prekernel { - -// See BCM2835-ARM-Peripherals.pdf section "6 General Purpose I/O" or bcm2711-peripherals.pdf "Chapter 5. General Purpose I/O". - -// "6.1 Register View" / "5.2 Register View" - -struct PinData { - u32 bits[2]; - u32 reserved; -}; - -struct GPIOControlRegisters { - u32 function_select[6]; // Every u32 stores a 3-bit function code for 10 pins. - u32 reserved; - PinData output_set; - PinData output_clear; - PinData level; - PinData event_detect_status; - PinData rising_edge_detect_enable; - PinData falling_edge_detect_enable; - PinData high_detect_enable; - PinData low_detect_enable; - PinData async_rising_edge_detect_enable; - PinData async_falling_edge_detect_enable; - u32 pull_up_down_enable; - PinData pull_up_down_enable_clock; - u32 test; -}; - -GPIO::GPIO() - : m_registers(MMIO::the().peripheral(0x20'0000)) -{ -} - -GPIO& GPIO::the() -{ - static GPIO instance; - return instance; -} - -void GPIO::set_pin_function(unsigned pin_number, PinFunction function) -{ - // pin_number must be <= 53. We can't VERIFY() that since this function runs too early to print assertion failures. - - unsigned function_select_index = pin_number / 10; - unsigned function_select_bits_start = (pin_number % 10) * 3; - - u32 function_bits = m_registers->function_select[function_select_index]; - function_bits = (function_bits & ~(0b111 << function_select_bits_start)) | (static_cast(function) << function_select_bits_start); - m_registers->function_select[function_select_index] = function_bits; -} - -void GPIO::internal_enable_pins(u32 enable[2], PullUpDownState state) -{ - // Section "GPIO Pull-up/down Clock Registers (GPPUDCLKn)": - // The GPIO Pull-up/down Clock Registers control the actuation of internal pull-downs on - // the respective GPIO pins. These registers must be used in conjunction with the GPPUD - // register to effect GPIO Pull-up/down changes. The following sequence of events is - // required: - // 1. Write to GPPUD to set the required control signal (i.e. Pull-up or Pull-Down or neither - // to remove the current Pull-up/down) - m_registers->pull_up_down_enable = static_cast(state); - - // 2. Wait 150 cycles – this provides the required set-up time for the control signal - wait_cycles(150); - - // 3. Write to GPPUDCLK0/1 to clock the control signal into the GPIO pads you wish to - // modify – NOTE only the pads which receive a clock will be modified, all others will - // retain their previous state. - m_registers->pull_up_down_enable_clock.bits[0] = enable[0]; - m_registers->pull_up_down_enable_clock.bits[1] = enable[1]; - - // 4. Wait 150 cycles – this provides the required hold time for the control signal - wait_cycles(150); - - // 5. Write to GPPUD to remove the control signal - m_registers->pull_up_down_enable = 0; - - // 6. Write to GPPUDCLK0/1 to remove the clock - m_registers->pull_up_down_enable_clock.bits[0] = 0; - m_registers->pull_up_down_enable_clock.bits[1] = 0; - - // bcm2711-peripherals.pdf documents GPIO_PUP_PDN_CNTRL_REG[4] registers that store 2 bits state per register, similar to function_select. - // I don't know if the RPi3 has that already, so this uses the old BCM2835 approach for now. -} - -} diff --git a/Kernel/Arch/aarch64/GPIO.h b/Kernel/Arch/aarch64/GPIO.h deleted file mode 100644 index 4f38f9c376..0000000000 --- a/Kernel/Arch/aarch64/GPIO.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Prekernel { - -struct GPIOControlRegisters; - -// Can configure the general-purpose I/O registers on a Raspberry Pi. -class GPIO { -public: - enum class PinFunction { - Input = 0, - Output = 1, - Alternate0 = 0b100, - Alternate1 = 0b101, - Alternate2 = 0b110, - Alternate3 = 0b111, - Alternate4 = 0b011, - Alternate5 = 0b010, - }; - - static GPIO& the(); - - void set_pin_function(unsigned pin_number, PinFunction); - - enum class PullUpDownState { - Disable = 0, - PullDown = 1, - PullUp = 2, - }; - - template - void set_pin_pull_up_down_state(Array pins, PullUpDownState state) - { - u32 enable[2] = {}; - for (int pin : pins) { - if (pin < 32) - enable[0] |= (1 << pin); - else - enable[1] |= (1 << (pin - 32)); - } - internal_enable_pins(enable, state); - } - -private: - GPIO(); - void internal_enable_pins(u32 enable[2], PullUpDownState state); - - GPIOControlRegisters volatile* m_registers; -}; - -} diff --git a/Kernel/Arch/aarch64/MMIO.cpp b/Kernel/Arch/aarch64/MMIO.cpp deleted file mode 100644 index a6b270a17d..0000000000 --- a/Kernel/Arch/aarch64/MMIO.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Prekernel { - -MMIO::MMIO() - : m_base_address(0xFE00'0000) -{ - MainIdRegister id; - if (id.part_num() <= MainIdRegister::RaspberryPi3) - m_base_address = 0x3F00'0000; -} - -MMIO& MMIO::the() -{ - static MMIO instance; - return instance; -} - -} diff --git a/Kernel/Arch/aarch64/MMIO.h b/Kernel/Arch/aarch64/MMIO.h deleted file mode 100644 index 895319d869..0000000000 --- a/Kernel/Arch/aarch64/MMIO.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Prekernel { - -// Knows about memory-mapped IO addresses on the Broadcom family of SOCs used in Raspberry Pis. -// RPi3 is the first Raspberry Pi that supports aarch64. -// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf (RPi3) -// https://datasheets.raspberrypi.org/bcm2711/bcm2711-peripherals.pdf (RPi4 Model B) -class MMIO { -public: - static MMIO& the(); - - u32 read(FlatPtr offset) { return *peripheral_address(offset); } - void write(FlatPtr offset, u32 value) { *peripheral_address(offset) = value; } - - u32 volatile* peripheral_address(FlatPtr offset) { return (u32 volatile*)(m_base_address + offset); } - template - T volatile* peripheral(FlatPtr offset) { return (T volatile*)peripheral_address(offset); } - - FlatPtr peripheral_base_address() const { return m_base_address; } - FlatPtr peripheral_end_address() const { return m_base_address + 0x00FFFFFF; } - -private: - MMIO(); - - unsigned int m_base_address; -}; - -} diff --git a/Kernel/Arch/aarch64/Mailbox.cpp b/Kernel/Arch/aarch64/Mailbox.cpp deleted file mode 100644 index d36a5305b4..0000000000 --- a/Kernel/Arch/aarch64/Mailbox.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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; - -Mailbox::Message::Message(u32 tag, u32 arguments_size) -{ - m_tag = tag; - m_arguments_size = arguments_size; - m_command_tag = MBOX_REQUEST; -} - -Mailbox::MessageHeader::MessageHeader() -{ - m_message_queue_size = 0; - m_command_tag = MBOX_REQUEST; -} - -bool Mailbox::MessageHeader::success() const -{ - return m_command_tag == MBOX_RESPONSE_SUCCESS; -} - -Mailbox& Mailbox::the() -{ - static Mailbox instance; - return instance; -} - -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::send_queue(void* queue, u32 queue_size) const -{ - // According to Raspberry Pi specs this is the only channel implemented. - const u32 channel = ARM_TO_VIDEOCORE_CHANNEL; - - auto message_header = reinterpret_cast(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); - - // The mailbox message is 32-bit based, so this assumes that message is in the first 4 GiB. - u32 request = static_cast(reinterpret_cast(queue) & ~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_header->success(); - } - - return true; -} - -} diff --git a/Kernel/Arch/aarch64/Mailbox.h b/Kernel/Arch/aarch64/Mailbox.h deleted file mode 100644 index f54eea7c99..0000000000 --- a/Kernel/Arch/aarch64/Mailbox.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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: - // Base class for Mailbox messages. Implemented in subsystems that use Mailbox. - class Message { - protected: - Message(u32 tag, u32 arguments_size); - - private: - u32 m_tag; - u32 m_arguments_size; - u32 m_command_tag; - }; - - // Must be at the beginning of every command message queue - class MessageHeader { - public: - MessageHeader(); - - u32 queue_size() { return m_message_queue_size; } - void set_queue_size(u32 size) { m_message_queue_size = size; } - bool success() const; - - private: - u32 m_message_queue_size; - u32 m_command_tag; - }; - - // Must be at the end of every command message queue - class MessageTail { - private: - u32 m_empty_tag = 0; - }; - - static Mailbox& the(); - - // Sends message queue to VideoCore - bool send_queue(void* queue, u32 queue_size) const; -}; - -} diff --git a/Kernel/Arch/aarch64/Prekernel.h b/Kernel/Arch/aarch64/Prekernel.h deleted file mode 100644 index 1012ad4e3b..0000000000 --- a/Kernel/Arch/aarch64/Prekernel.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2021, James Mintram - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -namespace Prekernel { - -void drop_to_exception_level_1(); -void init_prekernel_page_tables(); - -[[noreturn]] void panic(char const* msg); - -[[noreturn]] void halt(); - -} diff --git a/Kernel/Arch/aarch64/Prekernel/Aarch64_asm_utils.S b/Kernel/Arch/aarch64/Prekernel/Aarch64_asm_utils.S new file mode 100644 index 0000000000..33f2d4de7f --- /dev/null +++ b/Kernel/Arch/aarch64/Prekernel/Aarch64_asm_utils.S @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021, Nico Weber + * Copyright (c) 2021, Marcin Undak + * Copyright (c) 2021, Jesse Buhagiar + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +.global wait_cycles +.type wait_cycles, @function +wait_cycles: +Lstart: + // This is probably too fast when caching and branch prediction is turned on. + // FIXME: Make timer-based. + subs x0, x0, #1 + bne Lstart + ret + +.global enter_el2_from_el3 +.type enter_el2_from_el3, @function +enter_el2_from_el3: + adr x0, entered_el2 + msr elr_el3, x0 + eret +entered_el2: + ret + +.global enter_el1_from_el2 +.type enter_el1_from_el2, @function +enter_el1_from_el2: + adr x0, entered_el1 + msr elr_el2, x0 + eret +entered_el1: + ret + +// +// Installs the EL1 vector table +// Args: +// x0 - Address of vector table +// +// This function doesn't return a value +// +.global el1_vector_table_install +.type el1_vector_table_install, @function +el1_vector_table_install: + msr VBAR_EL1, x0 + ret diff --git a/Kernel/Arch/aarch64/Prekernel/Aarch64_asm_utils.h b/Kernel/Arch/aarch64/Prekernel/Aarch64_asm_utils.h new file mode 100644 index 0000000000..5536904b2b --- /dev/null +++ b/Kernel/Arch/aarch64/Prekernel/Aarch64_asm_utils.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2021, Marcin Undak + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +extern "C" void wait_cycles(int n); +extern "C" void el1_vector_table_install(void* vector_table); + +// CPU initialization functions +extern "C" [[noreturn]] void return_from_el2(); +extern "C" [[noreturn]] void return_from_el3(); diff --git a/Kernel/Arch/aarch64/Prekernel/Prekernel.h b/Kernel/Arch/aarch64/Prekernel/Prekernel.h new file mode 100644 index 0000000000..1012ad4e3b --- /dev/null +++ b/Kernel/Arch/aarch64/Prekernel/Prekernel.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2021, James Mintram + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +namespace Prekernel { + +void drop_to_exception_level_1(); +void init_prekernel_page_tables(); + +[[noreturn]] void panic(char const* msg); + +[[noreturn]] void halt(); + +} diff --git a/Kernel/Arch/aarch64/Prekernel/PrekernelCommon.cpp b/Kernel/Arch/aarch64/Prekernel/PrekernelCommon.cpp new file mode 100644 index 0000000000..5f7310daa3 --- /dev/null +++ b/Kernel/Arch/aarch64/Prekernel/PrekernelCommon.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021, James Mintram + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +#include +#include + +namespace Prekernel { + +[[noreturn]] void panic(char const* msg) +{ + auto& uart = Prekernel::UART::the(); + + if (msg) { + uart.print_str(msg); + } + + Prekernel::halt(); +} + +[[noreturn]] void halt() +{ + for (;;) { + asm volatile("wfi"); + } +} + +} diff --git a/Kernel/Arch/aarch64/Prekernel/PrekernelExceptions.cpp b/Kernel/Arch/aarch64/Prekernel/PrekernelExceptions.cpp new file mode 100644 index 0000000000..9feb7fb821 --- /dev/null +++ b/Kernel/Arch/aarch64/Prekernel/PrekernelExceptions.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2021, James Mintram + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +extern "C" void enter_el2_from_el3(); +extern "C" void enter_el1_from_el2(); + +using namespace Kernel; + +namespace Prekernel { + +static void drop_to_el2() +{ + Aarch64::SCR_EL3 secure_configuration_register_el3 = {}; + + secure_configuration_register_el3.ST = 1; // Don't trap access to Counter-timer Physical Secure registers + secure_configuration_register_el3.RW = 1; // Lower level to use Aarch64 + secure_configuration_register_el3.NS = 1; // Non-secure state + secure_configuration_register_el3.HCE = 1; // Enable Hypervisor instructions at all levels + + Aarch64::SCR_EL3::write(secure_configuration_register_el3); + + Aarch64::SPSR_EL3 saved_program_status_register_el3 = {}; + + // Mask (disable) all interrupts + saved_program_status_register_el3.A = 1; + saved_program_status_register_el3.I = 1; + saved_program_status_register_el3.F = 1; + saved_program_status_register_el3.D = 1; + + // Indicate EL1 as exception origin mode (so we go back there) + saved_program_status_register_el3.M = Aarch64::SPSR_EL3::Mode::EL2t; + + // Set the register + Aarch64::SPSR_EL3::write(saved_program_status_register_el3); + + // This will jump into os_start() below + enter_el2_from_el3(); +} +static void drop_to_el1() +{ + Aarch64::HCR_EL2 hypervisor_configuration_register_el2 = {}; + hypervisor_configuration_register_el2.RW = 1; // EL1 to use 64-bit mode + Aarch64::HCR_EL2::write(hypervisor_configuration_register_el2); + + Aarch64::SPSR_EL2 saved_program_status_register_el2 = {}; + + // Mask (disable) all interrupts + saved_program_status_register_el2.A = 1; + saved_program_status_register_el2.I = 1; + saved_program_status_register_el2.F = 1; + + // Indicate EL1 as exception origin mode (so we go back there) + saved_program_status_register_el2.M = Aarch64::SPSR_EL2::Mode::EL1t; + + Aarch64::SPSR_EL2::write(saved_program_status_register_el2); + enter_el1_from_el2(); +} + +static void set_up_el1() +{ + Aarch64::SCTLR_EL1 system_control_register_el1 = Aarch64::SCTLR_EL1::reset_value(); + + system_control_register_el1.UCT = 1; // Don't trap access to CTR_EL0 + system_control_register_el1.nTWE = 1; // Don't trap WFE instructions + system_control_register_el1.nTWI = 1; // Don't trap WFI instructions + system_control_register_el1.DZE = 1; // Don't trap DC ZVA instructions + system_control_register_el1.UMA = 1; // Don't trap access to DAIF (debugging) flags of EFLAGS register + system_control_register_el1.SA0 = 1; // Enable stack access alignment check for EL0 + system_control_register_el1.SA = 1; // Enable stack access alignment check for EL1 + system_control_register_el1.A = 1; // Enable memory access alignment check + + Aarch64::SCTLR_EL1::write(system_control_register_el1); +} + +void drop_to_exception_level_1() +{ + switch (Kernel::Aarch64::Asm::get_current_exception_level()) { + case Kernel::Aarch64::Asm::ExceptionLevel::EL3: + drop_to_el2(); + [[fallthrough]]; + case Kernel::Aarch64::Asm::ExceptionLevel::EL2: + drop_to_el1(); + [[fallthrough]]; + case Kernel::Aarch64::Asm::ExceptionLevel::EL1: + set_up_el1(); + break; + default: { + Prekernel::panic("FATAL: CPU booted in unsupported exception mode!\r\n"); + } + } +} + +} diff --git a/Kernel/Arch/aarch64/Prekernel/PrekernelMMU.cpp b/Kernel/Arch/aarch64/Prekernel/PrekernelMMU.cpp new file mode 100644 index 0000000000..31ec573299 --- /dev/null +++ b/Kernel/Arch/aarch64/Prekernel/PrekernelMMU.cpp @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2021, James Mintram + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +#include + +#include +#include +#include +#include + +// Documentation here for Aarch64 Address Translations +// https://documentation-service.arm.com/static/5efa1d23dbdee951c1ccdec5?token= + +using namespace Kernel; + +// These come from the linker script +extern u8 page_tables_phys_start[]; +extern u8 page_tables_phys_end[]; + +namespace Prekernel { + +// physical memory +constexpr u32 START_OF_NORMAL_MEMORY = 0x00000000; +constexpr u32 END_OF_NORMAL_MEMORY = 0x3EFFFFFF; + +// 4KiB page size was chosen for the prekernel to make this code slightly simpler +constexpr u32 GRANULE_SIZE = 0x1000; +constexpr u32 PAGE_TABLE_SIZE = 0x1000; + +// Documentation for translation table format +// https://developer.arm.com/documentation/101811/0101/Controlling-address-translation +constexpr u32 PAGE_DESCRIPTOR = 0b11; +constexpr u32 TABLE_DESCRIPTOR = 0b11; +constexpr u32 DESCRIPTOR_MASK = ~0b11; + +constexpr u32 ACCESS_FLAG = 1 << 10; + +// shareability +constexpr u32 OUTER_SHAREABLE = (2 << 8); +constexpr u32 INNER_SHAREABLE = (3 << 8); + +// these index into the MAIR attribute table +constexpr u32 NORMAL_MEMORY = (0 << 2); +constexpr u32 DEVICE_MEMORY = (1 << 2); + +ALWAYS_INLINE static u64* descriptor_to_pointer(FlatPtr descriptor) +{ + return (u64*)(descriptor & DESCRIPTOR_MASK); +} + +namespace { +class PageBumpAllocator { +public: + PageBumpAllocator(u64* start, u64* end) + : m_start(start) + , m_end(end) + , m_current(start) + { + if (m_start >= m_end) { + Prekernel::panic("Invalid memory range passed to PageBumpAllocator"); + } + if ((FlatPtr)m_start % PAGE_TABLE_SIZE != 0 || (FlatPtr)m_end % PAGE_TABLE_SIZE != 0) { + Prekernel::panic("Memory range passed into PageBumpAllocator not aligned to PAGE_TABLE_SIZE"); + } + } + + u64* take_page() + { + if (m_current == m_end) { + Prekernel::panic("Prekernel pagetable memory exhausted"); + } + + u64* page = m_current; + m_current += (PAGE_TABLE_SIZE / sizeof(FlatPtr)); + + zero_page(page); + return page; + } + +private: + void zero_page(u64* page) + { + // Memset all page table memory to zero + for (u64* p = page; p < page + (PAGE_TABLE_SIZE / sizeof(u64)); p++) { + *p = 0; + } + } + + u64 const* m_start; + u64 const* m_end; + u64* m_current; +}; +} + +static void insert_identity_entries_for_physical_memory_range(PageBumpAllocator& allocator, u64* page_table, FlatPtr start, FlatPtr end, u64 flags) +{ + // Not very efficient, but simple and it works. + for (FlatPtr addr = start; addr < end; addr += GRANULE_SIZE) { + // Each level has 9 bits (512 entries) + u64 level0_idx = (addr >> 39) & 0x1FF; + u64 level1_idx = (addr >> 30) & 0x1FF; + u64 level2_idx = (addr >> 21) & 0x1FF; + u64 level3_idx = (addr >> 12) & 0x1FF; + + u64* level1_table = page_table; + + if (level1_table[level0_idx] == 0) { + level1_table[level0_idx] = (FlatPtr)allocator.take_page(); + level1_table[level0_idx] |= TABLE_DESCRIPTOR; + } + + u64* level2_table = descriptor_to_pointer(level1_table[level0_idx]); + + if (level2_table[level1_idx] == 0) { + level2_table[level1_idx] = (FlatPtr)allocator.take_page(); + level2_table[level1_idx] |= TABLE_DESCRIPTOR; + } + + u64* level3_table = descriptor_to_pointer(level2_table[level1_idx]); + + if (level3_table[level2_idx] == 0) { + level3_table[level2_idx] = (FlatPtr)allocator.take_page(); + level3_table[level2_idx] |= TABLE_DESCRIPTOR; + } + + u64* level4_table = descriptor_to_pointer(level3_table[level2_idx]); + u64* l4_entry = &level4_table[level3_idx]; + *l4_entry = addr; + *l4_entry |= flags; + } +} + +static void build_identity_map(PageBumpAllocator& allocator) +{ + u64* level1_table = allocator.take_page(); + + u64 normal_memory_flags = ACCESS_FLAG | PAGE_DESCRIPTOR | INNER_SHAREABLE | NORMAL_MEMORY; + u64 device_memory_flags = ACCESS_FLAG | PAGE_DESCRIPTOR | OUTER_SHAREABLE | DEVICE_MEMORY; + + insert_identity_entries_for_physical_memory_range(allocator, level1_table, START_OF_NORMAL_MEMORY, END_OF_NORMAL_MEMORY, normal_memory_flags); + insert_identity_entries_for_physical_memory_range(allocator, level1_table, MMIO::the().peripheral_base_address(), MMIO::the().peripheral_end_address(), device_memory_flags); +} + +static void switch_to_page_table(u8* page_table) +{ + Aarch64::Asm::set_ttbr0_el1((FlatPtr)page_table); + Aarch64::Asm::set_ttbr1_el1((FlatPtr)page_table); +} + +static void activate_mmu() +{ + Aarch64::MAIR_EL1 mair_el1 = {}; + mair_el1.Attr[0] = 0xFF; // Normal memory + mair_el1.Attr[1] = 0b00000100; // Device-nGnRE memory (non-cacheble) + Aarch64::MAIR_EL1::write(mair_el1); + + // Configure cacheability attributes for memory associated with translation table walks + Aarch64::TCR_EL1 tcr_el1 = {}; + + tcr_el1.SH1 = Aarch64::TCR_EL1::InnerShareable; + tcr_el1.ORGN1 = Aarch64::TCR_EL1::NormalMemory_Outer_WriteBack_ReadAllocate_WriteAllocateCacheable; + tcr_el1.IRGN1 = Aarch64::TCR_EL1::NormalMemory_Inner_WriteBack_ReadAllocate_WriteAllocateCacheable; + + tcr_el1.SH0 = Aarch64::TCR_EL1::InnerShareable; + tcr_el1.ORGN0 = Aarch64::TCR_EL1::NormalMemory_Outer_WriteBack_ReadAllocate_WriteAllocateCacheable; + tcr_el1.IRGN0 = Aarch64::TCR_EL1::NormalMemory_Inner_WriteBack_ReadAllocate_WriteAllocateCacheable; + + tcr_el1.TG1 = Aarch64::TCR_EL1::TG1GranuleSize::Size_4KB; + tcr_el1.TG0 = Aarch64::TCR_EL1::TG0GranuleSize::Size_4KB; + + // Auto detect the Intermediate Physical Address Size + Aarch64::ID_AA64MMFR0_EL1 feature_register = Aarch64::ID_AA64MMFR0_EL1::read(); + tcr_el1.IPS = feature_register.PARange; + + Aarch64::TCR_EL1::write(tcr_el1); + + // Enable MMU in the system control register + Aarch64::SCTLR_EL1 sctlr_el1 = Aarch64::SCTLR_EL1::read(); + sctlr_el1.M = 1; // Enable MMU + Aarch64::SCTLR_EL1::write(sctlr_el1); + + Aarch64::Asm::flush(); +} + +void init_prekernel_page_tables() +{ + PageBumpAllocator allocator((u64*)page_tables_phys_start, (u64*)page_tables_phys_end); + build_identity_map(allocator); + switch_to_page_table(page_tables_phys_start); + activate_mmu(); +} + +} diff --git a/Kernel/Arch/aarch64/Prekernel/boot.S b/Kernel/Arch/aarch64/Prekernel/boot.S new file mode 100644 index 0000000000..4c44b5288a --- /dev/null +++ b/Kernel/Arch/aarch64/Prekernel/boot.S @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021, Nico Weber + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +// In a specially-named text section so that the linker script can put it first in .text. +.section ".text.first" + +.global start +.type start, @function +start: + // Let only core 0 continue, put other cores to sleep. + mrs x13, MPIDR_EL1 + and x13, x13, 0xff + cbnz x13, _ZN9Prekernel4haltEv + + // Let stack start before .text for now. + // 512 kiB (0x80000) of stack are probably not sufficient, especially once we give the other cores some stack too, + // but for now it's ok. + msr SPSel, #0 //Use the same SP as we descend into EL1 + ldr x14, =start + mov sp, x14 + + // Clear BSS. + ldr x14, =start_of_bss + ldr x15, =size_of_bss_divided_by_8 +Lbss_clear_loop: + str xzr, [x14], #8 + subs x15, x15, #1 + bne Lbss_clear_loop + + b init diff --git a/Kernel/Arch/aarch64/PrekernelCommon.cpp b/Kernel/Arch/aarch64/PrekernelCommon.cpp deleted file mode 100644 index 5f7310daa3..0000000000 --- a/Kernel/Arch/aarch64/PrekernelCommon.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2021, James Mintram - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -#include -#include - -namespace Prekernel { - -[[noreturn]] void panic(char const* msg) -{ - auto& uart = Prekernel::UART::the(); - - if (msg) { - uart.print_str(msg); - } - - Prekernel::halt(); -} - -[[noreturn]] void halt() -{ - for (;;) { - asm volatile("wfi"); - } -} - -} diff --git a/Kernel/Arch/aarch64/PrekernelExceptions.cpp b/Kernel/Arch/aarch64/PrekernelExceptions.cpp deleted file mode 100644 index 9feb7fb821..0000000000 --- a/Kernel/Arch/aarch64/PrekernelExceptions.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2021, James Mintram - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -extern "C" void enter_el2_from_el3(); -extern "C" void enter_el1_from_el2(); - -using namespace Kernel; - -namespace Prekernel { - -static void drop_to_el2() -{ - Aarch64::SCR_EL3 secure_configuration_register_el3 = {}; - - secure_configuration_register_el3.ST = 1; // Don't trap access to Counter-timer Physical Secure registers - secure_configuration_register_el3.RW = 1; // Lower level to use Aarch64 - secure_configuration_register_el3.NS = 1; // Non-secure state - secure_configuration_register_el3.HCE = 1; // Enable Hypervisor instructions at all levels - - Aarch64::SCR_EL3::write(secure_configuration_register_el3); - - Aarch64::SPSR_EL3 saved_program_status_register_el3 = {}; - - // Mask (disable) all interrupts - saved_program_status_register_el3.A = 1; - saved_program_status_register_el3.I = 1; - saved_program_status_register_el3.F = 1; - saved_program_status_register_el3.D = 1; - - // Indicate EL1 as exception origin mode (so we go back there) - saved_program_status_register_el3.M = Aarch64::SPSR_EL3::Mode::EL2t; - - // Set the register - Aarch64::SPSR_EL3::write(saved_program_status_register_el3); - - // This will jump into os_start() below - enter_el2_from_el3(); -} -static void drop_to_el1() -{ - Aarch64::HCR_EL2 hypervisor_configuration_register_el2 = {}; - hypervisor_configuration_register_el2.RW = 1; // EL1 to use 64-bit mode - Aarch64::HCR_EL2::write(hypervisor_configuration_register_el2); - - Aarch64::SPSR_EL2 saved_program_status_register_el2 = {}; - - // Mask (disable) all interrupts - saved_program_status_register_el2.A = 1; - saved_program_status_register_el2.I = 1; - saved_program_status_register_el2.F = 1; - - // Indicate EL1 as exception origin mode (so we go back there) - saved_program_status_register_el2.M = Aarch64::SPSR_EL2::Mode::EL1t; - - Aarch64::SPSR_EL2::write(saved_program_status_register_el2); - enter_el1_from_el2(); -} - -static void set_up_el1() -{ - Aarch64::SCTLR_EL1 system_control_register_el1 = Aarch64::SCTLR_EL1::reset_value(); - - system_control_register_el1.UCT = 1; // Don't trap access to CTR_EL0 - system_control_register_el1.nTWE = 1; // Don't trap WFE instructions - system_control_register_el1.nTWI = 1; // Don't trap WFI instructions - system_control_register_el1.DZE = 1; // Don't trap DC ZVA instructions - system_control_register_el1.UMA = 1; // Don't trap access to DAIF (debugging) flags of EFLAGS register - system_control_register_el1.SA0 = 1; // Enable stack access alignment check for EL0 - system_control_register_el1.SA = 1; // Enable stack access alignment check for EL1 - system_control_register_el1.A = 1; // Enable memory access alignment check - - Aarch64::SCTLR_EL1::write(system_control_register_el1); -} - -void drop_to_exception_level_1() -{ - switch (Kernel::Aarch64::Asm::get_current_exception_level()) { - case Kernel::Aarch64::Asm::ExceptionLevel::EL3: - drop_to_el2(); - [[fallthrough]]; - case Kernel::Aarch64::Asm::ExceptionLevel::EL2: - drop_to_el1(); - [[fallthrough]]; - case Kernel::Aarch64::Asm::ExceptionLevel::EL1: - set_up_el1(); - break; - default: { - Prekernel::panic("FATAL: CPU booted in unsupported exception mode!\r\n"); - } - } -} - -} diff --git a/Kernel/Arch/aarch64/PrekernelMMU.cpp b/Kernel/Arch/aarch64/PrekernelMMU.cpp deleted file mode 100644 index 31ec573299..0000000000 --- a/Kernel/Arch/aarch64/PrekernelMMU.cpp +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (c) 2021, James Mintram - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -#include - -#include -#include -#include -#include - -// Documentation here for Aarch64 Address Translations -// https://documentation-service.arm.com/static/5efa1d23dbdee951c1ccdec5?token= - -using namespace Kernel; - -// These come from the linker script -extern u8 page_tables_phys_start[]; -extern u8 page_tables_phys_end[]; - -namespace Prekernel { - -// physical memory -constexpr u32 START_OF_NORMAL_MEMORY = 0x00000000; -constexpr u32 END_OF_NORMAL_MEMORY = 0x3EFFFFFF; - -// 4KiB page size was chosen for the prekernel to make this code slightly simpler -constexpr u32 GRANULE_SIZE = 0x1000; -constexpr u32 PAGE_TABLE_SIZE = 0x1000; - -// Documentation for translation table format -// https://developer.arm.com/documentation/101811/0101/Controlling-address-translation -constexpr u32 PAGE_DESCRIPTOR = 0b11; -constexpr u32 TABLE_DESCRIPTOR = 0b11; -constexpr u32 DESCRIPTOR_MASK = ~0b11; - -constexpr u32 ACCESS_FLAG = 1 << 10; - -// shareability -constexpr u32 OUTER_SHAREABLE = (2 << 8); -constexpr u32 INNER_SHAREABLE = (3 << 8); - -// these index into the MAIR attribute table -constexpr u32 NORMAL_MEMORY = (0 << 2); -constexpr u32 DEVICE_MEMORY = (1 << 2); - -ALWAYS_INLINE static u64* descriptor_to_pointer(FlatPtr descriptor) -{ - return (u64*)(descriptor & DESCRIPTOR_MASK); -} - -namespace { -class PageBumpAllocator { -public: - PageBumpAllocator(u64* start, u64* end) - : m_start(start) - , m_end(end) - , m_current(start) - { - if (m_start >= m_end) { - Prekernel::panic("Invalid memory range passed to PageBumpAllocator"); - } - if ((FlatPtr)m_start % PAGE_TABLE_SIZE != 0 || (FlatPtr)m_end % PAGE_TABLE_SIZE != 0) { - Prekernel::panic("Memory range passed into PageBumpAllocator not aligned to PAGE_TABLE_SIZE"); - } - } - - u64* take_page() - { - if (m_current == m_end) { - Prekernel::panic("Prekernel pagetable memory exhausted"); - } - - u64* page = m_current; - m_current += (PAGE_TABLE_SIZE / sizeof(FlatPtr)); - - zero_page(page); - return page; - } - -private: - void zero_page(u64* page) - { - // Memset all page table memory to zero - for (u64* p = page; p < page + (PAGE_TABLE_SIZE / sizeof(u64)); p++) { - *p = 0; - } - } - - u64 const* m_start; - u64 const* m_end; - u64* m_current; -}; -} - -static void insert_identity_entries_for_physical_memory_range(PageBumpAllocator& allocator, u64* page_table, FlatPtr start, FlatPtr end, u64 flags) -{ - // Not very efficient, but simple and it works. - for (FlatPtr addr = start; addr < end; addr += GRANULE_SIZE) { - // Each level has 9 bits (512 entries) - u64 level0_idx = (addr >> 39) & 0x1FF; - u64 level1_idx = (addr >> 30) & 0x1FF; - u64 level2_idx = (addr >> 21) & 0x1FF; - u64 level3_idx = (addr >> 12) & 0x1FF; - - u64* level1_table = page_table; - - if (level1_table[level0_idx] == 0) { - level1_table[level0_idx] = (FlatPtr)allocator.take_page(); - level1_table[level0_idx] |= TABLE_DESCRIPTOR; - } - - u64* level2_table = descriptor_to_pointer(level1_table[level0_idx]); - - if (level2_table[level1_idx] == 0) { - level2_table[level1_idx] = (FlatPtr)allocator.take_page(); - level2_table[level1_idx] |= TABLE_DESCRIPTOR; - } - - u64* level3_table = descriptor_to_pointer(level2_table[level1_idx]); - - if (level3_table[level2_idx] == 0) { - level3_table[level2_idx] = (FlatPtr)allocator.take_page(); - level3_table[level2_idx] |= TABLE_DESCRIPTOR; - } - - u64* level4_table = descriptor_to_pointer(level3_table[level2_idx]); - u64* l4_entry = &level4_table[level3_idx]; - *l4_entry = addr; - *l4_entry |= flags; - } -} - -static void build_identity_map(PageBumpAllocator& allocator) -{ - u64* level1_table = allocator.take_page(); - - u64 normal_memory_flags = ACCESS_FLAG | PAGE_DESCRIPTOR | INNER_SHAREABLE | NORMAL_MEMORY; - u64 device_memory_flags = ACCESS_FLAG | PAGE_DESCRIPTOR | OUTER_SHAREABLE | DEVICE_MEMORY; - - insert_identity_entries_for_physical_memory_range(allocator, level1_table, START_OF_NORMAL_MEMORY, END_OF_NORMAL_MEMORY, normal_memory_flags); - insert_identity_entries_for_physical_memory_range(allocator, level1_table, MMIO::the().peripheral_base_address(), MMIO::the().peripheral_end_address(), device_memory_flags); -} - -static void switch_to_page_table(u8* page_table) -{ - Aarch64::Asm::set_ttbr0_el1((FlatPtr)page_table); - Aarch64::Asm::set_ttbr1_el1((FlatPtr)page_table); -} - -static void activate_mmu() -{ - Aarch64::MAIR_EL1 mair_el1 = {}; - mair_el1.Attr[0] = 0xFF; // Normal memory - mair_el1.Attr[1] = 0b00000100; // Device-nGnRE memory (non-cacheble) - Aarch64::MAIR_EL1::write(mair_el1); - - // Configure cacheability attributes for memory associated with translation table walks - Aarch64::TCR_EL1 tcr_el1 = {}; - - tcr_el1.SH1 = Aarch64::TCR_EL1::InnerShareable; - tcr_el1.ORGN1 = Aarch64::TCR_EL1::NormalMemory_Outer_WriteBack_ReadAllocate_WriteAllocateCacheable; - tcr_el1.IRGN1 = Aarch64::TCR_EL1::NormalMemory_Inner_WriteBack_ReadAllocate_WriteAllocateCacheable; - - tcr_el1.SH0 = Aarch64::TCR_EL1::InnerShareable; - tcr_el1.ORGN0 = Aarch64::TCR_EL1::NormalMemory_Outer_WriteBack_ReadAllocate_WriteAllocateCacheable; - tcr_el1.IRGN0 = Aarch64::TCR_EL1::NormalMemory_Inner_WriteBack_ReadAllocate_WriteAllocateCacheable; - - tcr_el1.TG1 = Aarch64::TCR_EL1::TG1GranuleSize::Size_4KB; - tcr_el1.TG0 = Aarch64::TCR_EL1::TG0GranuleSize::Size_4KB; - - // Auto detect the Intermediate Physical Address Size - Aarch64::ID_AA64MMFR0_EL1 feature_register = Aarch64::ID_AA64MMFR0_EL1::read(); - tcr_el1.IPS = feature_register.PARange; - - Aarch64::TCR_EL1::write(tcr_el1); - - // Enable MMU in the system control register - Aarch64::SCTLR_EL1 sctlr_el1 = Aarch64::SCTLR_EL1::read(); - sctlr_el1.M = 1; // Enable MMU - Aarch64::SCTLR_EL1::write(sctlr_el1); - - Aarch64::Asm::flush(); -} - -void init_prekernel_page_tables() -{ - PageBumpAllocator allocator((u64*)page_tables_phys_start, (u64*)page_tables_phys_end); - build_identity_map(allocator); - switch_to_page_table(page_tables_phys_start); - activate_mmu(); -} - -} diff --git a/Kernel/Arch/aarch64/RPi/Framebuffer.cpp b/Kernel/Arch/aarch64/RPi/Framebuffer.cpp new file mode 100644 index 0000000000..fb99de9adf --- /dev/null +++ b/Kernel/Arch/aarch64/RPi/Framebuffer.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2021, Marcin Undak + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace Prekernel { + +Framebuffer::Framebuffer() +{ + // FIXME: query HDMI for best mode + // https://github.com/raspberrypi/userland/blob/master/host_applications/linux/apps/tvservice/tvservice.c + m_width = 1280; + m_height = 720; + m_depth = 32; + m_initialized = false; + + struct __attribute__((aligned(16))) { + Mailbox::MessageHeader header; + FramebufferSetPhysicalSizeMboxMessage set_physical_size; + FramebufferSetVirtualSizeMboxMessage set_virtual_size; + FramebufferSetVirtualOffsetMboxMessage set_virtual_offset; + FramebufferSetDepthMboxMessage set_depth; + FramebufferSetPixelOrderMboxMessage set_pixel_order; + FramebufferAllocateBufferMboxMessage allocate_buffer; + FramebufferGetPithMboxMessage get_pitch; + Mailbox::MessageTail tail; + } message_queue; + + message_queue.header.set_queue_size(sizeof(message_queue)); + message_queue.set_physical_size.width = m_width; + message_queue.set_physical_size.height = m_height; + message_queue.set_virtual_size.width = m_width; + message_queue.set_virtual_size.height = m_height; + + // FIXME! those next 2 lines crash... + // message_queue.set_virtual_offset.x = 0; + // message_queue.set_virtual_offset.y = 0; + + message_queue.set_depth.depth_bits = 32; + message_queue.set_pixel_order.pixel_order = FramebufferSetPixelOrderMboxMessage::PixelOrder::RGB; + message_queue.allocate_buffer.alignment = 4096; + + if (!Mailbox::the().send_queue(&message_queue, sizeof(message_queue))) { + warnln("Framebuffer(): Mailbox send failed."); + return; + } + + // Now message queue contains responses. Process them. + + if (message_queue.set_physical_size.width != m_width || message_queue.set_physical_size.height != m_height) { + warnln("Framebuffer(): Setting physical dimension failed."); + return; + } + + if (message_queue.set_virtual_size.width != m_width || message_queue.set_virtual_size.height != m_height) { + warnln("Framebuffer(): Setting virtual dimension failed."); + return; + } + + if (message_queue.set_virtual_offset.x != 0 || message_queue.set_virtual_offset.y != 0) { + warnln("Framebuffer(): Setting virtual offset failed."); + return; + } + + if (message_queue.set_depth.depth_bits != m_depth) { + warnln("Framebuffer(): Setting depth failed."); + return; + } + + if (message_queue.allocate_buffer.size == 0 || message_queue.allocate_buffer.address == 0) { + warnln("Framebuffer(): Allocating buffer failed."); + return; + } + + if (message_queue.get_pitch.pitch == 0) { + warnln("Framebuffer(): Retrieving pitch failed."); + return; + } + + // Convert GPU address space to RAM + // GPU maps memory from 0x80000000 instead of 0x00000000 + m_gpu_buffer = reinterpret_cast(message_queue.allocate_buffer.address & 0x3FFFFFFF); + + m_buffer_size = message_queue.allocate_buffer.size; + m_pitch = message_queue.get_pitch.pitch; + + switch (message_queue.set_pixel_order.pixel_order) { + case FramebufferSetPixelOrderMboxMessage::PixelOrder::RGB: + m_pixel_order = PixelOrder::RGB; + break; + case FramebufferSetPixelOrderMboxMessage::PixelOrder::BGR: + m_pixel_order = PixelOrder::BGR; + break; + default: + warnln("Framebuffer(): Unsupported pixel order reported by GPU."); + m_pixel_order = PixelOrder::RGB; + break; + } + + dbgln("Initialized framebuffer: 1280 x 720 @ 32 bits"); + m_initialized = true; +} + +Framebuffer& Framebuffer::the() +{ + static Framebuffer instance; + return instance; +} +} diff --git a/Kernel/Arch/aarch64/RPi/Framebuffer.h b/Kernel/Arch/aarch64/RPi/Framebuffer.h new file mode 100644 index 0000000000..2cde37dfdb --- /dev/null +++ b/Kernel/Arch/aarch64/RPi/Framebuffer.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021, Marcin Undak + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Prekernel { + +class Framebuffer { +public: + enum class PixelOrder { + RGB, + BGR, + }; + + static Framebuffer& the(); + + bool initialized() const { return m_initialized; } + u16 width() const { return m_width; } + u16 height() const { return m_height; } + u8 depth() const { return m_depth; } + u8* gpu_buffer() const { return m_gpu_buffer; } + u32 buffer_size() const { return m_buffer_size; } + u32 pitch() const { return m_pitch; } + PixelOrder pixel_order() { return m_pixel_order; } + +private: + u16 m_width; + u16 m_height; + u8 m_depth; + u8* m_gpu_buffer; + u32 m_buffer_size; + u32 m_pitch; + bool m_initialized; + PixelOrder m_pixel_order; + + Framebuffer(); +}; +} diff --git a/Kernel/Arch/aarch64/RPi/FramebufferMailboxMessages.h b/Kernel/Arch/aarch64/RPi/FramebufferMailboxMessages.h new file mode 100644 index 0000000000..7dab801b73 --- /dev/null +++ b/Kernel/Arch/aarch64/RPi/FramebufferMailboxMessages.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2021, Marcin Undak + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Prekernel { + +class FramebufferSetPhysicalSizeMboxMessage : public Mailbox::Message { +public: + u32 width; + u32 height; + + FramebufferSetPhysicalSizeMboxMessage() + : Mailbox::Message(0x48003, 8) + { + width = 0; + height = 0; + } +}; +static_assert(sizeof(FramebufferSetPhysicalSizeMboxMessage) == 20); + +class FramebufferSetVirtualSizeMboxMessage : public Mailbox::Message { +public: + u32 width; + u32 height; + + FramebufferSetVirtualSizeMboxMessage() + : Mailbox::Message(0x48004, 8) + { + width = 0; + height = 0; + } +}; +static_assert(sizeof(FramebufferSetVirtualSizeMboxMessage) == 20); + +class FramebufferSetVirtualOffsetMboxMessage : public Mailbox::Message { +public: + u32 x; + u32 y; + + FramebufferSetVirtualOffsetMboxMessage() + : Mailbox::Message(0x48009, 8) + { + x = 0; + y = 0; + } +}; +static_assert(sizeof(FramebufferSetVirtualOffsetMboxMessage) == 20); + +class FramebufferSetDepthMboxMessage : public Mailbox::Message { +public: + u32 depth_bits; + + FramebufferSetDepthMboxMessage() + : Mailbox::Message(0x48005, 4) + { + depth_bits = 0; + } +}; +static_assert(sizeof(FramebufferSetDepthMboxMessage) == 16); + +class FramebufferSetPixelOrderMboxMessage : public Mailbox::Message { +public: + enum PixelOrder : u32 { + BGR = 0, + RGB = 1 + }; + + PixelOrder pixel_order; + + FramebufferSetPixelOrderMboxMessage() + : Mailbox::Message(0x48006, 4) + { + pixel_order = PixelOrder::BGR; + } +}; +static_assert(sizeof(FramebufferSetPixelOrderMboxMessage) == 16); + +class FramebufferAllocateBufferMboxMessage : public Mailbox::Message { +public: + union { + u32 alignment; + u32 address; + }; + u32 size = 0; + + FramebufferAllocateBufferMboxMessage() + : Mailbox::Message(0x40001, 8) + { + alignment = 0; + size = 0; + } +}; +static_assert(sizeof(FramebufferAllocateBufferMboxMessage) == 20); + +class FramebufferGetPithMboxMessage : public Mailbox::Message { +public: + u32 pitch; + + FramebufferGetPithMboxMessage() + : Mailbox::Message(0x40008, 4) + { + pitch = 0; + } +}; +static_assert(sizeof(FramebufferGetPithMboxMessage) == 16); + +} diff --git a/Kernel/Arch/aarch64/RPi/GPIO.cpp b/Kernel/Arch/aarch64/RPi/GPIO.cpp new file mode 100644 index 0000000000..bffba6bade --- /dev/null +++ b/Kernel/Arch/aarch64/RPi/GPIO.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2021, Nico Weber + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +extern "C" void wait_cycles(int n); + +namespace Prekernel { + +// See BCM2835-ARM-Peripherals.pdf section "6 General Purpose I/O" or bcm2711-peripherals.pdf "Chapter 5. General Purpose I/O". + +// "6.1 Register View" / "5.2 Register View" + +struct PinData { + u32 bits[2]; + u32 reserved; +}; + +struct GPIOControlRegisters { + u32 function_select[6]; // Every u32 stores a 3-bit function code for 10 pins. + u32 reserved; + PinData output_set; + PinData output_clear; + PinData level; + PinData event_detect_status; + PinData rising_edge_detect_enable; + PinData falling_edge_detect_enable; + PinData high_detect_enable; + PinData low_detect_enable; + PinData async_rising_edge_detect_enable; + PinData async_falling_edge_detect_enable; + u32 pull_up_down_enable; + PinData pull_up_down_enable_clock; + u32 test; +}; + +GPIO::GPIO() + : m_registers(MMIO::the().peripheral(0x20'0000)) +{ +} + +GPIO& GPIO::the() +{ + static GPIO instance; + return instance; +} + +void GPIO::set_pin_function(unsigned pin_number, PinFunction function) +{ + // pin_number must be <= 53. We can't VERIFY() that since this function runs too early to print assertion failures. + + unsigned function_select_index = pin_number / 10; + unsigned function_select_bits_start = (pin_number % 10) * 3; + + u32 function_bits = m_registers->function_select[function_select_index]; + function_bits = (function_bits & ~(0b111 << function_select_bits_start)) | (static_cast(function) << function_select_bits_start); + m_registers->function_select[function_select_index] = function_bits; +} + +void GPIO::internal_enable_pins(u32 enable[2], PullUpDownState state) +{ + // Section "GPIO Pull-up/down Clock Registers (GPPUDCLKn)": + // The GPIO Pull-up/down Clock Registers control the actuation of internal pull-downs on + // the respective GPIO pins. These registers must be used in conjunction with the GPPUD + // register to effect GPIO Pull-up/down changes. The following sequence of events is + // required: + // 1. Write to GPPUD to set the required control signal (i.e. Pull-up or Pull-Down or neither + // to remove the current Pull-up/down) + m_registers->pull_up_down_enable = static_cast(state); + + // 2. Wait 150 cycles – this provides the required set-up time for the control signal + wait_cycles(150); + + // 3. Write to GPPUDCLK0/1 to clock the control signal into the GPIO pads you wish to + // modify – NOTE only the pads which receive a clock will be modified, all others will + // retain their previous state. + m_registers->pull_up_down_enable_clock.bits[0] = enable[0]; + m_registers->pull_up_down_enable_clock.bits[1] = enable[1]; + + // 4. Wait 150 cycles – this provides the required hold time for the control signal + wait_cycles(150); + + // 5. Write to GPPUD to remove the control signal + m_registers->pull_up_down_enable = 0; + + // 6. Write to GPPUDCLK0/1 to remove the clock + m_registers->pull_up_down_enable_clock.bits[0] = 0; + m_registers->pull_up_down_enable_clock.bits[1] = 0; + + // bcm2711-peripherals.pdf documents GPIO_PUP_PDN_CNTRL_REG[4] registers that store 2 bits state per register, similar to function_select. + // I don't know if the RPi3 has that already, so this uses the old BCM2835 approach for now. +} + +} diff --git a/Kernel/Arch/aarch64/RPi/GPIO.h b/Kernel/Arch/aarch64/RPi/GPIO.h new file mode 100644 index 0000000000..4f38f9c376 --- /dev/null +++ b/Kernel/Arch/aarch64/RPi/GPIO.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021, Nico Weber + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Prekernel { + +struct GPIOControlRegisters; + +// Can configure the general-purpose I/O registers on a Raspberry Pi. +class GPIO { +public: + enum class PinFunction { + Input = 0, + Output = 1, + Alternate0 = 0b100, + Alternate1 = 0b101, + Alternate2 = 0b110, + Alternate3 = 0b111, + Alternate4 = 0b011, + Alternate5 = 0b010, + }; + + static GPIO& the(); + + void set_pin_function(unsigned pin_number, PinFunction); + + enum class PullUpDownState { + Disable = 0, + PullDown = 1, + PullUp = 2, + }; + + template + void set_pin_pull_up_down_state(Array pins, PullUpDownState state) + { + u32 enable[2] = {}; + for (int pin : pins) { + if (pin < 32) + enable[0] |= (1 << pin); + else + enable[1] |= (1 << (pin - 32)); + } + internal_enable_pins(enable, state); + } + +private: + GPIO(); + void internal_enable_pins(u32 enable[2], PullUpDownState state); + + GPIOControlRegisters volatile* m_registers; +}; + +} diff --git a/Kernel/Arch/aarch64/RPi/MMIO.cpp b/Kernel/Arch/aarch64/RPi/MMIO.cpp new file mode 100644 index 0000000000..a6b270a17d --- /dev/null +++ b/Kernel/Arch/aarch64/RPi/MMIO.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021, Nico Weber + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Prekernel { + +MMIO::MMIO() + : m_base_address(0xFE00'0000) +{ + MainIdRegister id; + if (id.part_num() <= MainIdRegister::RaspberryPi3) + m_base_address = 0x3F00'0000; +} + +MMIO& MMIO::the() +{ + static MMIO instance; + return instance; +} + +} diff --git a/Kernel/Arch/aarch64/RPi/MMIO.h b/Kernel/Arch/aarch64/RPi/MMIO.h new file mode 100644 index 0000000000..895319d869 --- /dev/null +++ b/Kernel/Arch/aarch64/RPi/MMIO.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021, Nico Weber + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Prekernel { + +// Knows about memory-mapped IO addresses on the Broadcom family of SOCs used in Raspberry Pis. +// RPi3 is the first Raspberry Pi that supports aarch64. +// https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf (RPi3) +// https://datasheets.raspberrypi.org/bcm2711/bcm2711-peripherals.pdf (RPi4 Model B) +class MMIO { +public: + static MMIO& the(); + + u32 read(FlatPtr offset) { return *peripheral_address(offset); } + void write(FlatPtr offset, u32 value) { *peripheral_address(offset) = value; } + + u32 volatile* peripheral_address(FlatPtr offset) { return (u32 volatile*)(m_base_address + offset); } + template + T volatile* peripheral(FlatPtr offset) { return (T volatile*)peripheral_address(offset); } + + FlatPtr peripheral_base_address() const { return m_base_address; } + FlatPtr peripheral_end_address() const { return m_base_address + 0x00FFFFFF; } + +private: + MMIO(); + + unsigned int m_base_address; +}; + +} diff --git a/Kernel/Arch/aarch64/RPi/Mailbox.cpp b/Kernel/Arch/aarch64/RPi/Mailbox.cpp new file mode 100644 index 0000000000..d36a5305b4 --- /dev/null +++ b/Kernel/Arch/aarch64/RPi/Mailbox.cpp @@ -0,0 +1,107 @@ +/* + * 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; + +Mailbox::Message::Message(u32 tag, u32 arguments_size) +{ + m_tag = tag; + m_arguments_size = arguments_size; + m_command_tag = MBOX_REQUEST; +} + +Mailbox::MessageHeader::MessageHeader() +{ + m_message_queue_size = 0; + m_command_tag = MBOX_REQUEST; +} + +bool Mailbox::MessageHeader::success() const +{ + return m_command_tag == MBOX_RESPONSE_SUCCESS; +} + +Mailbox& Mailbox::the() +{ + static Mailbox instance; + return instance; +} + +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::send_queue(void* queue, u32 queue_size) const +{ + // According to Raspberry Pi specs this is the only channel implemented. + const u32 channel = ARM_TO_VIDEOCORE_CHANNEL; + + auto message_header = reinterpret_cast(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); + + // The mailbox message is 32-bit based, so this assumes that message is in the first 4 GiB. + u32 request = static_cast(reinterpret_cast(queue) & ~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_header->success(); + } + + return true; +} + +} diff --git a/Kernel/Arch/aarch64/RPi/Mailbox.h b/Kernel/Arch/aarch64/RPi/Mailbox.h new file mode 100644 index 0000000000..f54eea7c99 --- /dev/null +++ b/Kernel/Arch/aarch64/RPi/Mailbox.h @@ -0,0 +1,54 @@ +/* + * 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: + // Base class for Mailbox messages. Implemented in subsystems that use Mailbox. + class Message { + protected: + Message(u32 tag, u32 arguments_size); + + private: + u32 m_tag; + u32 m_arguments_size; + u32 m_command_tag; + }; + + // Must be at the beginning of every command message queue + class MessageHeader { + public: + MessageHeader(); + + u32 queue_size() { return m_message_queue_size; } + void set_queue_size(u32 size) { m_message_queue_size = size; } + bool success() const; + + private: + u32 m_message_queue_size; + u32 m_command_tag; + }; + + // Must be at the end of every command message queue + class MessageTail { + private: + u32 m_empty_tag = 0; + }; + + static Mailbox& the(); + + // Sends message queue to VideoCore + bool send_queue(void* queue, u32 queue_size) const; +}; + +} diff --git a/Kernel/Arch/aarch64/RPi/Timer.cpp b/Kernel/Arch/aarch64/RPi/Timer.cpp new file mode 100644 index 0000000000..0a703c180b --- /dev/null +++ b/Kernel/Arch/aarch64/RPi/Timer.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2021, Nico Weber + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +namespace Prekernel { + +// "12.1 System Timer Registers" / "10.2 System Timer Registers" +struct TimerRegisters { + u32 control_and_status; + u32 counter_low; + u32 counter_high; + u32 compare[4]; +}; + +// Bits of the `control_and_status` register. +// See "CS register" in Broadcom doc for details. +enum FlagBits { + SystemTimerMatch0 = 1 << 0, + SystemTimerMatch1 = 1 << 1, + SystemTimerMatch2 = 1 << 2, + SystemTimerMatch3 = 1 << 3, +}; + +Timer::Timer() + : m_registers(MMIO::the().peripheral(0x3000)) +{ +} + +Timer& Timer::the() +{ + static Timer instance; + return instance; +} + +u64 Timer::microseconds_since_boot() +{ + u32 high = m_registers->counter_high; + u32 low = m_registers->counter_low; + if (high != m_registers->counter_high) { + high = m_registers->counter_high; + low = m_registers->counter_low; + } + return (static_cast(high) << 32) | low; +} + +class SetClockRateMboxMessage : Prekernel::Mailbox::Message { +public: + u32 clock_id; + u32 rate_hz; + u32 skip_setting_turbo; + + SetClockRateMboxMessage() + : Prekernel::Mailbox::Message(0x0003'8002, 12) + { + clock_id = 0; + rate_hz = 0; + skip_setting_turbo = 0; + } +}; + +u32 Timer::set_clock_rate(ClockID clock_id, u32 rate_hz, bool skip_setting_turbo) +{ + struct __attribute__((aligned(16))) { + Prekernel::Mailbox::MessageHeader header; + SetClockRateMboxMessage set_clock_rate; + Prekernel::Mailbox::MessageTail tail; + } message_queue; + + message_queue.set_clock_rate.clock_id = static_cast(clock_id); + message_queue.set_clock_rate.rate_hz = rate_hz; + message_queue.set_clock_rate.skip_setting_turbo = skip_setting_turbo ? 1 : 0; + + if (!Prekernel::Mailbox::the().send_queue(&message_queue, sizeof(message_queue))) { + warnln("Timer::set_clock_rate() failed!"); + return 0; + } + + return message_queue.set_clock_rate.rate_hz; +} + +} diff --git a/Kernel/Arch/aarch64/RPi/Timer.h b/Kernel/Arch/aarch64/RPi/Timer.h new file mode 100644 index 0000000000..27762930bf --- /dev/null +++ b/Kernel/Arch/aarch64/RPi/Timer.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021, Nico Weber + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Prekernel { + +struct TimerRegisters; + +class Timer { +public: + static Timer& the(); + + u64 microseconds_since_boot(); + + enum class ClockID { + Reserved = 0, + EMMC = 1, + UART = 2, + ARM = 3, + CORE = 4, + V3D = 5, + H264 = 6, + ISP = 7, + SDRAM = 8, + PIXEL = 9, + PWM = 10, + HEVC = 11, + EMMC2 = 12, + M2MC = 13, + PIXEL_BVB = 14, + }; + u32 set_clock_rate(ClockID, u32 rate_hz, bool skip_setting_turbo = true); + +private: + Timer(); + + TimerRegisters volatile* m_registers; +}; + +} diff --git a/Kernel/Arch/aarch64/RPi/UART.cpp b/Kernel/Arch/aarch64/RPi/UART.cpp new file mode 100644 index 0000000000..36852b6fce --- /dev/null +++ b/Kernel/Arch/aarch64/RPi/UART.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2021, Nico Weber + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +namespace Prekernel { + +// "13.4 Register View" / "11.5 Register View" +struct UARTRegisters { + u32 data; + u32 receive_status_or_error_clear; + u32 unused[4]; + u32 flag; + u32 unused2; + + u32 unused_ilpr; + u32 integer_baud_rate_divisor; // Only the lowest 16 bits are used. + u32 fractional_baud_rate_divisor; // Only the lowest 6 bits are used. + u32 line_control; + + u32 control; + u32 interrupt_fifo_level_select; + u32 interrupt_mask_set_clear; + u32 raw_interrupt_status; + + u32 masked_interrupt_status; + u32 interrupt_clear; + u32 dma_control; + u32 test_control; +}; + +// Bits of the `flag` register. +// See "FR register" in Broadcom doc for details. +enum FlagBits { + ClearToSend = 1 << 0, + UnsupportedDSR = 1 << 1, + UnsupportedDCD = 1 << 2, + UARTBusy = 1 << 3, + ReceiveFifoEmpty = 1 << 4, + TransmitFifoFull = 1 << 5, + ReceiveFifoFull = 1 << 6, + TransmitFifoEmpty = 1 << 7, +}; + +// Bits for the `line_control` register. +// See "LCRH register" in Broadcom doc for details. +enum LineControlBits { + SendBreak = 1 << 0, + EnableParityCheckingAndGeneration = 1 << 1, + EvenParity = 1 << 2, + TransmitTwoStopBits = 1 << 3, + EnableFIFOs = 1 << 4, + + WordLength5Bits = 0b00 << 5, + WordLength6Bits = 0b01 << 5, + WordLength7Bits = 0b10 << 5, + WordLength8Bits = 0b11 << 5, + + StickParity = 1 << 7, +}; + +// Bits for the `control` register. +// See "CR register" in Broadcom doc for details. From there: +// NOTE: Program the control registers as follows: +// 1. Disable the UART. +// 2. Wait for the end of transmission or reception of the current character. +// 3. Flush the transmit FIFO by setting the FEN bit to 0 in the Line Control Register, UART_LCRH. +// 4. Reprogram the Control Register, UART_CR. +// 5. Enable the UART +enum ControlBits { + UARTEnable = 1 << 0, + UnsupportedSIREN = 1 << 1, + UnsupportedSIRLP = 1 << 2, + // Bits 3-6 are reserved. + LoopbackEnable = 1 << 7, + TransmitEnable = 1 << 8, + ReceiveEnable = 1 << 9, + UnsupportedDTR = 1 << 10, + RequestToSend = 1 << 11, + UnsupportedOut1 = 1 << 12, + UnsupportedOut2 = 1 << 13, + RTSHardwareFlowControlEnable = 1 << 14, + CTSHardwareFlowControlEnable = 1 << 15, +}; + +UART::UART() + : m_registers(MMIO::the().peripheral(0x20'1000)) +{ + // Disable UART while changing configuration. + m_registers->control = 0; + + // FIXME: Should wait for current transmission to end and should flush FIFO. + + constexpr int baud_rate = 115'200; + + // Set UART clock so that the baud rate divisor ends up as 1.0. + // FIXME: Not sure if this is a good UART clock rate. + u32 rate_in_hz = Timer::the().set_clock_rate(Timer::ClockID::UART, 16 * baud_rate); + + // The BCM's PL011 UART is alternate function 0 on pins 14 and 15. + auto& gpio = Prekernel::GPIO::the(); + gpio.set_pin_function(14, Prekernel::GPIO::PinFunction::Alternate0); + gpio.set_pin_function(15, Prekernel::GPIO::PinFunction::Alternate0); + gpio.set_pin_pull_up_down_state(Array { 14, 15 }, Prekernel::GPIO::PullUpDownState::Disable); + + // Clock and pins are configured. Turn UART on. + set_baud_rate(baud_rate, rate_in_hz); + m_registers->line_control = EnableFIFOs | WordLength8Bits; + + m_registers->control = UARTEnable | TransmitEnable | ReceiveEnable; +} + +UART& UART::the() +{ + static UART instance; + return instance; +} + +void UART::send(u32 c) +{ + wait_until_we_can_send(); + m_registers->data = c; +} + +u32 UART::receive() +{ + wait_until_we_can_receive(); + + // Mask out error bits. + return m_registers->data & 0xFF; +} + +void UART::set_baud_rate(int baud_rate, int uart_frequency_in_hz) +{ + // Broadcom doc: """Baud rate divisor BAUDDIV = (FUARTCLK/(16 * Baud rate))""". + // BAUDDIV is stored as a 16.6 fixed point value, so do computation scaled by (1 << 6) == 64. + // 64*(FUARTCLK/(16 * Baud rate)) == 4*FUARTCLK/(Baud rate). For rounding, add 0.5 == (Baud rate/2)/(Baud rate). + u32 baud_rate_divisor_fixed_point = (4 * uart_frequency_in_hz + baud_rate / 2) / baud_rate; + + m_registers->integer_baud_rate_divisor = baud_rate_divisor_fixed_point / 64; + m_registers->fractional_baud_rate_divisor = baud_rate_divisor_fixed_point % 64; +} + +void UART::wait_until_we_can_send() +{ + while (m_registers->flag & TransmitFifoFull) + ; +} + +void UART::wait_until_we_can_receive() +{ + while (m_registers->flag & ReceiveFifoEmpty) + ; +} + +} diff --git a/Kernel/Arch/aarch64/RPi/UART.h b/Kernel/Arch/aarch64/RPi/UART.h new file mode 100644 index 0000000000..7b77cda167 --- /dev/null +++ b/Kernel/Arch/aarch64/RPi/UART.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021, Nico Weber + * Copyright (c) 2021, Jesse Buhagiar + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Prekernel { + +struct UARTRegisters; + +// Abstracts the PL011 UART on a Raspberry Pi. +// (The BCM2711 on a Raspberry Pi 4 has five PL011 UARTs; this is always the first of those.) +class UART { +public: + static UART& the(); + + void send(u32 c); + u32 receive(); + + void print_str(char const* s) + { + while (*s) + send(*s++); + } + void print_num(u64 n) + { + char buf[21]; + int i = 0; + do { + buf[i++] = (n % 10) + '0'; + n /= 10; + } while (n); + for (i--; i >= 0; i--) + send(buf[i]); + } + + void print_hex(u64 n) + { + char buf[17]; + static char const* digits = "0123456789ABCDEF"; + int i = 0; + do { + buf[i++] = digits[n % 16]; + n /= 16; + } while (n); + send(static_cast('0')); + send(static_cast('x')); + buf[16] = '\0'; + for (i--; i >= 0; i--) { + send(buf[i]); + } + } + +private: + UART(); + + void set_baud_rate(int baud_rate, int uart_frequency_in_hz); + void wait_until_we_can_send(); + void wait_until_we_can_receive(); + + UARTRegisters volatile* m_registers; +}; + +} diff --git a/Kernel/Arch/aarch64/Timer.cpp b/Kernel/Arch/aarch64/Timer.cpp deleted file mode 100644 index 0a703c180b..0000000000 --- a/Kernel/Arch/aarch64/Timer.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Prekernel { - -// "12.1 System Timer Registers" / "10.2 System Timer Registers" -struct TimerRegisters { - u32 control_and_status; - u32 counter_low; - u32 counter_high; - u32 compare[4]; -}; - -// Bits of the `control_and_status` register. -// See "CS register" in Broadcom doc for details. -enum FlagBits { - SystemTimerMatch0 = 1 << 0, - SystemTimerMatch1 = 1 << 1, - SystemTimerMatch2 = 1 << 2, - SystemTimerMatch3 = 1 << 3, -}; - -Timer::Timer() - : m_registers(MMIO::the().peripheral(0x3000)) -{ -} - -Timer& Timer::the() -{ - static Timer instance; - return instance; -} - -u64 Timer::microseconds_since_boot() -{ - u32 high = m_registers->counter_high; - u32 low = m_registers->counter_low; - if (high != m_registers->counter_high) { - high = m_registers->counter_high; - low = m_registers->counter_low; - } - return (static_cast(high) << 32) | low; -} - -class SetClockRateMboxMessage : Prekernel::Mailbox::Message { -public: - u32 clock_id; - u32 rate_hz; - u32 skip_setting_turbo; - - SetClockRateMboxMessage() - : Prekernel::Mailbox::Message(0x0003'8002, 12) - { - clock_id = 0; - rate_hz = 0; - skip_setting_turbo = 0; - } -}; - -u32 Timer::set_clock_rate(ClockID clock_id, u32 rate_hz, bool skip_setting_turbo) -{ - struct __attribute__((aligned(16))) { - Prekernel::Mailbox::MessageHeader header; - SetClockRateMboxMessage set_clock_rate; - Prekernel::Mailbox::MessageTail tail; - } message_queue; - - message_queue.set_clock_rate.clock_id = static_cast(clock_id); - message_queue.set_clock_rate.rate_hz = rate_hz; - message_queue.set_clock_rate.skip_setting_turbo = skip_setting_turbo ? 1 : 0; - - if (!Prekernel::Mailbox::the().send_queue(&message_queue, sizeof(message_queue))) { - warnln("Timer::set_clock_rate() failed!"); - return 0; - } - - return message_queue.set_clock_rate.rate_hz; -} - -} diff --git a/Kernel/Arch/aarch64/Timer.h b/Kernel/Arch/aarch64/Timer.h deleted file mode 100644 index 27762930bf..0000000000 --- a/Kernel/Arch/aarch64/Timer.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Prekernel { - -struct TimerRegisters; - -class Timer { -public: - static Timer& the(); - - u64 microseconds_since_boot(); - - enum class ClockID { - Reserved = 0, - EMMC = 1, - UART = 2, - ARM = 3, - CORE = 4, - V3D = 5, - H264 = 6, - ISP = 7, - SDRAM = 8, - PIXEL = 9, - PWM = 10, - HEVC = 11, - EMMC2 = 12, - M2MC = 13, - PIXEL_BVB = 14, - }; - u32 set_clock_rate(ClockID, u32 rate_hz, bool skip_setting_turbo = true); - -private: - Timer(); - - TimerRegisters volatile* m_registers; -}; - -} diff --git a/Kernel/Arch/aarch64/UART.cpp b/Kernel/Arch/aarch64/UART.cpp deleted file mode 100644 index 36852b6fce..0000000000 --- a/Kernel/Arch/aarch64/UART.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Prekernel { - -// "13.4 Register View" / "11.5 Register View" -struct UARTRegisters { - u32 data; - u32 receive_status_or_error_clear; - u32 unused[4]; - u32 flag; - u32 unused2; - - u32 unused_ilpr; - u32 integer_baud_rate_divisor; // Only the lowest 16 bits are used. - u32 fractional_baud_rate_divisor; // Only the lowest 6 bits are used. - u32 line_control; - - u32 control; - u32 interrupt_fifo_level_select; - u32 interrupt_mask_set_clear; - u32 raw_interrupt_status; - - u32 masked_interrupt_status; - u32 interrupt_clear; - u32 dma_control; - u32 test_control; -}; - -// Bits of the `flag` register. -// See "FR register" in Broadcom doc for details. -enum FlagBits { - ClearToSend = 1 << 0, - UnsupportedDSR = 1 << 1, - UnsupportedDCD = 1 << 2, - UARTBusy = 1 << 3, - ReceiveFifoEmpty = 1 << 4, - TransmitFifoFull = 1 << 5, - ReceiveFifoFull = 1 << 6, - TransmitFifoEmpty = 1 << 7, -}; - -// Bits for the `line_control` register. -// See "LCRH register" in Broadcom doc for details. -enum LineControlBits { - SendBreak = 1 << 0, - EnableParityCheckingAndGeneration = 1 << 1, - EvenParity = 1 << 2, - TransmitTwoStopBits = 1 << 3, - EnableFIFOs = 1 << 4, - - WordLength5Bits = 0b00 << 5, - WordLength6Bits = 0b01 << 5, - WordLength7Bits = 0b10 << 5, - WordLength8Bits = 0b11 << 5, - - StickParity = 1 << 7, -}; - -// Bits for the `control` register. -// See "CR register" in Broadcom doc for details. From there: -// NOTE: Program the control registers as follows: -// 1. Disable the UART. -// 2. Wait for the end of transmission or reception of the current character. -// 3. Flush the transmit FIFO by setting the FEN bit to 0 in the Line Control Register, UART_LCRH. -// 4. Reprogram the Control Register, UART_CR. -// 5. Enable the UART -enum ControlBits { - UARTEnable = 1 << 0, - UnsupportedSIREN = 1 << 1, - UnsupportedSIRLP = 1 << 2, - // Bits 3-6 are reserved. - LoopbackEnable = 1 << 7, - TransmitEnable = 1 << 8, - ReceiveEnable = 1 << 9, - UnsupportedDTR = 1 << 10, - RequestToSend = 1 << 11, - UnsupportedOut1 = 1 << 12, - UnsupportedOut2 = 1 << 13, - RTSHardwareFlowControlEnable = 1 << 14, - CTSHardwareFlowControlEnable = 1 << 15, -}; - -UART::UART() - : m_registers(MMIO::the().peripheral(0x20'1000)) -{ - // Disable UART while changing configuration. - m_registers->control = 0; - - // FIXME: Should wait for current transmission to end and should flush FIFO. - - constexpr int baud_rate = 115'200; - - // Set UART clock so that the baud rate divisor ends up as 1.0. - // FIXME: Not sure if this is a good UART clock rate. - u32 rate_in_hz = Timer::the().set_clock_rate(Timer::ClockID::UART, 16 * baud_rate); - - // The BCM's PL011 UART is alternate function 0 on pins 14 and 15. - auto& gpio = Prekernel::GPIO::the(); - gpio.set_pin_function(14, Prekernel::GPIO::PinFunction::Alternate0); - gpio.set_pin_function(15, Prekernel::GPIO::PinFunction::Alternate0); - gpio.set_pin_pull_up_down_state(Array { 14, 15 }, Prekernel::GPIO::PullUpDownState::Disable); - - // Clock and pins are configured. Turn UART on. - set_baud_rate(baud_rate, rate_in_hz); - m_registers->line_control = EnableFIFOs | WordLength8Bits; - - m_registers->control = UARTEnable | TransmitEnable | ReceiveEnable; -} - -UART& UART::the() -{ - static UART instance; - return instance; -} - -void UART::send(u32 c) -{ - wait_until_we_can_send(); - m_registers->data = c; -} - -u32 UART::receive() -{ - wait_until_we_can_receive(); - - // Mask out error bits. - return m_registers->data & 0xFF; -} - -void UART::set_baud_rate(int baud_rate, int uart_frequency_in_hz) -{ - // Broadcom doc: """Baud rate divisor BAUDDIV = (FUARTCLK/(16 * Baud rate))""". - // BAUDDIV is stored as a 16.6 fixed point value, so do computation scaled by (1 << 6) == 64. - // 64*(FUARTCLK/(16 * Baud rate)) == 4*FUARTCLK/(Baud rate). For rounding, add 0.5 == (Baud rate/2)/(Baud rate). - u32 baud_rate_divisor_fixed_point = (4 * uart_frequency_in_hz + baud_rate / 2) / baud_rate; - - m_registers->integer_baud_rate_divisor = baud_rate_divisor_fixed_point / 64; - m_registers->fractional_baud_rate_divisor = baud_rate_divisor_fixed_point % 64; -} - -void UART::wait_until_we_can_send() -{ - while (m_registers->flag & TransmitFifoFull) - ; -} - -void UART::wait_until_we_can_receive() -{ - while (m_registers->flag & ReceiveFifoEmpty) - ; -} - -} diff --git a/Kernel/Arch/aarch64/UART.h b/Kernel/Arch/aarch64/UART.h deleted file mode 100644 index 7b77cda167..0000000000 --- a/Kernel/Arch/aarch64/UART.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * Copyright (c) 2021, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Prekernel { - -struct UARTRegisters; - -// Abstracts the PL011 UART on a Raspberry Pi. -// (The BCM2711 on a Raspberry Pi 4 has five PL011 UARTs; this is always the first of those.) -class UART { -public: - static UART& the(); - - void send(u32 c); - u32 receive(); - - void print_str(char const* s) - { - while (*s) - send(*s++); - } - void print_num(u64 n) - { - char buf[21]; - int i = 0; - do { - buf[i++] = (n % 10) + '0'; - n /= 10; - } while (n); - for (i--; i >= 0; i--) - send(buf[i]); - } - - void print_hex(u64 n) - { - char buf[17]; - static char const* digits = "0123456789ABCDEF"; - int i = 0; - do { - buf[i++] = digits[n % 16]; - n /= 16; - } while (n); - send(static_cast('0')); - send(static_cast('x')); - buf[16] = '\0'; - for (i--; i >= 0; i--) { - send(buf[i]); - } - } - -private: - UART(); - - void set_baud_rate(int baud_rate, int uart_frequency_in_hz); - void wait_until_we_can_send(); - void wait_until_we_can_receive(); - - UARTRegisters volatile* m_registers; -}; - -} diff --git a/Kernel/Arch/aarch64/boot.S b/Kernel/Arch/aarch64/boot.S deleted file mode 100644 index 4c44b5288a..0000000000 --- a/Kernel/Arch/aarch64/boot.S +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2021, Nico Weber - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -// In a specially-named text section so that the linker script can put it first in .text. -.section ".text.first" - -.global start -.type start, @function -start: - // Let only core 0 continue, put other cores to sleep. - mrs x13, MPIDR_EL1 - and x13, x13, 0xff - cbnz x13, _ZN9Prekernel4haltEv - - // Let stack start before .text for now. - // 512 kiB (0x80000) of stack are probably not sufficient, especially once we give the other cores some stack too, - // but for now it's ok. - msr SPSel, #0 //Use the same SP as we descend into EL1 - ldr x14, =start - mov sp, x14 - - // Clear BSS. - ldr x14, =start_of_bss - ldr x15, =size_of_bss_divided_by_8 -Lbss_clear_loop: - str xzr, [x14], #8 - subs x15, x15, #1 - bne Lbss_clear_loop - - b init diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index 0452c6c19f..9835e1d13b 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -389,22 +389,35 @@ if (NOT "${SERENITY_ARCH}" STREQUAL "aarch64") ${CRYPTO_SOURCES} ) else() + set(PREKERNEL_SOURCES + Arch/aarch64/Prekernel/PrekernelMMU.cpp + Arch/aarch64/Prekernel/PrekernelExceptions.cpp + Arch/aarch64/Prekernel/PrekernelCommon.cpp + Arch/aarch64/Prekernel/boot.S + Arch/aarch64/Prekernel/Aarch64_asm_utils.S + + Prekernel/UBSanitizer.cpp + ) + set(RPI_SOURCES + Arch/aarch64/RPi/GPIO.cpp + Arch/aarch64/RPi/Framebuffer.cpp + Arch/aarch64/RPi/Mailbox.cpp + Arch/aarch64/RPi/Timer.cpp + Arch/aarch64/RPi/UART.cpp + Arch/aarch64/RPi/MMIO.cpp + ) set(SOURCES ${AK_SOURCES} + ${PREKERNEL_SOURCES} + ${RPI_SOURCES} Arch/aarch64/BootPPMParser.cpp - Arch/aarch64/CrashHandler.cpp - Arch/aarch64/GPIO.cpp - Arch/aarch64/Framebuffer.cpp - Arch/aarch64/Mailbox.cpp - Arch/aarch64/MainIdRegister.cpp - Arch/aarch64/MMIO.cpp + Arch/aarch64/CrashHandler.cpp + Arch/aarch64/MainIdRegister.cpp Arch/aarch64/PageDirectory.cpp - Arch/aarch64/Timer.cpp - Arch/aarch64/UART.cpp Arch/aarch64/Utils.cpp - Arch/aarch64/dummy.cpp + Arch/aarch64/Dummy.cpp # Preload specific Arch/aarch64/init.cpp @@ -416,12 +429,10 @@ else() Arch/aarch64/boot.S Arch/aarch64/Aarch64_asm_utils.S Arch/aarch64/vector_table.S - Arch/aarch64/SmapDisabler.cpp Arch/aarch64/ScopedCritical.cpp - MiniStdLib.cpp - Prekernel/UBSanitizer.cpp + MiniStdLib.cpp Heap/kmalloc.cpp Memory/AddressSpace.cpp -- cgit v1.2.3