From d7f5635b7cb693cca8b796fa8573b67525b8b32f Mon Sep 17 00:00:00 2001
From: Amelia Coutard <eliottulio.coutard@gmail.com>
Date: Thu, 5 May 2022 20:06:46 +0200
Subject: [PATCH] Added a basic framebuffer

---
 src/boot.S         |  8 +++++
 src/fb.cpp         | 53 +++++++++++++++++++++++++++
 src/fb.hpp         | 40 +++++++++++++++++++++
 src/kernel.cpp     | 24 +++++++++++++
 src/multiboot2.hpp | 89 ++++++++++++++++++++++++++++++++++++++++------
 5 files changed, 203 insertions(+), 11 deletions(-)
 create mode 100644 src/fb.cpp
 create mode 100644 src/fb.hpp

diff --git a/src/boot.S b/src/boot.S
index 53a9162..2be4f42 100644
--- a/src/boot.S
+++ b/src/boot.S
@@ -9,6 +9,14 @@ multiboot_header_start:
 	.int multiboot2_arch_i386_32bit
 	.int multiboot_header_end - multiboot_header_start
 	.int multiboot2_checksum(multiboot2_arch_i386_32bit, multiboot_header_end - multiboot_header_start)
+	/* Framebuffer tag: */
+	.align 8
+	.short 5
+	.short 0
+	.int 20
+	.int 0
+	.int 0
+	.int 32
 	/* End tag: */
 	.align 8
 	.short 0
diff --git a/src/fb.cpp b/src/fb.cpp
new file mode 100644
index 0000000..b4d6e16
--- /dev/null
+++ b/src/fb.cpp
@@ -0,0 +1,53 @@
+#include "serial.hpp"
+#include "fb.hpp"
+
+os::color os::operator*(color c1, color c2) {
+	return {
+		.r = uint8_t(int(c1.r) * int(c2.r) / 256),
+		.g = uint8_t(int(c1.g) * int(c2.g) / 256),
+		.b = uint8_t(int(c1.b) * int(c2.b) / 256),
+	};
+}
+
+os::framebuffer::framebuffer(uint64_t addr, uint32_t pitch, uint32_t width, uint32_t height, uint8_t bpp, uint8_t type, const uint8_t* color_info) {
+	address = phys_ptr<volatile uint8_t>(addr);
+	this->pitch = pitch;
+	this->width = width;
+	this->height = height;
+	this->bpp = bpp;
+	if (type != 1) {
+		os::println("Error: framebuffer not in direct RGB mode.");
+		os::halt();
+	}
+	if (bpp != 32) {
+		os::println("Error: framebuffer bpp != 32.");
+		os::halt();
+	}
+
+	r_pos =  color_info[0];
+	r_mask = (1 << color_info[1]) - 1;
+	g_pos =  color_info[2];
+	g_mask = (1 << color_info[3]) - 1;
+	b_pos =  color_info[4];
+	b_mask = (1 << color_info[5]) - 1;
+}
+void os::framebuffer::putpixel(size_t x, size_t y, color c) {
+	*reinterpret_cast<volatile uint32_t*>(&address[pitch * y + x * bpp / 8]) = color_to_data(c);
+}
+void os::framebuffer::clear(color c) {
+	for (size_t x = 0; x < width; x++) {
+		for (size_t y = 0; y < height; y++) {
+			putpixel(x, y, c);
+		}
+	}
+}
+size_t os::framebuffer::get_width() const {
+	return width;
+}
+size_t os::framebuffer::get_height() const {
+	return height;
+}
+
+uint32_t os::framebuffer::color_to_data(color c) const {
+return ((uint32_t(c.r) & r_mask) << r_pos) | ((uint32_t(c.g) & g_mask) << g_pos) | ((uint32_t(c.b) & b_mask) << b_pos);
+}
diff --git a/src/fb.hpp b/src/fb.hpp
new file mode 100644
index 0000000..15f6e9a
--- /dev/null
+++ b/src/fb.hpp
@@ -0,0 +1,40 @@
+#pragma once
+
+#include <stdint.h>
+#include "utils.hpp"
+
+namespace os {
+
+struct color {
+	uint8_t r;
+	uint8_t g;
+	uint8_t b;
+};
+color operator*(color c1, color c2);
+
+class framebuffer {
+public:
+	framebuffer() = default;
+	framebuffer(uint64_t addr, uint32_t pitch, uint32_t width, uint32_t height, uint8_t bpp, uint8_t type, const uint8_t* color_info);
+	void putpixel(size_t x, size_t y, color c);
+	void clear(color c);
+	size_t get_width() const;
+	size_t get_height() const;
+
+private:
+	uint32_t color_to_data(color c) const;
+
+	phys_ptr<volatile uint8_t> address = nullptr;
+	size_t pitch;
+	size_t width;
+	size_t height;
+	uint8_t bpp;
+	uint8_t r_pos;
+	uint8_t r_mask;
+	uint8_t g_pos;
+	uint8_t g_mask;
+	uint8_t b_pos;
+	uint8_t b_mask;
+};
+
+}
diff --git a/src/kernel.cpp b/src/kernel.cpp
index f6415ae..784859f 100644
--- a/src/kernel.cpp
+++ b/src/kernel.cpp
@@ -1,5 +1,6 @@
 #include "multiboot2.hpp"
 #include "paging.hpp"
+#include "fb.hpp"
 #include "utils.hpp"
 #include "serial.hpp"
 
@@ -14,11 +15,34 @@ extern "C" void kmain(unsigned long magic, os::phys_ptr<const multiboot2::info_s
 	asm("mov $test_ram - 0xFFFFFFFF80000000,%0" : "=ri"(ram_ptr));
 	os::paging::page_allocator page_allocator(ram_ptr);
 
+	os::framebuffer framebuffer;
+
 	for (auto it = multiboot2::next(info); it->type != multiboot2::info::type_t::end; it = multiboot2::next(it)) {
 		switch (it->type) {
+		case multiboot2::info::type_t::framebuffer_info:
+			framebuffer = os::framebuffer(
+				multiboot2::framebuffer_addr(it),
+				multiboot2::framebuffer_pitch(it),
+				multiboot2::framebuffer_width(it),
+				multiboot2::framebuffer_height(it),
+				multiboot2::framebuffer_bpp(it),
+				multiboot2::framebuffer_type(it),
+				multiboot2::color_info(it)
+			);
+			break;
 		default: break;
 		}
 	}
 
+	for (size_t x = 0; x < framebuffer.get_width(); x++) {
+		for (size_t y = 0; y < framebuffer.get_height(); y++) {
+			framebuffer.putpixel(x, y, {
+				.r = uint8_t(x * 256 / framebuffer.get_width()),
+				.g = 0,
+				.b = uint8_t(y * 256 / framebuffer.get_height()),
+			});
+		}
+	}
+
 	os::halt();
 }
diff --git a/src/multiboot2.hpp b/src/multiboot2.hpp
index 53e7135..41091ac 100644
--- a/src/multiboot2.hpp
+++ b/src/multiboot2.hpp
@@ -1,29 +1,96 @@
 #pragma once
 
 #ifdef __cplusplus
-namespace multiboot2 {
-#endif
+#	include <stdint.h>
+#	include "utils.hpp"
+	namespace multiboot2 {
+#endif // __cplusplus
 
 #ifdef __cplusplus
-	constexpr unsigned int magic = 0xE85250D6;
+	constexpr uint32_t magic = 0xE85250D6;
 #else
 #	define multiboot2_magic 0xE85250D6
-#endif
+#endif // __cplusplus
 
 #ifdef __cplusplus
-	constexpr unsigned int arch_i386_32bit = 0;
-	constexpr unsigned int arch_mips_32bit = 4;
+	constexpr uint32_t arch_i386_32bit = 0;
+	constexpr uint32_t arch_mips_32bit = 4;
 #else
 #	define multiboot2_arch_i386_32bit 0
 #	define multiboot2_arch_mips_32bit 4
-#endif
+#endif // __cplusplus
 
 #ifdef __cplusplus
-	constexpr unsigned int checksum(unsigned int arch, unsigned int length) { return -(magic + arch + length); }
+	constexpr uint32_t checksum(uint32_t arch, uint32_t length) { return -(magic + arch + length); }
 #else
 #	define multiboot2_checksum(arch, length) -(multiboot2_magic + (arch) + (length))
-#endif
+#endif // __cplusplus
 
 #ifdef __cplusplus
-}
-#endif
+	struct __attribute__((packed)) info_start {
+		uint32_t total_size;
+		uint32_t reserved;
+	};
+	struct __attribute__((packed)) info {
+		enum class type_t : uint32_t {
+			end = 0,
+			basic_memory_info = 4,
+			bios_boot_device = 5,
+			boot_command_line = 1,
+			modules = 3,
+			elf_symbols = 9,
+			memory_map = 6,
+			boot_loader_name = 2,
+			apm_table = 10,
+			vbe_info = 7,
+			framebuffer_info = 8,
+			efi_32bit_system_table_pointer = 11,
+			efi_64bit_system_table_pointer = 12,
+			smbios_tables = 13,
+			acpi_old_rsdp = 14,
+			acpi_new_rsdp = 15,
+			networking_info = 16,
+			efi_memory_map = 17,
+			efi_boot_services_not_terminated = 18,
+			efi_32bit_image_handle_pointer = 19,
+			efi_64bit_image_handle_pointer = 20,
+			image_load_base_physical_address = 21,
+		};
+		type_t type;
+		uint32_t size;
+		uint8_t rest[];
+	};
+
+	inline os::phys_ptr<const info> next(os::phys_ptr<const info_start> ptr) {
+		return os::phys_ptr<const info>{ptr.get_phys_addr() + 8};
+	}
+	inline os::phys_ptr<const info> next(os::phys_ptr<const info> ptr) {
+		return os::phys_ptr<const info>((ptr.get_phys_addr() + ptr->size + 7) / 8 * 8); // + 7) / 8 * 8 is required for alignment.
+	}
+
+	uint64_t framebuffer_addr(os::phys_ptr<const info> ptr) {
+		return *reinterpret_cast<const uint64_t*>(&ptr->rest[0]);
+	}
+	uint32_t framebuffer_pitch(os::phys_ptr<const info> ptr) {
+		return *reinterpret_cast<const uint32_t*>(&ptr->rest[8]);
+	}
+	uint32_t framebuffer_width(os::phys_ptr<const info> ptr) {
+		return *reinterpret_cast<const uint32_t*>(&ptr->rest[12]);
+	}
+	uint32_t framebuffer_height(os::phys_ptr<const info> ptr) {
+		return *reinterpret_cast<const uint32_t*>(&ptr->rest[16]);
+	}
+	uint8_t framebuffer_bpp(os::phys_ptr<const info> ptr) {
+		return *reinterpret_cast<const uint8_t*>(&ptr->rest[20]);
+	}
+	uint8_t framebuffer_type(os::phys_ptr<const info> ptr) {
+		return *reinterpret_cast<const uint8_t*>(&ptr->rest[21]);
+	}
+	const uint8_t* color_info(os::phys_ptr<const info> ptr) {
+		return reinterpret_cast<const uint8_t*>(&ptr->rest[24]);
+	}
+#endif // __cplusplus
+
+#ifdef __cplusplus
+	} // namespace multiboot2
+#endif // __cplusplus
-- 
2.46.0