lib/fb: Add framebuffer cache flush callbacks for all architectures
diff --git a/common/lib/fb.c b/common/lib/fb.c
index 05096b99..38ec277d 100644
--- a/common/lib/fb.c
+++ b/common/lib/fb.c
@@ -2,9 +2,11 @@
#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;
@@ -62,4 +64,105 @@ void fb_clear(struct fb_info *fb) {
}
}
}
+
+ 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);
+ 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);
+ 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);
+ 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);
}
diff --git a/common/lib/fb.h b/common/lib/fb.h
index fea4e28a..b34c6e1a 100644
--- a/common/lib/fb.h
+++ b/common/lib/fb.h
@@ -40,4 +40,6 @@ void fb_init(struct fb_info **ret, size_t *_fbs_count,
void fb_clear(struct fb_info *fb);
+void fb_flush(volatile void *base, size_t length);
+
#endif
diff --git a/common/lib/gterm.c b/common/lib/gterm.c
index d44b64ad..ca944723 100644
--- a/common/lib/gterm.c
+++ b/common/lib/gterm.c
@@ -475,40 +475,6 @@ static void generate_canvas(struct fb_info *fb) {
}
}
-#if defined (__riscv)
-__attribute__((target("arch=+zicbom")))
-static void riscv_flush_callback(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);
- for (uintptr_t ptr = start; ptr < end; ptr += cbom_block_size) {
- asm volatile("cbo.flush (%0)" :: "r"(ptr) : "memory");
- }
-}
-static void riscv_flush_callback_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 (__aarch64__)
-static void aarch64_flush_callback(volatile void *base, size_t length) {
- clean_dcache_poc((uintptr_t)base, (uintptr_t)base + length);
-}
-#endif
bool gterm_init(struct fb_info **_fbs, size_t *_fbs_count,
char *config, size_t width, size_t height) {
@@ -885,15 +851,7 @@ no_load_font:;
term->rows = min_rows;
flanterm_context_reinit(term);
-#if defined (__riscv)
- if (riscv_check_isa_extension("zicbom", NULL, NULL)) {
- flanterm_fb_set_flush_callback(term, riscv_flush_callback);
- } else {
- flanterm_fb_set_flush_callback(term, riscv_flush_callback_nozicbom);
- }
-#elif defined (__aarch64__)
- flanterm_fb_set_flush_callback(term, aarch64_flush_callback);
-#endif
+ flanterm_fb_set_flush_callback(term, (void *)fb_flush);
}
term_backend = GTERM;
diff --git a/common/lib/term.c b/common/lib/term.c
index cc9fc00e..c94c222f 100644
--- a/common/lib/term.c
+++ b/common/lib/term.c
@@ -298,6 +298,8 @@ void term_fallback(void) {
0,
FLANTERM_FB_ROTATE_0
);
+
+ flanterm_fb_set_flush_callback(terms[0], (void *)fb_flush);
}
return;
