174 lines
5.8 KiB
C
174 lines
5.8 KiB
C
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <lib/fb.h>
|
|
#include <lib/misc.h>
|
|
#include <drivers/vbe.h>
|
|
#include <drivers/gop.h>
|
|
#include <mm/pmm.h>
|
|
#include <sys/cpu.h>
|
|
|
|
struct fb_info *fb_fbs;
|
|
size_t fb_fbs_count = 0;
|
|
|
|
void fb_init(struct fb_info **ret, size_t *_fbs_count,
|
|
uint64_t target_width, uint64_t target_height, uint16_t target_bpp,
|
|
bool preserve_screen) {
|
|
if (quiet) {
|
|
preserve_screen = true;
|
|
}
|
|
|
|
#if defined (BIOS)
|
|
*ret = ext_mem_alloc(sizeof(struct fb_info));
|
|
if (init_vbe(*ret, target_width, target_height, target_bpp, preserve_screen)) {
|
|
*_fbs_count = 1;
|
|
|
|
(*ret)->edid = get_edid_info();
|
|
size_t mode_count;
|
|
(*ret)->mode_list = vbe_get_mode_list(&mode_count);
|
|
(*ret)->mode_count = mode_count;
|
|
} else {
|
|
*_fbs_count = 0;
|
|
pmm_free(*ret, sizeof(struct fb_info));
|
|
}
|
|
#elif defined (UEFI)
|
|
init_gop(ret, _fbs_count, target_width, target_height, target_bpp, preserve_screen);
|
|
#endif
|
|
|
|
fb_fbs = *ret;
|
|
fb_fbs_count = *_fbs_count;
|
|
}
|
|
|
|
void fb_clear(struct fb_info *fb) {
|
|
for (size_t y = 0; y < fb->framebuffer_height; y++) {
|
|
switch (fb->framebuffer_bpp) {
|
|
case 32: {
|
|
uint32_t *fbp = (void *)(uintptr_t)fb->framebuffer_addr;
|
|
size_t row = (y * fb->framebuffer_pitch) / 4;
|
|
for (size_t x = 0; x < fb->framebuffer_width; x++) {
|
|
fbp[row + x] = 0;
|
|
}
|
|
break;
|
|
}
|
|
case 16: {
|
|
uint16_t *fbp = (void *)(uintptr_t)fb->framebuffer_addr;
|
|
size_t row = (y * fb->framebuffer_pitch) / 2;
|
|
for (size_t x = 0; x < fb->framebuffer_width; x++) {
|
|
fbp[row + x] = 0;
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
uint8_t *fbp = (void *)(uintptr_t)fb->framebuffer_addr;
|
|
size_t row = y * fb->framebuffer_pitch;
|
|
size_t row_bytes = fb->framebuffer_width * (fb->framebuffer_bpp / 8);
|
|
for (size_t x = 0; x < row_bytes; x++) {
|
|
fbp[row + x] = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
fb_flush((volatile void *)(uintptr_t)fb->framebuffer_addr,
|
|
(size_t)fb->framebuffer_pitch * fb->framebuffer_height);
|
|
}
|
|
|
|
#if defined (__x86_64__) || defined (__i386__)
|
|
static void fb_flush_x86(volatile void *base, size_t length) {
|
|
static size_t clsz = 0;
|
|
if (clsz == 0) {
|
|
uint32_t eax, ebx, ecx, edx;
|
|
if (!cpuid(1, 0, &eax, &ebx, &ecx, &edx))
|
|
return;
|
|
clsz = ((ebx >> 8) & 0xFF) * 8;
|
|
if (clsz == 0)
|
|
return;
|
|
}
|
|
|
|
uintptr_t start = ALIGN_DOWN((uintptr_t)base, clsz);
|
|
uintptr_t end = ALIGN_UP((uintptr_t)base + length, clsz, panic(false, "fb: Alignment overflow"));
|
|
for (uintptr_t ptr = start; ptr < end; ptr += clsz) {
|
|
asm volatile ("clflush (%0)" :: "r"(ptr) : "memory");
|
|
}
|
|
}
|
|
|
|
static void fb_flush_x86_wbinvd(volatile void *base, size_t length) {
|
|
(void)base;
|
|
(void)length;
|
|
asm volatile ("wbinvd" ::: "memory");
|
|
}
|
|
#elif defined (__aarch64__)
|
|
static void fb_flush_aarch64(volatile void *base, size_t length) {
|
|
clean_dcache_poc((uintptr_t)base, (uintptr_t)base + length);
|
|
}
|
|
#elif defined (__riscv)
|
|
__attribute__((target("arch=+zicbom")))
|
|
static void fb_flush_riscv(volatile void *base, size_t length) {
|
|
const size_t cbom_block_size = 0x40;
|
|
uintptr_t start = ALIGN_DOWN((uintptr_t)base, cbom_block_size);
|
|
uintptr_t end = ALIGN_UP((uintptr_t)(base + length), cbom_block_size, panic(false, "fb: Alignment overflow"));
|
|
for (uintptr_t ptr = start; ptr < end; ptr += cbom_block_size) {
|
|
asm volatile("cbo.flush (%0)" :: "r"(ptr) : "memory");
|
|
}
|
|
}
|
|
|
|
static void fb_flush_riscv_nozicbom(volatile void *base, size_t length) {
|
|
(void)base;
|
|
(void)length;
|
|
|
|
// Without Zicbom, there is no portable instruction to flush dirty cache lines.
|
|
// Read through a dedicated eviction buffer to create cache pressure and displace
|
|
// dirty framebuffer lines. 128 KB covers typical RISC-V L1 D-caches (32-64 KB).
|
|
static volatile uint8_t *eviction_buf = NULL;
|
|
#define EVICTION_BUF_SIZE (128 * 1024)
|
|
if (eviction_buf == NULL) {
|
|
eviction_buf = ext_mem_alloc(EVICTION_BUF_SIZE);
|
|
}
|
|
|
|
volatile uint64_t *p = (volatile uint64_t *)eviction_buf;
|
|
for (size_t i = 0; i < EVICTION_BUF_SIZE / sizeof(uint64_t); i += (64 / sizeof(uint64_t))) {
|
|
(void)p[i];
|
|
}
|
|
asm volatile ("fence rw, rw" ::: "memory");
|
|
}
|
|
#elif defined (__loongarch64)
|
|
static void fb_flush_loongarch64(volatile void *base, size_t length) {
|
|
// cacop Hit_Writeback_Inv_LEAF0 = 0x10 (D-cache L1 writeback+invalidate)
|
|
const size_t clsz = 64;
|
|
uintptr_t start = ALIGN_DOWN((uintptr_t)base, clsz);
|
|
uintptr_t end = ALIGN_UP((uintptr_t)base + length, clsz, panic(false, "fb: Alignment overflow"));
|
|
for (uintptr_t ptr = start; ptr < end; ptr += clsz) {
|
|
asm volatile ("cacop 0x10, %0, 0" :: "r"(ptr) : "memory");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void fb_flush(volatile void *base, size_t length) {
|
|
typedef void (*flush_fn)(volatile void *, size_t);
|
|
static flush_fn fn = NULL;
|
|
|
|
if (fn == NULL) {
|
|
#if defined (__x86_64__) || defined (__i386__)
|
|
uint32_t eax, ebx, ecx, edx;
|
|
if (cpuid(1, 0, &eax, &ebx, &ecx, &edx) && ((edx >> 19) & 1)) {
|
|
fn = fb_flush_x86;
|
|
} else {
|
|
fn = fb_flush_x86_wbinvd;
|
|
}
|
|
#elif defined (__aarch64__)
|
|
fn = fb_flush_aarch64;
|
|
#elif defined (__riscv)
|
|
if (riscv_check_isa_extension("zicbom", NULL, NULL)) {
|
|
fn = fb_flush_riscv;
|
|
} else {
|
|
fn = fb_flush_riscv_nozicbom;
|
|
}
|
|
#elif defined (__loongarch64)
|
|
fn = fb_flush_loongarch64;
|
|
#endif
|
|
}
|
|
|
|
fn(base, length);
|
|
}
|