Skip to main content
The framebuffer driver provides low-level pixel manipulation for graphics output in Aeolos. It implements a linear framebuffer with double buffering to prevent screen tearing.

Overview

The framebuffer is initialized from bootloader-provided information (Stivale2 protocol) and mapped into the kernel’s virtual address space. All drawing operations are performed on a back buffer, which can be swapped to the front buffer for display.

Data Structures

Framebuffer Information

The fb_info structure contains the framebuffer configuration:
typedef struct {
    uint8_t* addr;      // Virtual address of framebuffer
    uint32_t width;     // Width in pixels
    uint32_t height;    // Height in pixels
    uint32_t pitch;     // Bytes per scanline
} fb_info;
The pitch field represents the number of bytes per scanline, which may be larger than width * bytes_per_pixel due to hardware alignment requirements.

API Functions

fb_init

Initializes the framebuffer driver and sets up double buffering.
void fb_init(stv2_struct_tag_fb* t);
Implementation details from fb.c:28-44:
void fb_init(stv2_struct_tag_fb* t)
{
    fb.addr = (uint8_t*)PHYS_TO_VIRT(t->fb_addr);
    fb.width = t->fb_width;
    fb.height = t->fb_height;
    fb.pitch = t->fb_pitch;
    klog_info("framebuffer resolution is %dx%d\n", fb.width, fb.height);

    /* mapping the framebuffer
     * also use write-combining cache
    */
    uint64_t fbsize = NUM_PAGES(fb.pitch * fb.height);
    vmm_map(NULL, (uint64_t)fb.addr, VIRT_TO_PHYS(fb.addr), fbsize, VMM_FLAGS_DEFAULT | VMM_FLAG_WRITECOMBINE);

    // initialize double buffering
    backbuffer = kmalloc(fb.pitch * fb.height);
    klog_ok("done\n");
}
The framebuffer is mapped with write-combining cache mode (VMM_FLAG_WRITECOMBINE) to improve graphics performance by allowing the CPU to batch writes to video memory.

fb_putpixel

Draws a pixel at the specified coordinates with the given color.
void fb_putpixel(uint32_t x, uint32_t y, uint32_t color);
Implementation from fb.c:17-20:
void fb_putpixel(uint32_t x, uint32_t y, uint32_t color)
{
    ((uint32_t*)(backbuffer + (fb.pitch * y)))[x] = color;
}
The function writes to the back buffer at the calculated offset. Color values are 32-bit RGB.

fb_getpixel

Reads the color value of a pixel at the specified coordinates.
uint32_t fb_getpixel(uint32_t x, uint32_t y);
Implementation from fb.c:22-25:
uint32_t fb_getpixel(uint32_t x, uint32_t y)
{
    return ((uint32_t*)(backbuffer + (fb.pitch * y)))[x];
}

fb_getinfo

Returns a pointer to the framebuffer information structure.
const fb_info* fb_getinfo();
Implementation from fb.c:12-15:
const fb_info* fb_getinfo()
{
    return &fb;
}

fb_swap_buffers

Copies the back buffer to the front buffer, making drawn content visible.
void fb_swap_buffers();
Implementation from fb.c:48-51:
void fb_swap_buffers()
{
    memcpy(backbuffer, fb.addr, fb.pitch * fb.height);
}
Note the unusual direction: this copies from backbuffer to fb.addr (the display buffer). To display changes, you must call this function after drawing.

Double Buffering

The framebuffer driver implements double buffering to prevent screen tearing:
  1. Back Buffer - Allocated in kernel memory where all drawing operations occur
  2. Front Buffer - The actual framebuffer memory mapped to the display
The workflow is:

Memory Layout

The framebuffer uses a linear memory model:
Offset = (y * pitch) + (x * bytes_per_pixel)
For 32-bit color (4 bytes per pixel):
Pixel address = base + (y * pitch) + (x * 4)

Source Files

kernel/dev/fb/fb.h

Build docs developers (and LLMs) love