The Pedigree Project 0.1
mach_virt/Serial.cc
1/*
2 * Copyright (c) 2026, Pedigree Developers
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted.
6 */
7
8#include "Serial.h"
9
10namespace {
11constexpr uintptr_t Data = 0x000;
12constexpr uintptr_t Flags = 0x018;
13constexpr uintptr_t IntegerBaud = 0x024;
14constexpr uintptr_t FractionalBaud = 0x028;
15constexpr uintptr_t LineControl = 0x02c;
16constexpr uintptr_t Control = 0x030;
17constexpr uintptr_t InterruptMask = 0x038;
18constexpr uintptr_t InterruptClear = 0x044;
19constexpr uint32_t TxFull = 1U << 5;
20constexpr uint32_t RxEmpty = 1U << 4;
21constexpr size_t PollLimit = 100000;
22
23volatile uint32_t& registerAt(uintptr_t base, uintptr_t offset) {
24 return *reinterpret_cast<volatile uint32_t*>(base + offset);
25}
26} // namespace
27
28VirtSerial::VirtSerial() : m_Base(0) {}
29
30void VirtSerial::setBase(uintptr_t base) {
31 m_Base = base;
32 if (!base) {
33 return;
34 }
35
36 registerAt(base, Control) = 0;
37 registerAt(base, InterruptMask) = 0;
38 registerAt(base, InterruptClear) = 0x7ff;
39 // QEMU virt exposes a 24 MHz PL011 clock. 13 + 1/64 gives 115200 baud.
40 registerAt(base, IntegerBaud) = 13;
41 registerAt(base, FractionalBaud) = 1;
42 registerAt(base, LineControl) = (3U << 5) | (1U << 4);
43 registerAt(base, Control) = (1U << 9) | (1U << 8) | 1U;
44}
45
46void VirtSerial::earlyWrite(uintptr_t base, char c) {
47 if (!base) {
48 return;
49 }
50 if (!(registerAt(base, Control) & 1U)) {
51 registerAt(base, Control) = 0;
52 registerAt(base, InterruptMask) = 0;
53 registerAt(base, IntegerBaud) = 13;
54 registerAt(base, FractionalBaud) = 1;
55 registerAt(base, LineControl) = (3U << 5) | (1U << 4);
56 registerAt(base, Control) = (1U << 9) | (1U << 8) | 1U;
57 }
58 for (size_t i = 0; i < PollLimit; ++i) {
59 if (!(registerAt(base, Flags) & TxFull)) {
60 registerAt(base, Data) = static_cast<uint8_t>(c);
61 return;
62 }
63 asm volatile("yield");
64 }
65}
66
67bool VirtSerial::hasData() {
68 return m_Base && !(registerAt(m_Base, Flags) & RxEmpty);
69}
70
71char VirtSerial::read() {
72 if (!m_Base) {
73 return 0;
74 }
75 while (!hasData()) {
76 asm volatile("yield");
77 }
78 return static_cast<char>(registerAt(m_Base, Data));
79}
80
81char VirtSerial::readNonBlock() {
82 return hasData() ? static_cast<char>(registerAt(m_Base, Data)) : 0;
83}
84
85void VirtSerial::write(char c) {
86 earlyWrite(m_Base, c);
87}
void setBase(uintptr_t base) override
static void earlyWrite(uintptr_t base, char c)