Skip to content

Commit

Permalink
Framebuffer rendering, x86 serial fix
Browse files Browse the repository at this point in the history
  • Loading branch information
marv7000 committed Aug 15, 2024
1 parent 35a5825 commit c0fd647
Show file tree
Hide file tree
Showing 11 changed files with 239 additions and 81 deletions.
3 changes: 2 additions & 1 deletion include/menix/log.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
#define kassert(expr, msg) \
if (!(expr)) \
{ \
kmesg("Assertion failed: " msg "\nExpression:\n\t" #expr "\n" __FILE__ ":" __PASTE_STR(__LINE__) "\n"); \
kmesg("Assertion failed: " msg "\nExpression:\n " #expr "\n" __FILE__ ":" __PASTE_STR(__LINE__) "\n"); \
ktrace(); \
kabort(); \
}

typedef struct ATTR(packed) StackFrame
Expand Down
12 changes: 11 additions & 1 deletion include/menix/video/fb.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ typedef struct
u32 width, height; // Resolution of the visible frame in pixels.
u32 v_width, v_height; // Resolution of the virtual frame in pixels.
u32 v_off_x, v_off_y; // Offset from virtual to visible resolution.
u8 bpp; // Amount of bits per pixel.
u8 cpp; // Amount of bytes per pixel.
FbColorBits red, green, blue, alpha; // Bitfields for each part of a pixel.
} FbModeInfo;

Expand All @@ -50,6 +50,14 @@ typedef struct
u32 width, height; // Width and height of the area to copy.
} FbCopyRegion;

// Arguments passed to `FbFuncs.draw_region`.
typedef struct
{
u32 x_src, y_src; // Top left corner of the framebuffer to draw to.
u32 width, height; // Width and height of the image to draw.
const u8* data; // Pointer to the image data.
} FbDrawRegion;

typedef struct FrameBuffer FrameBuffer;
// Callback functions for modifying a framebuffer.
typedef struct
Expand All @@ -64,6 +72,8 @@ typedef struct
void (*fill_region)(FrameBuffer* fb, FbFillRegion* args);
// Copies a rectangular region from one location to another.
void (*copy_region)(FrameBuffer* fb, FbCopyRegion* args);
// Draws an image to a location.
void (*draw_region)(FrameBuffer* fb, FbDrawRegion* args);
} FbFuncs;

// Stores information about a framebuffer.
Expand Down
6 changes: 3 additions & 3 deletions include/menix/video/fb_default.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
#pragma once
#include <menix/video/fb.h>

#define FB_DEFAULT_FUNCS \
{ \
}
void fb_default_fill_region(FrameBuffer* fb, FbFillRegion* args);
void fb_default_copy_region(FrameBuffer* fb, FbCopyRegion* args);
void fb_default_draw_region(FrameBuffer* fb, FbDrawRegion* args);
12 changes: 0 additions & 12 deletions kernel/arch/x86/arch.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,6 @@ void arch_early_init()

void arch_init(BootInfo* info)
{
// Initialize physical and virtual memory managers.
pm_init(info->phys_map, info->memory_map, info->mm_num);
vm_init(info->phys_map, info->kernel_phys, info->memory_map, info->mm_num);

// Print memory map.
kmesg("Physical memory map:\n");
for (usize i = 0; i < info->mm_num; i++)
{
kmesg(" [%u] 0x%p - 0x%p [%s]\n", i, info->memory_map[i].address,
info->memory_map[i].address + info->memory_map[i].length,
(info->memory_map[i].usage == PhysMemoryUsage_Free) ? "Usable" : "Reserved");
}
}

void arch_shutdown(BootInfo* info)
Expand Down
17 changes: 16 additions & 1 deletion kernel/arch/x86/io/serial.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@

#define TRANSMIT_FREE (arch_x86_read8(COM1_BASE + LINE_STATUS_REG) & 0x20)

// If the COM port works or not.
static bool can_use_serial = false;

void serial_initialize()
{
arch_x86_write8(COM1_BASE + INT_ENABLE_REG, 0x00); // Disable interrupts
Expand All @@ -26,13 +29,25 @@ void serial_initialize()
arch_x86_write8(COM1_BASE + LINE_CTRL_REG, 0x03); // 8 bits, no parity, one stop bit
arch_x86_write8(COM1_BASE + INT_ID_FIFO_CTRL_REG, 0xC7); // Enable FIFO, clear them, with 14-byte threshold
arch_x86_write8(COM1_BASE + MODEM_CTRL_REG, 0x0B); // IRQs enabled, RTS/DSR set
arch_x86_write8(COM1_BASE + MODEM_CTRL_REG, 0x1E); // Set to loopback mode for testing.

arch_x86_write8(COM1_BASE + DATA_REG, 0xAE); // Send a test byte.
if (arch_x86_read8(COM1_BASE + DATA_REG) == 0xAE) // If we get the same back, we're ready.
{
can_use_serial = true;
arch_x86_write8(COM1_BASE + MODEM_CTRL_REG, 0x0F); // Set back to normal operation mode.
}
}

void serial_putchar(char c)
{
// Wait for transmit to be empty.
if (!can_use_serial)
return;

// Wait until we can send things.
while (TRANSMIT_FREE == false)
;

switch (c)
{
case '\0': break; // Don't transmit null terminators.
Expand Down
13 changes: 11 additions & 2 deletions kernel/arch/x86/vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,17 @@ void vm_init(void* phys_base, PhysAddr kernel_base, PhysMemory* mem_map, usize n

// TODO: We could probably pre-allocate the upper half of pages, i.e. index 256..511

// Map the lower 256 GiB of physical space.
for (usize cur = 0; cur < 256UL * GiB; cur += 2UL * MiB)
// Map all physical space.
// Check for the highest usable physical memory address, so we know how much memory to map.
usize highest = 0;
for (usize i = 0; i < num_entries; i++)
{
const usize region_end = mem_map[i].address + mem_map[i].length;
if (region_end > highest)
highest = region_end;
}

for (usize cur = 0; cur < highest; cur += 2UL * MiB)
kassert(vm_arch_map_page(kernel_map, cur, phys_addr + cur, PAGE_PRESENT | PAGE_READ_WRITE, PageSize_2MiB),
"Unable to map lower memory!\n");

Expand Down
108 changes: 71 additions & 37 deletions kernel/boot/boot_limine/limine_entry.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#include <string.h>

#include "limine.h"
#include "menix/video/fb.h"
#include "menix/video/fb_default.h"

#define LIMINE_REQUEST(request, tag, rev) \
ATTR(used, section(".requests")) static volatile struct limine_##request request = { \
Expand All @@ -30,7 +30,7 @@ LIMINE_REQUEST(memmap_request, LIMINE_MEMMAP_REQUEST, 0); // Get memory map
LIMINE_REQUEST(hhdm_request, LIMINE_HHDM_REQUEST, 0); // Directly map 32-bit physical space.
LIMINE_REQUEST(kernel_address_request, LIMINE_KERNEL_ADDRESS_REQUEST, 0); // Get the physical kernel address.
LIMINE_REQUEST(kernel_file_request, LIMINE_KERNEL_FILE_REQUEST, 0); // For debug symbols.
LIMINE_REQUEST(framebuffer_request, LIMINE_FRAMEBUFFER_REQUEST, 0); // Initial console frame buffer.
LIMINE_REQUEST(framebuffer_request, LIMINE_FRAMEBUFFER_REQUEST, 1); // Initial console frame buffer.
LIMINE_REQUEST(module_request, LIMINE_MODULE_REQUEST, 0); // Get all other modules, logo.
#ifdef CONFIG_acpi
LIMINE_REQUEST(rsdp_request, LIMINE_RSDP_REQUEST, 0); // Get ACPI RSDP table if enabled.
Expand All @@ -45,26 +45,6 @@ void kernel_boot()

BootInfo info = {0};

// Get early framebuffer.
kassert(framebuffer_request.response, "Unable to get a framebuffer!\n");
FrameBuffer buffer = {0};
const struct limine_framebuffer* buf = framebuffer_request.response->framebuffers[0];
// Construct a simple framebuffer. This will get overridden by a driver loaded at a later stage.
buffer.info.mmio_base = buf->address;
buffer.mode.width = buf->width;
buffer.mode.height = buf->height;
buffer.mode.bpp = buf->bpp;

// If no early framebuffer has been set previously, do it now.
if (fb_get_early() == NULL)
{
fb_set_early(&buffer);
terminal_init();
}

kmesg("Early framebuffer: Address = 0x%p, Resolution = %ux%ux%u\n", buffer.info.mmio_base, buffer.mode.width,
buffer.mode.height, buffer.mode.bpp);

// Get the memory map.
kassert(memmap_request.response, "Unable to get memory map!\n");
struct limine_memmap_response* const res = memmap_request.response;
Expand All @@ -91,18 +71,68 @@ void kernel_boot()
default: map[i].usage = PhysMemoryUsage_Unknown; break;
}
}

// Make sure the first 4 GiB are identity mapped so we can write to "physical" memory.
kassert(hhdm_request.response, "Unable to get HHDM response!\n");
kmesg("HHDM offset: 0x%p\n", hhdm_request.response->offset);
kassert(kernel_address_request.response, "Unable to get kernel address info!\n");
kmesg("Kernel loaded at: 0x%p (0x%p)\n", kernel_address_request.response->virtual_base,
kernel_address_request.response->physical_base);

// Initialize virtual memory using the memory map we got.
info.kernel_phys = (PhysAddr)kernel_address_request.response->physical_base;
info.kernel_virt = (void*)kernel_address_request.response->virtual_base;
info.phys_map = (void*)hhdm_request.response->offset;

// Initialize physical and virtual memory managers.
pm_init(info.phys_map, info.memory_map, info.mm_num);
vm_init(info.phys_map, info.kernel_phys, info.memory_map, info.mm_num);

// Get early framebuffer.
FrameBuffer buffer = {0};

if (framebuffer_request.response == NULL)
kmesg("Unable to get a framebuffer!\n");
else if (framebuffer_request.response->framebuffer_count > 0)
{
const struct limine_framebuffer* buf = framebuffer_request.response->framebuffers[0];
// Construct a simple framebuffer. This will get overridden by a driver loaded at a later stage.
buffer.info.mmio_base = buf->address;
buffer.mode.width = buf->width;
buffer.mode.height = buf->height;
buffer.mode.cpp = buf->bpp / 8;

buffer.funcs.copy_region = fb_default_copy_region;
buffer.funcs.fill_region = fb_default_fill_region;
buffer.funcs.draw_region = fb_default_draw_region;

// If no early framebuffer has been set previously, do it now.
if (fb_get_early() == NULL)
{
fb_set_early(&buffer);
terminal_init();
}

kmesg("Early framebuffer: Address = 0x%p, Resolution = %ux%ux%u\n", buffer.info.mmio_base, buffer.mode.width,
buffer.mode.height, buffer.mode.cpp * 8);

// Print available video modes.
for (usize i = 0; i < buf->mode_count; i++)
{
const struct limine_video_mode* mode = buf->modes[i];
kmesg(" [%i] %ux%ux%u\n", i, mode->width, mode->height, mode->bpp);
}
}

// Print memory map.
kmesg("Physical memory map:\n");
for (usize i = 0; i < info.mm_num; i++)
{
kmesg(" [%u] 0x%p - 0x%p [%s]\n", i, info.memory_map[i].address,
info.memory_map[i].address + info.memory_map[i].length,
(info.memory_map[i].usage == PhysMemoryUsage_Free) ? "Usable" : "");
}
kmesg("HHDM offset: 0x%p\n", hhdm_request.response->offset);
kmesg("Kernel loaded at: 0x%p (0x%p)\n", kernel_address_request.response->virtual_base,
kernel_address_request.response->physical_base);

#ifdef CONFIG_acpi
// Get ACPI RSDP.
kassert(rsdp_request.response, "Unable to get ACPI RSDP!\n");
Expand Down Expand Up @@ -130,20 +160,24 @@ void kernel_boot()
info.cmd = kernel_res->kernel_file->cmdline;

// Get modules.
kassert(module_request.response, "Unable to get modules!\n");
kmesg("Got modules:\n");
const struct limine_module_response* module_res = module_request.response;
BootFile files[module_res->module_count]; // TODO: Convert to kalloc'ed memory.
for (usize i = 0; i < module_res->module_count; i++)
if (module_request.response == NULL)
kmesg("Unable to get modules, or none were provided!\n");
else
{
files[i].address = module_res->modules[i]->address;
files[i].size = module_res->modules[i]->size;
files[i].path = module_res->modules[i]->path;
kmesg(" [%i] Address = 0x%p, Size = 0x%p, Path = \"%s\"\n", i, files[i].address, files[i].size,
files[i].path);
kmesg("Got modules:\n");
const struct limine_module_response* module_res = module_request.response;
BootFile files[module_res->module_count];
for (usize i = 0; i < module_res->module_count; i++)
{
files[i].address = module_res->modules[i]->address;
files[i].size = module_res->modules[i]->size;
files[i].path = module_res->modules[i]->path;
kmesg(" [%i] Address = 0x%p, Size = 0x%p, Path = \"%s\"\n", i, files[i].address, files[i].size,
files[i].path);
}
info.file_num = module_res->module_count;
info.files = files;
}
info.file_num = module_res->module_count;
info.files = files;

arch_init(&info);
kernel_main(&info);
Expand Down
Loading

0 comments on commit c0fd647

Please sign in to comment.