:: limine / common / lib / fb.c 5.9 KB raw

1
#include <stddef.h>
2
#include <stdint.h>
3
#include <stdbool.h>
4
#include <lib/fb.h>
5
#include <lib/misc.h>
6
#include <drivers/vbe.h>
7
#include <drivers/gop.h>
8
#include <mm/pmm.h>
9
#include <mm/mtrr.h>
10
#include <mm/efi_pt.h>
11
#include <sys/cpu.h>
12
13
struct fb_info *fb_fbs;
14
size_t fb_fbs_count = 0;
15
16
void fb_init(struct fb_info **ret, size_t *_fbs_count,
17
             uint64_t target_width, uint64_t target_height, uint16_t target_bpp,
18
             bool preserve_screen, bool keep_wc) {
19
    if (quiet) {
20
        preserve_screen = true;
21
    }
22
23
#if defined (BIOS)
24
    *ret = ext_mem_alloc(sizeof(struct fb_info));
25
    if (init_vbe(*ret, target_width, target_height, target_bpp)) {
26
        *_fbs_count = 1;
27
28
        (*ret)->edid = get_edid_info();
29
        size_t mode_count;
30
        (*ret)->mode_list = vbe_get_mode_list(&mode_count);
31
        (*ret)->mode_count = mode_count;
32
    } else {
33
        *_fbs_count = 0;
34
        pmm_free(*ret, sizeof(struct fb_info));
35
    }
36
#elif defined (UEFI)
37
    init_gop(ret, _fbs_count, target_width, target_height, target_bpp);
38
#endif
39
40
    fb_fbs = *ret;
41
    fb_fbs_count = *_fbs_count;
42
43
    // Map the framebuffers as write-combining so the clear (and, when kept,
44
    // terminal rendering) is fast. keep_wc leaves it active for the caller.
45
    bool want_wc = keep_wc || !preserve_screen;
46
47
#if defined (__i386__) || defined (__x86_64__)
48
    if (want_wc) {
49
        for (size_t i = 0; i < *_fbs_count; i++) {
50
            uint64_t fb_size = (uint64_t)(*ret)[i].framebuffer_pitch
51
                             * (*ret)[i].framebuffer_height;
52
            if (fb_size == 0) {
53
                continue;
54
            }
55
#if defined (__x86_64__) && defined (UEFI)
56
            efi_pt_set_fb_wc((*ret)[i].framebuffer_addr, fb_size);
57
#else
58
            mtrr_wc_add_fb_range((*ret)[i].framebuffer_addr, fb_size);
59
#endif
60
        }
61
    }
62
#endif
63
64
    if (!preserve_screen) {
65
        for (size_t i = 0; i < *_fbs_count; i++) {
66
            fb_clear(&(*ret)[i]);
67
        }
68
    }
69
70
#if defined (__i386__) || defined (__x86_64__)
71
    if (want_wc && !keep_wc) {
72
#if defined (__x86_64__) && defined (UEFI)
73
        efi_pt_restore();
74
#else
75
        mtrr_restore();
76
#endif
77
    }
78
#else
79
    (void)want_wc;
80
#endif
81
}
82
83
void fb_clear(struct fb_info *fb) {
84
    for (size_t y = 0; y < fb->framebuffer_height; y++) {
85
        switch (fb->framebuffer_bpp) {
86
            case 32: {
87
                uint32_t *fbp = (void *)(uintptr_t)fb->framebuffer_addr;
88
                size_t row = (y * fb->framebuffer_pitch) / 4;
89
                for (size_t x = 0; x < fb->framebuffer_width; x++) {
90
                    fbp[row + x] = 0;
91
                }
92
                break;
93
            }
94
            case 16: {
95
                uint16_t *fbp = (void *)(uintptr_t)fb->framebuffer_addr;
96
                size_t row = (y * fb->framebuffer_pitch) / 2;
97
                for (size_t x = 0; x < fb->framebuffer_width; x++) {
98
                    fbp[row + x] = 0;
99
                }
100
                break;
101
            }
102
            default: {
103
                uint8_t *fbp = (void *)(uintptr_t)fb->framebuffer_addr;
104
                size_t row = y * fb->framebuffer_pitch;
105
                size_t row_bytes = fb->framebuffer_width * (fb->framebuffer_bpp / 8);
106
                for (size_t x = 0; x < row_bytes; x++) {
107
                    fbp[row + x] = 0;
108
                }
109
                break;
110
            }
111
        }
112
    }
113
114
    fb_flush((volatile void *)(uintptr_t)fb->framebuffer_addr,
115
             (size_t)fb->framebuffer_pitch * fb->framebuffer_height);
116
}
117
118
#if defined (__aarch64__)
119
static void fb_flush_aarch64(volatile void *base, size_t length) {
120
    clean_dcache_poc((uintptr_t)base, (uintptr_t)base + length);
121
}
122
#elif defined (__riscv)
123
__attribute__((target("arch=+zicbom")))
124
static void fb_flush_riscv(volatile void *base, size_t length) {
125
    const size_t cbom_block_size = 0x40;
126
    uintptr_t start = ALIGN_DOWN((uintptr_t)base, cbom_block_size);
127
    uintptr_t end = ALIGN_UP((uintptr_t)(base + length), cbom_block_size, panic(false, "fb: Alignment overflow"));
128
    for (uintptr_t ptr = start; ptr < end; ptr += cbom_block_size) {
129
        asm volatile("cbo.flush (%0)" :: "r"(ptr) : "memory");
130
    }
131
    asm volatile ("fence rw, rw" ::: "memory");
132
}
133
134
static void fb_flush_riscv_nozicbom(volatile void *base, size_t length) {
135
    (void)base;
136
    (void)length;
137
138
    // Without Zicbom, there is no portable instruction to flush dirty cache lines.
139
    // Read through a dedicated eviction buffer to create cache pressure and displace
140
    // dirty framebuffer lines. 128 KB covers typical RISC-V L1 D-caches (32-64 KB).
141
    static volatile uint8_t *eviction_buf = NULL;
142
    #define EVICTION_BUF_SIZE (128 * 1024)
143
    if (eviction_buf == NULL) {
144
        eviction_buf = ext_mem_alloc(EVICTION_BUF_SIZE);
145
    }
146
147
    volatile uint64_t *p = (volatile uint64_t *)eviction_buf;
148
    for (size_t i = 0; i < EVICTION_BUF_SIZE / sizeof(uint64_t); i += (64 / sizeof(uint64_t))) {
149
        (void)p[i];
150
    }
151
    asm volatile ("fence rw, rw" ::: "memory");
152
}
153
#elif defined (__loongarch64)
154
static void fb_flush_loongarch64(volatile void *base, size_t length) {
155
    // cacop Hit_Writeback_Inv_LEAF0 = 0x10 (D-cache L1 writeback+invalidate)
156
    const size_t clsz = 64;
157
    uintptr_t start = ALIGN_DOWN((uintptr_t)base, clsz);
158
    uintptr_t end = ALIGN_UP((uintptr_t)base + length, clsz, panic(false, "fb: Alignment overflow"));
159
    for (uintptr_t ptr = start; ptr < end; ptr += clsz) {
160
        asm volatile ("cacop 0x10, %0, 0" :: "r"(ptr) : "memory");
161
    }
162
}
163
#endif
164
165
void fb_flush(volatile void *base, size_t length) {
166
    typedef void (*flush_fn)(volatile void *, size_t);
167
    static flush_fn fn = NULL;
168
169
    if (fn == NULL) {
170
#if defined (__aarch64__)
171
        fn = fb_flush_aarch64;
172
#elif defined (__riscv)
173
        if (riscv_check_isa_extension("zicbom", NULL, NULL)) {
174
            fn = fb_flush_riscv;
175
        } else {
176
            fn = fb_flush_riscv_nozicbom;
177
        }
178
#elif defined (__loongarch64)
179
        fn = fb_flush_loongarch64;
180
#endif
181
    }
182
183
    if (fn != NULL) {
184
        fn(base, length);
185
    }
186
}
tab: 248 wrap: offon