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>
185 lines
7.2 KiB
C++
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");
|
|
}
|