From 027ab6719a30395276053f545d72a42308d1b15e Mon Sep 17 00:00:00 2001
From: Amelia Coutard <eliottulio.coutard@gmail.com>
Date: Fri, 29 Dec 2023 04:29:14 +0100
Subject: [PATCH] Remplacement de os::assert par une macro assert dans la
 "librairie standard"

---
 kernel/src/elf64.cpp        | 25 +++++++++++++------------
 kernel/src/interrupts.cpp   |  4 ++--
 kernel/src/interrupts.hpp   |  6 ++----
 kernel/src/kernel.cpp       | 23 ++++++++++++-----------
 kernel/src/lib/phys_ptr.hpp |  6 ++++++
 kernel/src/paging.hpp       | 19 ++++++++++---------
 kernel/src/ring3.cpp        | 17 +++++++++--------
 kernel/src/serial.cpp       | 17 ++++++++++++-----
 kernel/src/serial.hpp       | 34 +++++++++-------------------------
 kernel/src/utils.hpp        |  9 +++++----
 libcpp/assert.hpp           | 20 ++++++++++++++++++++
 11 files changed, 100 insertions(+), 80 deletions(-)
 create mode 100644 libcpp/assert.hpp

diff --git a/kernel/src/elf64.cpp b/kernel/src/elf64.cpp
index d5df9d9..9ced6fb 100644
--- a/kernel/src/elf64.cpp
+++ b/kernel/src/elf64.cpp
@@ -12,29 +12,30 @@
 // not, see <https://www.gnu.org/licenses/>.
 
 #include "elf64.hpp"
+#include <assert.hpp>
 
 void os::elf::load_elf(os::process& result, amy::byte* start, amy::size length, const paging::PML4T& original_PML4T) {
-	os::assert(length >= amy::byte_size<os::elf::header>(), "Elf file isn't big enough to contain a header: there is an error.");
+	assert(length >= amy::byte_size<os::elf::header>(), "Elf file isn't big enough to contain a header: there is an error.");
 	// TODO: Check that the elf file sections are all fully inside the file.
 
 	// Load test-module elf file:
 	const os::elf::header elf_header = *(os::elf::header*)start;
 
 	// Check if elf is valid:
-	os::assert(elf_header.magic[0] == '\x7f', "No elf header.");
-	os::assert(elf_header.magic[1] == 'E', "No elf header.");
-	os::assert(elf_header.magic[2] == 'L', "No elf header.");
-	os::assert(elf_header.magic[3] == 'F', "No elf header.");
-	os::assert(elf_header.bitn == 2, "Elf file not 64 bits.");
-	os::assert(elf_header.endianness == 1, "Elf file not little endian.");
-	os::assert(elf_header.type == 2, "Elf file not executable.");
-	os::assert(elf_header.arch == 0x3E, "Elf file not x86_64.");
+	assert(elf_header.magic[0] == '\x7f', "No elf header.");
+	assert(elf_header.magic[1] == 'E', "No elf header.");
+	assert(elf_header.magic[2] == 'L', "No elf header.");
+	assert(elf_header.magic[3] == 'F', "No elf header.");
+	assert(elf_header.bitn == 2, "Elf file not 64 bits.");
+	assert(elf_header.endianness == 1, "Elf file not little endian.");
+	assert(elf_header.type == 2, "Elf file not executable.");
+	assert(elf_header.arch == 0x3E, "Elf file not x86_64.");
 
 	constexpr amy::size stack_size = 16 * 0x1000 /* 64KiB */;
 	amy::byte* const stack = (amy::byte*)0x0000'8000'0000'0000 - stack_size;
 
 	const auto page = os::paging::page_allocator.allocate(1);
-	os::assert(page.ptr != nullptr, "Not enough memory for elf file loading.");
+	assert(page.ptr != amy::nil, "Not enough memory for elf file loading.");
 	result.PML4T = phys_ptr<paging::PML4T>(page.ptr.get_phys_addr());
 	result.rip = amy::u64(elf_header.entry);
 	result.rsp = amy::u64(stack + stack_size);
@@ -68,7 +69,7 @@ void os::elf::load_elf(os::process& result, amy::byte* start, amy::size length,
 			continue;
 		}
 		os::print("Segment: loadable\n");
-		os::assert(0x1000 <= amy::u64(program_header.p_vaddr)
+		assert(0x1000 <= amy::u64(program_header.p_vaddr)
 				&& amy::u64(program_header.p_vaddr + program_header.p_memsz) < 0x10'0000'0000,
 			"Program segments must be contained between 0x1000 and 0x10'0000'0000 (i.e. 64GiB).");
 
@@ -76,7 +77,7 @@ void os::elf::load_elf(os::process& result, amy::byte* start, amy::size length,
 		amy::size nb_pages = (amy::u64(program_header.p_vaddr) % 0x1000 + program_header.p_memsz + 0x1000 - 1) / 0x1000;
 		for (amy::size i = 0; i < nb_pages; i++) {
 			auto const alloc = os::paging::page_allocator.allocate(1);
-			os::assert(alloc.ptr != nullptr, "Failed to allocate enough memory for loading of elf binary.");
+			assert(alloc.ptr != nullptr, "Failed to allocate enough memory for loading of elf binary.");
 			os::paging::map_page(*result.PML4T, (os::paging::page<0>*)(program_header.p_vaddr + i * 0x1000), alloc.ptr, {
 				.RW = ((program_header.flags & 2) >> 1) != 0,
 				.US = true, .PWT = false, .PCD = false, .A = false, .D = false, .PAT = false, .G = false, .AVL = 0,
diff --git a/kernel/src/interrupts.cpp b/kernel/src/interrupts.cpp
index 2288039..3b7d9ef 100644
--- a/kernel/src/interrupts.cpp
+++ b/kernel/src/interrupts.cpp
@@ -112,7 +112,7 @@ extern "C" void int_page_fault(amy::u32 err_code, amy::u64 vaddr) {
 	) { // Kernel memory
 		os::print("Allocating (Ring 0): {}\n", (void*)vaddr);
 		auto const alloc = os::paging::page_allocator.allocate(1);
-		os::assert(alloc.ptr != nullptr, "Out of memory.");
+		assert(alloc.ptr != nullptr, "Out of memory.");
 		os::paging::map_page(os::paging::global_PML4T, (os::paging::page<0>*)vaddr, alloc.ptr,
 			{.RW = true, .US = false, .PWT = false, .PCD = false, .A = false, .D = false, .PAT = false, .G = true, .AVL = 0, .NX = true});
 		return;
@@ -121,7 +121,7 @@ extern "C" void int_page_fault(amy::u32 err_code, amy::u64 vaddr) {
 	) { // Userspace memory
 		os::print("Allocating (Ring 3): {}\n", (void*)vaddr);
 		auto const alloc = os::paging::page_allocator.allocate(1);
-		os::assert(alloc.ptr != nullptr, "Out of memory.");
+		assert(alloc.ptr != nullptr, "Out of memory.");
 		os::paging::map_page(*os::get_process(os::current_pid).PML4T, (os::paging::page<0>*)vaddr, alloc.ptr,
 			{.RW = true, .US = true, .PWT = false, .PCD = false, .A = false, .D = false, .PAT = false, .G = false, .AVL = 0, .NX = true});
 		return;
diff --git a/kernel/src/interrupts.hpp b/kernel/src/interrupts.hpp
index 1ea335f..846168f 100644
--- a/kernel/src/interrupts.hpp
+++ b/kernel/src/interrupts.hpp
@@ -14,6 +14,7 @@
 #pragma once
 
 #include <types.hpp>
+#include <assert.hpp>
 #include "lib/phys_ptr.hpp"
 #include "serial.hpp"
 #include "utils.hpp"
@@ -58,12 +59,9 @@ struct isr_info {
 	bool present : 1 = true;
 };
 
-template<typename... Ts>
-void assert(bool cond, const char* format, const Ts&... vs);
-
 template<amy::size interrupt_nb>
 void enable_interrupts(const isr_info (&ISRs)[interrupt_nb], os::idt<interrupt_nb>& idt) {
-	os::assert(is_APIC_builtin(), "No builtin APIC.");
+	assert(is_APIC_builtin(), "No builtin APIC.");
 
 	disable_PIC();
 	set_APIC_reg(0xF0, 0x1FF); // Enable APIC
diff --git a/kernel/src/kernel.cpp b/kernel/src/kernel.cpp
index 42d0355..76dcd4a 100644
--- a/kernel/src/kernel.cpp
+++ b/kernel/src/kernel.cpp
@@ -13,6 +13,7 @@
 
 #include <allocator.hpp>
 #include <vector.hpp>
+#include <assert.hpp>
 #include "lib/multiboot2.hpp"
 #include "paging.hpp"
 #include "utils.hpp"
@@ -37,18 +38,18 @@ extern "C" void kmain(unsigned long magic, os::phys_ptr<const multiboot2::info_s
 			os::hlt();
 		}
 	}
-	os::assert(magic == 0x36D76289, "Incorrect magic number: wasn't booted with multiboot2.");
+	assert(magic == 0x36D76289, "Incorrect magic number: wasn't booted with multiboot2.");
 
 	os::paging::page_allocator.deallocate({
 		.ptr = os::phys_ptr<os::paging::page<0>>(amy::ptr(&bootstrap_pages_for_memory) & amy::ptr(~0xFFFF'FFFF'8000'0000)),
 		.size = amy::byte_size<decltype(bootstrap_pages_for_memory)>() / amy::byte_size<decltype(bootstrap_pages_for_memory[0])>()
 	});
 
-	os::assert(os::cpu_has_msr(), "MSRs aren't supported.");
+	assert(os::cpu_has_msr(), "MSRs aren't supported.");
 	// Initialise kernel map.
-	os::assert(amy::ptr(&_kernel_phys_start) % 4096 == 0, "Kernel isn't page aligned !");
-	os::assert(amy::ptr(&_kernel_phys_rw_start) % 4096 == 0, "Kernel isn't page aligned !");
-	os::assert(amy::ptr(&_kernel_phys_end) % 4096 == 0, "Kernel isn't page aligned !");
+	assert(amy::ptr(&_kernel_phys_start) % 4096 == 0, "Kernel isn't page aligned !");
+	assert(amy::ptr(&_kernel_phys_rw_start) % 4096 == 0, "Kernel isn't page aligned !");
+	assert(amy::ptr(&_kernel_phys_end) % 4096 == 0, "Kernel isn't page aligned !");
 	for (amy::size i = amy::ptr(&_kernel_phys_start); i < amy::ptr(&_kernel_phys_rw_start); ) {
 		if (i % (1024 * 1024 * 1024) == 0 && i + 1024 * 1024 * 1024 < amy::ptr(&_kernel_phys_rw_start)) {
 			os::paging::map_page<2, 3>(os::paging::global_PML4T, (os::paging::page<2>*)(0xFFFF'FFFF'8000'0000 + i), os::phys_ptr<os::paging::page<2>>(i),
@@ -94,8 +95,8 @@ extern "C" void kmain(unsigned long magic, os::phys_ptr<const multiboot2::info_s
 				if (e <= 1024 * 1024) { // I'll ignore lower memory for now.
 					continue;
 				}
-				os::assert(s % 0x1000 == 0, "Memory map unaligned on page boundaries.");
-				os::assert(e % 0x1000 == 0, "Memory map unaligned on page boundaries.");
+				assert(s % 0x1000 == 0, "Memory map unaligned on page boundaries.");
+				assert(e % 0x1000 == 0, "Memory map unaligned on page boundaries.");
 				for (amy::ptr j = s; j < e; ) {
 					if (j % (1024 * 1024 * 1024) == 0 && j + 1024 * 1024 * 1024 < e) {
 						os::paging::map_page<2, 3>(
@@ -193,7 +194,7 @@ extern "C" void kmain(unsigned long magic, os::phys_ptr<const multiboot2::info_s
 		};
 		amy::stack_allocator<amy::byte_size<ram_range>() * 64, amy::byte_align<ram_range>()> available_ram_allocator;
 		amy::vector<ram_range, decltype(available_ram_allocator)> available_ram(available_ram_allocator);
-		os::assert(!available_ram.construction_failed(), "Failed to create vector.");
+		assert(!available_ram.construction_failed(), "Failed to create vector.");
 		amy::size available_ram_length = 0;
 
 		bool module_specified = false;
@@ -215,7 +216,7 @@ extern "C" void kmain(unsigned long magic, os::phys_ptr<const multiboot2::info_s
 					if (e.get_phys_addr() <= 1024 * 1024) { // I'll ignore lower memory for now.
 						continue;
 					}
-					os::assert(available_ram.push_back({.start_address = s, .end_address = e}),
+					assert(available_ram.push_back({.start_address = s, .end_address = e}),
 						"Too much available RAM sections to initialise correctly. Will fix eventually, probably.");
 				}
 				break;
@@ -231,7 +232,7 @@ extern "C" void kmain(unsigned long magic, os::phys_ptr<const multiboot2::info_s
 			}
 		}
 
-		os::assert(module_specified, "No modules specified in the multiboot. This is unsupported.");
+		assert(module_specified, "No modules specified in the multiboot. This is unsupported.");
 
 		// kernel_start and kernel_end are aligned to 4K by the linker script.
 		const os::phys_ptr<os::paging::page<0>> kernel_s{amy::ptr(&_kernel_phys_start)};
@@ -250,7 +251,7 @@ extern "C" void kmain(unsigned long magic, os::phys_ptr<const multiboot2::info_s
 			} else if (available_ram[i].end_address <= kernel_e) {
 				available_ram[i].end_address = kernel_s - 1; // Since start_address < s, start_address <= new end_address.
 			} else {
-				os::assert(available_ram.push_back(available_ram[i]),
+				assert(available_ram.push_back(available_ram[i]),
 					"Too much available RAM sections to initialise correctly. Will fix eventually, probably.");
 				available_ram[i].end_address = kernel_s - 1;
 				available_ram[available_ram.size() - 1].start_address = kernel_e + 1;
diff --git a/kernel/src/lib/phys_ptr.hpp b/kernel/src/lib/phys_ptr.hpp
index 4fb474a..b9e0989 100644
--- a/kernel/src/lib/phys_ptr.hpp
+++ b/kernel/src/lib/phys_ptr.hpp
@@ -15,6 +15,7 @@
 
 #include <types.hpp>
 #include <memory.hpp>
+#include <assert.hpp>
 
 namespace os {
 
@@ -28,9 +29,11 @@ public:
 	constexpr phys_ptr(amy::nil_t): phys_addr(0xF000'0000'0000'0000) {}
 
 	inline void* operator->() const {
+		assert(*this != amy::nil);
 		return get_virt_addr();
 	}
 	inline explicit operator void*() const {
+		assert(*this != amy::nil);
 		return get_virt_addr();
 	}
 
@@ -60,12 +63,15 @@ public:
 	constexpr phys_ptr(amy::nil_t): phys_addr(0xF000'0000'0000'0000) {}
 
 	T& operator[](amy::diff i) const {
+		assert(*this != amy::nil);
 		return get_virt_addr()[i];
 	}
 	T& operator*() const {
+		assert(*this != amy::nil);
 		return *get_virt_addr();
 	}
 	T* operator->() const {
+		assert(*this != amy::nil);
 		return get_virt_addr();
 	}
 	explicit operator T*() const {
diff --git a/kernel/src/paging.hpp b/kernel/src/paging.hpp
index ede7e3b..67df518 100644
--- a/kernel/src/paging.hpp
+++ b/kernel/src/paging.hpp
@@ -13,10 +13,11 @@
 
 #pragma once
 
+#include <assert.hpp>
 #include <types.hpp>
+#include "lib/phys_ptr.hpp"
 #include "serial.hpp"
 #include "utils.hpp"
-#include "lib/phys_ptr.hpp"
 
 namespace os { namespace paging {
 
@@ -53,12 +54,12 @@ template <amy::size depth> struct paging_entry {
 		return (data & (1 << 0)) != 0;
 	}
 	bool is_page() {
-		os::assert(is_present(), "Cannot check if a paging_entry is a page if it's not present.");
+		assert(is_present(), "Cannot check if a paging_entry is a page if it's not present.");
 		return depth == 0 || (data & (1 << 7)) != 0;
 	}
 
 	paging::page_info page_info() {
-		os::assert(is_page(), "Cannot access page info for a non-page.");
+		assert(is_page(), "Cannot access page info for a non-page.");
 		return {
 			.RW = (data & (1ull << 1)) != 0,
 			.US = (data & (1ull << 2)) != 0,
@@ -76,12 +77,12 @@ template <amy::size depth> struct paging_entry {
 		};
 	}
 	os::phys_ptr<os::paging::page<depth>> page_ptr() {
-		os::assert(is_page(), "Cannot access page physical pointer for a non-page.");
+		assert(is_page(), "Cannot access page physical pointer for a non-page.");
 		amy::ptr addr = data & (depth == 0 ? 0x0000'FFFF'FFFF'F000 : 0x0000'FFFF'FFFF'E000);
 		return os::phys_ptr<os::paging::page<depth>>(addr | ((addr & 0x0000'8000'0000'0000) != 0 ? 0xFFFF'8000'0000'0000 : 0));
 	}
 	void page_ptr(os::phys_ptr<os::paging::page<depth>> page) {
-		os::assert(is_page(), "Cannot access page physical pointer for a non-page.");
+		assert(is_page(), "Cannot access page physical pointer for a non-page.");
 		if (depth == 0) {
 			data = (data & ~0x0000'FFFF'FFFF'F000) | page.get_phys_addr();
 		} else {
@@ -89,7 +90,7 @@ template <amy::size depth> struct paging_entry {
 		}
 	}
 	paging::paging_table_info paging_table_info() {
-		os::assert(!is_page(), "Cannot access paging table info for a page.");
+		assert(!is_page(), "Cannot access paging table info for a page.");
 		return {
 			.RW = (data & (1ull << 1)) != 0,
 			.US = (data & (1ull << 2)) != 0,
@@ -103,12 +104,12 @@ template <amy::size depth> struct paging_entry {
 		};
 	}
 	os::phys_ptr<os::paging::paging_table<depth - 1>> paging_table_ptr() {
-		os::assert(!is_page(), "Cannot access paging table physical pointer for a page.");
+		assert(!is_page(), "Cannot access paging table physical pointer for a page.");
 		amy::ptr addr = data & 0x0000'FFFF'FFFF'F000;
 		return os::phys_ptr<os::paging::paging_table<depth - 1>>(addr | ((addr & 0x0000'8000'0000'0000) != 0 ? 0xFFFF'8000'0000'0000 : 0));
 	}
 	void paging_table_ptr(os::phys_ptr<os::paging::paging_table<depth - 1>> table) {
-		os::assert(!is_page(), "Cannot access paging table physical pointer for a page.");
+		assert(!is_page(), "Cannot access paging table physical pointer for a page.");
 		data = (data & ~0x0000'FFFF'FFFF'F000) | table.get_phys_addr();
 	}
 	void remove() {
@@ -220,7 +221,7 @@ template <amy::size depth, amy::size paging_depth>
 void map_page(paging_table<paging_depth>& paging_table, page<depth> const* vaddr, phys_ptr<page<depth>> paddr, os::paging::page_info page_info) {
 	amy::size const index = (amy::ptr(vaddr) >> (12 + 9 * paging_depth)) & 0x1FF;
 	if constexpr (depth == paging_depth) {
-		os::assert(!paging_table.contents[index].is_present(), "Virtual address 0x{} is already mapped.", vaddr);
+		assert(!paging_table.contents[index].is_present(), "Virtual address 0x{} is already mapped.", vaddr);
 		paging_table.contents[index].page_info(page_info);
 		paging_table.contents[index].page_ptr(paddr);
 		invlpg(vaddr);
diff --git a/kernel/src/ring3.cpp b/kernel/src/ring3.cpp
index 5df22c5..f0716d5 100644
--- a/kernel/src/ring3.cpp
+++ b/kernel/src/ring3.cpp
@@ -11,6 +11,7 @@
 // You should have received a copy of the GNU General Public License along with this program. If
 // not, see <https://www.gnu.org/licenses/>.
 
+#include <assert.hpp>
 #include "ring3.hpp"
 #include "utils.hpp"
 #include "serial.hpp"
@@ -51,19 +52,19 @@ void schedule_next_process() {
 
 // extern "C" int os::syscall_mount_kernel_device(amy::fd wd, char device, char const* path, amy::size path_len, int mount_type);
 extern "C" int os::syscall_mount_kernel_device(amy::fd, char, char const*, amy::size, int) {
-	os::assert(false, "mount_kernel_device not implemented yet.");
+	assert(false, "mount_kernel_device not implemented yet.");
 	__builtin_unreachable();
 }
 
 // extern "C" amy::fd os::syscall_open(amy::fd wd, char const* path, amy::size path_len, int options);
 extern "C" amy::fd os::syscall_open(amy::fd, char const*, amy::size, int) {
-	os::assert(false, "open not implemented yet.");
+	assert(false, "open not implemented yet.");
 	__builtin_unreachable();
 }
 
 extern "C" amy::size os::syscall_read(amy::fd file, char* data, amy::size len) {
-	os::assert(file == amy::fd(0), "Read isn't really implemented for now.");
-	os::assert(len >= 0, "Read expects a positive size.");
+	assert(file == amy::fd(0), "Read isn't really implemented for now.");
+	assert(len >= 0, "Read expects a positive size.");
 	if (len == 0) {
 		return 0;
 	}
@@ -82,8 +83,8 @@ extern "C" amy::size os::syscall_read(amy::fd file, char* data, amy::size len) {
 }
 
 extern "C" amy::size os::syscall_write(amy::fd file, char const* data, amy::size len) {
-	os::assert(file == amy::fd(0), "Write isn't really implemented for now.");
-	os::assert(len >= 0, "Write expects a positive size.");
+	assert(file == amy::fd(0), "Write isn't really implemented for now.");
+	assert(len >= 0, "Write expects a positive size.");
 	if (len == 0) {
 		return 0;
 	}
@@ -99,12 +100,12 @@ extern "C" amy::size os::syscall_write(amy::fd file, char const* data, amy::size
 
 // extern "C" int syscall_close(amy::fd file);
 extern "C" int os::syscall_close(amy::fd) {
-	os::assert(false, "Close isn't implemented for now.");
+	assert(false, "Close isn't implemented for now.");
 	__builtin_unreachable();
 }
 
 extern "C" void os::syscall_rax_error_handler() {
-	os::assert(false, "Incorrect %rax for syscall.");
+	assert(false, "Incorrect %rax for syscall.");
 	__builtin_unreachable();
 }
 
diff --git a/kernel/src/serial.cpp b/kernel/src/serial.cpp
index 5f2f58a..2bd0477 100644
--- a/kernel/src/serial.cpp
+++ b/kernel/src/serial.cpp
@@ -15,6 +15,13 @@
 #include "interrupts.hpp"
 #include "serial.hpp"
 
+void amy::assert_handler(char const* cond, char const* file, int line) {
+	os::print("Assertion error in file '{}', at line {}: {}.\n", file, (unsigned)line, cond);
+	while (true) {
+		os::hlt();
+	}
+}
+
 bool os::init_serial_port() {
 	outb(serial_port + 1, 0x00); // Disable interrupts.
 	outb(serial_port + 3, 0x80); // Prepare to set baud rate divisor (38400 baud).
@@ -52,13 +59,13 @@ void os::printc(char c) {
 	write_serial(c);
 }
 void os::print_formatted(const char* format, const char* val) {
-	os::assert(format[0] == '}', "Format string unsupported. TODO.");
+	assert(format[0] == '}', "Format string unsupported. TODO.");
 	for (amy::size i = 0; val[i] != '\0'; i++) {
 		os::printc(val[i]);
 	}
 }
 void os::print_formatted(const char* format, amy::u64 val) {
-	os::assert(format[0] == '}', "Format string unsupported. TODO.");
+	assert(format[0] == '}', "Format string unsupported. TODO.");
 	char data[20];
 	char* curr = data + 19;
 	while (val != 0) {
@@ -74,7 +81,7 @@ void os::print_formatted(const char* format, amy::u64 val) {
 	}
 }
 void os::print_formatted(const char* format, amy::i64 val) {
-	os::assert(format[0] == '}', "Format string unsupported. TODO.");
+	assert(format[0] == '}', "Format string unsupported. TODO.");
 	if (val < 0) {
 		os::printc('-');
 		os::print_formatted(format, amy::u64(-val));
@@ -84,7 +91,7 @@ void os::print_formatted(const char* format, amy::i64 val) {
 	}
 }
 void os::print_formatted(const char* format, phys_ptr<void> val) {
-	os::assert(format[0] == '}', "Format string unsupported. TODO.");
+	assert(format[0] == '}', "Format string unsupported. TODO.");
 	os::print("0x");
 	for (int i = 60; i >= 0; i -= 4) {
 		const int v = (val.get_phys_addr() >> i) & 0xF;
@@ -92,7 +99,7 @@ void os::print_formatted(const char* format, phys_ptr<void> val) {
 	}
 }
 void os::print_formatted(const char* format, const void* val) {
-	os::assert(format[0] == '}', "Format string unsupported. TODO.");
+	assert(format[0] == '}', "Format string unsupported. TODO.");
 	os::print("0x");
 	for (int i = 60; i >= 0; i -= 4) {
 		const int v = (reinterpret_cast<amy::ptr>(val) >> i) & 0xF;
diff --git a/kernel/src/serial.hpp b/kernel/src/serial.hpp
index 479f1f0..908ab50 100644
--- a/kernel/src/serial.hpp
+++ b/kernel/src/serial.hpp
@@ -13,14 +13,12 @@
 
 #pragma once
 
+#include <assert.hpp>
 #include "types.hpp"
 #include "lib/phys_ptr.hpp"
 
 namespace os {
 
-template<typename... Ts>
-void assert(bool cond, const char* format, const Ts&... vs);
-
 constexpr amy::u16 serial_port{0x3F8};
 
 bool init_serial_port();
@@ -72,24 +70,21 @@ void print(const char* format, const Ts&... vs) {
 	for (amy::size i = 0; format[i] != '\0'; i++) {
 		if (format[i] == '{') {
 			i++;
-			if (format[i] == '\0') {
-				os::assert(false, "Error in format string: unterminated '{{}'.");
-			} else if (format[i] == '{') {
+			assert(format[i] != '\0', "Error in format string: unterminated '{{}'.");
+			if (format[i] == '{') {
 				printc('{');
 				continue;
 			} else {
 				amy::size format_spec_end = i;
 				while (format[format_spec_end] != '}') {
-					if (format[format_spec_end++] == '\0') {
-						os::assert(false, "Error in format string: unterminated '{{}'.");
-					}
+					assert(format[format_spec_end++] != '\0', "Error in format string: unterminated '{{}'.");
 				}
 				amy::size n = arg_n;
 				if ('0' <= format[i] && format[i] <= '9') {
 					if (arg_n == 0) {
 						arg_n_is_given = true;
 					}
-					os::assert(arg_n_is_given, "Error in format string: either the arg_id is always given, or never.");
+					assert(arg_n_is_given, "Error in format string: either the arg_id is always given, or never.");
 					amy::size n_ = 0;
 					while ('0' <= format[i] && format[i] <= '9') {
 						n_ = n_ * 10 + (format[i++] - '0');
@@ -99,20 +94,20 @@ void print(const char* format, const Ts&... vs) {
 					if (arg_n == 0) {
 						arg_n_is_given = false;
 					}
-					os::assert(!arg_n_is_given, "Error in format string: either the arg_id is always given, or never.");
+					assert(!arg_n_is_given, "Error in format string: either the arg_id is always given, or never.");
 				}
 				if (format[i] == ':') {
 					i++;
 				} else {
-					os::assert(format[i] == '}', "Error in format string: ':' required before format spec.");
+					assert(format[i] == '}', "Error in format string: ':' required before format spec.");
 				}
-				os::assert(n < amy::size(sizeof...(vs)), "Error in format string: not enough arguments.");
+				assert(n < amy::size(sizeof...(vs)), "Error in format string: not enough arguments.");
 				(print_nth_helper{n, &format[i]} % ... % get_more_general_type_helper_t<Ts>(vs));
 				i = format_spec_end;
 				arg_n++;
 			}
 		} else if (format[i] == '}') {
-			os::assert(format[i + 1] == '}', "Error in format string: unexpected '}'.");
+			assert(format[i + 1] == '}', "Error in format string: unexpected '}'.");
 			i++;
 			printc('}');
 		} else {
@@ -124,15 +119,4 @@ void print(const char* format, const Ts&... vs) {
 void cli();
 void hlt();
 
-template<typename... Ts>
-void assert(bool cond, const char* format, const Ts&... vs) {
-	if (!cond) {
-		print("Error: ");
-		print(format, vs...);
-		printc('\n');
-		cli();
-		while (true) { hlt(); }
-	}
-}
-
 } // namespace os
diff --git a/kernel/src/utils.hpp b/kernel/src/utils.hpp
index 3ac872f..2381c04 100644
--- a/kernel/src/utils.hpp
+++ b/kernel/src/utils.hpp
@@ -13,6 +13,7 @@
 
 #pragma once
 
+#include <assert.hpp>
 #include <types.hpp>
 #include <memory.hpp>
 #include "serial.hpp"
@@ -51,22 +52,22 @@ public:
 		return 0 <= index && index < n && elems[index].present;
 	}
 	amy::i64 create() {
-		os::assert(has_room(), "Too many allocated elems in incrementing_int64_map.");
+		assert(has_room(), "Too many allocated elems in incrementing_int64_map.");
 		elems[n].present = true;
 		new(elems[n].buffer) T;
 		return n++;
 	}
 	void remove(amy::i64 index) {
-		os::assert(present(index), "Tried removing non-existant element of incrementing_int64_map.");
+		assert(present(index), "Tried removing non-existant element of incrementing_int64_map.");
 		get(index).~T();
 		elems[index].present = false;
 	}
 	T& get(amy::i64 index) {
-		os::assert(present(index), "Tried getting non-existant element of incrementing_int64_map.");
+		assert(present(index), "Tried getting non-existant element of incrementing_int64_map.");
 		return *amy::launder(reinterpret_cast<T*>(&elems[index].buffer[0]));
 	}
 	const T& get(amy::i64 index) const {
-		os::assert(present(index), "Tried getting non-existant element of incrementing_int64_map.");
+		assert(present(index), "Tried getting non-existant element of incrementing_int64_map.");
 		return *amy::launder(reinterpret_cast<T*>(&elems[index].buffer[0]));
 	}
 
diff --git a/libcpp/assert.hpp b/libcpp/assert.hpp
new file mode 100644
index 0000000..1e61440
--- /dev/null
+++ b/libcpp/assert.hpp
@@ -0,0 +1,20 @@
+// Copyright 2023 Amélia COUTARD.
+//
+// This file from the program "voyage au centre des fichiers" is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free Software Foundation,
+// either version 3 of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along with this program. If
+// not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#define assert(cond, ...) if (!(cond)) amy::assert_handler(#cond ", " #__VA_ARGS__, __FILE__, __LINE__)
+
+namespace amy {
+	void assert_handler(char const* cond, char const* file, int line) __attribute__((noreturn));
+} // namespace amy
-- 
2.46.0