diff --git a/hermit/Makefile b/hermit/Makefile new file mode 100644 index 000000000..838940c3c --- /dev/null +++ b/hermit/Makefile @@ -0,0 +1,116 @@ +TERM = xterm +TOPDIR = $(shell pwd) +ARCH = x86 +NAME = hermit +KERNDIRS = kernel mm libkern arch/$(ARCH)/kernel arch/$(ARCH)/mm +SUBDIRS = $(KERNDIRS) + +# Set your own cross compiler tool chain prefix here +CROSSCOMPREFIX = +STACKPROT = -fno-stack-protector + +CC_FOR_TARGET = $(CROSSCOMPREFIX)gcc +CXX_FOR_TARGET = $(CROSSCOMPREFIX)g++ +GCC_FOR_TARGET = $(CROSSCOMPREFIX)gcc +CPP_FOR_TARGET = $(CROSSCOMPREFIX)cpp +AR_FOR_TARGET = $(CROSSCOMPREFIX)ar +AS_FOR_TARGET = $(CROSSCOMPREFIX)as +LD_FOR_TARGET = $(CROSSCOMPREFIX)ld +NM_FOR_TARGET = $(CROSSCOMPREFIX)nm +OBJDUMP_FOR_TARGET = $(CROSSCOMPREFIX)objdump +OBJCOPY_FOR_TARGET = $(CROSSCOMPREFIX)objcopy +RANLIB_FOR_TARGET = $(CROSSCOMPREFIX)ranlib +STRIP_FOR_TARGET = $(CROSSCOMPREFIX)strip +READELF_FOR_TARGET = $(CROSSCOMPREFIX)readelf + +MAKE = make +NASM = nasm +NASMFLAGS = -felf64 -g -i$(TOPDIR)/include/hermit/ +GDB = $(CROSSCOMPREFIX)gdb +GDBFLAGS = -x debug.gdb +QEMU = qemu-system-x86_64 +QEMUFLAGS = -smp 2 -monitor stdio \ + -net nic,model=rtl8139 \ + -net user,hostfwd=tcp::12345-:7 +QEMUDEBUGFLAGS = -monitor none -daemonize \ + -net nic,model=rtl8139 \ + -net user,hostfwd=tcp::12345-:7 +QEMUSERIALFLAGS = -device pci-serial,chardev=tS0 \ + -chardev socket,host=localhost,port=4555,server,id=tS0 + +INCLUDE = -I$(TOPDIR)/include -I$(TOPDIR)/arch/$(ARCH)/include +# Compiler options for final code +CFLAGS = -g -m64 -Wall -O2 -mno-red-zone -fstrength-reduce -fomit-frame-pointer -finline-functions -ffreestanding -nostdinc -fno-stack-protector $(INCLUDE) +# Compiler options for debugging +debug debug-eclipse : CFLAGS = -g -O0 -m64 -Wall -fno-builtin -DWITH_FRAME_POINTER -nostdinc -mno-red-zone -fno-stack-protector $(INCLUDE) +AR = ar +ARFLAGS = rsv +RM = rm -rf +LDFLAGS = -T link.ld -z max-page-size=4096 --defsym __BUILD_DATE=$(shell date +'%Y%m%d') --defsym __BUILD_TIME=$(shell date +'%H%M%S') +STRIP_DEBUG = --strip-debug +KEEP_DEBUG = --only-keep-debug +OUTPUT_FORMAT = -O elf32-i386 + +# Prettify output +V = 0 +ifeq ($V,0) + Q = @ + P = > /dev/null +endif + +default: all + +all: $(NAME).elf + +$(NAME).elf: + $Q$(LD_FOR_TARGET) $(LDFLAGS) -o $(NAME).elf $^ + @echo [OBJCOPY] $(NAME).bin + $Q$(OBJCOPY_FOR_TARGET) -j .mboot -j .kmsg -j .text -j .data -j .rodata -j .bss -O binary $(NAME).elf $(NAME).bin + #@echo [OBJCOPY] $(NAME).sym + #$Q$(OBJCOPY_FOR_TARGET) $(KEEP_DEBUG) $(NAME).elf $(NAME).sym + #@echo [OBJCOPY] $(NAME).elf + #$Q$(OBJCOPY_FOR_TARGET) $(STRIP_DEBUG) $(OUTPUT_FORMAT) $(NAME).elf + +clean: + $Q$(RM) $(NAME).elf $(NAME).sym $(NAME).bin *~ + @echo Cleaned. + +veryclean: clean + $Q$(RM) qemu-vlan0.pcap include/hermit/config.inc + @echo Very cleaned + +qemu: all + $(QEMU) $(QEMUFLAGS) -kernel $(NAME).elf + +debug: all + $(TERM) -e $(GDB) $(GDBFLAGS) & + $(QEMU) $(QEMUDEBUGFLAGS) -s -S -kernel $(NAME).elf + +doc: + @echo Create documentation... + @doxygen + +%.o : %.c + @echo [CC] $@ + $Q$(CC_FOR_TARGET) -c -D__KERNEL__ $(CFLAGS) -o $@ $< + @echo [DEP] $*.dep + $Q$(CC_FOR_TARGET) -MF $*.dep -MT $*.o -MM $(CFLAGS) $< + +include/hermit/config.inc: include/hermit/config.h + @echo "; This file is generated automatically from the config.h file." > include/hermit/config.inc + @echo "; Before editing this, you should consider editing config.h." >> include/hermit/config.inc + @awk '/^#define KERNEL_STACK_SIZE/{ print "%define KERNEL_STACK_SIZE", $$3 }' include/hermit/config.h >> include/hermit/config.inc + @awk '/^#define VIDEO_MEM_ADDR/{ print "%define VIDEO_MEM_ADDR", $$3 }' include/hermit/config.h >> include/hermit/config.inc + @awk '/^#define CONFIG_VGA/{ print "%define CONFIG_VGA" }' include/hermit/config.h >> include/hermit/config.inc + +%.o : %.asm include/hermit/config.inc + @echo [ASM] $@ + $Q$(NASM) $(NASMFLAGS) -o $@ $< + +%.o : %.S + @echo [GCC-ASM] $@ + $Q$(CC_FOR_TARGET) $(CFLAGS) -c -o $@ $< + +.PHONY: default all clean emu gdb + +include $(addsuffix /Makefile,$(SUBDIRS)) diff --git a/hermit/Makefile.inc b/hermit/Makefile.inc new file mode 100644 index 000000000..743cacc25 --- /dev/null +++ b/hermit/Makefile.inc @@ -0,0 +1,20 @@ +C_source-$(MODULE) := $(addprefix $(subst _,/,$(MODULE))/,$(filter %.c,$(C_source))) +ASM_source-$(MODULE) := $(addprefix $(subst _,/,$(MODULE))/,$(filter %.asm,$(ASM_source))) +C_source := +ASM_source := + +OBJS-$(MODULE) := $(C_source-$(MODULE):.c=.o) +OBJS-$(MODULE) += $(ASM_source-$(MODULE):.asm=.o) + +$(MODULE): $(OBJS-$(MODULE)) + +$(NAME).elf: $(OBJS-$(MODULE)) + +clean: clean-$(MODULE) +clean-$(MODULE): clean-% : + @echo Cleaning $(subst _,/,$*) + $Q$(RM) $(OBJS-$*) $(C_source-$*:.c=.dep) + +.PHONY: clean-$(MODULE) $(MODULE) + +-include $(C_source-$(MODULE):.c=.dep) diff --git a/hermit/arch/x86/include/asm/atomic.h b/hermit/arch/x86/include/asm/atomic.h new file mode 100644 index 000000000..f71c15e39 --- /dev/null +++ b/hermit/arch/x86/include/asm/atomic.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file arch/x86/include/asm/atomic.h + * @brief Functions for atomic operations + * + * This file defines functions for atomic operations on int32 variables + * which will be used in locking-mechanisms. + */ + +#ifndef __ARCH_ATOMIC_H__ +#define __ARCH_ATOMIC_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if MAX_CORES > 1 +#define LOCK "lock ; " +#else +#define LOCK "" +#endif + +/** @brief Makro for initialization of atomic_int32_t vars + * + * Whenever you use an atomic_int32_t variable, init it with + * this macro first.\n + * Example: atomic_int32_t myAtomicVar = ATOMIC_INIT(123); + * + * @param i The number value you want to init it with. + */ +#define ATOMIC_INIT(i) { (i) } + +/** @brief Standard-datatype for atomic operations + * + * It just consists of an int32_t variable internally, marked as volatile. + */ +typedef struct { volatile int32_t counter; } atomic_int32_t; + +/** @brief Atomic test and set operation for int32 vars. + * + * This function will atomically exchange the value of an atomic variable and + * return its old value. Is used in locking-operations.\n + * \n + * Intel manuals: If a memory operand is referenced, the processor's locking + * protocol is automatically implemented for the duration of the exchange + * operation, regardless of the presence or absence of the LOCK prefix. + * + * @param d Pointer to the atomic_int_32_t with the value you want to exchange + * @param ret the value you want the var test for + * + * @return The old value of the atomic_int_32_t var before exchange + */ +inline static int32_t atomic_int32_test_and_set(atomic_int32_t* d, int32_t ret) +{ + asm volatile ("xchgl %0, %1" : "=r"(ret) : "m"(d->counter), "0"(ret) : "memory"); + return ret; +} + +/** @brief Atomic addition of values to atomic_int32_t vars + * + * This function lets you add values in an atomic operation + * + * @param d Pointer to the atomit_int32_t var you want do add a value to + * @param i The value you want to increment by + * + * @return The mathematical result + */ +inline static int32_t atomic_int32_add(atomic_int32_t *d, int32_t i) +{ + int32_t res = i; + asm volatile(LOCK "xaddl %0, %1" : "=r"(i) : "m"(d->counter), "0"(i) : "memory", "cc"); + return res+i; +} + +/** @brief Atomic subtraction of values from atomic_int32_t vars + * + * This function lets you subtract values in an atomic operation.\n + * This function is just for convenience. It uses atomic_int32_add(d, -i) + * + * @param d Pointer to the atomic_int32_t var you want to subtract from + * @param i The value you want to subtract by + * + * @return The mathematical result + */ +inline static int32_t atomic_int32_sub(atomic_int32_t *d, int32_t i) +{ + return atomic_int32_add(d, -i); +} + +/** @brief Atomic increment by one + * + * The atomic_int32_t var will be atomically incremented by one.\n + * + * @param d The atomic_int32_t var you want to increment + */ +inline static int32_t atomic_int32_inc(atomic_int32_t* d) { + return atomic_int32_add(d, 1); +} + +/** @brief Atomic decrement by one + * + * The atomic_int32_t var will be atomically decremented by one.\n + * + * @param d The atomic_int32_t var you want to decrement + */ +inline static int32_t atomic_int32_dec(atomic_int32_t* d) { + return atomic_int32_add(d, -1); +} + +/** @brief Read out an atomic_int32_t var + * + * This function is for convenience: It looks into the atomic_int32_t struct + * and returns the internal value for you. + * + * @param d Pointer to the atomic_int32_t var you want to read out + * @return It's number value + */ +inline static int32_t atomic_int32_read(atomic_int32_t *d) { + return d->counter; +} + +/** @brief Set the value of an atomic_int32_t var + * + * This function is for convenience: It sets the internal value of + * an atomic_int32_t var for you. + * + * @param d Pointer to the atomic_int32_t var you want to set + * @param v The value to set + */ +inline static void atomic_int32_set(atomic_int32_t *d, int32_t v) { + atomic_int32_test_and_set(d, v); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/arch/x86/include/asm/gdt.h b/hermit/arch/x86/include/asm/gdt.h new file mode 100644 index 000000000..10a982cc0 --- /dev/null +++ b/hermit/arch/x86/include/asm/gdt.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file arch/x86/include/asm/gdt.h + * @brief Definitions and functions related to segmentation + * @author Stefan Lankes + * + * This file defines the interface for segmentation as like structures to describe segments.\n + * There are also some other gdt-private functions in the gdt.c file defined. + */ + +#ifndef __ARCH_GDT_H__ +#define __ARCH_GDT_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// This segment is a data segment +#define GDT_FLAG_DATASEG 0x02 +/// This segment is a code segment +#define GDT_FLAG_CODESEG 0x0a +#define GDT_FLAG_TSS 0x09 +#define GDT_FLAG_TSS_BUSY 0x02 + +#define GDT_FLAG_SEGMENT 0x10 +/// Privilege level: Ring 0 +#define GDT_FLAG_RING0 0x00 +/// Privilege level: Ring 1 +#define GDT_FLAG_RING1 0x20 +/// Privilege level: Ring 2 +#define GDT_FLAG_RING2 0x40 +/// Privilege level: Ring 3 +#define GDT_FLAG_RING3 0x60 +/// Segment is present +#define GDT_FLAG_PRESENT 0x80 +/// Segment was accessed +#define GDT_FLAG_ACCESSED 0x01 +/** + * @brief Granularity of segment limit + * - set: segment limit unit is 4 KB (page size) + * - not set: unit is bytes + */ +#define GDT_FLAG_4K_GRAN 0x80 +/** + * @brief Default operand size + * - set: 32 bit + * - not set: 16 bit + */ +#define GDT_FLAG_16_BIT 0x00 +#define GDT_FLAG_32_BIT 0x40 +#define GDT_FLAG_64_BIT 0x20 + +/** @brief Defines a GDT entry + * + * A global descriptor table entry consists of: + * - 32 bit base address (chunkwise embedded into this structure) + * - 20 bit limit + */ +typedef struct { + /// Lower 16 bits of limit range + uint16_t limit_low; + /// Lower 16 bits of base address + uint16_t base_low; + /// middle 8 bits of base address + uint8_t base_middle; + /// Access bits + uint8_t access; + /// Granularity bits + uint8_t granularity; + /// Higher 8 bits of base address + uint8_t base_high; +} __attribute__ ((packed)) gdt_entry_t; + +/** @brief defines the GDT pointer structure + * + * This structure tells the address and size of the table. + */ +typedef struct { + /// Size of the table in bytes (not the number of entries!) + uint16_t limit; + /// Address of the table + size_t base; +} __attribute__ ((packed)) gdt_ptr_t; + +// a TSS descriptor is twice larger than a code/data descriptor +#define GDT_ENTRIES (6+1*2) + +#if GDT_ENTRIES > 8192 +#error Too many GDT entries! +#endif + +/** @brief Installs the global descriptor table + * + * The installation involves the following steps: + * - set up the special GDT pointer + * - set up the entries in our GDT + * - finally call gdt_flush() in our assembler file + * in order to tell the processor where the new GDT is + * - update the new segment registers + */ +void gdt_install(void); + +/** @brief Set gate with chosen attributes + */ +void gdt_set_gate(int num, unsigned long base, unsigned long limit, + unsigned char access, unsigned char gran); + +/** @brief Configures and returns a GDT descriptor with chosen attributes + * + * Just feed this function with address, limit and the flags + * you have seen in idt.h + * + * @return a preconfigured gdt descriptor + */ +void configure_gdt_entry(gdt_entry_t *dest_entry, unsigned long base, unsigned long limit, + unsigned char access, unsigned char gran); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/arch/x86/include/asm/idt.h b/hermit/arch/x86/include/asm/idt.h new file mode 100644 index 000000000..65dabc1de --- /dev/null +++ b/hermit/arch/x86/include/asm/idt.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file arch/x86/include/asm/idt.h + * @brief Definition of IDT flags and functions to set interrupts + * + * This file contains define-constants for interrupt flags + * and installer functions for interrupt gates.\n + * See idt.c for structure definitions. + */ + +#ifndef __ARCH_IDT_H__ +#define __ARCH_IDT_H__ + +#include + +/// This bit shall be set to 0 if the IDT slot is empty +#define IDT_FLAG_PRESENT 0x80 +/// Interrupt can be called from within RING0 +#define IDT_FLAG_RING0 0x00 +/// Interrupt can be called from within RING1 and lower +#define IDT_FLAG_RING1 0x20 +/// Interrupt can be called from within RING2 and lower +#define IDT_FLAG_RING2 0x40 +/// Interrupt can be called from within RING3 and lower +#define IDT_FLAG_RING3 0x60 +/// Size of gate is 16 bit +#define IDT_FLAG_16BIT 0x00 +/// Size of gate is 32 bit +#define IDT_FLAG_32BIT 0x08 +/// The entry describes an interrupt gate +#define IDT_FLAG_INTTRAP 0x06 +/// The entry describes a trap gate +#define IDT_FLAG_TRAPGATE 0x07 +/// The entry describes a task gate +#define IDT_FLAG_TASKGATE 0x05 + +/* + * This is not IDT-flag related. It's the segment selectors for kernel code and data. + */ +#define KERNEL_CODE_SELECTOR 0x08 +#define KERNEL_DATA_SELECTOR 0x10 + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Defines an IDT entry + * + * This structure defines interrupt descriptor table entries.\n + * They consist of the handling function's base address, some flags + * and a segment selector. + */ +typedef struct { + /// Handler function's lower 16 address bits + uint16_t base_lo; + /// Handler function's segment selector. + uint16_t sel; + /// These bits are reserved by Intel + uint8_t always0; + /// These 8 bits contain flags. Exact use depends on the type of interrupt gate. + uint8_t flags; + /// Higher 16 bits of handler function's base address + uint16_t base_hi; + /// In 64 bit mode, the "highest" 32 bits of the handler function's base address + uint32_t base_hi64; + /// resvered entries + uint32_t reserved; +} __attribute__ ((packed)) idt_entry_t; + +/** @brief Defines the idt pointer structure. + * + * This structure keeps information about + * base address and size of the interrupt descriptor table. + */ +typedef struct { + /// Size of the IDT in bytes (not the number of entries!) + uint16_t limit; + /// Base address of the IDT + size_t base; +} __attribute__ ((packed)) idt_ptr_t; + +/** @brief Installs IDT + * + * The installation involves the following steps: + * - Set up the IDT pointer + * - Set up int 0x80 for syscalls + * - process idt_load() + */ +void idt_install(void); + +/** @brief Set an entry in the IDT + * + * @param num index in the IDT + * @param base base-address of the handler function being installed + * @param sel Segment the IDT will use + * @param flags Flags this entry will have + */ +void idt_set_gate(unsigned char num, size_t base, unsigned short sel, + unsigned char flags); + +/** @brief Configures and returns a IDT entry with chosen attributes + * + * Just feed this function with base, selector and the flags + * you have seen in idt.h + * + * @return a preconfigured idt descriptor + */ +void configure_idt_entry(idt_entry_t *dest_entry, size_t base, + unsigned short sel, unsigned char flags); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/arch/x86/include/asm/io.h b/hermit/arch/x86/include/asm/io.h new file mode 100644 index 000000000..e122ae9d7 --- /dev/null +++ b/hermit/arch/x86/include/asm/io.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file arch/x86/include/asm/io.h + * @brief Functions related to processor IO + * + * This file contains inline functions for processor IO operations. + */ + +#ifndef __ARCH_IO_H__ +#define __ARCH_IO_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Read a byte from an IO port + * + * @param _port The port you want to read from + * @return The value which reads out from this port + */ +inline static unsigned char inportb(unsigned short _port) { + unsigned char rv; + asm volatile("inb %1, %0":"=a"(rv):"dN"(_port)); + return rv; +} + +/** @brief Read a word (2 byte) from an IO port + * + * @param _port The port you want to read from + * @return The value which reads out from this port + */ +inline static unsigned short inportw(unsigned short _port) { + unsigned short rv; + asm volatile("inw %1, %0":"=a"(rv):"dN"(_port)); + return rv; +} + +/** @brief Read a double word (4 byte) from an IO port + * + * @param _port The port you want to read from + * @return The value which reads out from this port + */ +inline static unsigned int inportl(unsigned short _port) { + unsigned int rv; + asm volatile("inl %1, %0":"=a"(rv):"dN"(_port)); + return rv; +} + +/** @brief Write a byte to an IO port + * + * @param _port The port you want to write to + * @param _data the 1 byte value you want to write + */ +inline static void outportb(unsigned short _port, unsigned char _data) { + asm volatile("outb %1, %0"::"dN"(_port), "a"(_data)); +} + +/** @brief Write a word (2 bytes) to an IO port + * + * @param _port The port you want to write to + * @param _data the 2 byte value you want to write + */ +inline static void outportw(unsigned short _port, unsigned short _data) { + asm volatile("outw %1, %0"::"dN"(_port), "a"(_data)); +} + +/** @brief Write a double word (4 bytes) to an IO port + * + * @param _port The port you want to write to + * @param _data the 4 byte value you want to write + */ +inline static void outportl(unsigned short _port, unsigned int _data) +{ + asm volatile("outl %1, %0"::"dN"(_port), "a"(_data)); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/arch/x86/include/asm/irq.h b/hermit/arch/x86/include/asm/irq.h new file mode 100644 index 000000000..7d51c70fe --- /dev/null +++ b/hermit/arch/x86/include/asm/irq.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file arch/x86/include/asm/irq.h + * @brief Functions related to IRQs + * + * This file contains functions and a pointer type related to interrupt requests. + */ + +#ifndef __ARCH_IRQ_H__ +#define __ARCH_IRQ_H__ + +#include +//#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Pointer-type to IRQ-handling functions + * + * Whenever you write a IRQ-handling function it has to match this signature. + */ +typedef void (*irq_handler_t)(struct state *); + +/** @brief Install a custom IRQ handler for a given IRQ + * + * @param irq The desired irq + * @param handler The handler to install + */ +int irq_install_handler(unsigned int irq, irq_handler_t handler); + +/** @brief Clear the handler for a given IRQ + * + * @param irq The handler's IRQ + */ +int irq_uninstall_handler(unsigned int irq); + +/** @brief Procedure to initialize IRQ + * + * This procedure is just a small collection of calls: + * - idt_install(); + * - isrs_install(); + * - irq_install(); + * + * @return Just returns 0 in any case + */ +int irq_init(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/arch/x86/include/asm/irqflags.h b/hermit/arch/x86/include/asm/irqflags.h new file mode 100644 index 000000000..48cfbd087 --- /dev/null +++ b/hermit/arch/x86/include/asm/irqflags.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file arch/x86/include/asm/irqflags.h + * @brief Functions related to IRQ configuration + * + * This file contains definitions of inline functions + * for enabling and disabling IRQ handling. + */ + +#ifndef __ARCH_IRQFLAGS_H__ +#define __ARCH_IRQFLAGS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Disable IRQs + * + * This inline function just clears out the interrupt bit + */ +inline static void irq_disable(void) { + asm volatile("cli" ::: "memory"); +} + +/** @brief Disable IRQs (nested) + * + * Disable IRQs when unsure if IRQs were enabled at all.\n + * This function together with irq_nested_enable can be used + * in situations when interrupts shouldn't be activated if they + * were not activated before calling this function. + * + * @return The set of flags which have been set until now + */ +inline static uint8_t irq_nested_disable(void) { + size_t flags; + asm volatile("pushf; cli; pop %0": "=r"(flags) : : "memory"); + if (flags & (1 << 9)) + return 1; + return 0; +} + +/** @brief Enable IRQs */ +inline static void irq_enable(void) { + asm volatile("sti" ::: "memory"); +} + +/** @brief Enable IRQs (nested) + * + * If called after calling irq_nested_disable, this function will + * not activate IRQs if they were not active before. + * + * @param flags Flags to set. Could be the old ones you got from irq_nested_disable. + */ +inline static void irq_nested_enable(uint8_t flags) { + if (flags) + irq_enable(); +} + +/** @brief Determines, if the interrupt flags (IF) is ser + * + * @return + * - 1 interrupt flag is set + * - 0 interrupt flag is cleared + */ +inline static uint8_t is_irq_enabled(void) +{ + size_t flags; + asm volatile("pushf; pop %0": "=r"(flags) : : "memory"); + if (flags & (1 << 9)) + return 1; + return 0; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/arch/x86/include/asm/isrs.h b/hermit/arch/x86/include/asm/isrs.h new file mode 100644 index 000000000..10e0e7c19 --- /dev/null +++ b/hermit/arch/x86/include/asm/isrs.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file arch/x86/include/asm/isrs.h + * @brief Installation of interrupt service routines + * + * This file contains the declaration of the ISR installer procedure.\n + * There is more interesting code related to ISRs in the isrs.c file. + */ + +#ifndef __ARCH_ISRS_H__ +#define __ARCH_ISRS_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief ISR installer procedure + * + * This procedure sets the first 32 entries in the IDT + * to the first 32 ISRs to call one general fault handler + * which will do some dispatching and exception message logging.\n + * The access flags are set to 0x8E (PRESENT, privilege: RING0, size: 32bit gate, type: interrupt gate). + */ +void isrs_install(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/arch/x86/include/asm/limits.h b/hermit/arch/x86/include/asm/limits.h new file mode 100644 index 000000000..e8ad07d4a --- /dev/null +++ b/hermit/arch/x86/include/asm/limits.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * author Stefan Lankes + * @file arch/x86/include/asm/limits.h + * @brief Define constants related to numerical value-ranges of variable types + * + * This file contains define constants for the numerical + * ranges of the most typical variable types. + */ + +#ifndef __ARCH_LIMITS_H__ +#define __ARCH_LIMITS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** Number of bits in a char */ +#define CHAR_BIT 8 + +/** Maximum value for a signed char */ +#define SCHAR_MAX 0x7f +/** Minimum value for a signed char */ +#define SCHAR_MIN (-0x7f - 1) + +/** Maximum value for an unsigned char */ +#define UCHAR_MAX 0xff + +/** Maximum value for an unsigned short */ +#define USHRT_MAX 0xffff +/** Maximum value for a short */ +#define SHRT_MAX 0x7fff +/** Minimum value for a short */ +#define SHRT_MIN (-0x7fff - 1) + +/** Maximum value for an unsigned int */ +#define UINT_MAX 0xffffffffU +/** Maximum value for an int */ +#define INT_MAX 0x7fffffff +/** Minimum value for an int */ +#define INT_MIN (-0x7fffffff - 1) + +/** Maximum value for an unsigned long */ +#define ULONG_MAX 0xffffffffUL +/** Maximum value for a long */ +#define LONG_MAX 0x7fffffffL +/** Minimum value for a long */ +#define LONG_MIN (-0x7fffffffL - 1) + +/** Maximum value for an unsigned long long */ +#define ULLONG_MAX 0xffffffffffffffffULL +/** Maximum value for a long long */ +#define LLONG_MAX 0x7fffffffffffffffLL +/** Minimum value for a long long */ +#define LLONG_MIN (-0x7fffffffffffffffLL - 1) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/arch/x86/include/asm/multiboot.h b/hermit/arch/x86/include/asm/multiboot.h new file mode 100644 index 000000000..c6bc13558 --- /dev/null +++ b/hermit/arch/x86/include/asm/multiboot.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file arch/x86/include/asm/multiboot.h + * @brief Structures related to the Multiboot interface + * + * HermitCore is able to use Multiboot (http://www.gnu.org/software/grub/manual/multiboot/), + * which specifies an interface between a boot loader and a operating system.\n + * \n + * This file contains several structures needed to match the interface. + */ + +#ifndef __ARCH_MULTIBOOT_H__ +#define __ARCH_MULTIBOOT_H__ + +#include + +/// Does the bootloader provide mem_* fields? +#define MULTIBOOT_INFO_MEM (1 << 0) +/// Does the bootloader provide the command-line? +#define MULTIBOOT_INFO_CMDLINE (1 << 2) +/// Does the bootloader provide a list of modules? +#define MULTIBOOT_INFO_MODS (1 << 3) +/// Does the bootloader provide a full memory map? +#define MULTIBOOT_INFO_MEM_MAP (1 << 6) + +typedef uint16_t multiboot_uint16_t; +typedef uint32_t multiboot_uint32_t; +typedef uint64_t multiboot_uint64_t; + +/* The symbol table for a.out. */ +struct multiboot_aout_symbol_table +{ + multiboot_uint32_t tabsize; + multiboot_uint32_t strsize; + multiboot_uint32_t addr; + multiboot_uint32_t reserved; +}; +typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t; + +/* The section header table for ELF. */ +struct multiboot_elf_section_header_table +{ + multiboot_uint32_t num; + multiboot_uint32_t size; + multiboot_uint32_t addr; + multiboot_uint32_t shndx; +}; +typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t; + +struct multiboot_info +{ + /** Multiboot info version number */ + multiboot_uint32_t flags; + + /** Available memory from BIOS */ + multiboot_uint32_t mem_lower; + multiboot_uint32_t mem_upper; + + /** "root" partition */ + multiboot_uint32_t boot_device; + + /** Kernel command line */ + multiboot_uint32_t cmdline; + + /** Boot-Module list */ + multiboot_uint32_t mods_count; + multiboot_uint32_t mods_addr; + + union + { + multiboot_aout_symbol_table_t aout_sym; + multiboot_elf_section_header_table_t elf_sec; + } u; + + /** Memory Mapping buffer */ + multiboot_uint32_t mmap_length; + multiboot_uint32_t mmap_addr; + + /** Drive Info buffer */ + multiboot_uint32_t drives_length; + multiboot_uint32_t drives_addr; + + /** ROM configuration table */ + multiboot_uint32_t config_table; + + /** Boot Loader Name */ + multiboot_uint32_t boot_loader_name; + + /** APM table */ + multiboot_uint32_t apm_table; + + /** Video */ + multiboot_uint32_t vbe_control_info; + multiboot_uint32_t vbe_mode_info; + multiboot_uint16_t vbe_mode; + multiboot_uint16_t vbe_interface_seg; + multiboot_uint16_t vbe_interface_off; + multiboot_uint16_t vbe_interface_len; +}; + +typedef struct multiboot_info multiboot_info_t; + +struct multiboot_mmap_entry +{ + multiboot_uint32_t size; + multiboot_uint64_t addr; + multiboot_uint64_t len; +#define MULTIBOOT_MEMORY_AVAILABLE 1 +#define MULTIBOOT_MEMORY_RESERVED 2 + multiboot_uint32_t type; +} __attribute__((packed)); +typedef struct multiboot_mmap_entry multiboot_memory_map_t; + +struct multiboot_mod_list +{ + /** the memory used goes from bytes ’mod start’ to ’mod end-1’ inclusive */ + multiboot_uint32_t mod_start; + multiboot_uint32_t mod_end; + + /** Module command line */ + multiboot_uint32_t cmdline; + + /** padding to take it to 16 bytes (must be zero) */ + multiboot_uint32_t pad; +}; +typedef struct multiboot_mod_list multiboot_module_t; + +/// Pointer to multiboot structure +/// This pointer is declared at set by entry.asm +extern multiboot_info_t* mb_info; + +#endif diff --git a/hermit/arch/x86/include/asm/page.h b/hermit/arch/x86/include/asm/page.h new file mode 100644 index 000000000..1c55f633d --- /dev/null +++ b/hermit/arch/x86/include/asm/page.h @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * 2014, Steffen Vogel, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Steffen Vogel + * @file arch/x86/include/asm/page.h + * @brief Paging related functions + * + * This file contains the several functions to manage the page tables + */ + +#include +#include + +#ifndef __PAGE_H__ +#define __PAGE_H__ + +/// Page offset bits +#define PAGE_BITS 12 +/// The size of a single page in bytes +#define PAGE_SIZE ( 1L << PAGE_BITS) +/// Mask the page address without page map flags and XD flag +#if 0 +#define PAGE_MASK (-1L << PAGE_BITS) +#else +#define PAGE_MASK ((-1L << PAGE_BITS) & ~PG_XD) +#endif + +#if 0 +/// Total operand width in bits +#define BITS 32 +/// Physical address width (we dont support PAE) +#define PHYS_BITS BITS +/// Linear/virtual address width +#define VIRT_BITS BITS +/// Page map bits +#define PAGE_MAP_BITS 10 +/// Number of page map indirections +#define PAGE_LEVELS 2 +#else +/// Total operand width in bits +#define BITS 64 +/// Physical address width (maximum value) +#define PHYS_BITS 52 +/// Linear/virtual address width +#define VIRT_BITS 48 +/// Page map bits +#define PAGE_MAP_BITS 9 +/// Number of page map indirections +#define PAGE_LEVELS 4 + +/** @brief Sign extending a integer + * + * @param addr The integer to extend + * @param bits The width if addr which should be extended + * @return The extended integer + */ +static inline size_t sign_extend(ssize_t addr, int bits) +{ + int shift = BITS - bits; + return (addr << shift) >> shift; // sign bit gets copied during arithmetic right shift +} +#endif + +/// Make address canonical +#if 0 +#define CANONICAL(addr) (addr) // only for 32 bit paging +#else +#define CANONICAL(addr) sign_extend(addr, VIRT_BITS) +#endif + +/// The number of entries in a page map table +#define PAGE_MAP_ENTRIES (1L << PAGE_MAP_BITS) + +/// Align to next page +#define PAGE_FLOOR(addr) (((addr) + PAGE_SIZE - 1) & PAGE_MASK) +/// Align to page +#define PAGE_CEIL(addr) ( (addr) & PAGE_MASK) + +/// Page is present +#define PG_PRESENT (1 << 0) +/// Page is read- and writable +#define PG_RW (1 << 1) +/// Page is addressable from userspace +#define PG_USER (1 << 2) +/// Page write through is activated +#define PG_PWT (1 << 3) +/// Page cache is disabled +#define PG_PCD (1 << 4) +/// Page was recently accessed (set by CPU) +#define PG_ACCESSED (1 << 5) +/// Page is dirty due to recent write-access (set by CPU) +#define PG_DIRTY (1 << 6) +/// Huge page: 4MB (or 2MB, 1GB) +#define PG_PSE (1 << 7) +/// Page attribute table +#define PG_PAT PG_PSE +/// Global TLB entry (Pentium Pro and later) +#define PG_GLOBAL (1 << 8) +/// This table is a self-reference and should skipped by page_map_copy() +#define PG_SELF (1 << 9) + +/// Disable execution for this page +#define PG_XD (1L << 63) + +/** @brief Converts a virtual address to a physical + * + * A non mapped virtual address causes a pagefault! + * + * @param addr Virtual address to convert + * @return physical address + */ +size_t virt_to_phys(size_t vir); + +/** @brief Initialize paging subsystem + * + * This function uses the existing bootstrap page tables (boot_{pgd, pgt}) + * to map required regions (video memory, kernel, etc..). + * Before calling page_init(), the bootstrap tables contain a simple identity + * paging. Which is replaced by more specific mappings. + */ +int page_init(void); + +/** @brief Map a continuous region of pages + * + * @param viraddr Desired virtual address + * @param phyaddr Physical address to map from + * @param npages The region's size in number of pages + * @param bits Further page flags + * @return + */ +int page_map(size_t viraddr, size_t phyaddr, size_t npages, size_t bits); + +/** @brief Unmap a continuous region of pages + * + * @param viraddr The virtual start address + * @param npages The range's size in pages + * @return + */ +int page_unmap(size_t viraddr, size_t npages); + +/** @brief Change the page permission in the page tables of the current task + * + * Applies given flags noted in the 'flags' parameter to + * the range denoted by virtual start and end addresses. + * + * @param start Range's virtual start address + * @param end Range's virtual end address + * @param flags flags to apply + * + * @return + * - 0 on success + * - -EINVAL (-22) on failure. + */ +int page_set_flags(size_t viraddr, uint32_t npages, int flags); + +/** @brief Copy a whole page map tree + * + * @param dest Physical address of new page map + * @retval 0 Success. Everything went fine. + * @retval <0 Error. Something went wrong. + */ +int page_map_copy(struct task *dest); + +/** @brief Free a whole page map tree */ +int page_map_drop(void); + +#endif diff --git a/hermit/arch/x86/include/asm/processor.h b/hermit/arch/x86/include/asm/processor.h new file mode 100644 index 000000000..47c60dcfb --- /dev/null +++ b/hermit/arch/x86/include/asm/processor.h @@ -0,0 +1,526 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file arch/x86/include/asm/processor.h + * @brief CPU-specific functions + * + * This file contains structures and functions related to CPU-specific assembler commands. + */ + +#ifndef __ARCH_PROCESSOR_H__ +#define __ARCH_PROCESSOR_H__ + +#include +#include +//#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// feature list 1 +#define CPU_FEATURE_FPU (1 << 0) +#define CPU_FEATUE_PSE (1 << 3) +#define CPU_FEATURE_MSR (1 << 5) +#define CPU_FEATURE_PAE (1 << 6) +#define CPU_FEATURE_APIC (1 << 9) +#define CPU_FEATURE_SEP (1 << 11) +#define CPU_FEATURE_PGE (1 << 13) +#define CPU_FEATURE_PAT (1 << 16) +#define CPU_FEATURE_PSE36 (1 << 17) +#define CPU_FEATURE_MMX (1 << 23) +#define CPU_FEATURE_FXSR (1 << 24) +#define CPU_FEATURE_SSE (1 << 25) +#define CPU_FEATURE_SSE2 (1 << 26) + +// feature list 2 +#define CPU_FEATURE_X2APIC (1 << 21) +#define CPU_FEATURE_AVX (1 << 28) +#define CPU_FEATURE_HYPERVISOR (1 << 31) + +// CPUID.80000001H:EDX feature list +#define CPU_FEATURE_SYSCALL (1 << 11) +#define CPU_FEATURE_NX (1 << 20) +#define CPU_FEATURE_1GBHP (1 << 26) +#define CPU_FEATURE_LM (1 << 29) + +// x86 control registers + +/// Protected Mode Enable +#define CR0_PE (1 << 0) +/// Monitor coprocessor +#define CR0_MP (1 << 1) +/// Enable FPU emulation +#define CR0_EM (1 << 2) +/// Task switched +#define CR0_TS (1 << 3) +/// Extension type of coprocessor +#define CR0_ET (1 << 4) +/// Enable FPU error reporting +#define CR0_NE (1 << 5) +/// Enable write protected pages +#define CR0_WP (1 << 16) +/// Enable alignment checks +#define CR0_AM (1 << 18) +/// Globally enables/disable write-back caching +#define CR0_NW (1 << 29) +/// Globally disable memory caching +#define CR0_CD (1 << 30) +/// Enable paging +#define CR0_PG (1 << 31) + +/// Virtual 8086 Mode Extensions +#define CR4_VME (1 << 0) +/// Protected-mode Virtual Interrupts +#define CR4_PVI (1 << 1) +/// Disable Time Stamp Counter register (rdtsc instruction) +#define CR4_TSD (1 << 2) +/// Enable debug extensions +#define CR4_DE (1 << 3) +/// Enable hugepage support +#define CR4_PSE (1 << 4) +/// Enable physical address extension +#define CR4_PAE (1 << 5) +/// Enable machine check exceptions +#define CR4_MCE (1 << 6) +/// Enable global pages +#define CR4_PGE (1 << 7) +/// Enable Performance-Monitoring Counter +#define CR4_PCE (1 << 8) +/// Enable Operating system support for FXSAVE and FXRSTOR instructions +#define CR4_OSFXSR (1 << 9) +/// Enable Operating System Support for Unmasked SIMD Floating-Point Exceptions +#define CR4_OSXMMEXCPT (1 << 10) +/// Enable Virtual Machine Extensions, see Intel VT-x +#define CR4_VMXE (1 << 13) +/// Enable Safer Mode Extensions, see Trusted Execution Technology (TXT) +#define CR4_SMXE (1 << 14) +/// Enables the instructions RDFSBASE, RDGSBASE, WRFSBASE, and WRGSBASE +#define CR4_FSGSBASE (1 << 16) +/// Enables process-context identifiers +#define CR4_PCIDE (1 << 17) +/// Enable XSAVE and Processor Extended States +#define CR4_OSXSAVE (1 << 18) +/// Enable Supervisor Mode Execution Protection +#define CR4_SMEP (1 << 20) +/// Enable Supervisor Mode Access Protection +#define CR4_SMAP (1 << 21) + +// x86-64 specific MSRs + +/// APIC register +#define MSR_APIC_BASE 0x0000001B +/// extended feature register +#define MSR_EFER 0xc0000080 +/// legacy mode SYSCALL target +#define MSR_STAR 0xc0000081 +/// long mode SYSCALL target +#define MSR_LSTAR 0xc0000082 +/// compat mode SYSCALL target +#define MSR_CSTAR 0xc0000083 +/// EFLAGS mask for syscall +#define MSR_SYSCALL_MASK 0xc0000084 +/// 64bit FS base +#define MSR_FS_BASE 0xc0000100 +/// 64bit GS base +#define MSR_GS_BASE 0xc0000101 +/// SwapGS GS shadow +#define MSR_KERNEL_GS_BASE 0xc0000102 + +// MSR EFER bits +#define EFER_SCE (1 << 0) +#define EFER_LME (1 << 8) +#define EFER_LMA (1 << 10) +#define EFER_NXE (1 << 11) +#define EFER_SVME (1 << 12) +#define EFER_LMSLE (1 << 13) +#define EFER_FFXSR (1 << 14) +#define EFER_TCE (1 << 15) + +typedef struct { + uint32_t feature1, feature2, feature3; + uint32_t addr_width; +} cpu_info_t; + +extern cpu_info_t cpu_info; + +// determine the cpu features +int cpu_detection(void); + +inline static uint32_t has_fpu(void) { + return (cpu_info.feature1 & CPU_FEATURE_FPU); +} + +inline static uint32_t has_msr(void) { + return (cpu_info.feature1 & CPU_FEATURE_MSR); +} + +inline static uint32_t has_apic(void) { + return (cpu_info.feature1 & CPU_FEATURE_APIC); +} + +inline static uint32_t has_fxsr(void) { + return (cpu_info.feature1 & CPU_FEATURE_FXSR); +} + +inline static uint32_t has_sse(void) { + return (cpu_info.feature1 & CPU_FEATURE_SSE); +} + +inline static uint32_t has_sse2(void) { + return (cpu_info.feature1 & CPU_FEATURE_SSE2); +} + +inline static uint32_t has_pge(void) +{ + return (cpu_info.feature1 & CPU_FEATURE_PGE); +} + +inline static uint32_t has_sep(void) { + return (cpu_info.feature1 & CPU_FEATURE_SEP); +} + +inline static uint32_t has_x2apic(void) { + return (cpu_info.feature2 & CPU_FEATURE_X2APIC); +} + +inline static uint32_t has_avx(void) { + return (cpu_info.feature2 & CPU_FEATURE_AVX); +} + +inline static uint32_t on_hypervisor(void) { + return (cpu_info.feature2 & CPU_FEATURE_HYPERVISOR); +} + +inline static uint32_t has_nx(void) +{ + return (cpu_info.feature3 & CPU_FEATURE_NX); +} + +/** @brief Read out time stamp counter + * + * The rdtsc asm command puts a 64 bit time stamp value + * into EDX:EAX. + * + * @return The 64 bit time stamp value + */ +inline static uint64_t rdtsc(void) +{ + uint64_t lo, hi; + asm volatile ("rdtsc" : "=a"(lo), "=d"(hi) ); + return (hi << 32 | lo); +} + +/** @brief Read MSR + * + * The asm instruction rdmsr which stands for "Read from model specific register" + * is used here. + * + * @param msr The parameter which rdmsr assumes in ECX + * @return The value rdmsr put into EDX:EAX + */ +inline static uint64_t rdmsr(uint32_t msr) { + uint32_t low, high; + + asm volatile ("rdmsr" : "=a" (low), "=d" (high) : "c" (msr)); + + return ((uint64_t)high << 32) | low; +} + +/** @brief Write a value to a Machine-Specific Registers (MSR) + * + * The asm instruction wrmsr which stands for "Write to model specific register" + * is used here. + * + * @param msr The MSR identifier + * @param value Value, which will be store in the MSR + */ +inline static void wrmsr(uint32_t msr, uint64_t value) +{ + uint32_t low = value & 0xFFFFFFFF; + uint32_t high = value >> 32; + + asm volatile("wrmsr" :: "a"(low), "c"(msr), "d"(high)); +} + +/** @brief Read cr0 register + * @return cr0's value + */ +static inline size_t read_cr0(void) { + size_t val; + asm volatile("mov %%cr0, %0" : "=r"(val)); + return val; +} + +/** @brief Write a value into cr0 register + * @param val The value you want to write into cr0 + */ +static inline void write_cr0(size_t val) { + asm volatile("mov %0, %%cr0" : : "r"(val)); +} + +/** @brief Read cr2 register + * @return cr2's value + */ +static inline size_t read_cr2(void) { + size_t val; + asm volatile("mov %%cr2, %0" : "=r"(val)); + return val; +} + +/** @brief Write a value into cr2 register + * @param val The value you want to write into cr2 + */ +static inline void write_cr2(size_t val) { + asm volatile("mov %0, %%cr2" : : "r"(val)); +} + +/** @brief Read cr3 register + * @return cr3's value + */ +static inline size_t read_cr3(void) { + size_t val; + asm volatile("mov %%cr3, %0" : "=r"(val)); + return val; +} + +/** @brief Write a value into cr3 register + * @param val The value you want to write into cr3 + */ +static inline void write_cr3(size_t val) { + asm volatile("mov %0, %%cr3" : : "r"(val)); +} + +/** @brief Read cr4 register + * @return cr4's value + */ +static inline size_t read_cr4(void) { + size_t val; + asm volatile("mov %%cr4, %0" : "=r"(val)); + return val; +} + +/** @brief Write a value into cr4 register + * @param val The value you want to write into cr4 + */ +static inline void write_cr4(size_t val) { + asm volatile("mov %0, %%cr4" : : "r"(val)); +} + +/** @brief Flush cache + * + * The wbinvd asm instruction which stands for "Write back and invalidate" + * is used here + */ +inline static void flush_cache(void) { + asm volatile ("wbinvd" ::: "memory"); +} + +/** @brief Invalidate cache + * + * The invd asm instruction which invalidates cache without writing back + * is used here + */ +inline static void invalidate_cache(void) { + asm volatile ("invd" ::: "memory"); +} + +/** @brief Flush Translation Lookaside Buffer + * + * Just reads cr3 and writes the same value back into it. + */ +static inline void flush_tlb(void) +{ + size_t val = read_cr3(); + + if (val) + write_cr3(val); +} + +/** @brief Flush a specific page entry in TLB + * @param addr The (virtual) address of the page to flush + */ +static inline void tlb_flush_one_page(size_t addr) +{ + asm volatile("invlpg (%0)" : : "r"(addr) : "memory"); +} + +/** @brief Invalidate cache + * + * The invd asm instruction which invalidates cache without writing back + * is used here + */ +inline static void invalid_cache(void) { + asm volatile ("invd"); +} + +/* Force strict CPU ordering */ +typedef void (*func_memory_barrier)(void); + +/// Force strict CPU ordering, serializes load and store operations. +extern func_memory_barrier mb; +/// Force strict CPU ordering, serializes load operations. +extern func_memory_barrier rmb; +/// Force strict CPU ordering, serializes store operations. +extern func_memory_barrier wmb; + +/** @brief Read out CPU ID + * + * The cpuid asm-instruction does fill some information into registers and + * this function fills those register values into the given uint32_t vars.\n + * + * @param code Input parameter for the cpuid instruction. Take a look into the intel manual. + * @param a EAX value will be stores here + * @param b EBX value will be stores here + * @param c ECX value will be stores here + * @param d EDX value will be stores here + */ +inline static void cpuid(uint32_t code, uint32_t* a, uint32_t* b, uint32_t* c, uint32_t* d) { + asm volatile ("cpuid" : "=a"(*a), "=b"(*b), "=c"(*c), "=d"(*d) : "0"(code), "2"(*c)); +} + +/** @brief Read EFLAGS + * + * @return The EFLAGS value + */ +static inline uint32_t read_eflags(void) +{ + uint32_t result; + asm volatile ("pushf; pop %0" : "=r"(result)); + return result; +} + +/** @brief search the first most significant bit + * + * @param i source operand + * @return + * - first bit, which is set in the source operand + * - invalid value, if not bit ist set + */ +static inline size_t msb(size_t i) +{ + size_t ret; + + if (!i) + return (sizeof(size_t)*8); + asm volatile ("bsr %1, %0" : "=r"(ret) : "r"(i) : "cc"); + + return ret; +} + +/** @brief search the least significant bit + * + * @param i source operand + * @return + * - first bit, which is set in the source operand + * - invalid value, if not bit ist set + */ +static inline size_t lsb(size_t i) +{ + size_t ret; + + if (!i) + return (sizeof(size_t)*8); + asm volatile ("bsf %1, %0" : "=r"(ret) : "r"(i) : "cc"); + + return ret; +} + +/// A one-instruction-do-nothing +#define NOP1 asm volatile ("nop") +/// A two-instruction-do-nothing +#define NOP2 asm volatile ("nop;nop") +/// A four-instruction-do-nothing +#define NOP4 asm volatile ("nop;nop;nop;nop") +/// A eight-instruction-do-nothing +#define NOP8 asm volatile ("nop;nop;nop;nop;nop;nop;nop;nop") +/// The PAUSE instruction provides a hint to the processor that the code sequence is a spin-wait loop. +#define PAUSE asm volatile ("pause") +/// The HALT instruction stops the processor until the next interrupt arrives +#define HALT asm volatile ("hlt") + +/** @brief Init several subsystems + * + * This function calls the initialization procedures for: + * - GDT + * - APIC + * - PCI [if configured] + * + * @return 0 in any case + */ +inline static int system_init(void) +{ + gdt_install(); + cpu_detection(); + + return 0; +} + +/** @brief Detect and read out CPU frequency + * + * @return The CPU frequency in MHz + */ +uint32_t detect_cpu_frequency(void); + +/** @brief Read out CPU frequency if detected before + * + * If you did not issue the detect_cpu_frequency() function before, + * this function will call it implicitly. + * + * @return The CPU frequency in MHz + */ +uint32_t get_cpu_frequency(void); + +/** @brief Busywait an microseconds interval of time + * @param usecs The time to wait in microseconds + */ +void udelay(uint32_t usecs); + +/** @brief System calibration + * + * This procedure will detect the CPU frequency and calibrate the APIC timer. + * + * @return 0 in any case. + */ +inline static int system_calibration(void) +{ + //apic_init(); + irq_enable(); + detect_cpu_frequency(); + //apic_calibration(); + + return 0; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/arch/x86/include/asm/stddef.h b/hermit/arch/x86/include/asm/stddef.h new file mode 100644 index 000000000..2fb58ecbc --- /dev/null +++ b/hermit/arch/x86/include/asm/stddef.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file arch/x86/include/asm/stddef.h + * @brief Standard datatypes + * + * This file contains typedefs for standard datatypes for numerical and character values. + */ + +#ifndef __ARCH_STDDEF_H__ +#define __ARCH_STDDEF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#if __SIZEOF_POINTER__ == 4 + +#define KERNEL_SPACE (1UL << 30) /* 1 GiB */ + +/// This type is used to represent the size of an object. +typedef unsigned long size_t; +/// Pointer differences +typedef long ptrdiff_t; +/// It is similar to size_t, but must be a signed type. +typedef long ssize_t; +/// The type represents an offset and is similar to size_t, but must be a signed type. +typedef long off_t; +#elif __SIZEOF_POINTER__ == 8 + +#define KERNEL_SPACE (1ULL << 30) + +// A popular type for addresses +typedef unsigned long long size_t; +/// Pointer differences +typedef long long ptrdiff_t; +#ifdef __KERNEL__ +typedef long long ssize_t; +typedef long long off_t; +#endif +#else +#error unsupported architecture +#endif + +/// Unsigned 64 bit integer +typedef unsigned long long uint64_t; +/// Signed 64 bit integer +typedef long long int64_t; +/// Unsigned 32 bit integer +typedef unsigned int uint32_t; +/// Signed 32 bit integer +typedef int int32_t; +/// Unsigned 16 bit integer +typedef unsigned short uint16_t; +/// Signed 16 bit integer +typedef short int16_t; +/// Unsigned 8 bit integer (/char) +typedef unsigned char uint8_t; +/// Signed 8 bit integer (/char) +typedef char int8_t; +/// 16 bit wide char type +typedef unsigned short wchar_t; + +/// This defines what the stack looks like after the task context is saved. +struct state { + /// R15 register + uint64_t r15; + /// R14 register + uint64_t r14; + /// R13 register + uint64_t r13; + /// R12 register + uint64_t r12; + /// R11 register + uint64_t r11; + /// R10 register + uint64_t r10; + /// R9 register + uint64_t r9; + /// R8 register + uint64_t r8; + /// RDI register + uint64_t rdi; + /// RSI register + uint64_t rsi; + /// RBP register + uint64_t rbp; + /// (pseudo) RSP register + uint64_t rsp; + /// RBX register + uint64_t rbx; + /// RDX register + uint64_t rdx; + /// RCX register + uint64_t rcx; + /// RAX register + uint64_t rax; + + /// Interrupt number + uint64_t int_no; + + // pushed by the processor automatically + uint64_t error; + uint64_t rip; + uint64_t cs; + uint64_t rflags; + uint64_t userrsp; + uint64_t ss; +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/arch/x86/include/asm/string.h b/hermit/arch/x86/include/asm/string.h new file mode 100644 index 000000000..5d74fed40 --- /dev/null +++ b/hermit/arch/x86/include/asm/string.h @@ -0,0 +1,130 @@ +/* + * Written by the Chair for Operating Systems, RWTH Aachen University + * + * NO Copyright (C) 2010-2011, Stefan Lankes + * consider these trivial functions to be public domain. + * + * These functions are distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + */ + +/** + * @author Stefan Lankes + * @file arch/x86/include/asm/string.h + * @brief Functions related to memcpy and strings. + * + * This file deals with memcpy, memset, string functions and everything related to + * continuous byte fields. + */ + +#ifndef __ARCH_STRING_H__ +#define __ARCH_STRING_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_ARCH_MEMCPY +/** @brief Copy a byte range from source to dest + * + * @param dest Destination address + * @param src Source address + * @param count Range of the byte field in bytes + */ +inline static void *memcpy(void* dest, const void *src, size_t count) +{ + size_t i, j, k; + + if (BUILTIN_EXPECT(!dest || !src, 0)) + return dest; + + asm volatile ( + "cld; rep movsq\n\t" + "movq %4, %%rcx\n\t" + "andq $7, %%rcx\n\t" + "rep movsb\n\t" + : "=&c"(i), "=&D"(j), "=&S"(k) + : "0"(count/8), "g"(count), "1"(dest), "2"(src) : "memory","cc"); + + return dest; +} +#endif + +#ifdef HAVE_ARCH_MEMSET +/** @brief Repeated write of a value to a whole range of bytes + * + * @param dest Destination address + * @param val Value to flood the range with + * @param count Size of target range in bytes + */ +inline static void *memset(void* dest, int val, size_t count) +{ + size_t i, j; + + if (BUILTIN_EXPECT(!dest, 0)) + return dest; + + asm volatile ("cld; rep stosb" + : "=&c"(i), "=&D"(j) + : "a"(val), "1"(dest), "0"(count) : "memory","cc"); + + return dest; +} +#endif + +#ifdef HAVE_ARCH_STRLEN +/** @brief Standard string length + * + * This function computed the length of the given null terminated string + * just like the strlen functions you are used to. + * + * @return + * - The length of the string + * - 0 if str is a NULL pointer + */ +inline static size_t strlen(const char* str) +{ + size_t len = 0; + size_t i, j; + + if (BUILTIN_EXPECT(!str, 0)) + return len; + + asm volatile("not %%rcx; cld; repne scasb; not %%rcx; dec %%rcx" + : "=&c"(len), "=&D"(i), "=&a"(j) + : "2"(0), "1"(str), "0"(len) + : "memory","cc"); + + return len; +} +#endif + +#ifdef HAVE_ARCH_STRNCPY +/** @brief Copy string with maximum of n byte length + * + * @param dest Destination string pointer + * @param src Source string pointer + * @param n maximum number of bytes to copy + */ +char* strncpy(char* dest, const char* src, size_t n); +#endif + +#ifdef HAVE_ARCH_STRCPY +/** @brief Copy string + * + * Note that there is another safer variant of this function: strncpy.\n + * That one could save you from accidents with buffer overruns. + * + * @param dest Destination string pointer + * @param src Source string pointer + */ +char* strcpy(char* dest, const char* src); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/arch/x86/include/asm/syscall.h b/hermit/arch/x86/include/asm/syscall.h new file mode 100644 index 000000000..a51d85c43 --- /dev/null +++ b/hermit/arch/x86/include/asm/syscall.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2011, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file arch/x86/include/asm/syscall.h + * @brief Systemcall related code + * + * This file defines the syscall function and convenience + * based macro definitions for calling it. + */ + +#ifndef __ARCH_SYSCALL_H__ +#define __ARCH_SYSCALL_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define _STR(token) #token +#define _SYSCALLSTR(x) "int $" _STR(x) " " + +/** @brief the syscall function which issues an interrupt to the kernel + * + * It's supposed to be used by the macros defined in this file as the could would read + * cleaner then. + * + * @param nr System call number + * @param arg0 Argument 0 + * @param arg1 Argument 1 + * @param arg2 Argument 2 + * @param arg3 Argument 3 + * @param arg4 Argument 4 + * @return The return value of the system call + */ +inline static long +syscall(int nr, unsigned long arg0, unsigned long arg1, unsigned long arg2, + unsigned long arg3, unsigned long arg4) +{ + long res; + + asm volatile ("mov %5, %%r8; mov %6, %%r9; syscall" + : "=a" (res) + : "D" (nr), "S" (arg0), "d" (arg1), "c" (arg2), "m" (arg3), "m" (arg4) + : "memory", "cc", "%r8", "%r9"); + + return res; +} + +/// System call macro with one single argument; the syscall number +#define SYSCALL0(NR) \ + syscall(NR, 0, 0, 0, 0, 0) +#define SYSCALL1(NR, ARG0) \ + syscall(NR, (unsigned long)ARG0, 0, 0, 0, 0) +#define SYSCALL2(NR, ARG0, ARG1) \ + syscall(NR, (unsigned long)ARG0, (unsigned long)ARG1, 0, 0, 0) +#define SYSCALL3(NR, ARG0, ARG1, ARG2) \ + syscall(NR, (unsigned long)ARG0, (unsigned long)ARG1, (unsigned long)ARG2, 0, 0) +#define SYSCALL4(NR, ARG0, ARG1, ARG2, ARG3) \ + syscall(NR, (unsigned long)ARG0, (unsigned long)ARG1, (unsigned long)ARG2, (unsigned long) ARG3, 0) +#define SYSCALL5(NR, ARG0, ARG1, ARG2, ARG3, ARG4) \ + syscall(NR, (unsigned long)ARG0, (unsigned long)ARG1, (unsigned long)ARG2, (unsigned long) ARG3, (unsigned long) ARG4) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/arch/x86/include/asm/tasks.h b/hermit/arch/x86/include/asm/tasks.h new file mode 100644 index 000000000..5eaef1987 --- /dev/null +++ b/hermit/arch/x86/include/asm/tasks.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file arch/x86/include/asm/tasks.h + * @brief Task related structure definitions + * + * This file contains the task_t structure definition + * and task state define constants + */ + +#ifndef __ASM_TASKS_H__ +#define __ASM_TASKS_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Switch to current task + * + * @param stack Pointer to the old stack pointer + */ +void switch_context(size_t** stack); + +/** @brief Setup a default frame for a new task + * + * @param task Pointer to the task structure + * @param ep The entry point for code execution + * @param arg Arguments list pointer for the task's stack + * @return + * - 0 on success + * - -EINVAL (-22) on failure + */ +int create_default_frame(task_t* task, entry_point_t ep, void* arg); + +/** @brief Register a task's TSS at GDT + * + * @return + * - 0 on success + */ +static inline int register_task(void) +{ + uint16_t sel = 6 << 3; + + asm volatile ("ltr %%ax" : : "a"(sel)); + + return 0; +} + +/** @brief Jump to user code + * + * This function runs the user code after stopping it just as if + * it was a return from a procedure. + * + * @return 0 in any case + */ +static inline int jump_to_user_code(size_t ep, size_t stack) +{ + // Create a pseudo interrupt on the stack and return to user function + size_t ds = 0x23, cs = 0x2b; + + // x86_64 doesn't longer use segment registers + //asm volatile ("mov %0, %%ds; mov %0, %%es" :: "r"(ds)); + asm volatile ("push %0; push %1; pushfq; push %2; push %3" :: "r"(ds), "r"(stack), "r"(cs), "r"(ep)); + asm volatile ("iretq"); + + return 0; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/arch/x86/include/asm/tasks_types.h b/hermit/arch/x86/include/asm/tasks_types.h new file mode 100644 index 000000000..837983c97 --- /dev/null +++ b/hermit/arch/x86/include/asm/tasks_types.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file arch/x86/include/asm/tasks_types.h + * @brief Task related structure definitions + * + * This file contains the task_t structure definition + * and task state define constants + */ + +#ifndef __ASM_TASKS_TYPES_H__ +#define __ASM_TASKS_TYPES_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + long cwd; + long swd; + long twd; + long fip; + long fcs; + long foo; + long fos; + long st_space[20]; + long status; +} i387_fsave_t; + +typedef struct i387_fxsave_struct { + unsigned short cwd; + unsigned short swd; + unsigned short twd; + unsigned short fop; + long fip; + long fcs; + long foo; + long fos; + long mxcsr; + long mxcsr_mask; + long st_space[32]; + long xmm_space[64]; + long padding[12]; + union { + long padding1[12]; + long sw_reserved[12]; + }; +} i387_fxsave_t __attribute__ ((aligned (16))); + +union fpu_state { + i387_fsave_t fsave; + i387_fxsave_t fxsave; +}; + +typedef void (*handle_fpu_state)(union fpu_state* state); + +extern handle_fpu_state save_fpu_state; +extern handle_fpu_state restore_fpu_state; +extern handle_fpu_state fpu_init; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/arch/x86/include/asm/tss.h b/hermit/arch/x86/include/asm/tss.h new file mode 100644 index 000000000..4347502b8 --- /dev/null +++ b/hermit/arch/x86/include/asm/tss.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file arch/x86/include/asm/tss.h + * @brief Task state segment structure definition + */ + +#ifndef __ARCH_TSS_H__ +#define __ARCH_TSS_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief The tast state segment structure + */ +typedef struct { + uint16_t res0, res1; // reserved entries + uint64_t rsp0; + uint64_t rsp1; + uint64_t rsp2; + uint32_t res2, res3; // reserved entries + uint64_t ist_rsp1; + uint64_t ist_rsp2; + uint64_t ist_rsp3; + uint64_t ist_rsp4; + uint64_t ist_rsp5; + uint64_t ist_rsp6; + uint64_t ist_rsp7; + uint32_t res4, res5; // reserved entries + uint16_t res6, bitmap; +} __attribute__ ((packed)) tss_t; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/arch/x86/include/asm/vga.h b/hermit/arch/x86/include/asm/vga.h new file mode 100644 index 000000000..4f7926c38 --- /dev/null +++ b/hermit/arch/x86/include/asm/vga.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file arch/x86/include/asm/vga.h + * @brief VGA output related code + */ + +#ifndef __ARCH_VGA_H__ +#define __ARCH_VGA_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Initialize VGA output and clear the screen */ +void vga_init(void); + +/** @brief Simple string output on screen. + * + * If you want a new line you will have to "\\n". + * + * @return Length of output in bytes + */ +int vga_puts(const char *text); + +/** @brief Simple character output on screen. + * + * @return The original input character casted to int + */ +int vga_putchar(unsigned char c); + +/** @brief Clear the screen */ +void vga_cls(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/arch/x86/kernel/Makefile b/hermit/arch/x86/kernel/Makefile new file mode 100644 index 000000000..9b4d2c985 --- /dev/null +++ b/hermit/arch/x86/kernel/Makefile @@ -0,0 +1,5 @@ +C_source := irq.c idt.c isrs.c gdt.c processor.c timer.c tasks.c vga.c +ASM_source := entry.asm string.asm +MODULE := arch_x86_kernel + +include $(TOPDIR)/Makefile.inc diff --git a/hermit/arch/x86/kernel/entry.asm b/hermit/arch/x86/kernel/entry.asm new file mode 100644 index 000000000..a2f65906c --- /dev/null +++ b/hermit/arch/x86/kernel/entry.asm @@ -0,0 +1,544 @@ + +; Copyright (c) 2010, Stefan Lankes, RWTH Aachen University +; All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; * Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; * Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; * Neither the name of the University nor the names of its contributors +; may be used to endorse or promote products derived from this software +; without specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +; DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +; This is the kernel's entry point. We could either call main here, +; or we can use this to setup the stack or other nice stuff, like +; perhaps setting up the GDT and segments. Please note that interrupts +; are disabled at this point: More on interrupts later! + +%include "config.inc" + +[BITS 32] + +extern kernel_start ; defined in linker script +extern kernel_end + +; We use a special name to map this section at the begin of our kernel +; => Multiboot expects its magic number at the beginning of the kernel. +SECTION .mboot +global start +start: + jmp stublet + +; This part MUST be 4 byte aligned, so we solve that issue using 'ALIGN 4'. +ALIGN 4 +mboot: + ; Multiboot macros to make a few lines more readable later + MULTIBOOT_PAGE_ALIGN equ (1 << 0) + MULTIBOOT_MEMORY_INFO equ (1 << 1) + MULTIBOOT_HEADER_MAGIC equ 0x1BADB002 + MULTIBOOT_HEADER_FLAGS equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO + MULTIBOOT_CHECKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) + + ; This is the GRUB Multiboot header. A boot signature + dd MULTIBOOT_HEADER_MAGIC + dd MULTIBOOT_HEADER_FLAGS + dd MULTIBOOT_CHECKSUM + dd 0, 0, 0, 0, 0 ; address fields + dd 0, 0, 0, 0, + global base + global limit + base dq 0 + limit dq 0 + +ALIGN 4 +; we need already a valid GDT to switch in the 64bit modus +GDT64: ; Global Descriptor Table (64-bit). + .Null: equ $ - GDT64 ; The null descriptor. + dw 0 ; Limit (low). + dw 0 ; Base (low). + db 0 ; Base (middle) + db 0 ; Access. + db 0 ; Granularity. + db 0 ; Base (high). + .Code: equ $ - GDT64 ; The code descriptor. + dw 0 ; Limit (low). + dw 0 ; Base (low). + db 0 ; Base (middle) + db 10011000b ; Access. + db 00100000b ; Granularity. + db 0 ; Base (high). + .Data: equ $ - GDT64 ; The data descriptor. + dw 0 ; Limit (low). + dw 0 ; Base (low). + db 0 ; Base (middle) + db 10010010b ; Access. + db 00000000b ; Granularity. + db 0 ; Base (high). + .Pointer: ; The GDT-pointer. + dw $ - GDT64 - 1 ; Limit. + dq GDT64 ; Base. + +SECTION .text +ALIGN 4 +stublet: + ; Initialize stack pointer + mov esp, boot_stack + add esp, KERNEL_STACK_SIZE - 16 + + ; Interpret multiboot information + mov DWORD [mb_info], ebx + + ; Initialize CPU features + call cpu_init + + lgdt [GDT64.Pointer] ; Load the 64-bit global descriptor table. + jmp GDT64.Code:start64 ; Set the code segment and enter 64-bit long mode. + +; This will set up the x86 control registers: +; Caching and the floating point unit are enabled +; Bootstrap page tables are loaded and page size +; extensions (huge pages) enabled. +global cpu_init +cpu_init: + +; initialize page tables + +; map vga 1:1 +%ifdef CONFIG_VGA + push edi + mov eax, VIDEO_MEM_ADDR ; map vga + and eax, 0xFFFFF000 ; page align lower half + mov edi, eax + shr edi, 9 ; (edi >> 12) * 8 (index for boot_pgt) + add edi, boot_pgt + or eax, 0x113 ; set present, global, writable and cache disable bits + mov DWORD [edi], eax + pop edi +%endif + + ; map multiboot info 1:1 + push edi + mov eax, DWORD [mb_info] ; map multiboot info + cmp eax, 0 + je Lno_mbinfo + and eax, 0xFFFFF000 ; page align lower half + mov edi, eax + shr edi, 9 ; (edi >> 12) * 8 (index for boot_pgt) + add edi, boot_pgt + or eax, 0x101 ; set present and global bits + mov DWORD [edi], eax +Lno_mbinfo: + pop edi + + ; map kernel 1:1, use a page size of 2MB + push edi + mov eax, kernel_start + and eax, 0xFFE00000 + mov edi, eax + shr edi, 18 ; (edi >> 21) * 8 (index for boot_pgd) + add edi, boot_pgd + or eax, 0x183 + mov DWORD [edi], eax + pop edi + + ; check for long mode + + ; do we have the instruction cpuid? + pushfd + pop eax + mov ecx, eax + xor eax, 1 << 21 + push eax + popfd + pushfd + pop eax + push ecx + popfd + xor eax, ecx + jz Linvalid + + ; cpuid > 0x80000000? + mov eax, 0x80000000 + cpuid + cmp eax, 0x80000001 + jb Linvalid ; It is less, there is no long mode. + + ; do we have a long mode? + mov eax, 0x80000001 + cpuid + test edx, 1 << 29 ; Test if the LM-bit, which is bit 29, is set in the D-register. + jz Linvalid ; They aren't, there is no long mode. + + + ; we need to enable PAE modus + mov eax, cr4 + or eax, 1 << 5 + mov cr4, eax + + ; switch to the compatibility mode (which is part of long mode) + mov ecx, 0xC0000080 + rdmsr + or eax, 1 << 8 + wrmsr + + ; Set CR3 + mov eax, boot_pml4 + mov cr3, eax + + ; Set CR4 + mov eax, cr4 + and eax, 0xfffbf9ff ; disable SSE + or eax, (1 << 4) ; enable PSE + mov cr4, eax + + ; Set CR0 + mov eax, cr0 + and eax, ~(1 << 30) ; enable caching + and eax, ~(1 << 16) ; allow kernel write access to read-only pages + or eax, (1 << 31) ; enable paging + or eax, (1 << 0) ; long mode also needs PM-bit set + mov cr0, eax + + ret + +; there is no long mode +Linvalid: + jmp $ + +[BITS 64] +start64: + ; initialize segment registers + mov ax, GDT64.Data + mov ds, ax + mov es, ax + mov ss, ax + mov ax, 0x00 + mov fs, ax + mov gs, ax + + ; set default stack pointer + mov rsp, boot_stack + add rsp, KERNEL_STACK_SIZE-16 + + ; jump to the boot processors's C code + extern main + call main + jmp $ + +global gdt_flush +extern gp + +; This will set up our new segment registers and is declared in +; C as 'extern void gdt_flush();' +gdt_flush: + lgdt [gp] + ret + +; The first 32 interrupt service routines (ISR) entries correspond to exceptions. +; Some exceptions will push an error code onto the stack which is specific to +; the exception caused. To decrease the complexity, we handle this by pushing a +; Dummy error code of 0 onto the stack for any ISR that doesn't push an error +; code already. +; +; ISRs are registered as "Interrupt Gate". +; Therefore, the interrupt flag (IF) is already cleared. + +; NASM macro which pushs also an pseudo error code +%macro isrstub_pseudo_error 1 + global isr%1 + isr%1: + push byte 0 ; pseudo error code + push byte %1 + jmp common_stub +%endmacro + +; Similar to isrstub_pseudo_error, but without pushing +; a pseudo error code => The error code is already +; on the stack. +%macro isrstub 1 + global isr%1 + isr%1: + push byte %1 + jmp common_stub +%endmacro + +; Create isr entries, where the number after the +; pseudo error code represents following interrupts: +; 0: Divide By Zero Exception +; 1: Debug Exception +; 2: Non Maskable Interrupt Exception +; 3: Int 3 Exception +; 4: INTO Exception +; 5: Out of Bounds Exception +; 6: Invalid Opcode Exception +; 7: Coprocessor Not Available Exception +%assign i 0 +%rep 8 + isrstub_pseudo_error i +%assign i i+1 +%endrep + +; 8: Double Fault Exception (With Error Code!) +isrstub 8 + +; 9: Coprocessor Segment Overrun Exception +isrstub_pseudo_error 9 + +; 10: Bad TSS Exception (With Error Code!) +; 11: Segment Not Present Exception (With Error Code!) +; 12: Stack Fault Exception (With Error Code!) +; 13: General Protection Fault Exception (With Error Code!) +; 14: Page Fault Exception (With Error Code!) +%assign i 10 +%rep 5 + isrstub i +%assign i i+1 +%endrep + +; 15: Reserved Exception +; 16: Floating Point Exception +; 17: Alignment Check Exception +; 18: Machine Check Exceptio +; 19-31: Reserved +%assign i 15 +%rep 17 + isrstub_pseudo_error i +%assign i i+1 +%endrep + +; NASM macro for asynchronous interrupts (no exceptions) +%macro irqstub 1 + global irq%1 + irq%1: + push byte 0 ; pseudo error code + push byte 32+%1 + jmp common_stub +%endmacro + +; Create entries for the interrupts 0 to 23 +%assign i 0 +%rep 24 + irqstub i +%assign i i+1 +%endrep + +global apic_timer +apic_timer: + push byte 0 ; pseudo error code + push byte 123 + jmp common_stub + +global apic_lint0 +apic_lint0: + push byte 0 ; pseudo error code + push byte 124 + jmp common_stub + +global apic_lint1 +apic_lint1: + push byte 0 ; pseudo error code + push byte 125 + jmp common_stub + +global apic_error +apic_error: + push byte 0 ; pseudo error code + push byte 126 + jmp common_stub + +global apic_svr +apic_svr: + push byte 0 ; pseudo error code + push byte 127 + jmp common_stub + +extern irq_handler +extern get_current_stack +extern finish_task_switch + +global isrsyscall +; used to realize system calls +isrsyscall: + cli + ; set kernel stack + extern get_kernel_stack + call get_kernel_stack + xchg rsp, rax ; => rax contains original rsp + + push rax ; contains original rsp + push r15 + push r14 + push r13 + push r12 + push r11 + push rbp + push rdx + push rcx + push rbx + push rdi + push rsi + sti + + extern syscall_handler + call syscall_handler + + cli + pop rsi + pop rdi + pop rbx + pop rcx + pop rdx + pop rbp + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + pop r10 + mov rsp, r10 + sti + o64 sysret + +global switch_context +ALIGN 8 +switch_context: + ; create on the stack a pseudo interrupt + ; afterwards, we switch to the task with iret + mov rax, rdi ; rdi contains the address to store the old rsp + pushf ; RFLAGS + push QWORD 0x08 ; CS + push QWORD rollback ; RIP + push QWORD 0x00 ; Interrupt number + push QWORD 0x00edbabe ; Error code + push rax + push rcx + push rdx + push rbx + push rsp + push rbp + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + + jmp common_switch + +ALIGN 8 +rollback: + ret + +ALIGN 8 +common_stub: + push rax + push rcx + push rdx + push rbx + push rsp + push rbp + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + + ; use the same handler for interrupts and exceptions + mov rdi, rsp + call irq_handler + + cmp rax, 0 + je no_context_switch + +common_switch: + mov [rax], rsp ; store old rsp + call get_current_stack ; get new rsp + xchg rax, rsp + + ; set task switched flag + mov rax, cr0 + or eax, 8 + mov cr0, rax + + ; set rsp0 in the task state segment + extern set_kernel_stack + call set_kernel_stack + + ; call cleanup code + call finish_task_switch + +no_context_switch: + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rbp + add rsp, 8 + pop rbx + pop rdx + pop rcx + pop rax + + add rsp, 16 + iretq + +SECTION .data + +global mb_info: +ALIGN 8 +mb_info: + DQ 0 + +ALIGN 4096 +global boot_stack +boot_stack: + TIMES (KERNEL_STACK_SIZE) DB 0xcd + +; Bootstrap page tables are used during the initialization. +ALIGN 4096 +boot_pml4: + DQ boot_pdpt + 0x107 ; PG_PRESENT | PG_GLOBAL | PG_RW | PG_USER + times 510 DQ 0 ; PAGE_MAP_ENTRIES - 2 + DQ boot_pml4 + 0x303 ; PG_PRESENT | PG_GLOBAL | PG_RW | PG_SELF (self-reference) +boot_pdpt: + DQ boot_pgd + 0x107 ; PG_PRESENT | PG_GLOBAL | PG_RW | PG_USER + times 510 DQ 0 ; PAGE_MAP_ENTRIES - 2 + DQ boot_pml4 + 0x303 ; PG_PRESENT | PG_GLOBAL | PG_RW | PG_SELF (self-reference) +boot_pgd: + DQ boot_pgt + 0x107 ; PG_PRESENT | PG_GLOBAL | PG_RW | PG_USER + times 510 DQ 0 ; PAGE_MAP_ENTRIES - 2 + DQ boot_pml4 + 0x303 ; PG_PRESENT | PG_GLOBAL | PG_RW | PG_SELF (self-reference) +boot_pgt: + times 512 DQ 0 + +; add some hints to the ELF file +SECTION .note.GNU-stack noalloc noexec nowrite progbits diff --git a/hermit/arch/x86/kernel/gdt.c b/hermit/arch/x86/kernel/gdt.c new file mode 100644 index 000000000..37e5370b4 --- /dev/null +++ b/hermit/arch/x86/kernel/gdt.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +gdt_ptr_t gp; +static tss_t task_state_segment __attribute__ ((aligned (PAGE_SIZE))); +// currently, our kernel has full access to the ioports +static gdt_entry_t gdt[GDT_ENTRIES] = {[0 ... GDT_ENTRIES-1] = {0, 0, 0, 0, 0, 0}}; + +/* + * This is defined in entry.asm. We use this to properly reload + * the new segment registers + */ +extern void gdt_flush(void); + +extern const void boot_stack; + +void set_kernel_stack(void) +{ + task_t* curr_task = current_task; + + task_state_segment.rsp0 = (size_t) curr_task->stack + KERNEL_STACK_SIZE - 16; // => stack is 16byte aligned +} + +size_t get_kernel_stack(void) +{ + task_t* curr_task = current_task; + + return (size_t) curr_task->stack + KERNEL_STACK_SIZE - 16; +} + +/* Setup a descriptor in the Global Descriptor Table */ +void gdt_set_gate(int num, unsigned long base, unsigned long limit, + unsigned char access, unsigned char gran) +{ + configure_gdt_entry(&gdt[num], base, limit, access, gran); +} + +void configure_gdt_entry(gdt_entry_t *dest_entry, unsigned long base, unsigned long limit, + unsigned char access, unsigned char gran) +{ + /* Setup the descriptor base address */ + dest_entry->base_low = (base & 0xFFFF); + dest_entry->base_middle = (base >> 16) & 0xFF; + dest_entry->base_high = (base >> 24) & 0xFF; + + /* Setup the descriptor limits */ + dest_entry->limit_low = (limit & 0xFFFF); + dest_entry->granularity = ((limit >> 16) & 0x0F); + + /* Finally, set up the granularity and access flags */ + dest_entry->granularity |= (gran & 0xF0); + dest_entry->access = access; +} + +/* + * This will setup the special GDT + * pointer, set up the entries in our GDT, and then + * finally call gdt_flush() in our assembler file in order + * to tell the processor where the new GDT is and update the + * new segment registers + */ +void gdt_install(void) +{ + unsigned long gran_ds, gran_cs, limit; + int num = 0; + + memset(&task_state_segment, 0x00, sizeof(tss_t)); + + gran_cs = GDT_FLAG_64_BIT; + gran_ds = 0; + limit = 0; + + /* Setup the GDT pointer and limit */ + gp.limit = (sizeof(gdt_entry_t) * GDT_ENTRIES) - 1; + gp.base = (size_t) &gdt; + + /* Our NULL descriptor */ + gdt_set_gate(num++, 0, 0, 0, 0); + + /* + * The second entry is our Code Segment. The base address + * is 0, the limit is 4 GByte, it uses 4KByte granularity, + * and is a Code Segment descriptor. + */ + gdt_set_gate(num++, 0, limit, + GDT_FLAG_RING0 | GDT_FLAG_SEGMENT | GDT_FLAG_CODESEG | GDT_FLAG_PRESENT, gran_cs); + + /* + * The third entry is our Data Segment. It's EXACTLY the + * same as our code segment, but the descriptor type in + * this entry's access byte says it's a Data Segment + */ + gdt_set_gate(num++, 0, limit, + GDT_FLAG_RING0 | GDT_FLAG_SEGMENT | GDT_FLAG_DATASEG | GDT_FLAG_PRESENT, gran_ds); + + /* + * Create code segment for 32bit user-space applications (ring 3) + */ + gdt_set_gate(num++, 0, 0xFFFFFFFF, + GDT_FLAG_RING3 | GDT_FLAG_SEGMENT | GDT_FLAG_CODESEG | GDT_FLAG_PRESENT, GDT_FLAG_32_BIT | GDT_FLAG_4K_GRAN); + + /* + * Create data segment for user-space applications (ring 3) + */ + gdt_set_gate(num++, 0, limit, + GDT_FLAG_RING3 | GDT_FLAG_SEGMENT | GDT_FLAG_DATASEG | GDT_FLAG_PRESENT, gran_ds); + + /* + * Create code segment for 64bit user-space applications (ring 3) + */ + gdt_set_gate(num++, 0, limit, + GDT_FLAG_RING3 | GDT_FLAG_SEGMENT | GDT_FLAG_CODESEG | GDT_FLAG_PRESENT, gran_cs); + + task_state_segment.rsp0 = (size_t) &boot_stack - 0x10; + gdt_set_gate(num++, (unsigned long) (&task_state_segment), sizeof(tss_t)-1, + GDT_FLAG_PRESENT | GDT_FLAG_TSS | GDT_FLAG_RING0, gran_ds); + + /* Flush out the old GDT and install the new changes! */ + gdt_flush(); +} diff --git a/hermit/arch/x86/kernel/idt.c b/hermit/arch/x86/kernel/idt.c new file mode 100644 index 000000000..f1eb740ae --- /dev/null +++ b/hermit/arch/x86/kernel/idt.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file arch/x86/kernel/idt.c + * @brief Definitions and functions related to IDT + * + * + * This file defines the interface for interrupts as like + * structures to describe interrupt descriptor table entries.\n + * See idt.h for flag definitions. + */ + +#include +#include + +/* + * Declare an IDT of 256 entries. Although we will only use the + * first 32 entries in this tutorial, the rest exists as a bit + * of a trap. If any undefined IDT entry is hit, it normally + * will cause an "Unhandled Interrupt" exception. Any descriptor + * for which the 'presence' bit is cleared (0) will generate an + * "Unhandled Interrupt" exception + */ +static idt_entry_t idt[256] = {[0 ... 255] = {0, 0, 0, 0, 0, 0, 0}}; +static idt_ptr_t idtp; + +void configure_idt_entry(idt_entry_t *dest_entry, size_t base, + unsigned short sel, unsigned char flags) +{ + /* The interrupt routine's base address */ + dest_entry->base_lo = (base & 0xFFFF); + dest_entry->base_hi = (base >> 16) & 0xFFFF; + + /* The segment or 'selector' that this IDT entry will use + * is set here, along with any access flags */ + dest_entry->sel = sel; + dest_entry->always0 = 0; + dest_entry->flags = flags; +} + +/* + * Use this function to set an entry in the IDT. Alot simpler + * than twiddling with the GDT ;) + */ +void idt_set_gate(unsigned char num, size_t base, unsigned short sel, + unsigned char flags) +{ + configure_idt_entry(&idt[num], base, sel, flags); +} + +extern void isrsyscall(void); + +/* Installs the IDT */ +void idt_install(void) +{ + static int initialized = 0; + + if (!initialized) { + initialized = 1; + + /* Sets the special IDT pointer up, just like in 'gdt.c' */ + idtp.limit = (sizeof(idt_entry_t) * 256) - 1; + idtp.base = (size_t)&idt; + +#if 0 + /* Add any new ISRs to the IDT here using idt_set_gate */ + idt_set_gate(INT_SYSCALL, (size_t)isrsyscall, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING3|IDT_FLAG_32BIT|IDT_FLAG_TRAPGATE); +#endif + } + + /* Points the processor's internal register to the new IDT */ + asm volatile("lidt %0" : : "m" (idtp)); +} diff --git a/hermit/arch/x86/kernel/irq.c b/hermit/arch/x86/kernel/irq.c new file mode 100644 index 000000000..ca3193a7b --- /dev/null +++ b/hermit/arch/x86/kernel/irq.c @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file arch/x86/kernel/irq.c + * @brief Function definitions for irq.h and a standard IRQ-handler + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * These are our own ISRs that point to our special IRQ handler + * instead of the regular 'fault_handler' function + */ +extern void irq0(void); +extern void irq1(void); +extern void irq2(void); +extern void irq3(void); +extern void irq4(void); +extern void irq5(void); +extern void irq6(void); +extern void irq7(void); +extern void irq8(void); +extern void irq9(void); +extern void irq10(void); +extern void irq11(void); +extern void irq12(void); +extern void irq13(void); +extern void irq14(void); +extern void irq15(void); +extern void irq16(void); +extern void irq17(void); +extern void irq18(void); +extern void irq19(void); +extern void irq20(void); +extern void irq21(void); +extern void irq22(void); +extern void irq23(void); +extern void apic_timer(void); +extern void apic_lint0(void); +extern void apic_lint1(void); +extern void apic_error(void); +extern void apic_svr(void); + +#define MAX_HANDLERS 256 + +/** @brief IRQ handle pointers + * + * This array is actually an array of function pointers. We use + * this to handle custom IRQ handlers for a given IRQ + */ +static void* irq_routines[MAX_HANDLERS] = {[0 ... MAX_HANDLERS-1] = NULL}; + +/* This installs a custom IRQ handler for the given IRQ */ +int irq_install_handler(unsigned int irq, irq_handler_t handler) +{ + if (irq >= MAX_HANDLERS) + return -EINVAL; + + irq_routines[irq] = handler; + + return 0; +} + +/* This clears the handler for a given IRQ */ +int irq_uninstall_handler(unsigned int irq) +{ + if (irq >= MAX_HANDLERS) + return -EINVAL; + + irq_routines[irq] = NULL; + + return 0; +} + +/** @brief Remapping IRQs with a couple of IO output operations + * + * Normally, IRQs 0 to 7 are mapped to entries 8 to 15. This + * is a problem in protected mode, because IDT entry 8 is a + * Double Fault! Without remapping, every time IRQ0 fires, + * you get a Double Fault Exception, which is NOT what's + * actually happening. We send commands to the Programmable + * Interrupt Controller (PICs - also called the 8259's) in + * order to make IRQ0 to 15 be remapped to IDT entries 32 to + * 47 + */ +static int irq_remap(void) +{ + outportb(0x20, 0x11); + outportb(0xA0, 0x11); + outportb(0x21, 0x20); + outportb(0xA1, 0x28); + outportb(0x21, 0x04); + outportb(0xA1, 0x02); + outportb(0x21, 0x01); + outportb(0xA1, 0x01); + outportb(0x21, 0x0); + outportb(0xA1, 0x0); + + return 0; +} + +/** @brief Remap IRQs and install ISRs in IDT + * + * We first remap the interrupt controllers, and then we install + * the appropriate ISRs to the correct entries in the IDT.\n + * This is just like installing the exception handlers + */ +static int irq_install(void) +{ + irq_remap(); + + idt_set_gate(32, (size_t)irq0, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(33, (size_t)irq1, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(34, (size_t)irq2, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(35, (size_t)irq3, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(36, (size_t)irq4, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(37, (size_t)irq5, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(38, (size_t)irq6, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(39, (size_t)irq7, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(40, (size_t)irq8, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(41, (size_t)irq9, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(42, (size_t)irq10, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(43, (size_t)irq11, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(44, (size_t)irq12, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(45, (size_t)irq13, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(46, (size_t)irq14, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(47, (size_t)irq15, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(48, (size_t)irq16, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(49, (size_t)irq17, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(50, (size_t)irq18, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(51, (size_t)irq19, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(52, (size_t)irq20, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(53, (size_t)irq21, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(54, (size_t)irq22, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(55, (size_t)irq23, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + + // add APIC interrupt handler + idt_set_gate(123, (size_t)apic_timer, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(124, (size_t)apic_lint0, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(125, (size_t)apic_lint1, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(126, (size_t)apic_error, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(127, (size_t)apic_svr, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + + return 0; +} + +int irq_init(void) +{ + idt_install(); + isrs_install(); + irq_install(); + + return 0; +} + +/** @brief Default IRQ handler + * + * Each of the IRQ ISRs point to this function, rather than + * the 'fault_handler' in 'isrs.c'. The IRQ Controllers need + * to be told when you are done servicing them, so you need + * to send them an "End of Interrupt" command. If we use the PIC + * instead of the APIC, we have two 8259 chips: The first one + * exists at 0x20, the second one exists at 0xA0. If the second + * controller (an IRQ from 8 to 15) gets an interrupt, you need to + * acknowledge the interrupt at BOTH controllers, otherwise, you + * only send an EOI command to the first controller. If you don't send + * an EOI, it won't raise any more IRQs. + * + * Note: If we enabled the APIC, we also disabled the PIC. Afterwards, + * we get no interrupts between 0 and 15. + */ +size_t** irq_handler(struct state *s) +{ + size_t** ret = NULL; + + /* This is a blank function pointer */ + void (*handler) (struct state * s); + + /* + * Find out if we have a custom handler to run for this + * IRQ and then finally, run it + */ + if (BUILTIN_EXPECT(s->int_no < MAX_HANDLERS, 1)) { + handler = irq_routines[s->int_no]; + if (handler) + handler(s); + } else kprintf("Invalid interrupt number %d\n", s->int_no); + +#if 0 + // timer interrupt? + if ((s->int_no == 32) || (s->int_no == 123)) + ret = scheduler(); // switch to a new task + else if ((s->int_no >= 32) && (get_highest_priority() > current_task->prio)) + ret = scheduler(); + + apic_eoi(s->int_no); +#endif + + /* + * If the IDT entry that was invoked was greater-than-or-equal to 40 + * and lower than 48 (meaning IRQ8 - 15), then we need to + * send an EOI to the slave controller of the PIC + */ + if (s->int_no >= 40) + outportb(0xA0, 0x20); + + /* + * In either case, we need to send an EOI to the master + * interrupt controller of the PIC, too + */ + outportb(0x20, 0x20); + + return ret; +} diff --git a/hermit/arch/x86/kernel/isrs.c b/hermit/arch/x86/kernel/isrs.c new file mode 100644 index 000000000..88287ab75 --- /dev/null +++ b/hermit/arch/x86/kernel/isrs.c @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file arch/x86/kernel/isrs.c + * @brief Installation of interrupt service routines and definition of fault handler. + * + * This file contains prototypes for the first 32 entries of the IDT, + * an ISR installer procedure and a fault handler.\n + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * These are function prototypes for all of the exception + * handlers: The first 32 entries in the IDT are reserved + * by Intel and are designed to service exceptions! + */ +extern void isr0(void); +extern void isr1(void); +extern void isr2(void); +extern void isr3(void); +extern void isr4(void); +extern void isr5(void); +extern void isr6(void); +extern void isr7(void); +extern void isr8(void); +extern void isr9(void); +extern void isr10(void); +extern void isr11(void); +extern void isr12(void); +extern void isr13(void); +extern void isr14(void); +extern void isr15(void); +extern void isr16(void); +extern void isr17(void); +extern void isr18(void); +extern void isr19(void); +extern void isr20(void); +extern void isr21(void); +extern void isr22(void); +extern void isr23(void); +extern void isr24(void); +extern void isr25(void); +extern void isr26(void); +extern void isr27(void); +extern void isr28(void); +extern void isr29(void); +extern void isr30(void); +extern void isr31(void); + +static void fault_handler(struct state *s); +static void fpu_handler(struct state *s); + +/* + * This is a very repetitive function... it's not hard, it's + * just annoying. As you can see, we set the first 32 entries + * in the IDT to the first 32 ISRs. We can't use a for loop + * for this, because there is no way to get the function names + * that correspond to that given entry. We set the access + * flags to 0x8E. This means that the entry is present, is + * running in ring 0 (kernel level), and has the lower 5 bits + * set to the required '14', which is represented by 'E' in + * hex. + */ +void isrs_install(void) +{ + int i; + + idt_set_gate(0, (size_t)isr0, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(1, (size_t)isr1, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(2, (size_t)isr2, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(3, (size_t)isr3, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(4, (size_t)isr4, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(5, (size_t)isr5, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(6, (size_t)isr6, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(7, (size_t)isr7, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(8, (size_t)isr8, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(9, (size_t)isr9, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(10, (size_t)isr10, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(11, (size_t)isr11, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(12, (size_t)isr12, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(13, (size_t)isr13, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(14, (size_t)isr14, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(15, (size_t)isr15, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(16, (size_t)isr16, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(17, (size_t)isr17, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(18, (size_t)isr18, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(19, (size_t)isr19, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(20, (size_t)isr20, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(21, (size_t)isr21, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(22, (size_t)isr22, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(23, (size_t)isr23, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(24, (size_t)isr24, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(25, (size_t)isr25, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(26, (size_t)isr26, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(27, (size_t)isr27, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(28, (size_t)isr28, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(29, (size_t)isr29, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(30, (size_t)isr30, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + idt_set_gate(31, (size_t)isr31, KERNEL_CODE_SELECTOR, + IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP); + + // install the default handler + for(i=0; i<32; i++) + irq_install_handler(i, fault_handler); + + // set hanlder for fpu exceptions + irq_uninstall_handler(7); + irq_install_handler(7, fpu_handler); +} + +static void fpu_handler(struct state *s) +{ + task_t* task = current_task; + + asm volatile ("clts"); // clear the TS flag of cr0 + if (!(task->flags & TASK_FPU_INIT)) { + // use the FPU at the first time => Initialize FPU + fpu_init(&task->fpu); + task->flags |= TASK_FPU_INIT; + } + + restore_fpu_state(&task->fpu); + task->flags |= TASK_FPU_USED; +} + +/** @brief Exception messages + * + * This is a simple string array. It contains the message that + * corresponds to each and every exception. We get the correct + * message by accessing it like this: + * exception_message[interrupt_number] + */ +static const char *exception_messages[] = { + "Division By Zero", "Debug", "Non Maskable Interrupt", + "Breakpoint", "Into 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" }; + +/* + * All of our Exception handling Interrupt Service Routines will + * point to this function. This will tell us what exception has + * occured! Right now, we simply abort the current task. + * All ISRs disable interrupts while they are being + * serviced as a 'locking' mechanism to prevent an IRQ from + * happening and messing up kernel data structures + */ +static void fault_handler(struct state *s) +{ + if (s->int_no < 32) { + kputs(exception_messages[s->int_no]); + kprintf(" Exception (%d) at 0x%llx:0x%llx, error code 0x%llx, rflags 0x%llx\n", + s->int_no, s->cs, s->rip, s->error, s->rflags); + outportb(0x20, 0x20); + + irq_enable(); + abort(); + } +} diff --git a/hermit/arch/x86/kernel/processor.c b/hermit/arch/x86/kernel/processor.c new file mode 100644 index 000000000..0dd7500a5 --- /dev/null +++ b/hermit/arch/x86/kernel/processor.c @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +extern void isrsyscall(void); + +cpu_info_t cpu_info = { 0, 0, 0, 0}; +static uint32_t cpu_freq = 0; + +static void default_mb(void) +{ + asm volatile ("lock; addl $0,0(%%esp)" ::: "memory", "cc"); +} + +static void default_save_fpu_state(union fpu_state* state) +{ + asm volatile ("fnsave %0; fwait" : "=m"((*state).fsave) :: "memory"); +} + +static void default_restore_fpu_state(union fpu_state* state) +{ + asm volatile ("frstor %0" :: "m"(state->fsave)); +} + +static void default_fpu_init(union fpu_state* fpu) +{ + i387_fsave_t *fp = &fpu->fsave; + + memset(fp, 0x00, sizeof(i387_fsave_t)); + fp->cwd = 0xffff037fu; + fp->swd = 0xffff0000u; + fp->twd = 0xffffffffu; + fp->fos = 0xffff0000u; +} + +func_memory_barrier mb = default_mb; +func_memory_barrier rmb = default_mb; +func_memory_barrier wmb = default_mb; + +static void mfence(void) { asm volatile("mfence" ::: "memory"); } +static void lfence(void) { asm volatile("lfence" ::: "memory"); } +static void sfence(void) { asm volatile("sfence" ::: "memory"); } +handle_fpu_state save_fpu_state = default_save_fpu_state; +handle_fpu_state restore_fpu_state = default_restore_fpu_state; +handle_fpu_state fpu_init = default_fpu_init; + +static void save_fpu_state_fxsr(union fpu_state* state) +{ + asm volatile ("fxsave %0; fnclex" : "=m"((*state).fxsave) :: "memory"); +} + +static void restore_fpu_state_fxsr(union fpu_state* state) +{ + asm volatile ("fxrstor %0" :: "m"(state->fxsave)); +} + +static void fpu_init_fxsr(union fpu_state* fpu) +{ + i387_fxsave_t* fx = &fpu->fxsave; + + memset(fx, 0x00, sizeof(i387_fxsave_t)); + fx->cwd = 0x37f; + if (BUILTIN_EXPECT(has_sse(), 1)) + fx->mxcsr = 0x1f80; +} + +uint32_t detect_cpu_frequency(void) +{ + uint64_t start, end, diff; + uint64_t ticks, old; + + if (BUILTIN_EXPECT(cpu_freq > 0, 0)) + return cpu_freq; + + old = get_clock_tick(); + + /* wait for the next time slice */ + while((ticks = get_clock_tick()) - old == 0) + HALT; + + rmb(); + start = rdtsc(); + /* wait a second to determine the frequency */ + while(get_clock_tick() - ticks < TIMER_FREQ) + HALT; + rmb(); + end = rdtsc(); + + diff = end > start ? end - start : start - end; + cpu_freq = (uint32_t) (diff / (uint64_t) 1000000); + + return cpu_freq; +} + +int cpu_detection(void) { + uint32_t a=0, b=0, c=0, d=0; + uint32_t family, model, stepping; + size_t cr4; + uint8_t first_time = 0; + + if (!cpu_info.feature1) { + first_time = 1; + cpuid(1, &a, &b, &cpu_info.feature2, &cpu_info.feature1); + + family = (a & 0x00000F00) >> 8; + model = (a & 0x000000F0) >> 4; + stepping = a & 0x0000000F; + if ((family == 6) && (model < 3) && (stepping < 3)) + cpu_info.feature1 &= ~CPU_FEATURE_SEP; + + cpuid(0x80000001, &a, &b, &c, &cpu_info.feature3); + cpuid(0x80000008, &cpu_info.addr_width, &b, &c, &d); + } + + if (first_time) { + kprintf("Paging features: %s%s%s%s%s%s%s%s\n", + (cpu_info.feature1 & CPU_FEATUE_PSE) ? "PSE (2/4Mb) " : "", + (cpu_info.feature1 & CPU_FEATURE_PAE) ? "PAE " : "", + (cpu_info.feature1 & CPU_FEATURE_PGE) ? "PGE " : "", + (cpu_info.feature1 & CPU_FEATURE_PAT) ? "PAT " : "", + (cpu_info.feature1 & CPU_FEATURE_PSE36) ? "PSE36 " : "", + (cpu_info.feature3 & CPU_FEATURE_NX) ? "NX " : "", + (cpu_info.feature3 & CPU_FEATURE_1GBHP) ? "PSE (1Gb) " : "", + (cpu_info.feature3 & CPU_FEATURE_LM) ? "LM" : ""); + + kprintf("Physical adress-width: %u bits\n", cpu_info.addr_width & 0xff); + kprintf("Linear adress-width: %u bits\n", (cpu_info.addr_width >> 8) & 0xff); + kprintf("Sysenter instruction: %s\n", (cpu_info.feature1 & CPU_FEATURE_SEP) ? "available" : "unavailable"); + kprintf("Syscall instruction: %s\n", (cpu_info.feature3 & CPU_FEATURE_SYSCALL) ? "available" : "unavailable"); + } + + cr4 = read_cr4(); + if (has_fxsr()) + cr4 |= CR4_OSFXSR; // set the OSFXSR bit + if (has_sse()) + cr4 |= CR4_OSXMMEXCPT; // set the OSXMMEXCPT bit + if (has_pge()) + cr4 |= CR4_PGE; + write_cr4(cr4); + + if (cpu_info.feature3 & CPU_FEATURE_SYSCALL) { + wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_LMA | EFER_SCE); + wrmsr(MSR_STAR, (0x1BULL << 48) | (0x08ULL << 32)); + wrmsr(MSR_LSTAR, (size_t) &isrsyscall); + wrmsr(MSR_SYSCALL_MASK, 0); // we didn't clear RFLAGS during an interrupt + } else kputs("Processor doesn't support syscalls\n"); + + if (has_nx()) + wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_NXE); + + if (first_time && has_sse()) + wmb = sfence; + + if (first_time && has_sse2()) { + rmb = lfence; + mb = mfence; + } + + if (first_time && has_avx()) + kprintf("The CPU owns the Advanced Vector Extensions (AVX). However, HermitCore doesn't support AVX!\n"); + + if (has_fpu()) { + if (first_time) + kputs("Found and initialized FPU!\n"); + asm volatile ("fninit"); + } + + if (first_time && has_fxsr()) { + save_fpu_state = save_fpu_state_fxsr; + restore_fpu_state = restore_fpu_state_fxsr; + fpu_init = fpu_init_fxsr; + } + + if (first_time && on_hypervisor()) { + uint32_t c, d; + char vendor_id[13]; + + kprintf("HermitCore is running on a hypervisor!\n"); + + cpuid(0x40000000, &a, &b, &c, &d); + memcpy(vendor_id, &b, 4); + memcpy(vendor_id + 4, &c, 4); + memcpy(vendor_id + 8, &d, 4); + vendor_id[12] = '\0'; + + kprintf("Hypervisor Vendor Id: %s\n", vendor_id); + kprintf("Maximum input value for hypervisor: 0x%x\n", a); + } + + return 0; +} + +uint32_t get_cpu_frequency(void) +{ + if (cpu_freq > 0) + return cpu_freq; + + return detect_cpu_frequency(); +} + diff --git a/hermit/arch/x86/kernel/string.asm b/hermit/arch/x86/kernel/string.asm new file mode 100644 index 000000000..a289375ae --- /dev/null +++ b/hermit/arch/x86/kernel/string.asm @@ -0,0 +1,89 @@ +; +; Written by the Chair for Operating Systems, RWTH Aachen University +; +; NO Copyright (C) 2010-2011, Stefan Lankes +; consider these trivial functions to be public domain. +; +; These functions are distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; + +%include "config.inc" + +%ifdef CONFIG_X86_32 +[BITS 32] +%else +[BITS 64] +%endif +SECTION .text +global strcpy +strcpy: +%ifdef CONFIG_X86_32 + push ebp + mov ebp, esp + push edi + push esi + + mov esi, [ebp+12] + mov edi, [ebp+8] +%else + push rdi +%endif + +L1: + lodsb + stosb + test al, al + jne L1 + +%ifdef CONFIG_X86_32 + mov eax, [ebp+8] + pop esi + pop edi + pop ebp +%else + pop rax +%endif + ret + +global strncpy +strncpy: +%ifdef CONFIG_X86_32 + push ebp + mov ebp, esp + push edi + push esi + + mov ecx, [ebp+16] + mov esi, [ebp+12] + mov edi, [ebp+8] + +L2: + dec ecx +%else + push rdi + mov rcx, rdx + +L2: + dec rcx +%endif + js L3 + lodsb + stosb + test al, al + jne L1 + rep + stosb + +L3: +%ifdef CONFIG_X86_32 + mov eax, [ebp+8] + pop esi + pop edi + pop ebp +%else + pop rax +%endif + ret + +SECTION .note.GNU-stack noalloc noexec nowrite progbits diff --git a/hermit/arch/x86/kernel/tasks.c b/hermit/arch/x86/kernel/tasks.c new file mode 100644 index 000000000..0636a99f4 --- /dev/null +++ b/hermit/arch/x86/kernel/tasks.c @@ -0,0 +1,426 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +//#include +#include +//#include +#include + +size_t* get_current_stack(void) +{ + task_t* curr_task = current_task; + + // use new page table + write_cr3(curr_task->page_map); + + return curr_task->last_stack_pointer; +} + +int create_default_frame(task_t* task, entry_point_t ep, void* arg) +{ + size_t *stack; + struct state *stptr; + size_t state_size; + + if (BUILTIN_EXPECT(!task, 0)) + return -EINVAL; + + if (BUILTIN_EXPECT(!task->stack, 0)) + return -EINVAL; + + memset(task->stack, 0xCD, KERNEL_STACK_SIZE); + + /* The difference between setting up a task for SW-task-switching + * and not for HW-task-switching is setting up a stack and not a TSS. + * This is the stack which will be activated and popped off for iret later. + */ + stack = (size_t*) (task->stack + KERNEL_STACK_SIZE - 16); // => stack is 16byte aligned + + /* Only marker for debugging purposes, ... */ + *stack-- = 0xDEADBEEF; + + /* and the "caller" we shall return to. + * This procedure cleans the task after exit. */ + *stack = (size_t) leave_kernel_task; + + /* Next bunch on the stack is the initial register state. + * The stack must look like the stack of a task which was + * scheduled away previously. */ + state_size = sizeof(struct state); + stack = (size_t*) ((size_t) stack - state_size); + + stptr = (struct state *) stack; + memset(stptr, 0x00, state_size); + stptr->rsp = (size_t)stack + state_size; + /* the first-function-to-be-called's arguments, ... */ + stptr->rdi = (size_t) arg; + stptr->int_no = 0xB16B00B5; + stptr->error = 0xC03DB4B3; + + /* The instruction pointer shall be set on the first function to be called + after IRETing */ + stptr->rip = (size_t)ep; + stptr->cs = 0x08; + stptr->ss = 0x10; + stptr->rflags = 0x1202; + stptr->userrsp = stptr->rsp; + + /* Set the task's stack pointer entry to the stack we have crafted right now. */ + task->last_stack_pointer = (size_t*)stack; + + return 0; +} + +#if 0 + +#define MAX_ARGS (PAGE_SIZE - 2*sizeof(int) - sizeof(vfs_node_t*)) + +/** @brief Structure which keeps all + * relevant data for a new user task to start */ +typedef struct { + /// Points to the node with the executable in the file system + vfs_node_t* node; + /// Argument count + int argc; + /// Environment var count + int envc; + /// Buffer for env and argv values + char buffer[MAX_ARGS]; +} load_args_t; + +/** @brief Internally used function to load tasks with a load_args_t structure + * keeping all the information needed to launch. + * + * This is where the serious loading action is done. + */ +static int load_task(load_args_t* largs) +{ + uint32_t i, offset, idx; + uint32_t addr, npages; + size_t stack = 0, heap = 0; + size_t flags; + elf_header_t header; + elf_program_header_t prog_header; + //elf_section_header_t sec_header; + ///!!! kfree is missing! + fildes_t *file = kmalloc(sizeof(fildes_t)); + file->offset = 0; + file->flags = 0; + + //TODO: init the hole fildes_t struct! + task_t* curr_task = current_task; + int err; + + if (!largs) + return -EINVAL; + + file->node = largs->node; + if (!file->node) + return -EINVAL; + + err = read_fs(file, (uint8_t*)&header, sizeof(elf_header_t)); + if (err < 0) { + kprintf("read_fs failed: %d\n", err); + return err; + } + + if (BUILTIN_EXPECT(header.ident.magic != ELF_MAGIC, 0)) + goto invalid; + + if (BUILTIN_EXPECT(header.type != ELF_ET_EXEC, 0)) + goto invalid; + +#ifdef CONFIG_X86_32 + if (BUILTIN_EXPECT(header.machine != ELF_EM_386, 0)) + goto invalid; +#elif defined(CONFIG_X86_64) + if (BUILTIN_EXPECT(header.machine != ELF_EM_X86_64, 0)) + goto invalid; +#else + goto invalid; +#endif + +#ifdef CONFIG_X86_32 + if (BUILTIN_EXPECT(header.ident._class != ELF_CLASS_32, 0)) + goto invalid; +#elif defined(CONFIG_X86_64) + if (BUILTIN_EXPECT(header.ident._class != ELF_CLASS_64, 0)) + goto invalid; +#else + goto invalid; +#endif + + if (BUILTIN_EXPECT(header.ident.data != ELF_DATA_2LSB, 0)) + goto invalid; + + if (header.entry <= KERNEL_SPACE) + goto invalid; + + // interpret program header table + for (i=0; ioffset = header.ph_offset+i*header.ph_entry_size; + if (read_fs(file, (uint8_t*)&prog_header, sizeof(elf_program_header_t)) == 0) { + kprintf("Could not read programm header!\n"); + continue; + } + + switch(prog_header.type) + { + case ELF_PT_LOAD: // load program segment + if (!prog_header.virt_addr) + continue; + + npages = (prog_header.mem_size >> PAGE_BITS); + if (prog_header.mem_size & (PAGE_SIZE-1)) + npages++; + + addr = get_pages(npages); + + flags = PG_USER; +#ifdef CONFIG_X86_64 + if (has_nx() && !(prog_header.flags & PF_X)) + flags |= PG_XD; +#endif + // map page frames in the address space of the current task + if (page_map(prog_header.virt_addr, addr, npages, flags|PG_RW)) + kprintf("Could not map 0x%x at 0x%x\n", addr, prog_header.virt_addr); + + // clear pages + memset((void*) prog_header.virt_addr, 0x00, npages*PAGE_SIZE); + + // update heap location + if (heap < prog_header.virt_addr + prog_header.mem_size) + heap = prog_header.virt_addr + prog_header.mem_size; + + // load program + file->offset = prog_header.offset; + read_fs(file, (uint8_t*)prog_header.virt_addr, prog_header.file_size); + + flags = VMA_CACHEABLE; + if (prog_header.flags & PF_R) + flags |= VMA_READ; + if (prog_header.flags & PF_W) + flags |= VMA_WRITE; + if (prog_header.flags & PF_X) + flags |= VMA_EXECUTE; + vma_add(prog_header.virt_addr, prog_header.virt_addr+npages*PAGE_SIZE-1, flags); + + if (!(prog_header.flags & PF_W)) + page_set_flags(prog_header.virt_addr, npages, flags); + break; + + case ELF_PT_GNU_STACK: // Indicates stack executability + // create user-level stack + npages = DEFAULT_STACK_SIZE >> PAGE_BITS; + if (DEFAULT_STACK_SIZE & (PAGE_SIZE-1)) + npages++; + + addr = get_pages(npages); + stack = header.entry*2; // virtual address of the stack + flags = PG_USER|PG_RW; +#ifdef CONFIG_X86_64 + if (has_nx() && !(prog_header.flags & PF_X)) + flags |= PG_XD; +#endif + + if (page_map(stack, addr, npages, flags)) { + kprintf("Could not map stack at 0x%x\n", stack); + return -ENOMEM; + } + memset((void*) stack, 0x00, npages*PAGE_SIZE); + + // create vma regions for the user-level stack + flags = VMA_CACHEABLE; + if (prog_header.flags & PF_R) + flags |= VMA_READ; + if (prog_header.flags & PF_W) + flags |= VMA_WRITE; + if (prog_header.flags & PF_X) + flags |= VMA_EXECUTE; + vma_add(stack, stack+npages*PAGE_SIZE-1, flags); + break; + } + } + + // setup heap + if (!curr_task->heap) + curr_task->heap = (vma_t*) kmalloc(sizeof(vma_t)); + + if (BUILTIN_EXPECT(!curr_task->heap || !heap, 0)) { + kprintf("load_task: heap is missing!\n"); + return -ENOMEM; + } + + curr_task->heap->flags = VMA_HEAP|VMA_USER; + curr_task->heap->start = PAGE_FLOOR(heap); + curr_task->heap->end = PAGE_FLOOR(heap); + + if (BUILTIN_EXPECT(!stack, 0)) { + kprintf("Stack is missing!\n"); + return -ENOMEM; + } + + // push strings on the stack + offset = DEFAULT_STACK_SIZE-8; + memset((void*) (stack+offset), 0, 4); + offset -= MAX_ARGS; + memcpy((void*) (stack+offset), largs->buffer, MAX_ARGS); + idx = offset; + + // push argv on the stack + offset -= largs->argc * sizeof(char*); + for(i=0; iargc; i++) { + ((char**) (stack+offset))[i] = (char*) (stack+idx); + + while(((char*) stack)[idx] != '\0') + idx++; + idx++; + } + + // push env on the stack + offset -= (largs->envc+1) * sizeof(char*); + for(i=0; ienvc; i++) { + ((char**) (stack+offset))[i] = (char*) (stack+idx); + + while(((char*) stack)[idx] != '\0') + idx++; + idx++; + } + ((char**) (stack+offset))[largs->envc] = NULL; + + // push pointer to env + offset -= sizeof(char**); + if (!(largs->envc)) + *((char***) (stack+offset)) = NULL; + else + *((char***) (stack+offset)) = (char**) (stack + offset + sizeof(char**)); + + // push pointer to argv + offset -= sizeof(char**); + *((char***) (stack+offset)) = (char**) (stack + offset + 2*sizeof(char**) + (largs->envc+1) * sizeof(char*)); + + // push argc on the stack + offset -= sizeof(ssize_t); + *((ssize_t*) (stack+offset)) = (ssize_t) largs->argc; + + kfree(largs); + + // clear fpu state => currently not supported + curr_task->flags &= ~(TASK_FPU_USED|TASK_FPU_INIT); + + jump_to_user_code(header.entry, stack+offset); + + return 0; + +invalid: + kprintf("Invalid executable!\n"); + kprintf("magic number 0x%x\n", (uint32_t) header.ident.magic); + kprintf("header type 0x%x\n", (uint32_t) header.type); + kprintf("machine type 0x%x\n", (uint32_t) header.machine); + kprintf("elf ident class 0x%x\n", (uint32_t) header.ident._class); + kprintf("elf identdata 0x%x\n", header.ident.data); + kprintf("program entry point 0x%lx\n", (size_t) header.entry); + + return -EINVAL; +} + +/** @brief This call is used to adapt create_task calls + * which want to have a start function and argument list */ +static int user_entry(void* arg) +{ + int ret; + + finish_task_switch(); + + if (BUILTIN_EXPECT(!arg, 0)) + return -EINVAL; + + ret = load_task((load_args_t*) arg); + + kfree(arg); + + return ret; +} + +/** @brief Luxus-edition of create_user_task functions. Just call with an exe name + * + * @param id Pointer to the tid_t structure which shall be filles + * @param fname Executable's path and filename + * @param argv Arguments list + * @return + * - 0 on success + * - -ENOMEM (-12) or -EINVAL (-22) on failure + */ +int create_user_task(tid_t* id, const char* fname, char** argv) +{ + vfs_node_t* node; + int argc = 0; + size_t i, buffer_size = 0; + load_args_t* load_args = NULL; + char *dest, *src; + + node = findnode_fs((char*) fname); + if (!node || !(node->type == FS_FILE)) + return -EINVAL; + + // determine buffer size of argv + if (argv) { + while (argv[argc]) { + buffer_size += (strlen(argv[argc]) + 1); + argc++; + } + } + + if (argc <= 0) + return -EINVAL; + if (buffer_size >= MAX_ARGS) + return -EINVAL; + + load_args = kmalloc(sizeof(load_args_t)); + if (BUILTIN_EXPECT(!load_args, 0)) + return -ENOMEM; + load_args->node = node; + load_args->argc = argc; + load_args->envc = 0; + dest = load_args->buffer; + for (i=0; i +#include +#include +#include +#include +#include +#include +#include + +/* + * This will keep track of how many ticks the system + * has been running for + */ +static volatile uint64_t timer_ticks = 0; + +uint64_t get_clock_tick(void) +{ + return timer_ticks; +} + +/* + * Handles the timer. In this case, it's very simple: We + * increment the 'timer_ticks' variable every time the + * timer fires. + */ +static void timer_handler(struct state *s) +{ + /* Increment our 'tick counter' */ + timer_ticks++; +} + +#define LATCH(f) ((CLOCK_TICK_RATE + f/2) / f) +#define WAIT_SOME_TIME() do { uint64_t start = rdtsc(); \ + while(rdtsc() - start < 1000000) ; \ + } while (0) + +/* + * Sets up the system clock by installing the timer handler + * into IRQ0 + */ +int timer_init(void) +{ + /* + * Installs 'timer_handler' for the PIC and APIC timer, + * only one handler will be later used. + */ + irq_install_handler(32, timer_handler); + irq_install_handler(123, timer_handler); + + /* + * Port 0x43 is for initializing the PIT: + * + * 0x34 means the following: + * 0b... (step-by-step binary representation) + * ... 00 - channel 0 + * ... 11 - write two values to counter register: + * first low-, then high-byte + * ... 010 - mode number 2: "rate generator" / frequency divider + * ... 0 - binary counter (the alternative is BCD) + */ + outportb(0x43, 0x34); + + WAIT_SOME_TIME(); + + /* Port 0x40 is for the counter register of channel 0 */ + + outportb(0x40, LATCH(TIMER_FREQ) & 0xFF); /* low byte */ + + WAIT_SOME_TIME(); + + outportb(0x40, LATCH(TIMER_FREQ) >> 8); /* high byte */ + + return 0; +} diff --git a/hermit/arch/x86/kernel/vga.c b/hermit/arch/x86/kernel/vga.c new file mode 100644 index 000000000..098b172d3 --- /dev/null +++ b/hermit/arch/x86/kernel/vga.c @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#ifdef CONFIG_VGA + +/* + * These define our textpointer, our background and foreground + * colors (attributes), and x and y cursor coordinates + */ +static unsigned short *textmemptr; +static int attrib = 0x0F; +static int csr_x = 0, csr_y = 0; + +inline static unsigned short *memsetw(unsigned short *dest, unsigned short val, size_t count) +{ + size_t i; + + if (BUILTIN_EXPECT(!dest, 0)) + return dest; + + for (i = 0; i < count; i++) + dest[i] = val; + + return dest; +} + +/* Scrolls the screen */ +static void scroll(void) +{ + unsigned blank, temp; + + /* + * A blank is defined as a space... we need to give it + * backcolor too + */ + blank = 0x20 | (attrib << 8); + + /* Row 25 is the end, this means we need to scroll up */ + if (csr_y >= 25) { + + /* + * Move the current text chunk that makes up the screen + * + * back in the buffer by one line + */ + temp = csr_y - 25 + 1; + memcpy(textmemptr, textmemptr + temp * 80, + (25 - temp) * 80 * 2); + + /* + * Finally, we set the chunk of memory that occupies + * the last line of text to our 'blank' character + */ + memsetw(textmemptr + (25 - temp) * 80, blank, 80); + csr_y = 25 - 1; + } +} + +/* + * Updates the hardware cursor: the little blinking line + * on the screen under the last character pressed! + */ +static void move_csr(void) +{ + unsigned temp; + + /* + * The equation for finding the index in a linear + * chunk of memory can be represented by: + * Index = [(y * width) + x] */ + temp = csr_y * 80 + csr_x; + + /* + * This sends a command to indicies 14 and 15 in the + * CRT Control Register of the VGA controller. These + * are the high and low bytes of the index that show + * where the hardware cursor is to be 'blinking'. To + * learn more, you should look up some VGA specific + * programming documents. A great start to graphics: + * http://www.brackeen.com/home/vga + */ + outportb(0x3D4, 14); + outportb(0x3D5, temp >> 8); + outportb(0x3D4, 15); + outportb(0x3D5, temp); +} + +/* Clears the screen */ +void vga_clear(void) +{ + unsigned blank; + int i; + + /* + * Again, we need the 'short' that will be used to + * represent a space with color + */ + blank = 0x20 | (attrib << 8); + + /* + * Fills the entire screen with spaces in our current + * color + **/ + for (i = 0; i < 25; i++) + memsetw(textmemptr + i * 80, blank, 80); + + /* + * Update out virtual cursor, and then move the + * hardware cursor + */ + csr_x = 0; + csr_y = 0; + move_csr(); +} + +/* Puts a single character on the screen */ +int vga_putchar(unsigned char c) +{ + unsigned short *where; + unsigned att = attrib << 8; + + /* Handle a backspace by moving the cursor back one space */ + if (c == 0x08) { + if (csr_x != 0) + csr_x--; + } + + /* + * Handles a tab by incrementing the cursor's x, but only + * to a point that will make it divisible by 8 + */ + else if (c == 0x09) { + csr_x = (csr_x + 8) & ~(8 - 1); + } + + /* + * Handles a 'Carriage Return', which simply brings the + * cursor back to the margin + */ + else if (c == '\r') { + csr_x = 0; + } + + /* + * We handle our newlines the way DOS and BIOS do: we + * treat it as if a 'CR' was there also, so we bring the + * cursor to the margin and increment the 'y' value + */ + else if (c == '\n') { + csr_x = 0; + csr_y++; + } + + /* + * Any character greater than and including the space is a + * printable character. The equation for finding the index + * in a linear chunk of memory can be represented by: + * Index = [(y * width) + x] + */ + else if (c >= ' ') { + where = textmemptr + (csr_y * 80 + csr_x); + *where = c | att; /* Character AND attributes: color */ + csr_x++; + } + + /* + * If the cursor has reached the edge of the screen's width, we + * insert a new line in there + */ + if (csr_x >= 80) { + csr_x = 0; + csr_y++; + } + + /* Scroll the screen if needed, and finally move the cursor */ + scroll(); + move_csr(); + + return (int) c; +} + +/* Uses the routine above to output a string... */ +int vga_puts(const char *text) +{ + size_t i; + + for (i = 0; i < strlen(text); i++) + vga_putchar(text[i]); + + return i-1; +} + +/* Sets the forecolor and backcolor we will use */ +//void settextcolor(unsigned char forecolor, unsigned char backcolor) +//{ + + /* + * Top 4 bytes are the background, bottom 4 bytes + * are the foreground color + */ +// attrib = (backcolor << 4) | (forecolor & 0x0F); +//} + +/* Sets our text-mode VGA pointer, then clears the screen for us */ +void vga_init(void) +{ + textmemptr = (unsigned short *)VIDEO_MEM_ADDR; + vga_clear(); +} + +#endif diff --git a/hermit/arch/x86/mm/Makefile b/hermit/arch/x86/mm/Makefile new file mode 100644 index 000000000..2f9b1834a --- /dev/null +++ b/hermit/arch/x86/mm/Makefile @@ -0,0 +1,4 @@ +C_source := page.c +MODULE := arch_x86_mm + +include $(TOPDIR)/Makefile.inc diff --git a/hermit/arch/x86/mm/page.c b/hermit/arch/x86/mm/page.c new file mode 100644 index 000000000..81acb5dd2 --- /dev/null +++ b/hermit/arch/x86/mm/page.c @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * 2014, Steffen Vogel, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * This is a 32/64 bit portable paging implementation for the x86 architecture + * using self-referenced page tables i. + * See http://www.noteblok.net/2014/06/14/bachelor/ for a detailed description. + * + * @author Steffen Vogel + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +/* Note that linker symbols are not variables, they have no memory + * allocated for maintaining a value, rather their address is their value. */ +extern const void kernel_start; +//extern const void kernel_end; + +/// This page is reserved for copying +#define PAGE_TMP (PAGE_FLOOR((size_t) &kernel_start) - PAGE_SIZE) + +/** Lock for kernel space page tables */ +static spinlock_t kslock = SPINLOCK_INIT; + +/** This PGD table is initialized in entry.asm */ +extern size_t* boot_map; + +#if 0 +/** A self-reference enables direct access to all page tables */ +static size_t * const self[PAGE_LEVELS] = { + (size_t *) 0xFFC00000, + (size_t *) 0xFFFFF000 +}; + +/** An other self-reference for page_map_copy() */ +static size_t * const other[PAGE_LEVELS] = { + (size_t *) 0xFF800000, + (size_t *) 0xFFFFE000 +}; +#else +/** A self-reference enables direct access to all page tables */ +static size_t* const self[PAGE_LEVELS] = { + (size_t *) 0xFFFFFF8000000000, + (size_t *) 0xFFFFFFFFC0000000, + (size_t *) 0xFFFFFFFFFFE00000, + (size_t *) 0xFFFFFFFFFFFFF000 +}; + +/** An other self-reference for page_map_copy() */ +static size_t * const other[PAGE_LEVELS] = { + (size_t *) 0xFFFFFF0000000000, + (size_t *) 0xFFFFFFFF80000000, + (size_t *) 0xFFFFFFFFFFC00000, + (size_t *) 0xFFFFFFFFFFFFE000 +}; +#endif + +size_t virt_to_phys(size_t addr) +{ + size_t vpn = addr >> PAGE_BITS; // virtual page number + size_t entry = self[0][vpn]; // page table entry + size_t off = addr & ~PAGE_MASK; // offset within page + size_t phy = entry & PAGE_MASK; // physical page frame number + + return phy | off; +} + +//TODO: code is missing +int page_set_flags(size_t viraddr, uint32_t npages, int flags) +{ + return -EINVAL; +} + +int page_map(size_t viraddr, size_t phyaddr, size_t npages, size_t bits) +{ + int lvl, ret = -ENOMEM; + long vpn = viraddr >> PAGE_BITS; + long first[PAGE_LEVELS], last[PAGE_LEVELS]; + + /* Calculate index boundaries for page map traversal */ + for (lvl=0; lvl> (lvl * PAGE_MAP_BITS); + last[lvl] = (vpn+npages-1) >> (lvl * PAGE_MAP_BITS); + } + + /** @todo: might not be sufficient! */ + if (bits & PG_USER) + spinlock_irqsave_lock(¤t_task->page_lock); + else + spinlock_lock(&kslock); + + /* Start iterating through the entries + * beginning at the root table (PGD or PML4) */ + for (lvl=PAGE_LEVELS-1; lvl>=0; lvl--) { + for (vpn=first[lvl]; vpn<=last[lvl]; vpn++) { + if (lvl) { /* PML4, PDPT, PGD */ + if (!(self[lvl][vpn] & PG_PRESENT)) { + /* There's no table available which covers the region. + * Therefore we need to create a new empty table. */ + size_t phyaddr = get_pages(1); + if (BUILTIN_EXPECT(!phyaddr, 0)) + goto out; + + if (bits & PG_USER) + atomic_int32_inc(¤t_task->user_usage); + + /* Reference the new table within its parent */ +#if 0 + self[lvl][vpn] = phyaddr | bits | PG_PRESENT | PG_USER | PG_RW; +#else + self[lvl][vpn] = (phyaddr | bits | PG_PRESENT | PG_USER | PG_RW) & ~PG_XD; +#endif + + /* Fill new table with zeros */ + memset(&self[lvl-1][vpn<page_lock); + else + spinlock_unlock(&kslock); + + return ret; +} + +/** Tables are freed by page_map_drop() */ +int page_unmap(size_t viraddr, size_t npages) +{ + /* We aquire both locks for kernel and task tables + * as we dont know to which the region belongs. */ + spinlock_irqsave_lock(¤t_task->page_lock); + spinlock_lock(&kslock); + + /* Start iterating through the entries. + * Only the PGT entries are removed. Tables remain allocated. */ + size_t vpn, start = viraddr>>PAGE_BITS; + for (vpn=start; vpnpage_lock); + spinlock_unlock(&kslock); + + /* This can't fail because we don't make checks here */ + return 0; +} + +int page_map_drop(void) +{ + void traverse(int lvl, long vpn) { + long stop; + for (stop=vpn+PAGE_MAP_ENTRIES; vpnuser_usage); + } + } + } + + spinlock_irqsave_lock(¤t_task->page_lock); + + traverse(PAGE_LEVELS-1, 0); + + spinlock_irqsave_unlock(¤t_task->page_lock); + + /* This can't fail because we don't make checks here */ + return 0; +} + +int page_map_copy(task_t *dest) +{ + int traverse(int lvl, long vpn) { + long stop; + for (stop=vpn+PAGE_MAP_ENTRIES; vpnuser_usage); + + other[lvl][vpn] = phyaddr | (self[lvl][vpn] & ~PAGE_MASK); + if (lvl) /* PML4, PDPT, PGD */ + traverse(lvl-1, vpn<page_lock); + self[PAGE_LEVELS-1][PAGE_MAP_ENTRIES-2] = dest->page_map | PG_PRESENT | PG_SELF | PG_RW; + + int ret = traverse(PAGE_LEVELS-1, 0); + + other[PAGE_LEVELS-1][PAGE_MAP_ENTRIES-1] = dest->page_map | PG_PRESENT | PG_SELF | PG_RW; + self [PAGE_LEVELS-1][PAGE_MAP_ENTRIES-2] = 0; + spinlock_irqsave_unlock(¤t_task->page_lock); + + /* Flush TLB entries of 'other' self-reference */ + flush_tlb(); + + return ret; +} + +void page_fault_handler(struct state *s) +{ + size_t viraddr = read_cr2(); + task_t* task = current_task; + + // on demand userspace heap mapping + if ((task->heap) && (viraddr >= task->heap->start) && (viraddr < task->heap->end)) { + viraddr &= PAGE_MASK; + + size_t phyaddr = get_page(); + if (BUILTIN_EXPECT(!phyaddr, 0)) { + kprintf("out of memory: task = %u\n", task->id); + goto default_handler; + } + + int ret = page_map(viraddr, phyaddr, 1, PG_USER|PG_RW); + if (BUILTIN_EXPECT(ret, 0)) { + kprintf("map_region: could not map %#lx to %#lx, task = %u\n", phyaddr, viraddr, task->id); + put_page(phyaddr); + + goto default_handler; + } + + memset((void*) viraddr, 0x00, PAGE_SIZE); // fill with zeros + + return; + } + +default_handler: +#if 0 + kprintf("Page Fault Exception (%d) at cs:ip = %#x:%#lx, task = %u, addr = %#lx, error = %#x [ %s %s %s %s %s ]\n", + s->int_no, s->cs, s->eip, current_task->id, viraddr, s->error, + (s->error & 0x4) ? "user" : "supervisor", + (s->error & 0x10) ? "instruction" : "data", + (s->error & 0x2) ? "write" : ((s->error & 0x10) ? "fetch" : "read"), + (s->error & 0x1) ? "protection" : "not present", + (s->error & 0x8) ? "reserved bit" : "\b"); +#else + kprintf("Page Fault Exception (%d) at cs:ip = %#x:%#lx, task = %u, addr = %#lx, error = %#x [ %s %s %s %s %s ]\n", + s->int_no, s->cs, s->rip, current_task->id, viraddr, s->error, + (s->error & 0x4) ? "user" : "supervisor", + (s->error & 0x10) ? "instruction" : "data", + (s->error & 0x2) ? "write" : ((s->error & 0x10) ? "fetch" : "read"), + (s->error & 0x1) ? "protection" : "not present", + (s->error & 0x8) ? "reserved bit" : "\b"); +#endif + + while(1) HALT; +} + +int page_init(void) +{ + size_t addr, npages; + int i; + + /* Replace default pagefault handler */ + irq_uninstall_handler(14); + irq_install_handler(14, page_fault_handler); + + /* Map multiboot information and modules */ + if (mb_info) { + // already mapped => entry.asm + //addr = (size_t) mb_info & PAGE_MASK; + //npages = PAGE_FLOOR(sizeof(*mb_info)) >> PAGE_BITS; + //page_map(addr, addr, npages, PG_GLOBAL); + + if (mb_info->flags & MULTIBOOT_INFO_MODS) { + addr = mb_info->mods_addr; + npages = PAGE_FLOOR(mb_info->mods_count*sizeof(multiboot_module_t)) >> PAGE_BITS; + page_map(addr, addr, npages, PG_GLOBAL); + kprintf("Map module info at 0x%lx\n", addr); + + multiboot_module_t* mmodule = (multiboot_module_t*) ((size_t) mb_info->mods_addr); + for(i=0; imods_count; i++) { + addr = mmodule[i].mod_start; + npages = PAGE_FLOOR(mmodule[i].mod_end - mmodule[i].mod_start) >> PAGE_BITS; + page_map(addr, addr, npages, PG_GLOBAL); + kprintf("Map modules at 0x%lx\n", addr); + } + } + } + + return 0; +} diff --git a/hermit/include/hermit/config.h b/hermit/include/hermit/config.h new file mode 100644 index 000000000..f5c667949 --- /dev/null +++ b/hermit/include/hermit/config.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __CONFIG_H__ +#define __CONFIG_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define HERMIT_VERSION "0.1" +#define MAX_TASKS 16 +#define MAX_FNAME 128 +#define TIMER_FREQ 100 /* in HZ */ +#define CLOCK_TICK_RATE 1193182 /* 8254 chip's internal oscillator frequency */ +#define VIDEO_MEM_ADDR 0xB8000 /* the video memory address */ +#define CACHE_LINE 64 +#define KERNEL_STACK_SIZE (8<<10) /* 8 KiB */ +#define DEFAULT_STACK_SIZE (16*1024) /* 16 KiB */ +#define BITMAP_SIZE (16<<5) /* for 16 MiB of RAM */ +#define KMSG_SIZE (8*1024) +#define INT_SYSCALL 0x80 +#define MAILBOX_SIZE 32 + +#define BYTE_ORDER LITTLE_ENDIAN + +//#define CONFIG_VGA + +#define BUILTIN_EXPECT(exp, b) __builtin_expect((exp), (b)) +//#define BUILTIN_EXPECT(exp, b) (exp) +#define NORETURN __attribute__((noreturn)) +#define STDCALL __attribute__((stdcall)) + +#define HAVE_ARCH_MEMSET +#define HAVE_ARCH_MEMCPY +#define HAVE_ARCH_STRLEN +#define HAVE_ARCH_STRCPY +#define HAVE_ARCH_STRNCPY + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/include/hermit/ctype.h b/hermit/include/hermit/ctype.h new file mode 100644 index 000000000..dc881db1d --- /dev/null +++ b/hermit/include/hermit/ctype.h @@ -0,0 +1,129 @@ +/**************************************************************************************** + * + * Author: Stefan Lankes + * Chair for Operating Systems, RWTH Aachen University + * Date: 24/03/2011 + * + **************************************************************************************** + * + * Written by the Chair for Operating Systems, RWTH Aachen University + * + * NO Copyright (C) 2010, Stefan Lankes, + * consider these trivial functions to be public domain. + * + * These functions are distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + */ + +/** + * @author Stefan Lankes + * @file include/hermit/ctype.h + * @brief Functions related to alphanumerical character values + * + * This file contains functions helping to determine + * the type of alphanumerical character values. + */ + +#ifndef __CTYPE_H_ +#define __CYTPE_H_ + +/** Returns true if the value of 'c' is an ASCII-charater */ +static inline int isascii(int c) +{ + return (((unsigned char)(c))<=0x7f); +} + +/** Applies an and-operation to + * push the value of 'c' into the ASCII-range */ +static inline int toascii(int c) +{ + return (((unsigned char)(c))&0x7f); +} + +/** Returns true if the value of 'c' is the + * space character or a control character */ +static inline int isspace(int c) +{ + if (!isascii(c)) + return 0; + + if (' ' == (unsigned char) c) + return 1; + if ('\n' == (unsigned char) c) + return 1; + if ('\r' == (unsigned char) c) + return 1; + if ('\t' == (unsigned char) c) + return 1; + if ('\v' == (unsigned char) c) + return 1; + if ('\f' == (unsigned char) c) + return 1; + + return 0; +} + +/** Returns true if the value of 'c' is a number */ +static inline int isdigit(int c) +{ + if (!isascii(c)) + return 0; + + if (((unsigned char) c >= '0') && ((unsigned char) c <= '9')) + return 1; + + return 0; +} + +/** Returns true if the value of 'c' is a lower case letter */ +static inline int islower(int c) +{ + if (!isascii(c)) + return 0; + + if (((unsigned char) c >= 'a') && ((unsigned char) c <= 'z')) + return 1; + + return 0; +} + +/** Returns true if the value of 'c' is an upper case letter */ +static inline int isupper(int c) +{ + if (!isascii(c)) + return 0; + + if (((unsigned char) c >= 'A') && ((unsigned char) c <= 'Z')) + return 1; + + return 0; +} + +/** Returns true if the value of 'c' is an alphabetic character */ +static inline int isalpha(int c) +{ + if (isupper(c) || islower(c)) + return 1; + + return 0; +} + +/** Makes the input character lower case.\n Will do nothing if it + * was something different than an upper case letter before. */ +static inline unsigned char tolower(unsigned char c) +{ + if (isupper(c)) + c -= 'A'-'a'; + return c; +} + +/** Makes the input character upper case.\n Will do nothing if it + * was something different than a lower case letter before. */ +static inline unsigned char toupper(unsigned char c) +{ + if (islower(c)) + c -= 'a'-'A'; + return c; +} + +#endif diff --git a/hermit/include/hermit/errno.h b/hermit/include/hermit/errno.h new file mode 100644 index 000000000..f387572fa --- /dev/null +++ b/hermit/include/hermit/errno.h @@ -0,0 +1,180 @@ +/** + * @author Stefan Lankes + * @file include/hermit/errno.h + * @brief Error number define constants + * + * This file just contains the full list of error numbers which can + * be returned somewhere. In principle, we use the same error codes + * than newlib. + */ + +#ifndef __ERRNO_H__ +#define __ERRNO_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define EPERM 1 /* Not super-user */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* I/O error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Arg list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file number */ +#define ECHILD 10 /* No children */ +#define EAGAIN 11 /* No more processes */ +#define ENOMEM 12 /* Not enough core */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#ifdef __LINUX_ERRNO_EXTENSIONS__ +#define ENOTBLK 15 /* Block device required */ +#endif +#define EBUSY 16 /* Mount device busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* No such device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* Too many open files in system */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Not a typewriter */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#define EDOM 33 /* Math arg out of domain of func */ +#define ERANGE 34 /* Math result not representable */ +#define ENOMSG 35 /* No message of desired type */ +#define EIDRM 36 /* Identifier removed */ +#ifdef __LINUX_ERRNO_EXTENSIONS__ +#define ECHRNG 37 /* Channel number out of range */ +#define EL2NSYNC 38 /* Level 2 not synchronized */ +#define EL3HLT 39 /* Level 3 halted */ +#define EL3RST 40 /* Level 3 reset */ +#define ELNRNG 41 /* Link number out of range */ +#define EUNATCH 42 /* Protocol driver not attached */ +#define ENOCSI 43 /* No CSI structure available */ +#define EL2HLT 44 /* Level 2 halted */ +#endif +#define EDEADLK 45 /* Deadlock condition */ +#define ENOLCK 46 /* No record locks available */ +#ifdef __LINUX_ERRNO_EXTENSIONS__ +#define EBADE 50 /* Invalid exchange */ +#define EBADR 51 /* Invalid request descriptor */ +#define EXFULL 52 /* Exchange full */ +#define ENOANO 53 /* No anode */ +#define EBADRQC 54 /* Invalid request code */ +#define EBADSLT 55 /* Invalid slot */ +#define EDEADLOCK 56 /* File locking deadlock error */ +#define EBFONT 57 /* Bad font file fmt */ +#endif +#define ENOSTR 60 /* Device not a stream */ +#define ENODATA 61 /* No data (for no delay io) */ +#define ETIME 62 /* Timer expired */ +#define ENOSR 63 /* Out of streams resources */ +#ifdef __LINUX_ERRNO_EXTENSIONS__ +#define ENONET 64 /* Machine is not on the network */ +#define ENOPKG 65 /* Package not installed */ +#define EREMOTE 66 /* The object is remote */ +#endif +#define ENOLINK 67 /* The link has been severed */ +#ifdef __LINUX_ERRNO_EXTENSIONS__ +#define EADV 68 /* Advertise error */ +#define ESRMNT 69 /* Srmount error */ +#define ECOMM 70 /* Communication error on send */ +#endif +#define EPROTO 71 /* Protocol error */ +#define EMULTIHOP 74 /* Multihop attempted */ +#ifdef __LINUX_ERRNO_EXTENSIONS__ +#define ELBIN 75 /* Inode is remote (not really error) */ +#define EDOTDOT 76 /* Cross mount point (not really error) */ +#endif +#define EBADMSG 77 /* Trying to read unreadable message */ +#define EFTYPE 79 /* Inappropriate file type or format */ +#ifdef __LINUX_ERRNO_EXTENSIONS__ +#define ENOTUNIQ 80 /* Given log. name not unique */ +#define EBADFD 81 /* f.d. invalid for this operation */ +#define EREMCHG 82 /* Remote address changed */ +#define ELIBACC 83 /* Can't access a needed shared lib */ +#define ELIBBAD 84 /* Accessing a corrupted shared lib */ +#define ELIBSCN 85 /* .lib section in a.out corrupted */ +#define ELIBMAX 86 /* Attempting to link in too many libs */ +#define ELIBEXEC 87 /* Attempting to exec a shared library */ +#endif +#define ENOSYS 88 /* Function not implemented */ +#ifdef __CYGWIN__ +#define ENMFILE 89 /* No more files */ +#endif +#define ENOTEMPTY 90 /* Directory not empty */ +#define ENAMETOOLONG 91 /* File or path name too long */ +#define ELOOP 92 /* Too many symbolic links */ +#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define EPFNOSUPPORT 96 /* Protocol family not supported */ +#define ECONNRESET 104 /* Connection reset by peer */ +#define ENOBUFS 105 /* No buffer space available */ +#define EAFNOSUPPORT 106 /* Address family not supported by protocol family */ +#define EPROTOTYPE 107 /* Protocol wrong type for socket */ +#define ENOTSOCK 108 /* Socket operation on non-socket */ +#define ENOPROTOOPT 109 /* Protocol not available */ +#ifdef __LINUX_ERRNO_EXTENSIONS__ +#define ESHUTDOWN 110 /* Can't send after socket shutdown */ +#endif +#define ECONNREFUSED 111 /* Connection refused */ +#define EADDRINUSE 112 /* Address already in use */ +#define ECONNABORTED 113 /* Connection aborted */ +#define ENETUNREACH 114 /* Network is unreachable */ +#define ENETDOWN 115 /* Network interface is not configured */ +#define ETIMEDOUT 116 /* Connection timed out */ +#define EHOSTDOWN 117 /* Host is down */ +#define EHOSTUNREACH 118 /* Host is unreachable */ +#define EINPROGRESS 119 /* Connection already in progress */ +#define EALREADY 120 /* Socket already connected */ +#define EDESTADDRREQ 121 /* Destination address required */ +#define EMSGSIZE 122 /* Message too long */ +#define EPROTONOSUPPORT 123 /* Unknown protocol */ +#ifdef __LINUX_ERRNO_EXTENSIONS__ +#define ESOCKTNOSUPPORT 124 /* Socket type not supported */ +#endif +#define EADDRNOTAVAIL 125 /* Address not available */ +#define ENETRESET 126 +#define EISCONN 127 /* Socket is already connected */ +#define ENOTCONN 128 /* Socket is not connected */ +#define ETOOMANYREFS 129 +#ifdef __LINUX_ERRNO_EXTENSIONS__ +#define EPROCLIM 130 +#define EUSERS 131 +#endif +#define EDQUOT 132 +#define ESTALE 133 +#define ENOTSUP 134 /* Not supported */ +#ifdef __LINUX_ERRNO_EXTENSIONS__ +#define ENOMEDIUM 135 /* No medium (in tape drive) */ +#endif +#ifdef __CYGWIN__ +#define ENOSHARE 136 /* No such host or network path */ +#define ECASECLASH 137 /* Filename exists with different case */ +#endif +#define EILSEQ 138 +#define EOVERFLOW 139 /* Value too large for defined data type */ +#define ECANCELED 140 /* Operation canceled */ +#define ENOTRECOVERABLE 141 /* State not recoverable */ +#define EOWNERDEAD 142 /* Previous owner died */ +#ifdef __LINUX_ERRNO_EXTENSIONS__ +#define ESTRPIPE 143 /* Streams pipe error */ +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/include/hermit/malloc.h b/hermit/include/hermit/malloc.h new file mode 100644 index 000000000..82eee9855 --- /dev/null +++ b/hermit/include/hermit/malloc.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2014, Steffen Vogel, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Steffen Vogel + */ + +#ifndef __MALLOC_H__ +#define __MALLOC_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// Binary exponent of maximal size for kmalloc() +#define BUDDY_MAX 32 // 4 GB +/// Binary exponent of minimal buddy size +#define BUDDY_MIN 3 // 8 Byte >= sizeof(buddy_t) +/// Binary exponent of the size which we allocate with buddy_fill() +#define BUDDY_ALLOC 16 // 64 KByte = 16 * PAGE_SIZE + +#define BUDDY_LISTS (BUDDY_MAX-BUDDY_MIN+1) +#define BUDDY_MAGIC 0xBABE + +union buddy; + +/** @brief Buddy + * + * Every free memory block is stored in a linked list according to its size. + * We can use this free memory to store store this buddy_t union which represents + * this block (the buddy_t union is alligned to the front). + * Therefore the address of the buddy_t union is equal with the address + * of the underlying free memory block. + * + * Every allocated memory block is prefixed with its binary size exponent and + * a known magic number. This prefix is hidden by the user because its located + * before the actual memory address returned by kmalloc() + */ +typedef union buddy { + /// Pointer to the next buddy in the linked list. + union buddy* next; + struct { + /// The binary exponent of the block size + uint8_t exponent; + /// Must be equal to BUDDY_MAGIC for a valid memory block + uint16_t magic; + } prefix; +} buddy_t; + +/** @brief Dump free buddies */ +void buddy_dump(void); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/hermit/include/hermit/memory.h b/hermit/include/hermit/memory.h new file mode 100644 index 000000000..739e414e3 --- /dev/null +++ b/hermit/include/hermit/memory.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Steffen Vogel + * @file include/memory.h + * @brief Memory related functions + * + * This file contains platform independent memory functions + */ + +#ifndef __MEMORY_H__ +#define __MEMORY_H__ + +/** @brief Initialize the memory subsystem */ +int memory_init(void); + +/** @brief Request physical page frames */ +size_t get_pages(size_t npages); + +/** @brief Get a single page + * + * Convenience function: uses get_pages(1); + */ +static inline size_t get_page(void) { return get_pages(1); } + +/** @brief release physical page frames */ +int put_pages(size_t phyaddr, size_t npages); + +/** @brief Put a single page + * + * Convenience function: uses put_pages(1); + */ +static inline int put_page(size_t phyaddr) { return put_pages(phyaddr, 1); } + +/** @brief Copy a physical page frame + * + * @param psrc physical address of source page frame + * @param pdest physical address of source page frame + * @return + * - 0 on success + * - -1 on failure + */ +int copy_page(size_t pdest, size_t psrc); + +#endif diff --git a/hermit/include/hermit/processor.h b/hermit/include/hermit/processor.h new file mode 100644 index 000000000..e69881922 --- /dev/null +++ b/hermit/include/hermit/processor.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __PROCESSOR_H__ +#define __PROCESSOR_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/include/hermit/spinlock.h b/hermit/include/hermit/spinlock.h new file mode 100644 index 000000000..a883cf4e8 --- /dev/null +++ b/hermit/include/hermit/spinlock.h @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2011, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file include/hermit/spinlock.h + * @brief Spinlock functions + */ + +#ifndef __SPINLOCK_H__ +#define __SPINLOCK_H__ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Initialization of a spinlock + * + * Initialize each spinlock before use! + * + * @param s Pointer to the spinlock structure to initialize. + * @return + * - 0 on success + * - -EINVAL (-22) on failure + */ +inline static int spinlock_init(spinlock_t* s) { + if (BUILTIN_EXPECT(!s, 0)) + return -EINVAL; + + atomic_int32_set(&s->queue, 0); + atomic_int32_set(&s->dequeue, 1); + s->owner = MAX_TASKS; + s->counter = 0; + + return 0; +} + +/** @brief Destroy spinlock after use + * @return + * - 0 on success + * - -EINVAL (-22) on failure + */ +inline static int spinlock_destroy(spinlock_t* s) { + if (BUILTIN_EXPECT(!s, 0)) + return -EINVAL; + + s->owner = MAX_TASKS; + s->counter = 0; + + return 0; +} + +/** @brief Lock spinlock at entry of critical section + * @return + * - 0 on success + * - -EINVAL (-22) on failure + */ +inline static int spinlock_lock(spinlock_t* s) { + int32_t ticket; + + if (BUILTIN_EXPECT(!s, 0)) + return -EINVAL; + + if (s->owner == current_task->id) { + s->counter++; + return 0; + } + + ticket = atomic_int32_add(&s->queue, 1); + while(atomic_int32_read(&s->dequeue) != ticket) { + PAUSE; + } + s->owner = current_task->id; + s->counter = 1; + + return 0; +} + +/** @brief Unlock spinlock on exit of critical section + * @return + * - 0 on success + * - -EINVAL (-22) on failure + */ +inline static int spinlock_unlock(spinlock_t* s) { + if (BUILTIN_EXPECT(!s, 0)) + return -EINVAL; + + s->counter--; + if (!s->counter) { + s->owner = MAX_TASKS; + atomic_int32_inc(&s->dequeue); + } + + return 0; +} + +/** @brief Initialization of a irqsave spinlock + * + * Initialize each irqsave spinlock before use! + * + * @return + * - 0 on success + * - -EINVAL (-22) on failure + */ +inline static int spinlock_irqsave_init(spinlock_irqsave_t* s) { + if (BUILTIN_EXPECT(!s, 0)) + return -EINVAL; + + atomic_int32_set(&s->queue, 0); + atomic_int32_set(&s->dequeue, 1); + s->flags = 0; + s->counter = 0; + + return 0; +} + +/** @brief Destroy irqsave spinlock after use + * @return + * - 0 on success + * - -EINVAL (-22) on failure + */ +inline static int spinlock_irqsave_destroy(spinlock_irqsave_t* s) { + if (BUILTIN_EXPECT(!s, 0)) + return -EINVAL; + + s->flags = 0; + s->counter = 0; + + return 0; +} + +/** @brief Unlock an irqsave spinlock on exit of critical section + * @return + * - 0 on success + * - -EINVAL (-22) on failure + */ +inline static int spinlock_irqsave_lock(spinlock_irqsave_t* s) { + int32_t ticket; + uint8_t flags; + + if (BUILTIN_EXPECT(!s, 0)) + return -EINVAL; + + flags = irq_nested_disable(); + if (s->counter == 1) { + s->counter++; + return 0; + } + + ticket = atomic_int32_add(&s->queue, 1); + while (atomic_int32_read(&s->dequeue) != ticket) { + PAUSE; + } + + s->flags = flags; + s->counter = 1; + + return 0; +} + +/** @brief Unlock irqsave spinlock on exit of critical section and re-enable interrupts + * @return + * - 0 on success + * - -EINVAL (-22) on failure + */ +inline static int spinlock_irqsave_unlock(spinlock_irqsave_t* s) { + uint8_t flags; + + if (BUILTIN_EXPECT(!s, 0)) + return -EINVAL; + + s->counter--; + if (!s->counter) { + flags = s->flags; + s->flags = 0; + atomic_int32_inc(&s->dequeue); + irq_nested_enable(flags); + } + + return 0; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/include/hermit/spinlock_types.h b/hermit/include/hermit/spinlock_types.h new file mode 100644 index 000000000..add747995 --- /dev/null +++ b/hermit/include/hermit/spinlock_types.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2011, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file include/hermit/spinlock_types.h + * @brief Spinlock type definition + */ + +#ifndef __SPINLOCK_TYPES_H__ +#define __SPINLOCK_TYPES_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/** @brief Spinlock structure */ +typedef struct spinlock { + /// Internal queue + atomic_int32_t queue; + /// Internal dequeue + atomic_int32_t dequeue; + /// Owner of this spinlock structure + tid_t owner; + /// Internal counter var + uint32_t counter; +} spinlock_t; + +typedef struct spinlock_irqsave { + /// Internal queue + atomic_int32_t queue; + /// Internal dequeue + atomic_int32_t dequeue; + /// Internal counter var + uint32_t counter; + /// Interrupt flag + uint8_t flags; +} spinlock_irqsave_t; + +/// Macro for spinlock initialization +#define SPINLOCK_INIT { ATOMIC_INIT(0), ATOMIC_INIT(1), MAX_TASKS, 0} +/// Macro for irqsave spinlock initialization +#define SPINLOCK_IRQSAVE_INIT { ATOMIC_INIT(0), ATOMIC_INIT(1), 0, 0} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/include/hermit/stdarg.h b/hermit/include/hermit/stdarg.h new file mode 100644 index 000000000..f5f643594 --- /dev/null +++ b/hermit/include/hermit/stdarg.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __STDARG_H__ +#define __STDARG_H__ + +/** + * @author Stefan Lankes + * @file include/hermit/stdarg.h + * @brief Definition of variable argument lists + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef __builtin_va_list va_list; + +/// Initialize a variable argument list +#define va_start __builtin_va_start +/// Retrieve next argument +#define va_arg __builtin_va_arg +/// End using variable argument list +#define va_end __builtin_va_end +/// copies the (previously initialized) variable argument list +#define va_copy __builtin_va_copy + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/include/hermit/stddef.h b/hermit/include/hermit/stddef.h new file mode 100644 index 000000000..2dc4bf603 --- /dev/null +++ b/hermit/include/hermit/stddef.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __STDDEF_H__ +#define __STDDEF_H__ + +/** + * @author Stefan Lankes + * @file include/hermit/stddef.h + * @brief Definition of basic data types + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define NULL ((void*) 0) + +/// represents a task identifier +typedef unsigned int tid_t; + +struct task; +/// pointer to the current (running) task +extern struct task* current_task; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/include/hermit/stdio.h b/hermit/include/hermit/stdio.h new file mode 100644 index 000000000..4d64a1934 --- /dev/null +++ b/hermit/include/hermit/stdio.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file include/hermit/stdio.h + * @brief Stringstream related functions. Mainly printf-stuff. + */ + +#ifndef __STDIO_H__ +#define __STDIO_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Works like the ANSI C function puts + */ +int kputs(const char *); + +/** + * Works like the ANSI C function putchar + */ +int kputchar(int); + +/** + * Works like the ANSI C function printf + */ +int kprintf(const char*, ...); + +/** + * Initialize the I/O functions + */ +int koutput_init(void); + +/** + * Works like the ANSI c function sprintf + */ +int ksprintf(char *str, const char *format, ...); + +/** + * Works like the ANSI c function sprintf + */ +int ksnprintf(char *str, size_t size, const char *format, ...); + +/** + * Scaled down version of printf(3) + */ +int kvprintf(char const *fmt, void (*func) (int, void *), void *arg, int radix, va_list ap); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/include/hermit/stdlib.h b/hermit/include/hermit/stdlib.h new file mode 100644 index 000000000..2169dbd33 --- /dev/null +++ b/hermit/include/hermit/stdlib.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file include/hermit/stdlib.h + * @brief Kernel space malloc and free functions and conversion functions + * + * This file contains some memory alloc and free calls for the kernel + * and conversion functions. + */ + +#ifndef __STDLIB_H__ +#define __STDLIB_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief General page allocator function + * + * This function allocates and maps whole pages. + * To avoid fragmentation you should use kmalloc() and kfree()! + * + * @param sz Desired size of the new memory + * @param flags Flags to for map_region(), vma_add() + * + * @return Pointer to the new memory range + */ +void* palloc(size_t sz, uint32_t flags); + +/** @brief Free general kernel memory + * + * The pmalloc() doesn't track how much memory was allocated for which pointer, + * so you have to specify how much memory shall be freed. + * + * @param sz The size which should freed + */ +void pfree(void* addr, size_t sz); + +/** @brief The memory allocator function + * + * This allocator uses a buddy system to manage free memory. + * + * @return Pointer to the new memory range + */ +void* kmalloc(size_t sz); + +/** @brief The memory free function + * + * Releases memory allocated by malloc() + * + * @param addr The address to the memory block allocated by malloc() + */ +void kfree(void* addr); + +/** @brief Create a new stack for a new task + * + * @return start address of the new stack + */ +void* create_stack(tid_t id); + +/** @brief String to long + * + * @return Long value of the parsed numerical string + */ +long strtol(const char* str, char** endptr, int base); + +/** @brief String to unsigned long + * + * @return Unsigned long value of the parsed numerical string + */ +unsigned long strtoul(const char* nptr, char** endptr, int base); + +/** @brief ASCII to integer + * + * Convenience function using strtol(). + * + * @return Integer value of the parsed numerical string + */ +static inline int atoi(const char *str) +{ + return (int)strtol(str, (char **)NULL, 10); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/include/hermit/string.h b/hermit/include/hermit/string.h new file mode 100644 index 000000000..efd38af7e --- /dev/null +++ b/hermit/include/hermit/string.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __STRING_H__ +#define __STRING_H__ + +/** + * @author Stefan Lankes + * @file include/hermit/string.h + * @brief Definition of basic string and memory opeations + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef HAVE_ARCH_MEMCPY +void *memcpy(void *dest, const void *src, size_t count); +#endif + +#ifndef HAVE_ARCH_MEMSET +void *memset(void *dest, int val, size_t count); +#endif + +#ifndef HAVE_ARCH_STRLEN +size_t strlen(const char *str); +#endif + +#ifndef HAVE_ARCH_STRNCPY +char *strncpy(char *dest, const char *src, size_t n); +#endif + +#ifndef HAVE_ARCH_STRCPY +char *strcpy(char *dest, const char *src); +#endif + +#ifndef HAVE_ARCH_STRCMP +int strcmp(const char *s1, const char *s2); +#endif + +#ifndef HAVE_ARCH_STRNCMP +int strncmp(const char *s1, const char *s2, size_t n); +#endif + +char *strstr(const char *s, const char *find); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/include/hermit/syscall.h b/hermit/include/hermit/syscall.h new file mode 100644 index 000000000..48a6dc103 --- /dev/null +++ b/hermit/include/hermit/syscall.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2011, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file include/hermit/syscall.h + * @brief System call number definitions + * + * This file contains define constants for every syscall's number. + */ + +#ifndef __SYSCALL_H__ +#define __SYSCALL_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define __NR_exit 0 +#define __NR_write 1 +#define __NR_open 2 +#define __NR_close 3 +#define __NR_read 4 +#define __NR_lseek 6 +#define __NR_unlink 7 +#define __NR_getpid 8 +#define __NR_kill 9 +#define __NR_fstat 10 +#define __NR_sbrk 11 +#define __NR_fork 12 +#define __NR_wait 13 +#define __NR_execve 14 +#define __NR_times 15 +#define __NR_accept 16 +#define __NR_bind 17 +#define __NR_closesocket 18 +#define __NR_connect 19 +#define __NR_listen 20 +#define __NR_recv 21 +#define __NR_send 22 +#define __NR_socket 23 +#define __NR_getsockopt 24 +#define __NR_setsockopt 25 +#define __NR_gethostbyname 26 +#define __NR_sendto 27 +#define __NR_recvfrom 28 +#define __NR_select 29 +#define __NR_stat 30 +#define __NR_dup 31 +#define __NR_dup2 32 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/include/hermit/tasks.h b/hermit/include/hermit/tasks.h new file mode 100644 index 000000000..3813d5482 --- /dev/null +++ b/hermit/include/hermit/tasks.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file include/hermit/tasks.h + * @brief Task related + * + * Create and leave tasks or fork them. + */ + +#ifndef __TASKS_H__ +#define __TASKS_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief System call to terminate a user level process */ +void NORETURN sys_exit(int); + +/** @brief Task switcher + * + * Timer-interrupted use of this function for task switching + * + * @return + * - 0 no context switch + * - !0 address of the old stack pointer + */ +size_t** scheduler(void); + +/** @brief Initialize the multitasking subsystem + * + * This procedure sets the current task to the + * current "task" (there are no tasks, yet) and that was it. + * + * @return + * - 0 on success + * - -ENOMEM (-12) on failure + */ +int multitasking_init(void); + +/** @brief create a kernel-level task. + * + * @param id The value behind this pointer will be set to the new task's id + * @param ep Pointer to the entry function for the new task + * @param args Arguments the task shall start with + * @param prio Desired priority of the new kernel task + * + * @return + * - 0 on success + * - -EINVAL (-22) on failure + */ +int create_kernel_task(tid_t* id, entry_point_t ep, void* args, uint8_t prio); + +/** @brief Create a user level task. + * + * @param id The value behind this pointer will be set to the new task's id + * @param fname Filename of the executable to start the task with + * @param argv Pointer to arguments array + * + * @return + * - 0 on success + * - -EINVAL (-22) or -ENOMEM (-12)on failure + */ +int create_user_task(tid_t* id, const char* fame, char** argv); + +/** @brief Create a task with a specific entry point + * + * @todo Don't acquire table_lock for the whole task creation. + * + * @param id Pointer to a tid_t struct were the id shall be set + * @param ep Pointer to the function the task shall start with + * @param arg Arguments list + * @param prio Desired priority of the new task + * @param core_id Start the new task on the core with this id + * + * @return + * - 0 on success + * - -ENOMEM (-12) or -EINVAL (-22) on failure + */ +int create_task(tid_t* id, entry_point_t ep, void* arg, uint8_t prio); + +/** @brief Cleanup function for the task termination + * + * On termination, the task call this function to cleanup its address space. + */ +void finish_task_switch(void); + +/** @brief determine the highest priority of all tasks, which are ready + * + * @return + * - return highest priority + * - if no task is ready, the function returns an invalid value (> MAX_PRIO) + */ +uint32_t get_highest_priority(void); + +/** @brief Call to rescheduling + * + * This is a purely assembled procedure for rescheduling + */ +void reschedule(void); + +/** @brief Wake up a blocked task + * + * The task's status will be changed to TASK_READY + * + * @return + * - 0 on success + * - -EINVAL (-22) on failure + */ +int wakeup_task(tid_t); + +/** @brief Block current task + * + * The current task's status will be changed to TASK_BLOCKED + * + * @return + * - 0 on success + * - -EINVAL (-22) on failure + */ +int block_current_task(void); + +/** @brief Abort current task */ +void NORETURN abort(void); + +/** @brief This function shall be called by leaving kernel-level tasks */ +void NORETURN leave_kernel_task(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/include/hermit/tasks_types.h b/hermit/include/hermit/tasks_types.h new file mode 100644 index 000000000..1bb063338 --- /dev/null +++ b/hermit/include/hermit/tasks_types.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file include/hermit/tasks_types.h + * @brief Task related structure definitions + * + * This file contains the task_t structure definition + * and task state define constants + */ + +#ifndef __TASKS_TYPES_H__ +#define __TASKS_TYPES_H__ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define TASK_INVALID 0 +#define TASK_READY 1 +#define TASK_RUNNING 2 +#define TASK_BLOCKED 3 +#define TASK_FINISHED 4 +#define TASK_IDLE 5 + +#define TASK_DEFAULT_FLAGS 0 +#define TASK_FPU_INIT (1 << 0) +#define TASK_FPU_USED (1 << 1) + +#define MAX_PRIO 31 +#define REALTIME_PRIO 31 +#define HIGH_PRIO 16 +#define NORMAL_PRIO 8 +#define LOW_PRIO 1 +#define IDLE_PRIO 0 + +typedef int (*entry_point_t)(void*); + +/** @brief Represents a the process control block */ +typedef struct task { + /// Task id = position in the task table + tid_t id __attribute__ ((aligned (CACHE_LINE))); + /// Task status (INVALID, READY, RUNNING, ...) + uint32_t status; + /// copy of the stack pointer before a context switch + size_t* last_stack_pointer; + /// start address of the stack + void* stack; + /// Additional status flags. For instance, to signalize the using of the FPU + uint8_t flags; + /// Task priority + uint8_t prio; + /// Physical address of root page table + size_t page_map; + /// Lock for page tables + spinlock_irqsave_t page_lock; + /// lock for the VMA_list + spinlock_t vma_lock; + /// list of VMAs + vma_t* vma_list; + /// the userspace heap + vma_t* heap; + /// usage in number of pages (including page map tables) + atomic_int32_t user_usage; + /// next task in the queue + struct task* next; + /// previous task in the queue + struct task* prev; + /// FPU state + union fpu_state fpu; +} task_t; + +typedef struct { + task_t* first; + task_t* last; +} task_list_t; + +/** @brief Represents a queue for all runable tasks */ +typedef struct { + /// idle task + task_t* idle __attribute__ ((aligned (CACHE_LINE))); + /// previous task + task_t* old_task; + /// total number of tasks in the queue + uint32_t nr_tasks; + /// indicates the used priority queues + uint32_t prio_bitmap; + /// a queue for each priority + task_list_t queue[MAX_PRIO]; + /// lock for this runqueue + spinlock_irqsave_t lock; +} readyqueues_t; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/include/hermit/time.h b/hermit/include/hermit/time.h new file mode 100644 index 000000000..9768e0bcf --- /dev/null +++ b/hermit/include/hermit/time.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file include/hermit/time.h + * @brief Time related functions + */ + +#ifndef __TIME_H__ +#define __TIME_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Initialize Timer interrupts + * + * This procedure installs IRQ handlers for timer interrupts + */ +int timer_init(void); + +/** @brief Returns the current number of ticks. + * @return Current number of ticks + */ +uint64_t get_clock_tick(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/include/hermit/vma.h b/hermit/include/hermit/vma.h new file mode 100644 index 000000000..9f903683b --- /dev/null +++ b/hermit/include/hermit/vma.h @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2011, Stefan Lankes, RWTH Aachen University + * 2014, Steffen Vogel, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @author Steffen Vogel + * @file include/hermit/vma.h + * @brief VMA related sructure and functions + */ + +#ifndef __VMA_H__ +#define __VMA_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// Read access to this VMA is allowed +#define VMA_READ (1 << 0) +/// Write access to this VMA is allowed +#define VMA_WRITE (1 << 1) +/// Instructions fetches in this VMA are allowed +#define VMA_EXECUTE (1 << 2) +/// This VMA is cacheable +#define VMA_CACHEABLE (1 << 3) +/// This VMA is not accessable +#define VMA_NO_ACCESS (1 << 4) +/// This VMA should be part of the userspace +#define VMA_USER (1 << 5) +/// A collection of flags used for the kernel heap (kmalloc) +#define VMA_HEAP (VMA_READ|VMA_WRITE|VMA_CACHEABLE) + +// boundaries for VAS allocation +#define VMA_KERN_MIN 0xC0000 +#define VMA_KERN_MAX KERNEL_SPACE +#define VMA_USER_MIN KERNEL_SPACE + +// last three top level entries are reserved +#define VMA_USER_MAX 0xFFFFFE8000000000 + +struct vma; + +/** @brief VMA structure definition + * + * Each item in this linked list marks a used part of the virtual address space. + * Its used by vm_alloc() to find holes between them. + */ +typedef struct vma { + /// Start address of the memory area + size_t start; + /// End address of the memory area + size_t end; + /// Type flags field + uint32_t flags; + /// Pointer of next VMA element in the list + struct vma* next; + /// Pointer to previous VMA element in the list + struct vma* prev; +} vma_t; + +/** @brief Initalize the kernelspace VMA list + * + * Reserves several system-relevant virtual memory regions: + * - SMP boot page (SMP_SETUP_ADDR) + * - VGA video memory (VIDEO_MEM_ADDR) + * - The kernel (kernel_start - kernel_end) + * - Multiboot structure (mb_info) + * - Multiboot mmap (mb_info->mmap_*) + * - Multiboot modules (mb_info->mods_*) + * - Init Ramdisk + * + * @return + * - 0 on success + * - <0 on failure + */ +int vma_init(void); + +/** @brief Add a new virtual memory area to the list of VMAs + * + * @param start Start address of the new area + * @param end End address of the new area + * @param flags Type flags the new area shall have + * + * @return + * - 0 on success + * - -EINVAL (-22) or -EINVAL (-12) on failure + */ +int vma_add(size_t start, size_t end, uint32_t flags); + +/** @brief Search for a free memory area + * + * @param size Size of requestes VMA in bytes + * @param flags + * @return Type flags the new area shall have + * - 0 on failure + * - the start address of a free area + */ +size_t vma_alloc(size_t size, uint32_t flags); + +/** @brief Free an allocated memory area + * + * @param start Start address of the area to be freed + * @param end End address of the to be freed + * @return + * - 0 on success + * - -EINVAL (-22) on failure + */ +int vma_free(size_t start, size_t end); + +/** @brief Free all virtual memory areas + * + * @return + * - 0 on success + */ +int drop_vma_list(struct task* task); + +/** @brief Copy the VMA list of the current task to task + * + * @param task The task where the list should be copied to + * @return + * - 0 on success + */ +int copy_vma_list(struct task* src, struct task* dest); + +/** @brief Dump information about this task's VMAs into the terminal. */ +void vma_dump(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/kernel/Makefile b/hermit/kernel/Makefile new file mode 100644 index 000000000..f99855354 --- /dev/null +++ b/hermit/kernel/Makefile @@ -0,0 +1,4 @@ +C_source := main.c tasks.c syscall.c +MODULE := kernel + +include $(TOPDIR)/Makefile.inc diff --git a/hermit/kernel/main.c b/hermit/kernel/main.c new file mode 100644 index 000000000..6532f5fe0 --- /dev/null +++ b/hermit/kernel/main.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * Note that linker symbols are not variables, they have no memory allocated for + * maintaining a value, rather their address is their value. + */ +extern const void kernel_start; +extern const void kernel_end; +extern const void bss_start; +extern const void bss_end; +extern char __BUILD_DATE; +extern char __BUILD_TIME; + +/* Page frame counters */ +extern atomic_int32_t total_pages; +extern atomic_int32_t total_allocated_pages; +extern atomic_int32_t total_available_pages; + +static int hermit_init(void) +{ + // initialize .bss section + memset((void*)&bss_start, 0x00, ((size_t) &bss_end - (size_t) &bss_start)); + + koutput_init(); + system_init(); + irq_init(); + timer_init(); + multitasking_init(); + memory_init(); + + return 0; +} + +int main(void) +{ + hermit_init(); + system_calibration(); // enables also interrupts + + kprintf("This is Hermit %s Build %u, %u\n", HERMIT_VERSION, &__BUILD_DATE, &__BUILD_TIME); + kprintf("Kernel starts at %p and ends at %p\n", &kernel_start, &kernel_end); + kprintf("Processor frequency: %u MHz\n", get_cpu_frequency()); + kprintf("Total memory: %lu KiB\n", atomic_int32_read(&total_pages) * PAGE_SIZE / 1024); + kprintf("Current allocated memory: %lu KiB\n", atomic_int32_read(&total_allocated_pages) * PAGE_SIZE / 1024); + kprintf("Current available memory: %lu KiB\n", atomic_int32_read(&total_available_pages) * PAGE_SIZE / 1024); + + while(1) { + HALT; + } + + return 0; +} diff --git a/hermit/kernel/syscall.c b/hermit/kernel/syscall.c new file mode 100644 index 000000000..41aa2f3da --- /dev/null +++ b/hermit/kernel/syscall.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +static int sys_write(int fd, const char* buf, size_t len) +{ + //TODO: Currently, we ignore the file descriptor + + if (BUILTIN_EXPECT(!buf, 0)) + return -1; + + kputs(buf); + + return 0; +} + +static ssize_t sys_sbrk(int incr) +{ + task_t* task = current_task; + vma_t* heap = task->heap; + ssize_t ret; + + spinlock_lock(&task->vma_lock); + + if (BUILTIN_EXPECT(!heap, 0)) { + kprintf("sys_sbrk: missing heap!\n"); + abort(); + } + + ret = heap->end; + heap->end += incr; + if (heap->end < heap->start) + heap->end = heap->start; + + // allocation and mapping of new pages for the heap + // is catched by the pagefault handler + + spinlock_unlock(&task->vma_lock); + + return ret; +} + +ssize_t syscall_handler(uint32_t sys_nr, ...) +{ + ssize_t ret = -EINVAL; + va_list vl; + + va_start(vl, sys_nr); + + switch(sys_nr) + { + case __NR_exit: + sys_exit(va_arg(vl, uint32_t)); + ret = 0; + break; + case __NR_write: { + int fd = va_arg(vl, int); + const char* buf = va_arg(vl, const char*); + size_t len = va_arg(vl, size_t); + ret = sys_write(fd, buf, len); + break; + } + //TODO: Currently, we ignore file descriptors + case __NR_open: + case __NR_close: + ret = 0; + break; + case __NR_sbrk: { + int incr = va_arg(vl, int); + + ret = sys_sbrk(incr); + break; + } + default: + kprintf("invalid system call: %u\n", sys_nr); + ret = -ENOSYS; + break; + }; + + va_end(vl); + + return ret; +} diff --git a/hermit/kernel/tasks.c b/hermit/kernel/tasks.c new file mode 100644 index 000000000..865e69177 --- /dev/null +++ b/hermit/kernel/tasks.c @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @brief Array of task structures (aka PCB) + * + * A task's id will be its position in this array. + */ +static task_t task_table[MAX_TASKS] = { \ + [0] = {0, TASK_IDLE, NULL, NULL, TASK_DEFAULT_FLAGS, 0, 0, SPINLOCK_IRQSAVE_INIT, SPINLOCK_INIT, NULL, NULL, ATOMIC_INIT(0), NULL, NULL}, \ + [1 ... MAX_TASKS-1] = {0, TASK_INVALID, NULL, NULL, TASK_DEFAULT_FLAGS, 0, 0, SPINLOCK_IRQSAVE_INIT, SPINLOCK_INIT, NULL, NULL,ATOMIC_INIT(0), NULL, NULL}}; + +static spinlock_irqsave_t table_lock = SPINLOCK_IRQSAVE_INIT; + +static readyqueues_t readyqueues = {task_table+0, NULL, 0, 0, {[0 ... MAX_PRIO-2] = {NULL, NULL}}, SPINLOCK_IRQSAVE_INIT}; + +task_t* current_task = task_table+0; +extern const void boot_stack; + +/** @brief helper function for the assembly code to determine the current task + * @return Pointer to the task_t structure of current task + */ +task_t* get_current_task(void) +{ + return current_task; +} + +uint32_t get_highest_priority(void) +{ + return msb(readyqueues.prio_bitmap); +} + +int multitasking_init(void) +{ + if (BUILTIN_EXPECT(task_table[0].status != TASK_IDLE, 0)) { + kputs("Task 0 is not an idle task\n"); + return -ENOMEM; + } + + task_table[0].prio = IDLE_PRIO; + task_table[0].stack = (void*) &boot_stack; + task_table[0].page_map = read_cr3(); + + // register idle task + register_task(); + + return 0; +} + +void finish_task_switch(void) +{ + task_t* old; + uint8_t prio; + + spinlock_irqsave_lock(&readyqueues.lock); + + if ((old = readyqueues.old_task) != NULL) { + if (old->status == TASK_INVALID) { + old->stack = NULL; + old->last_stack_pointer = NULL; + readyqueues.old_task = NULL; + } else { + prio = old->prio; + if (!readyqueues.queue[prio-1].first) { + old->next = old->prev = NULL; + readyqueues.queue[prio-1].first = readyqueues.queue[prio-1].last = old; + } else { + old->next = NULL; + old->prev = readyqueues.queue[prio-1].last; + readyqueues.queue[prio-1].last->next = old; + readyqueues.queue[prio-1].last = old; + } + readyqueues.old_task = NULL; + readyqueues.prio_bitmap |= (1 << prio); + } + } + + spinlock_irqsave_unlock(&readyqueues.lock); + + if (current_task->heap) + kfree(current_task->heap); +} + +/** @brief A procedure to be called by + * procedures which are called by exiting tasks. */ +static void NORETURN do_exit(int arg) +{ + task_t* curr_task = current_task; + + kprintf("Terminate task: %u, return value %d\n", curr_task->id, arg); + + page_map_drop(); + + // decrease the number of active tasks + spinlock_irqsave_lock(&readyqueues.lock); + readyqueues.nr_tasks--; + spinlock_irqsave_unlock(&readyqueues.lock); + + curr_task->status = TASK_FINISHED; + reschedule(); + + kprintf("Kernel panic: scheduler found no valid task\n"); + while(1) { + HALT; + } +} + +/** @brief A procedure to be called by kernel tasks */ +void NORETURN leave_kernel_task(void) { + int result; + + result = 0; //get_return_value(); + do_exit(result); +} + +/** @brief To be called by the systemcall to exit tasks */ +void NORETURN sys_exit(int arg) { + do_exit(arg); +} + +/** @brief Aborting a task is like exiting it with result -1 */ +void NORETURN abort(void) { + do_exit(-1); +} + +int create_task(tid_t* id, entry_point_t ep, void* arg, uint8_t prio) +{ + int ret = -ENOMEM; + uint32_t i; + + if (BUILTIN_EXPECT(!ep, 0)) + return -EINVAL; + if (BUILTIN_EXPECT(prio == IDLE_PRIO, 0)) + return -EINVAL; + if (BUILTIN_EXPECT(prio > MAX_PRIO, 0)) + return -EINVAL; + + spinlock_irqsave_lock(&table_lock); + + for(i=0; inext = task_table+i; + readyqueues.queue[prio-1].last = task_table+i; + } + spinlock_irqsave_unlock(&readyqueues.lock); + break; + } + } + +out: + spinlock_irqsave_unlock(&table_lock); + + return ret; +} + +int create_kernel_task(tid_t* id, entry_point_t ep, void* args, uint8_t prio) +{ + if (prio > MAX_PRIO) + prio = NORMAL_PRIO; + + return create_task(id, ep, args, prio); +} + +/** @brief Wakeup a blocked task + * @param id The task's tid_t structure + * @return + * - 0 on success + * - -EINVAL (-22) on failure + */ +int wakeup_task(tid_t id) +{ + task_t* task; + uint32_t prio; + int ret = -EINVAL; + uint8_t flags; + + flags = irq_nested_disable(); + + task = task_table + id; + prio = task->prio; + + if (task->status == TASK_BLOCKED) { + task->status = TASK_READY; + ret = 0; + + spinlock_irqsave_lock(&readyqueues.lock); + // increase the number of ready tasks + readyqueues.nr_tasks++; + + // add task to the runqueue + if (!readyqueues.queue[prio-1].last) { + readyqueues.queue[prio-1].last = readyqueues.queue[prio-1].first = task; + task->next = task->prev = NULL; + readyqueues.prio_bitmap |= (1 << prio); + } else { + task->prev = readyqueues.queue[prio-1].last; + task->next = NULL; + readyqueues.queue[prio-1].last->next = task; + readyqueues.queue[prio-1].last = task; + } + spinlock_irqsave_unlock(&readyqueues.lock); + } + + irq_nested_enable(flags); + + return ret; +} + +/** @brief Block current task + * + * The current task's status will be changed to TASK_BLOCKED + * + * @return + * - 0 on success + * - -EINVAL (-22) on failure + */ +int block_current_task(void) +{ + tid_t id; + uint32_t prio; + int ret = -EINVAL; + uint8_t flags; + + flags = irq_nested_disable(); + + id = current_task->id; + prio = current_task->prio; + + if (task_table[id].status == TASK_RUNNING) { + task_table[id].status = TASK_BLOCKED; + ret = 0; + + spinlock_irqsave_lock(&readyqueues.lock); + // reduce the number of ready tasks + readyqueues.nr_tasks--; + + // remove task from queue + if (task_table[id].prev) + task_table[id].prev->next = task_table[id].next; + if (task_table[id].next) + task_table[id].next->prev = task_table[id].prev; + if (readyqueues.queue[prio-1].first == task_table+id) + readyqueues.queue[prio-1].first = task_table[id].next; + if (readyqueues.queue[prio-1].last == task_table+id) { + readyqueues.queue[prio-1].last = task_table[id].prev; + if (!readyqueues.queue[prio-1].last) + readyqueues.queue[prio-1].last = readyqueues.queue[prio-1].first; + } + + // No valid task in queue => update prio_bitmap + if (!readyqueues.queue[prio-1].first) + readyqueues.prio_bitmap &= ~(1 << prio); + spinlock_irqsave_unlock(&readyqueues.lock); + } + + irq_nested_enable(flags); + + return ret; +} + +size_t** scheduler(void) +{ + task_t* orig_task; + uint32_t prio; + + orig_task = current_task; + + spinlock_irqsave_lock(&readyqueues.lock); + + /* signalizes that this task could be reused */ + if (current_task->status == TASK_FINISHED) { + current_task->status = TASK_INVALID; + readyqueues.old_task = current_task; + } else readyqueues.old_task = NULL; // reset old task + + prio = msb(readyqueues.prio_bitmap); // determines highest priority + if (prio > MAX_PRIO) { + if ((current_task->status == TASK_RUNNING) || (current_task->status == TASK_IDLE)) + goto get_task_out; + current_task = readyqueues.idle; + } else { + // Does the current task have an higher priority? => no task switch + if ((current_task->prio > prio) && (current_task->status == TASK_RUNNING)) + goto get_task_out; + + if (current_task->status == TASK_RUNNING) { + current_task->status = TASK_READY; + readyqueues.old_task = current_task; + } + + current_task = readyqueues.queue[prio-1].first; + if (BUILTIN_EXPECT(current_task->status == TASK_INVALID, 0)) { + kprintf("Upps!!!!!!! Got invalid task %d, orig task %d\n", current_task->id, orig_task->id); + } + current_task->status = TASK_RUNNING; + + // remove new task from queue + // by the way, priority 0 is only used by the idle task and doesn't need own queue + readyqueues.queue[prio-1].first = current_task->next; + if (!current_task->next) { + readyqueues.queue[prio-1].last = NULL; + readyqueues.prio_bitmap &= ~(1 << prio); + } + current_task->next = current_task->prev = NULL; + } + +get_task_out: + spinlock_irqsave_unlock(&readyqueues.lock); + + if (current_task != orig_task) { + /* if the original task is using the FPU, we need to save the FPU context */ + if ((orig_task->flags & TASK_FPU_USED) && (orig_task->status == TASK_READY)) { + save_fpu_state(&(orig_task->fpu)); + orig_task->flags &= ~TASK_FPU_USED; + } + + kprintf("schedule from %u to %u with prio %u\n", orig_task->id, current_task->id, (uint32_t)current_task->prio); + + return (size_t**) &(orig_task->last_stack_pointer); + } + + return NULL; +} + +void reschedule(void) +{ + size_t** stack; + uint8_t flags; + + flags = irq_nested_disable(); + if ((stack = scheduler())) + switch_context(stack); + irq_nested_enable(flags); +} diff --git a/hermit/libkern/Makefile b/hermit/libkern/Makefile new file mode 100644 index 000000000..3e29d4408 --- /dev/null +++ b/hermit/libkern/Makefile @@ -0,0 +1,4 @@ +C_source := string.c stdio.c printf.c sprintf.c strtol.c strtoul.c strstr.c +MODULE := libkern + +include $(TOPDIR)/Makefile.inc diff --git a/hermit/libkern/printf.c b/hermit/libkern/printf.c new file mode 100644 index 000000000..2284666fb --- /dev/null +++ b/hermit/libkern/printf.c @@ -0,0 +1,485 @@ +/*- + * Copyright (c) 1986, 1988, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)subr_prf.c 8.3 (Berkeley) 1/21/94 + */ + + +/* + * HermitCore's printf implementation is based on a implementation which was + * published at http://www.pagetable.com/?p=298. + * The authors built a full-featured standalone version of printf(). The + * base code has been taken from FreeBSD (sys/kern/subr_prf.c) and is + * consequently BSD-licensed. Unnecessary functions have been removed and + * all typedefs required have been added. + */ + +#include + +#define __64BIT__ + +#ifdef __64BIT__ +typedef unsigned long long uintmax_t; +typedef long long intmax_t; +#else +typedef unsigned int uintmax_t; +typedef int intmax_t; +#endif +typedef unsigned char u_char; +typedef unsigned int u_int; +typedef unsigned long u_long; +typedef unsigned short u_short; +typedef unsigned long long u_quad_t; +typedef long long quad_t; +typedef unsigned long uintptr_t; +#define NBBY 8 /* number of bits in a byte */ +char const hex2ascii_data[] = "0123456789abcdefghijklmnopqrstuvwxyz"; +#define hex2ascii(hex) (hex2ascii_data[hex]) +#define va_list __builtin_va_list +#define va_start __builtin_va_start +#define va_arg __builtin_va_arg +#define va_end __builtin_va_end +#define toupper(c) ((c) - 0x20 * (((c) >= 'a') && ((c) <= 'z'))) + +/* Max number conversion buffer length: a u_quad_t in base 2, plus NUL byte. */ +#define MAXNBUF (sizeof(intmax_t) * NBBY + 1) + +/* + * Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse + * order; return an optional length and a pointer to the last character + * written in the buffer (i.e., the first character of the string). + * The buffer pointed to by `nbuf' must have length >= MAXNBUF. + */ +static char *ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper) +{ + char *p, c; + + p = nbuf; + *p = '\0'; + do { + c = hex2ascii(num % base); + *++p = upper ? toupper(c) : c; + } while (num /= base); + if (lenp) + *lenp = p - nbuf; + return (p); +} + +/* + * Scaled down version of printf(3). + * + * Two additional formats: + * + * The format %b is supported to decode error registers. + * Its usage is: + * + * printf("reg=%b\n", regval, "*"); + * + * where the output base is expressed as a control character, e.g. + * \10 gives octal; \20 gives hex. Each arg is a sequence of characters, + * the first of which gives the bit number to be inspected (origin 1), and + * the next characters (up to a control character, i.e. a character <= 32), + * give the name of the register. Thus: + * + * kvprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n"); + * + * would produce output: + * + * reg=3 + * + * XXX: %D -- Hexdump, takes pointer and separator string: + * ("%6D", ptr, ":") -> XX:XX:XX:XX:XX:XX + * ("%*D", len, ptr, " " -> XX XX XX XX ... + */ +int kvprintf(char const *fmt, void (*func) (int, void *), void *arg, int radix, + va_list ap) +{ +#define PCHAR(c) {int cc=(c); if (func) (*func)(cc,arg); else *d++ = cc; retval++; } + char nbuf[MAXNBUF]; + char *d; + const char *p, *percent, *q; + u_char *up; + int ch, n; + uintmax_t num; + int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot; + int cflag, hflag, jflag, tflag, zflag; + int dwidth, upper; + char padc; + int stop = 0, retval = 0; + + num = 0; + if (!func) + d = (char *)arg; + else + d = NULL; + + if (fmt == NULL) + fmt = "(fmt null)\n"; + + if (radix < 2 || radix > 36) + radix = 10; + + for (;;) { + padc = ' '; + width = 0; + while ((ch = (u_char) * fmt++) != '%' || stop) { + if (ch == '\0') + return (retval); + PCHAR(ch); + } + percent = fmt - 1; + qflag = 0; + lflag = 0; + ladjust = 0; + sharpflag = 0; + neg = 0; + sign = 0; + dot = 0; + dwidth = 0; + upper = 0; + cflag = 0; + hflag = 0; + jflag = 0; + tflag = 0; + zflag = 0; + reswitch: switch (ch = (u_char) * fmt++) { + case '.': + dot = 1; + goto reswitch; + case '#': + sharpflag = 1; + goto reswitch; + case '+': + sign = 1; + goto reswitch; + case '-': + ladjust = 1; + goto reswitch; + case '%': + PCHAR(ch); + break; + case '*': + if (!dot) { + width = va_arg(ap, int); + if (width < 0) { + ladjust = !ladjust; + width = -width; + } + } else { + dwidth = va_arg(ap, int); + } + goto reswitch; + case '0': + if (!dot) { + padc = '0'; + goto reswitch; + } + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + for (n = 0;; ++fmt) { + n = n * 10 + ch - '0'; + ch = *fmt; + if (ch < '0' || ch > '9') + break; + } + if (dot) + dwidth = n; + else + width = n; + goto reswitch; + case 'b': + num = (u_int) va_arg(ap, int); + p = va_arg(ap, char *); + for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;) + PCHAR(*q--); + + if (num == 0) + break; + + for (tmp = 0; *p;) { + n = *p++; + if (num & (1 << (n - 1))) { + PCHAR(tmp ? ',' : '<'); + for (; (n = *p) > ' '; ++p) + PCHAR(n); + tmp = 1; + } else + for (; *p > ' '; ++p) + continue; + } + if (tmp) + PCHAR('>'); + break; + case 'c': + PCHAR(va_arg(ap, int)); + break; + case 'D': + up = va_arg(ap, u_char *); + p = va_arg(ap, char *); + if (!width) + width = 16; + while (width--) { + PCHAR(hex2ascii(*up >> 4)); + PCHAR(hex2ascii(*up & 0x0f)); + up++; + if (width) + for (q = p; *q; q++) + PCHAR(*q); + } + break; + case 'd': + case 'i': + base = 10; + sign = 1; + goto handle_sign; + case 'h': + if (hflag) { + hflag = 0; + cflag = 1; + } else + hflag = 1; + goto reswitch; + case 'j': + jflag = 1; + goto reswitch; + case 'l': + if (lflag) { + lflag = 0; + qflag = 1; + } else + lflag = 1; + goto reswitch; + case 'n': + if (jflag) + *(va_arg(ap, intmax_t *)) = retval; + else if (qflag) + *(va_arg(ap, quad_t *)) = retval; + else if (lflag) + *(va_arg(ap, long *)) = retval; + else if (zflag) + *(va_arg(ap, size_t *)) = retval; + else if (hflag) + *(va_arg(ap, short *)) = retval; + else if (cflag) + *(va_arg(ap, char *)) = retval; + else + *(va_arg(ap, int *)) = retval; + break; + case 'o': + base = 8; + goto handle_nosign; + case 'p': + base = 16; + sharpflag = (width == 0); + sign = 0; + num = (uintptr_t) va_arg(ap, void *); + goto number; + case 'q': + qflag = 1; + goto reswitch; + case 'r': + base = radix; + if (sign) + goto handle_sign; + goto handle_nosign; + case 's': + p = va_arg(ap, char *); + if (p == NULL) + p = "(null)"; + if (!dot) + n = strlen(p); + else + for (n = 0; n < dwidth && p[n]; n++) + continue; + + width -= n; + + if (!ladjust && width > 0) + while (width--) + PCHAR(padc); + while (n--) + PCHAR(*p++); + if (ladjust && width > 0) + while (width--) + PCHAR(padc); + break; + case 't': + tflag = 1; + goto reswitch; + case 'u': + base = 10; + goto handle_nosign; + case 'X': + upper = 1; + case 'x': + base = 16; + goto handle_nosign; + case 'y': + base = 16; + sign = 1; + goto handle_sign; + case 'z': + zflag = 1; + goto reswitch; + handle_nosign: + sign = 0; + if (jflag) + num = va_arg(ap, uintmax_t); + else if (qflag) + num = va_arg(ap, u_quad_t); + else if (tflag) + num = va_arg(ap, ptrdiff_t); + else if (lflag) + num = va_arg(ap, u_long); + else if (zflag) + num = va_arg(ap, size_t); + else if (hflag) + num = (u_short) va_arg(ap, int); + else if (cflag) + num = (u_char) va_arg(ap, int); + else + num = va_arg(ap, u_int); + goto number; + handle_sign: + if (jflag) + num = va_arg(ap, intmax_t); + else if (qflag) + num = va_arg(ap, quad_t); + else if (tflag) + num = va_arg(ap, ptrdiff_t); + else if (lflag) + num = va_arg(ap, long); + else if (zflag) + num = va_arg(ap, ssize_t); + else if (hflag) + num = (short)va_arg(ap, int); + else if (cflag) + num = (char)va_arg(ap, int); + else + num = va_arg(ap, int); + number: + if (sign && (intmax_t) num < 0) { + neg = 1; + num = -(intmax_t) num; + } + p = ksprintn(nbuf, num, base, &tmp, upper); + if (sharpflag && num != 0) { + if (base == 8) + tmp++; + else if (base == 16) + tmp += 2; + } + if (neg) + tmp++; + + if (!ladjust && padc != '0' && width + && (width -= tmp) > 0) + while (width--) + PCHAR(padc); + if (neg) + PCHAR('-'); + if (sharpflag && num != 0) { + if (base == 8) { + PCHAR('0'); + } else if (base == 16) { + PCHAR('0'); + PCHAR('x'); + } + } + if (!ladjust && width && (width -= tmp) > 0) + while (width--) + PCHAR(padc); + + while (*p) + PCHAR(*p--); + + if (ladjust && width && (width -= tmp) > 0) + while (width--) + PCHAR(padc); + + break; + default: + while (percent < fmt) + PCHAR(*percent++); + /* + * Since we ignore a formatting argument it is no + * longer safe to obey the remaining formatting + * arguments as the arguments will no longer match + * the format specs. + */ + stop = 1; + break; + } + } +#undef PCHAR +} + +/* + * Print directly a character on the screen + */ +extern int kputchar(int); + +/* + * A wrapper function for kputchar because + * kvprintf needs an output function, which possesses two arguments. + * The first arguments defines the output character, the second could be used to pass + * additional arguments. In the case of kputchar is no additional argument needed. + */ +static void _putchar(int c, void *arg) +{ + kputchar(c); +} + +int kprintf(const char *fmt, ...) +{ + int ret; + + /* http://www.pagetable.com/?p=298 */ + va_list ap; + + va_start(ap, fmt); + ret = kvprintf(fmt, + _putchar, /* output function */ + NULL, /* additional argument for the output function */ + 10, ap); + va_end(ap); + + return ret; +} diff --git a/hermit/libkern/sprintf.c b/hermit/libkern/sprintf.c new file mode 100644 index 000000000..40ceb76d9 --- /dev/null +++ b/hermit/libkern/sprintf.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +typedef struct { + char *str; + size_t pos; + size_t max; +} sputchar_arg_t; + +static void sputchar(int c, void *arg) +{ + sputchar_arg_t *dest = (sputchar_arg_t *) arg; + + if (dest->pos < dest->max) { + dest->str[dest->pos] = (char)c; + dest->pos++; + } +} + +int ksnprintf(char *str, size_t size, const char *format, ...) +{ + int ret; + va_list ap; + sputchar_arg_t dest; + + dest.str = str; + dest.pos = 0; + dest.max = size; + + va_start(ap, format); + ret = kvprintf(format, sputchar, &dest, 10, ap); + va_end(ap); + + str[ret] = 0; + + return ret; +} + +int ksprintf(char *str, const char *format, ...) +{ + int ret; + va_list ap; + sputchar_arg_t dest; + + dest.str = str; + dest.pos = 0; + dest.max = (size_t) -1; + + va_start(ap, format); + ret = kvprintf(format, sputchar, &dest, 10, ap); + va_end(ap); + + str[ret] = 0; + + return ret; +} diff --git a/hermit/libkern/stdio.c b/hermit/libkern/stdio.c new file mode 100644 index 000000000..2626b68c0 --- /dev/null +++ b/hermit/libkern/stdio.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_VGA +#include +#endif + +#define NO_EARLY_PRINT 0x00 +#define VGA_EARLY_PRINT 0x01 + +#ifdef CONFIG_VGA +static uint32_t early_print = VGA_EARLY_PRINT; +#else +static uint32_t early_print = NO_EARLY_PRINT; +#endif +static spinlock_irqsave_t olock = SPINLOCK_IRQSAVE_INIT; +static atomic_int32_t kmsg_counter = ATOMIC_INIT(0); +static unsigned char kmessages[KMSG_SIZE] __attribute__ ((section(".kmsg"))) = {[0 ... KMSG_SIZE-1] = 0x00}; + +int koutput_init(void) +{ +#ifdef CONFIG_VGA + vga_init(); +#endif + + return 0; +} + +int kputchar(int c) +{ + int pos; + + if (early_print != NO_EARLY_PRINT) + spinlock_irqsave_lock(&olock); + + pos = atomic_int32_inc(&kmsg_counter); + kmessages[pos % KMSG_SIZE] = (unsigned char) c; + +#ifdef CONFIG_VGA + if (early_print & VGA_EARLY_PRINT) + vga_putchar(c); +#endif + + if (early_print != NO_EARLY_PRINT) + spinlock_irqsave_unlock(&olock); + + return 1; +} + +int kputs(const char *str) +{ + int pos, i, len = strlen(str); + + if (early_print != NO_EARLY_PRINT) + spinlock_irqsave_lock(&olock); + + for(i=0; i + +#ifndef HAVE_ARCH_MEMCPY +void *memcpy(void *dest, const void *src, size_t count) +{ + size_t i; + + if (BUILTIN_EXPECT(!dest || !src, 0)) + return dest; + + for (i = 0; i < count; i++) + ((char*)dest)[i] = ((char*)src)[i]; + + return dest; +} +#endif + +#ifndef HAVE_ARCH_MEMSET +void *memset(void *dest, int val, size_t count) +{ + size_t i; + + if (BUILTIN_EXPECT(!dest, 0)) + return dest; + + for (i = 0; i < count; i++) + ((char*) dest)[i] = (char) val; + + return dest; +} +#endif + +#ifndef HAVE_ARCH_STRLEN +size_t strlen(const char *str) +{ + size_t len = 0; + + if (BUILTIN_EXPECT(!str, 0)) + return len; + + while (str[len] != '\0') + len++; + + return len; +} +#endif + +#ifndef HAVE_ARCH_STRNCPY +char* strncpy(char *dest, const char *src, size_t n) +{ + size_t i; + + if (BUILTIN_EXPECT(!dest || !src, 0)) + return dest; + + for (i = 0 ; i < n && src[i] != '\0' ; i++) + dest[i] = src[i]; + if (i < n) + dest[i] = '\0'; + else + dest[n-1] = '\0'; + + return dest; +} +#endif + +#ifndef HAVE_ARCH_STRCPY +char* strcpy(char *dest, const char *src) +{ + size_t i; + + if (BUILTIN_EXPECT(!dest || !src, 0)) + return dest; + + for (i = 0 ; src[i] != '\0' ; i++) + dest[i] = src[i]; + dest[i] = '\0'; + + return dest; +} +#endif + +#ifndef HAVE_ARCH_STRCMP +int strcmp(const char *s1, const char *s2) +{ + while (*s1 != '\0' && *s1 == *s2) { + s1++; + s2++; + } + + return (*(unsigned char *) s1) - (*(unsigned char *) s2); +} +#endif + +#ifndef HAVE_ARCH_STRNCMP +int strncmp(const char *s1, const char *s2, size_t n) +{ + if (BUILTIN_EXPECT(n == 0, 0)) + return 0; + + while (n-- != 0 && *s1 == *s2) { + if (n == 0 || *s1 == '\0') + break; + s1++; + s2++; + } + + return (*(unsigned char *) s1) - (*(unsigned char *) s2); +} +#endif diff --git a/hermit/libkern/strstr.c b/hermit/libkern/strstr.c new file mode 100644 index 000000000..fb313d529 --- /dev/null +++ b/hermit/libkern/strstr.c @@ -0,0 +1,73 @@ +/* $NetBSD: strstr.c,v 1.1 2005/12/20 19:28:52 christos Exp $ */ + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * The code has been taken from NetBSD (sys/libkern/strstr.c) and is consequently + * BSD-licensed. Unnecessary functions have been removed and all typedefs required + * have been added. + */ + +/* HermiCore prelude */ +#include +#include +#include +#include + +/* + * Find the first occurrence of find in s. + */ +char * +strstr(s, find) + const char *s, *find; +{ + char c, sc; + size_t len; + + if (BUILTIN_EXPECT(!s, 0)) + return NULL; + if (BUILTIN_EXPECT(!find, 0)) + return NULL; + + if ((c = *find++) != 0) { + len = strlen(find); + do { + do { + if ((sc = *s++) == 0) + return (NULL); + } while (sc != c); + } while (strncmp(s, find, len) != 0); + s--; + } + return ((char *) s); +} diff --git a/hermit/libkern/strtol.c b/hermit/libkern/strtol.c new file mode 100644 index 000000000..bf31f3162 --- /dev/null +++ b/hermit/libkern/strtol.c @@ -0,0 +1,133 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * From: @(#)strtol.c 8.1 (Berkeley) 6/4/93 + */ + +/* + * The code has been taken from FreeBSD (sys/libkern/strtol.c) and is consequently + * BSD-licensed. Unnecessary functions have been removed and all typedefs required + * have been added. + */ + +/* HermitCore prelude */ +#include +#include +#include + +/* + * Convert a string to a long integer. + * + * Ignores `locale' stuff. Assumes that the upper and lower case + * alphabets and digits are each contiguous. + */ +long +strtol(nptr, endptr, base) + const char *nptr; + char **endptr; + int base; +{ + const char *s = nptr; + unsigned long acc; + unsigned char c; + unsigned long cutoff; + int neg = 0, any, cutlim; + + /* + * Skip white space and pick up leading +/- sign if any. + * If base is 0, allow 0x for hex and 0 for octal, else + * assume decimal; if base is already 16, allow 0x. + */ + do { + c = *s++; + } while (isspace(c)); + if (c == '-') { + neg = 1; + c = *s++; + } else if (c == '+') + c = *s++; + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + + /* + * Compute the cutoff value between legal numbers and illegal + * numbers. That is the largest legal value, divided by the + * base. An input number that is greater than this value, if + * followed by a legal input character, is too big. One that + * is equal to this value may be valid or not; the limit + * between valid and invalid numbers is then based on the last + * digit. For instance, if the range for longs is + * [-2147483648..2147483647] and the input base is 10, + * cutoff will be set to 214748364 and cutlim to either + * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated + * a value > 214748364, or equal but the next digit is > 7 (or 8), + * the number is too big, and we will return a range error. + * + * Set any if any `digits' consumed; make it negative to indicate + * overflow. + */ + cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX; + cutlim = cutoff % (unsigned long)base; + cutoff /= (unsigned long)base; + for (acc = 0, any = 0;; c = *s++) { + if (!isascii(c)) + break; + if (isdigit(c)) + c -= '0'; + else if (isalpha(c)) + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + any = -1; + else { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) { + acc = neg ? LONG_MIN : LONG_MAX; + } else if (neg) + acc = -acc; + if (endptr != 0) + *((const char **)endptr) = any ? s - 1 : nptr; + return (acc); +} + diff --git a/hermit/libkern/strtoul.c b/hermit/libkern/strtoul.c new file mode 100644 index 000000000..e0752f803 --- /dev/null +++ b/hermit/libkern/strtoul.c @@ -0,0 +1,112 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * From: @(#)strtoul.c 8.1 (Berkeley) 6/4/93 + */ + +/* + * The code has been taken from FreeBSD (sys/libkern/strtoul.c) and is consequently + * BSD-licensed. Unnecessary functions have been removed and all typedefs required + * have been added. + */ + +/* HermitCore prelude */ +#include +#include +#include + +/* + * Convert a string to an unsigned long integer. + * + * Ignores `locale' stuff. Assumes that the upper and lower case + * alphabets and digits are each contiguous. + */ +unsigned long +strtoul(nptr, endptr, base) + const char *nptr; + char **endptr; + int base; +{ + const char *s = nptr; + unsigned long acc; + unsigned char c; + unsigned long cutoff; + int neg = 0, any, cutlim; + + /* + * See strtol for comments as to the logic used. + */ + do { + c = *s++; + } while (isspace(c)); + if (c == '-') { + neg = 1; + c = *s++; + } else if (c == '+') + c = *s++; + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + cutoff = (unsigned long)ULONG_MAX / (unsigned long)base; + cutlim = (unsigned long)ULONG_MAX % (unsigned long)base; + for (acc = 0, any = 0;; c = *s++) { + if (!isascii(c)) + break; + if (isdigit(c)) + c -= '0'; + else if (isalpha(c)) + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + any = -1; + else { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) { + acc = ULONG_MAX; + } else if (neg) + acc = -acc; + if (endptr != 0) + *((const char **)endptr) = any ? s - 1 : nptr; + return (acc); +} + diff --git a/hermit/link.ld b/hermit/link.ld new file mode 100644 index 000000000..0c4efc179 --- /dev/null +++ b/hermit/link.ld @@ -0,0 +1,30 @@ +OUTPUT_FORMAT("elf64-x86-64") +OUTPUT_ARCH("i386:x86-64") +ENTRY(start) +phys = 0x1400000; + +SECTIONS +{ + kernel_start = phys; + .mboot phys : AT(ADDR(.mboot)) { + *(.mboot) + . = ALIGN((1 << 12)); + *(.kmsg) + } + .text ALIGN(4096) : AT(ADDR(.text)) { + *(.text) + } + .rodata ALIGN(4096) : AT(ADDR(.rodata)) { + *(.rodata) + *(.rodata.*) + } + .data ALIGN(4096) : AT(ADDR(.data)) { + *(.data) + } + .bss ALIGN(4096) : AT(ADDR(.bss)) { + bss_start = .; + *(.bss) + } + bss_end = .; + kernel_end = .; +} diff --git a/hermit/mm/Makefile b/hermit/mm/Makefile new file mode 100644 index 000000000..6c2a4d83e --- /dev/null +++ b/hermit/mm/Makefile @@ -0,0 +1,4 @@ +C_source := memory.c malloc.c vma.c +MODULE := mm + +include $(TOPDIR)/Makefile.inc diff --git a/hermit/mm/malloc.c b/hermit/mm/malloc.c new file mode 100644 index 000000000..f9d658b49 --- /dev/null +++ b/hermit/mm/malloc.c @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2014, Steffen Vogel, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Steffen Vogel + */ + +#include +#include +#include +#include +#include + +/// A linked list for each binary size exponent +static buddy_t* buddy_lists[BUDDY_LISTS] = { [0 ... BUDDY_LISTS-1] = NULL }; +/// Lock for the buddy lists +static spinlock_t buddy_lock = SPINLOCK_INIT; + +/** @brief Check if larger free buddies are available */ +static inline int buddy_large_avail(uint8_t exp) +{ + while (exp(1< BUDDY_MAX) + exp = 0; + if (exp < BUDDY_MIN) + exp = BUDDY_MIN; + + return exp; +} + +/** @brief Get a free buddy by potentially splitting a larger one */ +static buddy_t* buddy_get(int exp) +{ + spinlock_lock(&buddy_lock); + buddy_t** list = &buddy_lists[exp-BUDDY_MIN]; + buddy_t* buddy = *list; + buddy_t* split; + + if (buddy) + // there is already a free buddy => + // we remove it from the list + *list = buddy->next; + else if (exp >= BUDDY_ALLOC && !buddy_large_avail(exp)) + // theres no free buddy larger than exp => + // we can allocate new memory + buddy = (buddy_t*) palloc(1<next = *list; + *list = split; + } + +out: + spinlock_unlock(&buddy_lock); + + return buddy; +} + +/** @brief Put a buddy back to its free list + * + * TODO: merge adjacent buddies (memory compaction) + */ +static void buddy_put(buddy_t* buddy) +{ + spinlock_lock(&buddy_lock); + buddy_t** list = &buddy_lists[buddy->prefix.exponent-BUDDY_MIN]; + buddy->next = *list; + *list = buddy; + spinlock_unlock(&buddy_lock); +} + +void buddy_dump(void) +{ + size_t free = 0; + int i; + for (i=0; inext) { + kprintf(" %p -> %p \n", buddy, buddy->next); + free += 1<> PAGE_BITS; + int err; + + //kprintf("palloc(%lu) (%lu pages)\n", sz, npages); + + // get free virtual address space + viraddr = vma_alloc(npages*PAGE_SIZE, VMA_HEAP); + if (BUILTIN_EXPECT(!viraddr, 0)) + return NULL; + + // get continous physical pages + phyaddr = get_pages(npages); + if (BUILTIN_EXPECT(!phyaddr, 0)) { + vma_free(viraddr, viraddr+npages*PAGE_SIZE); + return NULL; + } + + // map physical pages to VMA + err = page_map(viraddr, phyaddr, npages, PG_RW|PG_GLOBAL); + if (BUILTIN_EXPECT(err, 0)) { + vma_free(viraddr, viraddr+npages*PAGE_SIZE); + put_pages(phyaddr, npages); + return NULL; + } + + return (void*) viraddr; +} + +void pfree(void* addr, size_t sz) +{ + if (BUILTIN_EXPECT(!addr || !sz, 0)) + return; + + size_t i; + size_t phyaddr; + size_t viraddr = (size_t) addr & PAGE_MASK; + uint32_t npages = PAGE_FLOOR(sz) >> PAGE_BITS; + + // memory is probably not continuously mapped! (userspace heap) + for (i=0; iprefix.magic = BUDDY_MAGIC; + buddy->prefix.exponent = exp; + + //kprintf("kmalloc(%lu) = %p\n", sz, buddy+1); + + // pointer arithmetic: we hide the prefix + return buddy+1; +} + +void kfree(void *addr) +{ + if (BUILTIN_EXPECT(!addr, 0)) + return; + + //kprintf("kfree(%lu)\n", addr); + + buddy_t* buddy = (buddy_t*) addr - 1; // get prefix + + // check magic + if (BUILTIN_EXPECT(buddy->prefix.magic != BUDDY_MAGIC, 0)) + return; + + buddy_put(buddy); +} diff --git a/hermit/mm/memory.c b/hermit/mm/memory.c new file mode 100644 index 000000000..5482c83ca --- /dev/null +++ b/hermit/mm/memory.c @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +extern size_t base; +extern size_t limit; + +/* + * Note that linker symbols are not variables, they have no memory allocated for + * maintaining a value, rather their address is their value. + */ +extern const void kernel_start; +extern const void kernel_end; + +static char stack[MAX_TASKS-1][KERNEL_STACK_SIZE]; +static char bitmap[BITMAP_SIZE]; + +static spinlock_t bitmap_lock = SPINLOCK_INIT; + +atomic_int32_t total_pages = ATOMIC_INIT(0); +atomic_int32_t total_allocated_pages = ATOMIC_INIT(0); +atomic_int32_t total_available_pages = ATOMIC_INIT(0); + +void* create_stack(tid_t id) +{ + // idle task uses stack, which is defined in entry.asm + if (BUILTIN_EXPECT(!id, 0)) + return NULL; + // do we have a valid task id? + if (BUILTIN_EXPECT(id >= MAX_TASKS, 0)) + return NULL; + + return (void*) stack[id-1]; +} + +inline static int page_marked(size_t i) +{ + size_t index = i >> 3; + size_t mod = i & 0x7; + + return (bitmap[index] & (1 << mod)); +} + +inline static void page_set_mark(size_t i) +{ + size_t index = i >> 3; + size_t mod = i & 0x7; + + bitmap[index] = bitmap[index] | (1 << mod); +} + +inline static void page_clear_mark(size_t i) +{ + size_t index = i / 8; + size_t mod = i % 8; + + bitmap[index] = bitmap[index] & ~(1 << mod); +} + +size_t get_pages(size_t npages) +{ + size_t cnt, off; + static size_t alloc_start = (size_t) -1; + + if (BUILTIN_EXPECT(!npages, 0)) + return 0; + if (BUILTIN_EXPECT(npages > atomic_int32_read(&total_available_pages), 0)) + return 0; + + spinlock_lock(&bitmap_lock); + + if (alloc_start == (size_t)-1) + alloc_start = ((size_t) &kernel_end >> PAGE_BITS); + off = 1; + while (off <= BITMAP_SIZE*8 - npages) { + for (cnt=0; cnt> PAGE_BITS; + + if (BUILTIN_EXPECT(!phyaddr, 0)) + return -EINVAL; + if (BUILTIN_EXPECT(!npages, 0)) + return -EINVAL; + + spinlock_lock(&bitmap_lock); + + for (i=0; iflags & MULTIBOOT_INFO_MEM_MAP) { + size_t end_addr; + multiboot_memory_map_t* mmap = (multiboot_memory_map_t*) ((size_t) mb_info->mmap_addr); + multiboot_memory_map_t* mmap_end = (void*) ((size_t) mb_info->mmap_addr + mb_info->mmap_length); + + // mark available memory as free + while (mmap < mmap_end) { + if (mmap->type == MULTIBOOT_MEMORY_AVAILABLE) { + /* set the available memory as "unused" */ + addr = mmap->addr; + end_addr = addr + mmap->len; + + while ((addr < end_addr) && (addr < (BITMAP_SIZE*8*PAGE_SIZE))) { + if (page_marked(addr >> PAGE_BITS)) { + page_clear_mark(addr >> PAGE_BITS); + atomic_int32_inc(&total_pages); + atomic_int32_inc(&total_available_pages); + } + addr += PAGE_SIZE; + } + } + mmap = (multiboot_memory_map_t*) ((size_t) mmap + sizeof(uint32_t) + mmap->size); + } + } else if (mb_info->flags & MULTIBOOT_INFO_MEM) { + size_t page; + size_t pages_lower = mb_info->mem_lower >> 2; /* KiB to page number */ + size_t pages_upper = mb_info->mem_upper >> 2; + + for (page=0; page BITMAP_SIZE*8-256) + pages_upper = BITMAP_SIZE*8-256; + + for (page=0; page> PAGE_BITS); + atomic_int32_inc(&total_allocated_pages); + atomic_int32_dec(&total_available_pages); + + + if (mb_info->flags & MULTIBOOT_INFO_MODS) { + // mark modules list as used + for(addr=mb_info->mods_addr; addrmods_addr+mb_info->mods_count*sizeof(multiboot_module_t); addr+=PAGE_SIZE) { + page_set_mark(addr >> PAGE_BITS); + atomic_int32_inc(&total_allocated_pages); + atomic_int32_dec(&total_available_pages); + } + + // mark modules as used + multiboot_module_t* mmodule = (multiboot_module_t*) ((size_t) mb_info->mods_addr); + for(i=0; imods_count; i++) { + for(addr=mmodule[i].mod_start; addr> PAGE_BITS); + atomic_int32_inc(&total_allocated_pages); + atomic_int32_dec(&total_available_pages); + } + } + } + } else { + // mark available memory as free + for(addr=base; addr> PAGE_BITS)) { + page_clear_mark(addr >> PAGE_BITS); + atomic_int32_inc(&total_pages); + atomic_int32_inc(&total_available_pages); + } + } + } + + // mark kernel as used, we use 2MB pages to map the kernel + for(addr=(size_t) &kernel_start; addr<(((size_t) &kernel_end + 0x200000ULL) & 0xFFFFFFFFFFE00000ULL); addr+=PAGE_SIZE) { + page_set_mark(addr >> PAGE_BITS); + atomic_int32_inc(&total_allocated_pages); + atomic_int32_dec(&total_available_pages); + } + + ret = vma_init(); + if (BUILTIN_EXPECT(ret, 0)) { + kprintf("Failed to initialize VMA regions: %d\n", ret); + return ret; + } + + /* + * Modules like the init ram disk are already loaded. + * Therefore, we set these pages as used. + */ + if (mb_info && (mb_info->flags & MULTIBOOT_INFO_MODS)) { + multiboot_module_t* mmodule = (multiboot_module_t*) ((size_t) mb_info->mods_addr); + for(i=0; imods_count; i++) { + for(addr=mmodule[i].mod_start; addr> PAGE_BITS); + atomic_int32_inc(&total_allocated_pages); + atomic_int32_dec(&total_available_pages); + } + } + } + + return ret; +} diff --git a/hermit/mm/vma.c b/hermit/mm/vma.c new file mode 100644 index 000000000..8a555b016 --- /dev/null +++ b/hermit/mm/vma.c @@ -0,0 +1,400 @@ +/* + * Copyright (c) 2014, Steffen Vogel, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Note that linker symbols are not variables, they have no memory allocated for + * maintaining a value, rather their address is their value. + */ +extern const void kernel_start; +extern const void kernel_end; + +/* + * Kernel space VMA list and lock + * + * For bootstrapping we initialize the VMA list with one empty VMA + * (start == end) and expand this VMA by calls to vma_alloc() + */ +static vma_t vma_boot = { VMA_KERN_MIN, VMA_KERN_MIN, VMA_HEAP }; +static vma_t* vma_list = &vma_boot; +static spinlock_t vma_lock = SPINLOCK_INIT; + +// TODO: we might move the architecture specific VMA regions to a +// seperate function arch_vma_init() +int vma_init(void) +{ + int ret; + + // add Kernel + ret = vma_add(PAGE_CEIL((size_t) &kernel_start), + (((size_t) &kernel_end + 0x200000ULL) & 0xFFFFFFFFFFE00000ULL), /* we use 2MB pages to map the kernel */ + VMA_READ|VMA_WRITE|VMA_EXECUTE|VMA_CACHEABLE); + if (BUILTIN_EXPECT(ret, 0)) + goto out; + +#ifdef CONFIG_VGA + // add VGA video memory + ret = vma_add(VIDEO_MEM_ADDR, VIDEO_MEM_ADDR + PAGE_SIZE, VMA_READ|VMA_WRITE); + if (BUILTIN_EXPECT(ret, 0)) + goto out; +#endif + + // add Multiboot structures as modules + if (mb_info) { + ret = vma_add(PAGE_CEIL((size_t) mb_info), + PAGE_FLOOR((size_t) mb_info + sizeof(multiboot_info_t)), + VMA_READ|VMA_CACHEABLE); + if (BUILTIN_EXPECT(ret, 0)) + goto out; + + if (mb_info->flags & MULTIBOOT_INFO_MODS) { + multiboot_module_t* mmodule = (multiboot_module_t*) ((size_t) mb_info->mods_addr); + + ret = vma_add(PAGE_CEIL((size_t) mb_info->mods_addr), + PAGE_FLOOR((size_t) mb_info->mods_addr + mb_info->mods_count*sizeof(multiboot_module_t)), + VMA_READ|VMA_CACHEABLE); + + //TODO: Why do we get error code -22 (-EINVAL); + ret = 0; // TODO: Remove workaround + + int i; + for(i=0; imods_count; i++) { + ret = vma_add(PAGE_CEIL(mmodule[i].mod_start), + PAGE_FLOOR(mmodule[i].mod_end), + VMA_READ|VMA_WRITE|VMA_CACHEABLE); + if (BUILTIN_EXPECT(ret, 0)) + goto out; + } + } + } + +out: + return ret; +} + +size_t vma_alloc(size_t size, uint32_t flags) +{ + task_t* task = current_task; + spinlock_t* lock; + vma_t** list; + + //kprintf("vma_alloc: size = %#lx, flags = %#x\n", size, flags); + + size_t base, limit; // boundaries for search + size_t start, end; // boundaries of free gaps + + if (flags & VMA_USER) { + base = VMA_USER_MIN; + limit = VMA_USER_MAX; + list = &task->vma_list; + lock = &task->vma_lock; + } + else { + base = VMA_KERN_MIN; + limit = VMA_KERN_MAX; + list = &vma_list; + lock = &vma_lock; + } + + spinlock_lock(lock); + + // first fit search for free memory area + vma_t* pred = NULL; // vma before current gap + vma_t* succ = *list; // vma after current gap + do { + start = (pred) ? pred->end : base; + end = (succ) ? succ->start : limit; + + if (start + size < end && start >= base && start + size < limit) + goto found; // we found a gap which is large enough and in the bounds + + pred = succ; + succ = (pred) ? pred->next : NULL; + } while (pred || succ); + +fail: + spinlock_unlock(lock); // we were unlucky to find a free gap + + return 0; + +found: + if (pred && pred->flags == flags) + pred->end = start + size; // resize VMA + else { + // insert new VMA + vma_t* new = kmalloc(sizeof(vma_t)); + if (BUILTIN_EXPECT(!new, 0)) + goto fail; + + new->start = start; + new->end = start + size; + new->flags = flags; + new->next = succ; + new->prev = pred; + + if (succ) + succ->prev = new; + if (pred) + pred->next = new; + else + *list = new; + } + + spinlock_unlock(lock); + + return start; +} + +int vma_free(size_t start, size_t end) +{ + task_t* task = current_task; + spinlock_t* lock; + vma_t* vma; + vma_t** list = NULL; + + //kprintf("vma_free: start = %#lx, end = %#lx\n", start, end); + + if (BUILTIN_EXPECT(start >= end, 0)) + return -EINVAL; + + if (end < VMA_KERN_MAX) { + lock = &vma_lock; + list = &vma_list; + } + else if (start >= VMA_KERN_MAX) { + lock = &task->vma_lock; + list = &task->vma_list; + } + + if (BUILTIN_EXPECT(!list || !*list, 0)) + return -EINVAL; + + spinlock_lock(lock); + + // search vma + vma = *list; + while (vma) { + if (start >= vma->start && end <= vma->end) break; + vma = vma->next; + } + + if (BUILTIN_EXPECT(!vma, 0)) { + spinlock_unlock(lock); + return -EINVAL; + } + + // free/resize vma + if (start == vma->start && end == vma->end) { + if (vma == *list) + *list = vma->next; // update list head + if (vma->prev) + vma->prev->next = vma->next; + if (vma->next) + vma->next->prev = vma->prev; + kfree(vma); + } + else if (start == vma->start) + vma->start = end; + else if (end == vma->end) + vma->end = start; + else { + vma_t* new = kmalloc(sizeof(vma_t)); + if (BUILTIN_EXPECT(!new, 0)) { + spinlock_unlock(lock); + return -ENOMEM; + } + + new->end = vma->end; + vma->end = start; + new->start = end; + + new->next = vma->next; + vma->next = new; + new->prev = vma; + } + + spinlock_unlock(lock); + + return 0; +} + +int vma_add(size_t start, size_t end, uint32_t flags) +{ + task_t* task = current_task; + spinlock_t* lock; + vma_t** list; + + if (BUILTIN_EXPECT(start >= end, 0)) + return -EINVAL; + + if (flags & VMA_USER) { + list = &task->vma_list; + lock = &task->vma_lock; + + // check if address is in userspace + if (BUILTIN_EXPECT(start < VMA_KERN_MAX, 0)) + return -EINVAL; + } + else { + list = &vma_list; + lock = &vma_lock; + + // check if address is in kernelspace + if (BUILTIN_EXPECT(end >= VMA_KERN_MAX, 0)) + return -EINVAL; + } + + //kprintf("vma_add: start = %#lx, end = %#lx, flags = %#x\n", start, end, flags); + + spinlock_lock(lock); + + // search gap + vma_t* pred = NULL; + vma_t* succ = *list; + + while (pred || succ) { + if ((!pred || pred->end <= start) && + (!succ || succ->start >= end)) + break; + + pred = succ; + succ = (succ) ? succ->next : NULL; + } + + if (BUILTIN_EXPECT(*list && !pred && !succ, 0)) { + spinlock_unlock(lock); + return -EINVAL; + } + + // insert new VMA + vma_t* new = kmalloc(sizeof(vma_t)); + if (BUILTIN_EXPECT(!new, 0)) { + spinlock_unlock(lock); + return -ENOMEM; + } + + new->start = start; + new->end = end; + new->flags = flags; + new->next = succ; + new->prev = pred; + + if (succ) + succ->prev = new; + if (pred) + pred->next = new; + else + *list = new; + + spinlock_unlock(lock); + + return 0; +} + +int copy_vma_list(task_t* src, task_t* dest) +{ + spinlock_init(&dest->vma_lock); + + spinlock_lock(&src->vma_lock); + spinlock_lock(&dest->vma_lock); + + vma_t* last = NULL; + vma_t* old; + for (old=src->vma_list; old; old=old->next) { + vma_t *new = kmalloc(sizeof(vma_t)); + if (BUILTIN_EXPECT(!new, 0)) { + spinlock_unlock(&dest->vma_lock); + spinlock_unlock(&src->vma_lock); + return -ENOMEM; + } + + new->start = old->start; + new->end = old->end; + new->flags = old->flags; + new->prev = last; + + if (last) + last->next = new; + else + dest->vma_list = new; + + last = new; + } + + spinlock_unlock(&dest->vma_lock); + spinlock_unlock(&src->vma_lock); + + return 0; +} + +int drop_vma_list(task_t *task) +{ + vma_t* vma; + + spinlock_lock(&task->vma_lock); + + while ((vma = task->vma_list)) { + task->vma_list = vma->next; + kfree(vma); + } + + spinlock_unlock(&task->vma_lock); + + return 0; +} + +void vma_dump(void) +{ + void print_vma(vma_t *vma) { + while (vma) { + kprintf("0x%lx - 0x%lx: size=%x, flags=%c%c%c\n", vma->start, vma->end, vma->end - vma->start, + (vma->flags & VMA_READ) ? 'r' : '-', + (vma->flags & VMA_WRITE) ? 'w' : '-', + (vma->flags & VMA_EXECUTE) ? 'x' : '-'); + vma = vma->next; + } + } + + task_t* task = current_task; + + kputs("Kernelspace VMAs:\n"); + spinlock_lock(&vma_lock); + print_vma(vma_list); + spinlock_unlock(&vma_lock); + + kputs("Userspace VMAs:\n"); + spinlock_lock(&task->vma_lock); + print_vma(task->vma_list); + spinlock_unlock(&task->vma_lock); +}