Browse Source

Initial operating system.

master
Jonas 'Sortie' Termansen 4 years ago
parent
commit
084d1624be

+ 3
- 0
.gitignore View File

@@ -0,0 +1,3 @@
*.iso
isodir
sysroot

+ 7
- 0
build.sh View File

@@ -0,0 +1,7 @@
#!/bin/sh
set -e
. ./headers.sh

for PROJECT in $PROJECTS; do
(cd $PROJECT && DESTDIR="$SYSROOT" $MAKE install)
done

+ 11
- 0
clean.sh View File

@@ -0,0 +1,11 @@
#!/bin/sh
set -e
. ./config.sh

for PROJECT in $PROJECTS; do
(cd $PROJECT && $MAKE clean)
done

rm -rf sysroot
rm -rf isodir
rm -rf myos.iso

+ 28
- 0
config.sh View File

@@ -0,0 +1,28 @@
SYSTEM_HEADER_PROJECTS="libc kernel"
PROJECTS="libc kernel"

export MAKE=${MAKE:-make}
export HOST=${HOST:-$(./default-host.sh)}

export AR=${HOST}-ar
export AS=${HOST}-as
export CC=${HOST}-gcc

export PREFIX=/usr
export EXEC_PREFIX=$PREFIX
export BOOTDIR=/boot
export LIBDIR=$EXEC_PREFIX/lib
export INCLUDEDIR=$PREFIX/include

export CFLAGS='-O2 -g'
export CPPFLAGS=''

# Configure the cross-compiler to use the desired system root.
export SYSROOT="$(pwd)/sysroot"
export CC="$CC --sysroot=$SYSROOT"

# Work around that the -elf gcc targets doesn't have a system include directory
# because it was configured with --without-headers rather than --with-sysroot.
if echo "$HOST" | grep -Eq -- '-elf($|-)'; then
export CC="$CC -isystem=$INCLUDEDIR"
fi

+ 2
- 0
default-host.sh View File

@@ -0,0 +1,2 @@
#!/bin/sh
echo i686-elf

+ 9
- 0
headers.sh View File

@@ -0,0 +1,9 @@
#!/bin/sh
set -e
. ./config.sh

mkdir -p "$SYSROOT"

for PROJECT in $SYSTEM_HEADER_PROJECTS; do
(cd $PROJECT && DESTDIR="$SYSROOT" $MAKE install-headers)
done

+ 15
- 0
iso.sh View File

@@ -0,0 +1,15 @@
#!/bin/sh
set -e
. ./build.sh

mkdir -p isodir
mkdir -p isodir/boot
mkdir -p isodir/boot/grub

cp sysroot/boot/myos.kernel isodir/boot/myos.kernel
cat > isodir/boot/grub/grub.cfg << EOF
menuentry "myos" {
multiboot /boot/myos.kernel
}
EOF
grub-mkrescue -o myos.iso isodir

+ 3
- 0
kernel/.gitignore View File

@@ -0,0 +1,3 @@
*.d
*.kernel
*.o

+ 83
- 0
kernel/Makefile View File

@@ -0,0 +1,83 @@
DEFAULT_HOST!=../default-host.sh
HOST?=DEFAULT_HOST
HOSTARCH!=../target-triplet-to-arch.sh $(HOST)

CFLAGS?=-O2 -g
CPPFLAGS?=
LDFLAGS?=
LIBS?=

DESTDIR?=
PREFIX?=/usr/local
EXEC_PREFIX?=$(PREFIX)
BOOTDIR?=$(EXEC_PREFIX)/boot
INCLUDEDIR?=$(PREFIX)/include

CFLAGS:=$(CFLAGS) -ffreestanding -Wall -Wextra
CPPFLAGS:=$(CPPFLAGS) -D__is_kernel -Iinclude
LDFLAGS:=$(LDFLAGS)
LIBS:=$(LIBS) -nostdlib -lk -lgcc

ARCHDIR=arch/$(HOSTARCH)

include $(ARCHDIR)/make.config

CFLAGS:=$(CFLAGS) $(KERNEL_ARCH_CFLAGS)
CPPFLAGS:=$(CPPFLAGS) $(KERNEL_ARCH_CPPFLAGS)
LDFLAGS:=$(LDFLAGS) $(KERNEL_ARCH_LDFLAGS)
LIBS:=$(LIBS) $(KERNEL_ARCH_LIBS)

KERNEL_OBJS=\
$(KERNEL_ARCH_OBJS) \
kernel/kernel.o \

OBJS=\
$(ARCHDIR)/crti.o \
$(ARCHDIR)/crtbegin.o \
$(KERNEL_OBJS) \
$(ARCHDIR)/crtend.o \
$(ARCHDIR)/crtn.o \

LINK_LIST=\
$(LDFLAGS) \
$(ARCHDIR)/crti.o \
$(ARCHDIR)/crtbegin.o \
$(KERNEL_OBJS) \
$(LIBS) \
$(ARCHDIR)/crtend.o \
$(ARCHDIR)/crtn.o \

.PHONY: all clean install install-headers install-kernel
.SUFFIXES: .o .c .S

all: myos.kernel

myos.kernel: $(OBJS) $(ARCHDIR)/linker.ld
$(CC) -T $(ARCHDIR)/linker.ld -o [email protected] $(CFLAGS) $(LINK_LIST)
grub-file --is-x86-multiboot myos.kernel

$(ARCHDIR)/crtbegin.o $(ARCHDIR)/crtend.o:
OBJ=`$(CC) $(CFLAGS) $(LDFLAGS) -print-file-name=$(@F)` && cp "$$OBJ" [email protected]

.c.o:
$(CC) -MD -c $< -o [email protected] -std=gnu11 $(CFLAGS) $(CPPFLAGS)

.S.o:
$(CC) -MD -c $< -o [email protected] $(CFLAGS) $(CPPFLAGS)

clean:
rm -f myos.kernel
rm -f $(OBJS) *.o */*.o */*/*.o
rm -f $(OBJS:.o=.d) *.d */*.d */*/*.d

install: install-headers install-kernel

install-headers:
mkdir -p $(DESTDIR)$(INCLUDEDIR)
cp -R --preserve=timestamps include/. $(DESTDIR)$(INCLUDEDIR)/.

install-kernel: myos.kernel
mkdir -p $(DESTDIR)$(BOOTDIR)
cp myos.kernel $(DESTDIR)$(BOOTDIR)

-include $(OBJS:.o=.d)

+ 39
- 0
kernel/arch/i386/boot.S View File

@@ -0,0 +1,39 @@
# Declare constants for the multiboot header.
.set ALIGN, 1<<0 # align loaded modules on page boundaries
.set MEMINFO, 1<<1 # provide memory map
.set FLAGS, ALIGN | MEMINFO # this is the Multiboot 'flag' field
.set MAGIC, 0x1BADB002 # 'magic number' lets bootloader find the header
.set CHECKSUM, -(MAGIC + FLAGS) # checksum of above, to prove we are multiboot

# Declare a header as in the Multiboot Standard.
.section .multiboot
.align 4
.long MAGIC
.long FLAGS
.long CHECKSUM

# Reserve a stack for the initial thread.
.section .bss
.align 16
stack_bottom:
.skip 16384 # 16 KiB
stack_top:

# The kernel entry point.
.section .text
.global _start
.type _start, @function
_start:
movl $stack_top, %esp

# Call the global constructors.
call _init

# Transfer control to the main kernel.
call kernel_main

# Hang if kernel_main unexpectedly returns.
cli
1: hlt
jmp 1b
.size _start, . - _start

+ 15
- 0
kernel/arch/i386/crti.S View File

@@ -0,0 +1,15 @@
.section .init
.global _init
.type _init, @function
_init:
push %ebp
movl %esp, %ebp
/* gcc will nicely put the contents of crtbegin.o's .init section here. */

.section .fini
.global _fini
.type _fini, @function
_fini:
push %ebp
movl %esp, %ebp
/* gcc will nicely put the contents of crtbegin.o's .fini section here. */

+ 9
- 0
kernel/arch/i386/crtn.S View File

@@ -0,0 +1,9 @@
.section .init
/* gcc will nicely put the contents of crtend.o's .init section here. */
popl %ebp
ret

.section .fini
/* gcc will nicely put the contents of crtend.o's .fini section here. */
popl %ebp
ret

+ 43
- 0
kernel/arch/i386/linker.ld View File

@@ -0,0 +1,43 @@
/* The bootloader will look at this image and start execution at the symbol
designated at 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, put them in the proper place in
in this file, if you'd like to include them in the final kernel. */
}

+ 8
- 0
kernel/arch/i386/make.config View File

@@ -0,0 +1,8 @@
KERNEL_ARCH_CFLAGS=
KERNEL_ARCH_CPPFLAGS=
KERNEL_ARCH_LDFLAGS=
KERNEL_ARCH_LIBS=

KERNEL_ARCH_OBJS=\
$(ARCHDIR)/boot.o \
$(ARCHDIR)/tty.o \

+ 58
- 0
kernel/arch/i386/tty.c View File

@@ -0,0 +1,58 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>

#include <kernel/tty.h>

#include "vga.h"

static const size_t VGA_WIDTH = 80;
static const size_t VGA_HEIGHT = 25;
static uint16_t* const VGA_MEMORY = (uint16_t*) 0xB8000;

static size_t terminal_row;
static size_t terminal_column;
static uint8_t terminal_color;
static uint16_t* terminal_buffer;

void terminal_initialize(void) {
terminal_row = 0;
terminal_column = 0;
terminal_color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK);
terminal_buffer = VGA_MEMORY;
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(unsigned 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) {
unsigned char uc = c;
terminal_putentryat(uc, 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));
}

+ 33
- 0
kernel/arch/i386/vga.h View File

@@ -0,0 +1,33 @@
#ifndef ARCH_I386_VGA_H
#define ARCH_I386_VGA_H

#include <stdint.h>

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;
}

#endif

+ 11
- 0
kernel/include/kernel/tty.h View File

@@ -0,0 +1,11 @@
#ifndef _KERNEL_TTY_H
#define _KERNEL_TTY_H

#include <stddef.h>

void terminal_initialize(void);
void terminal_putchar(char c);
void terminal_write(const char* data, size_t size);
void terminal_writestring(const char* data);

#endif

+ 8
- 0
kernel/kernel/kernel.c View File

@@ -0,0 +1,8 @@
#include <stdio.h>

#include <kernel/tty.h>

void kernel_main(void) {
terminal_initialize();
printf("Hello, kernel World!\n");
}

+ 3
- 0
libc/.gitignore View File

@@ -0,0 +1,3 @@
*.a
*.d
*.o

+ 93
- 0
libc/Makefile View File

@@ -0,0 +1,93 @@
DEFAULT_HOST!=../default-host.sh
HOST?=DEFAULT_HOST
HOSTARCH!=../target-triplet-to-arch.sh $(HOST)

CFLAGS?=-O2 -g
CPPFLAGS?=
LDFLAGS?=
LIBS?=

DESTDIR?=
PREFIX?=/usr/local
EXEC_PREFIX?=$(PREFIX)
INCLUDEDIR?=$(PREFIX)/include
LIBDIR?=$(EXEC_PREFIX)/lib

CFLAGS:=$(CFLAGS) -ffreestanding -Wall -Wextra
CPPFLAGS:=$(CPPFLAGS) -D__is_libc -Iinclude
LIBK_CFLAGS:=$(CFLAGS)
LIBK_CPPFLAGS:=$(CPPFLAGS) -D__is_libk

ARCHDIR=arch/$(HOSTARCH)

include $(ARCHDIR)/make.config

CFLAGS:=$(CFLAGS) $(ARCH_CFLAGS)
CPPFLAGS:=$(CPPFLAGS) $(ARCH_CPPFLAGS)
LIBK_CFLAGS:=$(LIBK_CFLAGS) $(KERNEL_ARCH_CFLAGS)
LIBK_CPPFLAGS:=$(LIBK_CPPFLAGS) $(KERNEL_ARCH_CPPFLAGS)

FREEOBJS=\
$(ARCH_FREEOBJS) \
stdio/printf.o \
stdio/putchar.o \
stdio/puts.o \
stdlib/abort.o \
string/memcmp.o \
string/memcpy.o \
string/memmove.o \
string/memset.o \
string/strlen.o \

HOSTEDOBJS=\
$(ARCH_HOSTEDOBJS) \

OBJS=\
$(FREEOBJS) \
$(HOSTEDOBJS) \

LIBK_OBJS=$(FREEOBJS:.o=.libk.o)

#BINARIES=libc.a libk.a # Not ready for libc yet.
BINARIES=libk.a

.PHONY: all clean install install-headers install-libs
.SUFFIXES: .o .libk.o .c .S

all: $(BINARIES)

libc.a: $(OBJS)
$(AR) rcs [email protected] $(OBJS)

libk.a: $(LIBK_OBJS)
$(AR) rcs [email protected] $(LIBK_OBJS)

.c.o:
$(CC) -MD -c $< -o [email protected] -std=gnu11 $(CFLAGS) $(CPPFLAGS)

.c.S:
$(CC) -MD -c $< -o [email protected] $(CFLAGS) $(CPPFLAGS)

.c.libk.o:
$(CC) -MD -c $< -o [email protected] -std=gnu11 $(LIBK_CFLAGS) $(LIBK_CPPFLAGS)

.S.libk.o:
$(CC) -MD -c $< -o [email protected] $(LIBK_CFLAGS) $(LIBK_CPPFLAGS)

clean:
rm -f $(BINARIES) *.a
rm -f $(OBJS) $(LIBK_OBJS) *.o */*.o */*/*.o
rm -f $(OBJS:.o=.d) $(LIBK_OBJS:.o=.d) *.d */*.d */*/*.d

install: install-headers install-libs

install-headers:
mkdir -p $(DESTDIR)$(INCLUDEDIR)
cp -R --preserve=timestamps include/. $(DESTDIR)$(INCLUDEDIR)/.

install-libs: $(BINARIES)
mkdir -p $(DESTDIR)$(LIBDIR)
cp $(BINARIES) $(DESTDIR)$(LIBDIR)

-include $(OBJS:.o=.d)
-include $(LIBK_OBJS:.o=.d)

+ 8
- 0
libc/arch/i386/make.config View File

@@ -0,0 +1,8 @@
ARCH_CFLAGS=
ARCH_CPPFLAGS=
KERNEL_ARCH_CFLAGS=
KERNEL_ARCH_CPPFLAGS=

ARCH_FREEOBJS=\

ARCH_HOSTEDOBJS=\

+ 20
- 0
libc/include/stdio.h View File

@@ -0,0 +1,20 @@
#ifndef _STDIO_H
#define _STDIO_H 1

#include <sys/cdefs.h>

#define EOF (-1)

#ifdef __cplusplus
extern "C" {
#endif

int printf(const char* __restrict, ...);
int putchar(int);
int puts(const char*);

#ifdef __cplusplus
}
#endif

#endif

+ 17
- 0
libc/include/stdlib.h View File

@@ -0,0 +1,17 @@
#ifndef _STDLIB_H
#define _STDLIB_H 1

#include <sys/cdefs.h>

#ifdef __cplusplus
extern "C" {
#endif

__attribute__((__noreturn__))
void abort(void);

#ifdef __cplusplus
}
#endif

#endif

+ 22
- 0
libc/include/string.h View File

@@ -0,0 +1,22 @@
#ifndef _STRING_H
#define _STRING_H 1

#include <sys/cdefs.h>

#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

int memcmp(const void*, const void*, size_t);
void* memcpy(void* __restrict, const void* __restrict, size_t);
void* memmove(void*, const void*, size_t);
void* memset(void*, int, size_t);
size_t strlen(const char*);

#ifdef __cplusplus
}
#endif

#endif

+ 6
- 0
libc/include/sys/cdefs.h View File

@@ -0,0 +1,6 @@
#ifndef _SYS_CDEFS_H
#define _SYS_CDEFS_H 1

#define __myos_libc 1

#endif

+ 80
- 0
libc/stdio/printf.c View File

@@ -0,0 +1,80 @@
#include <limits.h>
#include <stdbool.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>

static bool print(const char* data, size_t length) {
const unsigned char* bytes = (const unsigned char*) data;
for (size_t i = 0; i < length; i++)
if (putchar(bytes[i]) == EOF)
return false;
return true;
}

int printf(const char* restrict format, ...) {
va_list parameters;
va_start(parameters, format);

int written = 0;

while (*format != '\0') {
size_t maxrem = INT_MAX - written;

if (format[0] != '%' || format[1] == '%') {
if (format[0] == '%')
format++;
size_t amount = 1;
while (format[amount] && format[amount] != '%')
amount++;
if (maxrem < amount) {
// TODO: Set errno to EOVERFLOW.
return -1;
}
if (!print(format, amount))
return -1;
format += amount;
written += amount;
continue;
}

const char* format_begun_at = format++;

if (*format == 'c') {
format++;
char c = (char) va_arg(parameters, int /* char promotes to int */);
if (!maxrem) {
// TODO: Set errno to EOVERFLOW.
return -1;
}
if (!print(&c, sizeof(c)))
return -1;
written++;
} else if (*format == 's') {
format++;
const char* str = va_arg(parameters, const char*);
size_t len = strlen(str);
if (maxrem < len) {
// TODO: Set errno to EOVERFLOW.
return -1;
}
if (!print(str, len))
return -1;
written += len;
} else {
format = format_begun_at;
size_t len = strlen(format);
if (maxrem < len) {
// TODO: Set errno to EOVERFLOW.
return -1;
}
if (!print(format, len))
return -1;
written += len;
format += len;
}
}

va_end(parameters);
return written;
}

+ 15
- 0
libc/stdio/putchar.c View File

@@ -0,0 +1,15 @@
#include <stdio.h>

#if defined(__is_libk)
#include <kernel/tty.h>
#endif

int putchar(int ic) {
#if defined(__is_libk)
char c = (char) ic;
terminal_write(&c, sizeof(c));
#else
// TODO: Implement stdio and the write system call.
#endif
return ic;
}

+ 5
- 0
libc/stdio/puts.c View File

@@ -0,0 +1,5 @@
#include <stdio.h>

int puts(const char* string) {
return printf("%s\n", string);
}

+ 15
- 0
libc/stdlib/abort.c View File

@@ -0,0 +1,15 @@
#include <stdio.h>
#include <stdlib.h>

__attribute__((__noreturn__))
void abort(void) {
#if defined(__is_libk)
// TODO: Add proper kernel panic.
printf("kernel: panic: abort()\n");
#else
// TODO: Abnormally terminate the process as if by SIGABRT.
printf("abort()\n");
#endif
while (1) { }
__builtin_unreachable();
}

+ 13
- 0
libc/string/memcmp.c View File

@@ -0,0 +1,13 @@
#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;
}

+ 9
- 0
libc/string/memcpy.c View File

@@ -0,0 +1,9 @@
#include <string.h>

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;
}

+ 14
- 0
libc/string/memmove.c View File

@@ -0,0 +1,14 @@
#include <string.h>

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;
}

+ 8
- 0
libc/string/memset.c View File

@@ -0,0 +1,8 @@
#include <string.h>

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;
}

+ 8
- 0
libc/string/strlen.c View File

@@ -0,0 +1,8 @@
#include <string.h>

size_t strlen(const char* str) {
size_t len = 0;
while (str[len])
len++;
return len;
}

+ 5
- 0
qemu.sh View File

@@ -0,0 +1,5 @@
#!/bin/sh
set -e
. ./iso.sh

qemu-system-$(./target-triplet-to-arch.sh $HOST) -cdrom myos.iso

+ 6
- 0
target-triplet-to-arch.sh View File

@@ -0,0 +1,6 @@
#!/bin/sh
if echo "$1" | grep -Eq 'i[[:digit:]]86-'; then
echo i386
else
echo "$1" | grep -Eo '^[[:alnum:]_]*'
fi

Loading…
Cancel
Save