Add kernel. Link everything

This commit is contained in:
2026-08-25 18:52:12 +03:00
parent 1893b8f054
commit 8e4a0c9607
4 changed files with 182 additions and 5 deletions
+39
View File
@@ -61,6 +61,40 @@ bool build_c(Nob_Cmd *cmd) {
return true; return true;
} }
bool link_everything(Nob_Cmd *cmd) {
Nob_File_Paths objectFiles = {0};
nob_da_append(&objectFiles, BUILD_FOLDER "kernel.o");
nob_da_append(&objectFiles, BUILD_FOLDER "boot.o");
nob_cmd_append(cmd,
"i686-elf-gcc",
"-T", SRC_FOLDER "linker.ld",
"-o", BUILD_FOLDER "kernel",
"-ffreestanding",
"-O2",
"-nostdlib");
for (size_t i = 0; i < objectFiles.count; ++i) {
nob_cmd_append(cmd, objectFiles.items[i]);
}
nob_cmd_append(cmd, "-lgcc");
if (!nob_cmd_run(cmd))
return false;
return true;
}
bool check_kernel(Nob_Cmd *cmd) {
nob_cmd_append(cmd, "grub-file", "--is-x86-multiboot", BUILD_FOLDER "kernel");
if (!nob_cmd_run(cmd))
return false;
return true;
}
bool clean_build() {
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
NOB_GO_REBUILD_URSELF(argc, argv); NOB_GO_REBUILD_URSELF(argc, argv);
@@ -71,10 +105,15 @@ int main(int argc, char **argv) {
if (!render_clangd_config()) if (!render_clangd_config())
return 1; return 1;
if (!build_assembly(&cmd)) if (!build_assembly(&cmd))
return 1; return 1;
if (!build_c(&cmd)) if (!build_c(&cmd))
return 1; return 1;
if (!link_everything(&cmd))
return 1;
if (!check_kernel(&cmd))
return 1;
return 0; return 0;
} }
+1
View File
@@ -6,6 +6,7 @@ pkgs.mkShell {
pkgsCross.i686-embedded.buildPackages.gcc pkgsCross.i686-embedded.buildPackages.gcc
pkgsCross.i686-embedded.buildPackages.binutils pkgsCross.i686-embedded.buildPackages.binutils
gdb gdb
grub2
]; ];
shellHook = '' shellHook = ''
+94 -5
View File
@@ -2,15 +2,104 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
/* Check if the compiler thinks you are targeting the wrong operating system. */
#if defined(__linux__) #if defined(__linux__)
#error "Not using a cross compiler" #error "You are not using a cross-compiler, you will most certainly run into trouble"
#endif #endif
/* This tutorial will only work for the 32-bit ix86 targets. */
#if !defined(__i386__) #if !defined(__i386__)
#error "Not using the right compiler" #error "This tutorial needs to be compiled with a ix86-elf compiler"
#endif #endif
int main() { /* Hardware text mode color constants. */
return 1; enum vga_color {
return false; VGA_COLOR_BLACK = 0,
VGA_COLOR_BLUE = 1,
VGA_COLOR_GREEN = 2,
VGA_COLOR_CYAN = 3,
VGA_COLOR_RED = 4,
VGA_COLOR_MAGENTA = 5,
VGA_COLOR_BROWN = 6,
VGA_COLOR_LIGHT_GREY = 7,
VGA_COLOR_DARK_GREY = 8,
VGA_COLOR_LIGHT_BLUE = 9,
VGA_COLOR_LIGHT_GREEN = 10,
VGA_COLOR_LIGHT_CYAN = 11,
VGA_COLOR_LIGHT_RED = 12,
VGA_COLOR_LIGHT_MAGENTA = 13,
VGA_COLOR_LIGHT_BROWN = 14,
VGA_COLOR_WHITE = 15,
};
static inline uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg) {
return fg | bg << 4;
}
static inline uint16_t vga_entry(unsigned char uc, uint8_t color) {
return (uint16_t)uc | (uint16_t)color << 8;
}
size_t strlen(const char *str) {
size_t len = 0;
while (str[len])
len++;
return len;
}
#define VGA_WIDTH 80
#define VGA_HEIGHT 25
#define VGA_MEMORY 0xB8000
size_t terminal_row;
size_t terminal_column;
uint8_t terminal_color;
uint16_t *terminal_buffer = (uint16_t *)VGA_MEMORY;
void terminal_initialize(void) {
terminal_row = 0;
terminal_column = 0;
terminal_color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK);
for (size_t y = 0; y < VGA_HEIGHT; y++) {
for (size_t x = 0; x < VGA_WIDTH; x++) {
const size_t index = y * VGA_WIDTH + x;
terminal_buffer[index] = vga_entry(' ', terminal_color);
}
}
}
void terminal_setcolor(uint8_t color) {
terminal_color = color;
}
void terminal_putentryat(char c, uint8_t color, size_t x, size_t y) {
const size_t index = y * VGA_WIDTH + x;
terminal_buffer[index] = vga_entry(c, color);
}
void terminal_putchar(char c) {
terminal_putentryat(c, terminal_color, terminal_column, terminal_row);
if (++terminal_column == VGA_WIDTH) {
terminal_column = 0;
if (++terminal_row == VGA_HEIGHT)
terminal_row = 0;
}
}
void terminal_write(const char *data, size_t size) {
for (size_t i = 0; i < size; i++)
terminal_putchar(data[i]);
}
void terminal_writestring(const char *data) {
terminal_write(data, strlen(data));
}
void kernel_main(void) {
/* Initialize terminal interface */
terminal_initialize();
/* Newline support is left as an exercise. */
terminal_writestring("Hello, kernel World!\n");
} }
+48
View File
@@ -0,0 +1,48 @@
ENTRY(_start)
SECTIONS
{
/* It used to be universally recommended to use 1M as a start offset,
as it was effectively guaranteed to be available under BIOS systems.
However, UEFI has made things more complicated, and experimental data
strongly suggests that 2M is a safer place to load. In 2016, a new
feature was introduced to the multiboot2 spec to inform bootloaders
that a kernel can be loaded anywhere within a range of addresses and
will be able to relocate itself to run from such a loader-selected
address, in order to give the loader freedom in selecting a span of
memory which is verified to be available by the firmware, in order to
work around this issue. This does not use that feature, so 2M was
chosen as a safer option than the traditional 1M. */
. = 2M;
/* First put the multiboot header, as it is required to be put very early
in the image or the bootloader won't recognize the file format.
Next we'll put the .text section. */
.text BLOCK(4K) : ALIGN(4K)
{
*(.multiboot)
*(.text)
}
/* Read-only data. */
.rodata BLOCK(4K) : ALIGN(4K)
{
*(.rodata)
}
/* Read-write data (initialized) */
.data BLOCK(4K) : ALIGN(4K)
{
*(.data)
}
/* Read-write data (uninitialized) and stack */
.bss BLOCK(4K) : ALIGN(4K)
{
*(COMMON)
*(.bss)
}
/* The compiler may produce other sections, by default it will put them in
a segment with the same name. Simply add stuff here as needed. */
}