summaryrefslogtreecommitdiff
path: root/na/drivers/uart.cc
blob: 264ec6afc6cb9d4bf960c9843fe660c5569afe46 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Copyright (C) 2026 Richard Qin */

/*
 * uart.cc — 16550a UART driver (early console)
 *
 * Implements the early UART output mapping required by panic and kernel_main.
 */

#include <lume/console.h>
#include <lume/fdt.h>
#include <lume/vmm.h>
#include <lume/addr.h>
#include <arch/config.h>

static uint64 g_uart_pa = 0;
static uint64 g_uart_size = 0;
static bool g_runtime_mapping_ready = false;
static bool g_mmu_enabled = false;

extern "C" void console_set_mmu_enabled() {
    g_mmu_enabled = true;
}

extern "C" void early_putc(char c) {
    if (!g_uart_pa) return;

    /*
     * Before vmm_init() activates the final page table, UART MMIO is only
     * reachable via the bootstrap fixed direct map from entry.S.
     * After activation, switch to the runtime (possibly KASLR-slid) base.
     */
    uint64 uart_va = 0;
    if (g_runtime_mapping_ready) {
        uart_va = pa_to_va(phys_addr(g_uart_pa)).raw;
    } else if (!g_mmu_enabled) {
        uart_va = g_uart_pa;
    } else {
        uart_va = boot_pa_to_va(phys_addr(g_uart_pa)).raw;
    }

    volatile auto* uart = reinterpret_cast<volatile uint8*>(uart_va);
    *uart = static_cast<uint8>(c);
}

extern "C" void early_puts(const char* s) {
    while (*s) early_putc(*s++);
}

void console_early_init(uint64 fdt_paddr) {
    uint64 pa = 0;
    uint64 size = 0;
    FdtManager::early_scan_uart(fdt_paddr, &pa, &size);
    if (pa != 0) {
        g_uart_pa = pa;
        if (size != 0) g_uart_size = size;
    }
}

void console_init() {
    /*
     * Once vmm_init() activates the new page table, the 1GB identity map is dropped.
     * The UART must be explicitly registered into the active page table.
     */
    if (g_uart_pa != 0 && g_uart_size != 0) {
        vmm_map_kernel_mmio(g_uart_pa, g_uart_size);
    }
}

void console_use_runtime_mapping() {
    g_runtime_mapping_ready = true;
}