:: commit dc850a177329763abc37006b16ff67f42573758c

Mintsuki <mintsuki@protonmail.com> — 2026-03-20 09:28

parents: 165e05ff3d

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;
tab: 248 wrap: offon