Initial commit (not working)

This commit is contained in:
Tunacan 2020-11-21 19:38:44 +03:00
commit 9caf9dcd62
13 changed files with 605 additions and 0 deletions

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
.cproject
.project
*.o
*.bin
*.iso

45
Makefile Normal file
View file

@ -0,0 +1,45 @@
PROJECT_ROOT = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
ifeq ($(BUILD_MODE),debug)
CFLAGS += -g
endif
TARGET = i686-elf
CC = $(TARGET)-gcc
STRIP = $(TARGET)-strip
NASM = nasm
QEMU = qemu-system-i386
CFLAGS += -std=gnu17 -ffreestanding -O2 -Wall -Wextra
LDFLAGS += -ffreestanding -O2 -nostdlib -lgcc
NASMFLAGS += -felf32
BIN = isodir/boot/hhhos.bin
ISO = HhhOS.iso
OBJS = boot.o kernel.o string.o terminal.o
.PHONY: all qemu clean
all: qemu
qemu: $(ISO)
$(QEMU) -cdrom $(ISO)
$(ISO): $(BIN)
mkdir -p isodir/boot/grub
cp $(PROJECT_ROOT)grub.cfg isodir/boot/grub/grub.cfg
grub-mkrescue -o $(ISO) isodir
$(BIN): $(OBJS)
mkdir -p isodir/boot
$(CC) -T $(PROJECT_ROOT)linker.ld $(LDFLAGS) -o $@ $^
%.o: $(PROJECT_ROOT)%.c
$(CC) -c $(CFLAGS) -I$(PROJECT_ROOT)include -o $@ $<
%.o: $(PROJECT_ROOT)%.asm
$(NASM) $(NASMFLAGS) -o $@ $<
clean:
rm -fr HhhOS $(OBJS)

113
boot.asm Normal file
View file

@ -0,0 +1,113 @@
; Declare constants for the multiboot header.
MBALIGN equ 1 << 0 ; align loaded modules on page boundaries
MEMINFO equ 1 << 1 ; provide memory map
FLAGS equ MBALIGN | MEMINFO ; this is the Multiboot 'flag' field
MAGIC equ 0x1BADB002 ; 'magic number' lets bootloader find the header
CHECKSUM equ -(MAGIC + FLAGS) ; checksum of above, to prove we are multiboot
; Declare a multiboot header that marks the program as a kernel. These are magic
; values that are documented in the multiboot standard. The bootloader will
; search for this signature in the first 8 KiB of the kernel file, aligned at a
; 32-bit boundary. The signature is in its own section so the header can be
; forced to be within the first 8 KiB of the kernel file.
section .multiboot
align 4
dd MAGIC
dd FLAGS
dd CHECKSUM
; The multiboot standard does not define the value of the stack pointer register
; (esp) and it is up to the kernel to provide a stack. This allocates room for a
; small stack by creating a symbol at the bottom of it, then allocating 16384
; bytes for it, and finally creating a symbol at the top. The stack grows
; downwards on x86. The stack is in its own section so it can be marked nobits,
; which means the kernel file is smaller because it does not contain an
; uninitialized stack. The stack on x86 must be 16-byte aligned according to the
; System V ABI standard and de-facto extensions. The compiler will assume the
; stack is properly aligned and failure to align the stack will result in
; undefined behavior.
section .bss
align 16
stack_bottom:
resb 16384 ; 16 KiB
stack_top:
section .data
gdt:
gdt_null:
dq 0
gdt_code:
dw 0FFFFh
dw 0
db 0
db 10011010b
db 11001111b
db 0
gdt_data:
dw 0FFFFh
dw 0
db 0
db 10010010b
db 11001111b
db 0
gdt_size: equ $ - gdt
gdt_desc:
dw gdt_size - 1
dd gdt
; The linker script specifies _start as the entry point to the kernel and the
; bootloader will jump to this position once the kernel has been loaded. It
; doesn't make sense to return from this function as the bootloader is gone.
; Declare _start as a function symbol with the given symbol size.
section .text
global _start:function (_start.end - _start)
_start:
; The bootloader has loaded us into 32-bit protected mode on a x86
; machine. Interrupts are disabled. Paging is disabled. The processor
; state is as defined in the multiboot standard. The kernel has full
; control of the CPU. The kernel can only make use of hardware features
; and any code it provides as part of itself. There's no printf
; function, unless the kernel provides its own <stdio.h> header and a
; printf implementation. There are no security restrictions, no
; safeguards, no debugging mechanisms, only what the kernel provides
; itself. It has absolute and complete power over the machine.
; To set up a stack, we set the esp register to point to the top of our
; stack (as it grows downwards on x86 systems). This is necessarily done
; in assembly as languages such as C cannot function without a stack.
mov esp, stack_top
; This is a good place to initialize crucial processor state before the
; high-level kernel is entered. It's best to minimize the early
; environment where crucial features are offline. Note that the
; processor is not fully initialized yet: Features such as floating
; point instructions and instruction set extensions are not initialized
; yet. The GDT should be loaded here. Paging should be enabled here.
; C++ features such as global constructors and exceptions will require
; runtime support to work as well.
lgdt [gdt_desc]
; Enter the high-level kernel. The ABI requires the stack is 16-byte
; aligned at the time of the call instruction (which afterwards pushes
; the return pointer of size 4 bytes). The stack was originally 16-byte
; aligned above and we've since pushed a multiple of 16 bytes to the
; stack since (pushed 0 bytes so far) and the alignment is thus
; preserved and the call is well defined.
; note, that if you are building on Windows, C functions may have "_" prefix in assembly: _kernel_main
extern kernel_main
call kernel_main
; If the system has nothing more to do, put the computer into an
; infinite loop. To do that:
; 1) Disable interrupts with cli (clear interrupt enable in eflags).
; They are already disabled by the bootloader, so this is not needed.
; Mind that you might later enable interrupts and return from
; kernel_main (which is sort of nonsensical to do).
; 2) Wait for the next interrupt to arrive with hlt (halt instruction).
; Since they are disabled, this will lock up the computer.
; 3) Jump to the hlt instruction if it ever wakes up due to a
; non-maskable interrupt occurring or due to system management mode.
cli
.hang: hlt
jmp .hang
.end:

View file

@ -0,0 +1,3 @@
menuentry "HhhOS" {
multiboot /boot/hhhos.bin
}

48
gdt.c Normal file
View file

@ -0,0 +1,48 @@
#include <stddef.h>
#include <stdint.h>
const struct gdt_entry {
size_t limit_low : 16;
size_t base_low : 24;
// attribute byte split into bitfields
size_t accessed : 1;
size_t read_write : 1; // readable for code, writable for data
size_t conforming_expand_down : 1; // conforming for code, expand down for data
size_t code : 1; // 1 for code, 0 for data
size_t always_1 : 1; // should be 1 for everything but TSS and LDT
size_t DPL : 2; // privilege level
size_t present : 1;
// and now into granularity
size_t limit_high : 4;
size_t available : 1;
size_t always_0 : 1; // should always be 0
size_t big : 1; // 32bit opcodes for code, uint32_t stack for data
size_t gran : 1; // 1 to use 4k page addressing, 0 for byte addressing
size_t base_high : 8;
} __attribute__((packed));
void initGDT() {
struct gdt_entry *code;
code->limit_low=0xFFFF;
code->present = 1;
code->DPL = 0;
code->always_1 = 1;
code->code = 1;
code->conforming_expand_down = 0;
code->read_write = 1;
code->accessed = 0;
code->base_high = 0;
code->gran = 1;
code->big = 0;
code->always_0 = 0;
code->available = 1;
code->limit_high = 0xF;
struct gdt_entry *data = *code;
data->code = 0;
struct gdt_entry *tss = 0;
}

3
grub.cfg Normal file
View file

@ -0,0 +1,3 @@
menuentry "HhhOS" {
multiboot /boot/hhhos.bin
}

60
include/inline.h Normal file
View file

@ -0,0 +1,60 @@
#ifndef INLINE_H_
#define INLINE_H_
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
static inline void outb(uint16_t const port, uint8_t const val) {
asm volatile ( "outb %0, %1" : : "a"(val), "Nd"(port) );
}
static inline void outw(uint16_t const port, uint16_t const val) {
asm volatile ( "outw %0, %1" : : "a"(val), "Nd"(port) );
}
static inline void outl(uint16_t const port, uint32_t const val) {
asm volatile ( "outl %0, %1" : : "a"(val), "Nd"(port) );
}
static inline uint8_t inb(uint16_t const port) {
uint8_t ret;
asm volatile ( "inb %1, %0" : "=a"(ret) : "Nd"(port) );
return ret;
}
static inline uint16_t inw(uint16_t const port) {
uint16_t ret;
asm volatile ( "inw %1, %0" : "=a"(ret) : "Nd"(port) );
return ret;
}
static inline uint32_t inl(uint16_t const port) {
uint32_t ret;
asm volatile ( "inl %1, %0" : "=a"(ret) : "Nd"(port) );
return ret;
}
static inline void io_wait(void) {
/* TODO: This is probably fragile. */
asm volatile ( "jmp 1f\n\t"
"1:jmp 2f\n\t"
"2:" );
}
static inline bool are_interrupts_enabled() {
unsigned long flags;
asm volatile ( "pushf\n\t"
"pop %0"
: "=g"(flags) );
return flags & (1 << 9);
}
#ifdef __cplusplus
}
#endif
#endif /* INLINE_H_ */

22
include/string.h Normal file
View file

@ -0,0 +1,22 @@
#ifndef _STRING_H
#define _STRING_H 1
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
int memcmp(const void* aptr, const void* bptr, size_t size);
void* memcpy(void* __restrict dstptr, const void* __restrict srcptr, size_t size);
void* memmove(void* dstptr, const void* srcptr, size_t size);
void* memset(void* bufptr, int value, size_t size);
size_t strlen(const char* str);
char* strcpy(char* dst, const char* src);
char* strcat(char* dst, const char* src);
#ifdef __cplusplus
}
#endif
#endif /* _STRING_H */

74
include/terminal.h Normal file
View file

@ -0,0 +1,74 @@
#ifndef _TERMINAL_H
#define _TERMINAL_H 1
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <inline.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Hardware text mode color constants. */
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 const fg, enum vga_color const bg) {
return fg | bg << 4;
}
static inline uint16_t vga_entry(unsigned char const uc, uint8_t const color) {
return (uint16_t) uc | (uint16_t) color << 8;
}
static const size_t VGA_WIDTH = 80;
static const size_t VGA_HEIGHT = 25;
size_t terminal_row;
size_t terminal_column;
uint8_t terminal_color;
uint16_t* terminal_buffer;
void terminal_clear(void);
void terminal_initialize(void);
void terminal_setcolor(uint8_t color);
void terminal_clearline(size_t line);
void terminal_clearlines(size_t from, size_t to);
void terminal_updatecursor(void);
void terminal_scrollup(size_t lines);
void terminal_checknewline(void);
void terminal_putentryat(char c, uint8_t color, size_t x, size_t y);
void terminal_putchar(char c);
void terminal_write(const char* data, size_t size);
static inline void terminal_writestring(const char* data) {
terminal_write(data, strlen(data));
}
static inline void terminal_writeline(const char* data) {
terminal_writestring(data);
terminal_putchar('\n');
}
#ifdef __cplusplus
}
#endif
#endif /* _TERMINAL_H */

33
kernel.c Normal file
View file

@ -0,0 +1,33 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <terminal.h>
/* Check if the compiler thinks you are targeting the wrong operating system. */
#if defined(__linux__)
#error "You are not using a cross-compiler, you will most certainly run into trouble"
#endif
/* This tutorial will only work for the 32-bit ix86 targets. */
#if !defined(__i386__)
#error "This tutorial needs to be compiled with a ix86-elf compiler"
#endif
static size_t rand_next = 1;
int rand(void) {
rand_next = rand_next * 1103515245 + 12345;
return (size_t)(rand_next/65536) % 32768;
}
void srand(size_t seed) {
rand_next = seed;
}
void kernel_main() {
terminal_initialize();
while(true)
terminal_writestring("gnhijsrg");
}

43
linker.ld Normal file
View file

@ -0,0 +1,43 @@
/* The bootloader will look at this image and start execution at the symbol
designated as the entry point. */
ENTRY(_start)
/* Tell where the various sections of the object files will be put in the final
kernel image. */
SECTIONS
{
/* Begin putting sections at 1 MiB, a conventional place for kernels to be
loaded at by the bootloader. */
. = 1M;
/* First put the multiboot header, as it is required to be put very early
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. */
}

57
string.c Normal file
View file

@ -0,0 +1,57 @@
#include <string.h>
int memcmp(const void* aptr, const void* bptr, size_t size) {
const unsigned char* a = (const unsigned char*) aptr;
const unsigned char* b = (const unsigned char*) bptr;
for (size_t i = 0; i < size; i++) {
if (a[i] < b[i])
return -1;
else if (b[i] < a[i])
return 1;
}
return 0;
}
void* memcpy(void* restrict dstptr, const void* restrict srcptr, size_t size) {
unsigned char* dst = (unsigned char*) dstptr;
const unsigned char* src = (const unsigned char*) srcptr;
for (size_t i = 0; i < size; i++)
dst[i] = src[i];
return dstptr;
}
void* memmove(void* dstptr, const void* srcptr, size_t size) {
unsigned char* dst = (unsigned char*) dstptr;
const unsigned char* src = (const unsigned char*) srcptr;
if (dst < src) {
for (size_t i = 0; i < size; i++)
dst[i] = src[i];
} else {
for (size_t i = size; i != 0; i--)
dst[i-1] = src[i-1];
}
return dstptr;
}
void* memset(void* bufptr, int value, size_t size) {
unsigned char* buf = (unsigned char*) bufptr;
for (size_t i = 0; i < size; i++)
buf[i] = (unsigned char) value;
return bufptr;
}
size_t strlen(const char* str) {
size_t len = 0;
while (str[len])
len++;
return len;
}
char* strcpy(char* dst, const char* src) {
return memcpy(dst, src, strlen(src) + 1);
}
char* strcat(char* dst, const char* src) {
strcpy(dst + strlen(dst), src);
return dst;
}

99
terminal.c Normal file
View file

@ -0,0 +1,99 @@
#include <terminal.h>
void terminal_clear(void) {
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_initialize(void) {
terminal_row = 0;
terminal_column = 0;
terminal_color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK);
terminal_buffer = (uint16_t*) 0xB8000;
terminal_clear();
}
void terminal_setcolor(uint8_t color) {
terminal_color = color;
}
void terminal_clearline(size_t line) {
for (size_t x = 0; x < VGA_WIDTH; x++) {
const size_t index = line * VGA_WIDTH + x;
terminal_buffer[index] = vga_entry(' ', terminal_color);
}
}
void terminal_clearlines(size_t from, size_t to) {
for (size_t y = from; y <= to; 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_updatecursor(void) {
size_t temp = terminal_row * VGA_WIDTH + terminal_column;
outb(0x3D4, 14);
outb(0x3D5, temp >> 8);
outb(0x3D4, 15);
outb(0x3D5, temp);
}
void terminal_scrollup(size_t lines) {
terminal_clearlines(0, lines - 1);
for (size_t index = 0; index < VGA_WIDTH * (VGA_HEIGHT - 1) * 2; index++) {
terminal_buffer[index] = terminal_buffer[index + VGA_WIDTH * 2 * lines];
}
terminal_clearlines(VGA_HEIGHT - 1 - lines, VGA_HEIGHT - 1);
if ((terminal_row - lines) < 0) {
terminal_row = 0;
terminal_column = 0;
} else {
terminal_row -= lines;
}
terminal_updatecursor();
}
void terminal_checknewline(void) {
if (terminal_row >= VGA_HEIGHT - 1) {
terminal_scrollup(1);
}
}
void terminal_putentryat(char c, uint8_t color, size_t x, size_t y) {
terminal_buffer[y * VGA_WIDTH + x] = vga_entry(c, color);
}
void terminal_putchar(char c) {
switch (c) {
case '\r':
terminal_column = 0;
break;
case '\n':
terminal_column = 0;
terminal_row++;
break;
default:
terminal_putentryat(c, terminal_color, terminal_column, terminal_row);
terminal_column++;
}
if (terminal_column >= VGA_WIDTH) {
terminal_column = 0;
terminal_row++;
}
terminal_updatecursor();
terminal_checknewline();
}
void terminal_write(const char* data, size_t size) {
for (size_t i = 0; i < size; i++)
terminal_putchar(data[i]);
}