fix: kernel concurrency, interrupt context, and user VA safety

This commit is contained in:
2026-08-01 15:43:38 +02:00
parent f222bf1f04
commit b1c55073c7
71 changed files with 2654 additions and 1092 deletions
+74 -10
View File
@@ -16,6 +16,8 @@ namespace gui {
class Framebuffer {
uint32_t* hw_fb;
uint32_t* hw_fb2; // second scanout buffer (page flip), nullptr if unsupported
uint32_t* hw_direct; // buffer known to be live after page-flip fallback
uint32_t* hw_mirror; // second possible live buffer when SURFLIVE is unavailable
uint32_t* back_buf;
int fb_width;
int fb_height;
@@ -66,18 +68,19 @@ class Framebuffer {
char user[64] = {};
if (montauk::getuser(user, sizeof(user)) > 0 && user[0]) {
auto doc = montauk::config::load_user(user, "display");
bool enabled = doc.get_bool("graphics.tear_free", true);
bool enabled = doc.get_bool("graphics.tear_free", false);
doc.destroy();
return enabled;
}
auto doc = montauk::config::load("display");
bool enabled = doc.get_bool("graphics.tear_free", true);
bool enabled = doc.get_bool("graphics.tear_free", false);
doc.destroy();
return enabled;
}
public:
Framebuffer() : hw_fb(nullptr), hw_fb2(nullptr), back_buf(nullptr),
Framebuffer() : hw_fb(nullptr), hw_fb2(nullptr), hw_direct(nullptr), hw_mirror(nullptr),
back_buf(nullptr),
fb_width(0), fb_height(0), fb_pitch(0), hw_next(1) {
montauk::abi::FbInfo info;
montauk::fb_info(&info);
@@ -87,14 +90,39 @@ public:
fb_pitch = (int)info.pitch;
hw_fb = (uint32_t*)montauk::fb_map();
hw_direct = hw_fb;
back_buf = (uint32_t*)montauk::alloc((uint64_t)fb_height * fb_pitch);
// Hardware page flipping: fb_map() maps the second scanout buffer
// directly after the first when the kernel supports flipping.
if (hw_fb && prefer_hardware_page_flip()
&& montauk::fb_flip(-1, 0) == 1) {
uint64_t pages = ((uint64_t)fb_height * fb_pitch + 0xFFF) / 0x1000;
hw_fb2 = (uint32_t*)((uint8_t*)hw_fb + pages * 0x1000);
// directly after the first when the kernel supports flipping. Acquire
// the scanout before choosing an off-screen buffer: another fullscreen
// process (notably login.elf) may have left either buffer live.
bool flip_supported = hw_fb && montauk::fb_flip(-1, 0) == 1;
uint64_t pages = ((uint64_t)fb_height * fb_pitch + 0xFFF) / 0x1000;
uint32_t* second = flip_supported
? (uint32_t*)((uint8_t*)hw_fb + pages * 0x1000) : nullptr;
if (flip_supported && prefer_hardware_page_flip()) {
int64_t front = montauk::fb_flip(-2, 0);
if (front == 0 || front == 1) {
hw_fb2 = second;
hw_next = (int)front ^ 1;
} else {
// If the live register cannot be trusted even at startup,
// avoid flipping and update both possible scanout surfaces.
hw_mirror = second;
}
} else if (flip_supported) {
// A direct-copy client must make buffer 0 live before it starts
// drawing there; the previous fullscreen client may have left
// buffer 1 scanning out.
if (montauk::fb_flip(0, 1) != 0) {
int64_t front = montauk::fb_flip(-2, 0);
if (front == 1) {
hw_direct = second;
} else if (front != 0) {
hw_mirror = second;
}
}
}
}
@@ -280,7 +308,7 @@ public:
// currently OFF screen, then ask the display engine to present it at
// the next vblank (tear-free). Without it, fall back to copying
// straight into the visible framebuffer.
uint32_t* dst_fb = hw_fb;
uint32_t* dst_fb = hw_direct;
if (hw_fb2) dst_fb = (hw_next == 1) ? hw_fb2 : hw_fb;
// Copy back buffer to hardware framebuffer, row by row (pitch may differ)
@@ -289,13 +317,49 @@ public:
uint32_t* src = (uint32_t*)((uint8_t*)back_buf + y * fb_pitch);
uint32_t* dst = (uint32_t*)((uint8_t*)dst_fb + y * fb_pitch);
montauk::memcpy(dst, src, row_bytes);
if (hw_mirror) {
uint32_t* mirror = (uint32_t*)((uint8_t*)hw_mirror + y * fb_pitch);
montauk::memcpy(mirror, src, row_bytes);
}
}
if (hw_mirror) asm volatile("sfence" ::: "memory");
if (hw_fb2) {
// Wait for the latch (vsync): the buffer we just left becomes
// safe to draw into only once the new one is actually scanning.
if (montauk::fb_flip(hw_next, 1) == hw_next) {
asm volatile("sfence" ::: "memory");
int64_t presented = montauk::fb_flip(hw_next, 1);
if (presented == hw_next) {
hw_next ^= 1;
} else {
// Do not retry a faulty page-flip path forever: that turns a
// transient latch failure into an apparently frozen desktop.
// Keep drawing directly into whichever buffer hardware says
// is live for the rest of this process. If SURFLIVE itself is
// unavailable, request buffer 0 without another blocking wait.
int64_t front = montauk::fb_flip(-2, 0);
if (front == 0 || front == 1) {
hw_direct = (front == 1) ? hw_fb2 : hw_fb;
} else {
(void)montauk::fb_flip(0, 0);
hw_direct = hw_fb;
hw_mirror = hw_fb2;
}
hw_fb2 = nullptr;
// The frame above was copied to the requested (possibly
// off-screen) buffer. Publish it once more to the live direct
// buffer so the cursor and desktop resume immediately.
for (int y = 0; y < fb_height; y++) {
uint32_t* src = (uint32_t*)((uint8_t*)back_buf + y * fb_pitch);
uint32_t* dst = (uint32_t*)((uint8_t*)hw_direct + y * fb_pitch);
montauk::memcpy(dst, src, row_bytes);
if (hw_mirror) {
uint32_t* mirror = (uint32_t*)((uint8_t*)hw_mirror + y * fb_pitch);
montauk::memcpy(mirror, src, row_bytes);
}
}
asm volatile("sfence" ::: "memory");
}
}
}