Branch sync and push, reformated and made the OS look more "official"

This commit is contained in:
cowmonk 2025-04-17 21:32:48 -07:00
parent 13389f9617
commit 59e39d9842
9 changed files with 270 additions and 0 deletions

View file

@ -1,2 +1,31 @@
# cowos # cowos
Custom OS from scratch in C Custom OS from scratch in C
# Notes
This only supports 32bit for now, please DO NOT compile with 64bit or it'll fail to boot
## compilation of any component
You are required to get your own cross C compiler. This is because normal C compilers for your Linux distro (assuming that you are on Linux) will optimize against your own libs and other things. This OS doesn't exist yet, so this is a big no no in OS development. Another thing is that you want to compile for 32 bit, so you'll need to cross compile anyways...
### general c files
```bash
i386-elf-gcc -c kernel.c -o kernel.o -std=c99 -ffreestanding -O2 -Wall -Wextra # i386 is the 32 bit version of x86
# also please compile with the -Iinclude flag to include the libraries like string.h in the arch
```
### "compiling" the "boot stub"
```bash
i386-elf-as boot.s -o boot.o
```
### linking the "compiled" boot stub w/compiled kernel img
```
i386-elf-gcc -T linker.ld -o cowos.bin -ffreestanding -O2 -nostdlib boot.o kernel.o
```
## Getting it bootable
You'll need grub and mtools. First move the compiled cowos.bin to isodir/boot. Then run the following command:
```bash
grub-mkrescue isodir/ -o cowos.iso
```
Now you can enjoy running this in qemu lol.

View file

@ -0,0 +1,3 @@
menuentry "cowos" {
multiboot /boot/kernel.elf
}

72
kernel/arch/i386/boot.s Normal file
View file

@ -0,0 +1,72 @@
.set ALIGN, 1<<0 /* align loaded modules on page boundaries */
.set MEMINFO, 1<<1 /* provide memory map */
.set FLAGS, ALIGN | MEMINFO /* Multiboot flag */
.set MAGIC, 0x1BADB002 /* Magic number lets bootloader find the header */
.set CHECKSUM, -(MAGIC + FLAGS) /* checksum of above, to prove we are multiboot (sanity check I think) */
/* so this basically declares the multiboot header that defines this program as a kernel,
and the following values are in the multiboot standard. The bootloader will look for the
first 8KiB of the kernel file, aligned with the 32-bit boundary, this means that it won't
work with 64bit until I keep on learning or I create my own bootloader (without multiboot)
*/
.section .multiboot
.align 4
.long MAGIC
.long FLAGS
.long CHECKSUM
/* This is where my knowledge is not high enough but here is the summary:
- esp is NOT in the multiboot standard so the kernel has to provide it.
this basically means that it doesn't define the value of the stack register
(whatever that means)
- This allocates some room for a stack by creating a symbol at the bottom of it
then allocate 16 KiB and then creating a symbol on top.
- The stack grows downwards on x86.
And many more, apparently it's re-aligned to System-V ABI standard as well? Not sure, but hopefully I can
continue learning
*/
.section .bss
.align 16
stack_bottom:
.skip 16384 # 16 KiB
stack_top:
/* linker specifies a _start as the entry point to the kernel and the bootloader will jump to the position
however it doesn't make any sense to return to this after the bootloader is gone
*/
.section .text
.global _start
.type _start, @function
_start:
/* We are now in... no printf and other things, kernel has FULL CONTROL of the cpu. No security also lol */
/* To set up a stack, this sets the esp to register to the point of the stack because it grows downwards on
on x86 systems. this is done in assembly because languages like C can't function without a stack */
mov $stack_top, %esp
/* so we need to initialize some critical processor states before the high-level kernel is entered.
interestingly a lot of things are done at the boot level, we should load GDT, enable paging, and add runtime
support (for C++). Crazy!
*/
/* time to enter the kernel,
according to the guide, this is a well defined call as the stack was originally 16-byte aligned, we pushed a
multiple of 16-bytes to the stack (since 0 bytes were pushed so far), so alignment has been perserved.
*/
call kernel_main
/* If there is nothing else to do, the computer will be in an infinite loop
- Disable interrupts with cli (they wil be disabled by the bootloader)
- Wait for next interrupt to arrive with halt instruction (hlt)
- Jump to hlt instruction if it ever wakes up due to a non-maskable
interrupt occuring or due to system management mode.
*/
cli
1: hlt
jmp 1b
/* set the size of _start symbol to the current location minus its start.
Useful for debugging or if I implement call tracing
*/
.size _start, . - _start

View file

@ -0,0 +1,25 @@
ENTRY(_start) /* Entry point symbol */
SECTIONS {
/* Load address in memory: 2MB for kernel */
. = 2M;
/* Multiboot header must be in the first 8KB of the file and 32-bit aligned */
.text BLOCK(4K) : ALIGN(4K) {
*(.multiboot)
*(.text)
}
.rodata BLOCK(4K) : ALIGN(4K) {
*(.rodata)
}
.data BLOCK(4K) : ALIGN(4K) {
*(.data)
}
.bss BLOCK(4K) : ALIGN(4K) {
*(COMMON)
*(.bss)
}
}

View file

@ -0,0 +1,61 @@
#include "../../include/video/vga.h"
#include <string.h>
size_t term_row;
size_t term_col;
uint8_t term_color;
uint16_t* term_buf = (uint16_t*)VGA_MEMORY;
void
term_init(void)
{
term_row = 0;
term_col = 0;
term_color = vga_entry_color(VGA_COLOR_WHITE, 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;
term_buf[index] = vga_entry(' ', term_color);
}
}
}
void
term_setcolor(uint8_t color)
{
term_color = color;
}
void
term_putentryat(char c, uint8_t color, size_t x, size_t y)
{
const size_t index = y * VGA_HEIGHT + x;
term_buf[index] = vga_entry(c, color);
}
void
term_putchar(char c)
{
term_putentryat(c, term_color, term_row);
if (++term_col == VGA_WIDTH) {
term_col = 0;
if (++term_row == VGA_HEIGHT) {
term_row = 0;
}
}
}
void
term_write(const char* data, size_t size)
{
for (size_t i = 0; i < size; i++) {
term_putchar(data[i];
}
}
void
term_writestr(const char* data)
{
term_write(data, strlen(data));
}

View file

@ -0,0 +1,51 @@
#ifndef VGA_H
#define VGA_H
#include <stdint.h>
#include <stddef.h>
/* hardware text mode const */
enum vga_color {
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;
}
/* using mode 3 of VGA 80x25 */
#define VGA_WIDTH 80
#define VGA_HEIGHT 25
#define VGA_MEMORY 0xB8000 /* VGA memory location */
void term init(void);
void term_setcolor(uint8_t color);
void term_putentryat(char c, uint8_t color, size_t x, size_t y);
void term_putchar(char c);
void term_write(const char* data, size_t size);
void term_writestr(const char* data);
#endif #VGA_H

View file

@ -0,0 +1,8 @@
#ifndef STRING_H
#define STRING_H
#include <stddef.h>
size_t strlen(const char* str);
#endif // STRING_H

10
kernel/init/kernel.c Normal file
View file

@ -0,0 +1,10 @@
#include "../include/drivers/video/vga.h"
#include <string.h>
void
kernel_main(void)
{
term_init();
term_writestr("Hello World!\n");
}

11
kernel/lib/string.c Normal file
View file

@ -0,0 +1,11 @@
#include "../include/lib/string.h"
#include <stddef.h>
#include <stdint.h>
size_t
strlen(const char* str)
{
size_t len = 0;
while (str[len]) len++;
return len;
}