Merge kernel
This commit is contained in:
parent
690a47836a
commit
3f4293d357
81
Makefile
81
Makefile
@ -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
3
kernel/.gdb_history
Normal file
@ -0,0 +1,3 @@
|
||||
info line *0x00112449
|
||||
info line *0x00100ba7
|
||||
info line *0x00100ba7
|
60
kernel/boot.S
Normal file
60
kernel/boot.S
Normal 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
102
kernel/cpu/gdt.c
Normal 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
53
kernel/cpu/idt.c
Normal 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
164
kernel/cpu/irq.c
Normal 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
94
kernel/cpu/isr.c
Normal 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
194
kernel/devices/cmos.c
Normal 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
142
kernel/devices/fpu.c
Normal 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
113
kernel/devices/pci.c
Normal 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
90
kernel/devices/timer.c
Normal 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
64
kernel/ds/bitset.c
Normal 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
217
kernel/ds/hashmap.c
Normal 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
248
kernel/ds/list.c
Normal 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
170
kernel/ds/ringbuffer.c
Normal 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
192
kernel/ds/tree.c
Normal 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
296
kernel/fs/pipe.c
Normal 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
110
kernel/fs/ramdisk.c
Normal 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
435
kernel/fs/tty.c
Normal 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
150
kernel/fs/unixpipe.c
Normal 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
1044
kernel/fs/vfs.c
Normal file
File diff suppressed because it is too large
Load Diff
20
kernel/gdt.S
Normal file
20
kernel/gdt.S
Normal 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
10
kernel/idt.S
Normal 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
9
kernel/include/args.h
Normal 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
141
kernel/include/ata.h
Normal 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
19
kernel/include/bitset.h
Normal 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
20
kernel/include/boot.h
Normal 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
185
kernel/include/elf.h
Normal 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
1
kernel/include/errno_defs.h
Symbolic link
@ -0,0 +1 @@
|
||||
../../include/errno.h
|
174
kernel/include/ext2.h
Normal file
174
kernel/include/ext2.h
Normal 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
170
kernel/include/fs.h
Normal 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
47
kernel/include/hashmap.h
Normal 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
1
kernel/include/ioctl.h
Symbolic link
@ -0,0 +1 @@
|
||||
../../include/sys/ioctl.h
|
188
kernel/include/ipv4.h
Normal file
188
kernel/include/ipv4.h
Normal 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
37
kernel/include/libc.h
Normal 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
50
kernel/include/list.h
Normal 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
27
kernel/include/logging.h
Normal 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
16
kernel/include/mem.h
Normal 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
32
kernel/include/mod/net.h
Normal 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
4
kernel/include/mod/rtl.h
Normal file
@ -0,0 +1,4 @@
|
||||
#ifndef KERNEL_MOD_RTL_H
|
||||
#define KERNEL_MOD_RTL_H
|
||||
|
||||
#endif
|
34
kernel/include/mod/shell.h
Normal file
34
kernel/include/mod/shell.h
Normal 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
52
kernel/include/mod/snd.h
Normal 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 */
|
36
kernel/include/mod/sound.h
Normal file
36
kernel/include/mod/sound.h
Normal 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
|
||||
|
38
kernel/include/mod/tmpfs.h
Normal file
38
kernel/include/mod/tmpfs.h
Normal 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
41
kernel/include/module.h
Normal 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
20
kernel/include/mouse.h
Normal 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
|
||||
|
92
kernel/include/multiboot.h
Normal file
92
kernel/include/multiboot.h
Normal 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
69
kernel/include/pci.h
Normal 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
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
27
kernel/include/pipe.h
Normal 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
8
kernel/include/printf.h
Normal 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
156
kernel/include/process.h
Normal 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);
|
||||
|
26
kernel/include/ringbuffer.h
Normal file
26
kernel/include/ringbuffer.h
Normal 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
42
kernel/include/shm.h
Normal 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
11
kernel/include/signal.h
Normal 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>
|
||||
|
1
kernel/include/signal_defs.h
Symbolic link
1
kernel/include/signal_defs.h
Symbolic link
@ -0,0 +1 @@
|
||||
../../include/sys/signal_defs.h
|
1
kernel/include/syscall_nums.h
Symbolic link
1
kernel/include/syscall_nums.h
Symbolic link
@ -0,0 +1 @@
|
||||
../../include/syscall_nums.h
|
234
kernel/include/system.h
Normal file
234
kernel/include/system.h
Normal 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
25
kernel/include/task.h
Normal 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
1
kernel/include/termemu.h
Symbolic link
@ -0,0 +1 @@
|
||||
../../lib/termemu.h
|
1
kernel/include/termios.h
Symbolic link
1
kernel/include/termios.h
Symbolic link
@ -0,0 +1 @@
|
||||
../../include/sys/termios.h
|
4
kernel/include/tokenize.h
Normal file
4
kernel/include/tokenize.h
Normal file
@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
int tokenize(char *, char *, char **);
|
||||
|
37
kernel/include/tree.h
Normal file
37
kernel/include/tree.h
Normal 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
37
kernel/include/tss.h
Normal 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
18
kernel/include/types.h
Normal 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
68
kernel/include/ubsan.h
Normal 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
1
kernel/include/utsname.h
Symbolic link
@ -0,0 +1 @@
|
||||
../../include/sys/utsname.h
|
8
kernel/include/va_list.h
Normal file
8
kernel/include/va_list.h
Normal 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
19
kernel/include/version.h
Normal 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
18
kernel/include/video.h
Normal 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
67
kernel/irq.S
Normal 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
91
kernel/isr.S
Normal 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
454
kernel/libc.c
Normal 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
49
kernel/link.ld
Normal 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
255
kernel/main.c
Normal 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
969
kernel/mem/alloc.c
Normal 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
564
kernel/mem/mem.c
Normal 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 = ¤t_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], ¤t_directory->tables[i], &kernel_directory->tables[i], i * 0x1000 * 1024);
|
||||
for (uint16_t j = 0; j < 1024; ++j) {
|
||||
#if 1
|
||||
page_t * p= ¤t_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], ¤t_directory->tables[i], i * 0x1000 * 1024, kernel_directory->tables[i]);
|
||||
for (uint16_t j = 0; j < 1024; ++j) {
|
||||
#if 1
|
||||
page_t * p= ¤t_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
369
kernel/mem/shm.c
Normal 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
91
kernel/misc/args.c
Normal 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
322
kernel/misc/elf.c
Normal 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 *)¤t_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
158
kernel/misc/kprintf.c
Normal 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
53
kernel/misc/logging.c
Normal 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
76
kernel/misc/multiboot.c
Normal 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
23
kernel/misc/tokenize.c
Normal 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
76
kernel/misc/ubsan.c
Normal 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
57
kernel/spin.c
Normal 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
2791
kernel/symbols.S
Normal file
File diff suppressed because it is too large
Load Diff
390
kernel/sys/module.c
Normal file
390
kernel/sys/module.c
Normal 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
103
kernel/sys/panic.c
Normal 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
963
kernel/sys/process.c
Normal 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 *)¤t_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
227
kernel/sys/signal.c
Normal 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
975
kernel/sys/syscall.c
Normal 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
88
kernel/sys/system.c
Normal 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
532
kernel/sys/task.c
Normal 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
56
kernel/sys/version.c
Normal 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
64
kernel/task.S
Normal 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
10
kernel/tss.S
Normal 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
61
kernel/user.S
Normal 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
302
modules/ac97.c
Normal 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
841
modules/ata.c
Normal 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
Loading…
Reference in New Issue
Block a user