Merge kernel

This commit is contained in:
K. Lange 2018-03-16 21:56:19 +09:00 committed by Kevin Lange
parent 690a47836a
commit 3f4293d357
135 changed files with 35357 additions and 5 deletions

View File

@ -1,9 +1,10 @@
APPS=init hello sh ls terminal uname compositor drawlines background session kdebug cat yutani-test sysinfo hostname yutani-query env mount date echo nyancat kill ps pstree bim terminal-vga cursor-off font-server migrate free uptime
KERNEL_TARGET=i686-elf
KERNEL_TARGET=i686-pc-toaru
KCC = $(KERNEL_TARGET)-gcc
KAS = $(KERNEL_TARGET)-as
KLD = $(KERNEL_TARGET)-ld
KNM = $(KERNEL_TARGET)-nm
CC=i686-pc-toaru-gcc
AR=i686-pc-toaru-ar
@ -16,6 +17,54 @@ APPS_X=$(foreach app,$(APPS),base/bin/$(app))
all: image.iso
# Kernel
KCFLAGS = -O2 -std=c99
KCFLAGS += -finline-functions -ffreestanding
KCFLAGS += -Wall -Wextra -Wno-unused-function -Wno-unused-parameter -Wno-format
KCFLAGS += -pedantic -fno-omit-frame-pointer
KCFLAGS += -D_KERNEL_
KCFLAGS += -DKERNEL_GIT_TAG=$(shell util/make-version)
KASFLAGS = --32
KERNEL_OBJS = $(patsubst %.c,%.o,$(wildcard kernel/*.c))
KERNEL_OBJS += $(patsubst %.c,%.o,$(wildcard kernel/*/*.c))
KERNEL_OBJS += $(patsubst %.c,%.o,$(wildcard kernel/*/*/*.c))
KERNEL_ASMOBJS = $(filter-out kernel/symbols.o,$(patsubst %.S,%.o,$(wildcard kernel/*.S)))
cdrom/kernel: ${KERNEL_ASMOBJS} ${KERNEL_OBJS} kernel/symbols.o
${KCC} -T kernel/link.ld ${KCFLAGS} -nostdlib -o $@ ${KERNEL_ASMOBJS} ${KERNEL_OBJS} kernel/symbols.o -lgcc
kernel/symbols.o: ${KERNEL_ASMOBJS} ${KERNEL_OBJS} util/generate_symbols.py
-rm -f kernel/symbols.o
${KCC} -T kernel/link.ld ${KCFLAGS} -nostdlib -o .toaruos-kernel ${KERNEL_ASMOBJS} ${KERNEL_OBJS} -lgcc
${KNM} .toaruos-kernel -g | util/generate_symbols.py > kernel/symbols.S
${KAS} ${KASFLAGS} kernel/symbols.S -o $@
-rm -f .toaruos-kernel
kernel/sys/version.o: kernel/*/*.c kernel/*.c
cdrom/mod:
@mkdir -p $@
MODULES = $(patsubst modules/%.c,cdrom/mod/%.ko,$(wildcard modules/*.c))
HEADERS = $(shell find kernel/include/ -type f -name '*.h')
cdrom/mod/%.ko: modules/%.c ${HEADERS} | cdrom/mod
${KCC} -T modules/link.ld -I./kernel/include -nostdlib ${KCFLAGS} -c -o $@ $<
modules: ${MODULES}
kernel/%.o: kernel/%.S
${KAS} ${ASFLAGS} $< -o $@
kernel/%.o: kernel/%.c ${HEADERS}
${KCC} ${KCFLAGS} -nostdlib -g -I./kernel/include -c -o $@ $<
# Root Filesystem
base/dev:
mkdir -p base/dev
base/tmp:
@ -30,18 +79,24 @@ cdrom/boot:
mkdir -p cdrom/boot
dirs: base/dev base/tmp base/proc base/bin base/lib cdrom/boot
# C Library
libc/%.o: libc/%.c
$(CC) -fPIC -c -m32 -Wa,--32 -O3 -isystem include -o $@ $<
base/lib/ld.so: linker/linker.c base/lib/libnihc.a | dirs
$(CC) -static -Wl,-static $(CFLAGS) -o $@ -Os -T linker/link.ld $< $(LIBS)
base/lib/libnihc.a: ${LIBC_OBJS} | dirs
$(AR) cr $@ $^
base/lib/libnihc.so: ${LIBC_OBJS} | dirs
$(CC) -o $@ $(CFLAGS) -shared -fPIC $^
# Userspace Linker/Loader
base/lib/ld.so: linker/linker.c base/lib/libnihc.a | dirs
$(CC) -static -Wl,-static $(CFLAGS) -o $@ -Os -T linker/link.ld $< $(LIBS)
# Shared Libraries
base/lib/libtoaru_graphics.so: lib/graphics.c lib/graphics.h
$(CC) -o $@ $(CFLAGS) -shared -fPIC $<
@ -81,12 +136,18 @@ base/lib/libtoaru_drawstring.so: lib/drawstring.c lib/drawstring.h base/lib/libt
base/lib/libtoaru_decorations.so: lib/decorations.c lib/decorations.h base/lib/libtoaru_graphics.so
$(CC) -o $@ $(CFLAGS) -shared -fPIC $< -ltoaru_graphics
# Decoration Themes
base/lib/libtoaru-decor-fancy.so: decors/decor-fancy.c lib/decorations.h base/lib/libtoaru_graphics.so base/lib/libtoaru_decorations.so base/lib/libtoaru_drawstring.so
$(CC) -o $@ $(CFLAGS) -shared -fPIC $< -ltoaru_decorations -ltoaru_drawstring -ltoaru_graphics
# Init
base/bin/init: apps/init.c base/lib/libnihc.a | dirs
$(CC) -static -Wl,-static $(CFLAGS) -o $@ $< $(LIBS)
# Userspace
base/bin/sh: apps/sh.c base/lib/libnihc.so base/lib/libtoaru_list.so base/lib/libtoaru_rline.so
$(CC) $(CFLAGS) -o $@ $< -ltoaru_rline -ltoaru_list -ltoaru_kbd $(LIBS)
@ -132,12 +193,18 @@ base/bin/pstree: apps/pstree.c base/lib/libnihc.so base/lib/libtoaru_tree.so bas
base/bin/%: apps/%.c base/lib/libnihc.so | dirs
$(CC) $(CFLAGS) -o $@ $< $(LIBS)
# Ramdisk
cdrom/ramdisk.img: ${APPS_X} base/lib/ld.so base/lib/libtoaru-decor-fancy.so Makefile | dirs
genext2fs -B 4096 -d base -U -b 4096 -N 2048 cdrom/ramdisk.img
image.iso: cdrom/ramdisk.img cdrom/boot/boot.sys cdrom/kernel
# CD image
image.iso: cdrom/ramdisk.img cdrom/boot/boot.sys cdrom/kernel ${MODULES}
xorriso -as mkisofs -R -J -c boot/bootcat -b boot/boot.sys -no-emul-boot -boot-load-size 20 -o image.iso cdrom
# Boot loader
cdrom/boot/boot.sys: boot/boot.o boot/cstuff.o boot/link.ld | cdrom/boot
${KLD} -T boot/link.ld -o $@ boot/boot.o boot/cstuff.o
@ -157,3 +224,7 @@ clean:
rm -f cdrom/ramdisk.img
rm -f cdrom/boot/boot.sys
rm -f boot/*.o
rm -f cdrom/kernel
rm -f ${KERNEL_OBJS} ${KERNEL_ASMOBJS}
rm -f ${MODULES}

3
kernel/.gdb_history Normal file
View File

@ -0,0 +1,3 @@
info line *0x00112449
info line *0x00100ba7
info line *0x00100ba7

60
kernel/boot.S Normal file
View File

@ -0,0 +1,60 @@
.set MB_MAGIC, 0x1BADB002
.set MB_FLAG_PAGE_ALIGN, 1 << 0
.set MB_FLAG_MEMORY_INFO, 1 << 1
.set MB_FLAG_GRAPHICS, 1 << 2
.set MB_FLAGS, MB_FLAG_PAGE_ALIGN | MB_FLAG_MEMORY_INFO | MB_FLAG_GRAPHICS
.set MB_CHECKSUM, -(MB_MAGIC + MB_FLAGS)
.section .multiboot
.align 4
/* Multiboot section */
.long MB_MAGIC
.long MB_FLAGS
.long MB_CHECKSUM
.long 0x00000000 /* header_addr */
.long 0x00000000 /* load_addr */
.long 0x00000000 /* load_end_addr */
.long 0x00000000 /* bss_end_addr */
.long 0x00000000 /* entry_addr */
/* Request linear graphics mode */
.long 0x00000000
.long 0
.long 0
.long 32
/* .stack resides in .bss */
.section .stack, "aw", @nobits
stack_bottom:
.skip 32768 /* 32KiB */
stack_top:
.section .text
.global start
.type start, @function
.extern kmain
.type kmain, @function
start:
/* Setup our stack */
mov $stack_top, %esp
/* Make sure our stack is 16-byte aligned */
and $-16, %esp
pushl %esp
pushl %eax /* Multiboot header magic */
pushl %ebx /* Multiboot header pointer */
/* Disable interrupts and call kernel proper */
cli
call kmain
/* Clear interrupts and hang if we return from kmain */
cli
hang:
hlt
jmp hang

102
kernel/cpu/gdt.c Normal file
View File

@ -0,0 +1,102 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2011-2013 Kevin Lange
* Copyright (C) 2015 Dale Weiler
*
* Global Descriptor Tables module
*
*/
#include <system.h>
#include <logging.h>
#include <tss.h>
typedef struct {
/* Limits */
uint16_t limit_low;
/* Segment address */
uint16_t base_low;
uint8_t base_middle;
/* Access modes */
uint8_t access;
uint8_t granularity;
uint8_t base_high;
} __attribute__((packed)) gdt_entry_t;
typedef struct {
uint16_t limit;
uintptr_t base;
} __attribute__((packed)) gdt_pointer_t;
/* In the future we may need to put a lock on the access of this */
static struct {
gdt_entry_t entries[6];
gdt_pointer_t pointer;
tss_entry_t tss;
} gdt __attribute__((used));
extern void gdt_flush(uintptr_t);
#define ENTRY(X) (gdt.entries[(X)])
void gdt_set_gate(uint8_t num, uint64_t base, uint64_t limit, uint8_t access, uint8_t gran) {
/* Base Address */
ENTRY(num).base_low = (base & 0xFFFF);
ENTRY(num).base_middle = (base >> 16) & 0xFF;
ENTRY(num).base_high = (base >> 24) & 0xFF;
/* Limits */
ENTRY(num).limit_low = (limit & 0xFFFF);
ENTRY(num).granularity = (limit >> 16) & 0X0F;
/* Granularity */
ENTRY(num).granularity |= (gran & 0xF0);
/* Access flags */
ENTRY(num).access = access;
}
static void write_tss(int32_t num, uint16_t ss0, uint32_t esp0);
void gdt_install(void) {
gdt_pointer_t *gdtp = &gdt.pointer;
gdtp->limit = sizeof gdt.entries - 1;
gdtp->base = (uintptr_t)&ENTRY(0);
gdt_set_gate(0, 0, 0, 0, 0); /* NULL segment */
gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); /* Code segment */
gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); /* Data segment */
gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); /* User code */
gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); /* User data */
write_tss(5, 0x10, 0x0);
/* Go go go */
gdt_flush((uintptr_t)gdtp);
tss_flush();
}
static void write_tss(int32_t num, uint16_t ss0, uint32_t esp0) {
tss_entry_t * tss = &gdt.tss;
uintptr_t base = (uintptr_t)tss;
uintptr_t limit = base + sizeof *tss;
/* Add the TSS descriptor to the GDT */
gdt_set_gate(num, base, limit, 0xE9, 0x00);
memset(tss, 0x0, sizeof *tss);
tss->ss0 = ss0;
tss->esp0 = esp0;
tss->cs = 0x0b;
tss->ss = 0x13;
tss->ds = 0x13;
tss->es = 0x13;
tss->fs = 0x13;
tss->gs = 0x13;
tss->iomap_base = sizeof *tss;
}
void set_kernel_stack(uintptr_t stack) {
/* Set the kernel stack */
gdt.tss.esp0 = stack;
}

53
kernel/cpu/idt.c Normal file
View File

@ -0,0 +1,53 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2011-2013 Kevin Lange
* Copyright (C) 2015 Dale Weiler
*
* Interrupt Descriptor Tables
*
*/
#include <system.h>
#include <logging.h>
typedef struct {
uint16_t base_low;
uint16_t sel;
uint8_t zero;
uint8_t flags;
uint16_t base_high;
} __attribute__((packed)) idt_entry_t;
typedef struct {
uint16_t limit;
uintptr_t base;
} __attribute__((packed)) idt_pointer_t;
/* In the future we may need to put a lock on the access of this */
static struct {
idt_entry_t entries[256];
idt_pointer_t pointer;
} idt __attribute__((used));
#define ENTRY(X) (idt.entries[(X)])
typedef void (*idt_gate_t)(void);
extern void idt_load(uintptr_t);
void idt_set_gate(uint8_t num, idt_gate_t base, uint16_t sel, uint8_t flags) {
ENTRY(num).base_low = ((uintptr_t)base & 0xFFFF);
ENTRY(num).base_high = ((uintptr_t)base >> 16) & 0xFFFF;
ENTRY(num).sel = sel;
ENTRY(num).zero = 0;
ENTRY(num).flags = flags | 0x60;
}
void idt_install(void) {
idt_pointer_t * idtp = &idt.pointer;
idtp->limit = sizeof idt.entries - 1;
idtp->base = (uintptr_t)&ENTRY(0);
memset(&ENTRY(0), 0, sizeof idt.entries);
idt_load((uintptr_t)idtp);
}

164
kernel/cpu/irq.c Normal file
View File

@ -0,0 +1,164 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2011-2014 Kevin Lange
* Copyright (C) 2015 Dale Weiler
*
* Interrupt Requests
*
*/
#include <system.h>
#include <logging.h>
#include <module.h>
#include <printf.h>
/* Programmable interrupt controller */
#define PIC1 0x20
#define PIC1_COMMAND PIC1
#define PIC1_OFFSET 0x20
#define PIC1_DATA (PIC1+1)
#define PIC2 0xA0
#define PIC2_COMMAND PIC2
#define PIC2_OFFSET 0x28
#define PIC2_DATA (PIC2+1)
#define PIC_EOI 0x20
#define ICW1_ICW4 0x01
#define ICW1_INIT 0x10
#define PIC_WAIT() \
do { \
/* May be fragile */ \
asm volatile("jmp 1f\n\t" \
"1:\n\t" \
" jmp 2f\n\t" \
"2:"); \
} while (0)
/* Interrupts */
static volatile int sync_depth = 0;
#define SYNC_CLI() asm volatile("cli")
#define SYNC_STI() asm volatile("sti")
void int_disable(void) {
/* Check if interrupts are enabled */
uint32_t flags;
asm volatile("pushf\n\t"
"pop %%eax\n\t"
"movl %%eax, %0\n\t"
: "=r"(flags)
:
: "%eax");
/* Disable interrupts */
SYNC_CLI();
/* If interrupts were enabled, then this is the first call depth */
if (flags & (1 << 9)) {
sync_depth = 1;
} else {
/* Otherwise there is now an additional call depth */
sync_depth++;
}
}
void int_resume(void) {
/* If there is one or no call depths, reenable interrupts */
if (sync_depth == 0 || sync_depth == 1) {
SYNC_STI();
} else {
sync_depth--;
}
}
void int_enable(void) {
sync_depth = 0;
SYNC_STI();
}
/* Interrupt Requests */
#define IRQ_CHAIN_SIZE 16
#define IRQ_CHAIN_DEPTH 4
static void (*irqs[IRQ_CHAIN_SIZE])(void);
static irq_handler_chain_t irq_routines[IRQ_CHAIN_SIZE * IRQ_CHAIN_DEPTH] = { NULL };
void irq_install_handler(size_t irq, irq_handler_chain_t handler) {
/* Disable interrupts when changing handlers */
SYNC_CLI();
for (size_t i = 0; i < IRQ_CHAIN_DEPTH; i++) {
if (irq_routines[i * IRQ_CHAIN_SIZE + irq])
continue;
irq_routines[i * IRQ_CHAIN_SIZE + irq] = handler;
break;
}
SYNC_STI();
}
void irq_uninstall_handler(size_t irq) {
/* Disable interrupts when changing handlers */
SYNC_CLI();
for (size_t i = 0; i < IRQ_CHAIN_DEPTH; i++)
irq_routines[i * IRQ_CHAIN_SIZE + irq] = NULL;
SYNC_STI();
}
static void irq_remap(void) {
/* Cascade initialization */
outportb(PIC1_COMMAND, ICW1_INIT|ICW1_ICW4); PIC_WAIT();
outportb(PIC2_COMMAND, ICW1_INIT|ICW1_ICW4); PIC_WAIT();
/* Remap */
outportb(PIC1_DATA, PIC1_OFFSET); PIC_WAIT();
outportb(PIC2_DATA, PIC2_OFFSET); PIC_WAIT();
/* Cascade identity with slave PIC at IRQ2 */
outportb(PIC1_DATA, 0x04); PIC_WAIT();
outportb(PIC2_DATA, 0x02); PIC_WAIT();
/* Request 8086 mode on each PIC */
outportb(PIC1_DATA, 0x01); PIC_WAIT();
outportb(PIC2_DATA, 0x01); PIC_WAIT();
}
static void irq_setup_gates(void) {
for (size_t i = 0; i < IRQ_CHAIN_SIZE; i++) {
idt_set_gate(32 + i, irqs[i], 0x08, 0x8E);
}
}
void irq_install(void) {
char buffer[16];
for (int i = 0; i < IRQ_CHAIN_SIZE; i++) {
sprintf(buffer, "_irq%d", i);
irqs[i] = symbol_find(buffer);
}
irq_remap();
irq_setup_gates();
}
void irq_ack(size_t irq_no) {
if (irq_no >= 8) {
outportb(PIC2_COMMAND, PIC_EOI);
}
outportb(PIC1_COMMAND, PIC_EOI);
}
void irq_handler(struct regs *r) {
/* Disable interrupts when handling */
int_disable();
if (r->int_no <= 47 && r->int_no >= 32) {
for (size_t i = 0; i < IRQ_CHAIN_DEPTH; i++) {
irq_handler_chain_t handler = irq_routines[i * IRQ_CHAIN_SIZE + (r->int_no - 32)];
if (handler && handler(r)) {
goto done;
}
}
irq_ack(r->int_no - 32);
}
done:
int_resume();
}

94
kernel/cpu/isr.c Normal file
View File

@ -0,0 +1,94 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2011-2014 Kevin Lange
* Copyright (C) 2015 Dale Weiler
*
* Interrupt Service Requests
*/
#include <system.h>
#include <logging.h>
#include <module.h>
#include <printf.h>
/* The count is treated as is when setting up IDT gates. However there is an
* additional ISR for the system call vector which is handled explicitly since
* it's mapped at a different address.
*/
#define ISR_COUNT 32
static struct {
size_t index;
void (*stub)(void);
} isrs[32 + 1] __attribute__((used));
static irq_handler_t isr_routines[256] = { 0 };
void isrs_install_handler(size_t isrs, irq_handler_t handler) {
isr_routines[isrs] = handler;
}
void isrs_uninstall_handler(size_t isrs) {
isr_routines[isrs] = 0;
}
void isrs_install(void) {
char buffer[16];
for (int i = 0; i < ISR_COUNT; i++) {
sprintf(buffer, "_isr%d", i);
isrs[i].index = i;
isrs[i].stub = symbol_find(buffer);
}
isrs[ISR_COUNT].index = SYSCALL_VECTOR;
isrs[ISR_COUNT].stub = symbol_find("_isr127");
for (int i = 0; i < ISR_COUNT + 1; i++) {
idt_set_gate(isrs[i].index, isrs[i].stub, 0x08, 0x8E);
}
}
static const char *exception_messages[32] = {
"Division by zero",
"Debug",
"Non-maskable interrupt",
"Breakpoint",
"Detected overflow",
"Out-of-bounds",
"Invalid opcode",
"No coprocessor",
"Double fault",
"Coprocessor segment overrun",
"Bad TSS",
"Segment not present",
"Stack fault",
"General protection fault",
"Page fault",
"Unknown interrupt",
"Coprocessor fault",
"Alignment check",
"Machine check",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved"
};
void fault_handler(struct regs * r) {
irq_handler_t handler = isr_routines[r->int_no];
if (handler) {
handler(r);
} else {
debug_print(CRITICAL, "Unhandled exception: [%d] %s", r->int_no, exception_messages[r->int_no]);
HALT_AND_CATCH_FIRE("Process caused an unhandled exception", r);
STOP;
}
}

194
kernel/devices/cmos.c Normal file
View File

@ -0,0 +1,194 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2011-2014 Kevin Lange
*
* CMOS Driver
*
*/
#include <system.h>
/* CMOS values are stored like so:
* Say it's 8:42 AM, then the values are stored as:
* 0x08, 0x42... why this was a good idea, I have no
* clue, but that's how it usually is.
*
* This function will convert between this "BCD" format
* and regular decimal integers. */
#define from_bcd(val) ((val / 16) * 10 + (val & 0xf))
#define CMOS_ADDRESS 0x70
#define CMOS_DATA 0x71
enum
{
CMOS_SECOND = 0,
CMOS_MINUTE = 2,
CMOS_HOUR = 4,
CMOS_DAY = 7,
CMOS_MONTH = 8,
CMOS_YEAR = 9
};
void
cmos_dump(
uint16_t * values
) {
uint16_t index;
for (index = 0; index < 128; ++index) {
outportb(CMOS_ADDRESS, index);
values[index] = inportb(CMOS_DATA);
}
}
int is_update_in_progress(void)
{
outportb(CMOS_ADDRESS, 0x0a);
return inportb(CMOS_DATA) & 0x80;
}
/**
* Get the current month and day.
*
* @param month Pointer to a short to store the month
* @param day Pointer to a short to store the day
*/
void
get_date(
uint16_t * month,
uint16_t * day
) {
uint16_t values[128]; /* CMOS dump */
cmos_dump(values);
*month = from_bcd(values[CMOS_MONTH]);
*day = from_bcd(values[CMOS_DAY]);
}
/**
* Get the current time.
*
* @param hours Pointer to a short to store the current hour (/24)
* @param minutes Pointer to a short to store the current minute
* @param seconds Pointer to a short to store the current second
*/
void
get_time(
uint16_t * hours,
uint16_t * minutes,
uint16_t * seconds
) {
uint16_t values[128]; /* CMOS dump */
cmos_dump(values);
*hours = from_bcd(values[CMOS_HOUR]);
*minutes = from_bcd(values[CMOS_MINUTE]);
*seconds = from_bcd(values[CMOS_SECOND]);
}
uint32_t secs_of_years(int years) {
uint32_t days = 0;
years += 2000;
while (years > 1969) {
days += 365;
if (years % 4 == 0) {
if (years % 100 == 0) {
if (years % 400 == 0) {
days++;
}
} else {
days++;
}
}
years--;
}
return days * 86400;
}
uint32_t secs_of_month(int months, int year) {
year += 2000;
uint32_t days = 0;
switch(months) {
case 11:
days += 30;
case 10:
days += 31;
case 9:
days += 30;
case 8:
days += 31;
case 7:
days += 31;
case 6:
days += 30;
case 5:
days += 31;
case 4:
days += 30;
case 3:
days += 31;
case 2:
days += 28;
if ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))) {
days++;
}
case 1:
days += 31;
default:
break;
}
return days * 86400;
}
uint32_t boot_time = 0;
uint32_t read_cmos(void) {
uint16_t values[128];
uint16_t old_values[128];
while (is_update_in_progress())
;
cmos_dump(values);
do
{
memcpy(old_values, values, 128);
while (is_update_in_progress())
;
cmos_dump(values);
} while ((old_values[CMOS_SECOND] != values[CMOS_SECOND]) ||
(old_values[CMOS_MINUTE] != values[CMOS_MINUTE]) ||
(old_values[CMOS_HOUR] != values[CMOS_HOUR]) ||
(old_values[CMOS_DAY] != values[CMOS_DAY]) ||
(old_values[CMOS_MONTH] != values[CMOS_MONTH]) ||
(old_values[CMOS_YEAR] != values[CMOS_YEAR]));
/* Math Time */
uint32_t time =
secs_of_years(from_bcd(values[CMOS_YEAR]) - 1) +
secs_of_month(from_bcd(values[CMOS_MONTH]) - 1,
from_bcd(values[CMOS_YEAR])) +
(from_bcd(values[CMOS_DAY]) - 1) * 86400 +
(from_bcd(values[CMOS_HOUR])) * 3600 +
(from_bcd(values[CMOS_MINUTE])) * 60 +
from_bcd(values[CMOS_SECOND]) + 0;
return time;
}
int gettimeofday(struct timeval * t, void *z) {
t->tv_sec = boot_time + timer_ticks + timer_drift;
t->tv_usec = timer_subticks * 1000;
return 0;
}
uint32_t now(void) {
struct timeval t;
gettimeofday(&t, NULL);
return t.tv_sec;
}

142
kernel/devices/fpu.c Normal file
View File

@ -0,0 +1,142 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2011-2013 Kevin Lange
*
* FPU and SSE context handling.
*
* FPU context is kept through context switches,
* but the FPU is disabled. When an FPU instruction
* is executed, it will trap here and the context
* will be saved to its original owner and the context
* for the current process will be loaded or the FPU
* will be reset for the new process.
*
* FPU states are per kernel thread.
*
*/
#include <system.h>
#include <logging.h>
#define NO_LAZY_FPU
process_t * fpu_thread = NULL;
/**
* Set the FPU control word
*
* @param cw What to set the control word to.
*/
void
set_fpu_cw(const uint16_t cw) {
asm volatile("fldcw %0" :: "m"(cw));
}
/**
* Enable the FPU and SSE
*/
void enable_fpu(void) {
asm volatile ("clts");
size_t t;
asm volatile ("mov %%cr0, %0" : "=r"(t));
t &= ~(1 << 2);
t |= (1 << 1);
asm volatile ("mov %0, %%cr0" :: "r"(t));
asm volatile ("mov %%cr4, %0" : "=r"(t));
t |= 3 << 9;
asm volatile ("mov %0, %%cr4" :: "r"(t));
}
/**
* Disable FPU and SSE so it traps to the kernel
*/
void disable_fpu(void) {
size_t t;
asm volatile ("mov %%cr0, %0" : "=r"(t));
t |= 1 << 3;
asm volatile ("mov %0, %%cr0" :: "r"(t));
}
/* Temporary aligned buffer for copying around FPU contexts */
uint8_t saves[512] __attribute__((aligned(16)));
/**
* Restore the FPU for a process
*/
void restore_fpu(process_t * proc) {
memcpy(&saves,(uint8_t *)&proc->thread.fp_regs,512);
asm volatile ("fxrstor (%0)" :: "r"(saves));
}
/**
* Save the FPU for a process
*/
void save_fpu(process_t * proc) {
asm volatile ("fxsave (%0)" :: "r"(saves));
memcpy((uint8_t *)&proc->thread.fp_regs,&saves,512);
}
/**
* Initialize the FPU
*/
void init_fpu(void) {
asm volatile ("fninit");
}
/**
* Kernel trap for FPU usage when FPU is disabled
*/
void invalid_op(struct regs * r) {
/* First, turn the FPU on */
enable_fpu();
if (fpu_thread == current_process) {
/* If this is the thread that last used the FPU, do nothing */
return;
}
if (fpu_thread) {
/* If there is a thread that was using the FPU, save its state */
save_fpu(fpu_thread);
}
fpu_thread = (process_t *)current_process;
if (!fpu_thread->thread.fpu_enabled) {
/*
* If the FPU has not been used in this thread previously,
* we need to initialize it.
*/
init_fpu();
fpu_thread->thread.fpu_enabled = 1;
return;
}
/* Otherwise we restore the context for this thread. */
restore_fpu(fpu_thread);
}
/* Called during a context switch; disable the FPU */
void switch_fpu(void) {
#ifdef NO_LAZY_FPU
save_fpu((process_t *)current_process);
#else
disable_fpu();
#endif
}
void unswitch_fpu(void) {
#ifdef NO_LAZY_FPU
restore_fpu((process_t *)current_process);
#endif
}
/* Enable the FPU context handling */
void fpu_install(void) {
#ifdef NO_LAZY_FPU
enable_fpu();
init_fpu();
save_fpu((void*)current_process);
#else
enable_fpu();
disable_fpu();
isrs_install_handler(7, &invalid_op);
#endif
}

113
kernel/devices/pci.c Normal file
View File

@ -0,0 +1,113 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2011-2014 Kevin Lange
*
* ToAruOS PCI Initialization
*/
#include <system.h>
#include <pci.h>
#include <pci_list.h>
void pci_write_field(uint32_t device, int field, int size, uint32_t value) {
outportl(PCI_ADDRESS_PORT, pci_get_addr(device, field));
outportl(PCI_VALUE_PORT, value);
}
uint32_t pci_read_field(uint32_t device, int field, int size) {
outportl(PCI_ADDRESS_PORT, pci_get_addr(device, field));
if (size == 4) {
uint32_t t = inportl(PCI_VALUE_PORT);
return t;
} else if (size == 2) {
uint16_t t = inports(PCI_VALUE_PORT + (field & 2));
return t;
} else if (size == 1) {
uint8_t t = inportb(PCI_VALUE_PORT + (field & 3));
return t;
}
return 0xFFFF;
}
uint16_t pci_find_type(uint32_t dev) {
return (pci_read_field(dev, PCI_CLASS, 1) << 8) | pci_read_field(dev, PCI_SUBCLASS, 1);
}
const char * pci_vendor_lookup(unsigned short vendor_id) {
for (unsigned int i = 0; i < PCI_VENTABLE_LEN; ++i) {
if (PciVenTable[i].VenId == vendor_id) {
return PciVenTable[i].VenFull;
}
}
return "";
}
const char * pci_device_lookup(unsigned short vendor_id, unsigned short device_id) {
for (unsigned int i = 0; i < PCI_DEVTABLE_LEN; ++i) {
if (PciDevTable[i].VenId == vendor_id && PciDevTable[i].DevId == device_id) {
return PciDevTable[i].ChipDesc;
}
}
return "";
}
void pci_scan_hit(pci_func_t f, uint32_t dev, void * extra) {
int dev_vend = (int)pci_read_field(dev, PCI_VENDOR_ID, 2);
int dev_dvid = (int)pci_read_field(dev, PCI_DEVICE_ID, 2);
f(dev, dev_vend, dev_dvid, extra);
}
void pci_scan_func(pci_func_t f, int type, int bus, int slot, int func, void * extra) {
uint32_t dev = pci_box_device(bus, slot, func);
if (type == -1 || type == pci_find_type(dev)) {
pci_scan_hit(f, dev, extra);
}
if (pci_find_type(dev) == PCI_TYPE_BRIDGE) {
pci_scan_bus(f, type, pci_read_field(dev, PCI_SECONDARY_BUS, 1), extra);
}
}
void pci_scan_slot(pci_func_t f, int type, int bus, int slot, void * extra) {
uint32_t dev = pci_box_device(bus, slot, 0);
if (pci_read_field(dev, PCI_VENDOR_ID, 2) == PCI_NONE) {
return;
}
pci_scan_func(f, type, bus, slot, 0, extra);
if (!pci_read_field(dev, PCI_HEADER_TYPE, 1)) {
return;
}
for (int func = 1; func < 8; func++) {
uint32_t dev = pci_box_device(bus, slot, func);
if (pci_read_field(dev, PCI_VENDOR_ID, 2) != PCI_NONE) {
pci_scan_func(f, type, bus, slot, func, extra);
}
}
}
void pci_scan_bus(pci_func_t f, int type, int bus, void * extra) {
for (int slot = 0; slot < 32; ++slot) {
pci_scan_slot(f, type, bus, slot, extra);
}
}
void pci_scan(pci_func_t f, int type, void * extra) {
if ((pci_read_field(0, PCI_HEADER_TYPE, 1) & 0x80) == 0) {
pci_scan_bus(f,type,0,extra);
return;
}
for (int func = 0; func < 8; ++func) {
uint32_t dev = pci_box_device(0, 0, func);
if (pci_read_field(dev, PCI_VENDOR_ID, 2) != PCI_NONE) {
pci_scan_bus(f, type, func, extra);
} else {
break;
}
}
}

90
kernel/devices/timer.c Normal file
View File

@ -0,0 +1,90 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2011-2013 Kevin Lange
*
* Programmable Interrupt Timer
*/
#include <system.h>
#include <logging.h>
#include <process.h>
#define PIT_A 0x40
#define PIT_B 0x41
#define PIT_C 0x42
#define PIT_CONTROL 0x43
#define PIT_MASK 0xFF
#define PIT_SCALE 1193180
#define PIT_SET 0x34
#define TIMER_IRQ 0
#define SUBTICKS_PER_TICK 1000
#define RESYNC_TIME 1
/*
* Set the phase (in hertz) for the Programmable
* Interrupt Timer (PIT).
*/
void
timer_phase(
int hz
) {
int divisor = PIT_SCALE / hz;
outportb(PIT_CONTROL, PIT_SET);
outportb(PIT_A, divisor & PIT_MASK);
outportb(PIT_A, (divisor >> 8) & PIT_MASK);
}
/*
* Internal timer counters
*/
unsigned long timer_ticks = 0;
unsigned long timer_subticks = 0;
signed long timer_drift = 0;
signed long _timer_drift = 0;
static int behind = 0;
/*
* IRQ handler for when the timer fires
*/
int timer_handler(struct regs *r) {
if (++timer_subticks == SUBTICKS_PER_TICK || (behind && ++timer_subticks == SUBTICKS_PER_TICK)) {
timer_ticks++;
timer_subticks = 0;
if (timer_ticks % RESYNC_TIME == 0) {
uint32_t new_time = read_cmos();
_timer_drift = new_time - boot_time - timer_ticks;
if (_timer_drift > 0) behind = 1;
else behind = 0;
}
}
irq_ack(TIMER_IRQ);
wakeup_sleepers(timer_ticks, timer_subticks);
switch_task(1);
return 1;
}
void relative_time(unsigned long seconds, unsigned long subseconds, unsigned long * out_seconds, unsigned long * out_subseconds) {
if (subseconds + timer_subticks > SUBTICKS_PER_TICK) {
*out_seconds = timer_ticks + seconds + 1;
*out_subseconds = (subseconds + timer_subticks) - SUBTICKS_PER_TICK;
} else {
*out_seconds = timer_ticks + seconds;
*out_subseconds = timer_subticks + subseconds;
}
}
/*
* Device installer for the PIT
*/
void timer_install(void) {
debug_print(NOTICE,"Initializing interval timer");
boot_time = read_cmos();
irq_install_handler(TIMER_IRQ, timer_handler);
timer_phase(SUBTICKS_PER_TICK); /* 100Hz */
}

64
kernel/ds/bitset.c Normal file
View File

@ -0,0 +1,64 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2015 Dale Weiler
* 2015 Kevin Lange
*/
#include "bitset.h"
#define CEIL(NUMBER, BASE) \
(((NUMBER) + (BASE) - 1) & ~((BASE) - 1))
#define iom \
size_t index = bit >> 3; \
bit = bit - index * 8; \
size_t offset = bit & 7; \
size_t mask = 1 << offset;
void bitset_init(bitset_t *set, size_t size) {
set->size = CEIL(size, 8);
set->data = calloc(set->size, 1);
}
void bitset_free(bitset_t *set) {
free(set->data);
}
static void bitset_resize(bitset_t *set, size_t size) {
if (set->size >= size) {
return;
}
set->data = realloc(set->data, size);
memset(set->data + set->size, 0, size - set->size);
set->size = size;
}
void bitset_set(bitset_t *set, size_t bit) {
iom;
if (set->size <= index) {
bitset_resize(set, set->size << 1);
}
set->data[index] |= mask;
}
int bitset_ffub(bitset_t *set) {
for (size_t i = 0; i < set->size * 8; i++) {
if (bitset_test(set, i)) {
continue;
}
return (int)i;
}
return -1;
}
void bitset_clear(bitset_t *set, size_t bit) {
iom;
set->data[index] &= ~mask;
}
int bitset_test(bitset_t *set, size_t bit) {
iom;
return !!(mask & set->data[index]);
}

217
kernel/ds/hashmap.c Normal file
View File

@ -0,0 +1,217 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2013-2014 Kevin Lange
*/
#include "list.h"
#include "hashmap.h"
unsigned int hashmap_string_hash(void * _key) {
unsigned int hash = 0;
char * key = (char *)_key;
int c;
/* This is the so-called "sdbm" hash. It comes from a piece of
* public domain code from a clone of ndbm. */
while ((c = *key++)) {
hash = c + (hash << 6) + (hash << 16) - hash;
}
return hash;
}
int hashmap_string_comp(void * a, void * b) {
return !strcmp(a,b);
}
void * hashmap_string_dupe(void * key) {
return strdup(key);
}
unsigned int hashmap_int_hash(void * key) {
return (unsigned int)key;
}
int hashmap_int_comp(void * a, void * b) {
return (int)a == (int)b;
}
void * hashmap_int_dupe(void * key) {
return key;
}
static void hashmap_int_free(void * ptr) {
return;
}
hashmap_t * hashmap_create(int size) {
hashmap_t * map = malloc(sizeof(hashmap_t));
map->hash_func = &hashmap_string_hash;
map->hash_comp = &hashmap_string_comp;
map->hash_key_dup = &hashmap_string_dupe;
map->hash_key_free = &free;
map->hash_val_free = &free;
map->size = size;
map->entries = malloc(sizeof(hashmap_entry_t *) * size);
memset(map->entries, 0x00, sizeof(hashmap_entry_t *) * size);
return map;
}
hashmap_t * hashmap_create_int(int size) {
hashmap_t * map = malloc(sizeof(hashmap_t));
map->hash_func = &hashmap_int_hash;
map->hash_comp = &hashmap_int_comp;
map->hash_key_dup = &hashmap_int_dupe;
map->hash_key_free = &hashmap_int_free;
map->hash_val_free = &free;
map->size = size;
map->entries = malloc(sizeof(hashmap_entry_t *) * size);
memset(map->entries, 0x00, sizeof(hashmap_entry_t *) * size);
return map;
}
void * hashmap_set(hashmap_t * map, void * key, void * value) {
unsigned int hash = map->hash_func(key) % map->size;
hashmap_entry_t * x = map->entries[hash];
if (!x) {
hashmap_entry_t * e = malloc(sizeof(hashmap_entry_t));
e->key = map->hash_key_dup(key);
e->value = value;
e->next = NULL;
map->entries[hash] = e;
return NULL;
} else {
hashmap_entry_t * p = NULL;
do {
if (map->hash_comp(x->key, key)) {
void * out = x->value;
x->value = value;
return out;
} else {
p = x;
x = x->next;
}
} while (x);
hashmap_entry_t * e = malloc(sizeof(hashmap_entry_t));
e->key = map->hash_key_dup(key);
e->value = value;
e->next = NULL;
p->next = e;
return NULL;
}
}
void * hashmap_get(hashmap_t * map, void * key) {
unsigned int hash = map->hash_func(key) % map->size;
hashmap_entry_t * x = map->entries[hash];
if (!x) {
return NULL;
} else {
do {
if (map->hash_comp(x->key, key)) {
return x->value;
}
x = x->next;
} while (x);
return NULL;
}
}
void * hashmap_remove(hashmap_t * map, void * key) {
unsigned int hash = map->hash_func(key) % map->size;
hashmap_entry_t * x = map->entries[hash];
if (!x) {
return NULL;
} else {
if (map->hash_comp(x->key, key)) {
void * out = x->value;
map->entries[hash] = x->next;
map->hash_key_free(x->key);
map->hash_val_free(x);
return out;
} else {
hashmap_entry_t * p = x;
x = x->next;
do {
if (map->hash_comp(x->key, key)) {
void * out = x->value;
p->next = x->next;
map->hash_key_free(x->key);
map->hash_val_free(x);
return out;
}
p = x;
x = x->next;
} while (x);
}
return NULL;
}
}
int hashmap_has(hashmap_t * map, void * key) {
unsigned int hash = map->hash_func(key) % map->size;
hashmap_entry_t * x = map->entries[hash];
if (!x) {
return 0;
} else {
do {
if (map->hash_comp(x->key, key)) {
return 1;
}
x = x->next;
} while (x);
return 0;
}
}
list_t * hashmap_keys(hashmap_t * map) {
list_t * l = list_create();
for (unsigned int i = 0; i < map->size; ++i) {
hashmap_entry_t * x = map->entries[i];
while (x) {
list_insert(l, x->key);
x = x->next;
}
}
return l;
}
list_t * hashmap_values(hashmap_t * map) {
list_t * l = list_create();
for (unsigned int i = 0; i < map->size; ++i) {
hashmap_entry_t * x = map->entries[i];
while (x) {
list_insert(l, x->value);
x = x->next;
}
}
return l;
}
void hashmap_free(hashmap_t * map) {
for (unsigned int i = 0; i < map->size; ++i) {
hashmap_entry_t * x = map->entries[i], * p;
while (x) {
p = x;
x = x->next;
map->hash_key_free(p->key);
map->hash_val_free(p);
}
}
free(map->entries);
}

248
kernel/ds/list.c Normal file
View File

@ -0,0 +1,248 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2011-2014 Kevin Lange
*
* General-purpose list implementations.
*/
#include "list.h"
#ifdef _KERNEL_
# include <system.h>
#else
# include <stddef.h>
# include <stdlib.h>
#endif
void list_destroy(list_t * list) {
/* Free all of the contents of a list */
node_t * n = list->head;
while (n) {
free(n->value);
n = n->next;
}
}
void list_free(list_t * list) {
/* Free the actual structure of a list */
node_t * n = list->head;
while (n) {
node_t * s = n->next;
free(n);
n = s;
}
}
void list_append(list_t * list, node_t * node) {
assert(!(node->next || node->prev) && "Node is already in a list.");
node->next = NULL;
/* Insert a node onto the end of a list */
node->owner = list;
if (!list->length) {
list->head = node;
list->tail = node;
node->prev = NULL;
node->next = NULL;
list->length++;
return;
}
list->tail->next = node;
node->prev = list->tail;
list->tail = node;
list->length++;
}
node_t * list_insert(list_t * list, void * item) {
/* Insert an item into a list */
node_t * node = malloc(sizeof(node_t));
node->value = item;
node->next = NULL;
node->prev = NULL;
node->owner = NULL;
list_append(list, node);
return node;
}
void list_append_after(list_t * list, node_t * before, node_t * node) {
assert(!(node->next || node->prev) && "Node is already in a list.");
node->owner = list;
if (!list->length) {
list_append(list, node);
return;
}
if (before == NULL) {
node->next = list->head;
node->prev = NULL;
list->head->prev = node;
list->head = node;
list->length++;
return;
}
if (before == list->tail) {
list->tail = node;
} else {
before->next->prev = node;
node->next = before->next;
}
node->prev = before;
before->next = node;
list->length++;
}
node_t * list_insert_after(list_t * list, node_t * before, void * item) {
node_t * node = malloc(sizeof(node_t));
node->value = item;
node->next = NULL;
node->prev = NULL;
node->owner = NULL;
list_append_after(list, before, node);
return node;
}
void list_append_before(list_t * list, node_t * after, node_t * node) {
assert(!(node->next || node->prev) && "Node is already in a list.");
node->owner = list;
if (!list->length) {
list_append(list, node);
return;
}
if (after == NULL) {
node->next = NULL;
node->prev = list->tail;
list->tail->next = node;
list->tail = node;
list->length++;
return;
}
if (after == list->head) {
list->head = node;
} else {
after->prev->next = node;
node->prev = after->prev;
}
node->next = after;
after->prev = node;
list->length++;
}
node_t * list_insert_before(list_t * list, node_t * after, void * item) {
node_t * node = malloc(sizeof(node_t));
node->value = item;
node->next = NULL;
node->prev = NULL;
node->owner = NULL;
list_append_before(list, after, node);
return node;
}
list_t * list_create(void) {
/* Create a fresh list */
list_t * out = malloc(sizeof(list_t));
out->head = NULL;
out->tail = NULL;
out->length = 0;
return out;
}
node_t * list_find(list_t * list, void * value) {
foreach(item, list) {
if (item->value == value) {
return item;
}
}
return NULL;
}
int list_index_of(list_t * list, void * value) {
int i = 0;
foreach(item, list) {
if (item->value == value) {
return i;
}
i++;
}
return -1; /* not find */
}
void list_remove(list_t * list, size_t index) {
/* remove index from the list */
if (index > list->length) return;
size_t i = 0;
node_t * n = list->head;
while (i < index) {
n = n->next;
i++;
}
list_delete(list, n);
}
void list_delete(list_t * list, node_t * node) {
/* remove node from the list */
assert(node->owner == list && "Tried to remove a list node from a list it does not belong to.");
if (node == list->head) {
list->head = node->next;
}
if (node == list->tail) {
list->tail = node->prev;
}
if (node->prev) {
node->prev->next = node->next;
}
if (node->next) {
node->next->prev = node->prev;
}
node->prev = NULL;
node->next = NULL;
node->owner = NULL;
list->length--;
}
node_t * list_pop(list_t * list) {
/* Remove and return the last value in the list
* If you don't need it, you still probably want to free it!
* Try free(list_pop(list)); !
* */
if (!list->tail) return NULL;
node_t * out = list->tail;
list_delete(list, out);
return out;
}
node_t * list_dequeue(list_t * list) {
if (!list->head) return NULL;
node_t * out = list->head;
list_delete(list, out);
return out;
}
list_t * list_copy(list_t * original) {
/* Create a new copy of original */
list_t * out = list_create();
node_t * node = original->head;
while (node) {
list_insert(out, node->value);
}
return out;
}
void list_merge(list_t * target, list_t * source) {
/* Destructively merges source into target */
foreach(node, source) {
node->owner = target;
}
if (source->head) {
source->head->prev = target->tail;
}
if (target->tail) {
target->tail->next = source->head;
} else {
target->head = source->head;
}
if (source->tail) {
target->tail = source->tail;
}
target->length += source->length;
free(source);
}

170
kernel/ds/ringbuffer.c Normal file
View File

@ -0,0 +1,170 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2013-2014 Kevin Lange
*/
#include <system.h>
#include <ringbuffer.h>
#include <process.h>
size_t ring_buffer_unread(ring_buffer_t * ring_buffer) {
if (ring_buffer->read_ptr == ring_buffer->write_ptr) {
return 0;
}
if (ring_buffer->read_ptr > ring_buffer->write_ptr) {
return (ring_buffer->size - ring_buffer->read_ptr) + ring_buffer->write_ptr;
} else {
return (ring_buffer->write_ptr - ring_buffer->read_ptr);
}
}
size_t ring_buffer_size(fs_node_t * node) {
ring_buffer_t * ring_buffer = (ring_buffer_t *)node->device;
return ring_buffer_unread(ring_buffer);
}
size_t ring_buffer_available(ring_buffer_t * ring_buffer) {
if (ring_buffer->read_ptr == ring_buffer->write_ptr) {
return ring_buffer->size - 1;
}
if (ring_buffer->read_ptr > ring_buffer->write_ptr) {
return ring_buffer->read_ptr - ring_buffer->write_ptr - 1;
} else {
return (ring_buffer->size - ring_buffer->write_ptr) + ring_buffer->read_ptr - 1;
}
}
static inline void ring_buffer_increment_read(ring_buffer_t * ring_buffer) {
ring_buffer->read_ptr++;
if (ring_buffer->read_ptr == ring_buffer->size) {
ring_buffer->read_ptr = 0;
}
}
static inline void ring_buffer_increment_write(ring_buffer_t * ring_buffer) {
ring_buffer->write_ptr++;
if (ring_buffer->write_ptr == ring_buffer->size) {
ring_buffer->write_ptr = 0;
}
}
static void ring_buffer_alert_waiters(ring_buffer_t * ring_buffer) {
if (ring_buffer->alert_waiters) {
while (ring_buffer->alert_waiters->head) {
node_t * node = list_dequeue(ring_buffer->alert_waiters);
process_t * p = node->value;
process_alert_node(p, ring_buffer);
free(node);
}
}
}
void ring_buffer_select_wait(ring_buffer_t * ring_buffer, void * process) {
if (!ring_buffer->alert_waiters) {
ring_buffer->alert_waiters = list_create();
}
if (!list_find(ring_buffer->alert_waiters, process)) {
list_insert(ring_buffer->alert_waiters, process);
}
list_insert(((process_t *)process)->node_waits, ring_buffer);
}
size_t ring_buffer_read(ring_buffer_t * ring_buffer, size_t size, uint8_t * buffer) {
size_t collected = 0;
while (collected == 0) {
spin_lock(ring_buffer->lock);
while (ring_buffer_unread(ring_buffer) > 0 && collected < size) {
buffer[collected] = ring_buffer->buffer[ring_buffer->read_ptr];
ring_buffer_increment_read(ring_buffer);
collected++;
}
spin_unlock(ring_buffer->lock);
wakeup_queue(ring_buffer->wait_queue_writers);
if (collected == 0) {
if (sleep_on(ring_buffer->wait_queue_readers) && ring_buffer->internal_stop) {
ring_buffer->internal_stop = 0;
break;
}
}
}
wakeup_queue(ring_buffer->wait_queue_writers);
return collected;
}
size_t ring_buffer_write(ring_buffer_t * ring_buffer, size_t size, uint8_t * buffer) {
size_t written = 0;
while (written < size) {
spin_lock(ring_buffer->lock);
while (ring_buffer_available(ring_buffer) > 0 && written < size) {
ring_buffer->buffer[ring_buffer->write_ptr] = buffer[written];
ring_buffer_increment_write(ring_buffer);
written++;
}
spin_unlock(ring_buffer->lock);
wakeup_queue(ring_buffer->wait_queue_readers);
ring_buffer_alert_waiters(ring_buffer);
if (written < size) {
if (ring_buffer->discard) {
break;
}
if (sleep_on(ring_buffer->wait_queue_writers) && ring_buffer->internal_stop) {
ring_buffer->internal_stop = 0;
break;
}
}
}
wakeup_queue(ring_buffer->wait_queue_readers);
ring_buffer_alert_waiters(ring_buffer);
return written;
}
ring_buffer_t * ring_buffer_create(size_t size) {
ring_buffer_t * out = malloc(sizeof(ring_buffer_t));
out->buffer = malloc(size);
out->write_ptr = 0;
out->read_ptr = 0;
out->size = size;
out->alert_waiters = NULL;
spin_init(out->lock);
out->internal_stop = 0;
out->discard = 0;
out->wait_queue_readers = list_create();
out->wait_queue_writers = list_create();
return out;
}
void ring_buffer_destroy(ring_buffer_t * ring_buffer) {
free(ring_buffer->buffer);
wakeup_queue(ring_buffer->wait_queue_writers);
wakeup_queue(ring_buffer->wait_queue_readers);
ring_buffer_alert_waiters(ring_buffer);
list_free(ring_buffer->wait_queue_writers);
list_free(ring_buffer->wait_queue_readers);
free(ring_buffer->wait_queue_writers);
free(ring_buffer->wait_queue_readers);
if (ring_buffer->alert_waiters) {
list_free(ring_buffer->alert_waiters);
free(ring_buffer->alert_waiters);
}
}
void ring_buffer_interrupt(ring_buffer_t * ring_buffer) {
ring_buffer->internal_stop = 1;
wakeup_queue_interrupted(ring_buffer->wait_queue_readers);
wakeup_queue_interrupted(ring_buffer->wait_queue_writers);
}

192
kernel/ds/tree.c Normal file
View File

@ -0,0 +1,192 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2011-2014 Kevin Lange
*
* General-purpose tree implementation
*/
#include "tree.h"
#ifdef _KERNEL_
# include <system.h>
#else
# include <stddef.h>
# include <stdlib.h>
#endif
tree_t * tree_create(void) {
/* Create a new tree */
tree_t * out = malloc(sizeof(tree_t));
out->nodes = 0;
out->root = NULL;
return out;
}
void tree_set_root(tree_t * tree, void * value) {
/* Set the root node for a new tree. */
tree_node_t * root = tree_node_create(value);
tree->root = root;
tree->nodes = 1;
}
void tree_node_destroy(tree_node_t * node) {
/* Free the contents of a node and its children, but not the nodes themselves */
foreach(child, node->children) {
tree_node_destroy((tree_node_t *)child->value);
}
free(node->value);
}
void tree_destroy(tree_t * tree) {
/* Free the contents of a tree, but not the nodes */
if (tree->root) {
tree_node_destroy(tree->root);
}
}
void tree_node_free(tree_node_t * node) {
/* Free a node and its children, but not their contents */
if (!node) return;
foreach(child, node->children) {
tree_node_free(child->value);
}
free(node);
}
void tree_free(tree_t * tree) {
/* Free all of the nodes in a tree, but not their contents */
tree_node_free(tree->root);
}
tree_node_t * tree_node_create(void * value) {
/* Create a new tree node pointing to the given value */
tree_node_t * out = malloc(sizeof(tree_node_t));
out->value = value;
out->children = list_create();
out->parent = NULL;
return out;
}
void tree_node_insert_child_node(tree_t * tree, tree_node_t * parent, tree_node_t * node) {
/* Insert a node as a child of parent */
list_insert(parent->children, node);
node->parent = parent;
tree->nodes++;
}
tree_node_t * tree_node_insert_child(tree_t * tree, tree_node_t * parent, void * value) {
/* Insert a (fresh) node as a child of parent */
tree_node_t * out = tree_node_create(value);
tree_node_insert_child_node(tree, parent, out);
return out;
}
tree_node_t * tree_node_find_parent(tree_node_t * haystack, tree_node_t * needle) {
/* Recursive node part of tree_find_parent */
tree_node_t * found = NULL;
foreach(child, haystack->children) {
if (child->value == needle) {
return haystack;
}
found = tree_node_find_parent((tree_node_t *)child->value, needle);
if (found) {
break;
}
}
return found;
}
tree_node_t * tree_find_parent(tree_t * tree, tree_node_t * node) {
/* Return the parent of a node, inefficiently. */
if (!tree->root) return NULL;
return tree_node_find_parent(tree->root, node);
}
size_t tree_count_children(tree_node_t * node) {
/* return the number of children this node has */
if (!node) return 0;
if (!node->children) return 0;
size_t out = node->children->length;
foreach(child, node->children) {
out += tree_count_children((tree_node_t *)child->value);
}
return out;
}
void tree_node_parent_remove(tree_t * tree, tree_node_t * parent, tree_node_t * node) {
/* remove a node when we know its parent; update node counts for the tree */
tree->nodes -= tree_count_children(node) + 1;
list_delete(parent->children, list_find(parent->children, node));
tree_node_free(node);
}
void tree_node_remove(tree_t * tree, tree_node_t * node) {
/* remove an entire branch given its root */
tree_node_t * parent = node->parent;
if (!parent) {
if (node == tree->root) {
tree->nodes = 0;
tree->root = NULL;
tree_node_free(node);
}
}
tree_node_parent_remove(tree, parent, node);
}
void tree_remove(tree_t * tree, tree_node_t * node) {
/* Remove this node and move its children into its parent's list of children */
tree_node_t * parent = node->parent;
/* This is something we just can't do. We don't know how to merge our
* children into our "parent" because then we'd have more than one root node.
* A good way to think about this is actually what this tree struct
* primarily exists for: processes. Trying to remove the root is equivalent
* to trying to kill init! Which is bad. We immediately fault on such
* a case anyway ("Tried to kill init, shutting down!").
*/
if (!parent) return;
tree->nodes--;
list_delete(parent->children, list_find(parent->children, node));
foreach(child, node->children) {
/* Reassign the parents */
((tree_node_t *)child->value)->parent = parent;
}
list_merge(parent->children, node->children);
free(node);
}
void tree_remove_reparent_root(tree_t * tree, tree_node_t * node) {
/* Remove this node and move its children into the root children */
tree_node_t * parent = node->parent;
if (!parent) return;
tree->nodes--;
list_delete(parent->children, list_find(parent->children, node));
foreach(child, node->children) {
/* Reassign the parents */
((tree_node_t *)child->value)->parent = tree->root;
}
list_merge(tree->root->children, node->children);
free(node);
}
void tree_break_off(tree_t * tree, tree_node_t * node) {
tree_node_t * parent = node->parent;
if (!parent) return;
list_delete(parent->children, list_find(parent->children, node));
}
tree_node_t * tree_node_find(tree_node_t * node, void * search, tree_comparator_t comparator) {
if (comparator(node->value,search)) {
return node;
}
tree_node_t * found;
foreach(child, node->children) {
found = tree_node_find((tree_node_t *)child->value, search, comparator);
if (found) return found;
}
return NULL;
}
tree_node_t * tree_find(tree_t * tree, void * value, tree_comparator_t comparator) {
return tree_node_find(tree->root, value, comparator);
}

296
kernel/fs/pipe.c Normal file
View File

@ -0,0 +1,296 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2012-2014 Kevin Lange
*
* Buffered Pipe
*
*/
#include <system.h>
#include <fs.h>
#include <printf.h>
#include <pipe.h>
#include <logging.h>
#define DEBUG_PIPES 0
uint32_t read_pipe(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer);
uint32_t write_pipe(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer);
void open_pipe(fs_node_t *node, unsigned int flags);
void close_pipe(fs_node_t *node);
static inline size_t pipe_unread(pipe_device_t * pipe) {
if (pipe->read_ptr == pipe->write_ptr) {
return 0;
}
if (pipe->read_ptr > pipe->write_ptr) {
return (pipe->size - pipe->read_ptr) + pipe->write_ptr;
} else {
return (pipe->write_ptr - pipe->read_ptr);
}
}
int pipe_size(fs_node_t * node) {
pipe_device_t * pipe = (pipe_device_t *)node->device;
return pipe_unread(pipe);
}
static inline size_t pipe_available(pipe_device_t * pipe) {
if (pipe->read_ptr == pipe->write_ptr) {
return pipe->size - 1;
}
if (pipe->read_ptr > pipe->write_ptr) {
return pipe->read_ptr - pipe->write_ptr - 1;
} else {
return (pipe->size - pipe->write_ptr) + pipe->read_ptr - 1;
}
}
int pipe_unsize(fs_node_t * node) {
pipe_device_t * pipe = (pipe_device_t *)node->device;
return pipe_available(pipe);
}
static inline void pipe_increment_read(pipe_device_t * pipe) {
pipe->read_ptr++;
if (pipe->read_ptr == pipe->size) {
pipe->read_ptr = 0;
}
}
static inline void pipe_increment_write(pipe_device_t * pipe) {
pipe->write_ptr++;
if (pipe->write_ptr == pipe->size) {
pipe->write_ptr = 0;
}
}
static inline void pipe_increment_write_by(pipe_device_t * pipe, size_t amount) {
pipe->write_ptr = (pipe->write_ptr + amount) % pipe->size;
}
static void pipe_alert_waiters(pipe_device_t * pipe) {
if (pipe->alert_waiters) {
while (pipe->alert_waiters->head) {
node_t * node = list_dequeue(pipe->alert_waiters);
process_t * p = node->value;
process_alert_node(p, pipe);
free(node);
}
}
}
uint32_t read_pipe(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer) {
assert(node->device != 0 && "Attempted to read from a fully-closed pipe.");
/* Retreive the pipe object associated with this file node */
pipe_device_t * pipe = (pipe_device_t *)node->device;
#if DEBUG_PIPES
if (pipe->size > 300) { /* Ignore small pipes (ie, keyboard) */
debug_print(INFO, "[debug] Call to read from pipe 0x%x", node->device);
debug_print(INFO, " Unread bytes: %d", pipe_unread(pipe));
debug_print(INFO, " Total size: %d", pipe->size);
debug_print(INFO, " Request size: %d", size);
debug_print(INFO, " Write pointer: %d", pipe->write_ptr);
debug_print(INFO, " Read pointer: %d", pipe->read_ptr);
debug_print(INFO, " Buffer address: 0x%x", pipe->buffer);
}
#endif
if (pipe->dead) {
debug_print(WARNING, "Pipe is dead?");
send_signal(getpid(), SIGPIPE);
return 0;
}
size_t collected = 0;
while (collected == 0) {
spin_lock(pipe->lock_read);
while (pipe_unread(pipe) > 0 && collected < size) {
buffer[collected] = pipe->buffer[pipe->read_ptr];
pipe_increment_read(pipe);
collected++;
}
spin_unlock(pipe->lock_read);
wakeup_queue(pipe->wait_queue_writers);
/* Deschedule and switch */
if (collected == 0) {
sleep_on(pipe->wait_queue_readers);
}
}
return collected;
}
uint32_t write_pipe(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer) {
assert(node->device != 0 && "Attempted to write to a fully-closed pipe.");
/* Retreive the pipe object associated with this file node */
pipe_device_t * pipe = (pipe_device_t *)node->device;
#if DEBUG_PIPES
if (pipe->size > 300) { /* Ignore small pipes (ie, keyboard) */
debug_print(INFO, "[debug] Call to write to pipe 0x%x", node->device);
debug_print(INFO, " Available space: %d", pipe_available(pipe));
debug_print(INFO, " Total size: %d", pipe->size);
debug_print(INFO, " Request size: %d", size);
debug_print(INFO, " Write pointer: %d", pipe->write_ptr);
debug_print(INFO, " Read pointer: %d", pipe->read_ptr);
debug_print(INFO, " Buffer address: 0x%x", pipe->buffer);
debug_print(INFO, " Write: %s", buffer);
}
#endif
if (pipe->dead) {
debug_print(WARNING, "Pipe is dead?");
send_signal(getpid(), SIGPIPE);
return 0;
}
size_t written = 0;
while (written < size) {
spin_lock(pipe->lock_write);
#if 0
size_t available = 0;
if (pipe->read_ptr <= pipe->write_ptr) {
available = pipe->size - pipe->write_ptr;
} else {
available = pipe->read_ptr - pipe->write_ptr - 1;
}
if (available) {
available = min(available, size - written);
memcpy(&pipe->buffer[pipe->write_ptr], buffer, available);
pipe_increment_write_by(pipe, available);
written += available;
}
#else
while (pipe_available(pipe) > 0 && written < size) {
pipe->buffer[pipe->write_ptr] = buffer[written];
pipe_increment_write(pipe);
written++;
}
#endif
spin_unlock(pipe->lock_write);
wakeup_queue(pipe->wait_queue_readers);
pipe_alert_waiters(pipe);
if (written < size) {
sleep_on(pipe->wait_queue_writers);
}
}
return written;
}
void open_pipe(fs_node_t * node, unsigned int flags) {
assert(node->device != 0 && "Attempted to open a fully-closed pipe.");
/* Retreive the pipe object associated with this file node */
pipe_device_t * pipe = (pipe_device_t *)node->device;
/* Add a reference */
pipe->refcount++;
return;
}
void close_pipe(fs_node_t * node) {
assert(node->device != 0 && "Attempted to close an already fully-closed pipe.");
/* Retreive the pipe object associated with this file node */
pipe_device_t * pipe = (pipe_device_t *)node->device;
/* Drop one reference */
pipe->refcount--;
/* Check the reference count number */
if (pipe->refcount == 0) {
#if 0
/* No other references exist, free the pipe (but not its buffer) */
free(pipe->buffer);
list_free(pipe->wait_queue);
free(pipe->wait_queue);
free(pipe);
/* And let the creator know there are no more references */
node->device = 0;
#endif
}
return;
}
static int pipe_check(fs_node_t * node) {
pipe_device_t * pipe = (pipe_device_t *)node->device;
if (pipe_unread(pipe) > 0) {
return 0;
}
return 1;
}
static int pipe_wait(fs_node_t * node, void * process) {
pipe_device_t * pipe = (pipe_device_t *)node->device;
if (!pipe->alert_waiters) {
pipe->alert_waiters = list_create();
}
if (!list_find(pipe->alert_waiters, process)) {
list_insert(pipe->alert_waiters, process);
}
list_insert(((process_t *)process)->node_waits, pipe);
return 0;
}
fs_node_t * make_pipe(size_t size) {
fs_node_t * fnode = malloc(sizeof(fs_node_t));
pipe_device_t * pipe = malloc(sizeof(pipe_device_t));
memset(fnode, 0, sizeof(fs_node_t));
memset(pipe, 0, sizeof(pipe_device_t));
fnode->device = 0;
fnode->name[0] = '\0';
sprintf(fnode->name, "[pipe]");
fnode->uid = 0;
fnode->gid = 0;
fnode->mask = 0666;
fnode->flags = FS_PIPE;
fnode->read = read_pipe;
fnode->write = write_pipe;
fnode->open = open_pipe;
fnode->close = close_pipe;
fnode->readdir = NULL;
fnode->finddir = NULL;
fnode->ioctl = NULL; /* TODO ioctls for pipes? maybe */
fnode->get_size = pipe_size;
fnode->selectcheck = pipe_check;
fnode->selectwait = pipe_wait;
fnode->atime = now();
fnode->mtime = fnode->atime;
fnode->ctime = fnode->atime;
fnode->device = pipe;
pipe->buffer = malloc(size);
pipe->write_ptr = 0;
pipe->read_ptr = 0;
pipe->size = size;
pipe->refcount = 0;
pipe->dead = 0;
spin_init(pipe->lock_read);
spin_init(pipe->lock_write);
pipe->wait_queue_writers = list_create();
pipe->wait_queue_readers = list_create();
return fnode;
}

110
kernel/fs/ramdisk.c Normal file
View File

@ -0,0 +1,110 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2014 Kevin Lange
*
* Ramdisk driver.
*
* Provide raw block access to files loaded into kernel memory.
*/
#include <system.h>
#include <logging.h>
#include <module.h>
#include <fs.h>
#include <printf.h>
#include <mem.h>
static uint32_t read_ramdisk(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer);
static uint32_t write_ramdisk(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer);
static void open_ramdisk(fs_node_t *node, unsigned int flags);
static void close_ramdisk(fs_node_t *node);
static uint32_t read_ramdisk(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer) {
if (offset > node->length) {
return 0;
}
if (offset + size > node->length) {
unsigned int i = node->length - offset;
size = i;
}
memcpy(buffer, (void *)(node->inode + offset), size);
return size;
}
static uint32_t write_ramdisk(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer) {
if (offset > node->length) {
return 0;
}
if (offset + size > node->length) {
unsigned int i = node->length - offset;
size = i;
}
memcpy((void *)(node->inode + offset), buffer, size);
return size;
}
static void open_ramdisk(fs_node_t * node, unsigned int flags) {
return;
}
static void close_ramdisk(fs_node_t * node) {
return;
}
static int ioctl_ramdisk(fs_node_t * node, int request, void * argp) {
switch (request) {
case 0x4001:
if (current_process->user != 0) {
return -EPERM;
} else {
/* Clear all of the memory used by this ramdisk */
for (uintptr_t i = node->inode; i < node->inode + node->length; i += 0x1000) {
clear_frame(i);
}
/* Mark the file length as 0 */
node->length = 0;
return 0;
}
default:
return -EINVAL;
}
}
static fs_node_t * ramdisk_device_create(int device_number, uintptr_t location, size_t size) {
fs_node_t * fnode = malloc(sizeof(fs_node_t));
memset(fnode, 0x00, sizeof(fs_node_t));
fnode->inode = location;
sprintf(fnode->name, "ram%d", device_number);
fnode->uid = 0;
fnode->gid = 0;
fnode->mask = 0660;
fnode->length = size;
fnode->flags = FS_BLOCKDEVICE;
fnode->read = read_ramdisk;
fnode->write = write_ramdisk;
fnode->open = open_ramdisk;
fnode->close = close_ramdisk;
fnode->ioctl = ioctl_ramdisk;
return fnode;
}
static int last_device_number = 0;
fs_node_t * ramdisk_mount(uintptr_t location, size_t size) {
fs_node_t * ramdisk = ramdisk_device_create(last_device_number, location, size);
if (ramdisk) {
char tmp[64];
sprintf(tmp, "/dev/%s", ramdisk->name);
vfs_mount(tmp, ramdisk);
last_device_number += 1;
return ramdisk;
}
return NULL;
}

435
kernel/fs/tty.c Normal file
View File

@ -0,0 +1,435 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2013-2014 Kevin Lange
*/
#include <system.h>
#include <fs.h>
#include <pipe.h>
#include <logging.h>
#include <printf.h>
#include <ioctl.h>
#include <termios.h>
#include <ringbuffer.h>
#define TTY_BUFFER_SIZE 4096
//4096
typedef struct pty {
/* the PTY number */
int name;
/* Master and slave endpoints */
fs_node_t * master;
fs_node_t * slave;
/* term io "window size" struct (width/height) */
struct winsize size;
/* termios data structure */
struct termios tios;
/* directional pipes */
ring_buffer_t * in;
ring_buffer_t * out;
char * canon_buffer;
size_t canon_bufsize;
size_t canon_buflen;
pid_t ct_proc; /* Controlling process (shell) */
pid_t fg_proc; /* Foreground process (might also be shell) */
} pty_t;
list_t * pty_list = NULL;
#define IN(character) ring_buffer_write(pty->in, 1, (uint8_t *)&(character))
#define OUT(character) ring_buffer_write(pty->out, 1, (uint8_t *)&(character))
static void dump_input_buffer(pty_t * pty) {
char * c = pty->canon_buffer;
while (pty->canon_buflen > 0) {
IN(*c);
pty->canon_buflen--;
c++;
}
}
static void clear_input_buffer(pty_t * pty) {
pty->canon_buflen = 0;
pty->canon_buffer[0] = '\0';
}
static void output_process(pty_t * pty, uint8_t c) {
if (ring_buffer_available(pty->out) < 2) return; /* uh oh */
if (c == '\n' && (pty->tios.c_oflag & ONLCR)) {
uint8_t d = '\r';
OUT(d);
}
OUT(c);
}
static void output_process_slave(pty_t * pty, uint8_t c) {
if (c == '\n' && (pty->tios.c_oflag & ONLCR)) {
uint8_t d = '\r';
OUT(d);
}
OUT(c);
}
static void input_process(pty_t * pty, uint8_t c) {
if (pty->tios.c_lflag & ICANON) {
if (c == pty->tios.c_cc[VKILL]) {
while (pty->canon_buflen > 0) {
pty->canon_buflen--;
pty->canon_buffer[pty->canon_buflen] = '\0';
if (pty->tios.c_lflag & ECHO) {
output_process(pty, '\010');
output_process(pty, ' ');
output_process(pty, '\010');
}
}
return;
}
if (c == pty->tios.c_cc[VERASE]) {
/* Backspace */
if (pty->canon_buflen > 0) {
pty->canon_buflen--;
pty->canon_buffer[pty->canon_buflen] = '\0';
if (pty->tios.c_lflag & ECHO) {
output_process(pty, '\010');
output_process(pty, ' ');
output_process(pty, '\010');
}
}
return;
}
if (c == pty->tios.c_cc[VINTR]) {
if (pty->tios.c_lflag & ECHO) {
output_process(pty, '^');
output_process(pty, '@' + c);
output_process(pty, '\n');
}
clear_input_buffer(pty);
if (pty->fg_proc) {
send_signal(pty->fg_proc, SIGINT);
}
return;
}
if (c == pty->tios.c_cc[VQUIT]) {
if (pty->tios.c_lflag & ECHO) {
output_process(pty, '^');
output_process(pty, '@' + c);
output_process(pty, '\n');
}
clear_input_buffer(pty);
if (pty->fg_proc) {
send_signal(pty->fg_proc, SIGQUIT);
}
return;
}
if (c == pty->tios.c_cc[VEOF]) {
if (pty->canon_buflen) {
dump_input_buffer(pty);
} else {
ring_buffer_interrupt(pty->in);
}
return;
}
if (pty->canon_buflen < pty->canon_bufsize) {
pty->canon_buffer[pty->canon_buflen] = c;
pty->canon_buflen++;
}
if (pty->tios.c_lflag & ECHO) {
output_process(pty, c);
}
if (c == '\n') {
pty->canon_buffer[pty->canon_buflen-1] = c;
dump_input_buffer(pty);
return;
}
return;
} else if (pty->tios.c_lflag & ECHO) {
output_process(pty, c);
}
IN(c);
}
int pty_ioctl(pty_t * pty, int request, void * argp) {
switch (request) {
case IOCTLDTYPE:
/*
* This is a special toaru-specific call to get a simple
* integer that describes the kind of device this is.
* It's more specific than just "character device" or "file",
* but for here we just need to say we're a TTY.
*/
return IOCTL_DTYPE_TTY;
case TIOCSWINSZ:
if (!argp) return -1;
validate(argp);
memcpy(&pty->size, argp, sizeof(struct winsize));
/* TODO send sigwinch to fg_prog */
return 0;
case TIOCGWINSZ:
if (!argp) return -1;
validate(argp);
memcpy(argp, &pty->size, sizeof(struct winsize));
return 0;
case TCGETS:
if (!argp) return -1;
validate(argp);
memcpy(argp, &pty->tios, sizeof(struct termios));
return 0;
case TIOCSPGRP:
if (!argp) return -1;
validate(argp);
pty->fg_proc = *(pid_t *)argp;
debug_print(NOTICE, "Setting PTY group to %d", pty->fg_proc);
return 0;
case TIOCGPGRP:
if (!argp) return -1;
validate(argp);
*(pid_t *)argp = pty->fg_proc;
return 0;
case TCSETS:
case TCSETSW:
case TCSETSF:
if (!argp) return -1;
validate(argp);
if (!(((struct termios *)argp)->c_lflag & ICANON) && (pty->tios.c_lflag & ICANON)) {
/* Switch out of canonical mode, the dump the input buffer */
dump_input_buffer(pty);
}
memcpy(&pty->tios, argp, sizeof(struct termios));
return 0;
default:
return -EINVAL;
}
}
uint32_t read_pty_master(fs_node_t * node, uint32_t offset, uint32_t size, uint8_t *buffer) {
pty_t * pty = (pty_t *)node->device;
/* Standard pipe read */
return ring_buffer_read(pty->out, size, buffer);
}
uint32_t write_pty_master(fs_node_t * node, uint32_t offset, uint32_t size, uint8_t *buffer) {
pty_t * pty = (pty_t *)node->device;
size_t l = 0;
for (uint8_t * c = buffer; l < size; ++c, ++l) {
input_process(pty, *c);
}
return l;
}
void open_pty_master(fs_node_t * node, unsigned int flags) {
return;
}
void close_pty_master(fs_node_t * node) {
return;
}
uint32_t read_pty_slave(fs_node_t * node, uint32_t offset, uint32_t size, uint8_t *buffer) {
pty_t * pty = (pty_t *)node->device;
if (pty->tios.c_lflag & ICANON) {
return ring_buffer_read(pty->in, size, buffer);
} else {
if (pty->tios.c_cc[VMIN] == 0) {
return ring_buffer_read(pty->in, MIN(size, ring_buffer_unread(pty->in)), buffer);
} else {
return ring_buffer_read(pty->in, MIN(pty->tios.c_cc[VMIN], size), buffer);
}
}
}
uint32_t write_pty_slave(fs_node_t * node, uint32_t offset, uint32_t size, uint8_t *buffer) {
pty_t * pty = (pty_t *)node->device;
size_t l = 0;
for (uint8_t * c = buffer; l < size; ++c, ++l) {
output_process_slave(pty, *c);
}
return l;
}
void open_pty_slave(fs_node_t * node, unsigned int flags) {
return;
}
void close_pty_slave(fs_node_t * node) {
return;
}
/*
* These are separate functions just in case I ever feel the need to do
* things differently in the slave or master.
*/
int ioctl_pty_master(fs_node_t * node, int request, void * argp) {
pty_t * pty = (pty_t *)node->device;
return pty_ioctl(pty, request, argp);
}
int ioctl_pty_slave(fs_node_t * node, int request, void * argp) {
pty_t * pty = (pty_t *)node->device;
return pty_ioctl(pty, request, argp);
}
int pty_available_input(fs_node_t * node) {
pty_t * pty = (pty_t *)node->device;
return ring_buffer_unread(pty->in);
}
int pty_available_output(fs_node_t * node) {
pty_t * pty = (pty_t *)node->device;
return ring_buffer_unread(pty->out);
}
static int check_pty_master(fs_node_t * node) {
pty_t * pty = (pty_t *)node->device;
if (ring_buffer_unread(pty->out) > 0) {
return 0;
}
return 1;
}
static int check_pty_slave(fs_node_t * node) {
pty_t * pty = (pty_t *)node->device;
if (ring_buffer_unread(pty->in) > 0) {
return 0;
}
return 1;
}
static int wait_pty_master(fs_node_t * node, void * process) {
pty_t * pty = (pty_t *)node->device;
ring_buffer_select_wait(pty->out, process);
return 0;
}
static int wait_pty_slave(fs_node_t * node, void * process) {
pty_t * pty = (pty_t *)node->device;
ring_buffer_select_wait(pty->in, process);
return 0;
}
fs_node_t * pty_master_create(pty_t * pty) {
fs_node_t * fnode = malloc(sizeof(fs_node_t));
memset(fnode, 0x00, sizeof(fs_node_t));
fnode->name[0] = '\0';
sprintf(fnode->name, "pty master");
fnode->uid = 0;
fnode->gid = 0;
fnode->mask = 0666;
fnode->flags = FS_PIPE;
fnode->read = read_pty_master;
fnode->write = write_pty_master;
fnode->open = open_pty_master;
fnode->close = close_pty_master;
fnode->selectcheck = check_pty_master;
fnode->selectwait = wait_pty_master;
fnode->readdir = NULL;
fnode->finddir = NULL;
fnode->ioctl = ioctl_pty_master;
fnode->get_size = pty_available_output;
fnode->device = pty;
return fnode;
}
fs_node_t * pty_slave_create(pty_t * pty) {
fs_node_t * fnode = malloc(sizeof(fs_node_t));
memset(fnode, 0x00, sizeof(fs_node_t));
fnode->name[0] = '\0';
sprintf(fnode->name, "pty slave");
fnode->uid = 0;
fnode->gid = 0;
fnode->mask = 0666;
fnode->flags = FS_PIPE;
fnode->read = read_pty_slave;
fnode->write = write_pty_slave;
fnode->open = open_pty_slave;
fnode->close = close_pty_slave;
fnode->selectcheck = check_pty_slave;
fnode->selectwait = wait_pty_slave;
fnode->readdir = NULL;
fnode->finddir = NULL;
fnode->ioctl = ioctl_pty_slave;
fnode->get_size = pty_available_input;
fnode->device = pty;
return fnode;
}
void pty_install(void) {
pty_list = list_create();
}
pty_t * pty_new(struct winsize * size) {
pty_t * pty = malloc(sizeof(pty_t));
/* stdin linkage; characters from terminal → PTY slave */
pty->in = ring_buffer_create(TTY_BUFFER_SIZE);
pty->out = ring_buffer_create(TTY_BUFFER_SIZE);
pty->in->discard = 1;
/* Master endpoint - writes go to stdin, reads come from stdout */
pty->master = pty_master_create(pty);
/* Slave endpoint, reads come from stdin, writes go to stdout */
pty->slave = pty_slave_create(pty);
/* TODO PTY name */
pty->name = 0;
if (size) {
memcpy(&pty->size, size, sizeof(struct winsize));
} else {
/* Sane defaults */
pty->size.ws_row = 25;
pty->size.ws_col = 80;
}
/* Controlling and foreground processes are set to 0 by default */
pty->ct_proc = 0;
pty->fg_proc = 0;
pty->tios.c_iflag = ICRNL | BRKINT;
pty->tios.c_oflag = ONLCR | OPOST;
pty->tios.c_lflag = ECHO | ECHOE | ECHOK | ICANON | ISIG | IEXTEN;
pty->tios.c_cflag = CREAD;
pty->tios.c_cc[VEOF] = 4; /* ^D */
pty->tios.c_cc[VEOL] = 0; /* Not set */
pty->tios.c_cc[VERASE] = '\b';
pty->tios.c_cc[VINTR] = 3; /* ^C */
pty->tios.c_cc[VKILL] = 21; /* ^U */
pty->tios.c_cc[VMIN] = 1;
pty->tios.c_cc[VQUIT] = 28; /* ^\ */
pty->tios.c_cc[VSTART] = 17; /* ^Q */
pty->tios.c_cc[VSTOP] = 19; /* ^S */
pty->tios.c_cc[VSUSP] = 26; /* ^Z */
pty->tios.c_cc[VTIME] = 0;
pty->canon_buffer = malloc(TTY_BUFFER_SIZE);
pty->canon_bufsize = TTY_BUFFER_SIZE-2;
pty->canon_buflen = 0;
return pty;
}
int pty_create(void *size, fs_node_t ** fs_master, fs_node_t ** fs_slave) {
pty_t * pty = pty_new(size);
*fs_master = pty->master;
*fs_slave = pty->slave;
return 0;
}

150
kernel/fs/unixpipe.c Normal file
View File

@ -0,0 +1,150 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2014 Kevin Lange
*/
#include <system.h>
#include <fs.h>
#include <pipe.h>
#include <logging.h>
#include <printf.h>
#include <ioctl.h>
#include <ringbuffer.h>
#define UNIX_PIPE_BUFFER 512
struct unix_pipe {
fs_node_t * read_end;
fs_node_t * write_end;
volatile int read_closed;
volatile int write_closed;
ring_buffer_t * buffer;
};
static void close_complete(struct unix_pipe * self) {
ring_buffer_destroy(self->buffer);
}
static uint32_t read_unixpipe(fs_node_t * node, uint32_t offset, uint32_t size, uint8_t *buffer) {
struct unix_pipe * self = node->device;
size_t read = 0;
while (read < size) {
if (self->write_closed && !ring_buffer_unread(self->buffer)) {
return read;
}
size_t r = ring_buffer_read(self->buffer, 1, buffer+read);
if (r && *((char *)(buffer + read)) == '\n') {
return read+r;
}
read += r;
}
return read;
}
static uint32_t write_unixpipe(fs_node_t * node, uint32_t offset, uint32_t size, uint8_t *buffer) {
struct unix_pipe * self = node->device;
size_t written = 0;
while (written < size) {
if (self->read_closed) {
/* SIGPIPE to current process */
signal_t * sig = malloc(sizeof(signal_t));
sig->handler = current_process->signals.functions[SIGPIPE];
sig->signum = SIGPIPE;
handle_signal((process_t *)current_process, sig);
return written;
}
size_t w = ring_buffer_write(self->buffer, 1, buffer+written);
written += w;
}
return written;
}
static void close_read_pipe(fs_node_t * node) {
struct unix_pipe * self = node->device;
debug_print(NOTICE, "Closing read end of pipe.");
self->read_closed = 1;
if (self->write_closed) {
debug_print(NOTICE, "Both ends now closed, should clean up.");
} else {
ring_buffer_interrupt(self->buffer);
}
}
static void close_write_pipe(fs_node_t * node) {
struct unix_pipe * self = node->device;
debug_print(NOTICE, "Closing write end of pipe.");
self->write_closed = 1;
if (self->read_closed) {
debug_print(NOTICE, "Both ends now closed, should clean up.");
} else {
ring_buffer_interrupt(self->buffer);
}
}
static int check_pipe(fs_node_t * node) {
struct unix_pipe * self = node->device;
if (ring_buffer_unread(self->buffer) > 0) {
return 0;
}
return 1;
}
static int wait_pipe(fs_node_t * node, void * process) {
struct unix_pipe * self = node->device;
ring_buffer_select_wait(self->buffer, process);
return 0;
}
int make_unix_pipe(fs_node_t ** pipes) {
size_t size = UNIX_PIPE_BUFFER;
pipes[0] = malloc(sizeof(fs_node_t));
pipes[1] = malloc(sizeof(fs_node_t));
memset(pipes[0], 0, sizeof(fs_node_t));
memset(pipes[1], 0, sizeof(fs_node_t));
sprintf(pipes[0]->name, "[pipe:read]");
sprintf(pipes[1]->name, "[pipe:write]");
pipes[0]->mask = 0666;
pipes[1]->mask = 0666;
pipes[0]->flags = FS_PIPE;
pipes[1]->flags = FS_PIPE;
pipes[0]->read = read_unixpipe;
pipes[1]->write = write_unixpipe;
pipes[0]->close = close_read_pipe;
pipes[1]->close = close_write_pipe;
/* Read end can wait */
pipes[0]->selectcheck = check_pipe;
pipes[0]->selectwait = wait_pipe;
struct unix_pipe * internals = malloc(sizeof(struct unix_pipe));
internals->read_end = pipes[0];
internals->write_end = pipes[1];
internals->read_closed = 0;
internals->write_closed = 0;
internals->buffer = ring_buffer_create(size);
pipes[0]->device = internals;
pipes[1]->device = internals;
return 0;
}

1044
kernel/fs/vfs.c Normal file

File diff suppressed because it is too large Load Diff

20
kernel/gdt.S Normal file
View File

@ -0,0 +1,20 @@
.section .text
.align 4
.global gdt_flush
.type gdt_flush, @function
gdt_flush:
/* Load GDT */
mov 4(%esp), %eax
lgdt (%eax)
mov $0x10, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %ss
ljmp $0x08, $.flush
.flush:
ret

10
kernel/idt.S Normal file
View File

@ -0,0 +1,10 @@
.section .text
.align 4
.global idt_load
.type idt_load, @function
idt_load:
mov 4(%esp), %eax
lidt (%eax)
ret

9
kernel/include/args.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
int args_present(char * karg);
char * args_value(char * karg);
void args_parse(char * _arg);
void early_stage_args(void);
void late_stage_args(void);

141
kernel/include/ata.h Normal file
View File

@ -0,0 +1,141 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
*
* Values for ATA / PATA devices
*/
#pragma once
#define ATA_SR_BSY 0x80
#define ATA_SR_DRDY 0x40
#define ATA_SR_DF 0x20
#define ATA_SR_DSC 0x10
#define ATA_SR_DRQ 0x08
#define ATA_SR_CORR 0x04
#define ATA_SR_IDX 0x02
#define ATA_SR_ERR 0x01
#define ATA_ER_BBK 0x80
#define ATA_ER_UNC 0x40
#define ATA_ER_MC 0x20
#define ATA_ER_IDNF 0x10
#define ATA_ER_MCR 0x08
#define ATA_ER_ABRT 0x04
#define ATA_ER_TK0NF 0x02
#define ATA_ER_AMNF 0x01
#define ATA_CMD_READ_PIO 0x20
#define ATA_CMD_READ_PIO_EXT 0x24
#define ATA_CMD_READ_DMA 0xC8
#define ATA_CMD_READ_DMA_EXT 0x25
#define ATA_CMD_WRITE_PIO 0x30
#define ATA_CMD_WRITE_PIO_EXT 0x34
#define ATA_CMD_WRITE_DMA 0xCA
#define ATA_CMD_WRITE_DMA_EXT 0x35
#define ATA_CMD_CACHE_FLUSH 0xE7
#define ATA_CMD_CACHE_FLUSH_EXT 0xEA
#define ATA_CMD_PACKET 0xA0
#define ATA_CMD_IDENTIFY_PACKET 0xA1
#define ATA_CMD_IDENTIFY 0xEC
#define ATAPI_CMD_READ 0xA8
#define ATAPI_CMD_EJECT 0x1B
#define ATA_IDENT_DEVICETYPE 0
#define ATA_IDENT_CYLINDERS 2
#define ATA_IDENT_HEADS 6
#define ATA_IDENT_SECTORS 12
#define ATA_IDENT_SERIAL 20
#define ATA_IDENT_MODEL 54
#define ATA_IDENT_CAPABILITIES 98
#define ATA_IDENT_FIELDVALID 106
#define ATA_IDENT_MAX_LBA 120
#define ATA_IDENT_COMMANDSETS 164
#define ATA_IDENT_MAX_LBA_EXT 200
#define IDE_ATA 0x00
#define IDE_ATAPI 0x01
#define ATA_MASTER 0x00
#define ATA_SLAVE 0x01
#define ATA_REG_DATA 0x00
#define ATA_REG_ERROR 0x01
#define ATA_REG_FEATURES 0x01
#define ATA_REG_SECCOUNT0 0x02
#define ATA_REG_LBA0 0x03
#define ATA_REG_LBA1 0x04
#define ATA_REG_LBA2 0x05
#define ATA_REG_HDDEVSEL 0x06
#define ATA_REG_COMMAND 0x07
#define ATA_REG_STATUS 0x07
#define ATA_REG_SECCOUNT1 0x08
#define ATA_REG_LBA3 0x09
#define ATA_REG_LBA4 0x0A
#define ATA_REG_LBA5 0x0B
#define ATA_REG_CONTROL 0x0C
#define ATA_REG_ALTSTATUS 0x0C
#define ATA_REG_DEVADDRESS 0x0D
// Channels:
#define ATA_PRIMARY 0x00
#define ATA_SECONDARY 0x01
// Directions:
#define ATA_READ 0x00
#define ATA_WRITE 0x01
typedef struct {
uint16_t base;
uint16_t ctrl;
uint16_t bmide;
uint16_t nien;
} ide_channel_regs_t;
typedef struct {
uint8_t reserved;
uint8_t channel;
uint8_t drive;
uint16_t type;
uint16_t signature;
uint16_t capabilities;
uint32_t command_sets;
uint32_t size;
uint8_t model[41];
} ide_device_t;
typedef struct {
uint8_t status;
uint8_t chs_first_sector[3];
uint8_t type;
uint8_t chs_last_sector[3];
uint32_t lba_first_sector;
uint32_t sector_count;
} partition_t;
typedef struct {
uint16_t flags;
uint16_t unused1[9];
char serial[20];
uint16_t unused2[3];
char firmware[8];
char model[40];
uint16_t sectors_per_int;
uint16_t unused3;
uint16_t capabilities[2];
uint16_t unused4[2];
uint16_t valid_ext_data;
uint16_t unused5[5];
uint16_t size_of_rw_mult;
uint32_t sectors_28;
uint16_t unused6[38];
uint64_t sectors_48;
uint16_t unused7[152];
} __attribute__((packed)) ata_identify_t;
typedef struct {
uint8_t boostrap[446];
partition_t partitions[4];
uint8_t signature[2];
} __attribute__((packed)) mbr_t;

19
kernel/include/bitset.h Normal file
View File

@ -0,0 +1,19 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
*/
#pragma once
#include <system.h>
typedef struct {
unsigned char *data;
size_t size;
} bitset_t;
void bitset_init(bitset_t *set, size_t size);
void bitset_free(bitset_t *set);
void bitset_set(bitset_t *set, size_t bit);
void bitset_clear(bitset_t *set, size_t bit);
int bitset_test(bitset_t *set, size_t bit);
/* Find first unset bit */
int bitset_ffub(bitset_t *set);

20
kernel/include/boot.h Normal file
View File

@ -0,0 +1,20 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
*/
#pragma once
/*
* Boot Information Types
* Used in the kernel boot process to determine
* how we booted and where we can get BIOS
* information from that bootloader.
*
*/
#include <system.h>
/*
* Multiboot
* A format managed by GNU and used in GRUB.
* Also supported natively by QEMU and a few
* other emulators.
*/
#include <multiboot.h>

185
kernel/include/elf.h Normal file
View File

@ -0,0 +1,185 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
*
* ELF Binary Executable headers
*
*/
#pragma once
/*
* Different bits of our build environment
* require different header files for definitions
*/
#ifdef _KERNEL_
# include <types.h>
#else
# include <stdint.h>
#endif
/*
* Unless otherwise stated, the definitions herein
* are sourced from the Portable Formats Specification,
* version 1.1 - ELF: Executable and Linkable Format
*/
/*
* ELF Magic Signature
*/
#define ELFMAG0 0x7f
#define ELFMAG1 'E'
#define ELFMAG2 'L'
#define ELFMAG3 'F'
#define EI_NIDENT 16
/*
* ELF Datatypes
*/
typedef uint32_t Elf32_Word;
typedef uint32_t Elf32_Addr;
typedef uint32_t Elf32_Off;
typedef uint32_t Elf32_Sword;
typedef uint16_t Elf32_Half;
/*
* ELF Header
*/
typedef struct {
unsigned char e_ident[EI_NIDENT];
Elf32_Half e_type;
Elf32_Half e_machine;
Elf32_Word e_version;
Elf32_Addr e_entry;
Elf32_Off e_phoff;
Elf32_Off e_shoff;
Elf32_Word e_flags;
Elf32_Half e_ehsize;
Elf32_Half e_phentsize;
Elf32_Half e_phnum;
Elf32_Half e_shentsize;
Elf32_Half e_shnum;
Elf32_Half e_shstrndx;
} Elf32_Header;
/*
* e_type
*/
#define ET_NONE 0 /* No file type */
#define ET_REL 1 /* Relocatable file */
#define ET_EXEC 2 /* Executable file */
#define ET_DYN 3 /* Shared object file */
#define ET_CORE 4 /* Core file */
#define ET_LOPROC 0xff0 /* [Processor Specific] */
#define ET_HIPROC 0xfff /* [Processor Specific] */
/*
* Machine types
*/
#define EM_NONE 0
#define EM_386 3
#define EV_NONE 0
#define EV_CURRENT 1
/** Program Header */
typedef struct {
Elf32_Word p_type;
Elf32_Off p_offset;
Elf32_Addr p_vaddr;
Elf32_Addr p_paddr;
Elf32_Word p_filesz;
Elf32_Word p_memsz;
Elf32_Word p_flags;
Elf32_Word p_align;
} Elf32_Phdr;
/* p_type values */
#define PT_NULL 0 /* Unused, skip me */
#define PT_LOAD 1 /* Loadable segment */
#define PT_DYNAMIC 2 /* Dynamic linking information */
#define PT_INTERP 3 /* Interpreter (null-terminated string, pathname) */
#define PT_NOTE 4 /* Auxillary information */
#define PT_SHLIB 5 /* Reserved. */
#define PT_PHDR 6 /* Oh, it's me. Hello! Back-reference to the header table itself */
#define PT_LOPROC 0x70000000
#define PT_HIPROC 0x7FFFFFFF
/** Section Header */
typedef struct {
Elf32_Word sh_name;
Elf32_Word sh_type;
Elf32_Word sh_flags;
Elf32_Addr sh_addr;
Elf32_Off sh_offset;
Elf32_Word sh_size;
Elf32_Word sh_link;
Elf32_Word sh_info;
Elf32_Word sh_addralign;
Elf32_Word sh_entsize;
} Elf32_Shdr;
typedef struct {
uint32_t id;
uintptr_t ptr;
} Elf32_auxv;
typedef struct {
Elf32_Word st_name;
Elf32_Addr st_value;
Elf32_Word st_size;
unsigned char st_info;
unsigned char st_other;
Elf32_Half st_shndx;
} Elf32_Sym;
typedef struct {
Elf32_Addr r_offset;
Elf32_Word r_info;
} Elf32_Rel;
typedef struct {
Elf32_Sword d_tag;
union {
Elf32_Word d_val;
Elf32_Addr d_ptr;
Elf32_Off d_off;
} d_un;
} Elf32_Dyn;
/* sh_type values */
#define SHT_NONE 0
#define SHT_PROGBITS 1
#define SHT_SYMTAB 2
#define SHT_STRTAB 3
#define SHT_NOBITS 8
#define SHT_REL 9
#define ELF32_R_SYM(i) ((i) >> 8)
#define ELF32_R_TYPE(i) ((unsigned char)(i))
#define ELF32_R_INFO(s,t) (((s) << 8) + (unsigned char)(t))
#define ELF32_ST_BIND(i) ((i) >> 4)
#define ELF32_ST_TYPE(i) ((i) & 0xf)
#define ELF32_ST_INFO(b,t) (((b) << 4) + ((t) & 0xf))
#define STB_LOCAL 0
#define STB_GLOBAL 1
#define STB_WEAK 2
#define STB_NUM 3
#define STB_LOPROC 13
#define STB_HIPROC 15
#define STT_NOTYPE 0
#define STT_OBJECT 1
#define STT_FUNC 2
#define STT_SECTION 3
#define STT_FILE 4
#define STT_COMMON 5
#define STT_TLS 6
#define STT_NUM 7
#define STT_LOPROC 13
#define STT_HIPROC 15

1
kernel/include/errno_defs.h Symbolic link
View File

@ -0,0 +1 @@
../../include/errno.h

174
kernel/include/ext2.h Normal file
View File

@ -0,0 +1,174 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
*/
#pragma once
#ifdef _KERNEL_
# include <types.h>
#else
# ifdef BOOTLOADER
# include <types.h>
# else
# include <stdint.h>
# endif
#endif
#define EXT2_SUPER_MAGIC 0xEF53
#define EXT2_DIRECT_BLOCKS 12
/* Super block struct. */
struct ext2_superblock {
uint32_t inodes_count;
uint32_t blocks_count;
uint32_t r_blocks_count;
uint32_t free_blocks_count;
uint32_t free_inodes_count;
uint32_t first_data_block;
uint32_t log_block_size;
uint32_t log_frag_size;
uint32_t blocks_per_group;
uint32_t frags_per_group;
uint32_t inodes_per_group;
uint32_t mtime;
uint32_t wtime;
uint16_t mnt_count;
uint16_t max_mnt_count;
uint16_t magic;
uint16_t state;
uint16_t errors;
uint16_t minor_rev_level;
uint32_t lastcheck;
uint32_t checkinterval;
uint32_t creator_os;
uint32_t rev_level;
uint16_t def_resuid;
uint16_t def_resgid;
/* EXT2_DYNAMIC_REV */
uint32_t first_ino;
uint16_t inode_size;
uint16_t block_group_nr;
uint32_t feature_compat;
uint32_t feature_incompat;
uint32_t feature_ro_compat;
uint8_t uuid[16];
uint8_t volume_name[16];
uint8_t last_mounted[64];
uint32_t algo_bitmap;
/* Performance Hints */
uint8_t prealloc_blocks;
uint8_t prealloc_dir_blocks;
uint16_t _padding;
/* Journaling Support */
uint8_t journal_uuid[16];
uint32_t journal_inum;
uint32_t jounral_dev;
uint32_t last_orphan;
/* Directory Indexing Support */
uint32_t hash_seed[4];
uint8_t def_hash_version;
uint16_t _padding_a;
uint8_t _padding_b;
/* Other Options */
uint32_t default_mount_options;
uint32_t first_meta_bg;
uint8_t _unused[760];
} __attribute__ ((packed));
typedef struct ext2_superblock ext2_superblock_t;
/* Block group descriptor. */
struct ext2_bgdescriptor {
uint32_t block_bitmap;
uint32_t inode_bitmap; // block no. of inode bitmap
uint32_t inode_table;
uint16_t free_blocks_count;
uint16_t free_inodes_count;
uint16_t used_dirs_count;
uint16_t pad;
uint8_t reserved[12];
} __attribute__ ((packed));
typedef struct ext2_bgdescriptor ext2_bgdescriptor_t;
/* File Types */
#define EXT2_S_IFSOCK 0xC000
#define EXT2_S_IFLNK 0xA000
#define EXT2_S_IFREG 0x8000
#define EXT2_S_IFBLK 0x6000
#define EXT2_S_IFDIR 0x4000
#define EXT2_S_IFCHR 0x2000
#define EXT2_S_IFIFO 0x1000
/* setuid, etc. */
#define EXT2_S_ISUID 0x0800
#define EXT2_S_ISGID 0x0400
#define EXT2_S_ISVTX 0x0200
/* rights */
#define EXT2_S_IRUSR 0x0100
#define EXT2_S_IWUSR 0x0080
#define EXT2_S_IXUSR 0x0040
#define EXT2_S_IRGRP 0x0020
#define EXT2_S_IWGRP 0x0010
#define EXT2_S_IXGRP 0x0008
#define EXT2_S_IROTH 0x0004
#define EXT2_S_IWOTH 0x0002
#define EXT2_S_IXOTH 0x0001
/* This is not actually the inode table.
* It represents an inode in an inode table on disk. */
struct ext2_inodetable {
uint16_t mode;
uint16_t uid;
uint32_t size; // file length in byte.
uint32_t atime;
uint32_t ctime;
uint32_t mtime;
uint32_t dtime;
uint16_t gid;
uint16_t links_count;
uint32_t blocks;
uint32_t flags;
uint32_t osd1;
uint32_t block[15];
uint32_t generation;
uint32_t file_acl;
uint32_t dir_acl;
uint32_t faddr;
uint8_t osd2[12];
} __attribute__ ((packed));
typedef struct ext2_inodetable ext2_inodetable_t;
/* Represents directory entry on disk. */
struct ext2_dir {
uint32_t inode;
uint16_t rec_len;
uint8_t name_len;
uint8_t file_type;
char name[]; /* Actually a set of characters, at most 255 bytes */
} __attribute__ ((packed));
typedef struct ext2_dir ext2_dir_t;
typedef struct {
uint32_t block_no;
uint32_t last_use;
uint8_t dirty;
uint8_t *block;
} ext2_disk_cache_entry_t;
typedef int (*ext2_block_io_t) (void *, uint32_t, uint8_t *);

170
kernel/include/fs.h Normal file
View File

@ -0,0 +1,170 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
*/
#pragma once
#define PATH_SEPARATOR '/'
#define PATH_SEPARATOR_STRING "/"
#define PATH_UP ".."
#define PATH_DOT "."
#define O_RDONLY 0x0000
#define O_WRONLY 0x0001
#define O_RDWR 0x0002
#define O_APPEND 0x0008
#define O_CREAT 0x0200
#define O_TRUNC 0x0400
#define O_EXCL 0x0800
#define O_NOFOLLOW 0x1000
#define O_PATH 0x2000
#define FS_FILE 0x01
#define FS_DIRECTORY 0x02
#define FS_CHARDEVICE 0x04
#define FS_BLOCKDEVICE 0x08
#define FS_PIPE 0x10
#define FS_SYMLINK 0x20
#define FS_MOUNTPOINT 0x40
#define _IFMT 0170000 /* type of file */
#define _IFDIR 0040000 /* directory */
#define _IFCHR 0020000 /* character special */
#define _IFBLK 0060000 /* block special */
#define _IFREG 0100000 /* regular */
#define _IFLNK 0120000 /* symbolic link */
#define _IFSOCK 0140000 /* socket */
#define _IFIFO 0010000 /* fifo */
struct fs_node;
typedef uint32_t (*read_type_t) (struct fs_node *, uint32_t, uint32_t, uint8_t *);
typedef uint32_t (*write_type_t) (struct fs_node *, uint32_t, uint32_t, uint8_t *);
typedef void (*open_type_t) (struct fs_node *, unsigned int flags);
typedef void (*close_type_t) (struct fs_node *);
typedef struct dirent *(*readdir_type_t) (struct fs_node *, uint32_t);
typedef struct fs_node *(*finddir_type_t) (struct fs_node *, char *name);
typedef void (*create_type_t) (struct fs_node *, char *name, uint16_t permission);
typedef void (*unlink_type_t) (struct fs_node *, char *name);
typedef void (*mkdir_type_t) (struct fs_node *, char *name, uint16_t permission);
typedef int (*ioctl_type_t) (struct fs_node *, int request, void * argp);
typedef int (*get_size_type_t) (struct fs_node *);
typedef int (*chmod_type_t) (struct fs_node *, int mode);
typedef void (*symlink_type_t) (struct fs_node *, char * name, char * value);
typedef int (*readlink_type_t) (struct fs_node *, char * buf, size_t size);
typedef int (*selectcheck_type_t) (struct fs_node *);
typedef int (*selectwait_type_t) (struct fs_node *, void * process);
typedef int (*chown_type_t) (struct fs_node *, int, int);
typedef struct fs_node {
char name[256]; /* The filename. */
void * device; /* Device object (optional) */
uint32_t mask; /* The permissions mask. */
uint32_t uid; /* The owning user. */
uint32_t gid; /* The owning group. */
uint32_t flags; /* Flags (node type, etc). */
uint32_t inode; /* Inode number. */
uint32_t length; /* Size of the file, in byte. */
uint32_t impl; /* Used to keep track which fs it belongs to. */
uint32_t open_flags; /* Flags passed to open (read/write/append, etc.) */
/* times */
uint32_t atime; /* Accessed */
uint32_t mtime; /* Modified */
uint32_t ctime; /* Created */
/* File operations */
read_type_t read;
write_type_t write;
open_type_t open;
close_type_t close;
readdir_type_t readdir;
finddir_type_t finddir;
create_type_t create;
mkdir_type_t mkdir;
ioctl_type_t ioctl;
get_size_type_t get_size;
chmod_type_t chmod;
unlink_type_t unlink;
symlink_type_t symlink;
readlink_type_t readlink;
struct fs_node *ptr; /* Alias pointer, for symlinks. */
uint32_t offset; /* Offset for read operations XXX move this to new "file descriptor" entry */
int32_t refcount;
uint32_t nlink;
selectcheck_type_t selectcheck;
selectwait_type_t selectwait;
chown_type_t chown;
} fs_node_t;
struct dirent {
uint32_t ino; /* Inode number. */
char name[256]; /* The filename. */
};
struct stat {
uint16_t st_dev;
uint16_t st_ino;
uint32_t st_mode;
uint16_t st_nlink;
uint16_t st_uid;
uint16_t st_gid;
uint16_t st_rdev;
uint32_t st_size;
uint32_t st_atime;
uint32_t __unused1;
uint32_t st_mtime;
uint32_t __unused2;
uint32_t st_ctime;
uint32_t __unused3;
uint32_t st_blksize;
uint32_t st_blocks;
};
struct vfs_entry {
char * name;
fs_node_t * file;
char * device;
char * fs_type;
};
extern fs_node_t *fs_root;
extern int pty_create(void *size, fs_node_t ** fs_master, fs_node_t ** fs_slave);
int has_permission(fs_node_t *node, int permission_bit);
uint32_t read_fs(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer);
uint32_t write_fs(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer);
void open_fs(fs_node_t *node, unsigned int flags);
void close_fs(fs_node_t *node);
struct dirent *readdir_fs(fs_node_t *node, uint32_t index);
fs_node_t *finddir_fs(fs_node_t *node, char *name);
int mkdir_fs(char *name, uint16_t permission);
int create_file_fs(char *name, uint16_t permission);
fs_node_t *kopen(char *filename, uint32_t flags);
char *canonicalize_path(char *cwd, char *input);
fs_node_t *clone_fs(fs_node_t * source);
int ioctl_fs(fs_node_t *node, int request, void * argp);
int chmod_fs(fs_node_t *node, int mode);
int chown_fs(fs_node_t *node, int uid, int gid);
int unlink_fs(char * name);
int symlink_fs(char * value, char * name);
int readlink_fs(fs_node_t * node, char * buf, size_t size);
int selectcheck_fs(fs_node_t * node);
int selectwait_fs(fs_node_t * node, void * process);
void vfs_install(void);
void * vfs_mount(char * path, fs_node_t * local_root);
typedef fs_node_t * (*vfs_mount_callback)(char * arg, char * mount_point);
int vfs_register(char * name, vfs_mount_callback callback);
int vfs_mount_type(char * type, char * arg, char * mountpoint);
void vfs_lock(fs_node_t * node);
/* Debug purposes only, please */
void debug_print_vfs_tree(void);
void map_vfs_directory(char *);
int make_unix_pipe(fs_node_t ** pipes);

47
kernel/include/hashmap.h Normal file
View File

@ -0,0 +1,47 @@
#pragma once
#include "list.h"
#ifdef _KERNEL_
# include <system.h>
#else
# include <string.h>
# include <stddef.h>
# include <stdlib.h>
#endif
typedef unsigned int (*hashmap_hash_t) (void * key);
typedef int (*hashmap_comp_t) (void * a, void * b);
typedef void (*hashmap_free_t) (void *);
typedef void * (*hashmap_dupe_t) (void *);
typedef struct hashmap_entry {
char * key;
void * value;
struct hashmap_entry * next;
} hashmap_entry_t;
typedef struct hashmap {
hashmap_hash_t hash_func;
hashmap_comp_t hash_comp;
hashmap_dupe_t hash_key_dup;
hashmap_free_t hash_key_free;
hashmap_free_t hash_val_free;
size_t size;
hashmap_entry_t ** entries;
} hashmap_t;
hashmap_t * hashmap_create(int size);
hashmap_t * hashmap_create_int(int size);
void * hashmap_set(hashmap_t * map, void * key, void * value);
void * hashmap_get(hashmap_t * map, void * key);
void * hashmap_remove(hashmap_t * map, void * key);
int hashmap_has(hashmap_t * map, void * key);
list_t * hashmap_keys(hashmap_t * map);
list_t * hashmap_values(hashmap_t * map);
void hashmap_free(hashmap_t * map);
unsigned int hashmap_string_hash(void * key);
int hashmap_string_comp(void * a, void * b);
void * hashmap_string_dupe(void * key);

1
kernel/include/ioctl.h Symbolic link
View File

@ -0,0 +1 @@
../../include/sys/ioctl.h

188
kernel/include/ipv4.h Normal file
View File

@ -0,0 +1,188 @@
#pragma once
#include <system.h>
struct ethernet_packet {
uint8_t destination[6];
uint8_t source[6];
uint16_t type;
uint8_t payload[];
} __attribute__((packed));
struct ipv4_packet {
uint8_t version_ihl;
uint8_t dscp_ecn;
uint16_t length;
uint16_t ident;
uint16_t flags_fragment;
uint8_t ttl;
uint8_t protocol;
uint16_t checksum;
uint32_t source;
uint32_t destination;
uint8_t payload[];
} __attribute__ ((packed));
struct udp_packet {
uint16_t source_port;
uint16_t destination_port;
uint16_t length;
uint16_t checksum;
uint8_t payload[];
} __attribute__ ((packed));
struct dhcp_packet {
uint8_t op;
uint8_t htype;
uint8_t hlen;
uint8_t hops;
uint32_t xid;
uint16_t secs;
uint16_t flags;
uint32_t ciaddr;
uint32_t yiaddr;
uint32_t siaddr;
uint32_t giaddr;
uint8_t chaddr[16];
uint8_t sname[64];
uint8_t file[128];
uint32_t magic;
uint8_t options[];
} __attribute__ ((packed));
struct dns_packet {
uint16_t qid;
uint16_t flags;
uint16_t questions;
uint16_t answers;
uint16_t authorities;
uint16_t additional;
uint8_t data[];
} __attribute__ ((packed));
struct tcp_header {
uint16_t source_port;
uint16_t destination_port;
uint32_t seq_number;
uint32_t ack_number;
uint16_t flags;
uint16_t window_size;
uint16_t checksum;
uint16_t urgent;
uint8_t payload[];
} __attribute__((packed));
struct tcp_check_header {
uint32_t source;
uint32_t destination;
uint8_t zeros;
uint8_t protocol;
uint16_t tcp_len;
uint8_t tcp_header[];
};
#define SOCK_STREAM 1
#define SOCK_DGRAM 2
// Note: Data offset is in upper 4 bits of flags field. Shift and subtract 5 since that is the min TCP size.
// If the value is more than 5, multiply by 4 because this field is specified in number of words
#define TCP_OPTIONS_LENGTH(tcp) (((((tcp)->flags) >> 12) - 5) * 4)
#define TCP_HEADER_LENGTH(tcp) ((((tcp)->flags) >> 12) * 4)
#define TCP_HEADER_LENGTH_FLIPPED(tcp) (((htons((tcp)->flags)) >> 12) * 4)
#define htonl(l) ( (((l) & 0xFF) << 24) | (((l) & 0xFF00) << 8) | (((l) & 0xFF0000) >> 8) | (((l) & 0xFF000000) >> 24))
#define htons(s) ( (((s) & 0xFF) << 8) | (((s) & 0xFF00) >> 8) )
#define ntohl(l) htonl((l))
#define ntohs(s) htons((s))
#define BROADCAST_MAC {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
#define IPV4_PROT_UDP 17
#define IPV4_PROT_TCP 6
#define DHCP_MAGIC 0x63825363
#define TCP_FLAGS_FIN (1 << 0)
#define TCP_FLAGS_SYN (1 << 1)
#define TCP_FLAGS_RES (1 << 2)
#define TCP_FLAGS_PSH (1 << 3)
#define TCP_FLAGS_ACK (1 << 4)
#define TCP_FLAGS_URG (1 << 5)
#define TCP_FLAGS_ECE (1 << 6)
#define TCP_FLAGS_CWR (1 << 7)
#define TCP_FLAGS_NS (1 << 8)
#define DATA_OFFSET_5 (0x5 << 12)
#define ETHERNET_TYPE_IPV4 0x0800
#define ETHERNET_TYPE_ARP 0x0806
extern uint32_t ip_aton(const char * in);
extern void ip_ntoa(uint32_t src_addr, char * out);
extern uint16_t calculate_ipv4_checksum(struct ipv4_packet * p);
uint16_t calculate_tcp_checksum(struct tcp_check_header * p, struct tcp_header * h, void * d, size_t d_words);
struct tcp_socket {
list_t* is_connected;
uint32_t seq_no;
uint32_t ack_no;
int status;
};
// Note: for now, not sure what to put in here, so removing from the union to get rid of compiler warnings about empty struct
// struct udp_socket {
// };
struct socket {
uint32_t ip;
uint8_t mac[6];
uint32_t port_dest;
uint32_t port_recv;
list_t* packet_queue;
spin_lock_t packet_queue_lock;
list_t* packet_wait;
int32_t status;
size_t bytes_available;
size_t bytes_read;
void * current_packet;
uint32_t sock_type;
union {
struct tcp_socket tcp_socket;
// struct udp_socket udp_socket;
} proto_sock;
list_t * alert_waiters;
};
struct sized_blob {
size_t size;
uint8_t blob[];
};
struct in_addr {
unsigned long s_addr; // load with inet_pton()
};
struct sockaddr {
uint16_t sa_family;
char sa_data[14];
};
struct sockaddr_in {
short sin_family; // e.g. AF_INET, AF_INET6
unsigned short sin_port; // e.g. htons(3490)
struct in_addr sin_addr; // see struct in_addr, below
char sin_zero[8]; // zero this if you want to
};
typedef struct {
uint8_t *payload;
size_t payload_size;
} tcpdata_t;

37
kernel/include/libc.h Normal file
View File

@ -0,0 +1,37 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
*/
#pragma once
#include <stddef.h>
#define MIN(A, B) ((A) < (B) ? (A) : (B))
#define MAX(A, B) ((A) > (B) ? (A) : (B))
extern void * memcpy(void * restrict dest, const void * restrict src, size_t n);
extern void * memset(void * dest, int c, size_t n);
extern void * memchr(const void * src, int c, size_t n);
extern void * memrchr(const void * m, int c, size_t n);
extern void * memmove(void *dest, const void *src, size_t n);
extern int memcmp(const void *vl, const void *vr, size_t n);
extern char * strdup(const char * s);
extern char * stpcpy(char * restrict d, const char * restrict s);
extern char * strcpy(char * restrict dest, const char * restrict src);
extern char * strchrnul(const char * s, int c);
extern char * strchr(const char * s, int c);
extern char * strrchr(const char * s, int c);
extern char * strpbrk(const char * s, const char * b);
extern char * strstr(const char * h, const char * n);
extern int strcmp(const char * l, const char * r);
extern size_t strcspn(const char * s, const char * c);
extern size_t strspn(const char * s, const char * c);
extern size_t strlen(const char * s);
extern int atoi(const char * s);
/* Non-standard broken strtok_r */
extern char * strtok_r(char * str, const char * delim, char ** saveptr);

50
kernel/include/list.h Normal file
View File

@ -0,0 +1,50 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
*
* General-purpose list implementations.
*/
#pragma once
#ifdef _KERNEL_
# include <types.h>
#else
# include <stdint.h>
# include <stddef.h>
# include <assert.h>
#endif
typedef struct node {
struct node * next;
struct node * prev;
void * value;
void * owner;
} __attribute__((packed)) node_t;
typedef struct {
node_t * head;
node_t * tail;
size_t length;
} __attribute__((packed)) list_t;
void list_destroy(list_t * list);
void list_free(list_t * list);
void list_append(list_t * list, node_t * item);
node_t * list_insert(list_t * list, void * item);
list_t * list_create(void);
node_t * list_find(list_t * list, void * value);
int list_index_of(list_t * list, void * value);
void list_remove(list_t * list, size_t index);
void list_delete(list_t * list, node_t * node);
node_t * list_pop(list_t * list);
node_t * list_dequeue(list_t * list);
list_t * list_copy(list_t * original);
void list_merge(list_t * target, list_t * source);
void list_append_after(list_t * list, node_t * before, node_t * node);
node_t * list_insert_after(list_t * list, node_t * before, void * item);
void list_append_before(list_t * list, node_t * after, node_t * node);
node_t * list_insert_before(list_t * list, node_t * after, void * item);
#define foreach(i, list) for (node_t * i = (list)->head; i != NULL; i = i->next)
#define foreachr(i, list) for (node_t * i = (list)->tail; i != NULL; i = i->prev)

27
kernel/include/logging.h Normal file
View File

@ -0,0 +1,27 @@
#pragma once
typedef enum {
INFO = 0, /* Unimportant */
NOTICE, /* Important, but not bad */
WARNING, /* Not what was expected, but still okay */
ERROR, /* This is bad... */
CRITICAL, /* Shit */
INSANE
} log_type_t;
extern log_type_t debug_level;
extern void * debug_file;
extern void _debug_print(char * title, int line_no, log_type_t level, char *fmt, ...);
extern void (*debug_hook)(void *, char *);
extern void (*debug_video_crash)(char **);
#ifndef MODULE_NAME
#define MODULE_NAME __FILE__
#endif
#ifndef QUIET
#define debug_print(level, ...) _debug_print(MODULE_NAME, __LINE__, level, __VA_ARGS__)
#else
#define debug_print(level, ...)
#endif

16
kernel/include/mem.h Normal file
View File

@ -0,0 +1,16 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
*/
#pragma once
#include <types.h>
extern uintptr_t heap_end;
extern void set_frame(uintptr_t frame_addr);
extern void clear_frame(uintptr_t frame_addr);
extern uint32_t test_frame(uintptr_t frame_addr);
extern uint32_t first_frame(void);
extern uintptr_t map_to_physical(uintptr_t virtual);

32
kernel/include/mod/net.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef KERNEL_MOD_NET_H
#define KERNEL_MOD_NET_H
typedef uint8_t* (*get_mac_func)(void);
typedef struct ethernet_packet* (*get_packet_func)(void);
typedef void (*send_packet_func)(uint8_t*, size_t);
struct netif {
void *extra;
get_mac_func get_mac;
get_packet_func get_packet;
send_packet_func send_packet;
uint8_t hwaddr[6];
uint32_t source;
char * driver;
uint32_t gateway;
};
extern void init_netif_funcs(get_mac_func mac_func, get_packet_func get_func, send_packet_func send_func, char * device);
extern void net_handler(void * data, char * name);
extern size_t write_dhcp_packet(uint8_t * buffer);
extern struct socket* net_open(uint32_t type);
extern int net_send(struct socket* socket, uint8_t* payload, size_t payload_size, int flags);
extern size_t net_recv(struct socket* socket, uint8_t* buffer, size_t len);
extern int net_connect(struct socket* socket, uint32_t dest_ip, uint16_t dest_port);
extern int net_close(struct socket* socket);
#endif

4
kernel/include/mod/rtl.h Normal file
View File

@ -0,0 +1,4 @@
#ifndef KERNEL_MOD_RTL_H
#define KERNEL_MOD_RTL_H
#endif

View File

@ -0,0 +1,34 @@
#ifndef KERNEL_MOD_SHELL_H
#define KERNEL_MOD_SHELL_H
#include <fs.h>
/*
* We're going to have a list of shell commands.
* We'll search through it linearly because I don't
* care to write a hashmap right now. Maybe later.
*/
struct shell_command {
char * name;
int (*function) (fs_node_t * tty, int argc, char * argv[]);
char * description;
};
extern void debug_shell_install(struct shell_command * sh);
extern int debug_shell_readline(fs_node_t * dev, char * linebuf, int max);
extern void tty_set_buffered(fs_node_t * dev);
extern void tty_set_unbuffered(fs_node_t * dev);
#define DEFINE_SHELL_FUNCTION(n, desc) \
static int shell_ ## n (fs_node_t * tty, int argc, char * argv[]); \
static struct shell_command shell_ ## n ## _desc = { \
.name = #n , \
.function = &shell_ ## n , \
.description = desc \
}; \
static int shell_ ## n (fs_node_t * tty, int argc, char * argv[])
#define BIND_SHELL_FUNCTION(name) \
debug_shell_install(&shell_ ## name ## _desc);
#endif

52
kernel/include/mod/snd.h Normal file
View File

@ -0,0 +1,52 @@
#ifndef KERNEL_MOD_SND_H
#define KERNEL_MOD_SND_H
/* The format isn't really used for anything right now */
#define SND_FORMAT_L16SLE 0 /* Linear 16-bit signed little endian */
#include <mod/sound.h>
#include <logging.h>
#include <system.h>
#define SND_KNOB_VENDOR 1024
typedef uint16_t snd_mixer_enum_t;
typedef struct snd_knob {
char name[SND_KNOB_NAME_SIZE];
uint32_t id;
} snd_knob_t;
typedef struct snd_device {
char name[256]; /* Name of the device. */
void * device; /* Private data for the device. May be NULL. */
uint32_t playback_speed; /* Playback speed in Hz */
uint32_t playback_format; /* Playback format (SND_FORMAT_*) */
snd_knob_t *knobs;
uint32_t num_knobs;
int (*mixer_read)(uint32_t knob_id, uint32_t *val);
int (*mixer_write)(uint32_t knob_id, uint32_t val);
uint32_t id;
} snd_device_t;
/*
* Register a device to be used with snd
*/
int snd_register(snd_device_t * device);
/*
* Unregister a device
*/
int snd_unregister(snd_device_t * device);
/*
* Request a buffer to play from snd. This is to be called from the device in
* order to fill a buffer on demand. After the call the buffer is garaunteed
* to be filled to the size requested even if that means writing zeroes for
* when there are no other samples.
*/
int snd_request_buf(snd_device_t * device, uint32_t size, uint8_t *buffer);
#endif /* KERNEL_MOD_SND_H */

View File

@ -0,0 +1,36 @@
#pragma once
#include <stdint.h>
#define SND_MAX_KNOBS 256
#define SND_KNOB_NAME_SIZE 256
#define SND_KNOB_MAX_VALUE UINT32_MAX
#define SND_KNOB_MASTER 0
#define SND_DEVICE_MAIN 0
typedef struct snd_knob_list {
uint32_t device; /* IN */
uint32_t num; /* OUT */
uint32_t ids[SND_MAX_KNOBS]; /* OUT */
} snd_knob_list_t;
typedef struct snd_knob_info {
uint32_t device; /* IN */
uint32_t id; /* IN */
char name[SND_KNOB_NAME_SIZE]; /* OUT */
} snd_knob_info_t;
typedef struct snd_knob_value {
uint32_t device; /* IN */
uint32_t id; /* IN */
uint32_t val; /* OUT for SND_MIXER_READ_KNOB, IN for SND_MIXER_WRITE_KNOB */
} snd_knob_value_t;
/* IOCTLs */
#define SND_MIXER_GET_KNOBS 0
#define SND_MIXER_GET_KNOB_INFO 1
#define SND_MIXER_READ_KNOB 2
#define SND_MIXER_WRITE_KNOB 3

View File

@ -0,0 +1,38 @@
#ifndef _TMPFS_H__
#define _TMPFS_H__
#include <fs.h>
fs_node_t * tmpfs_create(char * name);
struct tmpfs_file {
char * name;
int type;
int mask;
int uid;
int gid;
unsigned int atime;
unsigned int mtime;
unsigned int ctime;
size_t length;
size_t block_count;
size_t pointers;
char ** blocks;
char * target;
};
struct tmpfs_dir;
struct tmpfs_dir {
char * name;
int type;
int mask;
int uid;
int gid;
unsigned int atime;
unsigned int mtime;
unsigned int ctime;
list_t * files;
struct tmpfs_dir * parent;
};
#endif /* _TMPFS_H__ */

41
kernel/include/module.h Normal file
View File

@ -0,0 +1,41 @@
#pragma once
#include <types.h>
#include <hashmap.h>
typedef struct {
char * name;
int (* initialize)(void);
int (* finalize)(void);
} module_defs;
typedef struct {
module_defs * mod_info;
void * bin_data;
hashmap_t * symbols;
uintptr_t end;
size_t deps_length;
char * deps;
} module_data_t;
void (* symbol_find(const char * name))(void);
extern int module_quickcheck(void * blob);
extern void * module_load_direct(void * blob, size_t size);
extern void * module_load(char * filename);
extern void module_unload(char * name);
extern void modules_install(void);
#define MODULE_DEF(n,init,fini) \
module_defs module_info_ ## n = { \
.name = #n, \
.initialize = &init, \
.finalize = &fini \
}
extern hashmap_t * modules_get_list(void);
extern hashmap_t * modules_get_symbols(void);
#define MODULE_DEPENDS(n) \
static char _mod_dependency_ ## n [] __attribute__((section("moddeps"), used)) = #n

20
kernel/include/mouse.h Normal file
View File

@ -0,0 +1,20 @@
#pragma once
typedef enum {
LEFT_CLICK = 0x01,
RIGHT_CLICK = 0x02,
MIDDLE_CLICK = 0x04,
MOUSE_SCROLL_UP = 0x10,
MOUSE_SCROLL_DOWN = 0x20,
} mouse_click_t;
typedef struct {
uint32_t magic;
int32_t x_difference;
int32_t y_difference;
mouse_click_t buttons;
} mouse_device_packet_t;
#define MOUSE_MAGIC 0xFEED1234

View File

@ -0,0 +1,92 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
*/
#pragma once
#include <system.h>
#define MULTIBOOT_MAGIC 0x1BADB002
#define MULTIBOOT_EAX_MAGIC 0x2BADB002
#define MULTIBOOT_FLAG_MEM 0x001
#define MULTIBOOT_FLAG_DEVICE 0x002
#define MULTIBOOT_FLAG_CMDLINE 0x004
#define MULTIBOOT_FLAG_MODS 0x008
#define MULTIBOOT_FLAG_AOUT 0x010
#define MULTIBOOT_FLAG_ELF 0x020
#define MULTIBOOT_FLAG_MMAP 0x040
#define MULTIBOOT_FLAG_CONFIG 0x080
#define MULTIBOOT_FLAG_LOADER 0x100
#define MULTIBOOT_FLAG_APM 0x200
#define MULTIBOOT_FLAG_VBE 0x400
struct multiboot
{
uintptr_t flags;
uintptr_t mem_lower;
uintptr_t mem_upper;
uintptr_t boot_device;
uintptr_t cmdline;
uintptr_t mods_count;
uintptr_t mods_addr;
uintptr_t num;
uintptr_t size;
uintptr_t addr;
uintptr_t shndx;
uintptr_t mmap_length;
uintptr_t mmap_addr;
uintptr_t drives_length;
uintptr_t drives_addr;
uintptr_t config_table;
uintptr_t boot_loader_name;
uintptr_t apm_table;
uintptr_t vbe_control_info;
uintptr_t vbe_mode_info;
uintptr_t vbe_mode;
uintptr_t vbe_interface_seg;
uintptr_t vbe_interface_off;
uintptr_t vbe_interface_len;
} __attribute__ ((packed));
typedef struct {
uint16_t attributes;
uint8_t winA, winB;
uint16_t granularity;
uint16_t winsize;
uint16_t segmentA, segmentB;
uint32_t realFctPtr;
uint16_t pitch;
uint16_t Xres, Yres;
uint8_t Wchar, Ychar, planes, bpp, banks;
uint8_t memory_model, bank_size, image_pages;
uint8_t reserved0;
uint8_t red_mask, red_position;
uint8_t green_mask, green_position;
uint8_t blue_mask, blue_position;
uint8_t rsv_mask, rsv_position;
uint8_t directcolor_attributes;
uint32_t physbase;
uint32_t reserved1;
uint16_t reserved2;
} __attribute__ ((packed)) vbe_info_t;
typedef struct {
uintptr_t mod_start;
uintptr_t mod_end;
uintptr_t cmdline;
uintptr_t reserved;
} __attribute__ ((packed)) mboot_mod_t;
typedef struct {
uint32_t size;
uint64_t base_addr;
uint64_t length;
uint32_t type;
} __attribute__ ((packed)) mboot_memmap_t;
extern struct multiboot *copy_multiboot(struct multiboot *mboot_ptr);
extern void dump_multiboot(struct multiboot *mboot_ptr);
extern char * ramdisk;
extern struct multiboot * mboot_ptr;

69
kernel/include/pci.h Normal file
View File

@ -0,0 +1,69 @@
#pragma once
#define PCI_VENDOR_ID 0x00 // 2
#define PCI_DEVICE_ID 0x02 // 2
#define PCI_COMMAND 0x04 // 2
#define PCI_STATUS 0x06 // 2
#define PCI_REVISION_ID 0x08 // 1
#define PCI_PROG_IF 0x09 // 1
#define PCI_SUBCLASS 0x0a // 1
#define PCI_CLASS 0x0b // 1
#define PCI_CACHE_LINE_SIZE 0x0c // 1
#define PCI_LATENCY_TIMER 0x0d // 1
#define PCI_HEADER_TYPE 0x0e // 1
#define PCI_BIST 0x0f // 1
#define PCI_BAR0 0x10 // 4
#define PCI_BAR1 0x14 // 4
#define PCI_BAR2 0x18 // 4
#define PCI_BAR3 0x1C // 4
#define PCI_BAR4 0x20 // 4
#define PCI_BAR5 0x24 // 4
#define PCI_INTERRUPT_LINE 0x3C // 1
#define PCI_SECONDARY_BUS 0x19 // 1
#define PCI_HEADER_TYPE_DEVICE 0
#define PCI_HEADER_TYPE_BRIDGE 1
#define PCI_HEADER_TYPE_CARDBUS 2
#define PCI_TYPE_BRIDGE 0x0604
#define PCI_TYPE_SATA 0x0106
#define PCI_ADDRESS_PORT 0xCF8
#define PCI_VALUE_PORT 0xCFC
#define PCI_NONE 0xFFFF
typedef void (*pci_func_t)(uint32_t device, uint16_t vendor_id, uint16_t device_id, void * extra);
static inline int pci_extract_bus(uint32_t device) {
return (uint8_t)((device >> 16));
}
static inline int pci_extract_slot(uint32_t device) {
return (uint8_t)((device >> 8));
}
static inline int pci_extract_func(uint32_t device) {
return (uint8_t)(device);
}
static inline uint32_t pci_get_addr(uint32_t device, int field) {
return 0x80000000 | (pci_extract_bus(device) << 16) | (pci_extract_slot(device) << 11) | (pci_extract_func(device) << 8) | ((field) & 0xFC);
}
static inline uint32_t pci_box_device(int bus, int slot, int func) {
return (uint32_t)((bus << 16) | (slot << 8) | func);
}
uint32_t pci_read_field(uint32_t device, int field, int size);
void pci_write_field(uint32_t device, int field, int size, uint32_t value);
uint16_t pci_find_type(uint32_t dev);
const char * pci_vendor_lookup(unsigned short vendor_id);
const char * pci_device_lookup(unsigned short vendor_id, unsigned short device_id);
void pci_scan_hit(pci_func_t f, uint32_t dev, void * extra);
void pci_scan_func(pci_func_t f, int type, int bus, int slot, int func, void * extra);
void pci_scan_slot(pci_func_t f, int type, int bus, int slot, void * extra);
void pci_scan_bus(pci_func_t f, int type, int bus, void * extra);
void pci_scan(pci_func_t f, int type, void * extra);

7057
kernel/include/pci_list.h Normal file

File diff suppressed because it is too large Load Diff

27
kernel/include/pipe.h Normal file
View File

@ -0,0 +1,27 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
*
* Pipe
*/
#pragma once
#include <types.h>
typedef struct _pipe_device {
uint8_t * buffer;
size_t write_ptr;
size_t read_ptr;
size_t size;
size_t refcount;
volatile int lock_read[2];
volatile int lock_write[2];
list_t * wait_queue_readers;
list_t * wait_queue_writers;
int dead;
list_t * alert_waiters;
} pipe_device_t;
fs_node_t * make_pipe(size_t size);
int pipe_size(fs_node_t * node);
int pipe_unsize(fs_node_t * node);

8
kernel/include/printf.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include <fs.h>
extern size_t vasprintf(char * buf, const char *fmt, va_list args);
extern int sprintf(char *buf, const char *fmt, ...);
extern int fprintf(fs_node_t * device, char *fmt, ...);

156
kernel/include/process.h Normal file
View File

@ -0,0 +1,156 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
*/
#pragma once
//#include <system.h>
#include <tree.h>
#include <signal.h>
#include <task.h>
#define KERNEL_STACK_SIZE 0x8000
typedef signed int pid_t;
typedef unsigned int user_t;
typedef unsigned char status_t;
#define USER_ROOT_UID (user_t)0
/* Unix waitpid() options */
enum wait_option{
WCONTINUED,
WNOHANG,
WUNTRACED
};
/* x86 task */
typedef struct thread {
uintptr_t esp; /* Stack Pointer */
uintptr_t ebp; /* Base Pointer */
uintptr_t eip; /* Instruction Pointer */
uint8_t fpu_enabled;
uint8_t fp_regs[512];
uint8_t padding[32]; /* I don't know */
page_directory_t * page_directory; /* Page Directory */
} thread_t;
/* Portable image struct */
typedef struct image {
size_t size; /* Image size */
uintptr_t entry; /* Binary entry point */
uintptr_t heap; /* Heap pointer */
uintptr_t heap_actual; /* Actual heap location */
uintptr_t stack; /* Process kernel stack */
uintptr_t user_stack; /* User stack */
uintptr_t start;
uintptr_t shm_heap;
volatile int lock[2];
} image_t;
/* Resizable descriptor table */
typedef struct descriptor_table {
fs_node_t ** entries;
size_t length;
size_t capacity;
size_t refs;
} fd_table_t;
/* XXX */
#define SIG_COUNT 10
/* Signal Table */
typedef struct signal_table {
uintptr_t functions[NUMSIGNALS+1];
} sig_table_t;
/* Portable process struct */
typedef struct process {
pid_t id; /* Process ID (pid) */
char * name; /* Process Name */
char * description; /* Process description */
user_t user; /* Effective user */
int mask; /* Umask */
char ** cmdline;
pid_t group; /* Process thread group */
pid_t job; /* Process job group */
pid_t session; /* Session group */
thread_t thread; /* Associated task information */
tree_node_t * tree_entry; /* Process Tree Entry */
image_t image; /* Binary image information */
fs_node_t * wd_node; /* Working directory VFS node */
char * wd_name; /* Working directory path name */
fd_table_t * fds; /* File descriptor table */
status_t status; /* Process status */
sig_table_t signals; /* Signal table */
uint8_t finished; /* Status indicator */
uint8_t started;
uint8_t running;
struct regs * syscall_registers; /* Registers at interrupt */
list_t * wait_queue;
list_t * shm_mappings; /* Shared memory chunk mappings */
list_t * signal_queue; /* Queued signals */
thread_t signal_state;
char * signal_kstack;
node_t sched_node;
node_t sleep_node;
node_t * timed_sleep_node;
uint8_t is_tasklet;
volatile uint8_t sleep_interrupted;
list_t * node_waits;
int awoken_index;
node_t * timeout_node;
struct timeval start;
} process_t;
typedef struct {
unsigned long end_tick;
unsigned long end_subtick;
process_t * process;
int is_fswait;
} sleeper_t;
extern void initialize_process_tree(void);
extern process_t * spawn_process(volatile process_t * parent, int reuse_fds);
extern void debug_print_process_tree(void);
extern process_t * spawn_init(void);
extern process_t * spawn_kidle(void);
extern void set_process_environment(process_t * proc, page_directory_t * directory);
extern void make_process_ready(process_t * proc);
extern uint8_t process_available(void);
extern process_t * next_ready_process(void);
extern uint32_t process_append_fd(process_t * proc, fs_node_t * node);
extern process_t * process_from_pid(pid_t pid);
extern void delete_process(process_t * proc);
process_t * process_get_parent(process_t * process);
extern uint32_t process_move_fd(process_t * proc, int src, int dest);
extern int process_is_ready(process_t * proc);
extern void wakeup_sleepers(unsigned long seconds, unsigned long subseconds);
extern void sleep_until(process_t * process, unsigned long seconds, unsigned long subseconds);
extern volatile process_t * current_process;
extern process_t * kernel_idle_task;
extern list_t * process_list;
extern int process_wait_nodes(process_t * process,fs_node_t * nodes[], int timeout);
extern int process_alert_node(process_t * process, void * value);
extern int process_awaken_from_fswait(process_t * process, int index);
typedef void (*tasklet_t) (void *, char *);
extern int create_kernel_tasklet(tasklet_t tasklet, char * name, void * argp);
extern void release_directory(page_directory_t * dir);
extern void release_directory_for_exec(page_directory_t * dir);
extern void cleanup_process(process_t * proc, int retval);
extern void reap_process(process_t * proc);
extern int waitpid(int pid, int * status, int options);
extern int is_valid_process(process_t * process);

View File

@ -0,0 +1,26 @@
#pragma once
typedef struct {
unsigned char * buffer;
size_t write_ptr;
size_t read_ptr;
size_t size;
volatile int lock[2];
list_t * wait_queue_readers;
list_t * wait_queue_writers;
int internal_stop;
list_t * alert_waiters;
int discard;
} ring_buffer_t;
size_t ring_buffer_unread(ring_buffer_t * ring_buffer);
size_t ring_buffer_size(fs_node_t * node);
size_t ring_buffer_available(ring_buffer_t * ring_buffer);
size_t ring_buffer_read(ring_buffer_t * ring_buffer, size_t size, uint8_t * buffer);
size_t ring_buffer_write(ring_buffer_t * ring_buffer, size_t size, uint8_t * buffer);
ring_buffer_t * ring_buffer_create(size_t size);
void ring_buffer_destroy(ring_buffer_t * ring_buffer);
void ring_buffer_interrupt(ring_buffer_t * ring_buffer);
void ring_buffer_select_wait(ring_buffer_t * ring_buffer, void * process);

42
kernel/include/shm.h Normal file
View File

@ -0,0 +1,42 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
*/
#pragma once
#include <system.h>
#include <types.h>
#define SHM_PATH_SEPARATOR "."
/* Types */
struct shm_node;
typedef struct {
struct shm_node * parent;
volatile uint8_t lock;
int32_t ref_count;
uint32_t num_frames;
uintptr_t *frames;
} shm_chunk_t;
typedef struct shm_node {
char name[256];
shm_chunk_t * chunk;
} shm_node_t;
typedef struct {
shm_chunk_t * chunk;
uint8_t volatile lock;
uint32_t num_vaddrs;
uintptr_t *vaddrs;
} shm_mapping_t;
/* Syscalls */
extern void * shm_obtain(char * path, size_t * size);
extern int shm_release(char * path);
/* Other exposed functions */
extern void shm_install(void);
extern void shm_release_all(process_t * proc);

11
kernel/include/signal.h Normal file
View File

@ -0,0 +1,11 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
*/
#pragma once
#include <types.h>
void return_from_signal_handler(void);
void fix_signal_stacks(void);
#include <signal_defs.h>

View File

@ -0,0 +1 @@
../../include/sys/signal_defs.h

View File

@ -0,0 +1 @@
../../include/syscall_nums.h

234
kernel/include/system.h Normal file
View File

@ -0,0 +1,234 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
*/
#pragma once
#include <types.h>
#include <fs.h>
#include <va_list.h>
#include <list.h>
#include <task.h>
#include <process.h>
#include <libc.h>
#define ASSUME(cond) __extension__ ({ if (!(cond)) { __builtin_unreachable(); } })
#define STR(x) #x
#define STRSTR(x) STR(x)
#define asm __asm__
#define volatile __volatile__
void int_disable(void);
void int_resume(void);
void int_enable(void);
#define IRQ_OFF int_disable()
#define IRQ_RES int_resume()
#define IRQ_ON int_enable()
#define PAUSE { asm volatile ("hlt"); }
#define STOP while (1) { PAUSE; }
#define SYSCALL_VECTOR 0x7F
#define SIGNAL_RETURN 0xFFFFDEAF
#define THREAD_RETURN 0xFFFFB00F
extern void * code;
extern void * end;
extern char * boot_arg; /* Argument to pass to init */
extern char * boot_arg_extra; /* Extra data to pass to init */
extern void *sbrk(uintptr_t increment);
/* spin.c */
typedef volatile int spin_lock_t[2];
extern void spin_init(spin_lock_t lock);
extern void spin_lock(spin_lock_t lock);
extern void spin_unlock(spin_lock_t lock);
extern void return_to_userspace(void);
/* Kernel Main */
extern unsigned short *memsetw(unsigned short *dest, unsigned short val, int count);
extern unsigned char inportb(unsigned short _port);
extern void outportb(unsigned short _port, unsigned char _data);
extern unsigned short inports(unsigned short _port);
extern void outports(unsigned short _port, unsigned short _data);
extern unsigned int inportl(unsigned short _port);
extern void outportl(unsigned short _port, unsigned int _data);
extern void outportsm(unsigned short port, unsigned char * data, unsigned long size);
extern void inportsm(unsigned short port, unsigned char * data, unsigned long size);
extern size_t lfind(const char * str, const char accept);
extern size_t rfind(const char * str, const char accept);
extern uint32_t krand(void);
extern uint8_t startswith(const char * str, const char * accept);
/* GDT */
extern void gdt_install(void);
extern void gdt_set_gate(uint8_t num, uint64_t base, uint64_t limit, uint8_t access, uint8_t gran);
extern void set_kernel_stack(uintptr_t stack);
/* IDT */
extern void idt_install(void);
extern void idt_set_gate(uint8_t num, void (*base)(void), uint16_t sel, uint8_t flags);
/* Registers
*
* Note: if the order of these changes, sys/task.S must be changed to use
* the correct offsets as well.
*/
struct regs {
unsigned int gs, fs, es, ds;
unsigned int edi, esi, ebp, esp, ebx, edx, ecx, eax;
unsigned int int_no, err_code;
unsigned int eip, cs, eflags, useresp, ss;
};
typedef struct regs regs_t;
typedef void (*irq_handler_t) (struct regs *);
typedef int (*irq_handler_chain_t) (struct regs *);
/* Panic */
#define HALT_AND_CATCH_FIRE(mesg, regs) halt_and_catch_fire(mesg, __FILE__, __LINE__, regs)
#define assert(statement) ((statement) ? (void)0 : assert_failed(__FILE__, __LINE__, #statement))
void halt_and_catch_fire(char *error_message, const char *file, int line, struct regs * regs);
void assert_failed(const char *file, uint32_t line, const char *desc);
/* ISRS */
extern void isrs_install(void);
extern void isrs_install_handler(size_t isrs, irq_handler_t);
extern void isrs_uninstall_handler(size_t isrs);
/* Interrupt Handlers */
extern void irq_install(void);
extern void irq_install_handler(size_t irq, irq_handler_chain_t);
extern void irq_uninstall_handler(size_t irq);
extern int irq_is_handler_free(size_t irq);
extern void irq_gates(void);
extern void irq_ack(size_t);
/* Timer */
extern void timer_install(void);
extern unsigned long timer_ticks;
extern unsigned long timer_subticks;
extern signed long timer_drift;
extern void relative_time(unsigned long seconds, unsigned long subseconds, unsigned long * out_seconds, unsigned long * out_subseconds);
/* Memory Management */
extern uintptr_t placement_pointer;
extern void kmalloc_startat(uintptr_t address);
extern uintptr_t kmalloc_real(size_t size, int align, uintptr_t * phys);
extern uintptr_t kmalloc(size_t size);
extern uintptr_t kvmalloc(size_t size);
extern uintptr_t kmalloc_p(size_t size, uintptr_t * phys);
extern uintptr_t kvmalloc_p(size_t size, uintptr_t * phys);
// Page types moved to task.h
extern page_directory_t *kernel_directory;
extern page_directory_t *current_directory;
extern void paging_install(uint32_t memsize);
extern void paging_prestart(void);
extern void paging_finalize(void);
extern void paging_mark_system(uint64_t addr);
extern void switch_page_directory(page_directory_t * new);
extern void invalidate_page_tables(void);
extern void invalidate_tables_at(uintptr_t addr);
extern page_t *get_page(uintptr_t address, int make, page_directory_t * dir);
extern void page_fault(struct regs *r);
extern void dma_frame(page_t * page, int, int, uintptr_t);
extern void debug_print_directory(page_directory_t *);
int debug_shell_start(void);
void heap_install(void);
void alloc_frame(page_t *page, int is_kernel, int is_writeable);
void free_frame(page_t *page);
uintptr_t memory_use(void);
uintptr_t memory_total(void);
/* klmalloc */
void * __attribute__ ((malloc)) malloc(size_t size);
void * __attribute__ ((malloc)) realloc(void *ptr, size_t size);
void * __attribute__ ((malloc)) calloc(size_t nmemb, size_t size);
void * __attribute__ ((malloc)) valloc(size_t size);
void free(void *ptr);
/* Tasks */
extern uintptr_t read_eip(void);
extern void copy_page_physical(uint32_t, uint32_t);
extern page_directory_t * clone_directory(page_directory_t * src);
extern page_table_t * clone_table(page_table_t * src, uintptr_t * physAddr);
extern void move_stack(void *new_stack_start, size_t size);
extern void kexit(int retval);
extern void task_exit(int retval);
extern uint32_t next_pid;
extern void tasking_install(void);
extern void switch_task(uint8_t reschedule);
extern void switch_next(void);
extern uint32_t fork(void);
extern uint32_t clone(uintptr_t new_stack, uintptr_t thread_func, uintptr_t arg);
extern uint32_t getpid(void);
extern void enter_user_jmp(uintptr_t location, int argc, char ** argv, uintptr_t stack);
extern uintptr_t initial_esp;
/* Kernel Argument Parser */
extern void parse_args(char * argv);
/* CMOS */
extern void get_time(uint16_t * hours, uint16_t * minutes, uint16_t * seconds);
extern void get_date(uint16_t * month, uint16_t * day);
extern uint32_t boot_time;
extern uint32_t read_cmos(void);
extern int gettimeofday(struct timeval * t, void * z);
extern uint32_t now(void);
/* Floating Point Unit */
extern void switch_fpu(void);
extern void unswitch_fpu(void);
extern void fpu_install(void);
/* ELF */
extern int exec( char *, int, char **, char **);
extern int system( char *, int, char **);
/* Sytem Calls */
extern void syscalls_install(void);
/* wakeup queue */
extern int wakeup_queue(list_t * queue);
extern int wakeup_queue_interrupted(list_t * queue);
extern int sleep_on(list_t * queue);
typedef struct {
uint32_t signum;
uintptr_t handler;
regs_t registers_before;
} signal_t;
extern void handle_signal(process_t *, signal_t *);
extern int send_signal(pid_t process, uint32_t signal);
#define USER_STACK_BOTTOM 0xAFF00000
#define USER_STACK_TOP 0xB0000000
#define SHM_START 0xB0000000
extern void validate(void * ptr);
extern int validate_safe(void * ptr);
#include <errno_defs.h>

25
kernel/include/task.h Normal file
View File

@ -0,0 +1,25 @@
#pragma once
#include <types.h>
typedef struct page {
unsigned int present:1;
unsigned int rw:1;
unsigned int user:1;
unsigned int writethrough:1;
unsigned int cachedisable:1;
unsigned int unused:7;
unsigned int frame:20;
} __attribute__((packed)) page_t;
typedef struct page_table {
page_t pages[1024];
} page_table_t;
typedef struct page_directory {
uintptr_t physical_tables[1024]; /* Physical addresses of the tables */
page_table_t *tables[1024]; /* 1024 pointers to page tables... */
uintptr_t physical_address; /* The physical address of physical_tables */
int32_t ref_count;
} page_directory_t;

1
kernel/include/termemu.h Symbolic link
View File

@ -0,0 +1 @@
../../lib/termemu.h

1
kernel/include/termios.h Symbolic link
View File

@ -0,0 +1 @@
../../include/sys/termios.h

View File

@ -0,0 +1,4 @@
#pragma once
int tokenize(char *, char *, char **);

37
kernel/include/tree.h Normal file
View File

@ -0,0 +1,37 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
*
* General-purpose tree implementation
*/
#pragma once
#include "list.h"
typedef struct tree_node {
void * value;
list_t * children;
struct tree_node * parent;
} tree_node_t;
typedef struct {
size_t nodes;
tree_node_t * root;
} tree_t;
typedef uint8_t (*tree_comparator_t) (void *, void *);
tree_t * tree_create(void);
void tree_set_root(tree_t * tree, void * value);
void tree_node_destroy(tree_node_t * node);
void tree_destroy(tree_t * tree);
void tree_free(tree_t * tree);
tree_node_t * tree_node_create(void * value);
void tree_node_insert_child_node(tree_t * tree, tree_node_t * parent, tree_node_t * node);
tree_node_t * tree_node_insert_child(tree_t * tree, tree_node_t * parent, void * value);
tree_node_t * tree_node_find_parent(tree_node_t * haystack, tree_node_t * needle);
void tree_node_parent_remove(tree_t * tree, tree_node_t * parent, tree_node_t * node);
void tree_node_remove(tree_t * tree, tree_node_t * node);
void tree_remove(tree_t * tree, tree_node_t * node);
tree_node_t * tree_find(tree_t * tree, void * value, tree_comparator_t comparator);
void tree_break_off(tree_t * tree, tree_node_t * node);

37
kernel/include/tss.h Normal file
View File

@ -0,0 +1,37 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
*/
#pragma once
extern void tss_flush(void);
typedef struct tss_entry {
uint32_t prev_tss;
uint32_t esp0;
uint32_t ss0;
uint32_t esp1;
uint32_t ss1;
uint32_t esp2;
uint32_t ss2;
uint32_t cr3;
uint32_t eip;
uint32_t eflags;
uint32_t eax;
uint32_t ecx;
uint32_t edx;
uint32_t ebx;
uint32_t esp;
uint32_t ebp;
uint32_t esi;
uint32_t edi;
uint32_t es;
uint32_t cs;
uint32_t ss;
uint32_t ds;
uint32_t fs;
uint32_t gs;
uint32_t ldt;
uint16_t trap;
uint16_t iomap_base;
} __attribute__ ((packed)) tss_entry_t;

18
kernel/include/types.h Normal file
View File

@ -0,0 +1,18 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
*/
#pragma once
/* Types */
#define NULL ((void *)0UL)
#include <stdint.h>
typedef unsigned long size_t;
#define CHAR_BIT 8
struct timeval {
uint32_t tv_sec;
uint32_t tv_usec;
};

68
kernel/include/ubsan.h Normal file
View File

@ -0,0 +1,68 @@
#pragma once
#include <types.h>
struct TypeDescriptor {
uint16_t type_kind;
uint16_t type_info;
char type_name[1];
};
struct SourceLocation {
const char *file_name;
uint32_t line;
uint32_t column;
};
struct OverflowData {
struct SourceLocation location;
struct TypeDescriptor *type;
};
struct TypeMismatchData {
struct SourceLocation location;
struct TypeDescriptor *type;
unsigned long alignment;
unsigned char type_check_kind;
};
struct NonnullArgData {
struct SourceLocation location;
struct SourceLocation attr_location;
int arg_index;
};
struct NonnullReturnData {
struct SourceLocation location;
struct SourceLocation attr_location;
};
struct VLABoundData {
struct SourceLocation location;
struct TypeDescriptor *type;
};
struct OutOfBoundsData {
struct SourceLocation location;
struct TypeDescriptor *array_type;
struct TypeDescriptor *index_type;
};
struct ShiftOutOfBoundsData {
struct SourceLocation location;
struct TypeDescriptor *lhs_type;
struct TypeDescriptor *rhs_type;
};
struct UnreachableData {
struct SourceLocation location;
};
struct InvalidValueData {
struct SourceLocation location;
struct TypeDescriptor *type;
};
typedef int64_t s_max;
typedef uint64_t u_max;

1
kernel/include/utsname.h Symbolic link
View File

@ -0,0 +1 @@
../../include/sys/utsname.h

8
kernel/include/va_list.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
typedef __builtin_va_list va_list;
#define va_start(ap,last) __builtin_va_start(ap, last)
#define va_end(ap) __builtin_va_end(ap)
#define va_arg(ap,type) __builtin_va_arg(ap,type)
#define va_copy(dest, src) __builtin_va_copy(dest,src)

19
kernel/include/version.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
extern char * __kernel_name;
extern char * __kernel_version_format;
extern int __kernel_version_major;
extern int __kernel_version_minor;
extern int __kernel_version_lower;
extern char * __kernel_version_suffix;
extern char * __kernel_version_codename;
extern char * __kernel_arch;
extern char * __kernel_build_date;
extern char * __kernel_build_time;
extern char * __kernel_compiler_version;

18
kernel/include/video.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#define IO_VID_WIDTH 0x5001
#define IO_VID_HEIGHT 0x5002
#define IO_VID_DEPTH 0x5003
#define IO_VID_ADDR 0x5004
#define IO_VID_SIGNAL 0x5005
#define IO_VID_SET 0x5006
#define IO_VID_STRIDE 0x5007
#ifdef _KERNEL_
extern void lfb_set_resolution(uint16_t x, uint16_t y);
extern uint16_t lfb_resolution_x;
extern uint16_t lfb_resolution_y;
extern uint16_t lfb_resolution_b;
extern uint8_t * lfb_vid_memory;
#endif

67
kernel/irq.S Normal file
View File

@ -0,0 +1,67 @@
.section .text
.align 4
.macro IRQ ident byte
.global _irq\ident
.type _irq\ident, @function
_irq\ident:
cli
push $0x00
push $\byte
jmp irq_common
.endm
/* Interrupt Requests */
IRQ 0, 32
IRQ 1, 33
IRQ 2, 34
IRQ 3, 35
IRQ 4, 36
IRQ 5, 37
IRQ 6, 38
IRQ 7, 39
IRQ 8, 40
IRQ 9, 41
IRQ 10, 42
IRQ 11, 43
IRQ 12, 44
IRQ 13, 45
IRQ 14, 46
IRQ 15, 47
.extern irq_handler
.type irq_handler, @function
irq_common:
/* Save all registers */
pusha
/* Save segment registers */
push %ds
push %es
push %fs
push %gs
mov $0x10, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
cld
/* Call interrupt handler */
push %esp
call irq_handler
add $4, %esp
/* Restore segment registers */
pop %gs
pop %fs
pop %es
pop %ds
/* Restore all registers */
popa
/* Cleanup error code and IRQ # */
add $8, %esp
/* pop CS, EIP, EFLAGS, SS and ESP */
iret

91
kernel/isr.S Normal file
View File

@ -0,0 +1,91 @@
.section .text
.align 4
.macro ISR_NOERR index
.global _isr\index
_isr\index:
cli
push $0
push $\index
jmp isr_common
.endm
.macro ISR_ERR index
.global _isr\index
_isr\index:
cli
push $\index
jmp isr_common
.endm
/* Standard X86 interrupt service routines */
ISR_NOERR 0
ISR_NOERR 1
ISR_NOERR 2
ISR_NOERR 3
ISR_NOERR 4
ISR_NOERR 5
ISR_NOERR 6
ISR_NOERR 7
ISR_ERR 8
ISR_NOERR 9
ISR_ERR 10
ISR_ERR 11
ISR_ERR 12
ISR_ERR 13
ISR_ERR 14
ISR_NOERR 15
ISR_NOERR 16
ISR_NOERR 17
ISR_NOERR 18
ISR_NOERR 19
ISR_NOERR 20
ISR_NOERR 21
ISR_NOERR 22
ISR_NOERR 23
ISR_NOERR 24
ISR_NOERR 25
ISR_NOERR 26
ISR_NOERR 27
ISR_NOERR 28
ISR_NOERR 29
ISR_NOERR 30
ISR_NOERR 31
ISR_NOERR 127
.extern fault_handler
.type fault_handler, @function
isr_common:
/* Push all registers */
pusha
/* Save segment registers */
push %ds
push %es
push %fs
push %gs
mov $0x10, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
cld
/* Call fault handler */
push %esp
call fault_handler
add $4, %esp
/* Restore segment registers */
pop %gs
pop %fs
pop %es
pop %ds
/* Restore registers */
popa
/* Cleanup error code and ISR # */
add $8, %esp
/* pop CS, EIP, EFLAGS, SS and ESP */
iret

454
kernel/libc.c Normal file
View File

@ -0,0 +1,454 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2015 Dale Weiler
*
* Standard C library for kernel
*
*/
#include <system.h>
#include <limits.h>
#define ALIGN (sizeof(size_t))
#define ONES ((size_t)-1/UCHAR_MAX)
#define HIGHS (ONES * (UCHAR_MAX/2+1))
#define HASZERO(X) (((X)-ONES) & ~(X) & HIGHS)
#define BITOP(A, B, OP) \
((A)[(size_t)(B)/(8*sizeof *(A))] OP (size_t)1<<((size_t)(B)%(8*sizeof *(A))))
void * memcpy(void * restrict dest, const void * restrict src, size_t n) {
asm volatile("cld; rep movsb"
: "=c"((int){0})
: "D"(dest), "S"(src), "c"(n)
: "flags", "memory");
return dest;
}
void * memset(void * dest, int c, size_t n) {
asm volatile("cld; rep stosb"
: "=c"((int){0})
: "D"(dest), "a"(c), "c"(n)
: "flags", "memory");
return dest;
}
int memcmp(const void * vl, const void * vr, size_t n) {
const unsigned char *l = vl;
const unsigned char *r = vr;
for (; n && *l == *r; n--, l++, r++);
return n ? *l-*r : 0;
}
void * memchr(const void * src, int c, size_t n) {
const unsigned char * s = src;
c = (unsigned char)c;
for (; ((uintptr_t)s & (ALIGN - 1)) && n && *s != c; s++, n--);
if (n && *s != c) {
const size_t * w;
size_t k = ONES * c;
for (w = (const void *)s; n >= sizeof(size_t) && !HASZERO(*w^k); w++, n -= sizeof(size_t));
for (s = (const void *)w; n && *s != c; s++, n--);
}
return n ? (void *)s : 0;
}
void * memrchr(const void * m, int c, size_t n) {
const unsigned char * s = m;
c = (unsigned char)c;
while (n--) {
if (s[n] == c) {
return (void*)(s+n);
}
}
return 0;
}
void * memmove(void * dest, const void * src, size_t n) {
char * d = dest;
const char * s = src;
if (d==s) {
return d;
}
if (s+n <= d || d+n <= s) {
return memcpy(d, s, n);
}
if (d<s) {
if ((uintptr_t)s % sizeof(size_t) == (uintptr_t)d % sizeof(size_t)) {
while ((uintptr_t)d % sizeof(size_t)) {
if (!n--) {
return dest;
}
*d++ = *s++;
}
for (; n >= sizeof(size_t); n -= sizeof(size_t), d += sizeof(size_t), s += sizeof(size_t)) {
*(size_t *)d = *(size_t *)s;
}
}
for (; n; n--) {
*d++ = *s++;
}
} else {
if ((uintptr_t)s % sizeof(size_t) == (uintptr_t)d % sizeof(size_t)) {
while ((uintptr_t)(d+n) % sizeof(size_t)) {
if (!n--) {
return dest;
}
d[n] = s[n];
}
while (n >= sizeof(size_t)) {
n -= sizeof(size_t);
*(size_t *)(d+n) = *(size_t *)(s+n);
}
}
while (n) {
n--;
d[n] = s[n];
}
}
return dest;
}
int strcmp(const char * l, const char * r) {
for (; *l == *r && *l; l++, r++);
return *(unsigned char *)l - *(unsigned char *)r;
}
size_t strlen(const char * s) {
const char * a = s;
const size_t * w;
for (; (uintptr_t)s % ALIGN; s++) {
if (!*s) {
return s-a;
}
}
for (w = (const void *)s; !HASZERO(*w); w++);
for (s = (const void *)w; *s; s++);
return s-a;
}
char * strdup(const char * s) {
size_t l = strlen(s);
return memcpy(malloc(l+1), s, l+1);
}
char * stpcpy(char * restrict d, const char * restrict s) {
size_t * wd;
const size_t * ws;
if ((uintptr_t)s % ALIGN == (uintptr_t)d % ALIGN) {
for (; (uintptr_t)s % ALIGN; s++, d++) {
if (!(*d = *s)) {
return d;
}
}
wd = (void *)d;
ws = (const void *)s;
for (; !HASZERO(*ws); *wd++ = *ws++);
d = (void *)wd;
s = (const void *)ws;
}
for (; (*d=*s); s++, d++);
return d;
}
char * strcpy(char * restrict dest, const char * restrict src) {
stpcpy(dest, src);
return dest;
}
size_t strspn(const char * s, const char * c) {
const char * a = s;
size_t byteset[32/sizeof(size_t)] = { 0 };
if (!c[0]) {
return 0;
}
if (!c[1]) {
for (; *s == *c; s++);
return s-a;
}
for (; *c && BITOP(byteset, *(unsigned char *)c, |=); c++);
for (; *s && BITOP(byteset, *(unsigned char *)s, &); s++);
return s-a;
}
char * strchrnul(const char * s, int c) {
size_t * w;
size_t k;
c = (unsigned char)c;
if (!c) {
return (char *)s + strlen(s);
}
for (; (uintptr_t)s % ALIGN; s++) {
if (!*s || *(unsigned char *)s == c) {
return (char *)s;
}
}
k = ONES * c;
for (w = (void *)s; !HASZERO(*w) && !HASZERO(*w^k); w++);
for (s = (void *)w; *s && *(unsigned char *)s != c; s++);
return (char *)s;
}
char * strchr(const char * s, int c) {
char *r = strchrnul(s, c);
return *(unsigned char *)r == (unsigned char)c ? r : 0;
}
char * strrchr(const char * s, int c) {
return memrchr(s, c, strlen(s) + 1);
}
size_t strcspn(const char * s, const char * c) {
const char *a = s;
if (c[0] && c[1]) {
size_t byteset[32/sizeof(size_t)] = { 0 };
for (; *c && BITOP(byteset, *(unsigned char *)c, |=); c++);
for (; *s && !BITOP(byteset, *(unsigned char *)s, &); s++);
return s-a;
}
return strchrnul(s, *c)-a;
}
char * strpbrk(const char * s, const char * b) {
s += strcspn(s, b);
return *s ? (char *)s : 0;
}
static char *strstr_2b(const unsigned char * h, const unsigned char * n) {
uint16_t nw = n[0] << 8 | n[1];
uint16_t hw = h[0] << 8 | h[1];
for (h++; *h && hw != nw; hw = hw << 8 | *++h);
return *h ? (char *)h-1 : 0;
}
static char *strstr_3b(const unsigned char * h, const unsigned char * n) {
uint32_t nw = n[0] << 24 | n[1] << 16 | n[2] << 8;
uint32_t hw = h[0] << 24 | h[1] << 16 | h[2] << 8;
for (h += 2; *h && hw != nw; hw = (hw|*++h) << 8);
return *h ? (char *)h-2 : 0;
}
static char *strstr_4b(const unsigned char * h, const unsigned char * n) {
uint32_t nw = n[0] << 24 | n[1] << 16 | n[2] << 8 | n[3];
uint32_t hw = h[0] << 24 | h[1] << 16 | h[2] << 8 | h[3];
for (h += 3; *h && hw != nw; hw = hw << 8 | *++h);
return *h ? (char *)h-3 : 0;
}
static char *strstr_twoway(const unsigned char * h, const unsigned char * n) {
size_t mem;
size_t mem0;
size_t byteset[32 / sizeof(size_t)] = { 0 };
size_t shift[256];
size_t l;
/* Computing length of needle and fill shift table */
for (l = 0; n[l] && h[l]; l++) {
BITOP(byteset, n[l], |=);
shift[n[l]] = l+1;
}
if (n[l]) {
return 0; /* hit the end of h */
}
/* Compute maximal suffix */
size_t ip = -1;
size_t jp = 0;
size_t k = 1;
size_t p = 1;
while (jp+k<l) {
if (n[ip+k] == n[jp+k]) {
if (k == p) {
jp += p;
k = 1;
} else {
k++;
}
} else if (n[ip+k] > n[jp+k]) {
jp += k;
k = 1;
p = jp - ip;
} else {
ip = jp++;
k = p = 1;
}
}
size_t ms = ip;
size_t p0 = p;
/* And with the opposite comparison */
ip = -1;
jp = 0;
k = p = 1;
while (jp+k<l) {
if (n[ip+k] == n[jp+k]) {
if (k == p) {
jp += p;
k = 1;
} else {
k++;
}
} else if (n[ip+k] < n[jp+k]) {
jp += k;
k = 1;
p = jp - ip;
} else {
ip = jp++;
k = p = 1;
}
}
if (ip+1 > ms+1) {
ms = ip;
} else {
p = p0;
}
/* Periodic needle? */
if (memcmp(n, n+p, ms+1)) {
mem0 = 0;
p = MAX(ms, l-ms-1) + 1;
} else {
mem0 = l-p;
}
mem = 0;
/* Initialize incremental end-of-haystack pointer */
const unsigned char * z = h;
/* Search loop */
for (;;) {
/* Update incremental end-of-haystack pointer */
if ((size_t)(z-h) < l) {
/* Fast estimate for MIN(l,63) */
size_t grow = l | 63;
const unsigned char *z2 = memchr(z, 0, grow);
if (z2) {
z = z2;
if ((size_t)(z-h) < l) {
return 0;
}
} else {
z += grow;
}
}
/* Check last byte first; advance by shift on mismatch */
if (BITOP(byteset, h[l-1], &)) {
k = l-shift[h[l-1]];
if (k) {
if (mem0 && mem && k < p) k = l-p;
h += k;
mem = 0;
continue;
}
} else {
h += l;
mem = 0;
continue;
}
/* Compare right half */
for (k=MAX(ms+1,mem); n[k] && n[k] == h[k]; k++);
if (n[k]) {
h += k-ms;
mem = 0;
continue;
}
/* Compare left half */
for (k=ms+1; k>mem && n[k-1] == h[k-1]; k--);
if (k <= mem) {
return (char *)h;
}
h += p;
mem = mem0;
}
}
char *strstr(const char * h, const char * n) {
/* Return immediately on empty needle */
if (!n[0]) {
return (char *)h;
}
/* Use faster algorithms for short needles */
h = strchr(h, *n);
if (!h || !n[1]) {
return (char *)h;
}
if (!h[1]) return 0;
if (!n[2]) return strstr_2b((void *)h, (void *)n);
if (!h[2]) return 0;
if (!n[3]) return strstr_3b((void *)h, (void *)n);
if (!h[3]) return 0;
if (!n[4]) return strstr_4b((void *)h, (void *)n);
/* Two-way on large needles */
return strstr_twoway((void *)h, (void *)n);
}
static inline int isdigit(int ch) {
return (unsigned int)ch-'0' < 10;
}
static inline int isspace(int ch) {
return ch == ' ' || (unsigned int)ch-'\t' < 5;
}
int atoi(const char * s) {
int n = 0;
int neg = 0;
while (isspace(*s)) {
s++;
}
switch (*s) {
case '-':
neg = 1;
/* Fallthrough is intentional here */
case '+':
s++;
}
while (isdigit(*s)) {
n = 10*n - (*s++ - '0');
}
/* The sign order may look incorrect here but this is correct as n is calculated
* as a negative number to avoid overflow on INT_MAX.
*/
return neg ? n : -n;
}
char * strtok_r(char * str, const char * delim, char ** saveptr) {
char * token;
if (str == NULL) {
str = *saveptr;
}
str += strspn(str, delim);
if (*str == '\0') {
*saveptr = str;
return NULL;
}
token = str;
str = strpbrk(token, delim);
if (str == NULL) {
*saveptr = (char *)lfind(token, '\0');
} else {
*str = '\0';
*saveptr = str + 1;
}
return token;
}

49
kernel/link.ld Normal file
View File

@ -0,0 +1,49 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* Kernel linker script for x86
*/
OUTPUT_FORMAT(elf32-i386)
ENTRY(start)
SECTIONS
{
. = 1M;
phys = .;
.text BLOCK(4K) : ALIGN(4K)
{
code = .;
*(.multiboot)
*(.text)
}
.rodata BLOCK(4K) : ALIGN(4K)
{
*(.rodata)
}
.data BLOCK(4K) : ALIGN(4K)
{
data = .;
*(.data)
*(.symbols)
PROVIDE(kernel_symbols_start = .);
PROVIDE(kernel_symbols_end = .);
}
.bss BLOCK(4K) : ALIGN(4K)
{
bss = .;
*(COMMON)
*(.bss)
*(.stack)
}
end = .;
/DISCARD/ :
{
*(.comment)
*(.eh_frame)
*(.note.gnu.build-id)
}
}

255
kernel/main.c Normal file
View File

@ -0,0 +1,255 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
*
* The ToAruOS kernel is released under the terms of the
* University of Illinois / NCSA License.
*
* Copyright (C) 2011-2014 Kevin Lange. All rights reserved.
* Copyright (C) 2012 Markus Schober
* Copyright (C) 2014 Lioncash
*
* Dedicated to the memory of
* Dennis Ritchie
* 1941-2011
*
* Developed by: ToAruOS Kernel Development Team
* http://github.com/klange/osdev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal with the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimers.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimers in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the ToAruOS Kernel Development Team, Kevin Lange,
* nor the names of its contributors may be used to endorse
* or promote products derived from this Software without specific prior
* written permission.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* WITH THE SOFTWARE.
*/
#include <system.h>
#include <boot.h>
#include <ext2.h>
#include <fs.h>
#include <logging.h>
#include <process.h>
#include <shm.h>
#include <args.h>
#include <module.h>
uintptr_t initial_esp = 0;
fs_node_t * ramdisk_mount(uintptr_t, size_t);
#ifdef EARLY_BOOT_LOG
#define EARLY_LOG_DEVICE 0x3F8
static uint32_t _early_log_write(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer) {
for (unsigned int i = 0; i < size; ++i) {
outportb(EARLY_LOG_DEVICE, buffer[i]);
}
return size;
}
fs_node_t _early_log = { .write = &_early_log_write };
#define ENABLE_EARLY_BOOT_LOG(level) do { debug_file = &_early_log; debug_level = (level); } while (0)
#define DISABLE_EARLY_BOOT_LOG() do { debug_file = NULL; debug_level = NOTICE; } while (0)
#else
#define ENABLE_EARLY_BOOT_LOG(level)
#define DISABLE_EARLY_BOOT_LOG()
#endif
struct pack_header {
char head[4];
uint32_t region_size;
};
/*
* multiboot i386 (pc) kernel entry point
*/
int kmain(struct multiboot *mboot, uint32_t mboot_mag, uintptr_t esp) {
initial_esp = esp;
extern char * cmdline;
uint32_t mboot_mods_count = 0;
mboot_mod_t * mboot_mods = NULL;
mboot_ptr = mboot;
ENABLE_EARLY_BOOT_LOG(0);
assert(mboot_mag == MULTIBOOT_EAX_MAGIC && "Didn't boot with multiboot, not sure how we got here.");
debug_print(NOTICE, "Processing Multiboot information.");
/* Initialize core modules */
gdt_install(); /* Global descriptor table */
idt_install(); /* IDT */
isrs_install(); /* Interrupt service requests */
irq_install(); /* Hardware interrupt requests */
if (mboot_ptr->flags & (1 << 3)) {
debug_print(NOTICE, "There %s %d module%s starting at 0x%x.", mboot_ptr->mods_count == 1 ? "is" : "are", mboot_ptr->mods_count, mboot_ptr->mods_count == 1 ? "" : "s", mboot_ptr->mods_addr);
debug_print(NOTICE, "Current kernel heap start point would be 0x%x.", &end);
if (mboot_ptr->mods_count > 0) {
uintptr_t last_mod = (uintptr_t)&end;
uint32_t i;
mboot_mods = (mboot_mod_t *)mboot_ptr->mods_addr;
mboot_mods_count = mboot_ptr->mods_count;
for (i = 0; i < mboot_ptr->mods_count; ++i ) {
mboot_mod_t * mod = &mboot_mods[i];
uint32_t module_start = mod->mod_start;
uint32_t module_end = mod->mod_end;
if ((uintptr_t)mod + sizeof(mboot_mod_t) > last_mod) {
/* Just in case some silly person put this *behind* the modules... */
last_mod = (uintptr_t)mod + sizeof(mboot_mod_t);
}
debug_print(NOTICE, "Module %d is at 0x%x:0x%x", i, module_start, module_end);
if (last_mod < module_end) {
last_mod = module_end;
}
}
debug_print(NOTICE, "Moving kernel heap start to 0x%x", last_mod);
kmalloc_startat(last_mod);
}
}
paging_install(mboot_ptr->mem_upper + mboot_ptr->mem_lower);
if (mboot_ptr->flags & (1 << 6)) {
debug_print(NOTICE, "Parsing memory map.");
mboot_memmap_t * mmap = (void *)mboot_ptr->mmap_addr;
while ((uintptr_t)mmap < mboot_ptr->mmap_addr + mboot_ptr->mmap_length) {
if (mmap->type == 2) {
for (unsigned long long int i = 0; i < mmap->length; i += 0x1000) {
if (mmap->base_addr + i > 0xFFFFFFFF) break; /* xxx */
debug_print(INFO, "Marking 0x%x", (uint32_t)(mmap->base_addr + i));
paging_mark_system((mmap->base_addr + i) & 0xFFFFF000);
}
}
mmap = (mboot_memmap_t *) ((uintptr_t)mmap + mmap->size + sizeof(uintptr_t));
}
}
paging_finalize();
{
char cmdline_[1024];
size_t len = strlen((char *)mboot_ptr->cmdline);
memmove(cmdline_, (char *)mboot_ptr->cmdline, len + 1);
/* Relocate the command line */
cmdline = (char *)kmalloc(len + 1);
memcpy(cmdline, cmdline_, len + 1);
}
/* Memory management */
heap_install(); /* Kernel heap */
if (cmdline) {
args_parse(cmdline);
}
vfs_install();
tasking_install(); /* Multi-tasking */
timer_install(); /* PIC driver */
fpu_install(); /* FPU/SSE magic */
syscalls_install(); /* Install the system calls */
shm_install(); /* Install shared memory */
modules_install(); /* Modules! */
DISABLE_EARLY_BOOT_LOG();
/* Load modules from bootloader */
debug_print(NOTICE, "%d modules to load", mboot_mods_count);
for (unsigned int i = 0; i < mboot_ptr->mods_count; ++i ) {
mboot_mod_t * mod = &mboot_mods[i];
uint32_t module_start = mod->mod_start;
uint32_t module_end = mod->mod_end;
size_t module_size = module_end - module_start;
int check_result = module_quickcheck((void *)module_start);
if (check_result == 1) {
debug_print(NOTICE, "Loading a module: 0x%x:0x%x", module_start, module_end);
module_data_t * mod_info = (module_data_t *)module_load_direct((void *)(module_start), module_size);
if (mod_info) {
debug_print(NOTICE, "Loaded: %s", mod_info->mod_info->name);
}
} else if (check_result == 2) {
/* Mod pack */
debug_print(NOTICE, "Loading modpack. %x", module_start);
struct pack_header * pack_header = (struct pack_header *)module_start;
while (pack_header->region_size) {
void * start = (void *)((uintptr_t)pack_header + 4096);
int result = module_quickcheck(start);
if (result != 1) {
debug_print(WARNING, "Not actually a module?! %x", start);
}
module_data_t * mod_info = (module_data_t *)module_load_direct(start, pack_header->region_size);
if (mod_info) {
debug_print(NOTICE, "Loaded: %s", mod_info->mod_info->name);
}
pack_header = (struct pack_header *)((uintptr_t)start + pack_header->region_size);
}
debug_print(NOTICE, "Done with modpack.");
} else {
debug_print(NOTICE, "Loading ramdisk: 0x%x:0x%x", module_start, module_end);
ramdisk_mount(module_start, module_size);
}
}
/* Map /dev to a device mapper */
map_vfs_directory("/dev");
if (args_present("root")) {
char * root_type = "ext2";
if (args_present("root_type")) {
root_type = args_value("root_type");
}
vfs_mount_type(root_type, args_value("root"), "/");
}
if (args_present("start")) {
char * c = args_value("start");
if (!c) {
debug_print(WARNING, "Expected an argument to kernel option `start`. Ignoring.");
} else {
debug_print(NOTICE, "Got start argument: %s", c);
boot_arg = strdup(c);
}
}
if (!fs_root) {
map_vfs_directory("/");
}
char * boot_app = "/bin/init";
if (args_present("init")) {
boot_app = args_value("init");
}
/* Prepare to run /bin/init */
char * argv[] = {
boot_app,
boot_arg,
NULL
};
int argc = 0;
while (argv[argc]) {
argc++;
}
system(argv[0], argc, argv); /* Run init */
debug_print(CRITICAL, "init failed");
switch_task(0);
return 0;
}

969
kernel/mem/alloc.c Normal file
View File

@ -0,0 +1,969 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
*
* Kevin Lange's Slab Allocator
*
* Implemented for CS241, Fall 2010, machine problem 7
* at the University of Illinois, Urbana-Champaign.
*
* Overall competition winner for speed.
* Well ranked in memory usage.
*
* Copyright (c) 2010 Kevin Lange. All rights reserved.
*
* Developed by: Kevin Lange <lange7@acm.uiuc.edu>
* Dave Majnemer <dmajnem2@acm.uiuc.edu>
* Assocation for Computing Machinery
* University of Illinois, Urbana-Champaign
* http://acm.uiuc.edu
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal with the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimers.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimers in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the Association for Computing Machinery, the
* University of Illinois, nor the names of its contributors may be used
* to endorse or promote products derived from this Software without
* specific prior written permission.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* WITH THE SOFTWARE.
*
* ##########
* # README #
* ##########
*
* About the slab allocator
* """"""""""""""""""""""""
*
* This is a simple implementation of a "slab" allocator. It works by operating
* on "bins" of items of predefined sizes and a set of pseudo-bins of any size.
* When a new allocation request is made, the allocator determines if it will
* fit in an existing bin. If there are no bins of the correct size for a given
* allocation request, the allocator will make a bin and add it to a(n empty)
* list of available bins of that size. In this implementation, we use sizes
* from 4 bytes (32 bit) or 8 bytes (64-bit) to 2KB for bins, fitting a 4K page
* size. The implementation allows the number of pages in a single bin to be
* increased, as well as allowing for changing the size of page (though this
* should, for the most part, remain 4KB under any modern system).
*
* Special thanks
* """"""""""""""
*
* I would like to thank Dave Majnemer, who I have credited above as a
* contributor, for his assistance. Without Dave, klmalloc would be a mash
* up of bits of forward movement in no discernible pattern. Dave helped
* me ensure that I could build a proper slab allocator and has consantly
* derided me for not fixing the bugs and to-do items listed in the last
* section of this readme.
*
* GCC Function Attributes
* """""""""""""""""""""""
*
* A couple of GCC function attributes, designated by the __attribute__
* directive, are used in this code to streamline optimization.
* I've chosen to include a brief overview of the particular attributes
* I am making use of:
*
* - malloc:
* Tells gcc that a given function is a memory allocator
* and that non-NULL values it returns should never be
* associated with other chunks of memory. We use this for
* alloc, realloc and calloc, as is requested in the gcc
* documentation for the attribute.
*
* - always_inline:
* Tells gcc to always inline the given code, regardless of the
* optmization level. Small functions that would be noticeably
* slower with the overhead of paramter handling are given
* this attribute.
*
* - pure:
* Tells gcc that a function only uses inputs and its output.
*
* Things to work on
* """""""""""""""""
*
* TODO: Try to be more consistent on comment widths...
* FIXME: Make thread safe! Not necessary for competition, but would be nice.
* FIXME: Splitting/coalescing is broken. Fix this ASAP!
*
**/
/* Includes {{{ */
#include <system.h>
/* }}} */
/* Definitions {{{ */
/*
* Defines for often-used integral values
* related to our binning and paging strategy.
*/
#define NUM_BINS 11U /* Number of bins, total, under 32-bit. */
#define SMALLEST_BIN_LOG 2U /* Logarithm base two of the smallest bin: log_2(sizeof(int32)). */
#define BIG_BIN (NUM_BINS - 1) /* Index for the big bin, (NUM_BINS - 1) */
#define SMALLEST_BIN (1UL << SMALLEST_BIN_LOG) /* Size of the smallest bin. */
#define PAGE_SIZE 0x1000 /* Size of a page (in bytes), should be 4KB */
#define PAGE_MASK (PAGE_SIZE - 1) /* Block mask, size of a page * number of pages - 1. */
#define SKIP_P INT32_MAX /* INT32_MAX is half of UINT32_MAX; this gives us a 50% marker for skip lists. */
#define SKIP_MAX_LEVEL 6 /* We have a maximum of 6 levels in our skip lists. */
#define BIN_MAGIC 0xDEFAD00D
/* }}} */
/*
* Internal functions.
*/
static void * __attribute__ ((malloc)) klmalloc(uintptr_t size);
static void * __attribute__ ((malloc)) klrealloc(void * ptr, uintptr_t size);
static void * __attribute__ ((malloc)) klcalloc(uintptr_t nmemb, uintptr_t size);
static void * __attribute__ ((malloc)) klvalloc(uintptr_t size);
static void klfree(void * ptr);
static spin_lock_t mem_lock = { 0 };
void * __attribute__ ((malloc)) malloc(uintptr_t size) {
spin_lock(mem_lock);
void * ret = klmalloc(size);
spin_unlock(mem_lock);
return ret;
}
void * __attribute__ ((malloc)) realloc(void * ptr, uintptr_t size) {
spin_lock(mem_lock);
void * ret = klrealloc(ptr, size);
spin_unlock(mem_lock);
return ret;
}
void * __attribute__ ((malloc)) calloc(uintptr_t nmemb, uintptr_t size) {
spin_lock(mem_lock);
void * ret = klcalloc(nmemb, size);
spin_unlock(mem_lock);
return ret;
}
void * __attribute__ ((malloc)) valloc(uintptr_t size) {
spin_lock(mem_lock);
void * ret = klvalloc(size);
spin_unlock(mem_lock);
return ret;
}
void free(void * ptr) {
spin_lock(mem_lock);
if ((uintptr_t)ptr > placement_pointer) {
klfree(ptr);
}
spin_unlock(mem_lock);
}
/* Bin management {{{ */
/*
* Adjust bin size in bin_size call to proper bounds.
*/
inline static uintptr_t __attribute__ ((always_inline, pure)) klmalloc_adjust_bin(uintptr_t bin)
{
if (bin <= (uintptr_t)SMALLEST_BIN_LOG)
{
return 0;
}
bin -= SMALLEST_BIN_LOG + 1;
if (bin > (uintptr_t)BIG_BIN) {
return BIG_BIN;
}
return bin;
}
/*
* Given a size value, find the correct bin
* to place the requested allocation in.
*/
inline static uintptr_t __attribute__ ((always_inline, pure)) klmalloc_bin_size(uintptr_t size) {
uintptr_t bin = sizeof(size) * CHAR_BIT - __builtin_clzl(size);
bin += !!(size & (size - 1));
return klmalloc_adjust_bin(bin);
}
/*
* Bin header - One page of memory.
* Appears at the front of a bin to point to the
* previous bin (or NULL if the first), the next bin
* (or NULL if the last) and the head of the bin, which
* is a stack of cells of data.
*/
typedef struct _klmalloc_bin_header {
struct _klmalloc_bin_header * next; /* Pointer to the next node. */
void * head; /* Head of this bin. */
uintptr_t size; /* Size of this bin, if big; otherwise bin index. */
uint32_t bin_magic;
} klmalloc_bin_header;
/*
* A big bin header is basically the same as a regular bin header
* only with a pointer to the previous (physically) instead of
* a "next" and with a list of forward headers.
*/
typedef struct _klmalloc_big_bin_header {
struct _klmalloc_big_bin_header * next;
void * head;
uintptr_t size;
uint32_t bin_magic;
struct _klmalloc_big_bin_header * prev;
struct _klmalloc_big_bin_header * forward[SKIP_MAX_LEVEL+1];
} klmalloc_big_bin_header;
/*
* List of pages in a bin.
*/
typedef struct _klmalloc_bin_header_head {
klmalloc_bin_header * first;
} klmalloc_bin_header_head;
/*
* Array of available bins.
*/
static klmalloc_bin_header_head klmalloc_bin_head[NUM_BINS - 1]; /* Small bins */
static struct _klmalloc_big_bins {
klmalloc_big_bin_header head;
int level;
} klmalloc_big_bins;
static klmalloc_big_bin_header * klmalloc_newest_big = NULL; /* Newest big bin */
/* }}} Bin management */
/* Doubly-Linked List {{{ */
/*
* Remove an entry from a page list.
* Decouples the element from its
* position in the list by linking
* its neighbors to eachother.
*/
inline static void __attribute__ ((always_inline)) klmalloc_list_decouple(klmalloc_bin_header_head *head, klmalloc_bin_header *node) {
klmalloc_bin_header *next = node->next;
head->first = next;
node->next = NULL;
}
/*
* Insert an entry into a page list.
* The new entry is placed at the front
* of the list and the existing border
* elements are updated to point back
* to it (our list is doubly linked).
*/
inline static void __attribute__ ((always_inline)) klmalloc_list_insert(klmalloc_bin_header_head *head, klmalloc_bin_header *node) {
node->next = head->first;
head->first = node;
}
/*
* Get the head of a page list.
* Because redundant function calls
* are really great, and just in case
* we change the list implementation.
*/
inline static klmalloc_bin_header * __attribute__ ((always_inline)) klmalloc_list_head(klmalloc_bin_header_head *head) {
return head->first;
}
/* }}} Lists */
/* Skip List {{{ */
/*
* Skip lists are efficient
* data structures for storing
* and searching ordered data.
*
* Here, the skip lists are used
* to keep track of big bins.
*/
/*
* Generate a random value in an appropriate range.
* This is a xor-shift RNG.
*/
static uint32_t __attribute__ ((pure)) klmalloc_skip_rand(void) {
static uint32_t x = 123456789;
static uint32_t y = 362436069;
static uint32_t z = 521288629;
static uint32_t w = 88675123;
uint32_t t;
t = x ^ (x << 11);
x = y; y = z; z = w;
return w = w ^ (w >> 19) ^ t ^ (t >> 8);
}
/*
* Generate a random level for a skip node
*/
inline static int __attribute__ ((pure, always_inline)) klmalloc_random_level(void) {
int level = 0;
/*
* Keep trying to check rand() against 50% of its maximum.
* This provides 50%, 25%, 12.5%, etc. chance for each level.
*/
while (klmalloc_skip_rand() < SKIP_P && level < SKIP_MAX_LEVEL) {
++level;
}
return level;
}
/*
* Find best fit for a given value.
*/
static klmalloc_big_bin_header * klmalloc_skip_list_findbest(uintptr_t search_size) {
klmalloc_big_bin_header * node = &klmalloc_big_bins.head;
/*
* Loop through the skip list until we hit something > our search value.
*/
int i;
for (i = klmalloc_big_bins.level; i >= 0; --i) {
while (node->forward[i] && (node->forward[i]->size < search_size)) {
node = node->forward[i];
if (node)
assert((node->size + sizeof(klmalloc_big_bin_header)) % PAGE_SIZE == 0);
}
}
/*
* This value will either be NULL (we found nothing)
* or a node (we found a minimum fit).
*/
node = node->forward[0];
if (node) {
assert((uintptr_t)node % PAGE_SIZE == 0);
assert((node->size + sizeof(klmalloc_big_bin_header)) % PAGE_SIZE == 0);
}
return node;
}
/*
* Insert a header into the skip list.
*/
static void klmalloc_skip_list_insert(klmalloc_big_bin_header * value) {
/*
* You better be giving me something valid to insert,
* or I will slit your ****ing throat.
*/
assert(value != NULL);
assert(value->head != NULL);
assert((uintptr_t)value->head > (uintptr_t)value);
if (value->size > NUM_BINS) {
assert((uintptr_t)value->head < (uintptr_t)value + value->size);
} else {
assert((uintptr_t)value->head < (uintptr_t)value + PAGE_SIZE);
}
assert((uintptr_t)value % PAGE_SIZE == 0);
assert((value->size + sizeof(klmalloc_big_bin_header)) % PAGE_SIZE == 0);
assert(value->size != 0);
/*
* Starting from the head node of the bin locator...
*/
klmalloc_big_bin_header * node = &klmalloc_big_bins.head;
klmalloc_big_bin_header * update[SKIP_MAX_LEVEL + 1];
/*
* Loop through the skiplist to find the right place
* to insert the node (where ->forward[] > value)
*/
int i;
for (i = klmalloc_big_bins.level; i >= 0; --i) {
while (node->forward[i] && node->forward[i]->size < value->size) {
node = node->forward[i];
if (node)
assert((node->size + sizeof(klmalloc_big_bin_header)) % PAGE_SIZE == 0);
}
update[i] = node;
}
node = node->forward[0];
/*
* Make the new skip node and update
* the forward values.
*/
if (node != value) {
int level = klmalloc_random_level();
/*
* Get all of the nodes before this.
*/
if (level > klmalloc_big_bins.level) {
for (i = klmalloc_big_bins.level + 1; i <= level; ++i) {
update[i] = &klmalloc_big_bins.head;
}
klmalloc_big_bins.level = level;
}
/*
* Make the new node.
*/
node = value;
/*
* Run through and point the preceeding nodes
* for each level to the new node.
*/
for (i = 0; i <= level; ++i) {
node->forward[i] = update[i]->forward[i];
if (node->forward[i])
assert((node->forward[i]->size + sizeof(klmalloc_big_bin_header)) % PAGE_SIZE == 0);
update[i]->forward[i] = node;
}
}
}
/*
* Delete a header from the skip list.
* Be sure you didn't change the size, or we won't be able to find it.
*/
static void klmalloc_skip_list_delete(klmalloc_big_bin_header * value) {
/*
* Debug assertions
*/
assert(value != NULL);
assert(value->head);
assert((uintptr_t)value->head > (uintptr_t)value);
if (value->size > NUM_BINS) {
assert((uintptr_t)value->head < (uintptr_t)value + value->size);
} else {
assert((uintptr_t)value->head < (uintptr_t)value + PAGE_SIZE);
}
/*
* Starting from the bin header, again...
*/
klmalloc_big_bin_header * node = &klmalloc_big_bins.head;
klmalloc_big_bin_header * update[SKIP_MAX_LEVEL + 1];
/*
* Find the node.
*/
int i;
for (i = klmalloc_big_bins.level; i >= 0; --i) {
while (node->forward[i] && node->forward[i]->size < value->size) {
node = node->forward[i];
if (node)
assert((node->size + sizeof(klmalloc_big_bin_header)) % PAGE_SIZE == 0);
}
update[i] = node;
}
node = node->forward[0];
while (node != value) {
node = node->forward[0];
}
if (node != value) {
node = klmalloc_big_bins.head.forward[0];
while (node->forward[0] && node->forward[0] != value) {
node = node->forward[0];
}
node = node->forward[0];
}
/*
* If we found the node, delete it;
* otherwise, we do nothing.
*/
if (node == value) {
for (i = 0; i <= klmalloc_big_bins.level; ++i) {
if (update[i]->forward[i] != node) {
break;
}
update[i]->forward[i] = node->forward[i];
if (update[i]->forward[i]) {
assert((uintptr_t)(update[i]->forward[i]) % PAGE_SIZE == 0);
assert((update[i]->forward[i]->size + sizeof(klmalloc_big_bin_header)) % PAGE_SIZE == 0);
}
}
while (klmalloc_big_bins.level > 0 && klmalloc_big_bins.head.forward[klmalloc_big_bins.level] == NULL) {
--klmalloc_big_bins.level;
}
}
}
/* }}} */
/* Stack {{{ */
/*
* Pop an item from a block.
* Free space is stored as a stack,
* so we get a free space for a bin
* by popping a free node from the
* top of the stack.
*/
static void * klmalloc_stack_pop(klmalloc_bin_header *header) {
assert(header);
assert(header->head != NULL);
assert((uintptr_t)header->head > (uintptr_t)header);
if (header->size > NUM_BINS) {
assert((uintptr_t)header->head < (uintptr_t)header + header->size);
} else {
assert((uintptr_t)header->head < (uintptr_t)header + PAGE_SIZE);
assert((uintptr_t)header->head > (uintptr_t)header + sizeof(klmalloc_bin_header) - 1);
}
/*
* Remove the current head and point
* the head to where the old head pointed.
*/
void *item = header->head;
uintptr_t **head = header->head;
uintptr_t *next = *head;
header->head = next;
return item;
}
/*
* Push an item into a block.
* When we free memory, we need
* to add the freed cell back
* into the stack of free spaces
* for the block.
*/
static void klmalloc_stack_push(klmalloc_bin_header *header, void *ptr) {
assert(ptr != NULL);
assert((uintptr_t)ptr > (uintptr_t)header);
if (header->size > NUM_BINS) {
assert((uintptr_t)ptr < (uintptr_t)header + header->size);
} else {
assert((uintptr_t)ptr < (uintptr_t)header + PAGE_SIZE);
}
uintptr_t **item = (uintptr_t **)ptr;
*item = (uintptr_t *)header->head;
header->head = item;
}
/*
* Is this cell stack empty?
* If the head of the stack points
* to NULL, we have exhausted the
* stack, so there is no more free
* space available in the block.
*/
inline static int __attribute__ ((always_inline)) klmalloc_stack_empty(klmalloc_bin_header *header) {
return header->head == NULL;
}
/* }}} Stack */
/* malloc() {{{ */
static void * __attribute__ ((malloc)) klmalloc(uintptr_t size) {
/*
* C standard implementation:
* If size is zero, we can choose do a number of things.
* This implementation will return a NULL pointer.
*/
if (__builtin_expect(size == 0, 0))
return NULL;
/*
* Find the appropriate bin for the requested
* allocation and start looking through that list.
*/
unsigned int bucket_id = klmalloc_bin_size(size);
if (bucket_id < BIG_BIN) {
/*
* Small bins.
*/
klmalloc_bin_header * bin_header = klmalloc_list_head(&klmalloc_bin_head[bucket_id]);
if (!bin_header) {
/*
* Grow the heap for the new bin.
*/
bin_header = (klmalloc_bin_header*)sbrk(PAGE_SIZE);
bin_header->bin_magic = BIN_MAGIC;
assert((uintptr_t)bin_header % PAGE_SIZE == 0);
/*
* Set the head of the stack.
*/
bin_header->head = (void*)((uintptr_t)bin_header + sizeof(klmalloc_bin_header));
/*
* Insert the new bin at the front of
* the list of bins for this size.
*/
klmalloc_list_insert(&klmalloc_bin_head[bucket_id], bin_header);
/*
* Initialize the stack inside the bin.
* The stack is initially full, with each
* entry pointing to the next until the end
* which points to NULL.
*/
uintptr_t adj = SMALLEST_BIN_LOG + bucket_id;
uintptr_t i, available = ((PAGE_SIZE - sizeof(klmalloc_bin_header)) >> adj) - 1;
uintptr_t **base = bin_header->head;
for (i = 0; i < available; ++i) {
/*
* Our available memory is made into a stack, with each
* piece of memory turned into a pointer to the next
* available piece. When we want to get a new piece
* of memory from this block, we just pop off a free
* spot and give its address.
*/
base[i << bucket_id] = (uintptr_t *)&base[(i + 1) << bucket_id];
}
base[available << bucket_id] = NULL;
bin_header->size = bucket_id;
}
uintptr_t ** item = klmalloc_stack_pop(bin_header);
if (klmalloc_stack_empty(bin_header)) {
klmalloc_list_decouple(&(klmalloc_bin_head[bucket_id]),bin_header);
}
return item;
} else {
/*
* Big bins.
*/
klmalloc_big_bin_header * bin_header = klmalloc_skip_list_findbest(size);
if (bin_header) {
assert(bin_header->size >= size);
/*
* If we found one, delete it from the skip list
*/
klmalloc_skip_list_delete(bin_header);
/*
* Retreive the head of the block.
*/
uintptr_t ** item = klmalloc_stack_pop((klmalloc_bin_header *)bin_header);
#if 0
/*
* Resize block, if necessary
*/
assert(bin_header->head == NULL);
uintptr_t old_size = bin_header->size;
//uintptr_t rsize = size;
/*
* Round the requeste size to our full required size.
*/
size = ((size + sizeof(klmalloc_big_bin_header)) / PAGE_SIZE + 1) * PAGE_SIZE - sizeof(klmalloc_big_bin_header);
assert((size + sizeof(klmalloc_big_bin_header)) % PAGE_SIZE == 0);
if (bin_header->size > size * 2) {
assert(old_size != size);
/*
* If we have extra space, start splitting.
*/
bin_header->size = size;
assert(sbrk(0) >= bin_header->size + (uintptr_t)bin_header);
/*
* Make a new block at the end of the needed space.
*/
klmalloc_big_bin_header * header_new = (klmalloc_big_bin_header *)((uintptr_t)bin_header + sizeof(klmalloc_big_bin_header) + size);
assert((uintptr_t)header_new % PAGE_SIZE == 0);
memset(header_new, 0, sizeof(klmalloc_big_bin_header) + sizeof(void *));
header_new->prev = bin_header;
if (bin_header->next) {
bin_header->next->prev = header_new;
}
header_new->next = bin_header->next;
bin_header->next = header_new;
if (klmalloc_newest_big == bin_header) {
klmalloc_newest_big = header_new;
}
header_new->size = old_size - (size + sizeof(klmalloc_big_bin_header));
assert(((uintptr_t)header_new->size + sizeof(klmalloc_big_bin_header)) % PAGE_SIZE == 0);
fprintf(stderr, "Splitting %p [now %zx] at %p [%zx] from [%zx,%zx].\n", (void*)bin_header, bin_header->size, (void*)header_new, header_new->size, old_size, size);
/*
* Free the new block.
*/
klfree((void *)((uintptr_t)header_new + sizeof(klmalloc_big_bin_header)));
}
#endif
return item;
} else {
/*
* Round requested size to a set of pages, plus the header size.
*/
uintptr_t pages = (size + sizeof(klmalloc_big_bin_header)) / PAGE_SIZE + 1;
bin_header = (klmalloc_big_bin_header*)sbrk(PAGE_SIZE * pages);
bin_header->bin_magic = BIN_MAGIC;
assert((uintptr_t)bin_header % PAGE_SIZE == 0);
/*
* Give the header the remaining space.
*/
bin_header->size = pages * PAGE_SIZE - sizeof(klmalloc_big_bin_header);
assert((bin_header->size + sizeof(klmalloc_big_bin_header)) % PAGE_SIZE == 0);
/*
* Link the block in physical memory.
*/
bin_header->prev = klmalloc_newest_big;
if (bin_header->prev) {
bin_header->prev->next = bin_header;
}
klmalloc_newest_big = bin_header;
bin_header->next = NULL;
/*
* Return the head of the block.
*/
bin_header->head = NULL;
return (void*)((uintptr_t)bin_header + sizeof(klmalloc_big_bin_header));
}
}
}
/* }}} */
/* free() {{{ */
static void klfree(void *ptr) {
/*
* C standard implementation: Do nothing when NULL is passed to free.
*/
if (__builtin_expect(ptr == NULL, 0)) {
return;
}
/*
* Woah, woah, hold on, was this a page-aligned block?
*/
if ((uintptr_t)ptr % PAGE_SIZE == 0) {
/*
* Well howdy-do, it was.
*/
ptr = (void *)((uintptr_t)ptr - 1);
}
/*
* Get our pointer to the head of this block by
* page aligning it.
*/
klmalloc_bin_header * header = (klmalloc_bin_header *)((uintptr_t)ptr & (uintptr_t)~PAGE_MASK);
assert((uintptr_t)header % PAGE_SIZE == 0);
if (header->bin_magic != BIN_MAGIC)
return;
/*
* For small bins, the bin number is stored in the size
* field of the header. For large bins, the actual size
* available in the bin is stored in this field. It's
* easy to tell which is which, though.
*/
uintptr_t bucket_id = header->size;
if (bucket_id > (uintptr_t)NUM_BINS) {
bucket_id = BIG_BIN;
klmalloc_big_bin_header *bheader = (klmalloc_big_bin_header*)header;
assert(bheader);
assert(bheader->head == NULL);
assert((bheader->size + sizeof(klmalloc_big_bin_header)) % PAGE_SIZE == 0);
/*
* Coalesce forward blocks into us.
*/
#if 0
if (bheader != klmalloc_newest_big) {
/*
* If we are not the newest big bin, there is most definitely
* something in front of us that we can read.
*/
assert((bheader->size + sizeof(klmalloc_big_bin_header)) % PAGE_SIZE == 0);
klmalloc_big_bin_header * next = (void *)((uintptr_t)bheader + sizeof(klmalloc_big_bin_header) + bheader->size);
assert((uintptr_t)next % PAGE_SIZE == 0);
if (next == bheader->next && next->head) { //next->size > NUM_BINS && next->head) {
/*
* If that something is an available big bin, we can
* coalesce it into us to form one larger bin.
*/
uintptr_t old_size = bheader->size;
klmalloc_skip_list_delete(next);
bheader->size = (uintptr_t)bheader->size + (uintptr_t)sizeof(klmalloc_big_bin_header) + next->size;
assert((bheader->size + sizeof(klmalloc_big_bin_header)) % PAGE_SIZE == 0);
if (next == klmalloc_newest_big) {
/*
* If the guy in front of us was the newest,
* we are now the newest (as we are him).
*/
klmalloc_newest_big = bheader;
} else {
if (next->next) {
next->next->prev = bheader;
}
}
fprintf(stderr,"Coelesced (forwards) %p [%zx] <- %p [%zx] = %zx\n", (void*)bheader, old_size, (void*)next, next->size, bheader->size);
}
}
#endif
/*
* Coalesce backwards
*/
#if 0
if (bheader->prev && bheader->prev->head) {
/*
* If there is something behind us, it is available, and there is nothing between
* it and us, we can coalesce ourselves into it to form a big block.
*/
if ((uintptr_t)bheader->prev + (bheader->prev->size + sizeof(klmalloc_big_bin_header)) == (uintptr_t)bheader) {
uintptr_t old_size = bheader->prev->size;
klmalloc_skip_list_delete(bheader->prev);
bheader->prev->size = (uintptr_t)bheader->prev->size + (uintptr_t)bheader->size + sizeof(klmalloc_big_bin_header);
assert((bheader->prev->size + sizeof(klmalloc_big_bin_header)) % PAGE_SIZE == 0);
klmalloc_skip_list_insert(bheader->prev);
if (klmalloc_newest_big == bheader) {
klmalloc_newest_big = bheader->prev;
} else {
if (bheader->next) {
bheader->next->prev = bheader->prev;
}
}
fprintf(stderr,"Coelesced (backwards) %p [%zx] <- %p [%zx] = %zx\n", (void*)bheader->prev, old_size, (void*)bheader, bheader->size, bheader->size);
/*
* If we coalesced backwards, we are done.
*/
return;
}
}
#endif
/*
* Push new space back into the stack.
*/
klmalloc_stack_push((klmalloc_bin_header *)bheader, (void *)((uintptr_t)bheader + sizeof(klmalloc_big_bin_header)));
assert(bheader->head != NULL);
/*
* Insert the block into list of available slabs.
*/
klmalloc_skip_list_insert(bheader);
} else {
/*
* If the stack is empty, we are freeing
* a block from a previously full bin.
* Return it to the busy bins list.
*/
if (klmalloc_stack_empty(header)) {
klmalloc_list_insert(&klmalloc_bin_head[bucket_id], header);
}
/*
* Push new space back into the stack.
*/
klmalloc_stack_push(header, ptr);
}
}
/* }}} */
/* valloc() {{{ */
static void * __attribute__ ((malloc)) klvalloc(uintptr_t size) {
/*
* Allocate a page-aligned block.
* XXX: THIS IS HORRIBLY, HORRIBLY WASTEFUL!! ONLY USE THIS
* IF YOU KNOW WHAT YOU ARE DOING!
*/
uintptr_t true_size = size + PAGE_SIZE - sizeof(klmalloc_big_bin_header); /* Here we go... */
void * result = klmalloc(true_size);
void * out = (void *)((uintptr_t)result + (PAGE_SIZE - sizeof(klmalloc_big_bin_header)));
assert((uintptr_t)out % PAGE_SIZE == 0);
return out;
}
/* }}} */
/* realloc() {{{ */
static void * __attribute__ ((malloc)) klrealloc(void *ptr, uintptr_t size) {
/*
* C standard implementation: When NULL is passed to realloc,
* simply malloc the requested size and return a pointer to that.
*/
if (__builtin_expect(ptr == NULL, 0))
return klmalloc(size);
/*
* C standard implementation: For a size of zero, free the
* pointer and return NULL, allocating no new memory.
*/
if (__builtin_expect(size == 0, 0))
{
free(ptr);
return NULL;
}
/*
* Find the bin for the given pointer
* by aligning it to a page.
*/
klmalloc_bin_header * header_old = (void *)((uintptr_t)ptr & (uintptr_t)~PAGE_MASK);
if (header_old->bin_magic != BIN_MAGIC) {
assert(0 && "Bad magic on realloc.");
return NULL;
}
uintptr_t old_size = header_old->size;
if (old_size < (uintptr_t)BIG_BIN) {
/*
* If we are copying from a small bin,
* we need to get the size of the bin
* from its id.
*/
old_size = (1UL << (SMALLEST_BIN_LOG + old_size));
}
/*
* (This will only happen for a big bin, mathematically speaking)
* If we still have room in our bin for the additonal space,
* we don't need to do anything.
*/
if (old_size >= size) {
/*
* TODO: Break apart blocks here, which is far more important
* than breaking them up on allocations.
*/
return ptr;
}
/*
* Reallocate more memory.
*/
void * newptr = klmalloc(size);
if (__builtin_expect(newptr != NULL, 1)) {
/*
* Copy the old value into the new value.
* Be sure to only copy as much as was in
* the old block.
*/
memcpy(newptr, ptr, old_size);
klfree(ptr);
return newptr;
}
/*
* We failed to allocate more memory,
* which means we're probably out.
*
* Bail and return NULL.
*/
return NULL;
}
/* }}} */
/* calloc() {{{ */
static void * __attribute__ ((malloc)) klcalloc(uintptr_t nmemb, uintptr_t size) {
/*
* Allocate memory and zero it before returning
* a pointer to the newly allocated memory.
*
* Implemented by way of a simple malloc followed
* by a memset to 0x00 across the length of the
* requested memory chunk.
*/
void *ptr = klmalloc(nmemb * size);
if (__builtin_expect(ptr != NULL, 1))
memset(ptr,0x00,nmemb * size);
return ptr;
}
/* }}} */

564
kernel/mem/mem.c Normal file
View File

@ -0,0 +1,564 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2011-2014 Kevin Lange
* Copyright (C) 2012 Markus Schober
*
* Kernel Memory Manager
*/
#include <mem.h>
#include <system.h>
#include <process.h>
#include <logging.h>
#include <signal.h>
#include <hashmap.h>
#include <module.h>
#define KERNEL_HEAP_INIT 0x00800000
#define KERNEL_HEAP_END 0x20000000
extern void *end;
uintptr_t placement_pointer = (uintptr_t)&end;
uintptr_t heap_end = (uintptr_t)NULL;
uintptr_t kernel_heap_alloc_point = KERNEL_HEAP_INIT;
//static volatile uint8_t frame_alloc_lock = 0;
static spin_lock_t frame_alloc_lock = { 0 };
uint32_t first_n_frames(int n);
void
kmalloc_startat(
uintptr_t address
) {
placement_pointer = address;
}
/*
* kmalloc() is the kernel's dumb placement allocator
*/
uintptr_t
kmalloc_real(
size_t size,
int align,
uintptr_t * phys
) {
if (heap_end) {
void * address;
if (align) {
address = valloc(size);
} else {
address = malloc(size);
}
if (phys) {
if (align && size >= 0x3000) {
debug_print(NOTICE, "Requested large aligned alloc of size 0x%x", size);
for (uintptr_t i = (uintptr_t)address; i < (uintptr_t)address + size; i += 0x1000) {
clear_frame(map_to_physical(i));
}
/* XXX This is going to get touchy... */
spin_lock(frame_alloc_lock);
uint32_t index = first_n_frames((size + 0xFFF) / 0x1000);
if (index == 0xFFFFFFFF) {
spin_unlock(frame_alloc_lock);
return 0;
}
for (unsigned int i = 0; i < (size + 0xFFF) / 0x1000; ++i) {
set_frame((index + i) * 0x1000);
page_t * page = get_page((uintptr_t)address + (i * 0x1000),0,kernel_directory);
ASSUME(page != NULL);
page->frame = index + i;
page->writethrough = 1;
page->cachedisable = 1;
}
spin_unlock(frame_alloc_lock);
}
*phys = map_to_physical((uintptr_t)address);
}
return (uintptr_t)address;
}
if (align && (placement_pointer & 0xFFFFF000)) {
placement_pointer &= 0xFFFFF000;
placement_pointer += 0x1000;
}
if (phys) {
*phys = placement_pointer;
}
uintptr_t address = placement_pointer;
placement_pointer += size;
return (uintptr_t)address;
}
/*
* Normal
*/
uintptr_t
kmalloc(
size_t size
) {
return kmalloc_real(size, 0, NULL);
}
/*
* Aligned
*/
uintptr_t
kvmalloc(
size_t size
) {
return kmalloc_real(size, 1, NULL);
}
/*
* With a physical address
*/
uintptr_t
kmalloc_p(
size_t size,
uintptr_t *phys
) {
return kmalloc_real(size, 0, phys);
}
/*
* Aligned, with a physical address
*/
uintptr_t
kvmalloc_p(
size_t size,
uintptr_t *phys
) {
return kmalloc_real(size, 1, phys);
}
/*
* Frame Allocation
*/
uint32_t *frames;
uint32_t nframes;
#define INDEX_FROM_BIT(b) (b / 0x20)
#define OFFSET_FROM_BIT(b) (b % 0x20)
void
set_frame(
uintptr_t frame_addr
) {
if (frame_addr < nframes * 4 * 0x400) {
uint32_t frame = frame_addr / 0x1000;
uint32_t index = INDEX_FROM_BIT(frame);
uint32_t offset = OFFSET_FROM_BIT(frame);
frames[index] |= ((uint32_t)0x1 << offset);
}
}
void
clear_frame(
uintptr_t frame_addr
) {
uint32_t frame = frame_addr / 0x1000;
uint32_t index = INDEX_FROM_BIT(frame);
uint32_t offset = OFFSET_FROM_BIT(frame);
frames[index] &= ~((uint32_t)0x1 << offset);
}
uint32_t test_frame(uintptr_t frame_addr) {
uint32_t frame = frame_addr / 0x1000;
uint32_t index = INDEX_FROM_BIT(frame);
uint32_t offset = OFFSET_FROM_BIT(frame);
return (frames[index] & ((uint32_t)0x1 << offset));
}
uint32_t first_n_frames(int n) {
for (uint32_t i = 0; i < nframes * 0x1000; i += 0x1000) {
int bad = 0;
for (int j = 0; j < n; ++j) {
if (test_frame(i + 0x1000 * j)) {
bad = j+1;
}
}
if (!bad) {
return i / 0x1000;
}
}
return 0xFFFFFFFF;
}
uint32_t first_frame(void) {
uint32_t i, j;
for (i = 0; i < INDEX_FROM_BIT(nframes); ++i) {
if (frames[i] != 0xFFFFFFFF) {
for (j = 0; j < 32; ++j) {
uint32_t testFrame = (uint32_t)0x1 << j;
if (!(frames[i] & testFrame)) {
return i * 0x20 + j;
}
}
}
}
debug_print(CRITICAL, "System claims to be out of usable memory, which means we probably overwrote the page frames.\033[0m");
if (debug_video_crash) {
char * msgs[] = {"Out of memory.", NULL};
debug_video_crash(msgs);
}
#if 0
signal_t * sig = malloc(sizeof(signal_t));
sig->handler = current_process->signals.functions[SIGSEGV];
sig->signum = SIGSEGV;
handle_signal((process_t *)current_process, sig);
#endif
STOP;
return -1;
}
void
alloc_frame(
page_t *page,
int is_kernel,
int is_writeable
) {
ASSUME(page != NULL);
if (page->frame != 0) {
page->present = 1;
page->rw = (is_writeable == 1) ? 1 : 0;
page->user = (is_kernel == 1) ? 0 : 1;
return;
} else {
spin_lock(frame_alloc_lock);
uint32_t index = first_frame();
assert(index != (uint32_t)-1 && "Out of frames.");
set_frame(index * 0x1000);
page->frame = index;
spin_unlock(frame_alloc_lock);
page->present = 1;
page->rw = (is_writeable == 1) ? 1 : 0;
page->user = (is_kernel == 1) ? 0 : 1;
}
}
void
dma_frame(
page_t *page,
int is_kernel,
int is_writeable,
uintptr_t address
) {
ASSUME(page != NULL);
/* Page this address directly */
page->present = 1;
page->rw = (is_writeable) ? 1 : 0;
page->user = (is_kernel) ? 0 : 1;
page->frame = address / 0x1000;
set_frame(address);
}
void
free_frame(
page_t *page
) {
uint32_t frame;
if (!(frame = page->frame)) {
assert(0);
return;
} else {
clear_frame(frame * 0x1000);
page->frame = 0x0;
}
}
uintptr_t memory_use(void ) {
uintptr_t ret = 0;
uint32_t i, j;
for (i = 0; i < INDEX_FROM_BIT(nframes); ++i) {
for (j = 0; j < 32; ++j) {
uint32_t testFrame = (uint32_t)0x1 << j;
if (frames[i] & testFrame) {
ret++;
}
}
}
return ret * 4;
}
uintptr_t memory_total(){
return nframes * 4;
}
void paging_install(uint32_t memsize) {
nframes = memsize / 4;
frames = (uint32_t *)kmalloc(INDEX_FROM_BIT(nframes * 8));
memset(frames, 0, INDEX_FROM_BIT(nframes * 8));
uintptr_t phys;
kernel_directory = (page_directory_t *)kvmalloc_p(sizeof(page_directory_t),&phys);
memset(kernel_directory, 0, sizeof(page_directory_t));
}
void paging_mark_system(uint64_t addr) {
set_frame(addr);
}
void paging_finalize(void) {
debug_print(INFO, "Placement pointer is at 0x%x", placement_pointer);
#if 1
get_page(0,1,kernel_directory)->present = 0;
set_frame(0);
for (uintptr_t i = 0x1000; i < 0x80000; i += 0x1000) {
#else
for (uintptr_t i = 0x0; i < 0x80000; i += 0x1000) {
#endif
dma_frame(get_page(i, 1, kernel_directory), 1, 0, i);
}
for (uintptr_t i = 0x80000; i < 0x100000; i += 0x1000) {
dma_frame(get_page(i, 1, kernel_directory), 1, 0, i);
}
for (uintptr_t i = 0x100000; i < placement_pointer + 0x3000; i += 0x1000) {
dma_frame(get_page(i, 1, kernel_directory), 1, 0, i);
}
debug_print(INFO, "Mapping VGA text-mode directly.");
for (uintptr_t j = 0xb8000; j < 0xc0000; j += 0x1000) {
dma_frame(get_page(j, 0, kernel_directory), 0, 1, j);
}
isrs_install_handler(14, page_fault);
kernel_directory->physical_address = (uintptr_t)kernel_directory->physical_tables;
uintptr_t tmp_heap_start = KERNEL_HEAP_INIT;
if (tmp_heap_start <= placement_pointer + 0x3000) {
debug_print(ERROR, "Foo: 0x%x, 0x%x", tmp_heap_start, placement_pointer + 0x3000);
tmp_heap_start = placement_pointer + 0x100000;
kernel_heap_alloc_point = tmp_heap_start;
}
/* Kernel Heap Space */
for (uintptr_t i = placement_pointer + 0x3000; i < tmp_heap_start; i += 0x1000) {
alloc_frame(get_page(i, 1, kernel_directory), 1, 0);
}
/* And preallocate the page entries for all the rest of the kernel heap as well */
for (uintptr_t i = tmp_heap_start; i < KERNEL_HEAP_END; i += 0x1000) {
get_page(i, 1, kernel_directory);
}
debug_print(NOTICE, "Setting directory.");
current_directory = clone_directory(kernel_directory);
switch_page_directory(kernel_directory);
}
uintptr_t map_to_physical(uintptr_t virtual) {
uintptr_t remaining = virtual % 0x1000;
uintptr_t frame = virtual / 0x1000;
uintptr_t table = frame / 1024;
uintptr_t subframe = frame % 1024;
if (current_directory->tables[table]) {
page_t * p = &current_directory->tables[table]->pages[subframe];
return p->frame * 0x1000 + remaining;
} else {
return 0;
}
}
void debug_print_directory(page_directory_t * arg) {
page_directory_t * current_directory = arg;
debug_print(INSANE, " ---- [k:0x%x u:0x%x]", kernel_directory, current_directory);
for (uintptr_t i = 0; i < 1024; ++i) {
if (!current_directory->tables[i] || (uintptr_t)current_directory->tables[i] == (uintptr_t)0xFFFFFFFF) {
continue;
}
if (kernel_directory->tables[i] == current_directory->tables[i]) {
debug_print(INSANE, " 0x%x - kern [0x%x/0x%x] 0x%x", current_directory->tables[i], &current_directory->tables[i], &kernel_directory->tables[i], i * 0x1000 * 1024);
for (uint16_t j = 0; j < 1024; ++j) {
#if 1
page_t * p= &current_directory->tables[i]->pages[j];
if (p->frame) {
debug_print(INSANE, " k 0x%x 0x%x %s", (i * 1024 + j) * 0x1000, p->frame * 0x1000, p->present ? "[present]" : "");
}
#endif
}
} else {
debug_print(INSANE, " 0x%x - user [0x%x] 0x%x [0x%x]", current_directory->tables[i], &current_directory->tables[i], i * 0x1000 * 1024, kernel_directory->tables[i]);
for (uint16_t j = 0; j < 1024; ++j) {
#if 1
page_t * p= &current_directory->tables[i]->pages[j];
if (p->frame) {
debug_print(INSANE, " 0x%x 0x%x %s", (i * 1024 + j) * 0x1000, p->frame * 0x1000, p->present ? "[present]" : "");
}
#endif
}
}
}
debug_print(INFO, " ---- [done]");
}
void
switch_page_directory(
page_directory_t * dir
) {
current_directory = dir;
asm volatile (
"mov %0, %%cr3\n"
"mov %%cr0, %%eax\n"
"orl $0x80000000, %%eax\n"
"mov %%eax, %%cr0\n"
:: "r"(dir->physical_address)
: "%eax");
}
void invalidate_page_tables(void) {
asm volatile (
"movl %%cr3, %%eax\n"
"movl %%eax, %%cr3\n"
::: "%eax");
}
void invalidate_tables_at(uintptr_t addr) {
asm volatile (
"movl %0,%%eax\n"
"invlpg (%%eax)\n"
:: "r"(addr) : "%eax");
}
page_t *
get_page(
uintptr_t address,
int make,
page_directory_t * dir
) {
address /= 0x1000;
uint32_t table_index = address / 1024;
if (dir->tables[table_index]) {
return &dir->tables[table_index]->pages[address % 1024];
} else if(make) {
uint32_t temp;
dir->tables[table_index] = (page_table_t *)kvmalloc_p(sizeof(page_table_t), (uintptr_t *)(&temp));
ASSUME(dir->tables[table_index] != NULL);
memset(dir->tables[table_index], 0, sizeof(page_table_t));
dir->physical_tables[table_index] = temp | 0x7; /* Present, R/w, User */
return &dir->tables[table_index]->pages[address % 1024];
} else {
return 0;
}
}
void
page_fault(
struct regs *r) {
ASSUME(r != NULL);
uint32_t faulting_address;
asm volatile("mov %%cr2, %0" : "=r"(faulting_address));
if (r->eip == SIGNAL_RETURN) {
return_from_signal_handler();
} else if (r->eip == THREAD_RETURN) {
debug_print(INFO, "Returned from thread.");
kexit(0);
}
#if 1
int present = !(r->err_code & 0x1) ? 1 : 0;
int rw = r->err_code & 0x2 ? 1 : 0;
int user = r->err_code & 0x4 ? 1 : 0;
int reserved = r->err_code & 0x8 ? 1 : 0;
int id = r->err_code & 0x10 ? 1 : 0;
debug_print(ERROR, "\033[1;37;41mSegmentation fault. (p:%d,rw:%d,user:%d,res:%d,id:%d) at 0x%x eip: 0x%x pid=%d,%d [%s]\033[0m",
present, rw, user, reserved, id, faulting_address, r->eip, current_process->id, current_process->group, current_process->name);
if (r->eip < heap_end) {
/* find closest symbol */
char * closest = NULL;
size_t distance = 0xFFFFFFFF;
uintptr_t addr = 0;
if (modules_get_symbols()) {
list_t * hash_keys = hashmap_keys(modules_get_symbols());
foreach(_key, hash_keys) {
char * key = (char *)_key->value;
uintptr_t a = (uintptr_t)hashmap_get(modules_get_symbols(), key);
if (!a) continue;
size_t d;
if (a <= r->eip) {
d = r->eip - a;
} else {
d = a - r->eip;
}
if (d < distance) {
closest = key;
distance = d;
addr = a;
}
}
free(hash_keys);
debug_print(ERROR, "\033[1;31mClosest symbol to faulting address:\033[0m %s [0x%x]", closest, addr);
hash_keys = hashmap_keys(modules_get_list());
foreach(_key, hash_keys) {
char * key = (char *)_key->value;
module_data_t * m = (module_data_t *)hashmap_get(modules_get_list(), key);
if ((r->eip >= (uintptr_t)m->bin_data) && (r->eip < m->end)) {
debug_print(ERROR, "\033[1;31mIn module:\033[0m %s (starts at 0x%x)", m->mod_info->name, m->bin_data);
break;
}
}
free(hash_keys);
debug_print(ERROR, "User EIP: 0x%x", current_process->syscall_registers->eip);
}
} else {
debug_print(ERROR, "\033[1;31m(In userspace)\033[0m");
}
#endif
signal_t * sig = malloc(sizeof(signal_t));
sig->handler = current_process->signals.functions[SIGSEGV];
sig->signum = SIGSEGV;
handle_signal((process_t *)current_process, sig);
}
/*
* Heap
* Stop using kalloc and friends after installing the heap
* otherwise shit will break. I've conveniently broken
* kalloc when installing the heap, just for those of you
* who feel the need to screw up.
*/
void heap_install(void ) {
heap_end = (placement_pointer + 0x1000) & ~0xFFF;
}
void * sbrk(uintptr_t increment) {
assert((increment % 0x1000 == 0) && "Kernel requested to expand heap by a non-page-multiple value");
assert((heap_end % 0x1000 == 0) && "Kernel heap is not page-aligned!");
assert((heap_end + increment <= KERNEL_HEAP_END - 1) && "The kernel has attempted to allocate beyond the end of its heap.");
uintptr_t address = heap_end;
if (heap_end + increment > kernel_heap_alloc_point) {
debug_print(INFO, "Hit the end of available kernel heap, going to allocate more (at 0x%x, want to be at 0x%x)", heap_end, heap_end + increment);
for (uintptr_t i = heap_end; i < heap_end + increment; i += 0x1000) {
debug_print(INFO, "Allocating frame at 0x%x...", i);
page_t * page = get_page(i, 0, kernel_directory);
assert(page && "Kernel heap allocation fault.");
alloc_frame(page, 1, 0);
}
invalidate_page_tables();
debug_print(INFO, "Done.");
}
heap_end += increment;
memset((void *)address, 0x0, increment);
return (void *)address;
}

369
kernel/mem/shm.c Normal file
View File

@ -0,0 +1,369 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2012-2014 Kevin Lange
* Copyright (C) 2012 Markus Schober
*
* Shared Memory
*/
#include <system.h>
#include <process.h>
#include <logging.h>
#include <shm.h>
#include <mem.h>
#include <tree.h>
#include <list.h>
//static volatile uint8_t bsl; // big shm lock
static spin_lock_t bsl; // big shm lock
tree_t * shm_tree = NULL;
void shm_install(void) {
debug_print(NOTICE, "Installing shared memory layer...");
shm_tree = tree_create();
tree_set_root(shm_tree, NULL);
}
/* Accessors */
static shm_node_t * _get_node(char * shm_path, int create, tree_node_t * from) {
char *pch, *save;
pch = strtok_r(shm_path, SHM_PATH_SEPARATOR, &save);
tree_node_t * tnode = from;
foreach(node, tnode->children) {
tree_node_t * _node = (tree_node_t *)node->value;
shm_node_t * snode = (shm_node_t *)_node->value;
if (!strcmp(snode->name, pch)) {
if (*save == '\0') {
return snode;
}
return _get_node(save, create, _node);
}
}
/* The next node in sequence was not found */
if (create) {
shm_node_t * nsnode = malloc(sizeof(shm_node_t));
memcpy(nsnode->name, pch, strlen(pch) + 1);
nsnode->chunk = NULL;
tree_node_t * nnode = tree_node_insert_child(shm_tree, from, nsnode);
if (*save == '\0') {
return nsnode;
}
return _get_node(save, create, nnode);
} else {
return NULL;
}
}
static shm_node_t * get_node (char * shm_path, int create) {
char * _path = malloc(strlen(shm_path)+1);
memcpy(_path, shm_path, strlen(shm_path)+1);
shm_node_t * node = _get_node(_path, create, shm_tree->root);
free(_path);
return node;
}
/* Create and Release */
static shm_chunk_t * create_chunk (shm_node_t * parent, size_t size) {
debug_print(WARNING, "Size supplied to create_chunk was 0");
if (!size) return NULL;
shm_chunk_t *chunk = malloc(sizeof(shm_chunk_t));
if (chunk == NULL) {
debug_print(ERROR, "Failed to allocate a shm_chunk_t!");
return NULL;
}
chunk->parent = parent;
chunk->lock = 0;
chunk->ref_count = 1;
chunk->num_frames = (size / 0x1000) + ((size % 0x1000) ? 1 : 0);
chunk->frames = malloc(sizeof(uintptr_t) * chunk->num_frames);
if (chunk->frames == NULL) {
debug_print(ERROR, "Failed to allocate uintptr_t[%d]", chunk->num_frames);
free(chunk);
return NULL;
}
/* Now grab some frames for this guy. */
for (uint32_t i = 0; i < chunk->num_frames; i++) {
page_t tmp = {0,0,0,0,0,0,0};
alloc_frame(&tmp, 0, 0);
chunk->frames[i] = tmp.frame;
#if 0
debug_print(WARNING, "Using frame 0x%x for chunk[%d] (name=%s)", tmp.frame * 0x1000, i, parent->name);
#endif
}
return chunk;
}
static int release_chunk (shm_chunk_t * chunk) {
if (chunk) {
chunk->ref_count--;
/* Does the chunk need to be freed? */
if (chunk->ref_count < 1) {
#if 0
debug_print(INFO, "Freeing chunk with name %s", chunk->parent->name);
#endif
/* First, free the frames used by this chunk */
for (uint32_t i = 0; i < chunk->num_frames; i++) {
clear_frame(chunk->frames[i] * 0x1000);
}
/* Then, get rid of the damn thing */
chunk->parent->chunk = NULL;
free(chunk->frames);
free(chunk);
}
return 0;
}
return -1;
}
/* Mapping and Unmapping */
static uintptr_t proc_sbrk(uint32_t num_pages, process_t * proc) {
uintptr_t initial = proc->image.shm_heap;
assert(!(initial & 0xFFF) && "shm_heap not page-aligned!");
if (initial % 0x1000) {
initial += 0x1000 - (initial % 0x1000);
proc->image.shm_heap = initial;
}
proc->image.shm_heap += num_pages * 0x1000;
assert(!(proc->image.shm_heap & 0xFFF) && "math is wrong, dumbass");
return initial;
}
static void * map_in (shm_chunk_t * chunk, process_t * proc) {
if (!chunk) {
return NULL;
}
shm_mapping_t * mapping = malloc(sizeof(shm_mapping_t));
mapping->chunk = chunk;
mapping->num_vaddrs = chunk->num_frames;
mapping->vaddrs = malloc(sizeof(uintptr_t) * mapping->num_vaddrs);
debug_print(INFO, "want %d bytes, running through mappings...", mapping->num_vaddrs * 0x1000);
uintptr_t last_address = SHM_START;
foreach(node, proc->shm_mappings) {
shm_mapping_t * m = node->value;
if (m->vaddrs[0] > last_address) {
size_t gap = (uintptr_t)m->vaddrs[0] - last_address;
debug_print(INFO, "gap found at 0x%x of size %d", last_address, gap);
if (gap >= mapping->num_vaddrs * 0x1000) {
debug_print(INFO, "Gap is sufficient, we can insert here.");
/* Map the gap */
for (unsigned int i = 0; i < chunk->num_frames; ++i) {
page_t * page = get_page(last_address + i * 0x1000, 1, proc->thread.page_directory);
page->frame = chunk->frames[i];
alloc_frame(page, 0, 1);
invalidate_tables_at(last_address + i * 0x1000);
mapping->vaddrs[i] = last_address + i * 0x1000;
}
/* Insert us before this node */
list_insert_before(proc->shm_mappings, node, mapping);
return (void *)mapping->vaddrs[0];
}
}
last_address = m->vaddrs[0] + m->num_vaddrs * 0x1000;
debug_print(INFO, "[0x%x:0x%x] %s", m->vaddrs[0], last_address, m->chunk->parent->name);
}
if (proc->image.shm_heap > last_address) {
size_t gap = proc->image.shm_heap - last_address;
debug_print(INFO, "gap found at 0x%x of size %d", last_address, gap);
if (gap >= mapping->num_vaddrs * 0x1000) {
debug_print(INFO, "Gap is sufficient, we can insert here.");
for (unsigned int i = 0; i < chunk->num_frames; ++i) {
page_t * page = get_page(last_address + i * 0x1000, 1, proc->thread.page_directory);
page->frame = chunk->frames[i];
alloc_frame(page, 0, 1);
invalidate_tables_at(last_address + i * 0x1000);
mapping->vaddrs[i] = last_address + i * 0x1000;
}
list_insert(proc->shm_mappings, mapping);
return (void *)mapping->vaddrs[0];
} else {
debug_print(INFO, "should be more efficient here - there is space available, but we are not going to use it");
}
}
for (uint32_t i = 0; i < chunk->num_frames; i++) {
uintptr_t new_vpage = proc_sbrk(1, proc);
assert(new_vpage % 0x1000 == 0);
page_t * page = get_page(new_vpage, 1, proc->thread.page_directory);
assert(page && "Page not allocated by sys_sbrk?");
page->frame = chunk->frames[i];
alloc_frame(page, 0, 1);
invalidate_tables_at(new_vpage);
mapping->vaddrs[i] = new_vpage;
#if 0
debug_print(INFO, "mapping vaddr 0x%x --> #%d", new_vpage, page->frame);
#endif
}
list_insert(proc->shm_mappings, mapping);
return (void *)mapping->vaddrs[0];
}
static size_t chunk_size (shm_chunk_t * chunk) {
return (size_t)(chunk->num_frames * 0x1000);
}
/* Kernel-Facing Functions and Syscalls */
void * shm_obtain (char * path, size_t * size) {
spin_lock(bsl);
process_t * proc = (process_t *)current_process;
if (proc->group != 0) {
proc = process_from_pid(proc->group);
}
shm_node_t * node = get_node(path, 1); // (if it exists, just get it)
assert(node && "shm_node_t not created by get_node");
shm_chunk_t * chunk = node->chunk;
if (chunk == NULL) {
/* There's no chunk for that key -- we need to allocate it! */
debug_print(NOTICE, "Allocating a new shmem chunk for process %d", proc->id);
if (size == 0) {
// The process doesn't want a chunk...?
spin_unlock(bsl);
return NULL;
}
chunk = create_chunk(node, *size);
if (chunk == NULL) {
debug_print(ERROR, "Could not allocate a shm_chunk_t");
spin_unlock(bsl);
return NULL;
}
node->chunk = chunk;
} else {
/* New accessor! */
chunk->ref_count++;
}
void * vshm_start = map_in(chunk, proc);
*size = chunk_size(chunk);
spin_unlock(bsl);
invalidate_page_tables();
return vshm_start;
}
int shm_release (char * path) {
spin_lock(bsl);
process_t * proc = (process_t *)current_process;
if (proc->group != 0) {
proc = process_from_pid(proc->group);
}
/* First, find the right chunk */
shm_node_t * _node = get_node(path, 0);
if (!_node) {
spin_unlock(bsl);
return 1;
}
shm_chunk_t * chunk = _node->chunk;
/* Next, find the proc's mapping for that chunk */
node_t * node = NULL;
foreach (n, proc->shm_mappings) {
shm_mapping_t * m = (shm_mapping_t *)n->value;
if (m->chunk == chunk) {
node = n;
break;
}
}
if (node == NULL) {
spin_unlock(bsl);
return 1;
}
shm_mapping_t * mapping = (shm_mapping_t *)node->value;
/* Clear the mappings from the process's address space */
for (uint32_t i = 0; i < mapping->num_vaddrs; i++) {
page_t * page = get_page(mapping->vaddrs[i], 0, proc->thread.page_directory);
assert(page && "Shared memory mapping was invalid!");
memset(page, 0, sizeof(page_t));
}
invalidate_page_tables();
/* Clean up */
release_chunk(chunk);
list_delete(proc->shm_mappings, node);
free(node);
free(mapping);
spin_unlock(bsl);
return 0;
}
/* This function should only be called if the process's address space
* is about to be destroyed -- chunks will not be unmounted therefrom ! */
void shm_release_all (process_t * proc) {
spin_lock(bsl);
node_t * node;
while ((node = list_pop(proc->shm_mappings)) != NULL) {
shm_mapping_t * mapping = node->value;
release_chunk(mapping->chunk);
free(mapping);
free(node);
}
/* Empty, but don't free, the mappings list */
list_free(proc->shm_mappings);
proc->shm_mappings->head = proc->shm_mappings->tail = NULL;
proc->shm_mappings->length = 0;
spin_unlock(bsl);
}

91
kernel/misc/args.c Normal file
View File

@ -0,0 +1,91 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2011-2013 Kevin Lange
*
* Kernel Argument Manager
*
* Arguments to the kernel are provided from the bootloader and
* provide information such as what mode to pass to init, or what
* hard disk partition should be mounted as root.
*
* This module provides access
*/
#include <system.h>
#include <logging.h>
#include <args.h>
#include <hashmap.h>
#include <tokenize.h>
char * cmdline = NULL;
hashmap_t * kernel_args_map = NULL;
/**
* Check if an argument was provided to the kernel. If the argument is
* a simple switch, a response of 1 can be considered "on" for that
* argument; otherwise, this just notes that the argument was present,
* so the caller should check whether it is correctly set.
*/
int args_present(char * karg) {
if (!kernel_args_map) return 0; /* derp */
return hashmap_has(kernel_args_map, karg);
}
/**
* Return the value associated with an argument provided to the kernel.
*/
char * args_value(char * karg) {
if (!kernel_args_map) return 0; /* derp */
return hashmap_get(kernel_args_map, karg);
}
/**
* Parse the given arguments to the kernel.
*
* @param arg A string containing all arguments, separated by spaces.
*/
void args_parse(char * _arg) {
/* Sanity check... */
if (!_arg) { return; }
char * arg = strdup(_arg);
char * argv[1024];
int argc = tokenize(arg, " ", argv);
/* New let's parse the tokens into the arguments list so we can index by key */
if (!kernel_args_map) {
kernel_args_map = hashmap_create(10);
}
for (int i = 0; i < argc; ++i) {
char * c = strdup(argv[i]);
char * name;
char * value;
name = c;
value = NULL;
/* Find the first = and replace it with a null */
char * v = c;
while (*v) {
if (*v == '=') {
*v = '\0';
v++;
value = v;
goto _break;
}
v++;
}
_break:
hashmap_set(kernel_args_map, name, value);
}
free(arg);
}

322
kernel/misc/elf.c Normal file
View File

@ -0,0 +1,322 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2011-2014 Kevin Lange
*
* ELF Static Executable Loader
*
*/
#include <system.h>
#include <fs.h>
#include <elf.h>
#include <process.h>
#include <logging.h>
int exec_elf(char * path, fs_node_t * file, int argc, char ** argv, char ** env, int interp) {
Elf32_Header header;
read_fs(file, 0, sizeof(Elf32_Header), (uint8_t *)&header);
if (header.e_ident[0] != ELFMAG0 ||
header.e_ident[1] != ELFMAG1 ||
header.e_ident[2] != ELFMAG2 ||
header.e_ident[3] != ELFMAG3) {
debug_print(ERROR, "Not a valid ELF executable.");
close_fs(file);
return -1;
}
if (file->mask & 0x800) {
debug_print(WARNING, "setuid binary executed [%s, uid:%d]", file->name, file->uid);
current_process->user = file->uid;
}
for (uintptr_t x = 0; x < (uint32_t)header.e_phentsize * header.e_phnum; x += header.e_phentsize) {
Elf32_Phdr phdr;
read_fs(file, header.e_phoff + x, sizeof(Elf32_Phdr), (uint8_t *)&phdr);
if (phdr.p_type == PT_DYNAMIC) {
/* Dynamic */
close_fs(file);
/* Find interpreter? */
debug_print(WARNING, "Dynamic executable");
unsigned int nargc = argc + 3;
char * args[nargc+1];
args[0] = "ld.so";
args[1] = "-e";
args[2] = strdup(current_process->name);
int j = 3;
for (int i = 0; i < argc; ++i, ++j) {
args[j] = argv[i];
}
args[j] = NULL;
fs_node_t * file = kopen("/lib/ld.so",0);
if (!file) return -1;
return exec_elf(NULL, file, nargc, args, env, 1);
}
}
uintptr_t entry = (uintptr_t)header.e_entry;
uintptr_t base_addr = 0xFFFFFFFF;
uintptr_t end_addr = 0x0;
for (uintptr_t x = 0; x < (uint32_t)header.e_phentsize * header.e_phnum; x += header.e_phentsize) {
Elf32_Phdr phdr;
read_fs(file, header.e_phoff + x, sizeof(Elf32_Phdr), (uint8_t *)&phdr);
if (phdr.p_type == PT_LOAD) {
if (phdr.p_vaddr < base_addr) {
base_addr = phdr.p_vaddr;
}
if (phdr.p_memsz + phdr.p_vaddr > end_addr) {
end_addr = phdr.p_memsz + phdr.p_vaddr;
}
}
}
current_process->image.entry = base_addr;
current_process->image.size = end_addr - base_addr;
release_directory_for_exec(current_directory);
invalidate_page_tables();
for (uintptr_t x = 0; x < (uint32_t)header.e_phentsize * header.e_phnum; x += header.e_phentsize) {
Elf32_Phdr phdr;
read_fs(file, header.e_phoff + x, sizeof(Elf32_Phdr), (uint8_t *)&phdr);
if (phdr.p_type == PT_LOAD) {
for (uintptr_t i = phdr.p_vaddr; i < phdr.p_vaddr + phdr.p_memsz; i += 0x1000) {
/* This doesn't care if we already allocated this page */
alloc_frame(get_page(i, 1, current_directory), 0, 1);
invalidate_tables_at(i);
}
IRQ_RES;
read_fs(file, phdr.p_offset, phdr.p_filesz, (uint8_t *)phdr.p_vaddr);
IRQ_OFF;
size_t r = phdr.p_filesz;
while (r < phdr.p_memsz) {
*(char *)(phdr.p_vaddr + r) = 0;
r++;
}
}
}
close_fs(file);
for (uintptr_t stack_pointer = USER_STACK_BOTTOM; stack_pointer < USER_STACK_TOP; stack_pointer += 0x1000) {
alloc_frame(get_page(stack_pointer, 1, current_directory), 0, 1);
invalidate_tables_at(stack_pointer);
}
/* Collect arguments */
int envc = 0;
for (envc = 0; env[envc] != NULL; ++envc);
/* Format auxv */
Elf32_auxv auxv[] = {
{256, 0xDEADBEEF},
{0, 0}
};
int auxvc = 0;
for (auxvc = 0; auxv[auxvc].id != 0; ++auxvc);
auxvc++;
uintptr_t heap = current_process->image.entry + current_process->image.size;
while (heap & 0xFFF) heap++;
alloc_frame(get_page(heap, 1, current_directory), 0, 1);
invalidate_tables_at(heap);
char ** argv_ = (char **)heap;
heap += sizeof(char *) * (argc + 1);
char ** env_ = (char **)heap;
heap += sizeof(char *) * (envc + 1);
void * auxv_ptr = (void *)heap;
heap += sizeof(Elf32_auxv) * (auxvc);
for (int i = 0; i < argc; ++i) {
size_t size = strlen(argv[i]) * sizeof(char) + 1;
for (uintptr_t x = heap; x < heap + size + 0x1000; x += 0x1000) {
alloc_frame(get_page(x, 1, current_directory), 0, 1);
}
invalidate_tables_at(heap);
argv_[i] = (char *)heap;
memcpy((void *)heap, argv[i], size);
heap += size;
}
/* Don't forget the NULL at the end of that... */
argv_[argc] = 0;
for (int i = 0; i < envc; ++i) {
size_t size = strlen(env[i]) * sizeof(char) + 1;
for (uintptr_t x = heap; x < heap + size + 0x1000; x += 0x1000) {
alloc_frame(get_page(x, 1, current_directory), 0, 1);
}
invalidate_tables_at(heap);
env_[i] = (char *)heap;
memcpy((void *)heap, env[i], size);
heap += size;
}
env_[envc] = 0;
memcpy(auxv_ptr, auxv, sizeof(Elf32_auxv) * (auxvc));
current_process->image.heap = heap; /* heap end */
current_process->image.heap_actual = heap + (0x1000 - heap % 0x1000);
alloc_frame(get_page(current_process->image.heap_actual, 1, current_directory), 0, 1);
invalidate_tables_at(current_process->image.heap_actual);
current_process->image.user_stack = USER_STACK_TOP;
current_process->image.start = entry;
/* Close all fds >= 3 */
for (unsigned int i = 3; i < current_process->fds->length; ++i) {
if (current_process->fds->entries[i]) {
close_fs(current_process->fds->entries[i]);
current_process->fds->entries[i] = NULL;
}
}
/* Go go go */
enter_user_jmp(entry, argc, argv_, USER_STACK_TOP);
/* We should never reach this code */
return -1;
}
int exec_shebang(char * path, fs_node_t * file, int argc, char ** argv, char ** env, int interp) {
/* Read MAX_LINE... */
char tmp[100];
read_fs(file, 0, 100, (unsigned char *)tmp); close_fs(file);
char * cmd = (char *)&tmp[2];
char * space_or_linefeed = strpbrk(cmd, " \n");
char * arg = NULL;
if (!space_or_linefeed) {
debug_print(WARNING, "No space or linefeed found.");
return -ENOEXEC;
}
if (*space_or_linefeed == ' ') {
/* Oh lovely, an argument */
*space_or_linefeed = '\0';
space_or_linefeed++;
arg = space_or_linefeed;
space_or_linefeed = strpbrk(space_or_linefeed, "\n");
if (!space_or_linefeed) {
debug_print(WARNING, "Argument exceeded maximum length");
return -ENOEXEC;
}
}
*space_or_linefeed = '\0';
char script[strlen(path)+1];
memcpy(script, path, strlen(path)+1);
unsigned int nargc = argc + (arg ? 2 : 1);
char * args[nargc + 1];
args[0] = cmd;
args[1] = arg ? arg : script;
args[2] = arg ? script : NULL;
args[3] = NULL;
int j = arg ? 3 : 2;
for (int i = 1; i < argc; ++i, ++j) {
args[j] = argv[i];
}
args[j] = NULL;
return exec(cmd, nargc, args, env);
}
/* Consider exposing this and making it a list so it can be extended ... */
typedef int (*exec_func)(char * path, fs_node_t * file, int argc, char ** argv, char ** env, int interp);
typedef struct {
exec_func func;
unsigned char bytes[4];
unsigned int match;
char * name;
} exec_def_t;
exec_def_t fmts[] = {
{exec_elf, {ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3}, 4, "ELF"},
{exec_shebang, {'#', '!', 0, 0}, 2, "#!"},
};
static int matches(unsigned char * a, unsigned char * b, unsigned int len) {
for (unsigned int i = 0; i < len; ++i) {
if (a[i] != b[i]) return 0;
}
return 1;
}
/**
* Load an execute a binary.
*
* This determines the binary type (eg., ELF binary, she-bang script, etc.)
* and then calls the appropriate underlying exec function.
*
* @param path Path to the executable to attempt to execute.
* @param argc Number of arguments (because I'm not counting for you)
* @param argv Pointer to a string of arguments
*/
int exec(
char * path, /* Path to the executable to run */
int argc, /* Argument count (ie, /bin/echo hello world = 3) */
char ** argv, /* Argument strings (including executable path) */
char ** env /* Environmen variables */
) {
/* Open the file */
fs_node_t * file = kopen(path,0);
if (!file) {
/* Command not found */
return -ENOENT;
}
/* Read four bytes of the file */
unsigned char head[4];
read_fs(file, 0, 4, head);
debug_print(WARNING, "First four bytes: %c%c%c%c", head[0], head[1], head[2], head[3]);
current_process->name = strdup(path);
gettimeofday((struct timeval *)&current_process->start, NULL);
for (unsigned int i = 0; i < sizeof(fmts) / sizeof(exec_def_t); ++i) {
if (matches(fmts[i].bytes, head, fmts[i].match)) {
debug_print(WARNING, "Matched executor: %s", fmts[i].name);
return fmts[i].func(path, file, argc, argv, env, 0);
}
}
debug_print(WARNING, "Exec failed?");
return -ENOEXEC;
}
int
system(
char * path, /* Path to the executable to run */
int argc, /* Argument count (ie, /bin/echo hello world = 3) */
char ** argv /* Argument strings (including executable path) */
) {
char ** argv_ = malloc(sizeof(char *) * (argc + 1));
for (int j = 0; j < argc; ++j) {
argv_[j] = malloc((strlen(argv[j]) + 1) * sizeof(char));
memcpy(argv_[j], argv[j], strlen(argv[j]) + 1);
}
argv_[argc] = 0;
char * env[] = {NULL};
set_process_environment((process_t*)current_process, clone_directory(current_directory));
current_directory = current_process->thread.page_directory;
switch_page_directory(current_directory);
current_process->cmdline = argv_;
exec(path,argc,argv_,env);
debug_print(ERROR, "Failed to execute process!");
kexit(-1);
return -1;
}

158
kernel/misc/kprintf.c Normal file
View File

@ -0,0 +1,158 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2011-2014 Kevin Lange
*
* Kernel printf implementation
*
* Simple, painfully lacking, implementation of printf(),
* for the kernel of all things.
*/
#include <system.h>
#include <process.h>
#include <va_list.h>
#include <fs.h>
/*
* Integer to string
*/
static void print_dec(unsigned int value, unsigned int width, char * buf, int * ptr ) {
unsigned int n_width = 1;
unsigned int i = 9;
while (value > i && i < UINT32_MAX) {
n_width += 1;
i *= 10;
i += 9;
}
int printed = 0;
while (n_width + printed < width) {
buf[*ptr] = '0';
*ptr += 1;
printed += 1;
}
i = n_width;
while (i > 0) {
unsigned int n = value / 10;
int r = value % 10;
buf[*ptr + i - 1] = r + '0';
i--;
value = n;
}
*ptr += n_width;
}
/*
* Hexadecimal to string
*/
static void print_hex(unsigned int value, unsigned int width, char * buf, int * ptr) {
int i = width;
if (i == 0) i = 8;
unsigned int n_width = 1;
unsigned int j = 0x0F;
while (value > j && j < UINT32_MAX) {
n_width += 1;
j *= 0x10;
j += 0x0F;
}
while (i > (int)n_width) {
buf[*ptr] = '0';
*ptr += 1;
i--;
}
i = (int)n_width;
while (i-- > 0) {
buf[*ptr] = "0123456789abcdef"[(value>>(i*4))&0xF];
*ptr += + 1;
}
}
/*
* vasprintf()
*/
size_t vasprintf(char * buf, const char * fmt, va_list args) {
int i = 0;
char * s;
char * b = buf;
for (const char *f = fmt; *f; f++) {
if (*f != '%') {
*b++ = *f;
continue;
}
++f;
unsigned int arg_width = 0;
while (*f >= '0' && *f <= '9') {
arg_width *= 10;
arg_width += *f - '0';
++f;
}
/* fmt[i] == '%' */
switch (*f) {
case 's': /* String pointer -> String */
s = (char *)va_arg(args, char *);
if (s == NULL) {
s = "(null)";
}
while (*s) {
*b++ = *s++;
}
break;
case 'c': /* Single character */
*b++ = (char)va_arg(args, int);
break;
case 'x': /* Hexadecimal number */
i = b - buf;
print_hex((unsigned long)va_arg(args, unsigned long), arg_width, buf, &i);
b = buf + i;
break;
case 'd': /* Decimal number */
i = b - buf;
print_dec((unsigned long)va_arg(args, unsigned long), arg_width, buf, &i);
b = buf + i;
break;
case '%': /* Escape */
*b++ = '%';
break;
default: /* Nothing at all, just dump it */
*b++ = *f;
break;
}
}
/* Ensure the buffer ends in a null */
*b = '\0';
return b - buf;
}
static unsigned short * textmemptr = (unsigned short *)0xB8000;
static void placech(unsigned char c, int x, int y, int attr) {
unsigned short *where;
unsigned att = attr << 8;
where = textmemptr + (y * 80 + x);
*where = c | att;
}
int fprintf(fs_node_t * device, char *fmt, ...) {
va_list args;
va_start(args, fmt);
char buffer[1024];
vasprintf(buffer, fmt, args);
va_end(args);
return write_fs(device, 0, strlen(buffer), (uint8_t *)buffer);
}
int sprintf(char * buf, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
int out = vasprintf(buf, fmt, args);
va_end(args);
return out;
}

53
kernel/misc/logging.c Normal file
View File

@ -0,0 +1,53 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2011-2014 Kevin Lange
*
* Kernel Logging Facility
*
* Maintains a log in-memory as well as to serial (unless
* told not to).
*/
#include <system.h>
#include <list.h>
#include <logging.h>
#include <va_list.h>
#include <printf.h>
log_type_t debug_level = NOTICE;
void * debug_file = NULL;
void (*debug_hook)(void *, char *) = NULL;
void (*debug_video_crash)(char **) = NULL;
static char * c_messages[] = {
" \033[1;34mINFO\033[0m:",
" \033[1;35mNOTICE\033[0m:",
" \033[1;33mWARNING\033[0m:",
" \033[1;31mERROR\033[0m:",
" \033[1;37;41mCRITICAL\033[0m:",
" \033[1;31;44mINSANE\033[0m:"
};
static char buffer[1024];
void _debug_print(char * title, int line_no, log_type_t level, char *fmt, ...) {
if (level >= debug_level && debug_file) {
va_list args;
va_start(args, fmt);
vasprintf(buffer, fmt, args);
va_end(args);
char * type;
if (level > INSANE) {
type = "";
} else {
type = c_messages[level];
}
fprintf(debug_file, "[%10d.%3d:%s:%d]%s %s\n", timer_ticks, timer_subticks, title, line_no, type, buffer);
}
/* else ignore */
}

76
kernel/misc/multiboot.c Normal file
View File

@ -0,0 +1,76 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2011-2014 Kevin Lange
*
* Multiboot (GRUB) handler
*/
#include <system.h>
#include <logging.h>
#include <multiboot.h>
char * ramdisk = NULL;
struct multiboot * mboot_ptr = NULL;
struct multiboot *
copy_multiboot(
struct multiboot *mboot_ptr
) {
struct multiboot *new_header = (struct multiboot *)kmalloc(sizeof(struct multiboot));
memcpy(new_header, mboot_ptr, sizeof(struct multiboot));
return new_header;
}
void
dump_multiboot(
struct multiboot *mboot_ptr
) {
debug_print(INFO, "MULTIBOOT header at 0x%x:", (uintptr_t)mboot_ptr);
debug_print(INFO, "Flags : 0x%x", mboot_ptr->flags);
debug_print(INFO, "Mem Lo: 0x%x", mboot_ptr->mem_lower);
debug_print(INFO, "Mem Hi: 0x%x", mboot_ptr->mem_upper);
debug_print(INFO, "Boot d: 0x%x", mboot_ptr->boot_device);
debug_print(INFO, "cmdlin: 0x%x", mboot_ptr->cmdline);
debug_print(INFO, "Mods : 0x%x", mboot_ptr->mods_count);
debug_print(INFO, "Addr : 0x%x", mboot_ptr->mods_addr);
debug_print(INFO, "ELF n : 0x%x", mboot_ptr->num);
debug_print(INFO, "ELF s : 0x%x", mboot_ptr->size);
debug_print(INFO, "ELF a : 0x%x", mboot_ptr->addr);
debug_print(INFO, "ELF h : 0x%x", mboot_ptr->shndx);
debug_print(INFO, "MMap : 0x%x", mboot_ptr->mmap_length);
debug_print(INFO, "Addr : 0x%x", mboot_ptr->mmap_addr);
debug_print(INFO, "Drives: 0x%x", mboot_ptr->drives_length);
debug_print(INFO, "Addr : 0x%x", mboot_ptr->drives_addr);
debug_print(INFO, "Config: 0x%x", mboot_ptr->config_table);
debug_print(INFO, "Loader: 0x%x", mboot_ptr->boot_loader_name);
debug_print(INFO, "APM : 0x%x", mboot_ptr->apm_table);
debug_print(INFO, "VBE Co: 0x%x", mboot_ptr->vbe_control_info);
debug_print(INFO, "VBE Mo: 0x%x", mboot_ptr->vbe_mode_info);
debug_print(INFO, "VBE In: 0x%x", mboot_ptr->vbe_mode);
debug_print(INFO, "VBE se: 0x%x", mboot_ptr->vbe_interface_seg);
debug_print(INFO, "VBE of: 0x%x", mboot_ptr->vbe_interface_off);
debug_print(INFO, "VBE le: 0x%x", mboot_ptr->vbe_interface_len);
if (mboot_ptr->flags & (1 << 2)) {
debug_print(INFO, "Started with: %s", (char *)mboot_ptr->cmdline);
}
if (mboot_ptr->flags & (1 << 9)) {
debug_print(INFO, "Booted from: %s", (char *)mboot_ptr->boot_loader_name);
}
if (mboot_ptr->flags & (1 << 0)) {
debug_print(INFO, "%dkB lower memory", mboot_ptr->mem_lower);
int mem_mb = mboot_ptr->mem_upper / 1024;
debug_print(INFO, "%dkB higher memory (%dMB)", mboot_ptr->mem_upper, mem_mb);
}
if (mboot_ptr->flags & (1 << 3)) {
debug_print(INFO, "Found %d module(s).", mboot_ptr->mods_count);
if (mboot_ptr->mods_count > 0) {
uint32_t i;
for (i = 0; i < mboot_ptr->mods_count; ++i ) {
uint32_t module_start = *((uint32_t*)mboot_ptr->mods_addr + 8 * i);
uint32_t module_end = *(uint32_t*)(mboot_ptr->mods_addr + 8 * i + 4);
debug_print(INFO, "Module %d is at 0x%x:0x%x", i+1, module_start, module_end);
}
}
}
}

23
kernel/misc/tokenize.c Normal file
View File

@ -0,0 +1,23 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2013 Kevin Lange
*/
#include <system.h>
#include <logging.h>
#include <tokenize.h>
int tokenize(char * str, char * sep, char **buf) {
char * pch_i;
char * save_i;
int argc = 0;
pch_i = strtok_r(str,sep,&save_i);
if (!pch_i) { return 0; }
while (pch_i != NULL) {
buf[argc] = (char *)pch_i;
++argc;
pch_i = strtok_r(NULL,sep,&save_i);
}
buf[argc] = NULL;
return argc;
}

76
kernel/misc/ubsan.c Normal file
View File

@ -0,0 +1,76 @@
#include <system.h>
#include <types.h>
#include <va_list.h>
#include <fs.h>
#include <printf.h>
#include <ubsan.h>
#define EARLY_LOG_DEVICE 0x3F8
static uint32_t _ubsan_log_write(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer) {
for (unsigned int i = 0; i < size; ++i) {
outportb(EARLY_LOG_DEVICE, buffer[i]);
}
return size;
}
static fs_node_t _ubsan_log = { .write = &_ubsan_log_write };
void ubsan_debug(struct SourceLocation * location) {
fprintf(&_ubsan_log, "[ubsan] %s:%d:%dc - ", location->file_name, location->line, location->column);
}
void __ubsan_handle_add_overflow(struct OverflowData * data, unsigned long lhs, unsigned long rhs) {
ubsan_debug(&data->location);
fprintf(&_ubsan_log, "Overflow in add: %d %d\n", lhs, rhs);
}
void __ubsan_handle_sub_overflow(struct OverflowData * data, unsigned long lhs, unsigned long rhs) {
ubsan_debug(&data->location);
fprintf(&_ubsan_log, "Overflow in sub: %d %d\n", lhs, rhs);
}
void __ubsan_handle_mul_overflow(struct OverflowData * data, unsigned long lhs, unsigned long rhs) {
fprintf(&_ubsan_log, "Overflow in mul: %d %d\n", lhs, rhs);
}
void __ubsan_handle_divrem_overflow(struct OverflowData * data, unsigned long lhs, unsigned long rhs) {
ubsan_debug(&data->location);
fprintf(&_ubsan_log, "Overflow in divrem: %d %d\n", lhs, rhs);
}
void __ubsan_handle_negate_overflow(struct OverflowData * data, unsigned long old) {
ubsan_debug(&data->location);
fprintf(&_ubsan_log, "Overflow in negate: %d\n", old);
}
void __ubsan_handle_builtin_unreachable(struct UnreachableData * data) {
ubsan_debug(&data->location);
fprintf(&_ubsan_log, "called __builtin_unreachable()\n");
}
void __ubsan_handle_out_of_bounds(struct OutOfBoundsData * data, unsigned long index) {
ubsan_debug(&data->location);
fprintf(&_ubsan_log, "out of bounds array reference at %s[%d]\n", data->array_type->type_name, index);
}
void __ubsan_handle_shift_out_of_bounds(struct ShiftOutOfBoundsData * data, unsigned long lhs, unsigned long rhs) {
ubsan_debug(&data->location);
fprintf(&_ubsan_log, "shift is out of bounds: %d %d\n", lhs, rhs);
}
#define IS_ALIGNED(a, b) (((a) & ((__typeof__(a))(b)-1)) == 0)
void __ubsan_handle_type_mismatch(struct TypeMismatchData * data, unsigned long ptr) {
return;
ubsan_debug(&data->location);
if (data->alignment && !IS_ALIGNED(ptr, data->alignment)) {
fprintf(&_ubsan_log, "bad alignment in read at 0x%x (wanted %d)\n", ptr, data->alignment);
} else {
fprintf(&_ubsan_log, "type mismatch in reference at 0x%x\n", ptr);
}
}
void __ubsan_handle_vla_bound_not_positive(struct VLABoundData * data, unsigned long bound) {
ubsan_debug(&data->location);
fprintf(&_ubsan_log, "vla bound not positive: %d\n", bound);
}

57
kernel/spin.c Normal file
View File

@ -0,0 +1,57 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2015 Dale Weiler
*
* Spin locks with waiters
*
*/
#include <system.h>
static inline int arch_atomic_swap(volatile int * x, int v) {
asm("xchg %0, %1" : "=r"(v), "=m"(*x) : "0"(v) : "memory");
return v;
}
static inline void arch_atomic_store(volatile int * p, int x) {
asm("movl %1, %0" : "=m"(*p) : "r"(x) : "memory");
}
static inline void arch_atomic_inc(volatile int * x) {
asm("lock; incl %0" : "=m"(*x) : "m"(*x) : "memory");
}
static inline void arch_atomic_dec(volatile int * x) {
asm("lock; decl %0" : "=m"(*x) : "m"(*x) : "memory");
}
void spin_wait(volatile int * addr, volatile int * waiters) {
if (waiters) {
arch_atomic_inc(waiters);
}
while (*addr) {
switch_task(1);
}
if (waiters) {
arch_atomic_dec(waiters);
}
}
void spin_lock(spin_lock_t lock) {
while (arch_atomic_swap(lock, 1)) {
spin_wait(lock, lock+1);
}
}
void spin_init(spin_lock_t lock) {
lock[0] = 0;
lock[1] = 0;
}
void spin_unlock(spin_lock_t lock) {
if (lock[0]) {
arch_atomic_store(lock, 0);
if (lock[1])
switch_task(1);
}
}

2791
kernel/symbols.S Normal file

File diff suppressed because it is too large Load Diff

390
kernel/sys/module.c Normal file
View File

@ -0,0 +1,390 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2014 Kevin Lange
*/
#include <system.h>
#include <logging.h>
#include <fs.h>
#include <hashmap.h>
#include <elf.h>
#include <module.h>
#define SYMBOLTABLE_HASHMAP_SIZE 10
#define MODULE_HASHMAP_SIZE 10
static hashmap_t * symboltable = NULL;
static hashmap_t * modules = NULL;
extern char kernel_symbols_start[];
extern char kernel_symbols_end[];
typedef struct {
uintptr_t addr;
char name[];
} kernel_symbol_t;
/* Cannot use symboltable here because symbol_find is used during initialization
* of IRQs and ISRs.
*/
void (* symbol_find(const char * name))(void) {
kernel_symbol_t * k = (kernel_symbol_t *)&kernel_symbols_start;
while ((uintptr_t)k < (uintptr_t)&kernel_symbols_end) {
if (strcmp(k->name, name)) {
k = (kernel_symbol_t *)((uintptr_t)k + sizeof *k + strlen(k->name) + 1);
continue;
}
return (void (*)(void))k->addr;
}
return NULL;
}
int module_quickcheck(void * blob) {
Elf32_Header * target = (Elf32_Header *)blob;
char * head = (char *)blob;
if (target->e_ident[0] != ELFMAG0 ||
target->e_ident[1] != ELFMAG1 ||
target->e_ident[2] != ELFMAG2 ||
target->e_ident[3] != ELFMAG3) {
goto _maybe_pack;
}
if (target->e_type != ET_REL) {
goto _maybe_pack;
}
return 1;
_maybe_pack:
if (head[0] == 'P' && head[1] == 'A' && head[2] == 'C' && head[3] == 'K') {
return 2;
}
return 0;
}
void * module_load_direct(void * blob, size_t length) {
Elf32_Header * target = (Elf32_Header *)blob;
if (target->e_ident[0] != ELFMAG0 ||
target->e_ident[1] != ELFMAG1 ||
target->e_ident[2] != ELFMAG2 ||
target->e_ident[3] != ELFMAG3) {
debug_print(ERROR, "Module is not a valid ELF object.");
goto mod_load_error_unload;
}
char * shstrtab = NULL;
char * symstrtab = NULL;
Elf32_Shdr * sym_shdr = NULL;
char * deps = NULL;
size_t deps_length = 0;
/* TODO: Actually load the ELF somewhere! This is moronic, you're not initializing a BSS! */
/* (and maybe keep the elf header somewhere) */
{
unsigned int i = 0;
for (unsigned int x = 0; x < (unsigned int)target->e_shentsize * target->e_shnum; x += target->e_shentsize) {
Elf32_Shdr * shdr = (Elf32_Shdr *)((uintptr_t)target + (target->e_shoff + x));
if (i == target->e_shstrndx) {
shstrtab = (char *)((uintptr_t)target + shdr->sh_offset);
}
i++;
}
}
if (!shstrtab) {
debug_print(ERROR, "Could not locate module section header string table.");
goto mod_load_error_unload;
}
{
for (unsigned int x = 0; x < (unsigned int)target->e_shentsize * target->e_shnum; x += target->e_shentsize) {
Elf32_Shdr * shdr = (Elf32_Shdr *)((uintptr_t)target + (target->e_shoff + x));
if (shdr->sh_type == SHT_STRTAB && (!strcmp((char *)((uintptr_t)shstrtab + shdr->sh_name), ".strtab"))) {
symstrtab = (char *)((uintptr_t)target + shdr->sh_offset);
}
}
}
if (!shstrtab) {
debug_print(ERROR, "Could not locate module symbol string table.");
goto mod_load_error_unload;
}
{
debug_print(INFO, "Checking dependencies.");
for (unsigned int x = 0; x < (unsigned int)target->e_shentsize * target->e_shnum; x += target->e_shentsize) {
Elf32_Shdr * shdr = (Elf32_Shdr *)((uintptr_t)target + (target->e_shoff + x));
if ((!strcmp((char *)((uintptr_t)shstrtab + shdr->sh_name), "moddeps"))) {
deps = (char*)((Elf32_Addr)target + shdr->sh_offset);
deps_length = shdr->sh_size;
unsigned int i = 0;
while (i < deps_length) {
if (strlen(&deps[i]) && !hashmap_get(modules, &deps[i])) {
debug_print(ERROR, " %s - not loaded", &deps[i]);
goto mod_load_error_unload;
}
debug_print(INFO, " %s", &deps[i]);
i += strlen(&deps[i]) + 1;
}
}
}
}
{
for (unsigned int x = 0; x < (unsigned int)target->e_shentsize * target->e_shnum; x += target->e_shentsize) {
Elf32_Shdr * shdr = (Elf32_Shdr *)((uintptr_t)target + (target->e_shoff + x));
if (shdr->sh_type == SHT_SYMTAB) {
sym_shdr = shdr;
}
}
}
if (!sym_shdr) {
debug_print(ERROR, "Could not locate section for symbol table.");
goto mod_load_error_unload;
}
{
debug_print(INFO, "Loading sections.");
for (unsigned int x = 0; x < (unsigned int)target->e_shentsize * target->e_shnum; x += target->e_shentsize) {
Elf32_Shdr * shdr = (Elf32_Shdr *)((uintptr_t)target + (target->e_shoff + x));
if (shdr->sh_type == SHT_NOBITS) {
shdr->sh_addr = (Elf32_Addr)malloc(shdr->sh_size);
memset((void *)shdr->sh_addr, 0x00, shdr->sh_size);
} else {
shdr->sh_addr = (Elf32_Addr)target + shdr->sh_offset;
}
}
}
int undefined = 0;
hashmap_t * local_symbols = hashmap_create(10);
{
Elf32_Sym * table = (Elf32_Sym *)((uintptr_t)target + sym_shdr->sh_offset);
while ((uintptr_t)table - ((uintptr_t)target + sym_shdr->sh_offset) < sym_shdr->sh_size) {
if (table->st_name) {
if (ELF32_ST_BIND(table->st_info) == STB_GLOBAL) {
char * name = (char *)((uintptr_t)symstrtab + table->st_name);
if (table->st_shndx == 0) {
if (!hashmap_get(symboltable, name)) {
debug_print(ERROR, "Unresolved symbol in module: %s", name);
undefined = 1;
}
} else {
Elf32_Shdr * s = NULL;
{
int i = 0;
int set = 0;
for (unsigned int x = 0; x < (unsigned int)target->e_shentsize * target->e_shnum; x += target->e_shentsize) {
Elf32_Shdr * shdr = (Elf32_Shdr *)((uintptr_t)target + (target->e_shoff + x));
if (i == table->st_shndx) {
set = 1;
s = shdr;
break;
}
i++;
}
/*
* Common symbols
* If we were a proper linker, we'd look at a bunch of objects
* to find out if one of them defined this, but instead we have
* a strict hierarchy of symbol resolution, so we know that an
* undefined common symbol at this point should be immediately
* allocated and zeroed.
*/
if (!set && table->st_shndx == 65522) {
if (!hashmap_get(symboltable, name)) {
void * final = calloc(1, table->st_value);
debug_print(NOTICE, "point %s to 0x%x", name, (uintptr_t)final);
hashmap_set(symboltable, name, (void *)final);
hashmap_set(local_symbols, name, (void *)final);
}
}
}
if (s) {
uintptr_t final = s->sh_addr + table->st_value;
hashmap_set(symboltable, name, (void *)final);
hashmap_set(local_symbols, name, (void *)final);
}
}
}
}
table++;
}
}
if (undefined) {
debug_print(ERROR, "This module is faulty! Verify it specifies all of its");
debug_print(ERROR, "dependencies properly with MODULE_DEPENDS.");
goto mod_load_error;
}
{
for (unsigned int x = 0; x < (unsigned int)target->e_shentsize * target->e_shnum; x += target->e_shentsize) {
Elf32_Shdr * shdr = (Elf32_Shdr *)((uintptr_t)target + (target->e_shoff + x));
if (shdr->sh_type == SHT_REL) {
Elf32_Rel * section_rel = (void *)(shdr->sh_addr);
Elf32_Rel * table = section_rel;
Elf32_Sym * symtable = (Elf32_Sym *)(sym_shdr->sh_addr);
while ((uintptr_t)table - (shdr->sh_addr) < shdr->sh_size) {
Elf32_Sym * sym = &symtable[ELF32_R_SYM(table->r_info)];
Elf32_Shdr * rs = (Elf32_Shdr *)((uintptr_t)target + (target->e_shoff + shdr->sh_info * target->e_shentsize));
uintptr_t addend = 0;
uintptr_t place = 0;
uintptr_t symbol = 0;
uintptr_t *ptr = NULL;
if (ELF32_ST_TYPE(sym->st_info) == STT_SECTION) {
Elf32_Shdr * s = (Elf32_Shdr *)((uintptr_t)target + (target->e_shoff + sym->st_shndx * target->e_shentsize));
ptr = (uintptr_t *)(table->r_offset + rs->sh_addr);
addend = *ptr;
place = (uintptr_t)ptr;
symbol = s->sh_addr;
} else {
char * name = (char *)((uintptr_t)symstrtab + sym->st_name);
ptr = (uintptr_t *)(table->r_offset + rs->sh_addr);
addend = *ptr;
place = (uintptr_t)ptr;
if (!hashmap_get(symboltable, name)) {
debug_print(ERROR, "Wat? Missing symbol %s", name);
}
symbol = (uintptr_t)hashmap_get(symboltable, name);
}
switch (ELF32_R_TYPE(table->r_info)) {
case 1:
*ptr = addend + symbol;
break;
case 2:
*ptr = addend + symbol - place;
break;
default:
debug_print(ERROR, "Unsupported relocation type: %d", ELF32_R_TYPE(table->r_info));
goto mod_load_error;
}
table++;
}
}
}
}
debug_print(INFO, "Locating module information...");
module_defs * mod_info = NULL;
list_t * hash_keys = hashmap_keys(local_symbols);
foreach(_key, hash_keys) {
char * key = (char *)_key->value;
if (startswith(key, "module_info_")) {
mod_info = hashmap_get(local_symbols, key);
}
}
list_free(hash_keys);
free(hash_keys);
if (!mod_info) {
debug_print(ERROR, "Failed to locate module information structure!");
goto mod_load_error;
}
mod_info->initialize();
debug_print(NOTICE, "Finished loading module %s", mod_info->name);
/* We don't do this anymore
* TODO: Do this in the module unload function
hashmap_free(local_symbols);
free(local_symbols);
*/
module_data_t * mod_data = malloc(sizeof(module_data_t));
mod_data->mod_info = mod_info;
mod_data->bin_data = target;
mod_data->symbols = local_symbols;
mod_data->end = (uintptr_t)target + length;
mod_data->deps = deps;
mod_data->deps_length = deps_length;
hashmap_set(modules, mod_info->name, (void *)mod_data);
return mod_data;
mod_load_error_unload:
return (void *)-1;
mod_load_error:
return NULL;
}
/**
* Install a module from a file and return
* a pointer to its module_info structure.
*/
void * module_load(char * filename) {
fs_node_t * file = kopen(filename, 0);
if (!file) {
debug_print(ERROR, "Failed to load module: %s", filename);
return NULL;
}
debug_print(NOTICE, "Attempting to load kernel module: %s", filename);
void * blob = (void *)kvmalloc(file->length);
read_fs(file, 0, file->length, (uint8_t *)blob);
void * result = module_load_direct(blob, file->length);
if (result == (void *)-1) {
debug_print(ERROR, "Error loading module.");
free(blob);
result = NULL;
}
close_fs(file);
return result;
}
/**
* Remove a loaded module.
*/
void module_unload(char * name) {
/* XXX: Lookup the module by name and verify it has no dependencies loaded. */
/* XXX: Call module_info->finish() */
/* XXX: Unmap symbols defined the module that weren't otherwise defined. */
/* XXX: Deallocate the regions the module was mapped into */
}
void modules_install(void) {
/* Initialize the symboltable, we use a hashmap of symbols to addresses */
symboltable = hashmap_create(SYMBOLTABLE_HASHMAP_SIZE);
/* Load all of the kernel symbols into the symboltable */
kernel_symbol_t * k = (kernel_symbol_t *)&kernel_symbols_start;
while ((uintptr_t)k < (uintptr_t)&kernel_symbols_end) {
hashmap_set(symboltable, k->name, (void *)k->addr);
k = (kernel_symbol_t *)((uintptr_t)k + sizeof(kernel_symbol_t) + strlen(k->name) + 1);
}
/* Also add the kernel_symbol_start and kernel_symbol_end (these were excluded from the generator) */
hashmap_set(symboltable, "kernel_symbols_start", &kernel_symbols_start);
hashmap_set(symboltable, "kernel_symbols_end", &kernel_symbols_end);
/* Initialize the module name -> object hashmap */
modules = hashmap_create(MODULE_HASHMAP_SIZE);
}
/* Accessors. */
hashmap_t * modules_get_list(void) {
return modules;
}
hashmap_t * modules_get_symbols(void) {
return symboltable;
}

103
kernel/sys/panic.c Normal file
View File

@ -0,0 +1,103 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2011-2014 Kevin Lange
*
* Panic functions
*/
#include <system.h>
#include <logging.h>
#include <printf.h>
#include <module.h>
void halt_and_catch_fire(char * error_message, const char * file, int line, struct regs * regs) {
IRQ_OFF;
debug_print(ERROR, "HACF: %s", error_message);
debug_print(ERROR, "Proc: %d", getpid());
debug_print(ERROR, "File: %s", file);
debug_print(ERROR, "Line: %d", line);
if (regs) {
debug_print(ERROR, "Registers at interrupt:");
debug_print(ERROR, "eax=0x%x ebx=0x%x", regs->eax, regs->ebx);
debug_print(ERROR, "ecx=0x%x edx=0x%x", regs->ecx, regs->edx);
debug_print(ERROR, "esp=0x%x ebp=0x%x", regs->esp, regs->ebp);
debug_print(ERROR, "Error code: 0x%x", regs->err_code);
debug_print(ERROR, "EFLAGS: 0x%x", regs->eflags);
debug_print(ERROR, "User ESP: 0x%x", regs->useresp);
debug_print(ERROR, "eip=0x%x", regs->eip);
}
debug_print(ERROR, "This process has been descheduled.");
kexit(1);
}
char * probable_function_name(uintptr_t ip, uintptr_t * out_addr) {
char * closest = NULL;
size_t distance = 0xFFFFFFFF;
uintptr_t addr = 0;
if (modules_get_symbols()) {
list_t * hash_keys = hashmap_keys(modules_get_symbols());
foreach(_key, hash_keys) {
char * key = (char *)_key->value;
uintptr_t a = (uintptr_t)hashmap_get(modules_get_symbols(), key);
if (!a) continue;
size_t d = 0xFFFFFFFF;
if (a <= ip) {
d = ip - a;
}
if (d < distance) {
closest = key;
distance = d;
addr = a;
}
}
free(hash_keys);
}
*out_addr = addr;
return closest;
}
void assert_failed(const char *file, uint32_t line, const char *desc) {
IRQ_OFF;
debug_print(INSANE, "Kernel Assertion Failed: %s", desc);
debug_print(INSANE, "File: %s", file);
debug_print(INSANE, "Line: %d", line);
debug_print(INSANE, "System Halted!");
#if 1
unsigned int * ebp = (unsigned int *)(&file - 2);
debug_print(INSANE, "Stack trace:");
for (unsigned int frame = 0; frame < 20; ++frame) {
unsigned int eip = ebp[1];
if (eip == 0) break;
ebp = (unsigned int *)(ebp[0]);
unsigned int * args = &ebp[2];
(void)args;
uintptr_t addr;
char * func = probable_function_name(eip, &addr);
debug_print(INSANE, " 0x%x (%s+%d)\n", eip, func, eip-addr);
}
#endif
if (debug_video_crash) {
char msg[4][256];
sprintf(msg[0], "Kernel Assertion Failed: %s", desc);
sprintf(msg[1], "File: %s", file);
sprintf(msg[2], "Line: %d", line);
sprintf(msg[3], "System Halted!");
char * msgs[] = {msg[0], msg[1], msg[2], msg[3], NULL};
debug_video_crash(msgs);
}
while (1) {
IRQ_OFF;
PAUSE;
}
}

963
kernel/sys/process.c Normal file
View File

@ -0,0 +1,963 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2011-2014 Kevin Lange
* Copyright (C) 2012 Markus Schober
* Copyright (C) 2015 Dale Weiler
*
* Processes
*
* Internal format format for a process and functions to spawn
* new processes and manage the process tree.
*/
#include <system.h>
#include <process.h>
#include <tree.h>
#include <list.h>
#include <bitset.h>
#include <logging.h>
#include <shm.h>
#include <printf.h>
tree_t * process_tree; /* Parent->Children tree */
list_t * process_list; /* Flat storage */
list_t * process_queue; /* Ready queue */
list_t * sleep_queue;
volatile process_t * current_process = NULL;
process_t * kernel_idle_task = NULL;
static spin_lock_t tree_lock = { 0 };
static spin_lock_t process_queue_lock = { 0 };
static spin_lock_t wait_lock_tmp = { 0 };
static spin_lock_t sleep_lock = { 0 };
static bitset_t pid_set;
/* Default process name string */
char * default_name = "[unnamed]";
int is_valid_process(process_t * process) {
foreach(lnode, process_list) {
if (lnode->value == process) {
return 1;
}
}
return 0;
}
/*
* This makes a nice 4096-byte bitmap. It also happens
* to be pid_max on 32-bit Linux, so that's kinda nice.
*/
#define MAX_PID 32768
/*
* Initialize the process tree and ready queue.
*/
void initialize_process_tree(void) {
process_tree = tree_create();
process_list = list_create();
process_queue = list_create();
sleep_queue = list_create();
/* Start off with enough bits for 64 processes */
bitset_init(&pid_set, MAX_PID / 8);
/* First two bits are set by default */
bitset_set(&pid_set, 0);
bitset_set(&pid_set, 1);
}
/*
* Recursively print a process node to the console.
*
* @param node Node to print.
* @param height Current depth in the tree.
*/
void debug_print_process_tree_node(tree_node_t * node, size_t height) {
/* End recursion on a blank entry */
if (!node) return;
char * tmp = malloc(512);
memset(tmp, 0, 512);
char * c = tmp;
/* Indent output */
for (uint32_t i = 0; i < height; ++i) {
c += sprintf(c, " ");
}
/* Get the current process */
process_t * proc = (process_t *)node->value;
/* Print the process name */
c += sprintf(c, "%d.%d %s", proc->group ? proc->group : proc->id, proc->id, proc->name);
if (proc->description) {
/* And, if it has one, its description */
c += sprintf(c, " %s", proc->description);
}
if (proc->finished) {
c += sprintf(c, " [zombie]");
}
/* Linefeed */
debug_print(NOTICE, "%s", tmp);
free(tmp);
foreach(child, node->children) {
/* Recursively print the children */
debug_print_process_tree_node(child->value, height + 1);
}
}
/*
* Print the process tree to the console.
*/
void debug_print_process_tree(void) {
debug_print_process_tree_node(process_tree->root, 0);
}
/*
* Retreive the next ready process.
* XXX: POPs from the ready queue!
*
* @return A pointer to the next process in the queue.
*/
process_t * next_ready_process(void) {
if (!process_available()) {
return kernel_idle_task;
}
if (process_queue->head->owner != process_queue) {
debug_print(ERROR, "Erroneous process located in process queue: node 0x%x has owner 0x%x, but process_queue is 0x%x", process_queue->head, process_queue->head->owner, process_queue);
process_t * proc = process_queue->head->value;
debug_print(ERROR, "PID associated with this node is %d", proc->id);
}
node_t * np = list_dequeue(process_queue);
assert(np && "Ready queue is empty.");
process_t * next = np->value;
return next;
}
/*
* Reinsert a process into the ready queue.
*
* @param proc Process to reinsert
*/
void make_process_ready(process_t * proc) {
if (proc->sleep_node.owner != NULL) {
if (proc->sleep_node.owner == sleep_queue) {
/* XXX can't wake from timed sleep */
if (proc->timed_sleep_node) {
IRQ_OFF;
spin_lock(sleep_lock);
list_delete(sleep_queue, proc->timed_sleep_node);
spin_unlock(sleep_lock);
IRQ_RES;
proc->sleep_node.owner = NULL;
free(proc->timed_sleep_node->value);
}
/* Else: I have no idea what happened. */
} else {
proc->sleep_interrupted = 1;
spin_lock(wait_lock_tmp);
list_delete((list_t*)proc->sleep_node.owner, &proc->sleep_node);
spin_unlock(wait_lock_tmp);
}
}
if (proc->sched_node.owner) {
debug_print(WARNING, "Can't make process ready without removing from owner list: %d", proc->id);
debug_print(WARNING, " (This is a bug) Current owner list is 0x%x (ready queue is 0x%x)", proc->sched_node.owner, process_queue);
return;
}
spin_lock(process_queue_lock);
list_append(process_queue, &proc->sched_node);
spin_unlock(process_queue_lock);
}
extern void tree_remove_reparent_root(tree_t * tree, tree_node_t * node);
/*
* Delete a process from the process tree
*
* @param proc Process to find and remove.
*/
void delete_process(process_t * proc) {
tree_node_t * entry = proc->tree_entry;
/* The process must exist in the tree, or the client is at fault */
if (!entry) return;
/* We can not remove the root, which is an error anyway */
assert((entry != process_tree->root) && "Attempted to kill init.");
if (process_tree->root == entry) {
/* We are init, don't even bother. */
return;
}
/* Remove the entry. */
spin_lock(tree_lock);
/* Reparent everyone below me to init */
int has_children = entry->children->length;
tree_remove_reparent_root(process_tree, entry);
list_delete(process_list, list_find(process_list, proc));
spin_unlock(tree_lock);
if (has_children) {
process_t * init = process_tree->root->value;
wakeup_queue(init->wait_queue);
}
bitset_clear(&pid_set, proc->id);
/* Uh... */
free(proc);
}
static void _kidle(void) {
while (1) {
IRQ_ON;
PAUSE;
}
}
/*
* Spawn the idle "process".
*/
process_t * spawn_kidle(void) {
process_t * idle = malloc(sizeof(process_t));
memset(idle, 0x00, sizeof(process_t));
idle->id = -1;
idle->name = strdup("[kidle]");
idle->is_tasklet = 1;
idle->image.stack = (uintptr_t)malloc(KERNEL_STACK_SIZE) + KERNEL_STACK_SIZE;
idle->thread.eip = (uintptr_t)&_kidle;
idle->thread.esp = idle->image.stack;
idle->thread.ebp = idle->image.stack;
idle->started = 1;
idle->running = 1;
idle->wait_queue = list_create();
idle->shm_mappings = list_create();
idle->signal_queue = list_create();
gettimeofday(&idle->start, NULL);
set_process_environment(idle, current_directory);
return idle;
}
/*
* Spawn the initial process.
*
* @return A pointer to the new initial process entry
*/
process_t * spawn_init(void) {
/* We can only do this once. */
assert((!process_tree->root) && "Tried to regenerate init!");
/* Allocate space for a new process */
process_t * init = malloc(sizeof(process_t));
/* Set it as the root process */
tree_set_root(process_tree, (void *)init);
/* Set its tree entry pointer so we can keep track
* of the process' entry in the process tree. */
init->tree_entry = process_tree->root;
init->id = 1; /* Init is PID 1 */
init->group = 0;
init->name = strdup("init"); /* Um, duh. */
init->cmdline = NULL;
init->user = 0; /* UID 0 */
init->mask = 022; /* umask */
init->group = 0; /* Task group 0 */
init->status = 0; /* Run status */
init->fds = malloc(sizeof(fd_table_t));
init->fds->refs = 1;
init->fds->length = 0; /* Initialize the file descriptors */
init->fds->capacity = 4;
init->fds->entries = malloc(sizeof(fs_node_t *) * init->fds->capacity);
/* Set the working directory */
init->wd_node = clone_fs(fs_root);
init->wd_name = strdup("/");
/* Heap and stack pointers (and actuals) */
init->image.entry = 0;
init->image.heap = 0;
init->image.heap_actual = 0;
init->image.stack = initial_esp + 1;
init->image.user_stack = 0;
init->image.size = 0;
init->image.shm_heap = SHM_START; /* Yeah, a bit of a hack. */
spin_init(init->image.lock);
/* Process is not finished */
init->finished = 0;
init->started = 1;
init->running = 1;
init->wait_queue = list_create();
init->shm_mappings = list_create();
init->signal_queue = list_create();
init->signal_kstack = NULL; /* None yet initialized */
init->sched_node.prev = NULL;
init->sched_node.next = NULL;
init->sched_node.value = init;
init->sleep_node.prev = NULL;
init->sleep_node.next = NULL;
init->sleep_node.value = init;
init->timed_sleep_node = NULL;
init->is_tasklet = 0;
set_process_environment(init, current_directory);
/* What the hey, let's also set the description on this one */
init->description = strdup("[init]");
list_insert(process_list, (void *)init);
return init;
}
/*
* Get the next available PID
*
* @return A usable PID for a new process.
*/
static int _next_pid = 2;
pid_t get_next_pid(void) {
if (_next_pid > MAX_PID) {
int index = bitset_ffub(&pid_set);
/*
* Honestly, we don't have the memory to really risk reaching
* the point where we have MAX_PID processes running
* concurrently, so this assertion should be "safe enough".
*/
assert(index != -1);
bitset_set(&pid_set, index);
return index;
}
int pid = _next_pid;
_next_pid++;
assert(!bitset_test(&pid_set, pid) && "Next PID already allocated?");
bitset_set(&pid_set, pid);
return pid;
}
/*
* Disown a process from its parent.
*/
void process_disown(process_t * proc) {
assert(process_tree->root && "No init, has the process tree been initialized?");
/* Find the process in the tree */
tree_node_t * entry = proc->tree_entry;
/* Break it of from its current parent */
spin_lock(tree_lock);
tree_break_off(process_tree, entry);
/* And insert it back elsewhere */
tree_node_insert_child_node(process_tree, process_tree->root, entry);
spin_unlock(tree_lock);
}
/*
* Spawn a new process.
*
* @param parent The parent process to spawn the new one off of.
* @return A pointer to the new process.
*/
process_t * spawn_process(volatile process_t * parent, int reuse_fds) {
assert(process_tree->root && "Attempted to spawn a process without init.");
/* Allocate a new process */
debug_print(INFO," process_t {");
process_t * proc = calloc(sizeof(process_t),1);
debug_print(INFO," }");
proc->id = get_next_pid(); /* Set its PID */
proc->group = proc->id; /* Set the GID */
proc->name = strdup(parent->name); /* Use the default name */
proc->description = NULL; /* No description */
proc->cmdline = parent->cmdline;
/* Copy permissions */
proc->user = parent->user;
proc->mask = parent->mask;
/* XXX this is wrong? */
proc->group = parent->group;
/* Zero out the ESP/EBP/EIP */
proc->thread.esp = 0;
proc->thread.ebp = 0;
proc->thread.eip = 0;
proc->thread.fpu_enabled = 0;
memcpy((void*)proc->thread.fp_regs, (void*)parent->thread.fp_regs, 512);
/* Set the process image information from the parent */
proc->image.entry = parent->image.entry;
proc->image.heap = parent->image.heap;
proc->image.heap_actual = parent->image.heap_actual;
proc->image.size = parent->image.size;
debug_print(INFO," stack {");
proc->image.stack = (uintptr_t)kvmalloc(KERNEL_STACK_SIZE) + KERNEL_STACK_SIZE;
debug_print(INFO," }");
proc->image.user_stack = parent->image.user_stack;
proc->image.shm_heap = SHM_START; /* Yeah, a bit of a hack. */
spin_init(proc->image.lock);
assert(proc->image.stack && "Failed to allocate kernel stack for new process.");
/* Clone the file descriptors from the original process */
if (reuse_fds) {
proc->fds = parent->fds;
proc->fds->refs++;
} else {
proc->fds = malloc(sizeof(fd_table_t));
proc->fds->refs = 1;
proc->fds->length = parent->fds->length;
proc->fds->capacity = parent->fds->capacity;
debug_print(INFO," fds / files {");
proc->fds->entries = malloc(sizeof(fs_node_t *) * proc->fds->capacity);
assert(proc->fds->entries && "Failed to allocate file descriptor table for new process.");
debug_print(INFO," ---");
for (uint32_t i = 0; i < parent->fds->length; ++i) {
proc->fds->entries[i] = clone_fs(parent->fds->entries[i]);
}
debug_print(INFO," }");
}
/* As well as the working directory */
proc->wd_node = clone_fs(parent->wd_node);
proc->wd_name = strdup(parent->wd_name);
/* Zero out the process status */
proc->status = 0;
proc->finished = 0;
proc->started = 0;
proc->running = 0;
memset(proc->signals.functions, 0x00, sizeof(uintptr_t) * NUMSIGNALS);
proc->wait_queue = list_create();
proc->shm_mappings = list_create();
proc->signal_queue = list_create();
proc->signal_kstack = NULL; /* None yet initialized */
proc->sched_node.prev = NULL;
proc->sched_node.next = NULL;
proc->sched_node.value = proc;
proc->sleep_node.prev = NULL;
proc->sleep_node.next = NULL;
proc->sleep_node.value = proc;
proc->timed_sleep_node = NULL;
proc->is_tasklet = 0;
gettimeofday(&proc->start, NULL);
/* Insert the process into the process tree as a child
* of the parent process. */
tree_node_t * entry = tree_node_create(proc);
assert(entry && "Failed to allocate a process tree node for new process.");
proc->tree_entry = entry;
spin_lock(tree_lock);
tree_node_insert_child_node(process_tree, parent->tree_entry, entry);
list_insert(process_list, (void *)proc);
spin_unlock(tree_lock);
/* Return the new process */
return proc;
}
uint8_t process_compare(void * proc_v, void * pid_v) {
pid_t pid = (*(pid_t *)pid_v);
process_t * proc = (process_t *)proc_v;
return (uint8_t)(proc->id == pid);
}
process_t * process_from_pid(pid_t pid) {
if (pid < 0) return NULL;
spin_lock(tree_lock);
tree_node_t * entry = tree_find(process_tree,&pid,process_compare);
spin_unlock(tree_lock);
if (entry) {
return (process_t *)entry->value;
}
return NULL;
}
process_t * process_get_parent(process_t * process) {
process_t * result = NULL;
spin_lock(tree_lock);
tree_node_t * entry = process->tree_entry;
if (entry->parent) {
result = entry->parent->value;
}
spin_unlock(tree_lock);
return result;
}
/*
* Wait for children.
*
* @param process Process doing the waiting.
* @param pid PID to wait for
* @param status [out] Where to put the status conditions of the waited-for process
* @param options Options (unused)
* @return A pointer to the process that broke the wait
*/
process_t * process_wait(process_t * process, pid_t pid, int * status, int options) {
/* `options` is ignored */
if (pid == -1) {
/* wait for any child process */
} else if (pid < 0) {
/* wait for any porcess whose ->group == processes[abs(pid)]->group */
} else if (pid == 0) {
/* wait for any process whose ->group == process->group */
} else {
/* wait for processes[pid] */
}
return NULL;
}
/*
* Wake up a sleeping process
*
* @param process Process to wake up
* @param caller Who woke it up
* @return Don't know yet, but I think it should return something.
*/
int process_wake(process_t * process, process_t * caller) {
return 0;
}
/*
* Set the directory for a process.
*
* @param proc Process to set the directory for.
* @param directory Directory to set.
*/
void set_process_environment(process_t * proc, page_directory_t * directory) {
assert(proc);
assert(directory);
proc->thread.page_directory = directory;
}
/*
* Are there any processes available in the queue?
* (Queue not empty)
*
* @return 1 if there are processes available, 0 otherwise
*/
uint8_t process_available(void) {
return (process_queue->head != NULL);
}
/*
* Append a file descriptor to a process.
*
* @param proc Process to append to
* @param node The VFS node
* @return The actual fd, for use in userspace
*/
uint32_t process_append_fd(process_t * proc, fs_node_t * node) {
/* Fill gaps */
for (unsigned int i = 0; i < proc->fds->length; ++i) {
if (!proc->fds->entries[i]) {
proc->fds->entries[i] = node;
return i;
}
}
/* No gaps, expand */
if (proc->fds->length == proc->fds->capacity) {
proc->fds->capacity *= 2;
proc->fds->entries = realloc(proc->fds->entries, sizeof(fs_node_t *) * proc->fds->capacity);
}
proc->fds->entries[proc->fds->length] = node;
proc->fds->length++;
return proc->fds->length-1;
}
/*
* dup2() -> Move the file pointed to by `s(ou)rc(e)` into
* the slot pointed to be `dest(ination)`.
*
* @param proc Process to do this for
* @param src Source file descriptor
* @param dest Destination file descriptor
* @return The destination file descriptor, -1 on failure
*/
uint32_t process_move_fd(process_t * proc, int src, int dest) {
if ((size_t)src > proc->fds->length || (dest != -1 && (size_t)dest > proc->fds->length)) {
return -1;
}
if (dest == -1) {
dest = process_append_fd(proc, NULL);
}
if (proc->fds->entries[dest] != proc->fds->entries[src]) {
close_fs(proc->fds->entries[dest]);
proc->fds->entries[dest] = proc->fds->entries[src];
open_fs(proc->fds->entries[dest], 0);
}
return dest;
}
int wakeup_queue(list_t * queue) {
int awoken_processes = 0;
while (queue->length > 0) {
spin_lock(wait_lock_tmp);
node_t * node = list_pop(queue);
spin_unlock(wait_lock_tmp);
if (!((process_t *)node->value)->finished) {
make_process_ready(node->value);
}
awoken_processes++;
}
return awoken_processes;
}
int wakeup_queue_interrupted(list_t * queue) {
int awoken_processes = 0;
while (queue->length > 0) {
spin_lock(wait_lock_tmp);
node_t * node = list_pop(queue);
spin_unlock(wait_lock_tmp);
if (!((process_t *)node->value)->finished) {
process_t * proc = node->value;
proc->sleep_interrupted = 1;
make_process_ready(proc);
}
awoken_processes++;
}
return awoken_processes;
}
int sleep_on(list_t * queue) {
if (current_process->sleep_node.owner) {
/* uh, we can't sleep right now, we're marked as ready */
switch_task(0);
return 0;
}
current_process->sleep_interrupted = 0;
spin_lock(wait_lock_tmp);
list_append(queue, (node_t *)&current_process->sleep_node);
spin_unlock(wait_lock_tmp);
switch_task(0);
return current_process->sleep_interrupted;
}
int process_is_ready(process_t * proc) {
return (proc->sched_node.owner != NULL);
}
void wakeup_sleepers(unsigned long seconds, unsigned long subseconds) {
IRQ_OFF;
spin_lock(sleep_lock);
if (sleep_queue->length) {
sleeper_t * proc = ((sleeper_t *)sleep_queue->head->value);
while (proc && (proc->end_tick < seconds || (proc->end_tick == seconds && proc->end_subtick <= subseconds))) {
if (proc->is_fswait) {
proc->is_fswait = -1;
process_alert_node(proc->process,proc);
} else {
process_t * process = proc->process;
process->sleep_node.owner = NULL;
process->timed_sleep_node = NULL;
if (!process_is_ready(process)) {
make_process_ready(process);
}
}
free(proc);
free(list_dequeue(sleep_queue));
if (sleep_queue->length) {
proc = ((sleeper_t *)sleep_queue->head->value);
} else {
break;
}
}
}
spin_unlock(sleep_lock);
IRQ_RES;
}
void sleep_until(process_t * process, unsigned long seconds, unsigned long subseconds) {
if (current_process->sleep_node.owner) {
/* Can't sleep, sleeping already */
return;
}
process->sleep_node.owner = sleep_queue;
IRQ_OFF;
spin_lock(sleep_lock);
node_t * before = NULL;
foreach(node, sleep_queue) {
sleeper_t * candidate = ((sleeper_t *)node->value);
if (candidate->end_tick > seconds || (candidate->end_tick == seconds && candidate->end_subtick > subseconds)) {
break;
}
before = node;
}
sleeper_t * proc = malloc(sizeof(sleeper_t));
proc->process = process;
proc->end_tick = seconds;
proc->end_subtick = subseconds;
proc->is_fswait = 0;
process->timed_sleep_node = list_insert_after(sleep_queue, before, proc);
spin_unlock(sleep_lock);
IRQ_RES;
}
void cleanup_process(process_t * proc, int retval) {
proc->status = retval;
proc->finished = 1;
list_free(proc->wait_queue);
free(proc->wait_queue);
list_free(proc->signal_queue);
free(proc->signal_queue);
free(proc->wd_name);
if (proc->node_waits) {
list_free(proc->node_waits);
free(proc->node_waits);
proc->node_waits = NULL;
}
debug_print(INFO, "Releasing shared memory for %d", proc->id);
shm_release_all(proc);
free(proc->shm_mappings);
debug_print(INFO, "Freeing more mems %d", proc->id);
if (proc->signal_kstack) {
free(proc->signal_kstack);
}
release_directory(proc->thread.page_directory);
debug_print(INFO, "Dec'ing fds for %d", proc->id);
proc->fds->refs--;
if (proc->fds->refs == 0) {
debug_print(INFO, "Reached 0, all dependencies are closed for %d's file descriptors and page directories", proc->id);
debug_print(INFO, "Going to clear out the file descriptors %d", proc->id);
for (uint32_t i = 0; i < proc->fds->length; ++i) {
if (proc->fds->entries[i]) {
close_fs(proc->fds->entries[i]);
proc->fds->entries[i] = NULL;
}
}
debug_print(INFO, "... and their storage %d", proc->id);
free(proc->fds->entries);
free(proc->fds);
debug_print(INFO, "... and the kernel stack (hope this ain't us) %d", proc->id);
free((void *)(proc->image.stack - KERNEL_STACK_SIZE));
}
}
void reap_process(process_t * proc) {
debug_print(INFO, "Reaping process %d; mem before = %d", proc->id, memory_use());
free(proc->name);
debug_print(INFO, "Reaped process %d; mem after = %d", proc->id, memory_use());
delete_process(proc);
debug_print_process_tree();
}
static int wait_candidate(process_t * parent, int pid, int options, process_t * proc) {
(void)options; /* there is only one option that affects candidacy, and we don't support it yet */
if (!proc) return 0;
if (pid < -1) {
if (proc->group == -pid || proc->id == -pid) return 1;
} else if (pid == 0) {
/* Matches our group ID */
if (proc->group == parent->id) return 1;
} else if (pid > 0) {
/* Specific pid */
if (proc->id == pid) return 1;
} else {
return 1;
}
return 0;
}
int waitpid(int pid, int * status, int options) {
process_t * proc = (process_t *)current_process;
if (proc->group) {
proc = process_from_pid(proc->group);
}
debug_print(INFO, "waitpid(%s%d, ..., %d) (from pid=%d.%d)", (pid >= 0) ? "" : "-", (pid >= 0) ? pid : -pid, options, current_process->id, current_process->group);
do {
process_t * candidate = NULL;
int has_children = 0;
/* First, find out if there is anyone to reap */
foreach(node, proc->tree_entry->children) {
if (!node->value) {
continue;
}
process_t * child = ((tree_node_t *)node->value)->value;
if (wait_candidate(proc, pid, options, child)) {
has_children = 1;
if (child->finished) {
candidate = child;
break;
}
}
}
if (!has_children) {
/* No valid children matching this description */
debug_print(INFO, "No children matching description.");
return -ECHILD;
}
if (candidate) {
debug_print(INFO, "Candidate found (%x:%d), bailing early.", candidate, candidate->id);
if (status) {
*status = candidate->status;
}
int pid = candidate->id;
reap_process(candidate);
return pid;
} else {
if (options & 1) {
return 0;
}
debug_print(INFO, "Sleeping until queue is done.");
/* Wait */
if (sleep_on(proc->wait_queue) != 0) {
debug_print(INFO, "wait() was interrupted");
return -EINTR;
}
}
} while (1);
}
int process_wait_nodes(process_t * process,fs_node_t * nodes[], int timeout) {
assert(!process->node_waits && "Tried to wait on nodes while already waiting on nodes.");
fs_node_t ** n = nodes;
int index = 0;
if (*n) {
do {
int result = selectcheck_fs(*n);
if (result < 0) {
debug_print(NOTICE, "An invalid descriptor was specified: %d (0x%x) (pid=%d)", index, *n, current_process->id);
return -1;
}
if (result == 0) {
return index;
}
n++;
index++;
} while (*n);
}
if (timeout == 0) {
return -2;
}
n = nodes;
process->node_waits = list_create();
if (*n) {
do {
if (selectwait_fs(*n, process) < 0) {
debug_print(NOTICE, "Bad selectwait? 0x%x", *n);
}
n++;
} while (*n);
}
if (timeout > 0) {
debug_print(INFO, "fswait with a timeout of %d (pid=%d)", timeout, current_process->id);
unsigned long s, ss;
relative_time(0, timeout, &s, &ss);
IRQ_OFF;
spin_lock(sleep_lock);
node_t * before = NULL;
foreach(node, sleep_queue) {
sleeper_t * candidate = ((sleeper_t *)node->value);
if (candidate->end_tick > s || (candidate->end_tick == s && candidate->end_subtick > ss)) {
break;
}
before = node;
}
sleeper_t * proc = malloc(sizeof(sleeper_t));
proc->process = process;
proc->end_tick = s;
proc->end_subtick = ss;
proc->is_fswait = 1;
list_insert(((process_t *)process)->node_waits, proc);
process->timeout_node = list_insert_after(sleep_queue, before, proc);
spin_unlock(sleep_lock);
IRQ_RES;
} else {
process->timeout_node = NULL;
}
process->awoken_index = -1;
/* Wait. */
switch_task(0);
return process->awoken_index;
}
int process_awaken_from_fswait(process_t * process, int index) {
process->awoken_index = index;
list_free(process->node_waits);
free(process->node_waits);
process->node_waits = NULL;
if (process->timeout_node && process->timeout_node->owner == sleep_queue) {
sleeper_t * proc = process->timeout_node->value;
if (proc->is_fswait != -1) {
list_delete(sleep_queue, process->timeout_node);
free(process->timeout_node->value);
free(process->timeout_node);
}
}
process->timeout_node = NULL;
make_process_ready(process);
return 0;
}
int process_alert_node(process_t * process, void * value) {
if (!is_valid_process(process)) {
debug_print(WARNING, "Invalid process in alert from fswait.");
return 0;
}
if (!process->node_waits) {
return 0; /* Possibly already returned. Wait for another call. */
}
int index = 0;
foreach(node, process->node_waits) {
if (value == node->value) {
return process_awaken_from_fswait(process, index);
}
index++;
}
return -1;
}

227
kernel/sys/signal.c Normal file
View File

@ -0,0 +1,227 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2012-2014 Kevin Lange
*
* Signal Handling
*/
#include <system.h>
#include <signal.h>
#include <logging.h>
void enter_signal_handler(uintptr_t location, int signum, uintptr_t stack) {
IRQ_OFF;
asm volatile(
"mov %2, %%esp\n"
"pushl %1\n" /* argument count */
"pushl $" STRSTR(SIGNAL_RETURN) "\n"
"mov $0x23, %%ax\n" /* Segment selector */
"mov %%ax, %%ds\n"
"mov %%ax, %%es\n"
"mov %%ax, %%fs\n"
"mov %%ax, %%gs\n"
"mov %%esp, %%eax\n" /* Stack -> EAX */
"pushl $0x23\n" /* Segment selector again */
"pushl %%eax\n"
"pushf\n" /* Push flags */
"popl %%eax\n" /* Fix the Interrupt flag */
"orl $0x200, %%eax\n"
"pushl %%eax\n"
"pushl $0x1B\n"
"pushl %0\n" /* Push the entry point */
"iret\n"
: : "m"(location), "m"(signum), "r"(stack) : "%ax", "%esp", "%eax");
debug_print(CRITICAL, "Failed to jump to signal handler!");
}
static spin_lock_t sig_lock;
static spin_lock_t sig_lock_b;
char isdeadly[] = {
0, /* 0? */
1, /* SIGHUP */
1, /* SIGINT */
2, /* SIGQUIT */
2, /* SIGILL */
2, /* SIGTRAP */
2, /* SIGABRT */
2, /* SIGEMT */
2, /* SIGFPE */
1, /* SIGKILL */
2, /* SIGBUS */
2, /* SIGSEGV */
2, /* SIGSYS */
1, /* SIGPIPE */
1, /* SIGALRM */
1, /* SIGTERM */
1, /* SIGUSR1 */
1, /* SIGUSR2 */
0, /* SIGCHLD */
0, /* SIGPWR */
0, /* SIGWINCH */
0, /* SIGURG */
0, /* SIGPOLL */
3, /* SIGSTOP */
3, /* SIGTSTP */
0, /* SIGCONT */
3, /* SIGTTIN */
3, /* SIGTTOUT */
1, /* SIGVTALRM */
1, /* SIGPROF */
2, /* SIGXCPU */
2, /* SIGXFSZ */
0, /* SIGWAITING */
1, /* SIGDIAF */
0, /* SIGHATE */
0, /* SIGWINEVENT*/
0, /* SIGCAT */
};
void handle_signal(process_t * proc, signal_t * sig) {
uintptr_t handler = sig->handler;
uintptr_t signum = sig->signum;
free(sig);
if (proc->finished) {
return;
}
if (signum == 0 || signum >= NUMSIGNALS) {
/* Ignore */
return;
}
if (!handler) {
char dowhat = isdeadly[signum];
if (dowhat == 1 || dowhat == 2) {
debug_print(WARNING, "Process %d killed by unhandled signal (%d)", proc->id, signum);
kexit(128 + signum);
__builtin_unreachable();
} else {
debug_print(WARNING, "Ignoring signal %d by default in pid %d", signum, proc->id);
}
/* XXX dowhat == 2: should dump core */
/* XXX dowhat == 3: stop */
return;
}
if (handler == 1) /* Ignore */ {
return;
}
debug_print(NOTICE, "handling signal in process %d (%d)", proc->id, signum);
uintptr_t stack = 0xFFFF0000;
if (proc->syscall_registers->useresp < 0x10000100) {
stack = proc->image.user_stack;
} else {
stack = proc->syscall_registers->useresp;
}
/* Not marked as ignored, must call signal */
enter_signal_handler(handler, signum, stack);
}
list_t * rets_from_sig;
void return_from_signal_handler(void) {
#if 0
debug_print(INFO, "Return From Signal for process %d", current_process->id);
#endif
if (__builtin_expect(!rets_from_sig, 0)) {
rets_from_sig = list_create();
}
spin_lock(sig_lock);
list_insert(rets_from_sig, (process_t *)current_process);
spin_unlock(sig_lock);
switch_next();
}
void fix_signal_stacks(void) {
uint8_t redo_me = 0;
if (rets_from_sig) {
spin_lock(sig_lock_b);
while (rets_from_sig->head) {
spin_lock(sig_lock);
node_t * n = list_dequeue(rets_from_sig);
spin_unlock(sig_lock);
if (!n) {
continue;
}
process_t * p = n->value;
free(n);
if (p == current_process) {
redo_me = 1;
continue;
}
p->thread.esp = p->signal_state.esp;
p->thread.eip = p->signal_state.eip;
p->thread.ebp = p->signal_state.ebp;
memcpy((void *)(p->image.stack - KERNEL_STACK_SIZE), p->signal_kstack, KERNEL_STACK_SIZE);
free(p->signal_kstack);
p->signal_kstack = NULL;
make_process_ready(p);
}
spin_unlock(sig_lock_b);
}
if (redo_me) {
spin_lock(sig_lock);
list_insert(rets_from_sig, (process_t *)current_process);
spin_unlock(sig_lock);
switch_next();
}
}
int send_signal(pid_t process, uint32_t signal) {
process_t * receiver = process_from_pid(process);
if (!receiver) {
/* Invalid pid */
return 1;
}
if (receiver->user != current_process->user && current_process->user != USER_ROOT_UID) {
/* No way in hell. */
return 1;
}
if (signal > NUMSIGNALS) {
/* Invalid signal */
return 1;
}
if (receiver->finished) {
/* Can't send signals to finished processes */
return 1;
}
if (!receiver->signals.functions[signal] && !isdeadly[signal]) {
/* If we're blocking a signal and it's not going to kill us, don't deliver it */
return 1;
}
/* Append signal to list */
signal_t * sig = malloc(sizeof(signal_t));
sig->handler = (uintptr_t)receiver->signals.functions[signal];
sig->signum = signal;
memset(&sig->registers_before, 0x00, sizeof(regs_t));
if (receiver->node_waits) {
process_awaken_from_fswait(receiver, -1);
}
if (!process_is_ready(receiver)) {
make_process_ready(receiver);
}
list_insert(receiver->signal_queue, sig);
return 0;
}

975
kernel/sys/syscall.c Normal file
View File

@ -0,0 +1,975 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2011-2014 Kevin Lange
* Copyright (C) 2012 Markus Schober
*
* Syscall Tables
*
*/
#include <system.h>
#include <process.h>
#include <logging.h>
#include <fs.h>
#include <pipe.h>
#include <version.h>
#include <shm.h>
#include <utsname.h>
#include <printf.h>
#include <module.h>
#include <syscall_nums.h>
static char hostname[256];
static size_t hostname_len = 0;
#define FD_INRANGE(FD) \
((FD) < (int)current_process->fds->length && (FD) >= 0)
#define FD_ENTRY(FD) \
(current_process->fds->entries[(FD)])
#define FD_CHECK(FD) \
(FD_INRANGE(FD) && FD_ENTRY(FD))
#define PTR_INRANGE(PTR) \
((uintptr_t)(PTR) > current_process->image.entry)
#define PTR_VALIDATE(PTR) \
ptr_validate((void *)(PTR), __func__)
static void ptr_validate(void * ptr, const char * syscall) {
if (ptr && !PTR_INRANGE(ptr)) {
debug_print(ERROR, "SEGFAULT: invalid pointer passed to %s. (0x%x < 0x%x)",
syscall, (uintptr_t)ptr, current_process->image.entry);
HALT_AND_CATCH_FIRE("Segmentation fault", NULL);
}
}
void validate(void * ptr) {
ptr_validate(ptr, "syscall");
}
/*
* Exit the current task.
* DOES NOT RETURN!
*/
static int __attribute__((noreturn)) sys_exit(int retval) {
/* Deschedule the current task */
task_exit(retval);
for (;;) ;
}
static int sys_read(int fd, char * ptr, int len) {
if (FD_CHECK(fd)) {
PTR_VALIDATE(ptr);
fs_node_t * node = FD_ENTRY(fd);
uint32_t out = read_fs(node, node->offset, len, (uint8_t *)ptr);
node->offset += out;
return (int)out;
}
return -1;
}
static int sys_ioctl(int fd, int request, void * argp) {
if (FD_CHECK(fd)) {
PTR_VALIDATE(argp);
return ioctl_fs(FD_ENTRY(fd), request, argp);
}
return -1;
}
static int sys_readdir(int fd, int index, struct dirent * entry) {
if (FD_CHECK(fd)) {
PTR_VALIDATE(entry);
struct dirent * kentry = readdir_fs(FD_ENTRY(fd), (uint32_t)index);
if (kentry) {
memcpy(entry, kentry, sizeof *entry);
free(kentry);
return 0;
} else {
return 1;
}
}
return -1;
}
static int sys_write(int fd, char * ptr, int len) {
if (FD_CHECK(fd)) {
PTR_VALIDATE(ptr);
fs_node_t * node = FD_ENTRY(fd);
if (!has_permission(node, 02)) {
debug_print(WARNING, "access denied (write, fd=%d)", fd);
return -EACCES;
}
uint32_t out = write_fs(node, node->offset, len, (uint8_t *)ptr);
node->offset += out;
return out;
}
return -1;
}
static int sys_waitpid(int pid, int * status, int options) {
if (status && !PTR_INRANGE(status)) {
return -EINVAL;
}
return waitpid(pid, status, options);
}
static int sys_open(const char * file, int flags, int mode) {
PTR_VALIDATE(file);
debug_print(NOTICE, "open(%s) flags=0x%x; mode=0x%x", file, flags, mode);
fs_node_t * node = kopen((char *)file, flags);
if (node && !has_permission(node, 04)) {
debug_print(WARNING, "access denied (read, sys_open, file=%s)", file);
return -EACCES;
}
if (node && ((flags & O_RDWR) || (flags & O_APPEND) || (flags & O_WRONLY))) {
if (!has_permission(node, 02)) {
debug_print(WARNING, "access denied (write, sys_open, file=%s)", file);
return -EACCES;
}
}
if (!node && (flags & O_CREAT)) {
/* TODO check directory permissions */
debug_print(NOTICE, "- file does not exist and create was requested.");
/* Um, make one */
int result = create_file_fs((char *)file, mode);
if (!result) {
node = kopen((char *)file, flags);
} else {
return result;
}
}
if (!node) {
debug_print(NOTICE, "File does not exist; someone should be setting errno?");
return -1;
}
node->offset = 0;
int fd = process_append_fd((process_t *)current_process, node);
debug_print(INFO, "[open] pid=%d %s -> %d", getpid(), file, fd);
return fd;
}
static int sys_access(const char * file, int flags) {
PTR_VALIDATE(file);
debug_print(INFO, "access(%s, 0x%x) from pid=%d", file, flags, getpid());
fs_node_t * node = kopen((char *)file, 0);
if (!node) return -1;
close_fs(node);
return 0;
}
static int sys_close(int fd) {
if (FD_CHECK(fd)) {
close_fs(FD_ENTRY(fd));
FD_ENTRY(fd) = NULL;
return 0;
}
return -1;
}
static int sys_sbrk(int size) {
process_t * proc = (process_t *)current_process;
if (proc->group != 0) {
proc = process_from_pid(proc->group);
}
spin_lock(proc->image.lock);
uintptr_t ret = proc->image.heap;
uintptr_t i_ret = ret;
ret = (ret + 0xfff) & ~0xfff; /* Rounds ret to 0x1000 in O(1) */
proc->image.heap += (ret - i_ret) + size;
while (proc->image.heap > proc->image.heap_actual) {
proc->image.heap_actual += 0x1000;
assert(proc->image.heap_actual % 0x1000 == 0);
alloc_frame(get_page(proc->image.heap_actual, 1, current_directory), 0, 1);
invalidate_tables_at(proc->image.heap_actual);
}
spin_unlock(proc->image.lock);
return ret;
}
static int sys_getpid(void) {
/* The user actually wants the pid of the originating thread (which can be us). */
if (current_process->group) {
return current_process->group;
} else {
/* We are the leader */
return current_process->id;
}
}
/* Actual getpid() */
static int sys_gettid(void) {
return getpid();
}
static int sys_execve(const char * filename, char *const argv[], char *const envp[]) {
PTR_VALIDATE(argv);
PTR_VALIDATE(filename);
PTR_VALIDATE(envp);
debug_print(NOTICE, "%d = exec(%s, ...)", current_process->id, filename);
int argc = 0;
int envc = 0;
while (argv[argc]) {
PTR_VALIDATE(argv[argc]);
++argc;
}
if (envp) {
while (envp[envc]) {
PTR_VALIDATE(envp[envc]);
++envc;
}
}
debug_print(INFO, "Allocating space for arguments...");
char ** argv_ = malloc(sizeof(char *) * (argc + 1));
for (int j = 0; j < argc; ++j) {
argv_[j] = malloc((strlen(argv[j]) + 1) * sizeof(char));
memcpy(argv_[j], argv[j], strlen(argv[j]) + 1);
}
argv_[argc] = 0;
char ** envp_;
if (envp && envc) {
envp_ = malloc(sizeof(char *) * (envc + 1));
for (int j = 0; j < envc; ++j) {
envp_[j] = malloc((strlen(envp[j]) + 1) * sizeof(char));
memcpy(envp_[j], envp[j], strlen(envp[j]) + 1);
}
envp_[envc] = 0;
} else {
envp_ = malloc(sizeof(char *));
envp_[0] = NULL;
}
debug_print(INFO,"Releasing all shmem regions...");
shm_release_all((process_t *)current_process);
current_process->cmdline = argv_;
debug_print(INFO,"Executing...");
/* Discard envp */
exec((char *)filename, argc, (char **)argv_, (char **)envp_);
return -1;
}
static int sys_seek(int fd, int offset, int whence) {
if (FD_CHECK(fd)) {
if (fd < 3) {
return 0;
}
switch (whence) {
case 0:
FD_ENTRY(fd)->offset = offset;
break;
case 1:
FD_ENTRY(fd)->offset += offset;
break;
case 2:
FD_ENTRY(fd)->offset = FD_ENTRY(fd)->length + offset;
break;
}
return FD_ENTRY(fd)->offset;
}
return -1;
}
static int stat_node(fs_node_t * fn, uintptr_t st) {
struct stat * f = (struct stat *)st;
PTR_VALIDATE(f);
if (!fn) {
memset(f, 0x00, sizeof(struct stat));
debug_print(INFO, "stat: This file doesn't exist");
return -1;
}
f->st_dev = (uint16_t)(((uint32_t)fn->device & 0xFFFF0) >> 8);
f->st_ino = fn->inode;
uint32_t flags = 0;
if (fn->flags & FS_FILE) { flags |= _IFREG; }
if (fn->flags & FS_DIRECTORY) { flags |= _IFDIR; }
if (fn->flags & FS_CHARDEVICE) { flags |= _IFCHR; }
if (fn->flags & FS_BLOCKDEVICE) { flags |= _IFBLK; }
if (fn->flags & FS_PIPE) { flags |= _IFIFO; }
if (fn->flags & FS_SYMLINK) { flags |= _IFLNK; }
f->st_mode = fn->mask | flags;
f->st_nlink = fn->nlink;
f->st_uid = fn->uid;
f->st_gid = fn->gid;
f->st_rdev = 0;
f->st_size = fn->length;
f->st_atime = fn->atime;
f->st_mtime = fn->mtime;
f->st_ctime = fn->ctime;
f->st_blksize = 512; /* whatever */
if (fn->get_size) {
f->st_size = fn->get_size(fn);
}
return 0;
}
static int sys_statf(char * file, uintptr_t st) {
int result;
PTR_VALIDATE(file);
PTR_VALIDATE(st);
fs_node_t * fn = kopen(file, 0);
result = stat_node(fn, st);
if (fn) {
close_fs(fn);
}
return result;
}
static int sys_chmod(char * file, int mode) {
int result;
PTR_VALIDATE(file);
fs_node_t * fn = kopen(file, 0);
if (fn) {
result = chmod_fs(fn, mode);
close_fs(fn);
return result;
} else {
return -1;
}
}
static int sys_chown(char * file, int uid, int gid) {
int result;
PTR_VALIDATE(file);
fs_node_t * fn = kopen(file, 0);
if (fn) {
result = chown_fs(fn, uid, gid);
close_fs(fn);
return result;
} else {
return -1;
}
}
static int sys_stat(int fd, uintptr_t st) {
PTR_VALIDATE(st);
if (FD_CHECK(fd)) {
return stat_node(FD_ENTRY(fd), st);
}
return -1;
}
static int sys_mkpipe(void) {
fs_node_t * node = make_pipe(4096 * 2);
open_fs(node, 0);
return process_append_fd((process_t *)current_process, node);
}
static int sys_dup2(int old, int new) {
return process_move_fd((process_t *)current_process, old, new);
}
static int sys_getuid(void) {
return current_process->user;
}
static int sys_setuid(user_t new_uid) {
if (current_process->user == USER_ROOT_UID) {
current_process->user = new_uid;
return 0;
}
return -1;
}
static int sys_uname(struct utsname * name) {
PTR_VALIDATE(name);
char version_number[256];
sprintf(version_number, __kernel_version_format,
__kernel_version_major,
__kernel_version_minor,
__kernel_version_lower,
__kernel_version_suffix);
char version_string[256];
sprintf(version_string, "%s %s %s",
__kernel_version_codename,
__kernel_build_date,
__kernel_build_time);
strcpy(name->sysname, __kernel_name);
strcpy(name->nodename, hostname);
strcpy(name->release, version_number);
strcpy(name->version, version_string);
strcpy(name->machine, __kernel_arch);
strcpy(name->domainname, "");
return 0;
}
static int sys_signal(uint32_t signum, uintptr_t handler) {
if (signum > NUMSIGNALS) {
return -1;
}
uintptr_t old = current_process->signals.functions[signum];
current_process->signals.functions[signum] = handler;
return (int)old;
}
/*
static void inspect_memory (uintptr_t vaddr) {
// Please use this scary hack of a function as infrequently as possible.
shmem_debug_frame(vaddr);
}
*/
static int sys_reboot(void) {
debug_print(NOTICE, "[kernel] Reboot requested from process %d by user #%d", current_process->id, current_process->user);
if (current_process->user != USER_ROOT_UID) {
return -1;
} else {
debug_print(NOTICE, "[kernel] Good bye!");
/* Goodbye, cruel world */
IRQ_OFF;
uint8_t out = 0x02;
while ((out & 0x02) != 0) {
out = inportb(0x64);
}
outportb(0x64, 0xFE); /* Reset */
STOP;
}
return 0;
}
static int sys_chdir(char * newdir) {
PTR_VALIDATE(newdir);
char * path = canonicalize_path(current_process->wd_name, newdir);
fs_node_t * chd = kopen(path, 0);
if (chd) {
if ((chd->flags & FS_DIRECTORY) == 0) {
close_fs(chd);
return -1;
}
close_fs(chd);
free(current_process->wd_name);
current_process->wd_name = malloc(strlen(path) + 1);
memcpy(current_process->wd_name, path, strlen(path) + 1);
return 0;
} else {
return -1;
}
}
static int sys_getcwd(char * buf, size_t size) {
if (buf) {
PTR_VALIDATE(buf);
size_t len = strlen(current_process->wd_name) + 1;
return (int)memcpy(buf, current_process->wd_name, MIN(size, len));
}
return 0;
}
static int sys_sethostname(char * new_hostname) {
if (current_process->user == USER_ROOT_UID) {
PTR_VALIDATE(new_hostname);
size_t len = strlen(new_hostname) + 1;
if (len > 256) {
return 1;
}
hostname_len = len;
memcpy(hostname, new_hostname, hostname_len);
return 0;
} else {
return 1;
}
}
static int sys_gethostname(char * buffer) {
PTR_VALIDATE(buffer);
memcpy(buffer, hostname, hostname_len);
return hostname_len;
}
extern int mkdir_fs(char *name, uint16_t permission);
static int sys_mkdir(char * path, uint32_t mode) {
return mkdir_fs(path, 0777);
}
/*
* Yield the rest of the quantum;
* useful for busy waiting and other such things
*/
static int sys_yield(void) {
switch_task(1);
return 1;
}
/*
* System Function
*/
static int sys_sysfunc(int fn, char ** args) {
/* System Functions are special debugging system calls */
if (current_process->user == USER_ROOT_UID) {
switch (fn) {
case 3:
debug_print(ERROR, "sync is currently unimplemented");
//ext2_disk_sync();
return 0;
case 4:
/* Request kernel output to file descriptor in arg0*/
debug_print(NOTICE, "Setting output to file object in process %d's fd=%d!", getpid(), (int)args);
debug_file = current_process->fds->entries[(int)args];
break;
case 5:
{
char *arg;
PTR_VALIDATE(args);
for (arg = args[0]; arg; arg++)
PTR_VALIDATE(arg);
debug_print(NOTICE, "Replacing process %d's file descriptors with pointers to %s", getpid(), (char *)args);
fs_node_t * repdev = kopen((char *)args, 0);
while (current_process->fds->length < 3) {
process_append_fd((process_t *)current_process, repdev);
}
FD_ENTRY(0) = repdev;
FD_ENTRY(1) = repdev;
FD_ENTRY(2) = repdev;
}
break;
case 6:
debug_print(WARNING, "writing contents of file %s to sdb", args[0]);
{
PTR_VALIDATE(args);
PTR_VALIDATE(args[0]);
fs_node_t * file = kopen((char *)args[0], 0);
if (!file) {
return -1;
}
size_t length = file->length;
uint8_t * buffer = malloc(length);
read_fs(file, 0, length, (uint8_t *)buffer);
close_fs(file);
debug_print(WARNING, "Finished reading file, going to write it now.");
fs_node_t * f = kopen("/dev/sdb", 0);
if (!f) {
return 1;
}
write_fs(f, 0, length, buffer);
free(buffer);
return 0;
}
case 7:
debug_print(NOTICE, "Spawning debug hook as child of process %d.", getpid());
if (debug_hook) {
fs_node_t * tty = FD_ENTRY(0);
return create_kernel_tasklet(debug_hook, "[kttydebug]", tty);
} else {
return -1;
}
case 8:
debug_print(NOTICE, "Loading module %s.", args[0]);
{
/* Check file existence */
fs_node_t * file = kopen(args[0], 0);
if (!file) {
return 1;
}
close_fs(file);
module_data_t * mod_info = module_load(args[0]);
if (!mod_info) {
return 2;
}
return 0;
}
}
}
switch (fn) {
/* The following functions are here to support the loader and are probably bad. */
case 9:
{
process_t * proc = (process_t *)current_process;
if (proc->group != 0) {
proc = process_from_pid(proc->group);
}
spin_lock(proc->image.lock);
/* Set new heap start */
proc->image.heap = (uintptr_t)args[0];
proc->image.heap_actual = proc->image.heap & 0xFFFFF000;
assert(proc->image.heap_actual % 0x1000 == 0);
alloc_frame(get_page(proc->image.heap_actual, 1, current_directory), 0, 1);
invalidate_tables_at(proc->image.heap_actual);
while (proc->image.heap > proc->image.heap_actual) {
proc->image.heap_actual += 0x1000;
alloc_frame(get_page(proc->image.heap_actual, 1, current_directory), 0, 1);
invalidate_tables_at(proc->image.heap_actual);
}
spin_unlock(proc->image.lock);
return 0;
}
case 10:
{
/* Load pages to fit region. */
uintptr_t address = (uintptr_t)args[0];
size_t size = (size_t)args[1];
/* TODO: Other arguments for read/write? */
if (address & 0xFFF) {
size += address & 0xFFF;
address &= 0xFFFFF000;
}
process_t * proc = (process_t *)current_process;
if (proc->group != 0) {
proc = process_from_pid(proc->group);
}
spin_lock(proc->image.lock);
for (size_t x = 0; x < size; x += 0x1000) {
alloc_frame(get_page(address + x, 1, current_directory), 0, 1);
invalidate_tables_at(address + x);
}
spin_unlock(proc->image.lock);
return 0;
}
case 11:
{
/* Set command line (meant for threads to set descriptions) */
int count = 0;
char **arg = args;
PTR_VALIDATE(args);
while (*arg) {
PTR_VALIDATE(*args);
count++;
arg++;
}
/*
* XXX We have a pretty obvious leak in storing command lines, since
* we never free them! Unfortunately, at the moment, they are
* shared between different processes, so until that gets fixed
* we're going to be just as bad as the rest of the codebase and
* just not free the previous value.
*/
current_process->cmdline = malloc(sizeof(char*)*(count+1));
int i = 0;
while (i < count) {
current_process->cmdline[i] = strdup(args[i]);
i++;
}
current_process->cmdline[i] = NULL;
return 0;
}
case 12:
/*
* Print a debug message to the kernel console
* XXX: This probably should be a thing normal users can do.
*/
PTR_VALIDATE(args);
debug_print(WARNING, "0x%x 0x%x 0x%x 0x%x", args[0], args[1], args[2], args[3]);
_debug_print(args[0], (uintptr_t)args[1], (uint32_t)args[2], args[3] ? args[3] : "(null)");
return 0;
break;
case 13:
/*
* Set VGA text-mode cursor location
* (Not actually used to place a cursor, we use this to move the cursor off screen)
*/
PTR_VALIDATE(args);
outportb(0x3D4, 14);
outportb(0x3D5, (unsigned int)args[0]);
outportb(0x3D4, 15);
outportb(0x3D5, (unsigned int)args[1]);
return 0;
default:
debug_print(ERROR, "Bad system function %d", fn);
break;
}
return -1; /* Bad system function or access failure */
}
static int sys_sleepabs(unsigned long seconds, unsigned long subseconds) {
/* Mark us as asleep until <some time period> */
sleep_until((process_t *)current_process, seconds, subseconds);
/* Switch without adding us to the queue */
switch_task(0);
if (seconds > timer_ticks || (seconds == timer_ticks && subseconds >= timer_subticks)) {
return 0;
} else {
return 1;
}
}
static int sys_sleep(unsigned long seconds, unsigned long subseconds) {
unsigned long s, ss;
relative_time(seconds, subseconds * 10, &s, &ss);
return sys_sleepabs(s, ss);
}
static int sys_umask(int mode) {
current_process->mask = mode & 0777;
return 0;
}
static int sys_unlink(char * file) {
PTR_VALIDATE(file);
return unlink_fs(file);
}
static int sys_fork(void) {
return (int)fork();
}
static int sys_clone(uintptr_t new_stack, uintptr_t thread_func, uintptr_t arg) {
if (!new_stack || !PTR_INRANGE(new_stack)) return -1;
if (!thread_func || !PTR_INRANGE(thread_func)) return -1;
return (int)clone(new_stack, thread_func, arg);
}
static int sys_shm_obtain(char * path, size_t * size) {
PTR_VALIDATE(path);
PTR_VALIDATE(size);
return (int)shm_obtain(path, size);
}
static int sys_shm_release(char * path) {
PTR_VALIDATE(path);
return shm_release(path);
}
static int sys_kill(pid_t process, uint32_t signal) {
return send_signal(process, signal);
}
static int sys_gettimeofday(struct timeval * tv, void * tz) {
PTR_VALIDATE(tv);
PTR_VALIDATE(tz);
return gettimeofday(tv, tz);
}
static int sys_openpty(int * master, int * slave, char * name, void * _ign0, void * size) {
/* We require a place to put these when we are done. */
if (!master || !slave) return -1;
if (master && !PTR_INRANGE(master)) return -1;
if (slave && !PTR_INRANGE(slave)) return -1;
if (size && !PTR_INRANGE(size)) return -1;
/* Create a new pseudo terminal */
fs_node_t * fs_master;
fs_node_t * fs_slave;
pty_create(size, &fs_master, &fs_slave);
/* Append the master and slave to the calling process */
*master = process_append_fd((process_t *)current_process, fs_master);
*slave = process_append_fd((process_t *)current_process, fs_slave);
open_fs(fs_master, 0);
open_fs(fs_slave, 0);
/* Return success */
return 0;
}
static int sys_pipe(int pipes[2]) {
if (pipes && !PTR_INRANGE(pipes)) {
return -EFAULT;
}
fs_node_t * outpipes[2];
make_unix_pipe(outpipes);
open_fs(outpipes[0], 0);
open_fs(outpipes[1], 0);
pipes[0] = process_append_fd((process_t *)current_process, outpipes[0]);
pipes[1] = process_append_fd((process_t *)current_process, outpipes[1]);
return 0;
}
static int sys_mount(char * arg, char * mountpoint, char * type, unsigned long flags, void * data) {
/* TODO: Make use of flags and data from mount command. */
(void)flags;
(void)data;
if (current_process->user != USER_ROOT_UID) {
return -EPERM;
}
if (PTR_INRANGE(arg) && PTR_INRANGE(mountpoint) && PTR_INRANGE(type)) {
return vfs_mount_type(type, arg, mountpoint);
}
return -EFAULT;
}
static int sys_symlink(char * target, char * name) {
PTR_VALIDATE(target);
PTR_VALIDATE(name);
return symlink_fs(target, name);
}
static int sys_readlink(const char * file, char * ptr, int len) {
PTR_VALIDATE(file);
fs_node_t * node = kopen((char *) file, O_PATH | O_NOFOLLOW);
if (!node) {
return -ENOENT;
}
int rv = readlink_fs(node, ptr, len);
close_fs(node);
return rv;
}
static int sys_lstat(char * file, uintptr_t st) {
int result;
PTR_VALIDATE(file);
PTR_VALIDATE(st);
fs_node_t * fn = kopen(file, O_PATH | O_NOFOLLOW);
result = stat_node(fn, st);
if (fn) {
close_fs(fn);
}
return result;
}
static int sys_fswait(int c, int fds[]) {
PTR_VALIDATE(fds);
for (int i = 0; i < c; ++i) {
if (!FD_CHECK(fds[i])) return -1;
}
fs_node_t ** nodes = malloc(sizeof(fs_node_t *)*(c+1));
for (int i = 0; i < c; ++i) {
nodes[i] = FD_ENTRY(fds[i]);
}
nodes[c] = NULL;
int result = process_wait_nodes((process_t *)current_process, nodes, -1);
free(nodes);
return result;
}
static int sys_fswait_timeout(int c, int fds[], int timeout) {
PTR_VALIDATE(fds);
for (int i = 0; i < c; ++i) {
if (!FD_CHECK(fds[i])) return -1;
}
fs_node_t ** nodes = malloc(sizeof(fs_node_t *)*(c+1));
for (int i = 0; i < c; ++i) {
nodes[i] = FD_ENTRY(fds[i]);
}
nodes[c] = NULL;
int result = process_wait_nodes((process_t *)current_process, nodes, timeout);
free(nodes);
return result;
}
/*
* System Call Internals
*/
static int (*syscalls[])() = {
/* System Call Table */
[SYS_EXT] = sys_exit,
[SYS_OPEN] = sys_open,
[SYS_READ] = sys_read,
[SYS_WRITE] = sys_write,
[SYS_CLOSE] = sys_close,
[SYS_GETTIMEOFDAY] = sys_gettimeofday,
[SYS_EXECVE] = sys_execve,
[SYS_FORK] = sys_fork,
[SYS_GETPID] = sys_getpid,
[SYS_SBRK] = sys_sbrk,
[SYS_UNAME] = sys_uname,
[SYS_OPENPTY] = sys_openpty,
[SYS_SEEK] = sys_seek,
[SYS_STAT] = sys_stat,
[SYS_MKPIPE] = sys_mkpipe,
[SYS_DUP2] = sys_dup2,
[SYS_GETUID] = sys_getuid,
[SYS_SETUID] = sys_setuid,
[SYS_REBOOT] = sys_reboot,
[SYS_READDIR] = sys_readdir,
[SYS_CHDIR] = sys_chdir,
[SYS_GETCWD] = sys_getcwd,
[SYS_CLONE] = sys_clone,
[SYS_SETHOSTNAME] = sys_sethostname,
[SYS_GETHOSTNAME] = sys_gethostname,
[SYS_MKDIR] = sys_mkdir,
[SYS_SHM_OBTAIN] = sys_shm_obtain,
[SYS_SHM_RELEASE] = sys_shm_release,
[SYS_KILL] = sys_kill,
[SYS_SIGNAL] = sys_signal,
[SYS_GETTID] = sys_gettid,
[SYS_YIELD] = sys_yield,
[SYS_SYSFUNC] = sys_sysfunc,
[SYS_SLEEPABS] = sys_sleepabs,
[SYS_SLEEP] = sys_sleep,
[SYS_IOCTL] = sys_ioctl,
[SYS_ACCESS] = sys_access,
[SYS_STATF] = sys_statf,
[SYS_CHMOD] = sys_chmod,
[SYS_UMASK] = sys_umask,
[SYS_UNLINK] = sys_unlink,
[SYS_WAITPID] = sys_waitpid,
[SYS_PIPE] = sys_pipe,
[SYS_MOUNT] = sys_mount,
[SYS_SYMLINK] = sys_symlink,
[SYS_READLINK] = sys_readlink,
[SYS_LSTAT] = sys_lstat,
[SYS_FSWAIT] = sys_fswait,
[SYS_FSWAIT2] = sys_fswait_timeout,
[SYS_CHOWN] = sys_chown,
};
uint32_t num_syscalls = sizeof(syscalls) / sizeof(*syscalls);
typedef uint32_t (*scall_func)(unsigned int, ...);
pid_t trace_pid = 0;
void syscall_handler(struct regs * r) {
if (r->eax >= num_syscalls) {
return;
}
uintptr_t location = (uintptr_t)syscalls[r->eax];
if (!location) {
return;
}
/* Update the syscall registers for this process */
current_process->syscall_registers = r;
if (trace_pid && current_process->id == trace_pid) {
debug_print(WARNING, "[syscall trace] %d (0x%x) 0x%x 0x%x 0x%x 0x%x 0x%x", r->eax, location, r->ebx, r->ecx, r->edx, r->esi, r->edi);
}
/* Call the syscall function */
scall_func func = (scall_func)location;
uint32_t ret = func(r->ebx, r->ecx, r->edx, r->esi, r->edi);
if ((current_process->syscall_registers == r) ||
(location != (uintptr_t)&fork && location != (uintptr_t)&clone)) {
r->eax = ret;
}
}
void syscalls_install(void) {
debug_print(NOTICE, "Initializing syscall table with %d functions", num_syscalls);
isrs_install_handler(0x7F, &syscall_handler);
}

88
kernel/sys/system.c Normal file
View File

@ -0,0 +1,88 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2011-2013 Kevin Lange
*
* System Functions
*
*/
#include <system.h>
char * boot_arg = NULL;
char * boot_arg_extra = NULL;
/*
* memsetw
* Set `count` shorts to `val`.
*/
unsigned short * memsetw(unsigned short * dest, unsigned short val, int count) {
int i = 0;
for ( ; i < count; ++i ) {
dest[i] = val;
}
return dest;
}
uint32_t __attribute__ ((pure)) krand(void) {
static uint32_t x = 123456789;
static uint32_t y = 362436069;
static uint32_t z = 521288629;
static uint32_t w = 88675123;
uint32_t t;
t = x ^ (x << 11);
x = y; y = z; z = w;
return w = w ^ (w >> 19) ^ t ^ (t >> 8);
}
unsigned short inports(unsigned short _port) {
unsigned short rv;
asm volatile ("inw %1, %0" : "=a" (rv) : "dN" (_port));
return rv;
}
void outports(unsigned short _port, unsigned short _data) {
asm volatile ("outw %1, %0" : : "dN" (_port), "a" (_data));
}
unsigned int inportl(unsigned short _port) {
unsigned int rv;
asm volatile ("inl %%dx, %%eax" : "=a" (rv) : "dN" (_port));
return rv;
}
void outportl(unsigned short _port, unsigned int _data) {
asm volatile ("outl %%eax, %%dx" : : "dN" (_port), "a" (_data));
}
unsigned char inportb(unsigned short _port) {
unsigned char rv;
asm volatile ("inb %1, %0" : "=a" (rv) : "dN" (_port));
return rv;
}
void outportb(unsigned short _port, unsigned char _data) {
asm volatile ("outb %1, %0" : : "dN" (_port), "a" (_data));
}
void outportsm(unsigned short port, unsigned char * data, unsigned long size) {
asm volatile ("rep outsw" : "+S" (data), "+c" (size) : "d" (port));
}
void inportsm(unsigned short port, unsigned char * data, unsigned long size) {
asm volatile ("rep insw" : "+D" (data), "+c" (size) : "d" (port) : "memory");
}
size_t lfind(const char * str, const char accept) {
return (size_t)strchr(str, accept);
}
size_t rfind(const char * str, const char accept) {
return (size_t)strrchr(str, accept);
}
uint8_t startswith(const char * str, const char * accept) {
return strstr(str, accept) == str;
}

532
kernel/sys/task.c Normal file
View File

@ -0,0 +1,532 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2011-2014 Kevin Lange
* Copyright (C) 2012 Markus Schober
*
* Task Switching and Management Functions
*
*/
#include <system.h>
#include <process.h>
#include <logging.h>
#include <shm.h>
#include <mem.h>
#define TASK_MAGIC 0xDEADBEEF
uint32_t next_pid = 0;
#define PUSH(stack, type, item) stack -= sizeof(type); \
*((type *) stack) = item
page_directory_t *kernel_directory;
page_directory_t *current_directory;
/*
* Clone a page directory and its contents.
* (If you do not intend to clone the contents, do it yourself!)
*
* @param src Pointer to source directory to clone from.
* @return A pointer to a new directory.
*/
page_directory_t *
clone_directory(
page_directory_t * src
) {
/* Allocate a new page directory */
uintptr_t phys;
page_directory_t * dir = (page_directory_t *)kvmalloc_p(sizeof(page_directory_t), &phys);
/* Clear it out */
memset(dir, 0, sizeof(page_directory_t));
dir->ref_count = 1;
/* And store it... */
dir->physical_address = phys;
uint32_t i;
for (i = 0; i < 1024; ++i) {
/* Copy each table */
if (!src->tables[i] || (uintptr_t)src->tables[i] == (uintptr_t)0xFFFFFFFF) {
continue;
}
if (kernel_directory->tables[i] == src->tables[i]) {
/* Kernel tables are simply linked together */
dir->tables[i] = src->tables[i];
dir->physical_tables[i] = src->physical_tables[i];
} else {
if (i * 0x1000 * 1024 < SHM_START) {
/* User tables must be cloned */
uintptr_t phys;
dir->tables[i] = clone_table(src->tables[i], &phys);
dir->physical_tables[i] = phys | 0x07;
}
}
}
return dir;
}
/*
* Free a directory and its tables
*/
void release_directory(page_directory_t * dir) {
dir->ref_count--;
if (dir->ref_count < 1) {
uint32_t i;
for (i = 0; i < 1024; ++i) {
if (!dir->tables[i] || (uintptr_t)dir->tables[i] == (uintptr_t)0xFFFFFFFF) {
continue;
}
if (kernel_directory->tables[i] != dir->tables[i]) {
if (i * 0x1000 * 1024 < SHM_START) {
for (uint32_t j = 0; j < 1024; ++j) {
if (dir->tables[i]->pages[j].frame) {
free_frame(&(dir->tables[i]->pages[j]));
}
}
}
free(dir->tables[i]);
}
}
free(dir);
}
}
void release_directory_for_exec(page_directory_t * dir) {
uint32_t i;
/* This better be the only owner of this directory... */
for (i = 0; i < 1024; ++i) {
if (!dir->tables[i] || (uintptr_t)dir->tables[i] == (uintptr_t)0xFFFFFFFF) {
continue;
}
if (kernel_directory->tables[i] != dir->tables[i]) {
if (i * 0x1000 * 1024 < USER_STACK_BOTTOM) {
for (uint32_t j = 0; j < 1024; ++j) {
if (dir->tables[i]->pages[j].frame) {
free_frame(&(dir->tables[i]->pages[j]));
}
}
dir->physical_tables[i] = 0;
free(dir->tables[i]);
dir->tables[i] = 0;
}
}
}
}
extern char * default_name;
/*
* Clone a page table
*
* @param src Pointer to a page table to clone.
* @param physAddr [out] Pointer to the physical address of the new page table
* @return A pointer to a new page table.
*/
page_table_t *
clone_table(
page_table_t * src,
uintptr_t * physAddr
) {
/* Allocate a new page table */
page_table_t * table = (page_table_t *)kvmalloc_p(sizeof(page_table_t), physAddr);
memset(table, 0, sizeof(page_table_t));
uint32_t i;
for (i = 0; i < 1024; ++i) {
/* For each frame in the table... */
if (!src->pages[i].frame) {
continue;
}
/* Allocate a new frame */
alloc_frame(&table->pages[i], 0, 0);
/* Set the correct access bit */
if (src->pages[i].present) table->pages[i].present = 1;
if (src->pages[i].rw) table->pages[i].rw = 1;
if (src->pages[i].user) table->pages[i].user = 1;
if (src->pages[i].writethrough) table->pages[i].writethrough = 1;
if (src->pages[i].cachedisable) table->pages[i].cachedisable = 1;
/* Copy the contents of the page from the old table to the new one */
copy_page_physical(src->pages[i].frame * 0x1000, table->pages[i].frame * 0x1000);
}
return table;
}
uintptr_t frozen_stack = 0;
/*
* Install multitasking functionality.
*/
void tasking_install(void) {
IRQ_OFF; /* Disable interrupts */
debug_print(NOTICE, "Initializing multitasking");
/* Initialize the process tree */
initialize_process_tree();
/* Spawn the initial process */
current_process = spawn_init();
kernel_idle_task = spawn_kidle();
/* Initialize the paging environment */
#if 0
set_process_environment((process_t *)current_process, current_directory);
#endif
/* Switch to the kernel directory */
switch_page_directory(current_process->thread.page_directory);
frozen_stack = (uintptr_t)valloc(KERNEL_STACK_SIZE);
/* Reenable interrupts */
IRQ_RES;
}
/*
* Fork.
*
* @return To the parent: PID of the child; to the child: 0
*/
uint32_t fork(void) {
IRQ_OFF;
uintptr_t esp, ebp;
current_process->syscall_registers->eax = 0;
/* Make a pointer to the parent process (us) on the stack */
process_t * parent = (process_t *)current_process;
assert(parent && "Forked from nothing??");
/* Clone the current process' page directory */
page_directory_t * directory = clone_directory(current_directory);
assert(directory && "Could not allocate a new page directory!");
/* Spawn a new process from this one */
debug_print(INFO,"\033[1;32mALLOC {\033[0m");
process_t * new_proc = spawn_process(current_process, 0);
debug_print(INFO,"\033[1;32m}\033[0m");
assert(new_proc && "Could not allocate a new process!");
/* Set the new process' page directory to clone */
set_process_environment(new_proc, directory);
struct regs r;
memcpy(&r, current_process->syscall_registers, sizeof(struct regs));
new_proc->syscall_registers = &r;
esp = new_proc->image.stack;
ebp = esp;
new_proc->syscall_registers->eax = 0;
PUSH(esp, struct regs, r);
new_proc->thread.esp = esp;
new_proc->thread.ebp = ebp;
new_proc->is_tasklet = parent->is_tasklet;
new_proc->thread.eip = (uintptr_t)&return_to_userspace;
/* Add the new process to the ready queue */
make_process_ready(new_proc);
IRQ_RES;
/* Return the child PID */
return new_proc->id;
}
int create_kernel_tasklet(tasklet_t tasklet, char * name, void * argp) {
IRQ_OFF;
uintptr_t esp, ebp;
if (current_process->syscall_registers) {
current_process->syscall_registers->eax = 0;
}
page_directory_t * directory = kernel_directory;
/* Spawn a new process from this one */
process_t * new_proc = spawn_process(current_process, 0);
assert(new_proc && "Could not allocate a new process!");
/* Set the new process' page directory to the original process' */
set_process_environment(new_proc, directory);
directory->ref_count++;
/* Read the instruction pointer */
if (current_process->syscall_registers) {
struct regs r;
memcpy(&r, current_process->syscall_registers, sizeof(struct regs));
new_proc->syscall_registers = &r;
}
esp = new_proc->image.stack;
ebp = esp;
if (current_process->syscall_registers) {
new_proc->syscall_registers->eax = 0;
}
new_proc->is_tasklet = 1;
new_proc->name = name;
PUSH(esp, uintptr_t, (uintptr_t)name);
PUSH(esp, uintptr_t, (uintptr_t)argp);
PUSH(esp, uintptr_t, (uintptr_t)&task_exit);
new_proc->thread.esp = esp;
new_proc->thread.ebp = ebp;
new_proc->thread.eip = (uintptr_t)tasklet;
/* Add the new process to the ready queue */
make_process_ready(new_proc);
IRQ_RES;
/* Return the child PID */
return new_proc->id;
}
/*
* clone the current thread and create a new one in the same
* memory space with the given pointer as its new stack.
*/
uint32_t
clone(uintptr_t new_stack, uintptr_t thread_func, uintptr_t arg) {
uintptr_t esp, ebp;
IRQ_OFF;
current_process->syscall_registers->eax = 0;
/* Make a pointer to the parent process (us) on the stack */
process_t * parent = (process_t *)current_process;
assert(parent && "Cloned from nothing??");
page_directory_t * directory = current_directory;
/* Spawn a new process from this one */
process_t * new_proc = spawn_process(current_process, 1);
assert(new_proc && "Could not allocate a new process!");
/* Set the new process' page directory to the original process' */
set_process_environment(new_proc, directory);
directory->ref_count++;
/* Read the instruction pointer */
struct regs r;
memcpy(&r, current_process->syscall_registers, sizeof(struct regs));
new_proc->syscall_registers = &r;
esp = new_proc->image.stack;
ebp = esp;
/* Set the gid */
if (current_process->group) {
new_proc->group = current_process->group;
} else {
/* We are the session leader */
new_proc->group = current_process->id;
}
new_proc->syscall_registers->ebp = new_stack;
new_proc->syscall_registers->eip = thread_func;
/* Push arg, bogus return address onto the new thread's stack */
PUSH(new_stack, uintptr_t, arg);
PUSH(new_stack, uintptr_t, THREAD_RETURN);
/* Set esp, ebp, and eip for the new thread */
new_proc->syscall_registers->esp = new_stack;
new_proc->syscall_registers->useresp = new_stack;
PUSH(esp, struct regs, r);
new_proc->thread.esp = esp;
new_proc->thread.ebp = ebp;
new_proc->is_tasklet = parent->is_tasklet;
new_proc->thread.eip = (uintptr_t)&return_to_userspace;
/* Add the new process to the ready queue */
make_process_ready(new_proc);
IRQ_RES;
/* Return the child PID */
return new_proc->id;
}
/*
* Get the process ID of the current process.
*
* @return The PID of the current process.
*/
uint32_t getpid(void) {
/* Fairly self-explanatory. */
return current_process->id;
}
/*
* Switch to the next ready task.
*
* This is called from the interrupt handler for the interval timer to
* perform standard task switching.
*/
void switch_task(uint8_t reschedule) {
if (!current_process) {
/* Tasking is not yet installed. */
return;
}
if (!current_process->running) {
switch_next();
}
/* Collect the current kernel stack and instruction pointers */
uintptr_t esp, ebp, eip;
asm volatile ("mov %%esp, %0" : "=r" (esp));
asm volatile ("mov %%ebp, %0" : "=r" (ebp));
eip = read_eip();
if (eip == 0x10000) {
/* Returned from EIP after task switch, we have
* finished switching. */
fix_signal_stacks();
/* XXX: Signals */
if (!current_process->finished) {
if (current_process->signal_queue->length > 0) {
node_t * node = list_dequeue(current_process->signal_queue);
signal_t * sig = node->value;
free(node);
handle_signal((process_t *)current_process, sig);
}
}
return;
}
/* Remember this process' ESP/EBP/EIP */
current_process->thread.eip = eip;
current_process->thread.esp = esp;
current_process->thread.ebp = ebp;
current_process->running = 0;
/* Save floating point state */
switch_fpu();
if (reschedule && current_process != kernel_idle_task) {
/* And reinsert it into the ready queue */
make_process_ready((process_t *)current_process);
}
/* Switch to the next task */
switch_next();
}
/*
* Immediately switch to the next task.
*
* Does not store the ESP/EBP/EIP of the current thread.
*/
void switch_next(void) {
uintptr_t esp, ebp, eip;
/* Get the next available process */
current_process = next_ready_process();
/* Retreive the ESP/EBP/EIP */
eip = current_process->thread.eip;
esp = current_process->thread.esp;
ebp = current_process->thread.ebp;
unswitch_fpu();
/* Validate */
if ((eip < (uintptr_t)&code) || (eip > (uintptr_t)heap_end)) {
debug_print(WARNING, "Skipping broken process %d! [eip=0x%x <0x%x or >0x%x]", current_process->id, eip, &code, &end);
switch_next();
}
if (current_process->finished) {
debug_print(WARNING, "Tried to switch to process %d, but it claims it is finished.", current_process->id);
switch_next();
}
/* Set the page directory */
current_directory = current_process->thread.page_directory;
switch_page_directory(current_directory);
/* Set the kernel stack in the TSS */
set_kernel_stack(current_process->image.stack);
if (current_process->started) {
if (!current_process->signal_kstack) {
if (current_process->signal_queue->length > 0) {
current_process->signal_kstack = malloc(KERNEL_STACK_SIZE);
current_process->signal_state.esp = current_process->thread.esp;
current_process->signal_state.eip = current_process->thread.eip;
current_process->signal_state.ebp = current_process->thread.ebp;
memcpy(current_process->signal_kstack, (void *)(current_process->image.stack - KERNEL_STACK_SIZE), KERNEL_STACK_SIZE);
}
}
} else {
current_process->started = 1;
}
current_process->running = 1;
/* Jump, baby, jump */
asm volatile (
"mov %0, %%ebx\n"
"mov %1, %%esp\n"
"mov %2, %%ebp\n"
"mov %3, %%cr3\n"
"mov $0x10000, %%eax\n" /* read_eip() will return 0x10000 */
"jmp *%%ebx"
: : "r" (eip), "r" (esp), "r" (ebp), "r" (current_directory->physical_address)
: "%ebx", "%esp", "%eax");
}
extern void enter_userspace(uintptr_t location, uintptr_t stack);
/*
* Enter ring 3 and jump to `location`.
*
* @param location Address to jump to in user space
* @param argc Argument count
* @param argv Argument pointers
* @param stack Userspace stack address
*/
void
enter_user_jmp(uintptr_t location, int argc, char ** argv, uintptr_t stack) {
IRQ_OFF;
set_kernel_stack(current_process->image.stack);
PUSH(stack, uintptr_t, (uintptr_t)argv);
PUSH(stack, int, argc);
enter_userspace(location, stack);
}
/*
* Dequeue the current task and set it as finished
*
* @param retval Set the return value to this.
*/
void task_exit(int retval) {
/* Free the image memory */
if (__builtin_expect(current_process->id == 0,0)) {
/* This is probably bad... */
switch_next();
return;
}
cleanup_process((process_t *)current_process, retval);
process_t * parent = process_get_parent((process_t *)current_process);
if (parent && !parent->finished) {
wakeup_queue(parent->wait_queue);
}
switch_next();
}
/*
* Call task_exit() and immediately STOP if we can't.
*/
void kexit(int retval) {
task_exit(retval);
debug_print(CRITICAL, "Process returned from task_exit! Environment is definitely unclean. Stopping.");
STOP;
}

56
kernel/sys/version.c Normal file
View File

@ -0,0 +1,56 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2011-2017 Kevin Lange
*/
#include <version.h>
/* Kernel name. If you change this, you're not
* my friend any more. */
char * __kernel_name = "toaru";
/* This really shouldn't change, and if it does,
* always ensure it still has the correct arguments
* when used as a vsprintf() format. */
char * __kernel_version_format = "%d.%d.%d-%s";
/* Version numbers X.Y.Z */
int __kernel_version_major = 1;
int __kernel_version_minor = 2;
int __kernel_version_lower = 2;
/* Kernel build suffix, which doesn't necessarily
* mean anything, but can be used to distinguish
* between different features included while
* building multiple kernels. */
#ifdef KERNEL_GIT_TAG
# define STR(x) #x
# define STRSTR(x) STR(x)
# define KERNEL_VERSION_SUFFIX STRSTR(KERNEL_GIT_TAG)
#else
# define KERNEL_VERSION_SUFFIX "r"
#endif
char * __kernel_version_suffix = KERNEL_VERSION_SUFFIX;
/* The release codename. */
char * __kernel_version_codename = "touma";
/* Build architecture (should probably not be
* here as a string, but rather some sort of
* preprocessor macro, or pulled from a script) */
char * __kernel_arch = "i686";
/* Rebuild from clean to reset these. */
char * __kernel_build_date = __DATE__;
char * __kernel_build_time = __TIME__;
#if (defined(__GNUC__) || defined(__GNUG__)) && !(defined(__clang__) || defined(__INTEL_COMPILER))
# define COMPILER_VERSION "gcc " __VERSION__
#elif (defined(__clang__))
# define COMPILER_VERSION "clang " __clang_version__
#else
# define COMPILER_VERSION "unknown-compiler how-did-you-do-that"
#endif
char * __kernel_compiler_version = COMPILER_VERSION;

64
kernel/task.S Normal file
View File

@ -0,0 +1,64 @@
.section .text
.align 4
/* Disable paging mask */
.set dp, 0x7FFFFFFF
/* Enable paging mask */
.set ep, 0x80000000
.global copy_page_physical
.type copy_page_physical, @function
copy_page_physical:
/* Preserve contents of EBX */
push %ebx
/* Preserve contents of EFLAGS */
pushf
cli
/* Load source and destination addresses */
mov 12(%esp), %ebx
mov 16(%esp), %ecx
/* Get control register and disable paging*/
mov %cr0, %edx
and $dp, %edx
mov %edx, %cr0
/* Copy 4096 bytes */
mov $0x400, %edx
.page_loop:
/* Get word at source address */
mov (%ebx), %eax
/* Store it at destination address */
mov %eax, (%ecx)
/* Increment source and destination addresses to next word */
add $4, %ebx
add $4, %ecx
/* One less word to copy */
dec %edx
jnz .page_loop
/* Get control register again and enable paging */
mov %cr0, %edx
or $ep, %edx
mov %edx, %cr0
/* Restore EFLAGS */
popf
/* Restore EBX */
pop %ebx
ret
/* Read EIP */
.global read_eip
.type read_eip, @function
read_eip:
mov (%esp), %eax
ret

10
kernel/tss.S Normal file
View File

@ -0,0 +1,10 @@
.section .text
.align 4
.global tss_flush
.type tss_flush, @function
tss_flush:
mov $0x2B, %ax
ltr %ax
ret

61
kernel/user.S Normal file
View File

@ -0,0 +1,61 @@
/* Return to Userspace (from thread creation) */
.global return_to_userspace
.type return_to_userspace, @function
return_to_userspace:
pop %gs
pop %fs
pop %es
pop %ds
popa
add $8, %esp
iret
/* Enter userspace (ring3) */
.global enter_userspace
.type enter_userspace, @function
.set MAGIC, 0xDECADE21
enter_userspace:
pushl %ebp
mov %esp, %ebp
mov 0xC(%ebp), %edx
mov %edx, %esp
pushl $MAGIC
/* Segement selector */
mov $0x23,%ax
/* Save segement registers */
mov %eax, %ds
mov %eax, %es
mov %eax, %fs
mov %eax, %gs
/* %ss is handled by iret */
/* Store stack address in %eax */
mov %esp, %eax
/* Data segmenet with bottom 2 bits set for ring3 */
pushl $0x23
/* Push the stack address */
pushl %eax
/* Push flags and fix interrupt flag */
pushf
popl %eax
/* Request ring3 */
orl $0x200, %eax
pushl %eax
pushl $0x1B
/* Push entry point */
pushl 0x8(%ebp)
iret
popl %ebp
ret

302
modules/ac97.c Normal file
View File

@ -0,0 +1,302 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2015 Michael Gerow
* Copyright (C) 2015 Kevin Lange
*
* Driver for the Intel AC'97.
*
* See <http://www.intel.com/design/chipsets/manuals/29802801.pdf>.
*/
#include <logging.h>
#include <mem.h>
#include <module.h>
#include <mod/snd.h>
#include <printf.h>
#include <pci.h>
#include <system.h>
/* Utility macros */
#define N_ELEMENTS(arr) (sizeof(arr) / sizeof((arr)[0]))
/* BARs! */
#define AC97_NAMBAR 0x10 /* Native Audio Mixer Base Address Register */
#define AC97_NABMBAR 0x14 /* Native Audio Bus Mastering Base Address Register */
/* Bus mastering IO port offsets */
#define AC97_PO_BDBAR 0x10 /* PCM out buffer descriptor BAR */
#define AC97_PO_CIV 0x14 /* PCM out current index value */
#define AC97_PO_LVI 0x15 /* PCM out last valid index */
#define AC97_PO_SR 0x16 /* PCM out status register */
#define AC97_PO_PICB 0x18 /* PCM out position in current buffer register */
#define AC97_PO_CR 0x1B /* PCM out control register */
/* Bus mastering misc */
/* Buffer descriptor list constants */
#define AC97_BDL_LEN 32 /* Buffer descriptor list length */
#define AC97_BDL_BUFFER_LEN 0x1000 /* Length of buffer in BDL */
#define AC97_CL_GET_LENGTH(cl) ((cl) & 0xFFFF) /* Decode length from cl */
#define AC97_CL_SET_LENGTH(cl, v) ((cl) = (v) & 0xFFFF) /* Encode length to cl */
#define AC97_CL_BUP ((uint32_t)1 << 30) /* Buffer underrun policy in cl */
#define AC97_CL_IOC ((uint32_t)1 << 31) /* Interrupt on completion flag in cl */
/* PCM out control register flags */
#define AC97_X_CR_RPBM (1 << 0) /* Run/pause bus master */
#define AC97_X_CR_RR (1 << 1) /* Reset registers */
#define AC97_X_CR_LVBIE (1 << 2) /* Last valid buffer interrupt enable */
#define AC97_X_CR_FEIE (1 << 3) /* FIFO error interrupt enable */
#define AC97_X_CR_IOCE (1 << 4) /* Interrupt on completion enable */
/* Status register flags */
#define AC97_X_SR_DCH (1 << 0) /* DMA controller halted */
#define AC97_X_SR_CELV (1 << 1) /* Current equals last valid */
#define AC97_X_SR_LVBCI (1 << 2) /* Last valid buffer completion interrupt */
#define AC97_X_SR_BCIS (1 << 3) /* Buffer completion interrupt status */
#define AC97_X_SR_FIFOE (1 << 3) /* FIFO error */
/* Mixer IO port offsets */
#define AC97_RESET 0x00
#define AC97_MASTER_VOLUME 0x02
#define AC97_AUX_OUT_VOLUME 0x04
#define AC97_MONO_VOLUME 0x06
#define AC97_PCM_OUT_VOLUME 0x18
/* snd values */
#define AC97_SND_NAME "Intel AC'97"
#define AC97_PLAYBACK_SPEED 48000
#define AC97_PLAYBACK_FORMAT SND_FORMAT_L16SLE
/* An entry in a buffer dscriptor list */
typedef struct {
uint32_t pointer; /* Pointer to buffer */
uint32_t cl; /* Control values and buffer length */
} __attribute__((packed)) ac97_bdl_entry_t;
typedef struct {
uint32_t pci_device;
uint16_t nabmbar; /* Native audio bus mastring BAR */
uint16_t nambar; /* Native audio mixing BAR */
size_t irq; /* This ac97's irq */
uint8_t lvi; /* The currently set last valid index */
uint8_t bits; /* How many bits of volume are supported (5 or 6) */
ac97_bdl_entry_t * bdl; /* Buffer descriptor list */
uint16_t * bufs[AC97_BDL_LEN]; /* Virtual addresses for buffers in BDL */
uint32_t bdl_p;
uint32_t mask;
} ac97_device_t;
static ac97_device_t _device;
#define AC97_KNOB_PCM_OUT (SND_KNOB_VENDOR + 0)
static snd_knob_t _knobs[] = {
{
"Master",
SND_KNOB_MASTER
},
{
"PCM Out",
SND_KNOB_VENDOR + 0
}
};
static int ac97_mixer_read(uint32_t knob_id, uint32_t *val);
static int ac97_mixer_write(uint32_t knob_id, uint32_t val);
static snd_device_t _snd = {
.name = AC97_SND_NAME,
.device = &_device,
.playback_speed = AC97_PLAYBACK_SPEED,
.playback_format = AC97_PLAYBACK_FORMAT,
.knobs = _knobs,
.num_knobs = N_ELEMENTS(_knobs),
.mixer_read = ac97_mixer_read,
.mixer_write = ac97_mixer_write,
};
/*
* This could be unnecessary if we instead allocate just two buffers and make
* the ac97 think there are more.
*/
static void find_ac97(uint32_t device, uint16_t vendorid, uint16_t deviceid, void * extra) {
ac97_device_t * ac97 = extra;
if ((vendorid == 0x8086) && (deviceid == 0x2415)) {
ac97->pci_device = device;
}
}
#define DIVISION 0x1000
static int irq_handler(struct regs * regs) {
uint16_t sr = inports(_device.nabmbar + AC97_PO_SR);
if (sr & AC97_X_SR_LVBCI) {
outports(_device.nabmbar + AC97_PO_SR, AC97_X_SR_LVBCI);
} else if (sr & AC97_X_SR_BCIS) {
size_t f = (_device.lvi + 2) % AC97_BDL_LEN;
for (size_t i = 0; i < AC97_BDL_BUFFER_LEN * sizeof(*_device.bufs[0]); i += DIVISION) {
snd_request_buf(&_snd, DIVISION, (uint8_t *)_device.bufs[f] + i);
switch_task(1);
}
_device.lvi = (_device.lvi + 1) % AC97_BDL_LEN;
outportb(_device.nabmbar + AC97_PO_LVI, _device.lvi);
outports(_device.nabmbar + AC97_PO_SR, AC97_X_SR_BCIS);
} else if (sr & AC97_X_SR_FIFOE) {
outports(_device.nabmbar + AC97_PO_SR, AC97_X_SR_FIFOE);
} else {
return 0;
}
irq_ack(_device.irq);
return 1;
}
/* Currently we just assume right and left are the same */
static int ac97_mixer_read(uint32_t knob_id, uint32_t *val) {
uint16_t tmp;
switch (knob_id) {
case SND_KNOB_MASTER:
tmp = inports(_device.nambar + AC97_MASTER_VOLUME);
if (tmp == 0x8000) {
*val = 0;
} else {
/* 6 bit value */
*val = (tmp & _device.mask) << (sizeof(*val) * 8 - _device.bits);
*val = ~*val;
*val &= (uint32_t)_device.mask << (sizeof(*val) * 8 - _device.bits);
}
break;
case AC97_KNOB_PCM_OUT:
tmp = inports(_device.nambar + AC97_PCM_OUT_VOLUME);
if (tmp == 0x8000) {
*val = 0;
} else {
/* 5 bit value */
*val = (tmp & 0x1f) << (sizeof(*val) * 8 - 5);
*val = ~*val;
*val &= 0x1f << (sizeof(*val) * 8 - 5);
}
break;
default:
return -1;
}
return 0;
}
static int ac97_mixer_write(uint32_t knob_id, uint32_t val) {
switch (knob_id) {
case SND_KNOB_MASTER: {
uint16_t encoded;
if (val == 0x0) {
encoded = 0x8000;
} else {
/* 0 is the highest volume */
val = ~val;
/* 6 bit value */
val >>= (sizeof(val) * 8 - _device.bits);
encoded = (val & 0xFF) | (val << 8);
}
outports(_device.nambar + AC97_MASTER_VOLUME, encoded);
break;
}
case AC97_KNOB_PCM_OUT: {
uint16_t encoded;
if (val == 0x0) {
encoded = 0x8000;
} else {
/* 0 is the highest volume */
val = ~val;
/* 5 bit value */
val >>= (sizeof(val) * 8 - 5);
encoded = (val & 0xFF) | (val << 8);
}
outports(_device.nambar + AC97_PCM_OUT_VOLUME, encoded);
break;
}
default:
return -1;
}
return 0;
}
static int init(void) {
debug_print(NOTICE, "Initializing AC97");
pci_scan(&find_ac97, -1, &_device);
if (!_device.pci_device) {
return 1;
}
_device.nabmbar = pci_read_field(_device.pci_device, AC97_NABMBAR, 2) & ((uint32_t) -1) << 1;
_device.nambar = pci_read_field(_device.pci_device, PCI_BAR0, 4) & ((uint32_t) -1) << 1;
_device.irq = pci_read_field(_device.pci_device, PCI_INTERRUPT_LINE, 1);
irq_install_handler(_device.irq, irq_handler);
/* Enable all matter of interrupts */
outportb(_device.nabmbar + AC97_PO_CR, AC97_X_CR_FEIE | AC97_X_CR_IOCE);
/* Enable bus mastering and disable memory mapped space */
pci_write_field(_device.pci_device, PCI_COMMAND, 2, 0x5);
/* Default the PCM output to full volume. */
outports(_device.nambar + AC97_PCM_OUT_VOLUME, 0x0000);
/* Allocate our BDL and our buffers */
_device.bdl = (void *)kmalloc_p(AC97_BDL_LEN * sizeof(*_device.bdl), &_device.bdl_p);
memset(_device.bdl, 0, AC97_BDL_LEN * sizeof(*_device.bdl));
for (int i = 0; i < AC97_BDL_LEN; i++) {
_device.bufs[i] = (uint16_t *)kmalloc_p(AC97_BDL_BUFFER_LEN * sizeof(*_device.bufs[0]),
&_device.bdl[i].pointer);
memset(_device.bufs[i], 0, AC97_BDL_BUFFER_LEN * sizeof(*_device.bufs[0]));
AC97_CL_SET_LENGTH(_device.bdl[i].cl, AC97_BDL_BUFFER_LEN);
/* Set all buffers to interrupt */
_device.bdl[i].cl |= AC97_CL_IOC;
}
/* Tell the ac97 where our BDL is */
outportl(_device.nabmbar + AC97_PO_BDBAR, _device.bdl_p);
/* Set the LVI to be the last index */
_device.lvi = 2;
outportb(_device.nabmbar + AC97_PO_LVI, _device.lvi);
/* detect whether device supports MSB */
outports(_device.nambar + AC97_MASTER_VOLUME, 0x2020);
uint16_t t = inports(_device.nambar + AC97_MASTER_VOLUME) & 0x1f;
if (t == 0x1f) {
debug_print(WARNING, "This device only supports 5 bits of audio volume.");
_device.bits = 5;
_device.mask = 0x1f;
outports(_device.nambar + AC97_MASTER_VOLUME, 0x0f0f);
} else {
_device.bits = 6;
_device.mask = 0x3f;
outports(_device.nambar + AC97_MASTER_VOLUME, 0x1f1f);
}
snd_register(&_snd);
/* Start things playing */
outportb(_device.nabmbar + AC97_PO_CR, inportb(_device.nabmbar + AC97_PO_CR) | AC97_X_CR_RPBM);
debug_print(NOTICE, "AC97 initialized successfully");
return 0;
}
static int fini(void) {
snd_unregister(&_snd);
free(_device.bdl);
for (int i = 0; i < AC97_BDL_LEN; i++) {
free(_device.bufs[i]);
}
return 0;
}
MODULE_DEF(ac97, init, fini);
MODULE_DEPENDS(snd);

841
modules/ata.c Normal file
View File

@ -0,0 +1,841 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2014 Kevin Lange
*
* ATA Disk Driver
*
* Provides raw block access to an (Parallel) ATA drive.
*/
#include <system.h>
#include <logging.h>
#include <module.h>
#include <fs.h>
#include <printf.h>
#include <list.h>
#include <pci.h>
/* TODO: Move this to mod/ata.h */
#include <ata.h>
static char ata_drive_char = 'a';
static int cdrom_number = 0;
static uint32_t ata_pci = 0x00000000;
static list_t * atapi_waiter;
static int atapi_in_progress = 0;
typedef union {
uint8_t command_bytes[12];
uint16_t command_words[6];
} atapi_command_t;
/* 8086:7010 */
static void find_ata_pci(uint32_t device, uint16_t vendorid, uint16_t deviceid, void * extra) {
if ((vendorid == 0x8086) && (deviceid == 0x7010 || deviceid == 0x7111)) {
*((uint32_t *)extra) = device;
}
}
typedef struct {
uintptr_t offset;
uint16_t bytes;
uint16_t last;
} prdt_t;
struct ata_device {
int io_base;
int control;
int slave;
int is_atapi;
ata_identify_t identity;
prdt_t * dma_prdt;
uintptr_t dma_prdt_phys;
uint8_t * dma_start;
uintptr_t dma_start_phys;
uint32_t bar4;
uint32_t atapi_lba;
uint32_t atapi_sector_size;
};
static struct ata_device ata_primary_master = {.io_base = 0x1F0, .control = 0x3F6, .slave = 0};
static struct ata_device ata_primary_slave = {.io_base = 0x1F0, .control = 0x3F6, .slave = 1};
static struct ata_device ata_secondary_master = {.io_base = 0x170, .control = 0x376, .slave = 0};
static struct ata_device ata_secondary_slave = {.io_base = 0x170, .control = 0x376, .slave = 1};
//static volatile uint8_t ata_lock = 0;
static spin_lock_t ata_lock = { 0 };
/* TODO support other sector sizes */
#define ATA_SECTOR_SIZE 512
static void ata_device_read_sector(struct ata_device * dev, uint32_t lba, uint8_t * buf);
static void ata_device_read_sector_atapi(struct ata_device * dev, uint32_t lba, uint8_t * buf);
static void ata_device_write_sector_retry(struct ata_device * dev, uint32_t lba, uint8_t * buf);
static uint32_t read_ata(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer);
static uint32_t write_ata(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer);
static void open_ata(fs_node_t *node, unsigned int flags);
static void close_ata(fs_node_t *node);
static uint64_t ata_max_offset(struct ata_device * dev) {
uint64_t sectors = dev->identity.sectors_48;
if (!sectors) {
/* Fall back to sectors_28 */
sectors = dev->identity.sectors_28;
}
return sectors * ATA_SECTOR_SIZE;
}
static uint64_t atapi_max_offset(struct ata_device * dev) {
uint64_t max_sector = dev->atapi_lba;
if (!max_sector) return 0;
return (max_sector + 1) * dev->atapi_sector_size;
}
static uint32_t read_ata(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer) {
struct ata_device * dev = (struct ata_device *)node->device;
unsigned int start_block = offset / ATA_SECTOR_SIZE;
unsigned int end_block = (offset + size - 1) / ATA_SECTOR_SIZE;
unsigned int x_offset = 0;
if (offset > ata_max_offset(dev)) {
return 0;
}
if (offset + size > ata_max_offset(dev)) {
unsigned int i = ata_max_offset(dev) - offset;
size = i;
}
if (offset % ATA_SECTOR_SIZE) {
unsigned int prefix_size = (ATA_SECTOR_SIZE - (offset % ATA_SECTOR_SIZE));
char * tmp = malloc(ATA_SECTOR_SIZE);
ata_device_read_sector(dev, start_block, (uint8_t *)tmp);
memcpy(buffer, (void *)((uintptr_t)tmp + (offset % ATA_SECTOR_SIZE)), prefix_size);
free(tmp);
x_offset += prefix_size;
start_block++;
}
if ((offset + size) % ATA_SECTOR_SIZE && start_block <= end_block) {
unsigned int postfix_size = (offset + size) % ATA_SECTOR_SIZE;
char * tmp = malloc(ATA_SECTOR_SIZE);
ata_device_read_sector(dev, end_block, (uint8_t *)tmp);
memcpy((void *)((uintptr_t)buffer + size - postfix_size), tmp, postfix_size);
free(tmp);
end_block--;
}
while (start_block <= end_block) {
ata_device_read_sector(dev, start_block, (uint8_t *)((uintptr_t)buffer + x_offset));
x_offset += ATA_SECTOR_SIZE;
start_block++;
}
return size;
}
static uint32_t read_atapi(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer) {
struct ata_device * dev = (struct ata_device *)node->device;
unsigned int start_block = offset / dev->atapi_sector_size;
unsigned int end_block = (offset + size - 1) / dev->atapi_sector_size;
unsigned int x_offset = 0;
if (offset > atapi_max_offset(dev)) {
return 0;
}
if (offset + size > atapi_max_offset(dev)) {
unsigned int i = atapi_max_offset(dev) - offset;
size = i;
}
if (offset % dev->atapi_sector_size) {
unsigned int prefix_size = (dev->atapi_sector_size - (offset % dev->atapi_sector_size));
char * tmp = malloc(dev->atapi_sector_size);
ata_device_read_sector_atapi(dev, start_block, (uint8_t *)tmp);
memcpy(buffer, (void *)((uintptr_t)tmp + (offset % dev->atapi_sector_size)), prefix_size);
free(tmp);
x_offset += prefix_size;
start_block++;
}
if ((offset + size) % dev->atapi_sector_size && start_block <= end_block) {
unsigned int postfix_size = (offset + size) % dev->atapi_sector_size;
char * tmp = malloc(dev->atapi_sector_size);
ata_device_read_sector_atapi(dev, end_block, (uint8_t *)tmp);
memcpy((void *)((uintptr_t)buffer + size - postfix_size), tmp, postfix_size);
free(tmp);
end_block--;
}
while (start_block <= end_block) {
ata_device_read_sector_atapi(dev, start_block, (uint8_t *)((uintptr_t)buffer + x_offset));
x_offset += dev->atapi_sector_size;
start_block++;
}
return size;
}
static uint32_t write_ata(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer) {
struct ata_device * dev = (struct ata_device *)node->device;
unsigned int start_block = offset / ATA_SECTOR_SIZE;
unsigned int end_block = (offset + size - 1) / ATA_SECTOR_SIZE;
unsigned int x_offset = 0;
if (offset > ata_max_offset(dev)) {
return 0;
}
if (offset + size > ata_max_offset(dev)) {
unsigned int i = ata_max_offset(dev) - offset;
size = i;
}
if (offset % ATA_SECTOR_SIZE) {
unsigned int prefix_size = (ATA_SECTOR_SIZE - (offset % ATA_SECTOR_SIZE));
char * tmp = malloc(ATA_SECTOR_SIZE);
ata_device_read_sector(dev, start_block, (uint8_t *)tmp);
debug_print(NOTICE, "Writing first block");
memcpy((void *)((uintptr_t)tmp + (offset % ATA_SECTOR_SIZE)), buffer, prefix_size);
ata_device_write_sector_retry(dev, start_block, (uint8_t *)tmp);
free(tmp);
x_offset += prefix_size;
start_block++;
}
if ((offset + size) % ATA_SECTOR_SIZE && start_block <= end_block) {
unsigned int postfix_size = (offset + size) % ATA_SECTOR_SIZE;
char * tmp = malloc(ATA_SECTOR_SIZE);
ata_device_read_sector(dev, end_block, (uint8_t *)tmp);
debug_print(NOTICE, "Writing last block");
memcpy(tmp, (void *)((uintptr_t)buffer + size - postfix_size), postfix_size);
ata_device_write_sector_retry(dev, end_block, (uint8_t *)tmp);
free(tmp);
end_block--;
}
while (start_block <= end_block) {
ata_device_write_sector_retry(dev, start_block, (uint8_t *)((uintptr_t)buffer + x_offset));
x_offset += ATA_SECTOR_SIZE;
start_block++;
}
return size;
}
static void open_ata(fs_node_t * node, unsigned int flags) {
return;
}
static void close_ata(fs_node_t * node) {
return;
}
static fs_node_t * atapi_device_create(struct ata_device * device) {
fs_node_t * fnode = malloc(sizeof(fs_node_t));
memset(fnode, 0x00, sizeof(fs_node_t));
fnode->inode = 0;
sprintf(fnode->name, "cdrom%d", cdrom_number);
fnode->device = device;
fnode->uid = 0;
fnode->gid = 0;
fnode->mask = 0660;
fnode->length = atapi_max_offset(device);
fnode->flags = FS_BLOCKDEVICE;
fnode->read = read_atapi;
fnode->write = NULL; /* no write support */
fnode->open = open_ata;
fnode->close = close_ata;
fnode->readdir = NULL;
fnode->finddir = NULL;
fnode->ioctl = NULL; /* TODO, identify, etc? */
return fnode;
}
static fs_node_t * ata_device_create(struct ata_device * device) {
fs_node_t * fnode = malloc(sizeof(fs_node_t));
memset(fnode, 0x00, sizeof(fs_node_t));
fnode->inode = 0;
sprintf(fnode->name, "atadev%d", ata_drive_char - 'a');
fnode->device = device;
fnode->uid = 0;
fnode->gid = 0;
fnode->mask = 0660;
fnode->length = ata_max_offset(device); /* TODO */
fnode->flags = FS_BLOCKDEVICE;
fnode->read = read_ata;
fnode->write = write_ata;
fnode->open = open_ata;
fnode->close = close_ata;
fnode->readdir = NULL;
fnode->finddir = NULL;
fnode->ioctl = NULL; /* TODO, identify, etc? */
return fnode;
}
static void ata_io_wait(struct ata_device * dev) {
inportb(dev->io_base + ATA_REG_ALTSTATUS);
inportb(dev->io_base + ATA_REG_ALTSTATUS);
inportb(dev->io_base + ATA_REG_ALTSTATUS);
inportb(dev->io_base + ATA_REG_ALTSTATUS);
}
static int ata_status_wait(struct ata_device * dev, int timeout) {
int status;
if (timeout > 0) {
int i = 0;
while ((status = inportb(dev->io_base + ATA_REG_STATUS)) & ATA_SR_BSY && (i < timeout)) i++;
} else {
while ((status = inportb(dev->io_base + ATA_REG_STATUS)) & ATA_SR_BSY);
}
return status;
}
static int ata_wait(struct ata_device * dev, int advanced) {
uint8_t status = 0;
ata_io_wait(dev);
status = ata_status_wait(dev, -1);
if (advanced) {
status = inportb(dev->io_base + ATA_REG_STATUS);
if (status & ATA_SR_ERR) return 1;
if (status & ATA_SR_DF) return 1;
if (!(status & ATA_SR_DRQ)) return 1;
}
return 0;
}
static void ata_soft_reset(struct ata_device * dev) {
outportb(dev->control, 0x04);
ata_io_wait(dev);
outportb(dev->control, 0x00);
}
static int ata_irq_handler(struct regs *r) {
inportb(ata_primary_master.io_base + ATA_REG_STATUS);
if (atapi_in_progress) {
wakeup_queue(atapi_waiter);
}
irq_ack(14);
return 1;
}
static int ata_irq_handler_s(struct regs *r) {
inportb(ata_secondary_master.io_base + ATA_REG_STATUS);
if (atapi_in_progress) {
wakeup_queue(atapi_waiter);
}
irq_ack(15);
return 1;
}
static void ata_device_init(struct ata_device * dev) {
debug_print(NOTICE, "Initializing IDE device on bus %d", dev->io_base);
outportb(dev->io_base + 1, 1);
outportb(dev->control, 0);
outportb(dev->io_base + ATA_REG_HDDEVSEL, 0xA0 | dev->slave << 4);
ata_io_wait(dev);
outportb(dev->io_base + ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
ata_io_wait(dev);
int status = inportb(dev->io_base + ATA_REG_COMMAND);
debug_print(INFO, "Device status: %d", status);
ata_wait(dev, 0);
uint16_t * buf = (uint16_t *)&dev->identity;
for (int i = 0; i < 256; ++i) {
buf[i] = inports(dev->io_base);
}
uint8_t * ptr = (uint8_t *)&dev->identity.model;
for (int i = 0; i < 39; i+=2) {
uint8_t tmp = ptr[i+1];
ptr[i+1] = ptr[i];
ptr[i] = tmp;
}
dev->is_atapi = 0;
debug_print(NOTICE, "Device Name: %s", dev->identity.model);
debug_print(NOTICE, "Sectors (48): %d", (uint32_t)dev->identity.sectors_48);
debug_print(NOTICE, "Sectors (24): %d", dev->identity.sectors_28);
debug_print(NOTICE, "Setting up DMA...");
dev->dma_prdt = (void *)kvmalloc_p(sizeof(prdt_t) * 1, &dev->dma_prdt_phys);
dev->dma_start = (void *)kvmalloc_p(4096, &dev->dma_start_phys);
debug_print(NOTICE, "Putting prdt at 0x%x (0x%x phys)", dev->dma_prdt, dev->dma_prdt_phys);
debug_print(NOTICE, "Putting prdt[0] at 0x%x (0x%x phys)", dev->dma_start, dev->dma_start_phys);
dev->dma_prdt[0].offset = dev->dma_start_phys;
dev->dma_prdt[0].bytes = 512;
dev->dma_prdt[0].last = 0x8000;
debug_print(NOTICE, "ATA PCI device ID: 0x%x", ata_pci);
uint16_t command_reg = pci_read_field(ata_pci, PCI_COMMAND, 4);
debug_print(NOTICE, "COMMAND register before: 0x%4x", command_reg);
if (command_reg & (1 << 2)) {
debug_print(NOTICE, "Bus mastering already enabled.");
} else {
command_reg |= (1 << 2); /* bit 2 */
debug_print(NOTICE, "Enabling bus mastering...");
pci_write_field(ata_pci, PCI_COMMAND, 4, command_reg);
command_reg = pci_read_field(ata_pci, PCI_COMMAND, 4);
debug_print(NOTICE, "COMMAND register after: 0x%4x", command_reg);
}
dev->bar4 = pci_read_field(ata_pci, PCI_BAR4, 4);
debug_print(NOTICE, "BAR4: 0x%x", dev->bar4);
if (dev->bar4 & 0x00000001) {
dev->bar4 = dev->bar4 & 0xFFFFFFFC;
} else {
debug_print(WARNING, "? ATA bus master registers are /usually/ I/O ports.\n");
return; /* No DMA because we're not sure what to do here */
}
#if 0
pci_write_field(ata_pci, PCI_INTERRUPT_LINE, 1, 0xFE);
if (pci_read_field(ata_pci, PCI_INTERRUPT_LINE, 1) == 0xFE) {
/* needs assignment */
pci_write_field(ata_pci, PCI_INTERRUPT_LINE, 1, 14);
}
#endif
}
static void atapi_device_init(struct ata_device * dev) {
dev->is_atapi = 1;
outportb(dev->io_base + 1, 1);
outportb(dev->control, 0);
outportb(dev->io_base + ATA_REG_HDDEVSEL, 0xA0 | dev->slave << 4);
ata_io_wait(dev);
outportb(dev->io_base + ATA_REG_COMMAND, ATA_CMD_IDENTIFY_PACKET);
ata_io_wait(dev);
int status = inportb(dev->io_base + ATA_REG_COMMAND);
debug_print(INFO, "Device status: %d", status);
ata_wait(dev, 0);
uint16_t * buf = (uint16_t *)&dev->identity;
for (int i = 0; i < 256; ++i) {
buf[i] = inports(dev->io_base);
}
uint8_t * ptr = (uint8_t *)&dev->identity.model;
for (int i = 0; i < 39; i+=2) {
uint8_t tmp = ptr[i+1];
ptr[i+1] = ptr[i];
ptr[i] = tmp;
}
debug_print(NOTICE, "Device Name: %s", dev->identity.model);
/* Detect medium */
atapi_command_t command;
command.command_bytes[0] = 0x25;
command.command_bytes[1] = 0;
command.command_bytes[2] = 0;
command.command_bytes[3] = 0;
command.command_bytes[4] = 0;
command.command_bytes[5] = 0;
command.command_bytes[6] = 0;
command.command_bytes[7] = 0;
command.command_bytes[8] = 0; /* bit 0 = PMI (0, last sector) */
command.command_bytes[9] = 0; /* control */
command.command_bytes[10] = 0;
command.command_bytes[11] = 0;
uint16_t bus = dev->io_base;
outportb(bus + ATA_REG_FEATURES, 0x00);
outportb(bus + ATA_REG_LBA1, 0x08);
outportb(bus + ATA_REG_LBA2, 0x08);
outportb(bus + ATA_REG_COMMAND, ATA_CMD_PACKET);
/* poll */
while (1) {
uint8_t status = inportb(dev->io_base + ATA_REG_STATUS);
if ((status & ATA_SR_ERR)) goto atapi_error;
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY)) break;
}
for (int i = 0; i < 6; ++i) {
outports(bus, command.command_words[i]);
}
/* poll */
while (1) {
uint8_t status = inportb(dev->io_base + ATA_REG_STATUS);
if ((status & ATA_SR_ERR)) goto atapi_error_read;
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY)) break;
if ((status & ATA_SR_DRQ)) break;
}
uint16_t data[4];
for (int i = 0; i < 4; ++i) {
data[i] = inports(bus);
}
#define htonl(l) ( (((l) & 0xFF) << 24) | (((l) & 0xFF00) << 8) | (((l) & 0xFF0000) >> 8) | (((l) & 0xFF000000) >> 24))
uint32_t lba, blocks;;
memcpy(&lba, &data[0], sizeof(uint32_t));
lba = htonl(lba);
memcpy(&blocks, &data[2], sizeof(uint32_t));
blocks = htonl(blocks);
dev->atapi_lba = lba;
dev->atapi_sector_size = blocks;
debug_print(WARNING, "Finished! LBA = %x; block length = %x", lba, blocks);
return;
atapi_error_read:
debug_print(ERROR, "ATAPI error; no medium?");
return;
atapi_error:
debug_print(ERROR, "ATAPI early error; unsure");
return;
}
static int ata_device_detect(struct ata_device * dev) {
ata_soft_reset(dev);
ata_io_wait(dev);
outportb(dev->io_base + ATA_REG_HDDEVSEL, 0xA0 | dev->slave << 4);
ata_io_wait(dev);
ata_status_wait(dev, 10000);
unsigned char cl = inportb(dev->io_base + ATA_REG_LBA1); /* CYL_LO */
unsigned char ch = inportb(dev->io_base + ATA_REG_LBA2); /* CYL_HI */
debug_print(NOTICE, "Device detected: 0x%2x 0x%2x", cl, ch);
if (cl == 0xFF && ch == 0xFF) {
/* Nothing here */
return 0;
}
if ((cl == 0x00 && ch == 0x00) ||
(cl == 0x3C && ch == 0xC3)) {
/* Parallel ATA device, or emulated SATA */
char devname[64];
sprintf((char *)&devname, "/dev/hd%c", ata_drive_char);
fs_node_t * node = ata_device_create(dev);
vfs_mount(devname, node);
ata_drive_char++;
ata_device_init(dev);
return 1;
} else if ((cl == 0x14 && ch == 0xEB) ||
(cl == 0x69 && ch == 0x96)) {
debug_print(WARNING, "Detected ATAPI device at io-base 0x%3x, control 0x%3x, slave %d", dev->io_base, dev->control, dev->slave);
char devname[64];
sprintf((char *)&devname, "/dev/cdrom%d", cdrom_number);
atapi_device_init(dev);
fs_node_t * node = atapi_device_create(dev);
vfs_mount(devname, node);
cdrom_number++;
return 2;
}
/* TODO: ATAPI, SATA, SATAPI */
return 0;
}
static void ata_device_read_sector(struct ata_device * dev, uint32_t lba, uint8_t * buf) {
uint16_t bus = dev->io_base;
uint8_t slave = dev->slave;
if (dev->is_atapi) return;
spin_lock(ata_lock);
#if 0
int errors = 0;
try_again:
#endif
ata_wait(dev, 0);
/* Stop */
outportb(dev->bar4, 0x00);
/* Set the PRDT */
outportl(dev->bar4 + 0x04, dev->dma_prdt_phys);
/* Enable error, irq status */
outportb(dev->bar4 + 0x2, inportb(dev->bar4 + 0x02) | 0x04 | 0x02);
/* set read */
outportb(dev->bar4, 0x08);
IRQ_ON;
while (1) {
uint8_t status = inportb(dev->io_base + ATA_REG_STATUS);
if (!(status & ATA_SR_BSY)) break;
}
outportb(bus + ATA_REG_CONTROL, 0x00);
outportb(bus + ATA_REG_HDDEVSEL, 0xe0 | slave << 4 | (lba & 0x0f000000) >> 24);
ata_io_wait(dev);
outportb(bus + ATA_REG_FEATURES, 0x00);
outportb(bus + ATA_REG_SECCOUNT0, 1);
outportb(bus + ATA_REG_LBA0, (lba & 0x000000ff) >> 0);
outportb(bus + ATA_REG_LBA1, (lba & 0x0000ff00) >> 8);
outportb(bus + ATA_REG_LBA2, (lba & 0x00ff0000) >> 16);
//outportb(bus + ATA_REG_COMMAND, ATA_CMD_READ_PIO);
#if 1
while (1) {
uint8_t status = inportb(dev->io_base + ATA_REG_STATUS);
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY)) break;
}
#endif
outportb(bus + ATA_REG_COMMAND, ATA_CMD_READ_DMA);
ata_io_wait(dev);
outportb(dev->bar4, 0x08 | 0x01);
while (1) {
int status = inportb(dev->bar4 + 0x02);
int dstatus = inportb(dev->io_base + ATA_REG_STATUS);
if (!(status & 0x04)) {
continue;
}
if (!(dstatus & ATA_SR_BSY)) {
break;
}
}
IRQ_OFF;
#if 0
if (ata_wait(dev, 1)) {
debug_print(WARNING, "Error during ATA read of lba block %d", lba);
errors++;
if (errors > 4) {
debug_print(WARNING, "-- Too many errors trying to read this block. Bailing.");
spin_unlock(ata_lock);
return;
}
goto try_again;
}
#endif
/* Copy from DMA buffer to output buffer. */
memcpy(buf, dev->dma_start, 512);
/* Inform device we are done. */
outportb(dev->bar4 + 0x2, inportb(dev->bar4 + 0x02) | 0x04 | 0x02);
#if 0
int size = 256;
inportsm(bus,buf,size);
ata_wait(dev, 0);
outportb(bus + ATA_REG_CONTROL, 0x02);
#endif
spin_unlock(ata_lock);
}
static void ata_device_read_sector_atapi(struct ata_device * dev, uint32_t lba, uint8_t * buf) {
if (!dev->is_atapi) return;
uint16_t bus = dev->io_base;
spin_lock(ata_lock);
outportb(dev->io_base + ATA_REG_HDDEVSEL, 0xA0 | dev->slave << 4);
ata_io_wait(dev);
outportb(bus + ATA_REG_FEATURES, 0x00);
outportb(bus + ATA_REG_LBA1, dev->atapi_sector_size & 0xFF);
outportb(bus + ATA_REG_LBA2, dev->atapi_sector_size >> 8);
outportb(bus + ATA_REG_COMMAND, ATA_CMD_PACKET);
/* poll */
while (1) {
uint8_t status = inportb(dev->io_base + ATA_REG_STATUS);
if ((status & ATA_SR_ERR)) goto atapi_error_on_read_setup;
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRQ)) break;
}
atapi_in_progress = 1;
atapi_command_t command;
command.command_bytes[0] = 0xA8;
command.command_bytes[1] = 0;
command.command_bytes[2] = (lba >> 0x18) & 0xFF;
command.command_bytes[3] = (lba >> 0x10) & 0xFF;
command.command_bytes[4] = (lba >> 0x08) & 0xFF;
command.command_bytes[5] = (lba >> 0x00) & 0xFF;
command.command_bytes[6] = 0;
command.command_bytes[7] = 0;
command.command_bytes[8] = 0; /* bit 0 = PMI (0, last sector) */
command.command_bytes[9] = 1; /* control */
command.command_bytes[10] = 0;
command.command_bytes[11] = 0;
for (int i = 0; i < 6; ++i) {
outports(bus, command.command_words[i]);
}
/* Wait */
sleep_on(atapi_waiter);
atapi_in_progress = 0;
while (1) {
uint8_t status = inportb(dev->io_base + ATA_REG_STATUS);
if ((status & ATA_SR_ERR)) goto atapi_error_on_read_setup;
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRQ)) break;
}
uint16_t size_to_read = inportb(bus + ATA_REG_LBA2) << 8;
size_to_read = size_to_read | inportb(bus + ATA_REG_LBA1);
inportsm(bus,buf,size_to_read/2);
while (1) {
uint8_t status = inportb(dev->io_base + ATA_REG_STATUS);
if ((status & ATA_SR_ERR)) goto atapi_error_on_read_setup;
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY)) break;
}
atapi_error_on_read_setup:
spin_unlock(ata_lock);
}
static void ata_device_write_sector(struct ata_device * dev, uint32_t lba, uint8_t * buf) {
uint16_t bus = dev->io_base;
uint8_t slave = dev->slave;
spin_lock(ata_lock);
outportb(bus + ATA_REG_CONTROL, 0x02);
ata_wait(dev, 0);
outportb(bus + ATA_REG_HDDEVSEL, 0xe0 | slave << 4 | (lba & 0x0f000000) >> 24);
ata_wait(dev, 0);
outportb(bus + ATA_REG_FEATURES, 0x00);
outportb(bus + ATA_REG_SECCOUNT0, 0x01);
outportb(bus + ATA_REG_LBA0, (lba & 0x000000ff) >> 0);
outportb(bus + ATA_REG_LBA1, (lba & 0x0000ff00) >> 8);
outportb(bus + ATA_REG_LBA2, (lba & 0x00ff0000) >> 16);
outportb(bus + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO);
ata_wait(dev, 0);
int size = ATA_SECTOR_SIZE / 2;
outportsm(bus,buf,size);
outportb(bus + 0x07, ATA_CMD_CACHE_FLUSH);
ata_wait(dev, 0);
spin_unlock(ata_lock);
}
static int buffer_compare(uint32_t * ptr1, uint32_t * ptr2, size_t size) {
assert(!(size % 4));
size_t i = 0;
while (i < size) {
if (*ptr1 != *ptr2) return 1;
ptr1++;
ptr2++;
i += sizeof(uint32_t);
}
return 0;
}
static void ata_device_write_sector_retry(struct ata_device * dev, uint32_t lba, uint8_t * buf) {
uint8_t * read_buf = malloc(ATA_SECTOR_SIZE);
do {
ata_device_write_sector(dev, lba, buf);
ata_device_read_sector(dev, lba, read_buf);
} while (buffer_compare((uint32_t *)buf, (uint32_t *)read_buf, ATA_SECTOR_SIZE));
free(read_buf);
}
static int ata_initialize(void) {
/* Detect drives and mount them */
/* Locate ATA device via PCI */
pci_scan(&find_ata_pci, -1, &ata_pci);
irq_install_handler(14, ata_irq_handler);
irq_install_handler(15, ata_irq_handler_s);
atapi_waiter = list_create();
ata_device_detect(&ata_primary_master);
ata_device_detect(&ata_primary_slave);
ata_device_detect(&ata_secondary_master);
ata_device_detect(&ata_secondary_slave);
return 0;
}
static int ata_finalize(void) {
return 0;
}
MODULE_DEF(ata, ata_initialize, ata_finalize);

Some files were not shown because too many files have changed in this diff Show More