universalisos/kernel/drivers/uos_fbcon.cpp
Fábio Coutada 9540b0528c feat(universalisos): PikeOS-style Phase B/C device drivers + Phase D microkernel
Phase B (Core Device Support) — all drivers verified in QEMU:
- Network: virtio-net cleanup, RTL8139, E1000, clause-22 MDIO PHY management,
  CAN bus, industrial protocols (Modbus/Profibus/EtherCAT), controller probe+dispatch
- Block storage: RAM disk backend (write->read->verify PASSED), virtio-blk transport,
  backend dispatch, real MBR+GPT partition parsers, SD/eMMC command framework
- GPIO: PL061 (verified), I2C: DesignWare (verified), SPI: PL022 (verified)

Phase C (Advanced Features):
- PCI: FULL PikeOS ARMv7 replica — transport-agnostic uos_pci_ops, config-address
  encoding, BAR sizing, capability walk, enumeration+bridge recursion, MSI/MSI-X
- USB: PikeOS-style layered stack — usb.h contract, usb_core.cpp (enumeration
  state machine), usb_ehci.cpp (EHCI transport)
- Display: FULL 1:1 PikeOS fbcon replica + copied font_8x16

Build foundation fixes:
- Freestanding aeabi_runtime.cpp (__aeabi_uidiv/__aeabi_uldivmod)
- PikeOS-style flat 4GB MMU section map + proper enable (unblocked device MMIO)
- guest.h MAX_GUEST_IMAGE_SIZE 256MB->16MB (BSS was 259MB)
- C/C++ linkage fixes, duplicate-virtio_net_init, MMIO access-size handling

Phase D (PikeOS ARMv7 Microkernel Port):
- D-1: Per-VM address spaces — cloned pgdirs, ASID-tagged TLB, 4K page walker,
  isolation PASSED (two guests, same VA->different PAs), guest fault recovery
- D-2: IRQ dispatch backbone — 1024-slot dispatch table, real GICv2 hardware
  (GICD_CTLR/GICC_CTLR/GICC_PMR/GICC_IAR/GICC_EOIR), arm_irq_handler wired
- D-3: Time subsystem — CNTVCT ns-since-boot, CNTP periodic ticker via D-2
- D-4: KDEV framework — linker-section driver registration, uos_kdev_init_all,
  name lookup
- D-5: VFP/NEON — lazy enable (undef trap->CPACR+FPEXC.EN), FPEXC=0x40000000
- D-6: SMP — per-CPU state, MPIDR, IPI/SGI framework (reschedule+TLB flush)

All uos_ naming (PikeOS p4_ convention adapted). Compiles -Werror freestanding C++17.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-09 09:10:53 +01:00

185 lines
7.2 KiB
C++

/*
* Universalisos Framebuffer Console — implementation
*
* 1:1 functional replica of PikeOS fbcon.c (src/target/arm/v7hf/psp/src):
* same draw_pixel colour packing for 32/24/16/15 bpp, same fbcon_put character
* rendering with the 8x16 font, newline/carriage-return/backspace handling,
* line wrap and cursor. Only the PikeOS console-descriptor registration
* (psp_desc.api.cnsput) is replaced by explicit uos_fbcon_putc/puts entry
* points, since Universalisos has no PSP descriptor.
*
* Author: PortugalFuturista Hypervisor Development Team (adapted from SYSGO PikeOS)
*/
#include "uos_fbcon.h"
#include "../arch/arm/uart.h"
#include <stddef.h>
#define UOS_FBCON_COLOR_LETTER 0xFFFFFFu
#define UOS_FBCON_COLOR_BACK 0x0u
#define UOS_FBCON_ZOOM 1u
/* ----------------------- framebuffer state ------------------------------- */
static volatile uint32_t *g_uos_lfb32 = NULL;
static volatile uint8_t *g_uos_lfb8 = NULL;
static struct uos_fb_geometry *g_uos_fb = NULL;
static uint32_t g_uos_curx = 0;
static uint32_t g_uos_cury = 0;
static bool g_uos_fbcon_active = false;
/* ----------------------- draw_pixel (PikeOS) ----------------------------- */
static void uos_fbcon_draw_pixel(uint32_t x, uint32_t y, uint32_t color) {
if (g_uos_fb == NULL) return;
uint32_t pitch = g_uos_fb->pitch;
uint32_t r = (color >> 16) & 0xffu;
uint32_t g = (color >> 8) & 0xffu;
uint32_t b = (color >> 0) & 0xffu;
uint32_t data32 = (r << g_uos_fb->rpos) | (g << g_uos_fb->gpos) | (b << g_uos_fb->bpos);
if (g_uos_fb->bpp == 32u) {
g_uos_lfb32[y * (pitch / sizeof(uint32_t)) + x] = data32;
} else if (g_uos_fb->bpp == 24u) {
g_uos_lfb8[y * pitch + (3u * x)] = (uint8_t)(data32 & 0xffu);
g_uos_lfb8[y * pitch + (3u * x) + 1] = (uint8_t)((data32 >> 8) & 0xffu);
g_uos_lfb8[y * pitch + (3u * x) + 2] = (uint8_t)((data32 >> 16) & 0xffu);
} else if (g_uos_fb->bpp == 16u || g_uos_fb->bpp == 15u) {
r = (r >> (8 - g_uos_fb->rsize)) & 0xffu;
g = (g >> (8 - g_uos_fb->gsize)) & 0xffu;
b = (b >> (8 - g_uos_fb->bsize)) & 0xffu;
uint16_t data16 = (uint16_t)(((r << g_uos_fb->rpos) | (g << g_uos_fb->gpos) |
(b << g_uos_fb->bpos)) & 0xffffu);
g_uos_lfb8[y * pitch + (2u * x)] = (uint8_t)(data16 & 0xffu);
g_uos_lfb8[y * pitch + (2u * x) + 1] = (uint8_t)((data16 >> 8) & 0xffu);
}
}
/* ----------------------- fbcon_put (PikeOS) ------------------------------ */
void uos_fbcon_putc(char letter) {
if (!g_uos_fbcon_active) return;
uint32_t i, j;
bool cleanline = false;
uint8_t ch = (uint8_t)letter;
if (ch > 0x20u) {
for (i = 0; i < 16u * UOS_FBCON_ZOOM; i++) {
for (j = 0; j < 8u * UOS_FBCON_ZOOM; j++) {
uint8_t glyph = uos_fontdata_8x16[16u * ch + (i / UOS_FBCON_ZOOM)];
uint32_t color = (glyph & (1u << (8u - (j / UOS_FBCON_ZOOM))))
? UOS_FBCON_COLOR_LETTER : UOS_FBCON_COLOR_BACK;
uos_fbcon_draw_pixel(g_uos_curx + j, g_uos_cury + i, color);
}
}
}
if (ch == (uint8_t)'\n') {
g_uos_curx = 0;
g_uos_cury += 20u * UOS_FBCON_ZOOM;
cleanline = true;
} else if (ch == (uint8_t)'\r') {
g_uos_curx = 0;
} else if (ch == (uint8_t)'\b' && g_uos_curx >= (10u * UOS_FBCON_ZOOM)) {
g_uos_curx -= 10u * UOS_FBCON_ZOOM;
} else {
g_uos_curx += 10u * UOS_FBCON_ZOOM;
if (g_uos_curx > (g_uos_fb->resx - 10u * UOS_FBCON_ZOOM)) {
g_uos_curx = 0;
g_uos_cury += 20u * UOS_FBCON_ZOOM;
cleanline = true;
}
}
if (cleanline) {
if (g_uos_cury > (g_uos_fb->resy - 20u * UOS_FBCON_ZOOM)) {
g_uos_cury = 0;
}
/* Rewrite the first character cell (cursor) at the current line. */
for (i = 0; i < 16u * UOS_FBCON_ZOOM; i++) {
for (j = 0; j < 8u * UOS_FBCON_ZOOM; j++) {
uos_fbcon_draw_pixel(j, g_uos_cury + i, UOS_FBCON_COLOR_BACK);
}
}
uint32_t nexty = g_uos_cury + 20u * UOS_FBCON_ZOOM;
if (nexty > (g_uos_fb->resy - 20u * UOS_FBCON_ZOOM)) {
nexty = 0;
}
for (i = 0; i < 16u * UOS_FBCON_ZOOM; i++) {
for (j = 0; j < 8u * UOS_FBCON_ZOOM; j++) {
uos_fbcon_draw_pixel(j, nexty + i, UOS_FBCON_COLOR_LETTER);
}
for (j = 8u * UOS_FBCON_ZOOM; j < g_uos_fb->resx; j++) {
uos_fbcon_draw_pixel(j, nexty + i, UOS_FBCON_COLOR_BACK);
}
}
}
}
void uos_fbcon_puts(const char *s) {
if (s == NULL) return;
while (*s != '\0') {
uos_fbcon_putc(*s);
s++;
}
}
/* ----------------------- init (PikeOS) ----------------------------------- */
int uos_fbcon_init(struct uos_fb_geometry *fb, uint32_t addr) {
g_uos_fb = fb;
g_uos_lfb32 = (volatile uint32_t *)(uintptr_t)addr;
g_uos_lfb8 = (volatile uint8_t *)(uintptr_t)addr;
g_uos_curx = 0;
g_uos_cury = 0;
/* Clear the screen. */
for (uint32_t y = 0; y < fb->resy; y++) {
for (uint32_t x = 0; x < fb->resx; x++) {
uos_fbcon_draw_pixel(x, y, UOS_FBCON_COLOR_BACK);
}
}
g_uos_fbcon_active = true;
return 1;
}
/* ==========================================================================
* Framework (headless) framebuffer — RAM-backed, for builds with no GPU
*
* QEMU virt has no display by default, so the console is backed by a small
* RAM buffer (RGB565) purely so the drawing code runs end-to-end. A board port
* with a real linear framebuffer calls uos_fbcon_init() with that address.
* ========================================================================== */
#define UOS_FW_FB_RESX 256u
#define UOS_FW_FB_RESY 64u
#define UOS_FW_FB_BPP 16u
#define UOS_FW_FB_PITCH (UOS_FW_FB_RESX * (UOS_FW_FB_BPP / 8u)) /* 512 bytes */
#define UOS_FW_FB_SIZE (UOS_FW_FB_PITCH * UOS_FW_FB_RESY) /* 32 KiB */
static uint8_t g_uos_fw_framebuffer[UOS_FW_FB_SIZE];
static struct uos_fb_geometry g_uos_fw_geometry = {
UOS_FW_FB_RESX, UOS_FW_FB_RESY, UOS_FW_FB_PITCH, UOS_FW_FB_BPP,
/* RGB565: r[15:11] g[10:5] b[4:0] */
11u, 5u, 5u, 6u, 0u, 5u
};
void uos_fbcon_driver_init(void) {
uart_puts("\n=== Framebuffer Console (PikeOS fbcon replica) ===\n");
/* Framework mode: RAM-backed framebuffer. */
uint32_t addr = (uint32_t)(uintptr_t)g_uos_fw_framebuffer;
int rc = uos_fbcon_init(&g_uos_fw_geometry, addr);
uart_puts("FBCON: framework framebuffer ");
uart_print_dec(UOS_FW_FB_RESX); uart_puts("x"); uart_print_dec(UOS_FW_FB_RESY);
uart_puts("x"); uart_print_dec(UOS_FW_FB_BPP);
uart_puts(" @ 0x"); uart_print_hex(addr);
uart_puts(rc ? " READY\n" : " FAIL\n");
}
void uos_fbcon_driver_demo(void) {
if (!g_uos_fbcon_active) {
uart_puts("FBCON: not active, skipping demo\n");
return;
}
/* Render a banner into the framebuffer (only visible on real HW; on the
* framework buffer it just exercises the drawing path). */
uos_fbcon_puts("Universalisos\n");
uos_fbcon_puts("Phase C Display\n");
uart_puts("FBCON: rendered demo text to framebuffer\n");
}